[PATCH] Davinci SDIO Support
From: Alagu Sankar alagusan...@embwise.com Added SDIO Support for Davinci Platforms. This is tested with DM355 and DM365 EVM platforms using Libertas driver with SD8686 and SD8688 SDIO WiFi cards. There is a hack to support Libertas firmware download. This hack may not be required for other SDIO cards. Signed-off-by: alagusan...@embwise.com --- drivers/mmc/host/davinci_mmc.c | 131 1 files changed, 120 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 933cd42..2836ad1 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -32,6 +32,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 @@ -66,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 */ @@ -135,6 +136,23 @@ /* MMCSD Init clock in Hz in opendrain mode */ #define MMCSD_INIT_CLOCK 20 +/* DAVINCI_SDIOCTL definitions */ +#define SDIOCTL_RDWTRQ_SETBIT(0) +#define SDIOCTL_RDWTCR_SETBIT(1) + +/* DAVINCI_SDIOST0 definitions */ +#define SDIOST0_DAT1_HI BIT(0) +#define SDIOST0_INTPRDBIT(1) +#define SDIOST0_RDWTSTBIT(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 @@ -147,6 +165,8 @@ #define 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 +184,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 +204,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 @@ -670,6 +691,24 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) host-buffer = NULL; host-bytes_left = data-blocks * data-blksz; +/* + * Hack for overcoming the 2ms delay required by Libertas helper + * firmware download. Without this delay the primary firmware load fails + * without an error. Not having the delay does not result in host + * controller error (or) helper firmware download error. But will result + * in primary firmware load error. + * + * May not be required for usage with other SDIO client drivers and + * should be removed after identifying the root cause in consultation + * with the libertas developers. + */ + if (host-mmc-card) { + if (mmc_card_sdio(host-mmc-card)) { + if ((data-blksz == 64)) + mdelay(2); + } + } + /* 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 @@ -862,6 +901,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); @@ -928,6 +980,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
[PATCH] Davinci SDIO Support
From: Alagu Sankar alagusan...@embwise.com Added SDIO Support for Davinci Platforms. This is tested with DM355 and DM365 EVM platforms using Libertas driver with SD8686 and SD8688 SDIO WiFi cards. There is a hack to support Libertas firmware download. This hack may not be required for other SDIO cards. Signed-off-by: alagusan...@embwise.com --- drivers/mmc/host/davinci_mmc.c | 131 1 files changed, 120 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 933cd42..2836ad1 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -32,6 +32,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 @@ -66,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 */ @@ -135,6 +136,23 @@ /* MMCSD Init clock in Hz in opendrain mode */ #define MMCSD_INIT_CLOCK 20 +/* DAVINCI_SDIOCTL definitions */ +#define SDIOCTL_RDWTRQ_SETBIT(0) +#define SDIOCTL_RDWTCR_SETBIT(1) + +/* DAVINCI_SDIOST0 definitions */ +#define SDIOST0_DAT1_HI BIT(0) +#define SDIOST0_INTPRDBIT(1) +#define SDIOST0_RDWTSTBIT(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 @@ -147,6 +165,8 @@ #define 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 +184,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 +204,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 @@ -670,6 +691,24 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) host-buffer = NULL; host-bytes_left = data-blocks * data-blksz; +/* + * Hack for overcoming the 2ms delay required by Libertas helper + * firmware download. Without this delay the primary firmware load fails + * without an error. Not having the delay does not result in host + * controller error (or) helper firmware download error. But will result + * in primary firmware load error. + * + * May not be required for usage with other SDIO client drivers and + * should be removed after identifying the root cause in consultation + * with the libertas developers. + */ + if (host-mmc-card) { + if (mmc_card_sdio(host-mmc-card)) { + if ((data-blksz == 64)) + mdelay(2); + } + } + /* 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 @@ -862,6 +901,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); @@ -928,6 +980,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