From patchwork Thu Aug 6 15:05:47 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Phaneendra kumar X-Patchwork-Id: 39627 Received: from bear.ext.ti.com (bear.ext.ti.com [192.94.94.41]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n76F7M9S006861 for ; Thu, 6 Aug 2009 15:07:23 GMT Received: from dlep34.itg.ti.com ([157.170.170.115]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id n76F7HuM028808 for ; Thu, 6 Aug 2009 10:07:22 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep34.itg.ti.com (8.13.7/8.13.7) with ESMTP id n76F7Hk9007728 for ; Thu, 6 Aug 2009 10:07:17 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id 09C9880682 for ; Thu, 6 Aug 2009 10:07:11 -0500 (CDT) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dflp53.itg.ti.com (dflp53.itg.ti.com [128.247.5.6]) by linux.omap.com (Postfix) with ESMTP id 0160480626 for ; Thu, 6 Aug 2009 10:06:36 -0500 (CDT) Received: from neches.ext.ti.com (localhost [127.0.0.1]) by dflp53.itg.ti.com (8.13.8/8.13.8) with ESMTP id n76F6a25008902 for ; Thu, 6 Aug 2009 10:06:36 -0500 (CDT) Received: from mail71-tx2-R.bigfish.com (mail-tx2.bigfish.com [65.55.88.112]) by neches.ext.ti.com (8.13.7/8.13.7) with ESMTP id n76F6V4q022985 for ; Thu, 6 Aug 2009 10:06:36 -0500 Received: from mail71-tx2 (localhost.localdomain [127.0.0.1]) by mail71-tx2-R.bigfish.com (Postfix) with ESMTP id 5E28DEE80ED for ; Thu, 6 Aug 2009 15:06:31 +0000 (UTC) X-SpamScore: 20 X-BigFish: vps20(zcb8kzzz1202hzz1497iz30ih21ch6bh66h) X-Spam-TCS-SCL: 5:0 X-FB-SS: 5,5, X-MS-Exchange-Organization-Antispam-Report: OrigIP: 64.71.235.11; Service: EHS Received: by mail71-tx2 (MessageSwitch) id 1249571171198638_17237; Thu, 6 Aug 2009 15:06:11 +0000 (UCT) Received: from mail10.myhsphere.biz (smtp13.myhsphere.biz [64.71.235.11]) by mail71-tx2.bigfish.com (Postfix) with ESMTP id 82A797D0062 for ; Thu, 6 Aug 2009 15:06:10 +0000 (UTC) Received: (qmail 9873 invoked by uid 399); 6 Aug 2009 15:06:09 -0000 Received: from mail-pz0-f196.google.com (phani@embwise.com@209.85.222.196) by mail10.myhsphere.biz with ESMTPAM; 6 Aug 2009 15:06:09 -0000 X-Originating-IP: 209.85.222.196 X-Sender: phani@embwise.com Received: by pzk34 with SMTP id 34so956624pzk.4 for ; Thu, 06 Aug 2009 08:06:07 -0700 (PDT) MIME-Version: 1.0 Received: by 10.114.74.3 with SMTP id w3mr12557810waa.88.1249571167127; Thu, 06 Aug 2009 08:06:07 -0700 (PDT) From: PhaneendraKumar A Date: Thu, 6 Aug 2009 20:35:47 +0530 Message-ID: <54d94f230908060805v2bc8cefai81f567116209b7dc@mail.gmail.com> To: "davinci-linux-open-source@linux.davincidsp.com" Subject: Subject: [PATCH] Davinci:DM3xx: SDIO support for DM3xx X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.4 Precedence: list List-Id: davinci-linux-open-source.linux.davincidsp.com List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: davinci-linux-open-source-bounces+patchwork-davinci=patchwork.kernel.org@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces+patchwork-davinci=patchwork.kernel.org@linux.davincidsp.com 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); 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 #include #include +#include #include #include @@ -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