Hi all, This patch will add SDIO support to the DM355 host controller driver.
I have verified this on DM355 EVM board in both DMA and PIO modes. And i have used open source libertas driver for verifying the SDIO functionality. Signed-off-by: Phaneendra Kumar <[email protected]> ----- diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 8907b72..0744059 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -31,6 +31,8 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/mmc/mmc.h> +#include <linux/timer.h> +#include <linux/mmc/card.h> #include <mach/mmc.h> #include <mach/edma.h> @@ -65,8 +67,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 */ @@ -133,6 +135,23 @@ /* MMCSD Init clock in Hz in opendrain mode */ #define MMCSD_INIT_CLOCK 200000 +/* DAVINCI_SDIOCTL definitions */ +#define SDIOCTL_RDWTRQ_SET (1 << 0) +#define SDIOCTL_RDWTCR_SET (1 << 0) + +/* DAVINCI_SDIOST0 definitions */ +#define SDIOST0_DAT1_HI (1 << 0) +#define SDIOST0_INTPRD (1 << 1) +#define SDIOST0_RDWTST (1 << 2) + +/* DAVINCI_SDIOIEN definitions */ +#define SDIOIEN_IOINTEN (1 << 0) +#define SDIOIEN_RWSEN (1 << 1) + +/* DAVINCI_SDIOIST definitions */ +#define SDIOIST_IOINT (1 << 0) +#define SDIOIST_RWS (1 << 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 @@ -181,6 +200,10 @@ struct mmc_davinci_host { u32 rxdma, txdma; bool use_dma; bool do_dma; + struct timer_list sdio_timer; + u32 sdioInt; + /* For sdio irq enabling and disabling */ + spinlock_t sdio_lock; /* Scatterlist DMA uses one or more parameter RAM entries: * the main one (associated with rxdma or txdma) plus zero or @@ -202,6 +225,41 @@ struct mmc_davinci_host { unsigned ns_in_one_cycle; }; +static void mmc_enable_sdio_irq(struct mmc_host *mmc, int enable); + +static inline void davinci_sdio_intr_chck(struct mmc_davinci_host *host) +{ + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) { + if (!((readl(host->base + DAVINCI_SDIOST0)) & SDIOST0_DAT1_HI) + && host->sdioInt) { + writel(SDIOIST_IOINT, host->base + DAVINCI_SDIOIST); + mmc_signal_sdio_irq(host->mmc); + } + } +} + +static inline void davinci_cmd_dat_reset(struct mmc_davinci_host *host) +{ + u32 temp = 0; + + if (host->mmc->card) { + if (mmc_card_sdio(host->mmc->card)) { + temp = readl(host->base + DAVINCI_MMCCTL); + writel(temp | MMCCTL_CMDRST | MMCCTL_DATRST, + host->base + DAVINCI_MMCCTL); + + temp &= ~(MMCCTL_CMDRST | MMCCTL_DATRST); + writel(temp, host->base + DAVINCI_MMCCTL); + } + } +} + +static void davinci_sdio_timer(unsigned long data) +{ + struct mmc_davinci_host *host = (struct mmc_davinci_host *)data; + + davinci_sdio_intr_chck(host); +} /* PIO only */ static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host) @@ -387,6 +445,15 @@ 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)) { + printk(KERN_ERR "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 +731,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 @@ -706,6 +781,8 @@ static void mmc_davinci_request(struct mmc_host *mmc, struct mmc_request *req) return; } + davinci_cmd_dat_reset(host); + host->do_dma = 0; mmc_davinci_prepare_data(host, req); mmc_davinci_start_command(host, req->cmd); @@ -826,12 +903,9 @@ 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); - dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, (data->flags & MMC_DATA_WRITE) ? DMA_TO_DEVICE @@ -839,11 +913,18 @@ 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; + + davinci_cmd_dat_reset(host); + if (!data->stop || (host->cmd && host->cmd->error)) { mmc_request_done(host->mmc, data->mrq); writel(0, host->base + DAVINCI_MMCIM); } else mmc_davinci_start_command(host, data->stop); + + davinci_sdio_intr_chck(host); } static void mmc_davinci_cmd_done(struct mmc_davinci_host *host, @@ -870,6 +951,7 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host *host, mmc_request_done(host->mmc, cmd->mrq); writel(0, host->base + DAVINCI_MMCIM); } + davinci_sdio_intr_chck(host); } static void @@ -895,6 +977,17 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) int end_transfer = 0; struct mmc_data *data = host->data; + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) { + 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); + } + } + if (host->cmd == NULL && host->data == NULL) { status = readl(host->base + DAVINCI_MMCST0); dev_dbg(mmc_dev(host->mmc), @@ -904,6 +997,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) return IRQ_NONE; } + davinci_sdio_intr_chck(host); status = readl(host->base + DAVINCI_MMCST0); qstatus = status; @@ -1031,11 +1125,41 @@ static int mmc_davinci_get_ro(struct mmc_host *mmc) return config->get_ro(pdev->id); } +static void mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct mmc_davinci_host *host = mmc_priv(mmc); + unsigned long flags; + + spin_lock_irqsave(&host->sdio_lock, flags); + + 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->sdioInt = 1; + mod_timer(&host->sdio_timer, jiffies + (HZ/100)); + writel(readl(host->base + DAVINCI_SDIOIEN) | + SDIOIEN_IOINTEN, host->base + DAVINCI_SDIOIEN); + } + } else { + host->sdioInt = 0; + del_timer(&host->sdio_timer); + writel(readl(host->base + DAVINCI_SDIOIEN) & ~SDIOIEN_IOINTEN, + host->base + DAVINCI_SDIOIEN); + } + + spin_unlock_irqrestore(&host->sdio_lock, flags); +} + 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_enable_sdio_irq, }; /*----------------------------------------------------------------------*/ @@ -1132,6 +1256,12 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) /* REVISIT: someday, support IRQ-driven card detection. */ mmc->caps |= MMC_CAP_NEEDS_POLL; + mmc->caps |= MMC_CAP_SDIO_IRQ; + spin_lock_init(&host->sdio_lock); + setup_timer(&host->sdio_timer, davinci_sdio_timer, + (unsigned long)host); + host->sdioInt = 0; + if (!pdata || pdata->wires == 4 || pdata->wires == 0) mmc->caps |= MMC_CAP_4_BIT_DATA; _______________________________________________ Davinci-linux-open-source mailing list [email protected] http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
