/* * SdioDrv.c * * Copyright (C) 2009 Texas Instruments, Inc. - http://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation version 2. * * This program is distributed "as is" WITHOUT ANY WARRANTY of any * kind, whether express or implied; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31)) #include #include #include #include #include #include #define IO_ADDRESS(pa) OMAP2_L4_IO_ADDRESS(pa) #else #include #include #include #include #include #include #endif typedef void *TI_HANDLE; #include "host_platform.h" #include "SdioDrvDbg.h" #include "SdioDrv.h" /* #define TI_SDIO_DEBUG */ #ifndef CONFIG_MMC_EMBEDDED_SDIO #define SDIOWQ_NAME "sdio_wq" /* * HSMMC Address and DMA Settings */ static unsigned long TIWLAN_MMC_CONTROLLER = 2; /* MMC3 */ static unsigned long TIWLAN_MMC_CONTROLLER_BASE_ADDR = OMAP_HSMMC3_BASE; #define TIWLAN_MMC_CONTROLLER_BASE_SIZE 512 #define TIWLAN_MMC_MAX_DMA 8192 static unsigned long TIWLAN_MMC_DMA_TX = OMAP34XX_DMA_MMC3_TX; static unsigned long TIWLAN_MMC_DMA_RX = OMAP34XX_DMA_MMC3_RX; static unsigned long OMAP_MMC_IRQ = INT_MMC3_IRQ; #define OMAP_MMC_MASTER_CLOCK 96000000 /* * HSMMC Host Controller Registers */ #define OMAP_HSMMC_SYSCONFIG 0x0010 #define OMAP_HSMMC_SYSSTATUS 0x0014 #define OMAP_HSMMC_CSRE 0x0024 #define OMAP_HSMMC_SYSTEST 0x0028 #define OMAP_HSMMC_CON 0x002C #define OMAP_HSMMC_BLK 0x0104 #define OMAP_HSMMC_ARG 0x0108 #define OMAP_HSMMC_CMD 0x010C #define OMAP_HSMMC_RSP10 0x0110 #define OMAP_HSMMC_RSP32 0x0114 #define OMAP_HSMMC_RSP54 0x0118 #define OMAP_HSMMC_RSP76 0x011C #define OMAP_HSMMC_DATA 0x0120 #define OMAP_HSMMC_PSTATE 0x0124 #define OMAP_HSMMC_HCTL 0x0128 #define OMAP_HSMMC_SYSCTL 0x012C #define OMAP_HSMMC_STAT 0x0130 #define OMAP_HSMMC_IE 0x0134 #define OMAP_HSMMC_ISE 0x0138 #define OMAP_HSMMC_AC12 0x013C #define OMAP_HSMMC_CAPA 0x0140 #define OMAP_HSMMC_CUR_CAPA 0x0148 #define OMAP_HSMMC_REV 0x01FC #define VS18 (1 << 26) #define VS30 (1 << 25) #define SRA (1 << 24) #define SDVS18 (0x5 << 9) #define SDVS30 (0x6 << 9) #define SDVSCLR 0xFFFFF1FF #define SDVSDET 0x00000400 #define SIDLE_MODE (0x2 << 3) #define AUTOIDLE 0x1 #define SDBP (1 << 8) #define DTO 0xE #define ICE 0x1 #define ICS 0x2 #define CEN (1 << 2) #define CLKD_MASK 0x0000FFC0 #define IE_EN_MASK 0x317F0137 #define INIT_STREAM (1 << 1) #define DP_SELECT (1 << 21) #define DDIR (1 << 4) #define DMA_EN 0x1 #define MSBS (1 << 5) #define BCE (1 << 1) #define ONE_BIT (~(0x2)) #define EIGHT_BIT (~(0x20)) #define CC 0x1 #define TC 0x02 #define OD 0x1 #define BRW 0x400 #define BRR 0x800 #define BRE (1 << 11) #define BWE (1 << 10) #define SBGR (1 << 16) #define CT (1 << 17) #define SDIO_READ (1 << 31) #define SDIO_BLKMODE (1 << 27) #define OMAP_HSMMC_ERR (1 << 15) /* Any error */ #define OMAP_HSMMC_CMD_TIMEOUT (1 << 16) /* Com mand response time-out */ #define OMAP_HSMMC_DATA_TIMEOUT (1 << 20) /* Data response time-out */ #define OMAP_HSMMC_CMD_CRC (1 << 17) /* Command CRC error */ #define OMAP_HSMMC_DATA_CRC (1 << 21) /* Date CRC error */ #define OMAP_HSMMC_CARD_ERR (1 << 28) /* Card ERR */ #define OMAP_HSMMC_STAT_CLEAR 0xFFFFFFFF #define INIT_STREAM_CMD 0x00000000 #define INT_CLEAR 0x00000000 #define BLK_CLEAR 0x00000000 /* SCM CONTROL_DEVCONF1 MMC1 overwrite but */ #define MMC1_ACTIVE_OVERWRITE (1 << 31) #define sdio_blkmode_regaddr 0x2000 #define sdio_blkmode_mask 0xFF00 #define IO_RW_DIRECT_MASK 0xF000FF00 #define IO_RW_DIRECT_ARG_MASK 0x80001A00 #define RMASK (MMC_RSP_MASK | MMC_RSP_CRC) #define MMC_TIMEOUT_MS 100 /*on the new 2430 it was 20, i changed back to 100*//* obc */ #define MMCA_VSN_4 4 #define VMMC1_DEV_GRP 0x27 #define P1_DEV_GRP 0x20 #define VMMC1_DEDICATED 0x2A #define VSEL_3V 0x02 #define VSEL_18V 0x00 #define PBIAS_3V 0x03 #define PBIAS_18V 0x02 #define PBIAS_LITE 0x04A0 #define PBIAS_CLR 0x00 #define OMAP_MMC_REGS_BASE IO_ADDRESS(TIWLAN_MMC_CONTROLLER_BASE_ADDR) /* * MMC Host controller read/write API's. */ #define OMAP_HSMMC_READ_OFFSET(offset) (__raw_readl((OMAP_MMC_REGS_BASE) + (offset))) #define OMAP_HSMMC_READ(reg) (__raw_readl((OMAP_MMC_REGS_BASE) + OMAP_HSMMC_##reg)) #define OMAP_HSMMC_WRITE(reg, val) (__raw_writel((val), (OMAP_MMC_REGS_BASE) + OMAP_HSMMC_##reg)) #define OMAP_HSMMC_SEND_COMMAND(cmd, arg) do \ { \ OMAP_HSMMC_WRITE(ARG, arg); \ OMAP_HSMMC_WRITE(CMD, cmd); \ } while (0) #define OMAP_HSMMC_CMD52_WRITE ((SD_IO_RW_DIRECT << 24) | (OMAP_HSMMC_CMD_SHORT_RESPONSE << 16)) #define OMAP_HSMMC_CMD52_READ (((SD_IO_RW_DIRECT << 24) | (OMAP_HSMMC_CMD_SHORT_RESPONSE << 16)) | DDIR) #define OMAP_HSMMC_CMD53_WRITE (((SD_IO_RW_EXTENDED << 24) | (OMAP_HSMMC_CMD_SHORT_RESPONSE << 16)) | DP_SELECT) #define OMAP_HSMMC_CMD53_READ (((SD_IO_RW_EXTENDED << 24) | (OMAP_HSMMC_CMD_SHORT_RESPONSE << 16)) | DP_SELECT | DDIR) #define OMAP_HSMMC_CMD53_READ_DMA (OMAP_HSMMC_CMD53_READ | DMA_EN) #define OMAP_HSMMC_CMD53_WRITE_DMA (OMAP_HSMMC_CMD53_WRITE | DMA_EN) /* Macros to build commands 52 and 53 in format according to SDIO spec */ #define SDIO_CMD52_READ(v1,v2,v3,v4) (SDIO_RWFLAG(v1)|SDIO_FUNCN(v2)|SDIO_RAWFLAG(v3)| SDIO_ADDRREG(v4)) #define SDIO_CMD52_WRITE(v1,v2,v3,v4,v5) (SDIO_RWFLAG(v1)|SDIO_FUNCN(v2)|SDIO_RAWFLAG(v3)| SDIO_ADDRREG(v4)|(v5)) #define SDIO_CMD53_READ(v1,v2,v3,v4,v5,v6) (SDIO_RWFLAG(v1)|SDIO_FUNCN(v2)|SDIO_BLKM(v3)| SDIO_OPCODE(v4)|SDIO_ADDRREG(v5)|(v6&0x1ff)) #define SDIO_CMD53_WRITE(v1,v2,v3,v4,v5,v6) (SDIO_RWFLAG(v1)|SDIO_FUNCN(v2)|SDIO_BLKM(v3)| SDIO_OPCODE(v4)|SDIO_ADDRREG(v5)|(v6&0x1ff)) #define SDIODRV_MAX_LOOPS 50000 #define VMMC2_DEV_GRP 0x2B #define VMMC2_DEDICATED 0x2E #define VSEL_S2_18V 0x05 #define LDO_CLR 0x00 #define VSEL_S2_CLR 0x40 #define GPIO_0_BIT_POS 1 << 0 #define GPIO_1_BIT_POS 1 << 1 #define VSIM_DEV_GRP 0x37 #define VSIM_DEDICATED 0x3A #define TWL4030_MODULE_PM_RECIEVER 0x13 typedef struct OMAP3430_sdiodrv { struct clk *fclk, *iclk, *dbclk; int ifclks_enabled; spinlock_t clk_lock; int dma_tx_channel; int dma_rx_channel; int irq; void (*BusTxnCB)(void* BusTxnHandle, int status); void* BusTxnHandle; unsigned int uBlkSize; unsigned int uBlkSizeShift; char *dma_buffer; void *async_buffer; unsigned int async_length; int async_status; int (*wlanDrvIf_pm_resume)(void); int (*wlanDrvIf_pm_suspend)(void); struct device *dev; dma_addr_t dma_read_addr; size_t dma_read_size; dma_addr_t dma_write_addr; size_t dma_write_size; struct workqueue_struct *sdio_wq; /* Work Queue */ struct work_struct sdiodrv_work; } OMAP3430_sdiodrv_t; struct omap_hsmmc_regs { u32 hctl; u32 capa; u32 sysconfig; u32 ise; u32 ie; u32 con; u32 sysctl; }; static struct omap_hsmmc_regs hsmmc_ctx; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31)) static struct platform_device dummy_pdev = { .dev = { .bus = &platform_bus_type, }, }; #endif #define SDIO_DRIVER_NAME "TIWLAN_SDIO" module_param(g_sdio_debug_level, int, 0644); MODULE_PARM_DESC(g_sdio_debug_level, "debug level"); int g_sdio_debug_level = SDIO_DEBUGLEVEL_ERR; EXPORT_SYMBOL(g_sdio_debug_level); OMAP3430_sdiodrv_t g_drv; static int sdiodrv_dma_on = 0; static int sdiodrv_irq_requested = 0; static int sdiodrv_iclk_got = 0; static int sdiodrv_fclk_got = 0; int sdioDrv_clk_enable(void); void sdioDrv_clk_disable(void); static void sdioDrv_hsmmc_save_ctx(void); static void sdioDrv_hsmmc_restore_ctx(void); #ifndef TI_SDIO_STANDALONE void sdio_init( int sdcnum ) { if( sdcnum <= 0 ) return; TIWLAN_MMC_CONTROLLER = sdcnum - 1; if( sdcnum == 2 ) { TIWLAN_MMC_CONTROLLER_BASE_ADDR = OMAP_HSMMC2_BASE; TIWLAN_MMC_DMA_TX = OMAP24XX_DMA_MMC2_TX; TIWLAN_MMC_DMA_RX = OMAP24XX_DMA_MMC2_RX; OMAP_MMC_IRQ = INT_MMC2_IRQ; } else if( sdcnum == 3 ) { TIWLAN_MMC_CONTROLLER_BASE_ADDR = OMAP_HSMMC3_BASE; TIWLAN_MMC_DMA_TX = OMAP34XX_DMA_MMC3_TX; TIWLAN_MMC_DMA_RX = OMAP34XX_DMA_MMC3_RX; OMAP_MMC_IRQ = INT_MMC3_IRQ; } } #endif static void sdioDrv_hsmmc_save_ctx(void) { /* MMC : context save */ hsmmc_ctx.hctl = OMAP_HSMMC_READ(HCTL); hsmmc_ctx.capa = OMAP_HSMMC_READ(CAPA); hsmmc_ctx.sysconfig = OMAP_HSMMC_READ(SYSCONFIG); hsmmc_ctx.ise = OMAP_HSMMC_READ(ISE); hsmmc_ctx.ie = OMAP_HSMMC_READ(IE); hsmmc_ctx.con = OMAP_HSMMC_READ(CON); hsmmc_ctx.sysctl = OMAP_HSMMC_READ(SYSCTL); OMAP_HSMMC_WRITE(ISE, 0); OMAP_HSMMC_WRITE(IE, 0); } static void sdioDrv_hsmmc_restore_ctx(void) { /* MMC : context restore */ OMAP_HSMMC_WRITE(HCTL, hsmmc_ctx.hctl); OMAP_HSMMC_WRITE(CAPA, hsmmc_ctx.capa); OMAP_HSMMC_WRITE(SYSCONFIG, hsmmc_ctx.sysconfig); OMAP_HSMMC_WRITE(CON, hsmmc_ctx.con); OMAP_HSMMC_WRITE(ISE, hsmmc_ctx.ise); OMAP_HSMMC_WRITE(IE, hsmmc_ctx.ie); OMAP_HSMMC_WRITE(SYSCTL, hsmmc_ctx.sysctl); OMAP_HSMMC_WRITE(HCTL, OMAP_HSMMC_READ(HCTL) | SDBP); } void sdiodrv_task(struct work_struct *unused) { PDEBUG("sdiodrv_tasklet()\n"); if (g_drv.dma_read_addr != 0) { dma_unmap_single(g_drv.dev, g_drv.dma_read_addr, g_drv.dma_read_size, DMA_FROM_DEVICE); g_drv.dma_read_addr = 0; g_drv.dma_read_size = 0; } if (g_drv.dma_write_addr != 0) { dma_unmap_single(g_drv.dev, g_drv.dma_write_addr, g_drv.dma_write_size, DMA_TO_DEVICE); g_drv.dma_write_addr = 0; g_drv.dma_write_size = 0; } if (g_drv.async_buffer) { memcpy(g_drv.async_buffer, g_drv.dma_buffer, g_drv.async_length); g_drv.async_buffer = NULL; } if (g_drv.BusTxnCB != NULL) { g_drv.BusTxnCB(g_drv.BusTxnHandle, g_drv.async_status); } } irqreturn_t sdiodrv_irq(int irq, void *drv) { int status; PDEBUG("sdiodrv_irq()\n"); status = OMAP_HSMMC_READ(STAT); OMAP_HSMMC_WRITE(ISE, 0); g_drv.async_status = status & (OMAP_HSMMC_ERR); if (g_drv.async_status) { PERR("sdiodrv_irq: ERROR in STAT = 0x%x\n", status); } queue_work(g_drv.sdio_wq, &g_drv.sdiodrv_work); return IRQ_HANDLED; } void sdiodrv_dma_read_cb(int lch, u16 ch_status, void *data) { PDEBUG("sdiodrv_dma_read_cb() channel=%d status=0x%x\n", lch, (int)ch_status); g_drv.async_status = ch_status & (1 << 7); queue_work(g_drv.sdio_wq, &g_drv.sdiodrv_work); } void sdiodrv_dma_write_cb(int lch, u16 ch_status, void *data) { } int sdiodrv_dma_init(void) { int rc; rc = omap_request_dma(TIWLAN_MMC_DMA_TX, "SDIO WRITE", sdiodrv_dma_write_cb, &g_drv, &g_drv.dma_tx_channel); if (rc != 0) { PERR("sdiodrv_dma_init() omap_request_dma(TIWLAN_MMC_DMA_TX) FAILED\n"); goto out; } rc = omap_request_dma(TIWLAN_MMC_DMA_RX, "SDIO READ", sdiodrv_dma_read_cb, &g_drv, &g_drv.dma_rx_channel); if (rc != 0) { PERR("sdiodrv_dma_init() omap_request_dma(TIWLAN_MMC_DMA_RX) FAILED\n"); goto freetx; } omap_set_dma_src_params(g_drv.dma_rx_channel, 0, // src_port is only for OMAP1 OMAP_DMA_AMODE_CONSTANT, (TIWLAN_MMC_CONTROLLER_BASE_ADDR) + OMAP_HSMMC_DATA, 0, 0); omap_set_dma_dest_params(g_drv.dma_tx_channel, 0, // dest_port is only for OMAP1 OMAP_DMA_AMODE_CONSTANT, (TIWLAN_MMC_CONTROLLER_BASE_ADDR) + OMAP_HSMMC_DATA, 0, 0); if ((g_drv.dma_buffer = kmalloc(TIWLAN_MMC_MAX_DMA, GFP_ATOMIC|GFP_DMA)) == NULL) { rc = -ENOMEM; goto freerx; } return 0; freerx: omap_free_dma(g_drv.dma_rx_channel); freetx: omap_free_dma(g_drv.dma_tx_channel); out: return rc; } void sdiodrv_dma_shutdown(void) { omap_free_dma(g_drv.dma_tx_channel); omap_free_dma(g_drv.dma_rx_channel); if (g_drv.dma_buffer) { kfree(g_drv.dma_buffer); g_drv.dma_buffer = NULL; } } /* sdiodrv_dma_shutdown() */ static u32 sdiodrv_poll_status(u32 reg_offset, u32 stat, unsigned int msecs) { u32 status=0, loops=0; do { status = OMAP_HSMMC_READ_OFFSET(reg_offset); if(( status & stat)) { break; } } while (loops++ < SDIODRV_MAX_LOOPS); return status; } /* sdiodrv_poll_status */ void dumpreg(void) { printk(KERN_ERR "\n MMCHS_SYSCONFIG for mmc3 = %x ", omap_readl( 0x480AD010 )); printk(KERN_ERR "\n MMCHS_SYSSTATUS for mmc3 = %x ", omap_readl( 0x480AD014 )); printk(KERN_ERR "\n MMCHS_CSRE for mmc3 = %x ", omap_readl( 0x480AD024 )); printk(KERN_ERR "\n MMCHS_SYSTEST for mmc3 = %x ", omap_readl( 0x480AD028 )); printk(KERN_ERR "\n MMCHS_CON for mmc3 = %x ", omap_readl( 0x480AD02C )); printk(KERN_ERR "\n MMCHS_PWCNT for mmc3 = %x ", omap_readl( 0x480AD030 )); printk(KERN_ERR "\n MMCHS_BLK for mmc3 = %x ", omap_readl( 0x480AD104 )); printk(KERN_ERR "\n MMCHS_ARG for mmc3 = %x ", omap_readl( 0x480AD108 )); printk(KERN_ERR "\n MMCHS_CMD for mmc3 = %x ", omap_readl( 0x480AD10C )); printk(KERN_ERR "\n MMCHS_RSP10 for mmc3 = %x ", omap_readl( 0x480AD110 )); printk(KERN_ERR "\n MMCHS_RSP32 for mmc3 = %x ", omap_readl( 0x480AD114 )); printk(KERN_ERR "\n MMCHS_RSP54 for mmc3 = %x ", omap_readl( 0x480AD118 )); printk(KERN_ERR "\n MMCHS_RSP76 for mmc3 = %x ", omap_readl( 0x480AD11C )); printk(KERN_ERR "\n MMCHS_DATA for mmc3 = %x ", omap_readl( 0x480AD120 )); printk(KERN_ERR "\n MMCHS_PSTATE for mmc3 = %x ", omap_readl( 0x480AD124 )); printk(KERN_ERR "\n MMCHS_HCTL for mmc3 = %x ", omap_readl( 0x480AD128 )); printk(KERN_ERR "\n MMCHS_SYSCTL for mmc3 = %x ", omap_readl( 0x480AD12C )); printk(KERN_ERR "\n MMCHS_STAT for mmc3 = %x ", omap_readl( 0x480AD130 )); printk(KERN_ERR "\n MMCHS_IE for mmc3 = %x ", omap_readl( 0x480AD134 )); printk(KERN_ERR "\n MMCHS_ISE for mmc3 = %x ", omap_readl( 0x480AD138 )); printk(KERN_ERR "\n MMCHS_AC12 for mmc3 = %x ", omap_readl( 0x480AD13C )); printk(KERN_ERR "\n MMCHS_CAPA for mmc3 = %x ", omap_readl( 0x480AD140 )); printk(KERN_ERR "\n MMCHS_CUR_CAPA for mmc3 = %x ", omap_readl( 0x480AD148 )); } //cmd flow p. 3609 obc static int sdiodrv_send_command(u32 cmdreg, u32 cmdarg) { OMAP_HSMMC_WRITE(STAT, OMAP_HSMMC_STAT_CLEAR); OMAP_HSMMC_SEND_COMMAND(cmdreg, cmdarg); return sdiodrv_poll_status(OMAP_HSMMC_STAT, CC, MMC_TIMEOUT_MS); } /* sdiodrv_send_command() */ /* * Disable clock to the card */ static void OMAP3430_mmc_stop_clock(void) { OMAP_HSMMC_WRITE(SYSCTL, OMAP_HSMMC_READ(SYSCTL) & ~CEN); if ((OMAP_HSMMC_READ(SYSCTL) & CEN) != 0x0) { PERR("MMC clock not stoped, clock freq can not be altered\n"); } } /* OMAP3430_mmc_stop_clock */ /* * Reset the SD system */ int OMAP3430_mmc_reset(void) { int status, loops=0; //p. 3598 - need to set SOFTRESET to 0x1 0bc OMAP_HSMMC_WRITE(SYSCTL, OMAP_HSMMC_READ(SYSCTL) | SRA); while ((status = OMAP_HSMMC_READ(SYSCTL) & SRA) && loops++ < SDIODRV_MAX_LOOPS); if (status & SRA) { PERR("OMAP3430_mmc_reset() MMC reset FAILED!! status=0x%x\n",status); } return status; } /* OMAP3430_mmc_reset */ //p. 3611 static void OMAP3430_mmc_set_clock(unsigned int clock, OMAP3430_sdiodrv_t *host) { u16 dsor = 0; unsigned long regVal; int status; PDEBUG("OMAP3430_mmc_set_clock(%d)\n",clock); if (clock) { /* Enable MMC_SD_CLK */ dsor = OMAP_MMC_MASTER_CLOCK / clock; if (dsor < 1) { dsor = 1; } if (OMAP_MMC_MASTER_CLOCK / dsor > clock) { dsor++; } if (dsor > 250) { dsor = 250; } } OMAP3430_mmc_stop_clock(); regVal = OMAP_HSMMC_READ(SYSCTL); regVal = regVal & ~(CLKD_MASK);//p. 3652 regVal = regVal | (dsor << 6); regVal = regVal | (DTO << 16);//data timeout OMAP_HSMMC_WRITE(SYSCTL, regVal); OMAP_HSMMC_WRITE(SYSCTL, OMAP_HSMMC_READ(SYSCTL) | ICE);//internal clock enable. obc not mentioned in the spec /* * wait till the the clock is stable (ICS) bit is set */ status = sdiodrv_poll_status(OMAP_HSMMC_SYSCTL, ICS, MMC_TIMEOUT_MS); if(!(status & ICS)) { PERR("OMAP3430_mmc_set_clock() clock not stable!! status=0x%x\n",status); } /* * Enable clock to the card */ OMAP_HSMMC_WRITE(SYSCTL, OMAP_HSMMC_READ(SYSCTL) | CEN); } /* OMAP3430_mmc_set_clock() */ static void sdiodrv_free_resources(void) { if(g_drv.ifclks_enabled) { sdioDrv_clk_disable(); } if (sdiodrv_fclk_got) { clk_put(g_drv.fclk); sdiodrv_fclk_got = 0; } if (sdiodrv_iclk_got) { clk_put(g_drv.iclk); sdiodrv_iclk_got = 0; } if (sdiodrv_irq_requested) { free_irq(OMAP_MMC_IRQ, &g_drv); sdiodrv_irq_requested = 0; } if (sdiodrv_dma_on) { sdiodrv_dma_shutdown(); sdiodrv_dma_on = 0; } } int sdioDrv_InitHw(void) { return 0; } /* sdiodrv_init */ void sdiodrv_shutdown(void) { PDEBUG("entering %s()\n" , __FUNCTION__ ); sdiodrv_free_resources(); PDEBUG("exiting %s\n", __FUNCTION__); } /* sdiodrv_shutdown() */ static int sdiodrv_send_data_xfer_commad(u32 cmd, u32 cmdarg, int length, u32 buffer_enable_status, unsigned int bBlkMode) { int status; PDEBUG("%s() writing CMD 0x%x ARG 0x%x\n",__FUNCTION__, cmd, cmdarg); /* block mode */ if(bBlkMode) { /* * Bits 31:16 of BLK reg: NBLK Blocks count for current transfer. * in case of Block MOde the lenght is treated here as number of blocks * (and not as a length). * Bits 11:0 of BLK reg: BLEN Transfer Block Size. in case of block mode set that field to block size. */ OMAP_HSMMC_WRITE(BLK, (length << 16) | (g_drv.uBlkSize << 0)); /* * In CMD reg: * BCE: Block Count Enable * MSBS: Multi/Single block select */ cmd |= MSBS | BCE ; } else { OMAP_HSMMC_WRITE(BLK, length); } status = sdiodrv_send_command(cmd, cmdarg); if(!(status & CC)) { PERR("sdiodrv_send_data_xfer_commad() SDIO Command error! STAT = 0x%x\n", status); return 0; } PDEBUG("%s() length = %d(%dw) BLK = 0x%x\n", __FUNCTION__, length,((length + 3) >> 2), OMAP_HSMMC_READ(BLK)); return sdiodrv_poll_status(OMAP_HSMMC_PSTATE, buffer_enable_status, MMC_TIMEOUT_MS); } /* sdiodrv_send_data_xfer_commad() */ int sdiodrv_data_xfer_sync(u32 cmd, u32 cmdarg, void *data, int length, u32 buffer_enable_status) { u32 buf_start, buf_end, data32; int status; status = sdiodrv_send_data_xfer_commad(cmd, cmdarg, length, buffer_enable_status, 0); if(!(status & buffer_enable_status)) { PERR("sdiodrv_data_xfer_sync() buffer disabled! length = %d BLK = 0x%x PSTATE = 0x%x\n", length, OMAP_HSMMC_READ(BLK), status); return -1; } buf_end = (u32)data+(u32)length; //obc need to check BRE/BWE every time, see p. 3605 /* * Read loop */ if (buffer_enable_status == BRE) { if (((u32)data & 3) == 0) /* 4 bytes aligned */ { for (buf_start = (u32)data; (u32)data < buf_end; data += sizeof(unsigned long)) { *((unsigned long*)(data)) = OMAP_HSMMC_READ(DATA); } } else /* 2 bytes aligned */ { for (buf_start = (u32)data; (u32)data < buf_end; data += sizeof(unsigned long)) { data32 = OMAP_HSMMC_READ(DATA); *((unsigned short *)data) = (unsigned short)data32; *((unsigned short *)data + 1) = (unsigned short)(data32 >> 16); } } } /* * Write loop */ else { if (((u32)data & 3) == 0) /* 4 bytes aligned */ { for (buf_start = (u32)data; (u32)data < buf_end; data += sizeof(unsigned long)) { OMAP_HSMMC_WRITE(DATA,*((unsigned long*)(data))); } } else /* 2 bytes aligned */ { for (buf_start = (u32)data; (u32)data < buf_end; data += sizeof(unsigned long)) { OMAP_HSMMC_WRITE(DATA,*((unsigned short*)data) | *((unsigned short*)data+1) << 16 ); } } } status = sdiodrv_poll_status(OMAP_HSMMC_STAT, TC, MMC_TIMEOUT_MS); if(!(status & TC)) { PERR("sdiodrv_data_xfer_sync() transfer error! STAT = 0x%x\n", status); return -1; } return 0; } /* sdiodrv_data_xfer_sync() */ int sdioDrv_ConnectBus (void * fCbFunc, void * hCbArg, unsigned int uBlkSizeShift, unsigned int uSdioThreadPriority, unsigned char **pTxDmaSrcAddr) { g_drv.BusTxnCB = fCbFunc; g_drv.BusTxnHandle = hCbArg; g_drv.uBlkSizeShift = uBlkSizeShift; g_drv.uBlkSize = 1 << uBlkSizeShift; INIT_WORK(&g_drv.sdiodrv_work, sdiodrv_task); /* Provide the DMA buffer address to the upper layer so it will use it as the transactions host buffer. */ if (pTxDmaSrcAddr) { *pTxDmaSrcAddr = g_drv.dma_buffer; } return sdioDrv_InitHw (); } int sdioDrv_DisconnectBus (void) { sdioDrv_clk_disable(); /* To process Stop command properly */ return 0; } //p.3609 cmd flow int sdioDrv_ExecuteCmd (unsigned int uCmd, unsigned int uArg, unsigned int uRespType, void * pResponse, unsigned int uLen) { unsigned int uCmdReg = 0; unsigned int uStatus = 0; unsigned int uResponse = 0; PDEBUG("sdioDrv_ExecuteCmd() starting cmd %02x arg %08x\n", (int)uCmd, (int)uArg); sdioDrv_clk_enable(); /* To make sure we have clocks enable */ uCmdReg = (uCmd << 24) | (uRespType << 16) ; uStatus = sdiodrv_send_command(uCmdReg, uArg); if (!(uStatus & CC)) { PERR("sdioDrv_ExecuteCmd() SDIO Command error status = 0x%x\n", uStatus); return -1; } if ((uLen > 0) && (uLen <= 4))/*obc - Len > 4 ? shouldn't read anything ? */ { uResponse = OMAP_HSMMC_READ(RSP10); memcpy (pResponse, (char *)&uResponse, uLen); PDEBUG("sdioDrv_ExecuteCmd() response = 0x%x\n", uResponse); } return 0; } /*--------------------------------------------------------------------------------------*/ int sdioDrv_ReadSync (unsigned int uFunc, unsigned int uHwAddr, void * pData, unsigned int uLen, unsigned int bIncAddr, unsigned int bMore) { unsigned int uCmdArg; int iStatus; // printk(KERN_INFO "in sdioDrv_ReadSync\n"); uCmdArg = SDIO_CMD53_READ(0, uFunc, 0, bIncAddr, uHwAddr, uLen); iStatus = sdiodrv_data_xfer_sync(OMAP_HSMMC_CMD53_READ, uCmdArg, pData, uLen, BRE); if (iStatus != 0) { PERR("sdioDrv_ReadSync() FAILED!!\n"); } #ifdef TI_SDIO_DEBUG if (uLen == 1) printk(KERN_INFO "R53: [0x%x](%u) = 0x%x\n", uHwAddr, uLen, (unsigned)(*(char *)pData)); else if (uLen == 2) printk(KERN_INFO "R53: [0x%x](%u) = 0x%x\n", uHwAddr, uLen, (unsigned)(*(short *)pData)); else if (uLen == 4) printk(KERN_INFO "R53: [0x%x](%u) = 0x%x\n", uHwAddr, uLen, (unsigned)(*(long *)pData)); else printk(KERN_INFO "R53: [0x%x](%u)\n", uHwAddr, uLen); #endif return iStatus; } /*--------------------------------------------------------------------------------------*/ int sdioDrv_ReadAsync (unsigned int uFunc, unsigned int uHwAddr, void * pData, unsigned int uLen, unsigned int bBlkMode, unsigned int bIncAddr, unsigned int bMore) { int iStatus; unsigned int uCmdArg; unsigned int uNumBlks; unsigned int uDmaBlockCount; unsigned int uNumOfElem; void *dma_buffer; dma_addr_t dma_bus_address; #ifdef TI_SDIO_DEBUG printk(KERN_INFO "R53: [0x%x](%u) F[%d]\n", uHwAddr, uLen, uFunc); #endif //printk(KERN_INFO "in sdioDrv_ReadAsync\n"); if (bBlkMode) { /* For block mode use number of blocks instead of length in bytes */ uNumBlks = uLen >> g_drv.uBlkSizeShift; uDmaBlockCount = uNumBlks; /* due to the DMA config to 32Bit per element (OMAP_DMA_DATA_TYPE_S32) the division is by 4 */ uNumOfElem = g_drv.uBlkSize >> 2; } else { uNumBlks = uLen; uDmaBlockCount = 1; uNumOfElem = (uLen + 3) >> 2; } if (((u32)pData & 3) == 0) /* 4 bytes aligned */ { dma_buffer = pData; } else /* 2 bytes aligned */ { dma_buffer = g_drv.dma_buffer; g_drv.async_buffer = pData; g_drv.async_length = uLen; } uCmdArg = SDIO_CMD53_READ(0, uFunc, bBlkMode, bIncAddr, uHwAddr, uNumBlks); iStatus = sdiodrv_send_data_xfer_commad(OMAP_HSMMC_CMD53_READ_DMA, uCmdArg, uNumBlks, BRE, bBlkMode); if (!(iStatus & BRE)) { PERR("sdioDrv_ReadAsync() buffer disabled! length = %d BLK = 0x%x PSTATE = 0x%x, BlkMode = %d\n", uLen, OMAP_HSMMC_READ(BLK), iStatus, bBlkMode); goto err; } PDEBUG("sdiodrv_read_async() dma_ch=%d \n",g_drv.dma_rx_channel); dma_bus_address = dma_map_single(g_drv.dev, dma_buffer, uLen, DMA_FROM_DEVICE); if (!dma_bus_address) { PERR("sdioDrv_ReadAsync: dma_map_single failed\n"); goto err; } if (g_drv.dma_read_addr != 0) { printk(KERN_ERR "sdioDrv_ReadAsync: previous DMA op is not finished!\n"); BUG(); } g_drv.dma_read_addr = dma_bus_address; g_drv.dma_read_size = uLen; omap_set_dma_dest_params (g_drv.dma_rx_channel, 0, // dest_port is only for OMAP1 OMAP_DMA_AMODE_POST_INC, dma_bus_address, 0, 0); omap_set_dma_transfer_params(g_drv.dma_rx_channel, OMAP_DMA_DATA_TYPE_S32, uNumOfElem , uDmaBlockCount , OMAP_DMA_SYNC_FRAME, TIWLAN_MMC_DMA_RX, OMAP_DMA_SRC_SYNC); omap_start_dma(g_drv.dma_rx_channel); /* Continued at sdiodrv_irq() after DMA transfer is finished */ #ifdef TI_SDIO_DEBUG printk(KERN_INFO "R53: [0x%x](%u) (A)\n", uHwAddr, uLen); #endif return 0; err: return -1; } /*--------------------------------------------------------------------------------------*/ int sdioDrv_WriteSync (unsigned int uFunc, unsigned int uHwAddr, void * pData, unsigned int uLen, unsigned int bIncAddr, unsigned int bMore) { unsigned int uCmdArg; int iStatus; // printk(KERN_INFO "in sdioDrv_WriteSync\n"); uCmdArg = SDIO_CMD53_WRITE(1, uFunc, 0, bIncAddr, uHwAddr, uLen); iStatus = sdiodrv_data_xfer_sync(OMAP_HSMMC_CMD53_WRITE, uCmdArg, pData, uLen, BWE); if (iStatus != 0) { PERR("sdioDrv_WriteSync() FAILED!!\n"); } #ifdef TI_SDIO_DEBUG if (uLen == 1) printk(KERN_INFO "W53: [0x%x](%u) < 0x%x\n", uHwAddr, uLen, (unsigned)(*(char *)pData)); else if (uLen == 2) printk(KERN_INFO "W53: [0x%x](%u) < 0x%x\n", uHwAddr, uLen, (unsigned)(*(short *)pData)); else if (uLen == 4) printk(KERN_INFO "W53: [0x%x](%u) < 0x%x\n", uHwAddr, uLen, (unsigned)(*(long *)pData)); else printk(KERN_INFO "W53: [0x%x](%u)\n", uHwAddr, uLen); #endif return iStatus; } /*--------------------------------------------------------------------------------------*/ int sdioDrv_WriteAsync (unsigned int uFunc, unsigned int uHwAddr, void * pData, unsigned int uLen, unsigned int bBlkMode, unsigned int bIncAddr, unsigned int bMore) { int iStatus; unsigned int uCmdArg; unsigned int uNumBlks; unsigned int uDmaBlockCount; unsigned int uNumOfElem; dma_addr_t dma_bus_address; #ifdef TI_SDIO_DEBUG printk(KERN_INFO "W53: [0x%x](%u) F[%d] B[%d] I[%d]\n", uHwAddr, uLen, uFunc, bBlkMode, bIncAddr); #endif // printk(KERN_INFO "in sdioDrv_WriteAsync\n"); if (bBlkMode) { /* For block mode use number of blocks instead of length in bytes */ uNumBlks = uLen >> g_drv.uBlkSizeShift; uDmaBlockCount = uNumBlks; /* due to the DMA config to 32Bit per element (OMAP_DMA_DATA_TYPE_S32) the division is by 4 */ uNumOfElem = g_drv.uBlkSize >> 2; } else { uNumBlks = uLen; uDmaBlockCount = 1; uNumOfElem = (uLen + 3) >> 2; } uCmdArg = SDIO_CMD53_WRITE(1, uFunc, bBlkMode, bIncAddr, uHwAddr, uNumBlks); iStatus = sdiodrv_send_data_xfer_commad(OMAP_HSMMC_CMD53_WRITE_DMA, uCmdArg, uNumBlks, BWE, bBlkMode); if (!(iStatus & BWE)) { PERR("sdioDrv_WriteAsync() buffer disabled! length = %d, BLK = 0x%x, Status = 0x%x\n", uLen, OMAP_HSMMC_READ(BLK), iStatus); goto err; } OMAP_HSMMC_WRITE(ISE, TC); dma_bus_address = dma_map_single(g_drv.dev, pData, uLen, DMA_TO_DEVICE); if (!dma_bus_address) { PERR("sdioDrv_WriteAsync: dma_map_single failed\n"); goto err; } if (g_drv.dma_write_addr != 0) { PERR("sdioDrv_WriteAsync: previous DMA op is not finished!\n"); BUG(); } g_drv.dma_write_addr = dma_bus_address; g_drv.dma_write_size = uLen; omap_set_dma_src_params (g_drv.dma_tx_channel, 0, // src_port is only for OMAP1 OMAP_DMA_AMODE_POST_INC, dma_bus_address, 0, 0); omap_set_dma_transfer_params(g_drv.dma_tx_channel, OMAP_DMA_DATA_TYPE_S32, uNumOfElem, uDmaBlockCount, OMAP_DMA_SYNC_FRAME, TIWLAN_MMC_DMA_TX, OMAP_DMA_DST_SYNC); omap_start_dma(g_drv.dma_tx_channel); /* Continued at sdiodrv_irq() after DMA transfer is finished */ return 0; err: return -1; } /*--------------------------------------------------------------------------------------*/ int sdioDrv_ReadSyncBytes (unsigned int uFunc, unsigned int uHwAddr, unsigned char *pData, unsigned int uLen, unsigned int bMore) { unsigned int uCmdArg; unsigned int i; int iStatus; for (i = 0; i < uLen; i++) { uCmdArg = SDIO_CMD52_READ(0, uFunc, 0, uHwAddr); iStatus = sdiodrv_send_command(OMAP_HSMMC_CMD52_READ, uCmdArg); if (!(iStatus & CC)) { PERR("sdioDrv_ReadSyncBytes() SDIO Command error status = 0x%x\n", iStatus); return -1; } else { *pData = (unsigned char)(OMAP_HSMMC_READ(RSP10)); } #ifdef TI_SDIO_DEBUG printk(KERN_INFO "R52: [0x%x](%u) = 0x%x\n", uHwAddr, uLen, (unsigned)*pData); #endif uHwAddr++; pData++; } return 0; } /*--------------------------------------------------------------------------------------*/ int sdioDrv_WriteSyncBytes (unsigned int uFunc, unsigned int uHwAddr, unsigned char *pData, unsigned int uLen, unsigned int bMore) { unsigned int uCmdArg; unsigned int i; int iStatus; for (i = 0; i < uLen; i++) { #ifdef TI_SDIO_DEBUG printk(KERN_INFO "W52: [0x%x](%u) < 0x%x\n", uHwAddr, uLen, (unsigned)*pData); #endif uCmdArg = SDIO_CMD52_WRITE(1, uFunc, 0, uHwAddr, *pData); iStatus = sdiodrv_send_command(OMAP_HSMMC_CMD52_WRITE, uCmdArg); if (!(iStatus & CC)) { PERR("sdioDrv_WriteSyncBytes() SDIO Command error status = 0x%x\n", iStatus); return -1; } uHwAddr++; pData++; } return 0; } static int sdioDrv_probe(struct platform_device *pdev) { int rc; u32 status; #ifdef SDIO_1_BIT /* see also in SdioAdapter.c */ unsigned long clock_rate = 6000000; #else unsigned long clock_rate = 24000000; #endif printk(KERN_INFO "TIWLAN SDIO probe: initializing mmc%d device\n", pdev->id + 1); /* remember device struct for future DMA operations */ g_drv.dev = &pdev->dev; g_drv.irq = platform_get_irq(pdev, 0); if (g_drv.irq < 0) return -ENXIO; rc= request_irq(OMAP_MMC_IRQ, sdiodrv_irq, 0, SDIO_DRIVER_NAME, &g_drv); if (rc != 0) { PERR("sdioDrv_InitHw() - request_irq FAILED!!\n"); return rc; } sdiodrv_irq_requested = 1; rc = sdiodrv_dma_init(); if (rc != 0) { PERR("sdiodrv_init() - sdiodrv_dma_init FAILED!!\n"); free_irq(OMAP_MMC_IRQ, &g_drv); return rc; } sdiodrv_dma_on = 1; spin_lock_init(&g_drv.clk_lock); #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31)) dummy_pdev.id = TIWLAN_MMC_CONTROLLER; dev_set_name(&dummy_pdev.dev, "mmci-omap-hs.%lu", TIWLAN_MMC_CONTROLLER); g_drv.fclk = clk_get(&dummy_pdev.dev, "fck"); #else g_drv.fclk = clk_get(&pdev->dev, "mmchs_fck"); #endif if (IS_ERR(g_drv.fclk)) { rc = PTR_ERR(g_drv.fclk); PERR("clk_get(fclk) FAILED !!!\n"); goto err; } sdiodrv_fclk_got = 1; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31)) g_drv.iclk = clk_get(&dummy_pdev.dev, "ick"); #else g_drv.iclk = clk_get(&pdev->dev, "mmchs_ick"); #endif if (IS_ERR(g_drv.iclk)) { rc = PTR_ERR(g_drv.iclk); PERR("clk_get(iclk) FAILED !!!\n"); goto err; } sdiodrv_iclk_got = 1; rc = sdioDrv_clk_enable(); if (rc) { PERR("sdioDrv_probe : clk_enable FAILED !!!\n"); goto err; } OMAP3430_mmc_reset(); //obc - init sequence p. 3600,3617 /* 1.8V */ OMAP_HSMMC_WRITE(CAPA, OMAP_HSMMC_READ(CAPA) | VS18); OMAP_HSMMC_WRITE(HCTL, OMAP_HSMMC_READ(HCTL) | SDVS18);//SDVS fits p. 3650 /* clock gating */ OMAP_HSMMC_WRITE(SYSCONFIG, OMAP_HSMMC_READ(SYSCONFIG) | AUTOIDLE); /* bus power */ OMAP_HSMMC_WRITE(HCTL, OMAP_HSMMC_READ(HCTL) | SDBP);//SDBP fits p. 3650 /* interrupts */ OMAP_HSMMC_WRITE(ISE, 0); OMAP_HSMMC_WRITE(IE, IE_EN_MASK); //p. 3601 suggests moving to the end OMAP3430_mmc_set_clock(clock_rate, &g_drv); printk(KERN_INFO "SDIO clock Configuration is now set to %dMhz\n",(int)clock_rate/1000000); /* Bus width */ #ifdef SDIO_1_BIT /* see also in SdioAdapter.c */ PDEBUG("%s() setting %d data lines\n",__FUNCTION__, 1); OMAP_HSMMC_WRITE(HCTL, OMAP_HSMMC_READ(HCTL) & (ONE_BIT)); #else PDEBUG("%s() setting %d data lines\n",__FUNCTION__, 4); OMAP_HSMMC_WRITE(HCTL, OMAP_HSMMC_READ(HCTL) | (1 << 1));//DTW 4 bits - p. 3650 #endif /* send the init sequence. 80 clocks of synchronization in the SDIO */ //doesn't match p. 3601,3617 - obc OMAP_HSMMC_WRITE( CON, OMAP_HSMMC_READ(CON) | INIT_STREAM); OMAP_HSMMC_SEND_COMMAND( 0, 0); status = sdiodrv_poll_status(OMAP_HSMMC_STAT, CC, MMC_TIMEOUT_MS); if (!(status & CC)) { PERR("sdioDrv_InitHw() SDIO Command error status = 0x%x\n", status); rc = -1; goto err; } OMAP_HSMMC_WRITE(CON, OMAP_HSMMC_READ(CON) & ~INIT_STREAM); return 0; err: sdiodrv_free_resources(); return rc; } static int sdioDrv_remove(struct platform_device *pdev) { printk(KERN_INFO "sdioDrv_remove: calling sdiodrv_shutdown\n"); sdiodrv_shutdown(); return 0; } #ifdef CONFIG_PM static int sdioDrv_suspend(struct platform_device *pdev, pm_message_t state) { #if 0 int rc = 0; /* Tell WLAN driver to suspend, if a suspension function has been registered */ if (g_drv.wlanDrvIf_pm_suspend) { printk(KERN_INFO "TISDIO: Asking TIWLAN to suspend\n"); rc = g_drv.wlanDrvIf_pm_suspend(); if (rc != 0) return rc; } sdiodrv_shutdown(); #endif printk(KERN_INFO "TISDIO: sdioDrv is suspending\n"); return 0; } /* Routine to resume the MMC device */ static int sdioDrv_resume(struct platform_device *pdev) { /* int rc; */ printk(KERN_INFO "TISDIO: sdioDrv is resuming\n"); #if 0 rc = sdioDrv_probe(pdev); if (rc != 0) { printk(KERN_ERR "TISDIO: resume error\n"); return rc; } if (g_drv.wlanDrvIf_pm_resume) { printk(KERN_INFO "TISDIO: Asking TIWLAN to resume\n"); return(g_drv.wlanDrvIf_pm_resume()); } #endif return 0; } #else #define sdioDrv_suspend NULL #define sdioDrv_resume NULL #endif static struct platform_driver sdioDrv_struct = { .probe = sdioDrv_probe, .remove = sdioDrv_remove, .suspend = sdioDrv_suspend, .resume = sdioDrv_resume, .driver = { .name = SDIO_DRIVER_NAME, }, }; void sdioDrv_register_pm(int (*wlanDrvIf_Start)(void), int (*wlanDrvIf_Stop)(void)) { g_drv.wlanDrvIf_pm_resume = wlanDrvIf_Start; g_drv.wlanDrvIf_pm_suspend = wlanDrvIf_Stop; } int sdioDrv_clk_enable(void) { unsigned long flags; int ret = 0; spin_lock_irqsave(&g_drv.clk_lock, flags); if (g_drv.ifclks_enabled) goto done; ret = clk_enable(g_drv.iclk); if (ret) goto clk_en_err1; ret = clk_enable(g_drv.fclk); if (ret) goto clk_en_err2; g_drv.ifclks_enabled = 1; sdioDrv_hsmmc_restore_ctx(); done: spin_unlock_irqrestore(&g_drv.clk_lock, flags); return ret; clk_en_err2: clk_disable(g_drv.iclk); clk_en_err1 : spin_unlock_irqrestore(&g_drv.clk_lock, flags); return ret; } void sdioDrv_clk_disable(void) { unsigned long flags; spin_lock_irqsave(&g_drv.clk_lock, flags); if (!g_drv.ifclks_enabled) goto done; sdioDrv_hsmmc_save_ctx(); clk_disable(g_drv.fclk); clk_disable(g_drv.iclk); g_drv.ifclks_enabled = 0; done: spin_unlock_irqrestore(&g_drv.clk_lock, flags); } #ifdef TI_SDIO_STANDALONE static int __init sdioDrv_init(void) #else int __init sdioDrv_init(int sdcnum) #endif { memset(&g_drv, 0, sizeof(g_drv)); memset(&hsmmc_ctx, 0, sizeof(hsmmc_ctx)); printk(KERN_INFO "TIWLAN SDIO init\n"); #ifndef TI_SDIO_STANDALONE sdio_init( sdcnum ); #endif g_drv.sdio_wq = create_freezeable_workqueue(SDIOWQ_NAME); if (!g_drv.sdio_wq) { printk("TISDIO: Fail to create SDIO WQ\n"); return -EINVAL; } /* Register the sdio driver */ return platform_driver_register(&sdioDrv_struct); } #ifdef TI_SDIO_STANDALONE static #endif void __exit sdioDrv_exit(void) { /* Unregister sdio driver */ platform_driver_unregister(&sdioDrv_struct); if (g_drv.sdio_wq) destroy_workqueue(g_drv.sdio_wq); } #ifdef TI_SDIO_STANDALONE module_init(sdioDrv_init); module_exit(sdioDrv_exit); #endif EXPORT_SYMBOL(sdioDrv_ConnectBus); EXPORT_SYMBOL(sdioDrv_DisconnectBus); EXPORT_SYMBOL(sdioDrv_ExecuteCmd); EXPORT_SYMBOL(sdioDrv_ReadSync); EXPORT_SYMBOL(sdioDrv_WriteSync); EXPORT_SYMBOL(sdioDrv_ReadAsync); EXPORT_SYMBOL(sdioDrv_WriteAsync); EXPORT_SYMBOL(sdioDrv_ReadSyncBytes); EXPORT_SYMBOL(sdioDrv_WriteSyncBytes); EXPORT_SYMBOL(sdioDrv_register_pm); MODULE_DESCRIPTION("TI WLAN SDIO driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS(SDIO_DRIVER_NAME); MODULE_AUTHOR("Texas Instruments Inc"); #endif