Added SDIO Support for Davinci. Tested on DM355 EVM with Marvell SD8385 and SD8686 cards using Libertas SDIO driver. This requires additional patches for Libertas SDIO interface as indicated in the linux-wireless mailing list and as referenced in the following links
http://marc.info/?l=linux-wireless&m=127136330220026&w=2 http://marc.info/?l=linux-wireless&m=127136362020814&w=2 Signed-off-by: Alagu Sankar <alagusan...@embwise.com> --- drivers/mmc/host/davinci_mmc.c | 98 +++++++++++++++++++++++++++++++++++++-- 1 files changed, 93 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index e4d1591..4afa6bf 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -66,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 */ @@ -135,6 +135,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 MAX_NR_SG segments. MMC_BLOCK_BOUNCE kicks in only @@ -147,6 +164,8 @@ #define MAX_NR_SG 16 +#define DAVINCI_SDIO_IRQ(dev_id) (((dev_id) == 0) ? "sdio0" : "sdio1") + static unsigned rw_threshold = 32; module_param(rw_threshold, uint, S_IRUGO); MODULE_PARM_DESC(rw_threshold, @@ -164,7 +183,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 @@ -184,6 +203,7 @@ struct mmc_davinci_host { u32 rxdma, txdma; bool use_dma; bool do_dma; + bool sdio_int; /* Scatterlist DMA uses one or more parameter RAM entries: * the main one (associated with rxdma or txdma) plus zero or @@ -866,6 +886,19 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data) { host->data = NULL; + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) { + /* SDIO Interrupt Detection work-around as suggested by + * Davinci Errata (TMS320DM355 Silicon Revision 1.1 Errata + * 2.1.5) + * Signal SDIO interrupt only if it is enabled by core + */ + 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) { davinci_abort_dma(host); @@ -932,6 +965,22 @@ davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data) mmc_davinci_reset_ctrl(host, 0); } +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; @@ -1076,11 +1125,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, }; /*----------------------------------------------------------------------*/ @@ -1209,7 +1281,8 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) host->nr_sg = MAX_NR_SG; host->use_dma = use_dma; - host->irq = irq; + host->mmc_irq = irq; + host->sdio_irq = platform_get_irq(pdev, 1); if (host->use_dma && davinci_acquire_dma_channels(host) != 0) host->use_dma = 0; @@ -1272,6 +1345,19 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) if (ret) goto out; + /* Failures in SDIO IRQ registration are ignored as the driver + * can still work in polled mode. + */ + if (host->sdio_irq != NO_IRQ) { + ret = request_irq(host->sdio_irq, + mmc_davinci_sdio_irq, 0, + DAVINCI_SDIO_IRQ(pdev->id), host); + if (ret == 0) { + mmc->caps |= MMC_CAP_SDIO_IRQ; + host->sdio_int = 0; + } + } + rename_region(mem, mmc_hostname(mmc)); dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n", @@ -1315,7 +1401,9 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev) mmc_davinci_cpufreq_deregister(host); mmc_remove_host(host->mmc); - free_irq(host->irq, host); + free_irq(host->mmc_irq, host); + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) + free_irq(host->sdio_irq, host); davinci_release_dma_channels(host); -- 1.6.0.6 _______________________________________________ Davinci-linux-open-source mailing list Davinci-linux-open-source@linux.davincidsp.com http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source