/* * Portions copyright (C) 2003 Russell King, PXA MMCI Driver * Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver * * Copyright 2008 Embedded Alley Solutions, Inc. * Copyright 2009-2010 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRIVER_NAME "mxs-mmc" /* * Card detect polling timeout */ #define MXS_MMC_DETECT_TIMEOUT (HZ/2) /* Max value supported for XFER_COUNT */ #define SSP_BUFFER_SIZE (65536) #ifndef BF #define BF(value, field) (((value) << BP_##field) & BM_##field) #endif #ifndef HW_SSP_XFER_SIZE #define HW_SSP_XFER_SIZE (0xFFFFFFFF) #endif #ifndef HW_SSP_BLOCK_SIZE #define HW_SSP_BLOCK_SIZE (0xFFFFFFFF) #endif #ifndef BP_SSP_XFER_SIZE_XFER_COUNT #define BP_SSP_XFER_SIZE_XFER_COUNT BP_SSP_CTRL0_XFER_COUNT #endif #ifndef BM_SSP_XFER_SIZE_XFER_COUNT #define BM_SSP_XFER_SIZE_XFER_COUNT BM_SSP_CTRL0_XFER_COUNT #endif #ifndef BF_SSP_XFER_SIZE_XFER_COUNT #define BF_SSP_XFER_SIZE_XFER_COUNT(v) \ (((v) << 0) & BM_SSP_CTRL0_XFER_COUNT) #endif #ifndef BP_SSP_BLOCK_SIZE_BLOCK_COUNT #define BP_SSP_BLOCK_SIZE_BLOCK_COUNT 8 #endif #ifndef BM_SSP_BLOCK_SIZE_BLOCK_COUNT #define BM_SSP_BLOCK_SIZE_BLOCK_COUNT 0x0000FF00 #endif #ifndef BF_SSP_BLOCK_SIZE_BLOCK_COUNT #define BF_SSP_BLOCK_SIZE_BLOCK_COUNT(v) \ (((v) << 8) & BM_SSP_BLOCK_SIZE_BLOCK_COUNT) #endif #ifndef BP_SSP_BLOCK_SIZE_BLOCK_SIZE #define BP_SSP_BLOCK_SIZE_BLOCK_SIZE 16 #endif #ifndef BM_SSP_BLOCK_SIZE_BLOCK_SIZE #define BM_SSP_BLOCK_SIZE_BLOCK_SIZE 0x000F0000 #endif #ifndef BF_SSP_BLOCK_SIZE_BLOCK_SIZE #define BF_SSP_BLOCK_SIZE_BLOCK_SIZE(v) \ (((v) << 16) & BM_SSP_BLOCK_SIZE_BLOCK_SIZE) #endif struct mxs_mmc_host { struct device *dev; struct mmc_host *mmc; struct clk *clk; unsigned int clkrt; struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_data *data; /* data bus width 0:1bit, 1:4bit, 2:8bit */ unsigned char bus_width; /* Whether SD card is present */ unsigned present:1; /* Polling timer */ struct timer_list timer; /* SSP interface which MMC/SD card slot is attached to */ void __iomem *ssp_base; /* DMA channel used for this host */ unsigned int dmach; /* IRQs */ int dmairq, errirq; /* DMA descriptor to transfer data over SSP interface */ struct mxs_dma_desc *dma_desc; /* DMA buffer */ dma_addr_t dma_buf_phys; char *dma_buf; struct completion dma_done; /* status on last interrupt */ u32 status; int read_uA, write_uA; struct regulator *regulator; /*! Regulator */ spinlock_t lock; int sdio_irq_en; }; /* Return read only state of card */ static int mxs_mmc_get_ro(struct mmc_host *mmc) { struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_mmc_platform_data *mmc_data = host->dev->platform_data; if (mmc_data && mmc_data->get_wp) return mmc_data->get_wp(); return 0; } /* Detect if card is plugged */ static inline int mxs_mmc_is_plugged(struct mxs_mmc_host *host) { u32 status = __raw_readl(host->ssp_base + HW_SSP_STATUS); return !(status & BM_SSP_STATUS_CARD_DETECT); } /* Card detection polling function */ static void mxs_mmc_detect_poll(unsigned long arg) { struct mxs_mmc_host *host = (struct mxs_mmc_host *)arg; int card_status; card_status = mxs_mmc_is_plugged(host); if (card_status != host->present) { host->present = card_status; mmc_detect_change(host->mmc, 0); } mod_timer(&host->timer, jiffies + MXS_MMC_DETECT_TIMEOUT); } #define MXS_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \ BM_SSP_CTRL1_RESP_ERR_IRQ | \ BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \ BM_SSP_CTRL1_DATA_TIMEOUT_IRQ | \ BM_SSP_CTRL1_DATA_CRC_IRQ | \ BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ | \ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \ BM_SSP_CTRL1_FIFO_OVERRUN_IRQ) #define MXS_MMC_ERR_BITS (BM_SSP_CTRL1_RESP_ERR_IRQ | \ BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \ BM_SSP_CTRL1_DATA_TIMEOUT_IRQ | \ BM_SSP_CTRL1_DATA_CRC_IRQ | \ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ) /* SSP DMA interrupt handler */ static irqreturn_t mmc_irq_handler(int irq, void *dev_id) { struct mxs_mmc_host *host = dev_id; u32 c1; c1 = __raw_readl(host->ssp_base + HW_SSP_CTRL1); __raw_writel(c1 & MXS_MMC_IRQ_BITS, host->ssp_base + HW_SSP_CTRL1_CLR); if (irq == host->dmairq) { dev_dbg(host->dev, "dma irq 0x%x and stop DMA.\n", irq); mxs_dma_ack_irq(host->dmach); /* STOP the dma transfer here. */ mxs_dma_cooked(host->dmach, NULL); } if ((irq == host->dmairq) || (c1 & MXS_MMC_ERR_BITS)) if (host->cmd) { host->status = __raw_readl(host->ssp_base + HW_SSP_STATUS); complete(&host->dma_done); } if ((c1 & BM_SSP_CTRL1_SDIO_IRQ) && (c1 & BM_SSP_CTRL1_SDIO_IRQ_EN)) mmc_signal_sdio_irq(host->mmc); return IRQ_HANDLED; } /* * Check for MMC command errors * Returns error code or zerro if no errors */ static inline int mxs_mmc_cmd_error(u32 status) { int err = 0; if (status & BM_SSP_STATUS_TIMEOUT) err = -ETIMEDOUT; else if (status & BM_SSP_STATUS_RESP_TIMEOUT) err = -ETIMEDOUT; else if (status & BM_SSP_STATUS_RESP_CRC_ERR) err = -EILSEQ; else if (status & BM_SSP_STATUS_RESP_ERR) err = -EIO; return err; } /* Send the BC command to the device */ static void mxs_mmc_bc(struct mxs_mmc_host *host) { struct mmc_command *cmd = host->cmd; struct mxs_dma_desc *dma_desc = host->dma_desc; unsigned long flags; dma_desc->cmd.cmd.bits.command = NO_DMA_XFER; dma_desc->cmd.cmd.bits.irq = 1; dma_desc->cmd.cmd.bits.dec_sem = 1; dma_desc->cmd.cmd.bits.wait4end = 1; dma_desc->cmd.cmd.bits.pio_words = 3; dma_desc->cmd.cmd.bits.bytes = 0; dma_desc->cmd.pio_words[0] = BM_SSP_CTRL0_ENABLE | BM_SSP_CTRL0_IGNORE_CRC; dma_desc->cmd.pio_words[1] = BF(cmd->opcode, SSP_CMD0_CMD) | BM_SSP_CMD0_APPEND_8CYC; dma_desc->cmd.pio_words[2] = BF(cmd->arg, SSP_CMD1_CMD_ARG); if (host->sdio_irq_en) { dma_desc->cmd.pio_words[0] |= BM_SSP_CTRL0_SDIO_IRQ_CHECK; dma_desc->cmd.pio_words[1] |= BM_SSP_CMD0_CONT_CLKING_EN \ | BM_SSP_CMD0_SLOW_CLKING_EN; } init_completion(&host->dma_done); mxs_dma_reset(host->dmach); if (mxs_dma_desc_append(host->dmach, host->dma_desc) < 0) dev_err(host->dev, "mmc_dma_desc_append failed\n"); dev_dbg(host->dev, "%s start DMA.\n", __func__); if (mxs_dma_enable(host->dmach) < 0) dev_err(host->dev, "mmc_dma_enable failed\n"); wait_for_completion(&host->dma_done); cmd->error = mxs_mmc_cmd_error(host->status); if (cmd->error) { dev_dbg(host->dev, "Command error 0x%x\n", cmd->error); mxs_dma_reset(host->dmach); } mxs_dma_disable(host->dmach); } /* Send the ac command to the device */ static void mxs_mmc_ac(struct mxs_mmc_host *host) { struct mmc_command *cmd = host->cmd; struct mxs_dma_desc *dma_desc = host->dma_desc; u32 ignore_crc, resp, long_resp; u32 ssp_ctrl0; u32 ssp_cmd0; u32 ssp_cmd1; unsigned long flags; ignore_crc = (mmc_resp_type(cmd) & MMC_RSP_CRC) ? 0 : BM_SSP_CTRL0_IGNORE_CRC; resp = (mmc_resp_type(cmd) & MMC_RSP_PRESENT) ? BM_SSP_CTRL0_GET_RESP : 0; long_resp = (mmc_resp_type(cmd) & MMC_RSP_136) ? BM_SSP_CTRL0_LONG_RESP : 0; dma_desc->cmd.cmd.bits.command = NO_DMA_XFER; dma_desc->cmd.cmd.bits.irq = 1; dma_desc->cmd.cmd.bits.dec_sem = 1; dma_desc->cmd.cmd.bits.wait4end = 1; dma_desc->cmd.cmd.bits.pio_words = 3; dma_desc->cmd.cmd.bits.bytes = 0; ssp_ctrl0 = BM_SSP_CTRL0_ENABLE | ignore_crc | long_resp | resp; ssp_cmd0 = BF(cmd->opcode, SSP_CMD0_CMD); ssp_cmd1 = BF(cmd->arg, SSP_CMD1_CMD_ARG); if (host->sdio_irq_en) { ssp_ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK; ssp_cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN \ | BM_SSP_CMD0_SLOW_CLKING_EN; } dma_desc->cmd.pio_words[0] = ssp_ctrl0; dma_desc->cmd.pio_words[1] = ssp_cmd0; dma_desc->cmd.pio_words[2] = ssp_cmd1; mxs_dma_reset(host->dmach); init_completion(&host->dma_done); if (mxs_dma_desc_append(host->dmach, host->dma_desc) < 0) dev_err(host->dev, "mmc_dma_desc_append failed\n"); dev_dbg(host->dev, "%s start DMA.\n", __func__); if (mxs_dma_enable(host->dmach) < 0) dev_err(host->dev, "mmc_dma_enable failed\n"); wait_for_completion(&host->dma_done); switch (mmc_resp_type(cmd)) { case MMC_RSP_NONE: while (__raw_readl(host->ssp_base + HW_SSP_CTRL0) & BM_SSP_CTRL0_RUN) continue; break; case MMC_RSP_R1: case MMC_RSP_R1B: case MMC_RSP_R3: cmd->resp[0] = __raw_readl(host->ssp_base + HW_SSP_SDRESP0); break; case MMC_RSP_R2: cmd->resp[3] = __raw_readl(host->ssp_base + HW_SSP_SDRESP0); cmd->resp[2] = __raw_readl(host->ssp_base + HW_SSP_SDRESP1); cmd->resp[1] = __raw_readl(host->ssp_base + HW_SSP_SDRESP2); cmd->resp[0] = __raw_readl(host->ssp_base + HW_SSP_SDRESP3); break; default: dev_warn(host->dev, "Unsupported response type 0x%x\n", mmc_resp_type(cmd)); BUG(); break; } cmd->error = mxs_mmc_cmd_error(host->status); if (cmd->error) { dev_dbg(host->dev, "Command error 0x%x\n", cmd->error); mxs_dma_reset(host->dmach); } mxs_dma_disable(host->dmach); } /* Copy data between sg list and dma buffer */ static unsigned int mxs_sg_dma_copy(struct mxs_mmc_host *host, unsigned int size, int to_dma) { struct mmc_data *data = host->cmd->data; unsigned int copy_size, bytes_copied = 0; struct scatterlist *sg; char *dmabuf = host->dma_buf; char *sgbuf; int len, i; sg = data->sg; len = data->sg_len; /* * Just loop through all entries. Size might not * be the entire list though so make sure that * we do not transfer too much. */ for (i = 0; i < len; i++) { sgbuf = kmap_atomic(sg_page(&sg[i]), KM_BIO_SRC_IRQ) + sg[i].offset; copy_size = size < sg[i].length ? size : sg[i].length; if (to_dma) memcpy(dmabuf, sgbuf, copy_size); else memcpy(sgbuf, dmabuf, copy_size); kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ); dmabuf += sg[i].length; bytes_copied += copy_size; size -= copy_size; if (size == 0) break; } return bytes_copied; } /* Convert ns to tick count according to the current sclk speed */ static unsigned short mxs_ns_to_ssp_ticks(unsigned clock_rate, unsigned ns) { const unsigned int ssp_timeout_mul = 4096; /* * Calculate ticks in ms since ns are large numbers * and might overflow */ const unsigned int clock_per_ms = clock_rate / 1000; const unsigned int ms = ns / 1000; const unsigned int ticks = ms * clock_per_ms; const unsigned int ssp_ticks = ticks / ssp_timeout_mul; BUG_ON(ssp_ticks == 0); return ssp_ticks; } static void __init_reg(struct device *dev, struct regulator **pp_reg) { #if 0 /* Up to now, there is not pwr ctrl. Just keep it for future usage. */ struct regulator *reg = *pp_reg; if (!reg) { reg = regulator_get(NULL, "mmc_ssp-1"); if (reg && !IS_ERR(reg)) regulator_set_mode(reg, REGULATOR_MODE_NORMAL); else reg = NULL; *pp_reg = reg; } #endif } /* Send adtc command to the card */ static void mxs_mmc_adtc(struct mxs_mmc_host *host) { struct mmc_command *cmd = host->cmd; struct mxs_dma_desc *dma_desc = host->dma_desc; int ignore_crc, resp, long_resp; int is_reading = 0; unsigned int copy_size; unsigned int ssp_ver_major; u32 ssp_ctrl0; u32 ssp_cmd0; u32 ssp_cmd1; u32 timeout; u32 val; u32 data_size = cmd->data->blksz * cmd->data->blocks; u32 log2_block_size; unsigned long flags; ignore_crc = mmc_resp_type(cmd) & MMC_RSP_CRC ? 0 : 1; resp = mmc_resp_type(cmd) & MMC_RSP_PRESENT ? 1 : 0; long_resp = mmc_resp_type(cmd) & MMC_RSP_136 ? 1 : 0; dev_dbg(host->dev, "ADTC command:\n" "response: %d, ignore crc: %d\n" "data list: %u, blocksz: %u, blocks: %u, timeout: %uns %uclks, " "flags: 0x%x\n", resp, ignore_crc, cmd->data->sg_len, cmd->data->blksz, cmd->data->blocks, cmd->data->timeout_ns, cmd->data->timeout_clks, cmd->data->flags); if (cmd->data->flags & MMC_DATA_WRITE) { dev_dbg(host->dev, "Data Write\n"); copy_size = mxs_sg_dma_copy(host, data_size, 1); BUG_ON(copy_size < data_size); is_reading = 0; if (!host->regulator) __init_reg(host->dev, &host->regulator); if (host->regulator) regulator_set_current_limit(host->regulator, host->write_uA, host->write_uA); } else if (cmd->data->flags & MMC_DATA_READ) { dev_dbg(host->dev, "Data Read\n"); is_reading = 1; if (!host->regulator) __init_reg(host->dev, &host->regulator); if (host->regulator) regulator_set_current_limit(host->regulator, host->read_uA, host->read_uA); } else { dev_warn(host->dev, "Unsuspported data mode, 0x%x\n", cmd->data->flags); BUG(); } BUG_ON(cmd->data->flags & MMC_DATA_STREAM); /* BUG_ON((data_size % 8) > 0); */ /* when is_reading is set, DMA controller performs WRITE operation. */ dma_desc->cmd.cmd.bits.command = is_reading ? DMA_WRITE : DMA_READ; dma_desc->cmd.cmd.bits.irq = 1; dma_desc->cmd.cmd.bits.dec_sem = 1; dma_desc->cmd.cmd.bits.wait4end = 1; dma_desc->cmd.cmd.bits.pio_words = 3; dma_desc->cmd.cmd.bits.bytes = data_size; ssp_ver_major = __raw_readl(host->ssp_base + HW_SSP_VERSION) >> 24; dev_dbg(host->dev, "ssp ver major is 0x%x\n", ssp_ver_major); if (ssp_ver_major > 3) { __raw_writel(data_size, host->ssp_base + HW_SSP_XFER_SIZE); ssp_ctrl0 = (ignore_crc ? BM_SSP_CTRL0_IGNORE_CRC : 0) | (resp ? BM_SSP_CTRL0_GET_RESP : 0) | (long_resp ? BM_SSP_CTRL0_LONG_RESP : 0) | (is_reading ? BM_SSP_CTRL0_READ : 0) | BM_SSP_CTRL0_DATA_XFER | BM_SSP_CTRL0_WAIT_FOR_IRQ | BM_SSP_CTRL0_ENABLE; if (host->bus_width == 2) ssp_ctrl0 |= BF(BV_SSP_CTRL0_BUS_WIDTH__EIGHT_BIT, SSP_CTRL0_BUS_WIDTH); else if (host->bus_width == 1) ssp_ctrl0 |= BF(BV_SSP_CTRL0_BUS_WIDTH__FOUR_BIT, SSP_CTRL0_BUS_WIDTH); else ssp_ctrl0 |= BF(BV_SSP_CTRL0_BUS_WIDTH__ONE_BIT, SSP_CTRL0_BUS_WIDTH); } else ssp_ctrl0 = (ignore_crc ? BM_SSP_CTRL0_IGNORE_CRC : 0) | (resp ? BM_SSP_CTRL0_GET_RESP : 0) | (long_resp ? BM_SSP_CTRL0_LONG_RESP : 0) | (is_reading ? BM_SSP_CTRL0_READ : 0) | BM_SSP_CTRL0_DATA_XFER | BM_SSP_CTRL0_WAIT_FOR_IRQ | BM_SSP_CTRL0_ENABLE | BF(data_size, SSP_XFER_SIZE_XFER_COUNT) | BF(host->bus_width ? BV_SSP_CTRL0_BUS_WIDTH__FOUR_BIT : BV_SSP_CTRL0_BUS_WIDTH__ONE_BIT, SSP_CTRL0_BUS_WIDTH); /* * We need to set the hardware register to the logarithm to base 2 of * the block size. */ log2_block_size = ilog2(cmd->data->blksz); dev_dbg(host->dev, "%s blksz is 0x%x.\n", __func__, log2_block_size); if (ssp_ver_major > 3) { /* Configure the BLOCK SIZE and BLOCK COUNT */ if ((1<data->blksz) { BUG_ON(cmd->data->blocks > 1); __raw_writel(0, host->ssp_base + HW_SSP_BLOCK_SIZE); } else{ val = BF(log2_block_size, SSP_BLOCK_SIZE_BLOCK_SIZE) | BF(cmd->data->blocks - 1, SSP_BLOCK_SIZE_BLOCK_COUNT); __raw_writel(val, host->ssp_base + HW_SSP_BLOCK_SIZE); } /* Configure the CMD0 */ ssp_cmd0 = BF(cmd->opcode, SSP_CMD0_CMD); } else { if ((1<data->blksz) { BUG_ON(cmd->data->blocks > 1); ssp_cmd0 = BF(0, SSP_BLOCK_SIZE_BLOCK_SIZE) | BF(cmd->opcode, SSP_CMD0_CMD) | BF(0, SSP_BLOCK_SIZE_BLOCK_COUNT); } else ssp_cmd0 = BF(log2_block_size, SSP_BLOCK_SIZE_BLOCK_SIZE) | BF(cmd->opcode, SSP_CMD0_CMD) | BF(cmd->data->blocks - 1, SSP_BLOCK_SIZE_BLOCK_COUNT); } if (host->sdio_irq_en) { ssp_ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK; ssp_cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN \ | BM_SSP_CMD0_SLOW_CLKING_EN; } if ((cmd->opcode == 12) || (cmd->opcode == 53)) ssp_cmd0 |= BM_SSP_CMD0_APPEND_8CYC; ssp_cmd1 = BF(cmd->arg, SSP_CMD1_CMD_ARG); dma_desc->cmd.pio_words[0] = ssp_ctrl0; dma_desc->cmd.pio_words[1] = ssp_cmd0; dma_desc->cmd.pio_words[2] = ssp_cmd1; /* Set the timeout count */ timeout = mxs_ns_to_ssp_ticks(host->clkrt, cmd->data->timeout_ns); val = __raw_readl(host->ssp_base + HW_SSP_TIMING); val &= ~(BM_SSP_TIMING_TIMEOUT); val |= BF(timeout, SSP_TIMING_TIMEOUT); __raw_writel(val, host->ssp_base + HW_SSP_TIMING); init_completion(&host->dma_done); mxs_dma_reset(host->dmach); if (mxs_dma_desc_append(host->dmach, host->dma_desc) < 0) dev_err(host->dev, "mmc_dma_desc_append failed\n"); dev_dbg(host->dev, "%s start DMA.\n", __func__); if (mxs_dma_enable(host->dmach) < 0) dev_err(host->dev, "mmc_dma_enable failed\n"); wait_for_completion(&host->dma_done); if (host->regulator) regulator_set_current_limit(host->regulator, 0, 0); switch (mmc_resp_type(cmd)) { case MMC_RSP_NONE: break; case MMC_RSP_R1: case MMC_RSP_R3: cmd->resp[0] = __raw_readl(host->ssp_base + HW_SSP_SDRESP0); break; case MMC_RSP_R2: cmd->resp[3] = __raw_readl(host->ssp_base + HW_SSP_SDRESP0); cmd->resp[2] = __raw_readl(host->ssp_base + HW_SSP_SDRESP1); cmd->resp[1] = __raw_readl(host->ssp_base + HW_SSP_SDRESP2); cmd->resp[0] = __raw_readl(host->ssp_base + HW_SSP_SDRESP3); break; default: dev_warn(host->dev, "Unsupported response type 0x%x\n", mmc_resp_type(cmd)); BUG(); break; } cmd->error = mxs_mmc_cmd_error(host->status); if (cmd->error) { dev_dbg(host->dev, "Command error 0x%x\n", cmd->error); mxs_dma_reset(host->dmach); } else { if (is_reading) { cmd->data->bytes_xfered = mxs_sg_dma_copy(host, data_size, 0); } else cmd->data->bytes_xfered = data_size; dev_dbg(host->dev, "Transferred %u bytes\n", cmd->data->bytes_xfered); } mxs_dma_disable(host->dmach); } /* Begin sedning a command to the card */ static void mxs_mmc_start_cmd(struct mxs_mmc_host *host, struct mmc_command *cmd) { dev_dbg(host->dev, "MMC command:\n" "type: 0x%x opcode: %u, arg: %u, flags 0x%x retries: %u\n", mmc_cmd_type(cmd), cmd->opcode, cmd->arg, cmd->flags, cmd->retries); host->cmd = cmd; switch (mmc_cmd_type(cmd)) { case MMC_CMD_BC: mxs_mmc_bc(host); break; case MMC_CMD_BCR: mxs_mmc_ac(host); break; case MMC_CMD_AC: mxs_mmc_ac(host); break; case MMC_CMD_ADTC: mxs_mmc_adtc(host); break; default: dev_warn(host->dev, "Unknown MMC command\n"); BUG(); break; } dev_dbg(host->dev, "response: %u %u %u %u errors: %u\n", cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error); } /* Handle MMC request */ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct mxs_mmc_host *host = mmc_priv(mmc); dev_dbg(host->dev, "MMC request\n"); if (!host->present) { mrq->cmd->error = -ETIMEDOUT; mmc_request_done(mmc, mrq); return; } BUG_ON(host->mrq != NULL); host->mrq = mrq; mxs_mmc_start_cmd(host, mrq->cmd); if (mrq->data && mrq->data->stop) { dev_dbg(host->dev, "Stop opcode is %u\n", mrq->data->stop->opcode); mxs_mmc_start_cmd(host, mrq->data->stop); } host->mrq = NULL; mmc_request_done(mmc, mrq); } /* * Change divisors to reflect the rate of 'hz'. Note that we should not * play with clock rate, because the same source is used to clock both * SSP ports. */ static void mxs_set_sclk_speed(struct mxs_mmc_host *host, unsigned int hz) { unsigned long ssp, bus_clk = 0; u32 div1, div2; u32 val; struct mxs_mmc_platform_data *mmc_data = host->dev->platform_data; if (mmc_data && mmc_data->setclock) { /* using SSP1, no timeout, clock rate 1 */ __raw_writel(BF(0xFFFF, SSP_TIMING_TIMEOUT) | BF(2, SSP_TIMING_CLOCK_DIVIDE) | BF(0, SSP_TIMING_CLOCK_RATE), host->ssp_base + HW_SSP_TIMING); /* if the SSP is buggy and platform provides callback... well, let it be. */ host->clkrt = mmc_data->setclock(hz); dev_dbg(host->dev, "Setting clock rate to %d Hz" "(requested %d)\n", host->clkrt, hz); dev_dbg(host->dev, "source %ldk\n", clk_get_rate(host->clk)); return; } /* ...but the RightIdea(tm) is to set divisors to match the requested clock. */ ssp = clk_get_rate(host->clk); for (div1 = 2; div1 < 254; div1 += 2) { div2 = ssp / hz / div1; if (div2 < 0x100) break; } if (div1 >= 254) { dev_err(host->dev, "Cannot set clock to %dHz\n", hz); return; } if (div2 == 0) bus_clk = ssp / div1; else bus_clk = ssp / div1 / div2; dev_dbg(host->dev, "Setting clock rate to %ld Hz [%x+%x] " "(requested %d), source %ldk\n", bus_clk, div1, div2, hz, ssp); val = __raw_readl(host->ssp_base + HW_SSP_TIMING); val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE); val |= BF(div1, SSP_TIMING_CLOCK_DIVIDE) | BF(div2 - 1, SSP_TIMING_CLOCK_RATE); __raw_writel(val, host->ssp_base + HW_SSP_TIMING); host->clkrt = bus_clk; } /* Configure card */ static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_mmc_platform_data *mmc_data; dev_dbg(host->dev, "MMC set ios:\n" "Clock %u, vdd %u, bus_mode %u, chip_select %u, " "power mode %u, bus_width %u\n", ios->clock, ios->vdd, ios->bus_mode, ios->chip_select, ios->power_mode, ios->bus_width); mmc_data = host->dev->platform_data; if (mmc_data->cmd_pullup) { if (ios->bus_mode == MMC_BUSMODE_PUSHPULL) mmc_data->cmd_pullup(0); else mmc_data->cmd_pullup(1); } else dev_warn(host->dev, "Platform does not support CMD pin pullup control\n"); if (ios->bus_width == MMC_BUS_WIDTH_8) host->bus_width = 2; else if (ios->bus_width == MMC_BUS_WIDTH_4) host->bus_width = 1; else host->bus_width = 0; dev_dbg(host->dev, "MMC bus_width %u\n", host->bus_width); if (ios->clock > 0) mxs_set_sclk_speed(host, ios->clock); } static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) { unsigned long flags; struct mxs_mmc_host *host = mmc_priv(mmc); spin_lock_irqsave(&host->lock, flags); if (enable) { if (host->sdio_irq_en) goto exit; host->sdio_irq_en = 1; __raw_writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, host->ssp_base + \ HW_SSP_CTRL0_SET); __raw_writel(BM_SSP_CTRL1_SDIO_IRQ_EN, host->ssp_base + \ HW_SSP_CTRL1_SET); if (__raw_readl(host->ssp_base + \ HW_SSP_STATUS) & BM_SSP_STATUS_SDIO_IRQ) mmc_signal_sdio_irq(host->mmc); } else { if (host->sdio_irq_en == 0) goto exit; host->sdio_irq_en = 0; __raw_writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, \ host->ssp_base + HW_SSP_CTRL0_CLR); __raw_writel(BM_SSP_CTRL1_SDIO_IRQ_EN, \ host->ssp_base + HW_SSP_CTRL1_CLR); } exit: spin_unlock_irqrestore(&host->lock, flags); return; } static const struct mmc_host_ops mxs_mmc_ops = { .request = mxs_mmc_request, .get_ro = mxs_mmc_get_ro, .set_ios = mxs_mmc_set_ios, .enable_sdio_irq = mxs_mmc_enable_sdio_irq, }; /* * MXS MMC/SD driver initialization */ /* Reset ssp peripheral to default values */ static void mxs_mmc_reset(struct mxs_mmc_host *host) { u32 ssp_ctrl0; u32 ssp_ctrl1; mxs_reset_block(host->ssp_base, 0); /* Configure SSP Control Register 0 */ ssp_ctrl0 = BM_SSP_CTRL0_IGNORE_CRC | BF(BV_SSP_CTRL0_BUS_WIDTH__ONE_BIT, SSP_CTRL0_BUS_WIDTH); /* Configure SSP Control Register 1 */ ssp_ctrl1 = BM_SSP_CTRL1_DMA_ENABLE | BM_SSP_CTRL1_POLARITY | BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN | BM_SSP_CTRL1_DATA_CRC_IRQ_EN | BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN | BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN | BM_SSP_CTRL1_RESP_ERR_IRQ_EN | BF(BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS, SSP_CTRL1_WORD_LENGTH) | BF(BV_SSP_CTRL1_SSP_MODE__SD_MMC, SSP_CTRL1_SSP_MODE); __raw_writel(BF(0xFFFF, SSP_TIMING_TIMEOUT) | BF(2, SSP_TIMING_CLOCK_DIVIDE) | BF(0, SSP_TIMING_CLOCK_RATE), host->ssp_base + HW_SSP_TIMING); if (host->sdio_irq_en) { ssp_ctrl1 |= BM_SSP_CTRL1_SDIO_IRQ_EN; ssp_ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK; } /* Write the SSP Control Register 0 and 1 values out to the interface */ __raw_writel(ssp_ctrl0, host->ssp_base + HW_SSP_CTRL0); __raw_writel(ssp_ctrl1, host->ssp_base + HW_SSP_CTRL1); } static void mxs_mmc_irq_release(struct mxs_mmc_host *host) { free_irq(host->dmairq, host); free_irq(host->errirq, host); } static int __init mxs_mmc_irq_init(struct mxs_mmc_host *host) { int ret; ret = request_irq(host->dmairq, mmc_irq_handler, 0, DRIVER_NAME " dma", host); if (ret) { dev_err(host->dev, "Unable to set up DMA irq handler\n"); goto out0; } ret = request_irq(host->errirq, mmc_irq_handler, IRQF_SHARED, DRIVER_NAME " error", host); if (ret) { dev_err(host->dev, "Unable to set up SSP error irq handler\n"); goto out1; } return 0; out1: free_irq(host->dmairq, host); out0: return ret; } /* Allocate and initialise the DMA chains */ static int mxs_mmc_dma_init(struct mxs_mmc_host *host, int reset) { int ret = 0; if (!reset) { /* Allocate DMA channel */ ret = mxs_dma_request(host->dmach, host->dev, "MXS MMC/SD"); if (ret) { dev_err(host->dev, "Unable to request DMA channel\n"); return ret; } host->dma_buf = dma_alloc_coherent(host->dev, SSP_BUFFER_SIZE, &host->dma_buf_phys, GFP_DMA); if (host->dma_buf == NULL) { dev_err(host->dev, "Unable to allocate DMA memory\n"); ret = -ENOMEM; goto out_mem; } host->dma_desc = mxs_dma_alloc_desc(); if (host->dma_desc == NULL) { dev_err(host->dev, "Unable to allocate DMA descriptor\n"); ret = -ENOMEM; goto out_cmd; } host->dma_desc->cmd.next = (u32) host->dma_desc->address; host->dma_desc->cmd.address = (u32) host->dma_buf_phys; host->dma_desc->buffer = host->dma_buf; } /* Reset DMA channel */ mxs_dma_reset(host->dmach); /* Enable DMA interrupt */ mxs_dma_ack_irq(host->dmach); mxs_dma_enable_irq(host->dmach, 1); return 0; out_cmd: dma_free_coherent(host->dev, SSP_BUFFER_SIZE, host->dma_buf, host->dma_buf_phys); out_mem: mxs_dma_release(host->dmach, host->dev); return ret; } static void mxs_mmc_dma_release(struct mxs_mmc_host *host) { mxs_dma_reset(host->dmach); mxs_dma_enable_irq(host->dmach, 0); mxs_dma_disable(host->dmach); mxs_dma_get_cooked(host->dmach, NULL); dma_free_coherent(host->dev, SSP_BUFFER_SIZE, host->dma_buf, host->dma_buf_phys); mxs_dma_free_desc(host->dma_desc); mxs_dma_release(host->dmach, host->dev); } /* Probe peripheral for connected cards */ static int __init mxs_mmc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mxs_mmc_platform_data *mmc_data; struct mxs_mmc_host *host; struct mmc_host *mmc; struct resource *r; int err = 0; unsigned int ssp_ver_major; mmc_data = dev->platform_data; if (mmc_data == NULL) { err = -EINVAL; dev_err(dev, "Missing platform data\n"); goto out; } /* Allocate main MMC host structure */ mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), dev); if (!mmc) { dev_err(dev, "Unable to allocate MMC host\n"); err = -ENOMEM; goto out; } host = mmc_priv(mmc); host->read_uA = mmc_data->read_uA; host->write_uA = mmc_data->write_uA; if (mmc_data->power_mmc != NULL) host->regulator = regulator_get(NULL, mmc_data->power_mmc); if (host->regulator && !IS_ERR(host->regulator)) regulator_set_mode(host->regulator, REGULATOR_MODE_NORMAL); else host->regulator = NULL; /* get resources: */ /* * 1. io memory */ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { dev_err(&pdev->dev, "failed to get IORESOURCE_MEM\n"); err = -ENXIO; goto out_res; } host->ssp_base = IO_ADDRESS(r->start); /* * 2. DMA channel */ r = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!r) { dev_err(&pdev->dev, "failed to get IORESOURCE_DMA\n"); err = -ENXIO; goto out_res; } host->dmach = r->start; /* * 3. two IRQs */ host->dmairq = platform_get_irq(pdev, 0); if (host->dmairq < 0) { dev_err(&pdev->dev, "failed to get IORESOURCE_IRQ/0\n"); err = host->dmairq; goto out_res; } host->errirq = platform_get_irq(pdev, 1); if (host->errirq < 0) { dev_err(&pdev->dev, "failed to get IORESOURCE_IRQ/1\n"); err = host->errirq; goto out_res; } /* Set up MMC pins */ if (mmc_data->hw_init) { err = mmc_data->hw_init(); if (err) { dev_err(dev, "MMC HW configuration failed\n"); goto out_res; } } host->mmc = mmc; host->dev = dev; host->sdio_irq_en = 0; /* Set minimal clock rate */ host->clk = clk_get(dev, mmc_data->clock_mmc); if (IS_ERR(host->clk)) { err = PTR_ERR(host->clk); dev_err(dev, "Clocks initialization failed\n"); goto out_clk; } clk_enable(host->clk); mxs_set_sclk_speed(host, mmc_data->min_clk); /* Reset MMC block */ mxs_mmc_reset(host); /* Enable DMA */ err = mxs_mmc_dma_init(host, 0); if (err) { dev_err(dev, "DMA init failed\n"); goto out_dma; } /* Set up interrupt handlers */ err = mxs_mmc_irq_init(host); if (err) { dev_err(dev, "IRQ initialization failed\n"); goto out_irq; } /* Get current card status for further cnanges tracking */ host->present = mxs_mmc_is_plugged(host); /* Add a card detection polling timer */ init_timer(&host->timer); host->timer.function = mxs_mmc_detect_poll; host->timer.data = (unsigned long)host; host->timer.expires = jiffies + MXS_MMC_DETECT_TIMEOUT; add_timer(&host->timer); mmc->ops = &mxs_mmc_ops; mmc->f_min = mmc_data->min_clk; mmc->f_max = mmc_data->max_clk; mmc->caps = mmc_data->caps; mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; mmc->caps |= MMC_CAP_SDIO_IRQ; /* Maximum block count requests. */ mmc->max_blk_size = 512; ssp_ver_major = __raw_readl(host->ssp_base + HW_SSP_VERSION) >> 24; dev_dbg(host->dev, "ssp ver major is 0x%x\n", ssp_ver_major); if (ssp_ver_major > 3) { mmc->max_blk_count = SSP_BUFFER_SIZE / 512; mmc->max_hw_segs = SSP_BUFFER_SIZE / 512; mmc->max_phys_segs = SSP_BUFFER_SIZE / 512; mmc->max_req_size = SSP_BUFFER_SIZE; mmc->max_seg_size = SSP_BUFFER_SIZE; } else { mmc->max_blk_count = SSP_BUFFER_SIZE / 512 - 1; mmc->max_hw_segs = SSP_BUFFER_SIZE / 512 - 1; mmc->max_phys_segs = SSP_BUFFER_SIZE / 512 - 1; mmc->max_req_size = SSP_BUFFER_SIZE - 512; mmc->max_seg_size = SSP_BUFFER_SIZE - 512; } mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; platform_set_drvdata(pdev, mmc); spin_lock_init(&host->lock); err = mmc_add_host(mmc); if (err) { dev_err(dev, "Oh God. mmc_add_host failed\n"); goto out_all; } dev_info(&pdev->dev, "%s: MXS SSP MMC DMAIRQ %d ERRIRQ %d \n", mmc_hostname(mmc), host->dmairq, host->errirq); return err; out_all: out_irq: mxs_mmc_dma_release(host); out_dma: clk_disable(host->clk); out_clk: if (mmc_data->hw_release) mmc_data->hw_release(); out_res: mmc_free_host(mmc); out: return err; } static int __exit mxs_mmc_remove(struct platform_device *pdev) { struct mxs_mmc_host *host; struct mxs_mmc_platform_data *mmc_data; struct mmc_host *mmc; dev_info(&pdev->dev, "Removing\n"); mmc_data = pdev->dev.platform_data; mmc = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); host = mmc_priv(mmc); mmc_remove_host(mmc); /* Disable SSP clock */ clk_disable(host->clk); clk_put(host->clk); /* Release IRQs */ mxs_mmc_irq_release(host); /* Delete card detection timer */ del_timer(&host->timer); /* Release DMA */ mxs_mmc_dma_release(host); if (host->regulator) regulator_put(host->regulator); mmc_free_host(mmc); if (mmc_data->hw_release) mmc_data->hw_release(); return 0; } #ifdef CONFIG_PM static int mxs_mmc_suspend(struct platform_device *pdev, pm_message_t state) { struct mxs_mmc_host *host; struct mxs_mmc_platform_data *mmc_data; struct mmc_host *mmc; int ret = 0; dev_dbg(&pdev->dev, "Suspending\n"); mmc_data = pdev->dev.platform_data; mmc = platform_get_drvdata(pdev); host = mmc_priv(mmc); ret = mmc_suspend_host(mmc, state); if (!ret) { if (mmc_data && mmc_data->hw_release) mmc_data->hw_release(); clk_disable(host->clk); } return ret; } static int mxs_mmc_resume(struct platform_device *pdev) { struct mxs_mmc_host *host; struct mxs_mmc_platform_data *mmc_data; struct mmc_host *mmc; dev_dbg(&pdev->dev, "Resuming\n"); mmc_data = pdev->dev.platform_data; mmc = platform_get_drvdata(pdev); host = mmc_priv(mmc); clk_enable(host->clk); if (mmc_data->hw_init) mmc_data->hw_init(); mxs_mmc_reset(host); mxs_mmc_dma_init(host, 1); return mmc_resume_host(mmc); } #else #define mxs_mmc_suspend NULL #define mxs_mmc_resume NULL #endif /* CONFIG_PM */ static struct platform_driver mxs_mmc_driver = { .probe = mxs_mmc_probe, .remove = __exit_p(mxs_mmc_remove), .suspend = mxs_mmc_suspend, .resume = mxs_mmc_resume, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, }; static int __init mxs_mmc_init(void) { int ret = 0; printk(KERN_INFO DRIVER_NAME ": MXS SSP Controller MMC Interface driver\n"); ret = platform_driver_register(&mxs_mmc_driver); if (ret < 0) return ret; return ret; } static void __exit mxs_mmc_exit(void) { platform_driver_unregister(&mxs_mmc_driver); } module_init(mxs_mmc_init); module_exit(mxs_mmc_exit); MODULE_DESCRIPTION("FREESCALE MXS MMC peripheral"); MODULE_LICENSE("GPL");