/** * ti_mmchs_intr - interrupt handler for MMC/SD/SDIO controller * @arg: pointer to the driver context * * Interrupt handler for the MMC/SD/SDIO controller, responsible for handling * the IRQ and clearing the status flags. * * LOCKING: * Called from interrupt context * * RETURNS: * nothing */ static void ti_mmchs_intr(void *arg) { struct ti_mmchs_softc *sc = (struct ti_mmchs_softc *) arg; uint32_t stat_reg; int done = 0; TI_MMCHS_LOCK(sc); stat_reg = ti_mmchs_read_4(sc, MMCHS_STAT) & (ti_mmchs_read_4(sc, MMCHS_IE) | MMCHS_STAT_ERRI); if (sc->curcmd == NULL) { device_printf(sc->sc_dev, "Error: current cmd NULL, already done?\n"); ti_mmchs_write_4(sc, MMCHS_STAT, stat_reg); TI_MMCHS_UNLOCK(sc); return; } if (stat_reg & MMCHS_STAT_ERRI) { /* An error has been tripped in the status register */ done = ti_mmchs_intr_error(sc, sc->curcmd, stat_reg); } else { /* NOTE: This implementation could be a bit inefficent, I don't think * it is necessary to handle both the 'command complete' and 'transfer * complete' for data transfers ... presumably just transfer complete * is enough. */ /* No error */ sc->curcmd->error = MMC_ERR_NONE; /* Check if the command completed */ if (stat_reg & MMCHS_STAT_CC) { done = ti_mmchs_intr_cmd_compl(sc, sc->curcmd); } /* Check if the transfer has completed */ if (stat_reg & MMCHS_STAT_TC) { done = ti_mmchs_intr_xfer_compl(sc, sc->curcmd); } } /* Clear all the interrupt status bits by writing the value back */ ti_mmchs_write_4(sc, MMCHS_STAT, stat_reg); /* This may mark the command as done if there is no stop request */ /* TODO: This is a bit ugly, needs fix-up */ if (done) { ti_mmchs_start(sc); } TI_MMCHS_UNLOCK(sc); }
/** * 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_fini - shutdown the MMC/SD/SIO controller * @dev: mmc device handle * * Responsible for shutting done the MMC controller, this function may be * called as part of a reset sequence. * * LOCKING: * No locking, assumed to be called during tear-down/reset. * * RETURNS: * nothing */ static void ti_mmchs_hw_fini(device_t dev) { struct ti_mmchs_softc *sc = device_get_softc(dev); /* Disable all interrupts */ ti_mmchs_write_4(sc, MMCHS_ISE, 0x00000000); ti_mmchs_write_4(sc, MMCHS_IE, 0x00000000); /* Disable the functional and interface clocks */ switch (device_get_unit(dev)) { case 0: ti_prcm_clk_disable(MMC1_CLK); break; case 1: ti_prcm_clk_disable(MMC2_CLK); break; case 2: ti_prcm_clk_disable(MMC3_CLK); break; } }
/** * ti_mmchs_update_ios - sets bus/controller settings * @brdev: mmc bridge device handle * @reqdev: device doing the request * * Called to set the bus and controller settings that need to be applied to * the actual HW. Currently this function just sets the bus width and the * clock speed. * * LOCKING: * * * RETURNS: * 0 if function succeeded */ static int ti_mmchs_update_ios(device_t brdev, device_t reqdev) { struct ti_mmchs_softc *sc; struct mmc_host *host; struct mmc_ios *ios; uint32_t clkdiv; uint32_t hctl_reg; uint32_t con_reg; uint32_t sysctl_reg; uint16_t mv; unsigned long timeout; int do_card_init = 0; sc = device_get_softc(brdev); host = &sc->host; ios = &host->ios; /* Read the initial values of the registers */ hctl_reg = ti_mmchs_read_4(sc, MMCHS_HCTL); con_reg = ti_mmchs_read_4(sc, MMCHS_CON); /* Set the bus width */ switch (ios->bus_width) { case bus_width_1: hctl_reg &= ~MMCHS_HCTL_DTW; con_reg &= ~MMCHS_CON_DW8; break; case bus_width_4: hctl_reg |= MMCHS_HCTL_DTW; con_reg &= ~MMCHS_CON_DW8; break; case bus_width_8: con_reg |= MMCHS_CON_DW8; break; } /* Finally write all these settings back to the registers */ ti_mmchs_write_4(sc, MMCHS_HCTL, hctl_reg); ti_mmchs_write_4(sc, MMCHS_CON, con_reg); /* Check if we need to change the external voltage regulator */ if (sc->sc_cur_power_mode != ios->power_mode) { if (ios->power_mode == power_up) { /* Set the power level */ hctl_reg = ti_mmchs_read_4(sc, MMCHS_HCTL); hctl_reg &= ~(MMCHS_HCTL_SDVS_MASK | MMCHS_HCTL_SDBP); if ((ios->vdd == -1) || (ios->vdd >= vdd_240)) { mv = 3000; hctl_reg |= MMCHS_HCTL_SDVS_V30; } else { mv = 1800; hctl_reg |= MMCHS_HCTL_SDVS_V18; } ti_mmchs_write_4(sc, MMCHS_HCTL, hctl_reg); /* Set the desired voltage on the regulator */ if (sc->sc_vreg_dev && sc->sc_vreg_name) twl_vreg_set_voltage(sc->sc_vreg_dev, sc->sc_vreg_name, mv); /* Enable the bus power */ ti_mmchs_write_4(sc, MMCHS_HCTL, (hctl_reg | MMCHS_HCTL_SDBP)); timeout = hz; while (!(ti_mmchs_read_4(sc, MMCHS_HCTL) & MMCHS_HCTL_SDBP)) { if (timeout-- == 0) break; pause("MMC_PWRON", 1); } } else if (ios->power_mode == power_off) { /* Disable the bus power */ hctl_reg = ti_mmchs_read_4(sc, MMCHS_HCTL); ti_mmchs_write_4(sc, MMCHS_HCTL, (hctl_reg & ~MMCHS_HCTL_SDBP)); /* Turn the power off on the voltage regulator */ if (sc->sc_vreg_dev && sc->sc_vreg_name) twl_vreg_set_voltage(sc->sc_vreg_dev, sc->sc_vreg_name, 0); } else if (ios->power_mode == power_on) { /* Force a card re-initialisation sequence */ do_card_init = 1; } /* Save the new power state */ sc->sc_cur_power_mode = ios->power_mode; } /* need the MMCHS_SYSCTL register */ sysctl_reg = ti_mmchs_read_4(sc, MMCHS_SYSCTL); /* Just in case this hasn't been setup before, set the timeout to the default */ sysctl_reg &= ~MMCHS_SYSCTL_DTO_MASK; sysctl_reg |= MMCHS_SYSCTL_DTO(0xe); /* Disable the clock output while configuring the new clock */ sysctl_reg &= ~(MMCHS_SYSCTL_ICE | MMCHS_SYSCTL_CEN); ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg); /* bus mode? */ if (ios->clock == 0) { clkdiv = 0; } else { clkdiv = sc->sc_ref_freq / ios->clock; if (clkdiv < 1) clkdiv = 1; if ((sc->sc_ref_freq / clkdiv) > ios->clock) clkdiv += 1; if (clkdiv > 250) clkdiv = 250; } /* Set the new clock divider */ sysctl_reg &= ~MMCHS_SYSCTL_CLKD_MASK; sysctl_reg |= MMCHS_SYSCTL_CLKD(clkdiv); /* Write the new settings ... */ ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg); /* ... write the internal clock enable bit ... */ ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg | MMCHS_SYSCTL_ICE); /* ... wait for the clock to stablise ... */ while (((sysctl_reg = ti_mmchs_read_4(sc, MMCHS_SYSCTL)) & MMCHS_SYSCTL_ICS) == 0) { continue; } /* ... then enable */ sysctl_reg |= MMCHS_SYSCTL_CEN; ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg); /* If the power state has changed to 'power_on' then run the init sequence*/ if (do_card_init) { ti_mmchs_send_init_stream(sc); } /* Set the bus mode (opendrain or normal) */ con_reg = ti_mmchs_read_4(sc, MMCHS_CON); if (ios->bus_mode == opendrain) con_reg |= MMCHS_CON_OD; else con_reg &= ~MMCHS_CON_OD; ti_mmchs_write_4(sc, MMCHS_CON, con_reg); return (0); }
/** * 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); }
/** * ti_mmchs_start_cmd - starts the given command * @sc: pointer to the driver context * @cmd: the command to start * * The call tree for this function is * - ti_mmchs_start_cmd * - ti_mmchs_start * - ti_mmchs_request * * LOCKING: * Caller should be holding the OMAP_MMC lock. * * RETURNS: * nothing */ static void ti_mmchs_start_cmd(struct ti_mmchs_softc *sc, struct mmc_command *cmd) { uint32_t cmd_reg, con_reg, ise_reg; struct mmc_data *data; struct mmc_request *req; void *vaddr; bus_addr_t paddr; uint32_t pktsize; sc->curcmd = cmd; data = cmd->data; req = cmd->mrq; /* Ensure the STR and MIT bits are cleared, these are only used for special * command types. */ con_reg = ti_mmchs_read_4(sc, MMCHS_CON); con_reg &= ~(MMCHS_CON_STR | MMCHS_CON_MIT); /* Load the command into bits 29:24 of the CMD register */ cmd_reg = (uint32_t)(cmd->opcode & 0x3F) << 24; /* Set the default set of interrupts */ ise_reg = (MMCHS_STAT_CERR | MMCHS_STAT_CTO | MMCHS_STAT_CC | MMCHS_STAT_CEB); /* Enable CRC checking if requested */ if (cmd->flags & MMC_RSP_CRC) ise_reg |= MMCHS_STAT_CCRC; /* Enable reply index checking if the response supports it */ if (cmd->flags & MMC_RSP_OPCODE) ise_reg |= MMCHS_STAT_CIE; /* Set the expected response length */ if (MMC_RSP(cmd->flags) == MMC_RSP_NONE) { cmd_reg |= MMCHS_CMD_RSP_TYPE_NO; } else { if (cmd->flags & MMC_RSP_136) cmd_reg |= MMCHS_CMD_RSP_TYPE_136; else if (cmd->flags & MMC_RSP_BUSY) cmd_reg |= MMCHS_CMD_RSP_TYPE_48_BSY; else cmd_reg |= MMCHS_CMD_RSP_TYPE_48; /* Enable command index/crc checks if necessary expected */ if (cmd->flags & MMC_RSP_CRC) cmd_reg |= MMCHS_CMD_CCCE; if (cmd->flags & MMC_RSP_OPCODE) cmd_reg |= MMCHS_CMD_CICE; } /* Set the bits for the special commands CMD12 (MMC_STOP_TRANSMISSION) and * CMD52 (SD_IO_RW_DIRECT) */ if (cmd->opcode == MMC_STOP_TRANSMISSION) cmd_reg |= MMCHS_CMD_CMD_TYPE_IO_ABORT; /* Check if there is any data to write */ if (data == NULL) { /* Clear the block count */ ti_mmchs_write_4(sc, MMCHS_BLK, 0); /* The no data case is fairly simple */ ti_mmchs_write_4(sc, MMCHS_CON, con_reg); ti_mmchs_write_4(sc, MMCHS_IE, ise_reg); ti_mmchs_write_4(sc, MMCHS_ISE, ise_reg); ti_mmchs_write_4(sc, MMCHS_ARG, cmd->arg); ti_mmchs_write_4(sc, MMCHS_CMD, cmd_reg); return; } /* Indicate that data is present */ cmd_reg |= MMCHS_CMD_DP | MMCHS_CMD_MSBS | MMCHS_CMD_BCE; /* Indicate a read operation */ if (data->flags & MMC_DATA_READ) cmd_reg |= MMCHS_CMD_DDIR; /* Streaming mode */ if (data->flags & MMC_DATA_STREAM) { con_reg |= MMCHS_CON_STR; } /* Multi-block mode */ if (data->flags & MMC_DATA_MULTI) { cmd_reg |= MMCHS_CMD_MSBS; } /* Enable extra interrupt sources for the transfer */ ise_reg |= (MMCHS_STAT_TC | MMCHS_STAT_DTO | MMCHS_STAT_DEB | MMCHS_STAT_CEB); if (cmd->flags & MMC_RSP_CRC) ise_reg |= MMCHS_STAT_DCRC; /* Enable the DMA transfer bit */ cmd_reg |= MMCHS_CMD_DE; /* Set the block size and block count */ ti_mmchs_write_4(sc, MMCHS_BLK, (1 << 16) | data->len); /* Setup the DMA stuff */ if (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)) { vaddr = data->data; data->xfer_len = 0; /* Map the buffer buf into bus space using the dmamap map. */ if (bus_dmamap_load(sc->sc_dmatag, sc->sc_dmamap, vaddr, data->len, ti_mmchs_getaddr, &paddr, 0) != 0) { if (req->cmd->flags & STOP_STARTED) req->stop->error = MMC_ERR_NO_MEMORY; else req->cmd->error = MMC_ERR_NO_MEMORY; sc->req = NULL; sc->curcmd = NULL; req->done(req); return; } /* Calculate the packet size, the max packet size is 512 bytes * (or 128 32-bit elements). */ pktsize = min((data->len / 4), (512 / 4)); /* Sync the DMA buffer and setup the DMA controller */ if (data->flags & MMC_DATA_READ) { bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_PREREAD); ti_sdma_start_xfer_packet(sc->sc_dmach_rd, sc->sc_data_reg_paddr, paddr, 1, (data->len / 4), pktsize); } else { bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_PREWRITE); ti_sdma_start_xfer_packet(sc->sc_dmach_wr, paddr, sc->sc_data_reg_paddr, 1, (data->len / 4), pktsize); } /* Increase the mapped count */ sc->sc_dmamapped++; sc->sc_cmd_data_vaddr = vaddr; sc->sc_cmd_data_len = data->len; } /* Finally kick off the command */ ti_mmchs_write_4(sc, MMCHS_CON, con_reg); ti_mmchs_write_4(sc, MMCHS_IE, ise_reg); ti_mmchs_write_4(sc, MMCHS_ISE, ise_reg); ti_mmchs_write_4(sc, MMCHS_ARG, cmd->arg); ti_mmchs_write_4(sc, MMCHS_CMD, cmd_reg); /* and we're done */ }
/** * ti_mmchs_hw_init - initialises the MMC/SD/SIO controller * @dev: mmc device handle * * Called by the driver attach function during driver initialisation. This * function is responsibly to setup the controller ready for transactions. * * LOCKING: * No locking, assumed to only be called during initialisation. * * RETURNS: * nothing */ static void ti_mmchs_hw_init(device_t dev) { struct ti_mmchs_softc *sc = device_get_softc(dev); clk_ident_t clk; unsigned long timeout; uint32_t sysctl; uint32_t capa; uint32_t con; /* 1: Enable the controller and interface/functional clocks */ clk = MMC1_CLK + device_get_unit(dev); if (ti_prcm_clk_enable(clk) != 0) { device_printf(dev, "Error: failed to enable MMC clock\n"); return; } /* 1a: Get the frequency of the source clock */ if (ti_prcm_clk_get_source_freq(clk, &sc->sc_ref_freq) != 0) { device_printf(dev, "Error: failed to get source clock freq\n"); return; } /* 2: Issue a softreset to the controller */ ti_mmchs_write_4(sc, MMCHS_SYSCONFIG, 0x0002); timeout = 100; while ((ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) & 0x01) == 0x0) { DELAY(1000); if (timeout-- == 0) { device_printf(dev, "Error: reset operation timed out\n"); return; } } /* 3: Reset both the command and data state machines */ sysctl = ti_mmchs_read_4(sc, MMCHS_SYSCTL); ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl | MMCHS_SYSCTL_SRA); timeout = 100; while ((ti_mmchs_read_4(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRA) != 0x0) { DELAY(1000); if (timeout-- == 0) { device_printf(dev, "Error: reset operation timed out\n"); return; } } /* 4: Set initial host configuration (1-bit mode, pwroff) and capabilities */ ti_mmchs_write_4(sc, MMCHS_HCTL, MMCHS_HCTL_SDVS_V30); capa = ti_mmchs_read_4(sc, MMCHS_CAPA); ti_mmchs_write_4(sc, MMCHS_CAPA, capa | MMCHS_CAPA_VS30 | MMCHS_CAPA_VS18); /* 5: Set the initial bus configuration * 0 CTPL_MMC_SD : Control Power for DAT1 line * 0 WPP_ACTIVE_HIGH : Write protect polarity * 0 CDP_ACTIVE_HIGH : Card detect polarity * 0 CTO_ENABLED : MMC interrupt command * 0 DW8_DISABLED : 8-bit mode MMC select * 0 MODE_FUNC : Mode select * 0 STREAM_DISABLED : Stream command * 0 HR_DISABLED : Broadcast host response * 0 INIT_DISABLED : Send initialization stream * 0 OD_DISABLED : No Open Drain */ con = ti_mmchs_read_4(sc, MMCHS_CON) & MMCHS_CON_DVAL_MASK; ti_mmchs_write_4(sc, MMCHS_CON, con); }