/** * ti_mmchs_reset_controller - * @arg: caller supplied arg * @segs: array of segments (although in our case should only be one) * @nsegs: number of segments (in our case should be 1) * @error: * * * */ static void ti_mmchs_reset_controller(struct ti_mmchs_softc *sc, uint32_t bit) { unsigned long attempts; uint32_t sysctl; ti_mmchs_dbg(sc, "reseting controller - bit 0x%08x\n", bit); sysctl = ti_mmchs_read_4(sc, MMCHS_SYSCTL); ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl | bit); /* * AM335x and OMAP4 >= ES2 have an updated reset logic. * Monitor a 0->1 transition first. */ if ((ti_chip() == CHIP_AM335X) || ((ti_chip() == CHIP_OMAP_4) && (ti_revision() > OMAP4430_REV_ES1_0))) { attempts = 10000; while (!(ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) && (attempts-- > 0)) continue; } attempts = 10000; while ((ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) && (attempts-- > 0)) continue; if (ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) device_printf(sc->sc_dev, "Error - Timeout waiting on controller reset\n"); }
/** * ti_mmchs_intr_error - handles error interrupts * @sc: pointer to the driver context * @cmd: the command that was sent previously * @stat_reg: the value that was in the status register * * * LOCKING: * Called from interrupt context * * RETURNS: * Return value indicates if the transaction is complete, not done = 0, done != 0 */ static int ti_mmchs_intr_error(struct ti_mmchs_softc *sc, struct mmc_command *cmd, uint32_t stat_reg) { ti_mmchs_dbg(sc, "error in xfer - stat 0x%08x\n", stat_reg); /* Ignore CRC errors on CMD2 and ACMD47, per relevant standards */ if ((stat_reg & MMCHS_STAT_CCRC) && (cmd->opcode == MMC_SEND_OP_COND || cmd->opcode == ACMD_SD_SEND_OP_COND)) cmd->error = MMC_ERR_NONE; else if (stat_reg & (MMCHS_STAT_CTO | MMCHS_STAT_DTO)) cmd->error = MMC_ERR_TIMEOUT; else if (stat_reg & (MMCHS_STAT_CCRC | MMCHS_STAT_DCRC)) cmd->error = MMC_ERR_BADCRC; else cmd->error = MMC_ERR_FAILED; /* If a dma transaction we should also stop the dma transfer */ if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DE) { /* Abort the DMA transfer (DDIR bit tells direction) */ if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DDIR) ti_sdma_stop_xfer(sc->sc_dmach_rd); else ti_sdma_stop_xfer(sc->sc_dmach_wr); /* If an error occure abort the DMA operation and free the dma map */ if ((sc->sc_dmamapped > 0) && (cmd->error != MMC_ERR_NONE)) { bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap); sc->sc_dmamapped--; } } /* Command error occured? ... if so issue a soft reset for the cmd fsm */ if (stat_reg & (MMCHS_STAT_CCRC | MMCHS_STAT_CTO)) { ti_mmchs_reset_controller(sc, MMCHS_SYSCTL_SRC); } /* Data error occured? ... if so issue a soft reset for the data line */ if (stat_reg & (MMCHS_STAT_DEB | MMCHS_STAT_DCRC | MMCHS_STAT_DTO)) { ti_mmchs_reset_controller(sc, MMCHS_SYSCTL_SRD); } /* On any error the command is cancelled ... so we are done */ return 1; }
/** * ti_mmchs_intr_error - handles error interrupts * @sc: pointer to the driver context * @cmd: the command that was sent previously * @stat_reg: the value that was in the status register * * * LOCKING: * Called from interrupt context * * RETURNS: * Return value indicates if the transaction is complete, not done = 0, done != 0 */ static int ti_mmchs_intr_error(struct ti_mmchs_softc *sc, struct mmc_command *cmd, uint32_t stat_reg) { ti_mmchs_dbg(sc, "error in xfer - stat 0x%08x\n", stat_reg); /* Ignore CRC errors on CMD2 and ACMD47, per relevant standards */ if ((stat_reg & MMCHS_STAT_CCRC) && (cmd->opcode == MMC_SEND_OP_COND || cmd->opcode == ACMD_SD_SEND_OP_COND)) cmd->error = MMC_ERR_NONE; else if (stat_reg & (MMCHS_STAT_CTO | MMCHS_STAT_DTO)) cmd->error = MMC_ERR_TIMEOUT; else if (stat_reg & (MMCHS_STAT_CCRC | MMCHS_STAT_DCRC)) cmd->error = MMC_ERR_BADCRC; else cmd->error = MMC_ERR_FAILED; /* If a dma transaction we should also stop the dma transfer */ if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DE) { /* Abort the DMA transfer (DDIR bit tells direction) */ if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DDIR) #ifdef SOC_TI_AM335X printf("%s: DMA unimplemented\n", __func__); #else ti_sdma_stop_xfer(sc->sc_dmach_rd); #endif else #ifdef SOC_TI_AM335X printf("%s: DMA unimplemented\n", __func__); #else ti_sdma_stop_xfer(sc->sc_dmach_wr); #endif /* If an error occure abort the DMA operation and free the dma map */ if ((sc->sc_dmamapped > 0) && (cmd->error != MMC_ERR_NONE)) { bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap); sc->sc_dmamapped--; } }
/** * ti_mmchs_send_init_stream - sets bus/controller settings * @brdev: mmc bridge device handle * @reqdev: device doing the request * * Send init stream sequence to card before sending IDLE command * * LOCKING: * * * RETURNS: * 0 if function succeeded */ static void ti_mmchs_send_init_stream(struct ti_mmchs_softc *sc) { unsigned long timeout; uint32_t ie, ise, con; ti_mmchs_dbg(sc, "Performing init sequence\n"); /* Prior to issuing any command, the MMCHS controller has to execute a * special INIT procedure. The MMCHS controller has to generate a clock * during 1ms. During the INIT procedure, the MMCHS controller generates 80 * clock periods. In order to keep the 1ms gap, the MMCHS controller should * be configured to generate a clock whose frequency is smaller or equal to * 80 KHz. If the MMCHS controller divider bitfield width doesn't allow to * choose big values, the MMCHS controller driver should perform the INIT * procedure twice or three times. Twice is generally enough. * * The INIt procedure is executed by setting MMCHS1.MMCHS_CON[1] INIT * bitfield to 1 and by sending a dummy command, writing 0x00000000 in * MMCHS1.MMCHS_CMD register. */ /* Disable interrupt status events but enable interrupt generation. * This doesn't seem right to me, but if the interrupt generation is not * enabled the CC bit doesn't seem to be set in the STAT register. */ /* Enable interrupt generation */ ie = ti_mmchs_read_4(sc, MMCHS_IE); ti_mmchs_write_4(sc, MMCHS_IE, 0x307F0033); /* Disable generation of status events (stops interrupt triggering) */ ise = ti_mmchs_read_4(sc, MMCHS_ISE); ti_mmchs_write_4(sc, MMCHS_ISE, 0); /* Set the initialise stream bit */ con = ti_mmchs_read_4(sc, MMCHS_CON); con |= MMCHS_CON_INIT; ti_mmchs_write_4(sc, MMCHS_CON, con); /* Write a dummy command 0x00 */ ti_mmchs_write_4(sc, MMCHS_CMD, 0x00000000); /* Loop waiting for the command to finish */ timeout = hz; do { pause("MMCINIT", 1); if (timeout-- == 0) { device_printf(sc->sc_dev, "Error: first stream init timed out\n"); break; } } while (!(ti_mmchs_read_4(sc, MMCHS_STAT) & MMCHS_STAT_CC)); /* Clear the command complete status bit */ ti_mmchs_write_4(sc, MMCHS_STAT, MMCHS_STAT_CC); /* Write another dummy command 0x00 */ ti_mmchs_write_4(sc, MMCHS_CMD, 0x00000000); /* Loop waiting for the second command to finish */ timeout = hz; do { pause("MMCINIT", 1); if (timeout-- == 0) { device_printf(sc->sc_dev, "Error: second stream init timed out\n"); break; } } while (!(ti_mmchs_read_4(sc, MMCHS_STAT) & MMCHS_STAT_CC)); /* Clear the stream init bit */ con &= ~MMCHS_CON_INIT; ti_mmchs_write_4(sc, MMCHS_CON, con); /* Clear the status register, then restore the IE and ISE registers */ ti_mmchs_write_4(sc, MMCHS_STAT, 0xffffffff); ti_mmchs_read_4(sc, MMCHS_STAT); ti_mmchs_write_4(sc, MMCHS_ISE, ise); ti_mmchs_write_4(sc, MMCHS_IE, ie); }