Message ID | 54d94f230908060805v2bc8cefai81f567116209b7dc@mail.gmail.com (mailing list archive) |
---|---|
State | Rejected |
Headers | show |
PhaneendraKumar A <phani@embwise.com> writes: > Fixed various issues related to SDIO interrupt handling and verified the > functionality on DM355EVM and DM365EVM, which were also later pointed by > David Brownell. > > Signed-off-by: phani@embwise.com patch is line wrapped. Kevin > --- >  drivers/mmc/host/davinci_mmc.c | 285 +++++++++++++++++++++++++++++----------- >  1 files changed, 206 insertions(+), 79 deletions(-) > > diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c > index 8907b72..a701962 100644 > --- a/drivers/mmc/host/davinci_mmc.c > +++ b/drivers/mmc/host/davinci_mmc.c > @@ -31,6 +31,7 @@ >  #include <linux/delay.h> >  #include <linux/dma-mapping.h> >  #include <linux/mmc/mmc.h> > +#include <linux/mmc/card.h> >  >  #include <mach/mmc.h> >  #include <mach/edma.h> > @@ -65,8 +66,8 @@ >  #define DAVINCI_MMCBLNC     0x60 >  #define DAVINCI_SDIOCTL     0x64 >  #define DAVINCI_SDIOST0     0x68 > -#define DAVINCI_SDIOEN      0x6C > -#define DAVINCI_SDIOST      0x70 > +#define DAVINCI_SDIOIEN     0x6C > +#define DAVINCI_SDIOIST     0x70 >  #define DAVINCI_MMCFIFOCTL  0x74 /* FIFO Control Register            */ >  >  /* DAVINCI_MMCCTL definitions */ > @@ -100,6 +101,8 @@ >  #define MMCST0_DATED         BIT(11)   /* DAT3 edge detect */ >  #define MMCST0_TRNDNE        BIT(12)   /* transfer done */ >  > +#define MMCST0_ERR_MASK        (0x00F8) > + >  /* DAVINCI_MMCST1 definitions */ >  #define MMCST1_BUSY          (1 << 0) >  > @@ -133,6 +136,23 @@ >  /* MMCSD Init clock in Hz in opendrain mode */ >  #define MMCSD_INIT_CLOCK      200000 >  > +/* DAVINCI_SDIOCTL definitions */ > +#define SDIOCTL_RDWTRQ_SET    BIT(0) > +#define SDIOCTL_RDWTCR_SET    BIT(1) > + > +/* DAVINCI_SDIOST0 definitions */ > +#define SDIOST0_DAT1_HI       BIT(0) > +#define SDIOST0_INTPRD       BIT(1) > +#define SDIOST0_RDWTST       BIT(2) > + > +/* DAVINCI_SDIOIEN definitions */ > +#define SDIOIEN_IOINTEN       BIT(0) > +#define SDIOIEN_RWSEN       BIT(1) > + > +/* DAVINCI_SDIOIST definitions */ > +#define SDIOIST_IOINT       BIT(0) > +#define SDIOIST_RWS          BIT(1) > + >  /* >  * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units, >  * and we handle up to NR_SG segments. MMC_BLOCK_BOUNCE kicks in only > @@ -145,6 +165,9 @@ >  >  #define NR_SG      16 >  > +#define DM355_SDIO_IRQ(deviceId)            \ > +   (((deviceId) == 0) ? "sdio0" : "sdio1") > + >  static unsigned rw_threshold = 32; >  module_param(rw_threshold, uint, S_IRUGO); >  MODULE_PARM_DESC(rw_threshold, > @@ -162,7 +185,7 @@ struct mmc_davinci_host { >     unsigned int mmc_input_clk; >     void __iomem *base; >     struct resource *mem_res; > -   int irq; > +   int mmc_irq, sdio_irq; >     unsigned char bus_mode; >  >  #define DAVINCI_MMC_DATADIR_NONE   0 > @@ -181,6 +204,7 @@ struct mmc_davinci_host { >     u32 rxdma, txdma; >     bool use_dma; >     bool do_dma; > +   u32 sdio_int; >  >     /* Scatterlist DMA uses one or more parameter RAM entries: >      * the main one (associated with rxdma or txdma) plus zero or > @@ -387,6 +411,16 @@ static void mmc_davinci_dma_cb(unsigned channel, u16 > ch_status, void *data) >     if (DMA_COMPLETE != ch_status) { >        struct mmc_davinci_host *host = data; >  > +      if (!(host->data)) { > +         dev_warn(mmc_dev(host->mmc), > +            "DMA Event Miss / NULL Transfr\n"); > +         edma_stop(host->txdma); > +         edma_clean_channel(host->txdma); > +         edma_stop(host->rxdma); > +         edma_clean_channel(host->rxdma); > +         return; > +      } > + >        /* Currently means: DMA Event Missed, or "null" transfer >         * request was seen. In the future, TC errors (like bad >         * addresses) might be presented too. > @@ -664,6 +698,14 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, > struct mmc_request *req) >     host->buffer = NULL; >     host->bytes_left = data->blocks * data->blksz; >  > +   if (host->mmc->card) { > +      if (mmc_card_sdio(host->mmc->card)) { > +         if ((data->blksz == 64)) { > +            mdelay(5); > +         } > +      } > +   } > + >     /* For now we try to use DMA whenever we won't need partial FIFO >      * reads or writes, either for the whole transfer (as tested here) >      * or for any individual scatterlist segment (tested when we call > @@ -826,12 +868,17 @@ static void mmc_davinci_set_ios(struct mmc_host *mmc, > struct mmc_ios *ios) >  static void >  mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data) >  { > -   host->data = NULL; > -   host->data_dir = DAVINCI_MMC_DATADIR_NONE; > +   davinci_abort_dma(host); >  > -   if (host->do_dma) { > -      davinci_abort_dma(host); > +   if (host->mmc->caps & MMC_CAP_SDIO_IRQ) { > +      if (host->sdio_int && (!((readl(host->base + DAVINCI_SDIOST0)) > +               & SDIOST0_DAT1_HI))) { > +         writel(SDIOIST_IOINT, host->base + DAVINCI_SDIOIST); > +         mmc_signal_sdio_irq(host->mmc); > +      } > +   } >  > +   if (host->do_dma) { >        dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, >               (data->flags & MMC_DATA_WRITE) >               ? DMA_TO_DEVICE > @@ -839,6 +886,9 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct > mmc_data *data) >        host->do_dma = false; >     } >  > +   host->data = NULL; > +   host->data_dir = DAVINCI_MMC_DATADIR_NONE; > + >     if (!data->stop || (host->cmd && host->cmd->error)) { >        mmc_request_done(host->mmc, data->mrq); >        writel(0, host->base + DAVINCI_MMCIM); > @@ -887,6 +937,22 @@ davinci_abort_data(struct mmc_davinci_host *host, struct > mmc_data *data) >     writel(temp, host->base + DAVINCI_MMCCTL); >  } >  > +static irqreturn_t mmc_davinci_sdio_irq(int irq, void *dev_id) > +{ > +   struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id; > +   unsigned int status; > + > +   status = readl(host->base + DAVINCI_SDIOIST); > +   if (status & SDIOIST_IOINT) { > +      dev_dbg(mmc_dev(host->mmc), > +            "SDIO interrupt status %x\n", status); > +      writel(status | SDIOIST_IOINT, > +            host->base + DAVINCI_SDIOIST); > +      mmc_signal_sdio_irq(host->mmc); > +   } > +   return IRQ_HANDLED; > +} > + >  static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) >  { >     struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id; > @@ -907,6 +973,73 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) >     status = readl(host->base + DAVINCI_MMCST0); >     qstatus = status; >  > +   if (qstatus & MMCST0_ERR_MASK) { > +      if (qstatus & MMCST0_TOUTRD) { > +         /* Read data timeout */ > +         data->error = -ETIMEDOUT; > +         end_transfer = 1; > + > +         dev_err(mmc_dev(host->mmc), > +               "read data timeout, status %x\n", > +               qstatus); > + > +         davinci_abort_data(host, data); > +         goto end_data; > +      } > + > +      if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) { > +         /* Data CRC error */ > +         data->error = -EILSEQ; > +         end_transfer = 1; > + > +         /* NOTE: this controller uses CRCWR to report both CRC > +          * errors and timeouts (on writes). MMCDRSP values are > +          * only weakly documented, but 0x9f was clearly a > +          * timeout case and the two three-bit patterns in > +          * various SD specs (101, 010) aren't part of it ... > +          */ > +         if (qstatus & MMCST0_CRCWR) { > +            u32 temp = readb(host->base + DAVINCI_MMCDRSP); > + > +            if (temp == 0x9f) > +               data->error = -ETIMEDOUT; > +         } > +         dev_err(mmc_dev(host->mmc), "data %s %s error\n", > +            (qstatus & MMCST0_CRCWR) ? "write" : > +            "read",   (data->error == -ETIMEDOUT) ? > +            "timeout" : "CRC"); > + > +         davinci_abort_data(host, data); > +         goto end_data; > +      } > + > +      if (qstatus & MMCST0_TOUTRS) { > +         /* Command timeout */ > +         if (host->cmd) { > +            dev_err(mmc_dev(host->mmc), > +                  "CMD%d timeout, status %x\n", > +                  host->cmd->opcode, qstatus); > +            host->cmd->error = -ETIMEDOUT; > +            if (data) { > +               end_transfer = 1; > +               davinci_abort_data(host, data); > +            } else > +               end_command = 1; > +         } > +         goto end_cmd; > +      } > + > +      if (qstatus & MMCST0_CRCRS) { > +         /* Command CRC error */ > +         dev_err(mmc_dev(host->mmc), "Command CRC error\n"); > +         if (host->cmd) { > +            host->cmd->error = -EILSEQ; > +            end_command = 1; > +         } > +         goto end_cmd; > +      } > +   } > + >     /* handle FIFO first when using PIO for data. >      * bytes_left will decrease to zero as I/O progress and status will >      * read zero over iteration because this controller status > @@ -922,6 +1055,11 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) >        qstatus |= status; >     } >  > +   if (qstatus & MMCST0_RSPDNE) { > +      /* End of command phase */ > +      end_command = (int) host->cmd; > +   } > + >     if (qstatus & MMCST0_DATDNE) { >        /* All blocks sent/received, and CRC checks passed */ >        if (data != NULL) { > @@ -939,73 +1077,10 @@ static irqreturn_t mmc_davinci_irq(int irq, void > *dev_id) >        } >     } >  > -   if (qstatus & MMCST0_TOUTRD) { > -      /* Read data timeout */ > -      data->error = -ETIMEDOUT; > -      end_transfer = 1; > - > -      dev_dbg(mmc_dev(host->mmc), > -         "read data timeout, status %x\n", > -         qstatus); > - > -      davinci_abort_data(host, data); > -   } > - > -   if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) { > -      /* Data CRC error */ > -      data->error = -EILSEQ; > -      end_transfer = 1; > - > -      /* NOTE: this controller uses CRCWR to report both CRC > -       * errors and timeouts (on writes). MMCDRSP values are > -       * only weakly documented, but 0x9f was clearly a timeout > -       * case and the two three-bit patterns in various SD specs > -       * (101, 010) aren't part of it ... > -       */ > -      if (qstatus & MMCST0_CRCWR) { > -         u32 temp = readb(host->base + DAVINCI_MMCDRSP); > - > -         if (temp == 0x9f) > -            data->error = -ETIMEDOUT; > -      } > -      dev_dbg(mmc_dev(host->mmc), "data %s %s error\n", > -         (qstatus & MMCST0_CRCWR) ? "write" : "read", > -         (data->error == -ETIMEDOUT) ? "timeout" : "CRC"); > - > -      davinci_abort_data(host, data); > -   } > - > -   if (qstatus & MMCST0_TOUTRS) { > -      /* Command timeout */ > -      if (host->cmd) { > -         dev_dbg(mmc_dev(host->mmc), > -            "CMD%d timeout, status %x\n", > -            host->cmd->opcode, qstatus); > -         host->cmd->error = -ETIMEDOUT; > -         if (data) { > -            end_transfer = 1; > -            davinci_abort_data(host, data); > -         } else > -            end_command = 1; > -      } > -   } > - > -   if (qstatus & MMCST0_CRCRS) { > -      /* Command CRC error */ > -      dev_dbg(mmc_dev(host->mmc), "Command CRC error\n"); > -      if (host->cmd) { > -         host->cmd->error = -EILSEQ; > -         end_command = 1; > -      } > -   } > - > -   if (qstatus & MMCST0_RSPDNE) { > -      /* End of command phase */ > -      end_command = (int) host->cmd; > -   } > - > +end_cmd: >     if (end_command) >        mmc_davinci_cmd_done(host, host->cmd); > +end_data: >     if (end_transfer) >        mmc_davinci_xfer_done(host, data); >     return IRQ_HANDLED; > @@ -1031,11 +1106,34 @@ static int mmc_davinci_get_ro(struct mmc_host *mmc) >     return config->get_ro(pdev->id); >  } >  > +static void mmc_davinci_enable_sdio_irq(struct mmc_host *mmc, int enable) > +{ > +   struct mmc_davinci_host *host = mmc_priv(mmc); > + > +   if (enable) { > +      if (!((readl(host->base + DAVINCI_SDIOST0)) > +            & SDIOST0_DAT1_HI)) { > +         writel(SDIOIST_IOINT, > +               host->base + DAVINCI_SDIOIST); > +         mmc_signal_sdio_irq(host->mmc); > +      }else { > +         host->sdio_int = 1; > +         writel(readl(host->base + DAVINCI_SDIOIEN) | > +            SDIOIEN_IOINTEN, host->base + DAVINCI_SDIOIEN); > +      } > +   } else { > +      host->sdio_int = 0; > +      writel(readl(host->base + DAVINCI_SDIOIEN) & ~SDIOIEN_IOINTEN, > +            host->base + DAVINCI_SDIOIEN); > +   } > + > +} >  static struct mmc_host_ops mmc_davinci_ops = { >     .request   = mmc_davinci_request, >     .set_ios   = mmc_davinci_set_ios, >     .get_cd      = mmc_davinci_get_cd, >     .get_ro      = mmc_davinci_get_ro, > +   .enable_sdio_irq   = mmc_davinci_enable_sdio_irq, >  }; >  >  /*----------------------------------------------------------------------*/ > @@ -1072,15 +1170,14 @@ static int __init davinci_mmcsd_probe(struct > platform_device *pdev) >     struct mmc_davinci_host *host = NULL; >     struct mmc_host *mmc = NULL; >     struct resource *r, *mem = NULL; > -   int ret = 0, irq = 0; > +   int ret = 0; >     size_t mem_size; >  >     /* REVISIT: when we're fully converted, fail if pdata is NULL */ >  >     ret = -ENODEV; >     r = platform_get_resource(pdev, IORESOURCE_MEM, 0); > -   irq = platform_get_irq(pdev, 0); > -   if (!r || irq == NO_IRQ) > +   if (!r) >        goto out; >  >     ret = -EBUSY; > @@ -1097,6 +1194,16 @@ static int __init davinci_mmcsd_probe(struct > platform_device *pdev) >     host = mmc_priv(mmc); >     host->mmc = mmc;   /* Important */ >  > +   r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > +   if (!r) > +      goto out; > +   host->mmc_irq = r->start; > + > +   r = platform_get_resource(pdev, IORESOURCE_IRQ, 1); > +   if (!r) > +      goto out; > +   host->sdio_irq = r->start; > + >     r = platform_get_resource(pdev, IORESOURCE_DMA, 0); >     if (!r) >        goto out; > @@ -1124,7 +1231,6 @@ static int __init davinci_mmcsd_probe(struct > platform_device *pdev) >     init_mmcsd_host(host); >  >     host->use_dma = use_dma; > -   host->irq = irq; >  >     if (host->use_dma && davinci_acquire_dma_channels(host) != 0) >        host->use_dma = 0; > @@ -1173,10 +1279,22 @@ static int __init davinci_mmcsd_probe(struct > platform_device *pdev) >     if (ret < 0) >        goto out; >  > -   ret = request_irq(irq, mmc_davinci_irq, 0, mmc_hostname(mmc), host); > +   ret = request_irq(host->mmc_irq, mmc_davinci_irq, 0, > +      mmc_hostname(mmc), host); >     if (ret) >        goto out; >  > +   if (host->sdio_irq > 0) { > +      ret = request_irq(host->sdio_irq, > +            mmc_davinci_sdio_irq, 0, > +            DM355_SDIO_IRQ(pdev->id), host); > +      if (ret == 0) { > +         mmc->caps |= MMC_CAP_SDIO_IRQ; > +         host->sdio_int = 0; > +      } else > +         goto out; > +   } > + >     rename_region(mem, mmc_hostname(mmc)); >  >     dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n", > @@ -1215,10 +1333,19 @@ static int __exit davinci_mmcsd_remove(struct > platform_device *pdev) >  >     platform_set_drvdata(pdev, NULL); >     if (host) { > + > +      writel((readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKEN), > +            host->base + DAVINCI_MMCCLK); > + >        mmc_remove_host(host->mmc); > -      free_irq(host->irq, host); >  > -      davinci_release_dma_channels(host); > +      free_irq(host->mmc_irq, host); > + > +      if (host->mmc->caps & MMC_CAP_SDIO_IRQ) > +         free_irq(host->sdio_irq, host); > + > +      if (host->use_dma) > +         davinci_release_dma_channels(host); >  >        clk_disable(host->clk); >        clk_put(host->clk); > -- > 1.6.0.4 > > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source@linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 8907b72..a701962 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -31,6 +31,7 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/mmc/mmc.h> +#include <linux/mmc/card.h> #include <mach/mmc.h> #include <mach/edma.h> @@ -65,8 +66,8 @@ #define DAVINCI_MMCBLNC 0x60 #define DAVINCI_SDIOCTL 0x64 #define DAVINCI_SDIOST0 0x68 -#define DAVINCI_SDIOEN 0x6C -#define DAVINCI_SDIOST 0x70 +#define DAVINCI_SDIOIEN 0x6C +#define DAVINCI_SDIOIST 0x70 #define DAVINCI_MMCFIFOCTL 0x74 /* FIFO Control Register */ /* DAVINCI_MMCCTL definitions */ @@ -100,6 +101,8 @@ #define MMCST0_DATED BIT(11) /* DAT3 edge detect */ #define MMCST0_TRNDNE BIT(12) /* transfer done */ +#define MMCST0_ERR_MASK (0x00F8) + /* DAVINCI_MMCST1 definitions */ #define MMCST1_BUSY (1 << 0) @@ -133,6 +136,23 @@ /* MMCSD Init clock in Hz in opendrain mode */ #define MMCSD_INIT_CLOCK 200000 +/* DAVINCI_SDIOCTL definitions */ +#define SDIOCTL_RDWTRQ_SET BIT(0) +#define SDIOCTL_RDWTCR_SET BIT(1) + +/* DAVINCI_SDIOST0 definitions */ +#define SDIOST0_DAT1_HI BIT(0) +#define SDIOST0_INTPRD BIT(1) +#define SDIOST0_RDWTST BIT(2) + +/* DAVINCI_SDIOIEN definitions */ +#define SDIOIEN_IOINTEN BIT(0) +#define SDIOIEN_RWSEN BIT(1) + +/* DAVINCI_SDIOIST definitions */ +#define SDIOIST_IOINT BIT(0) +#define SDIOIST_RWS BIT(1) + /* * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units, * and we handle up to NR_SG segments. MMC_BLOCK_BOUNCE kicks in only @@ -145,6 +165,9 @@ #define NR_SG 16 +#define DM355_SDIO_IRQ(deviceId) \ + (((deviceId) == 0) ? "sdio0" : "sdio1") + static unsigned rw_threshold = 32; module_param(rw_threshold, uint, S_IRUGO); MODULE_PARM_DESC(rw_threshold, @@ -162,7 +185,7 @@ struct mmc_davinci_host { unsigned int mmc_input_clk; void __iomem *base; struct resource *mem_res; - int irq; + int mmc_irq, sdio_irq; unsigned char bus_mode; #define DAVINCI_MMC_DATADIR_NONE 0 @@ -181,6 +204,7 @@ struct mmc_davinci_host { u32 rxdma, txdma; bool use_dma; bool do_dma; + u32 sdio_int; /* Scatterlist DMA uses one or more parameter RAM entries: * the main one (associated with rxdma or txdma) plus zero or @@ -387,6 +411,16 @@ static void mmc_davinci_dma_cb(unsigned channel, u16 ch_status, void *data) if (DMA_COMPLETE != ch_status) { struct mmc_davinci_host *host = data; + if (!(host->data)) { + dev_warn(mmc_dev(host->mmc), + "DMA Event Miss / NULL Transfr\n"); + edma_stop(host->txdma); + edma_clean_channel(host->txdma); + edma_stop(host->rxdma); + edma_clean_channel(host->rxdma); + return; + } + /* Currently means: DMA Event Missed, or "null" transfer * request was seen. In the future, TC errors (like bad
Fixed various issues related to SDIO interrupt handling and verified the functionality on DM355EVM and DM365EVM, which were also later pointed by David Brownell. Signed-off-by: phani@embwise.com --- drivers/mmc/host/davinci_mmc.c | 285 +++++++++++++++++++++++++++++----------- 1 files changed, 206 insertions(+), 79 deletions(-) * addresses) might be presented too. @@ -664,6 +698,14 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) host->buffer = NULL; host->bytes_left = data->blocks * data->blksz; + if (host->mmc->card) { + if (mmc_card_sdio(host->mmc->card)) { + if ((data->blksz == 64)) { + mdelay(5); + } + } + } + /* For now we try to use DMA whenever we won't need partial FIFO * reads or writes, either for the whole transfer (as tested here) * or for any individual scatterlist segment (tested when we call @@ -826,12 +868,17 @@ static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static void mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data) { - host->data = NULL; - host->data_dir = DAVINCI_MMC_DATADIR_NONE; + davinci_abort_dma(host); - if (host->do_dma) { - davinci_abort_dma(host); + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) { + if (host->sdio_int && (!((readl(host->base + DAVINCI_SDIOST0)) + & SDIOST0_DAT1_HI))) { + writel(SDIOIST_IOINT, host->base + DAVINCI_SDIOIST); + mmc_signal_sdio_irq(host->mmc); + } + } + if (host->do_dma) { dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, (data->flags & MMC_DATA_WRITE) ? DMA_TO_DEVICE @@ -839,6 +886,9 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data) host->do_dma = false; } + host->data = NULL; + host->data_dir = DAVINCI_MMC_DATADIR_NONE; + if (!data->stop || (host->cmd && host->cmd->error)) { mmc_request_done(host->mmc, data->mrq); writel(0, host->base + DAVINCI_MMCIM); @@ -887,6 +937,22 @@ davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data) writel(temp, host->base + DAVINCI_MMCCTL); } +static irqreturn_t mmc_davinci_sdio_irq(int irq, void *dev_id) +{ + struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id; + unsigned int status; + + status = readl(host->base + DAVINCI_SDIOIST); + if (status & SDIOIST_IOINT) { + dev_dbg(mmc_dev(host->mmc), + "SDIO interrupt status %x\n", status); + writel(status | SDIOIST_IOINT, + host->base + DAVINCI_SDIOIST); + mmc_signal_sdio_irq(host->mmc); + } + return IRQ_HANDLED; +} + static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) { struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id; @@ -907,6 +973,73 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) status = readl(host->base + DAVINCI_MMCST0); qstatus = status; + if (qstatus & MMCST0_ERR_MASK) { + if (qstatus & MMCST0_TOUTRD) { + /* Read data timeout */ + data->error = -ETIMEDOUT; + end_transfer = 1; + + dev_err(mmc_dev(host->mmc), + "read data timeout, status %x\n", + qstatus); + + davinci_abort_data(host, data); + goto end_data; + } + + if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) { + /* Data CRC error */ + data->error = -EILSEQ; + end_transfer = 1; + + /* NOTE: this controller uses CRCWR to report both CRC + * errors and timeouts (on writes). MMCDRSP values are + * only weakly documented, but 0x9f was clearly a + * timeout case and the two three-bit patterns in + * various SD specs (101, 010) aren't part of it ... + */ + if (qstatus & MMCST0_CRCWR) { + u32 temp = readb(host->base + DAVINCI_MMCDRSP); + + if (temp == 0x9f) + data->error = -ETIMEDOUT; + } + dev_err(mmc_dev(host->mmc), "data %s %s error\n", + (qstatus & MMCST0_CRCWR) ? "write" : + "read", (data->error == -ETIMEDOUT) ? + "timeout" : "CRC"); + + davinci_abort_data(host, data); + goto end_data; + } + + if (qstatus & MMCST0_TOUTRS) { + /* Command timeout */ + if (host->cmd) { + dev_err(mmc_dev(host->mmc), + "CMD%d timeout, status %x\n", + host->cmd->opcode, qstatus); + host->cmd->error = -ETIMEDOUT; + if (data) { + end_transfer = 1; + davinci_abort_data(host, data); + } else + end_command = 1; + } + goto end_cmd; + } + + if (qstatus & MMCST0_CRCRS) { + /* Command CRC error */ + dev_err(mmc_dev(host->mmc), "Command CRC error\n"); + if (host->cmd) { + host->cmd->error = -EILSEQ; + end_command = 1; + } + goto end_cmd; + } + } + /* handle FIFO first when using PIO for data. * bytes_left will decrease to zero as I/O progress and status will * read zero over iteration because this controller status @@ -922,6 +1055,11 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) qstatus |= status; } + if (qstatus & MMCST0_RSPDNE) { + /* End of command phase */ + end_command = (int) host->cmd; + } + if (qstatus & MMCST0_DATDNE) { /* All blocks sent/received, and CRC checks passed */ if (data != NULL) { @@ -939,73 +1077,10 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) } } - if (qstatus & MMCST0_TOUTRD) { - /* Read data timeout */ - data->error = -ETIMEDOUT; - end_transfer = 1; - - dev_dbg(mmc_dev(host->mmc), - "read data timeout, status %x\n", - qstatus); - - davinci_abort_data(host, data); - } - - if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) { - /* Data CRC error */ - data->error = -EILSEQ; - end_transfer = 1; - - /* NOTE: this controller uses CRCWR to report both CRC - * errors and timeouts (on writes). MMCDRSP values are - * only weakly documented, but 0x9f was clearly a timeout - * case and the two three-bit patterns in various SD specs - * (101, 010) aren't part of it ... - */ - if (qstatus & MMCST0_CRCWR) { - u32 temp = readb(host->base + DAVINCI_MMCDRSP); - - if (temp == 0x9f) - data->error = -ETIMEDOUT; - } - dev_dbg(mmc_dev(host->mmc), "data %s %s error\n", - (qstatus & MMCST0_CRCWR) ? "write" : "read", - (data->error == -ETIMEDOUT) ? "timeout" : "CRC"); - - davinci_abort_data(host, data); - } - - if (qstatus & MMCST0_TOUTRS) { - /* Command timeout */ - if (host->cmd) { - dev_dbg(mmc_dev(host->mmc), - "CMD%d timeout, status %x\n", - host->cmd->opcode, qstatus); - host->cmd->error = -ETIMEDOUT; - if (data) { - end_transfer = 1; - davinci_abort_data(host, data); - } else - end_command = 1; - } - } - - if (qstatus & MMCST0_CRCRS) { - /* Command CRC error */ - dev_dbg(mmc_dev(host->mmc), "Command CRC error\n"); - if (host->cmd) { - host->cmd->error = -EILSEQ; - end_command = 1; - } - } - - if (qstatus & MMCST0_RSPDNE) { - /* End of command phase */ - end_command = (int) host->cmd; - } - +end_cmd: if (end_command) mmc_davinci_cmd_done(host, host->cmd); +end_data: if (end_transfer) mmc_davinci_xfer_done(host, data); return IRQ_HANDLED; @@ -1031,11 +1106,34 @@ static int mmc_davinci_get_ro(struct mmc_host *mmc) return config->get_ro(pdev->id); } +static void mmc_davinci_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct mmc_davinci_host *host = mmc_priv(mmc); + + if (enable) { + if (!((readl(host->base + DAVINCI_SDIOST0)) + & SDIOST0_DAT1_HI)) { + writel(SDIOIST_IOINT, + host->base + DAVINCI_SDIOIST); + mmc_signal_sdio_irq(host->mmc); + }else { + host->sdio_int = 1; + writel(readl(host->base + DAVINCI_SDIOIEN) | + SDIOIEN_IOINTEN, host->base + DAVINCI_SDIOIEN); + } + } else { + host->sdio_int = 0; + writel(readl(host->base + DAVINCI_SDIOIEN) & ~SDIOIEN_IOINTEN, + host->base + DAVINCI_SDIOIEN); + } + +} static struct mmc_host_ops mmc_davinci_ops = { .request = mmc_davinci_request, .set_ios = mmc_davinci_set_ios, .get_cd = mmc_davinci_get_cd, .get_ro = mmc_davinci_get_ro, + .enable_sdio_irq = mmc_davinci_enable_sdio_irq, }; /*----------------------------------------------------------------------*/ @@ -1072,15 +1170,14 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) struct mmc_davinci_host *host = NULL; struct mmc_host *mmc = NULL; struct resource *r, *mem = NULL; - int ret = 0, irq = 0; + int ret = 0; size_t mem_size; /* REVISIT: when we're fully converted, fail if pdata is NULL */ ret = -ENODEV; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!r || irq == NO_IRQ) + if (!r) goto out; ret = -EBUSY; @@ -1097,6 +1194,16 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; /* Important */ + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!r) + goto out; + host->mmc_irq = r->start; + + r = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!r) + goto out; + host->sdio_irq = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!r) goto out; @@ -1124,7 +1231,6 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) init_mmcsd_host(host); host->use_dma = use_dma; - host->irq = irq; if (host->use_dma && davinci_acquire_dma_channels(host) != 0) host->use_dma = 0; @@ -1173,10 +1279,22 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) if (ret < 0) goto out; - ret = request_irq(irq, mmc_davinci_irq, 0, mmc_hostname(mmc), host); + ret = request_irq(host->mmc_irq, mmc_davinci_irq, 0, + mmc_hostname(mmc), host); if (ret) goto out; + if (host->sdio_irq > 0) { + ret = request_irq(host->sdio_irq, + mmc_davinci_sdio_irq, 0, + DM355_SDIO_IRQ(pdev->id), host); + if (ret == 0) { + mmc->caps |= MMC_CAP_SDIO_IRQ; + host->sdio_int = 0; + } else + goto out; + } + rename_region(mem, mmc_hostname(mmc)); dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n", @@ -1215,10 +1333,19 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); if (host) { + + writel((readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKEN), + host->base + DAVINCI_MMCCLK); + mmc_remove_host(host->mmc); - free_irq(host->irq, host); - davinci_release_dma_channels(host); + free_irq(host->mmc_irq, host); + + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) + free_irq(host->sdio_irq, host); + + if (host->use_dma) + davinci_release_dma_channels(host); clk_disable(host->clk); clk_put(host->clk);