On Fri, Sep 10, 2010 at 12:02:44AM +0200, Martin Fuzzey wrote:
> Even though the i.MX21 SDHC module has the same revision number as the i.MX27
> one there are a few differences!!
>         Some interrupt enables are inverted.
>         FIFO is only 16 bits wide.
>         The argument is written to 2x16bit registers (vs 1x32).
>         Interrupts must be acknowledged via the INT_CNTR register.
>         Card clock must be enabled for each request.
>       For writes DMA must be enabled on command response not before.
> 
> Signed-off-by: Martin Fuzzey <mfuz...@gmail.com>
> 
> ---
> Changes from V7 (February 2010):
> Rebased to 2.6.36-rc3
> 
>  arch/arm/mach-imx/clock-imx21.c |    4 +
>  drivers/mmc/host/mxcmmc.c       |  153 
> ++++++++++++++++++++++++++++++---------
>  2 files changed, 120 insertions(+), 37 deletions(-)
> 
> diff --git a/arch/arm/mach-imx/clock-imx21.c b/arch/arm/mach-imx/clock-imx21.c
> index dafc271..076cd43 100644
> --- a/arch/arm/mach-imx/clock-imx21.c
> +++ b/arch/arm/mach-imx/clock-imx21.c
> @@ -1170,8 +1170,8 @@ static struct clk_lookup lookups[] = {
>       _REGISTER_CLOCK(NULL, "gpt1", gpt_clk[1])
>       _REGISTER_CLOCK(NULL, "gpt1", gpt_clk[2])
>       _REGISTER_CLOCK(NULL, "pwm", pwm_clk[0])
> -     _REGISTER_CLOCK(NULL, "sdhc1", sdhc_clk[0])
> -     _REGISTER_CLOCK(NULL, "sdhc2", sdhc_clk[1])
> +     _REGISTER_CLOCK("mxc-mmc.0", NULL, sdhc_clk[0])
> +     _REGISTER_CLOCK("mxc-mmc.1", NULL, sdhc_clk[1])
>       _REGISTER_CLOCK(NULL, "cspi1", cspi_clk[0])
>       _REGISTER_CLOCK(NULL, "cspi2", cspi_clk[1])
>       _REGISTER_CLOCK(NULL, "cspi3", cspi_clk[2])

Please make a seperate patch from this hunk. This should go via the i.MX
tree.

I will give this a runtime test on !i.MX21 tomorrow. Otherwise the patch
looks fine to me.

Sascha

> diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
> index 350f78e..88dd31f 100644
> --- a/drivers/mmc/host/mxcmmc.c
> +++ b/drivers/mmc/host/mxcmmc.c
> @@ -56,6 +56,8 @@
>  #define MMC_REG_INT_CNTR             0x24
>  #define MMC_REG_CMD                  0x28
>  #define MMC_REG_ARG                  0x2C
> +#define MMC_REG_ARGH                 0x2C
> +#define MMC_REG_ARGL                 0x30
>  #define MMC_REG_RES_FIFO             0x34
>  #define MMC_REG_BUFFER_ACCESS                0x38
>  
> @@ -145,6 +147,49 @@ struct mxcmci_host {
>  
>  static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int 
> clk_ios);
>  
> +static void mxcmci_write_arg(struct mxcmci_host *host, u32 arg)
> +{
> +     if (cpu_is_mx21()) {
> +             writel(arg >> 16, host->base + MMC_REG_ARGH);
> +             writel(arg & 0xFFFF, host->base + MMC_REG_ARGL);
> +     } else {
> +             writel(arg, host->base + MMC_REG_ARG);
> +     }
> +}
> +
> +static void mxcmci_ack_int(struct mxcmci_host *host, u32 stat)
> +{
> +     if (cpu_is_mx21()) {
> +             u32 intclr = readl(host->base + MMC_REG_INT_CNTR);
> +
> +             if (stat & STATUS_DATA_TRANS_DONE)
> +                     intclr |= INT_READ_OP_EN;
> +             if (stat & STATUS_WRITE_OP_DONE)
> +                     intclr |= INT_WRITE_OP_DONE_EN;
> +             if (stat & STATUS_END_CMD_RESP)
> +                     intclr |= INT_END_CMD_RES_EN;
> +
> +             writel(intclr, host->base + MMC_REG_INT_CNTR);
> +     }
> +}
> +
> +static inline void mxcmci_set_int_cntr(struct mxcmci_host *host, u32 enables)
> +{
> +     if (cpu_is_mx21()) /* some interrupt enables have reverse polarity */
> +             enables ^=  0x1F;
> +     writel(enables, host->base + MMC_REG_INT_CNTR);
> +}
> +
> +static inline void mxcmci_start_clock(struct mxcmci_host *host)
> +{
> +     writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
> +}
> +
> +static inline void mxcmci_stop_clock(struct mxcmci_host *host)
> +{
> +     writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
> +}
> +
>  static inline int mxcmci_use_dma(struct mxcmci_host *host)
>  {
>       return host->do_dma;
> @@ -221,7 +266,10 @@ static int mxcmci_setup_data(struct mxcmci_host *host, 
> struct mmc_data *data)
>       }
>       wmb();
>  
> -     imx_dma_enable(host->dma);
> +     /* MX21: unreliable writes if dma enabled here - do on command done */
> +     if (mxcmci_use_dma(host) &&
> +                     (!cpu_is_mx21() || host->dma_dir == DMA_FROM_DEVICE))
> +             imx_dma_enable(host->dma);
>  #endif /* HAS_DMA */
>       return 0;
>  }
> @@ -263,12 +311,16 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, 
> struct mmc_command *cmd,
>       spin_lock_irqsave(&host->lock, flags);
>       if (host->use_sdio)
>               int_cntr |= INT_SDIO_IRQ_EN;
> -     writel(int_cntr, host->base + MMC_REG_INT_CNTR);
> +     mxcmci_set_int_cntr(host, int_cntr);
>       spin_unlock_irqrestore(&host->lock, flags);
>  
>       writew(cmd->opcode, host->base + MMC_REG_CMD);
> -     writel(cmd->arg, host->base + MMC_REG_ARG);
> +     mxcmci_write_arg(host, cmd->arg);
>       writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
> +     if (cpu_is_mx21()) {
> +             /* i.MX21 requires clock start after submitting command */
> +             mxcmci_start_clock(host);
> +     }
>  
>       return 0;
>  }
> @@ -282,7 +334,7 @@ static void mxcmci_finish_request(struct mxcmci_host 
> *host,
>       spin_lock_irqsave(&host->lock, flags);
>       if (host->use_sdio)
>               int_cntr |= INT_SDIO_IRQ_EN;
> -     writel(int_cntr, host->base + MMC_REG_INT_CNTR);
> +     mxcmci_set_int_cntr(host, int_cntr);
>       spin_unlock_irqrestore(&host->lock, flags);
>  
>       host->req = NULL;
> @@ -397,19 +449,29 @@ static int mxcmci_poll_status(struct mxcmci_host *host, 
> u32 mask)
>  static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
>  {
>       unsigned int stat;
> -     u32 *buf = _buf;
> -
> -     while (bytes > 3) {
> -             stat = mxcmci_poll_status(host,
> +     u16 *buf16 = _buf;
> +     u32 *buf32 = _buf;
> +     int count = 0;
> +     int fifo_size = host->cmdat & CMD_DAT_CONT_BUS_WIDTH_4 ? 64 : 16;
> +     int buffer_width = cpu_is_mx21() ? 2 : 4;
> +
> +     while (bytes >= buffer_width) {
> +             if (count % fifo_size == 0) {
> +                     stat = mxcmci_poll_status(host,
>                               STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
> -             if (stat)
> -                     return stat;
> -             *buf++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
> -             bytes -= 4;
> +                     if (stat)
> +                             return stat;
> +             }
> +             if (buffer_width == 2)
> +                     *buf16++ = (u16)readl(
> +                             host->base + MMC_REG_BUFFER_ACCESS);
> +             else
> +                     *buf32++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
> +             bytes -= buffer_width;
> +             count += buffer_width;
>       }
>  
>       if (bytes) {
> -             u8 *b = (u8 *)buf;
>               u32 tmp;
>  
>               stat = mxcmci_poll_status(host,
> @@ -417,7 +479,10 @@ static int mxcmci_pull(struct mxcmci_host *host, void 
> *_buf, int bytes)
>               if (stat)
>                       return stat;
>               tmp = readl(host->base + MMC_REG_BUFFER_ACCESS);
> -             memcpy(b, &tmp, bytes);
> +             if (buffer_width == 2)
> +                     memcpy((u8 *)buf16, &tmp, bytes);
> +             else
> +                     memcpy((u8 *)buf32, &tmp, bytes);
>       }
>  
>       return 0;
> @@ -426,33 +491,41 @@ static int mxcmci_pull(struct mxcmci_host *host, void 
> *_buf, int bytes)
>  static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
>  {
>       unsigned int stat;
> -     u32 *buf = _buf;
> -
> -     while (bytes > 3) {
> -             stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> -             if (stat)
> -                     return stat;
> -             writel(*buf++, host->base + MMC_REG_BUFFER_ACCESS);
> -             bytes -= 4;
> +     u16 *buf16 = _buf;
> +     u32 *buf32 = _buf;
> +     int count = 0;
> +     int fifo_size = host->cmdat & CMD_DAT_CONT_BUS_WIDTH_4 ? 64 : 16;
> +     int buffer_width = cpu_is_mx21() ? 2 : 4;
> +
> +     while (bytes >= buffer_width) {
> +             if (count % fifo_size == 0) {
> +                     stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> +                     if (stat)
> +                             return stat;
> +             }
> +             if (buffer_width == 2)
> +                     writel(*buf16++, host->base + MMC_REG_BUFFER_ACCESS);
> +             else
> +                     writel(*buf32++, host->base + MMC_REG_BUFFER_ACCESS);
> +             bytes -= buffer_width;
> +             count += buffer_width;
>       }
>  
>       if (bytes) {
> -             u8 *b = (u8 *)buf;
>               u32 tmp;
>  
>               stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
>               if (stat)
>                       return stat;
>  
> -             memcpy(&tmp, b, bytes);
> +             if (buffer_width == 2)
> +                     memcpy(&tmp, (u8 *)buf16, bytes);
> +             else
> +                     memcpy(&tmp, (u8 *)buf32, bytes);
>               writel(tmp, host->base + MMC_REG_BUFFER_ACCESS);
>       }
>  
> -     stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> -     if (stat)
> -             return stat;
> -
> -     return 0;
> +     return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
>  }
>  
>  static int mxcmci_transfer_data(struct mxcmci_host *host)
> @@ -540,13 +613,20 @@ static void mxcmci_cmd_done(struct mxcmci_host *host, 
> unsigned int stat)
>               return;
>       }
>  
> +     if (!host->data)
> +             return;
> +
> +#ifdef HAS_DMA
>       /* For the DMA case the DMA engine handles the data transfer
>        * automatically. For non DMA we have to do it ourselves.
>        * Don't do it in interrupt context though.
>        */
> -     if (!mxcmci_use_dma(host) && host->data)
> +     if (mxcmci_use_dma(host)) {
> +             if (cpu_is_mx21() && host->dma_dir == DMA_TO_DEVICE)
> +                     imx_dma_enable(host->dma);
> +     } else
> +#endif
>               schedule_work(&host->datawork);
> -
>  }
>  
>  static irqreturn_t mxcmci_irq(int irq, void *devid)
> @@ -559,6 +639,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
>       stat = readl(host->base + MMC_REG_STATUS);
>       writel(stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
>                       STATUS_WRITE_OP_DONE), host->base + MMC_REG_STATUS);
> +     mxcmci_ack_int(host, stat);
>  
>       dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
>  
> @@ -689,9 +770,9 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct 
> mmc_ios *ios)
>  
>       if (ios->clock) {
>               mxcmci_set_clk_rate(host, ios->clock);
> -             writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
> +             mxcmci_start_clock(host);
>       } else {
> -             writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
> +             mxcmci_stop_clock(host);
>       }
>  
>       host->clock = ios->clock;
> @@ -845,7 +926,7 @@ static int mxcmci_probe(struct platform_device *pdev)
>       /* recommended in data sheet */
>       writew(0x2db4, host->base + MMC_REG_READ_TO);
>  
> -     writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR);
> +     mxcmci_set_int_cntr(host, host->default_irq_mask);
>  
>  #ifdef HAS_DMA
>       host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
> @@ -862,7 +943,9 @@ static int mxcmci_probe(struct platform_device *pdev)
>       }
>  
>       ret = imx_dma_config_channel(host->dma,
> -                                  IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO,
> +                                 (cpu_is_mx21() ?
> +                                     IMX_DMA_MEMSIZE_16 : IMX_DMA_MEMSIZE_32)
> +                                     | IMX_DMA_TYPE_FIFO,
>                                    IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
>                                    r->start, 0);
>       if (ret) {
> 
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to