/****************************************************************************** ** ** FILE NAME : ifx_sdio.c ** PROJECT : Amazon-S ** MODULES : SDIO ** ** DATE : 07 Jan 2008 ** AUTHOR : Reddy Mallikarjuna ** DESCRIPTION : SDIO Driver ** COPYRIGHT : Copyright (c) 2006 ** Infineon Technologies AG ** Am Campeon 1-12, 85579 Neubiberg, Germany ** ** 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; either version 2 of the License, or ** (at your option) any later version. ** ** HISTORY ** $Version $Date $Author $Comment ** 1.0.0 07-Jan'08 Reddy Mallikarjuna first version *******************************************************************************/ #include /* retrieve the CONFIG_* macros */ #if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS) # define MODVERSIONS #endif #if defined(MODVERSIONS) && !defined(__GENKSYMS__) # include #endif #ifndef EXPORT_SYMTAB # define EXPORT_SYMTAB /* need this one 'cause we export symbols */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ENABLE_TIMER 0 #define AMAZON_S_SDIO_PROC_DIRNAME "amazon_s_sdio" #define AMAZON_S_SDIO_MAJOR 201 static int major = AMAZON_S_SDIO_MAJOR; typedef struct reg_entry { int *flag; char name[30]; /* To hold names? */ char description[100]; /* To hold description */ unsigned short low_ino; } reg_entry_t; #define PROC_ITEMS 21 static reg_entry_t regs[PROC_ITEMS]; /* total no. of items to be monitored by /proc/mei */ #define NUM_OF_REG_ENTRY (sizeof(regs)/sizeof(reg_entry_t)) uint32_t cmd_counter = 0, cmd_response_counter = 0, cmd_error_counter = 0; uint32_t data_read_counter = 0, data_read_total_size = 0, data_read_async_counter = 0, data_read_async_total_size = 0; uint32_t data_write_counter = 0, data_write_total_size = 0, data_write_async_counter = 0, data_write_async_total_size = 0; uint32_t data_error_counter = 0; uint32_t data_tx_error_counter = 0, data_tx_re_error_counter = 0, rx_or_error_counter = 0; uint32_t cmd_crc_error_counter=0, cmd_crc_rx_error_counter=0, cmd_crc_tx_error_counter=0; static int g_data_recv_len=0, g_data_len=0, g_data_len_offset=0; reg_entry_t regs_temp[PROC_ITEMS] = // Items being debugged { /* { flag, name, description } */ {(int *) 1, "gpio", "gpio number to used", 0}, {(int *) 2, "frequency", "sd controller frequency", 0}, {(int *) 3, "bus_width", "sd controller data bus width", 0}, {&cmd_counter, "cmd_counter", "command count", 0}, {&cmd_response_counter, "cmd_response_counter", "command response count", 0}, {&cmd_error_counter, "cmd_error_counter","command error count", 0}, {&data_read_counter, "data_read_counter", "data read command counter", 0}, {&data_read_async_counter, "data_read_async_counter", "data read async command counter", 0}, {&data_read_total_size, "data_read_total_size", "total data size of reading", 0}, {&data_read_async_total_size, "data_read_async_total_size", "total async data size of reading", 0}, {&data_write_counter, "data_write_counter", "data write command counter", 0}, {&data_write_async_counter, "data_write_async_counter", "data write async command counter", 0}, {&data_write_total_size, "data_write_total_size", "total data size of writing", 0}, {&data_write_async_total_size, "data_write_async_total_size", "total data size of writing in async mode", 0}, {&data_error_counter, "data_error_counter", "data error count", 0}, {&cmd_crc_error_counter, "cmd_crc_error_counter", "command crc error counter (rx+tx+cmd)", 0}, {&cmd_crc_rx_error_counter, "cmd_crc_rx_error_counter", "command crc rx error counter", 0}, {&cmd_crc_tx_error_counter, "cmd_crc_tx_error_counter", "command crc tx error counter", 0}, {&data_tx_error_counter, "data_tx_error_counter", "data error count", 0}, {&data_tx_re_error_counter, "data_tx_re_error_counter", "data re error count", 0}, {&rx_or_error_counter, "rx_or_error_counter", "rx over run error count", 0}, }; static int ifx_sdio_ioctl (struct inode *ino, struct file *fil, unsigned int command, unsigned long lon); static struct file_operations ifx_sdio_operations = { ioctl:ifx_sdio_ioctl, }; int ifx_sdio_send_cmd (x_IFX_sdio_cmd_t *pCmd ); int ifx_sdio_controller_set_ops (int type, uint32_t data); int ifx_sdio_write_stream_data_fifo_mode(x_IFX_sdio_cmd_t *pCmd, void *buffer, int length, unsigned long *status, unsigned long timeout); int ifx_sdio_read_stream_data_fifo_mode(x_IFX_sdio_cmd_t *pCmd, void *buffer, int length, unsigned long *status, unsigned long timeout); int ifx_sdio_write_block_data_fifo_mode(x_IFX_sdio_cmd_t *pCmd, void *buffer, int block_size_shift,unsigned long *status, unsigned long timeout); int ifx_sdio_read_blcok_data_fifo_mode(x_IFX_sdio_cmd_t *pCmd, void *buffer, int block_size_shift,unsigned long *status, unsigned long timeout); int ifx_sdio_write_stream_data_dma_mode (x_IFX_sdio_cmd_t *pCmd, void *buffer, int length); int ifx_sdio_read_stream_data_dma_mode (x_IFX_sdio_cmd_t *pCmd, void *buffer, int length); int ifx_sdio_write_block_data_dma_mode (x_IFX_sdio_cmd_t *pCmd, x_IFX_sdio_block_request_t *pRequest, unsigned long timeout); int ifx_sdio_read_block_data_dma_mode (x_IFX_sdio_cmd_t *pCmd, x_IFX_sdio_block_request_t *pRequest, unsigned long timeout); void *ifx_sdio_controller_register_callback(void (*fct)(unsigned long)); static void (*g_sdio_completion_callback)(unsigned long); uint8_t *g_sdio_async_receive_buffer = NULL; static struct proc_dir_entry *ifx_sdio_dir; static int ifx_sdio_proc_read (struct file *file, char *buf, size_t nbytes, loff_t * ppos); static struct file_operations proc_operations = { read:ifx_sdio_proc_read, }; typedef struct { struct dma_device_info *dma_device; uint32_t mclk_speed; uint32_t current_speed; struct ifx_sdio_host *host; } ifx_sdio_controller_priv_t; ifx_sdio_controller_priv_t *ifx_priv; #define AMAZON_S_SDIO_WLAN_VERSION "1.0.0" #define DRIVER_NAME "ifx_sdio_host" static unsigned int fmax = 25000000; static int ifx_sdio_gpio_configure (void) { printk("using GPIO %d as MCLCLK.\n", MCLCLK); IFX_SD_PIN_RESERVE(MCLCLK); IFX_SD_DIR_OUT(MCLCLK); IFX_SD_ALTSEL0_CLR(MCLCLK); IFX_SD_ALTSEL1_SET(MCLCLK); IFX_SD_OD_SET(MCLCLK); IFX_SD_PUDSEL_SET(MCLCLK); IFX_SD_PUDEN_SET(MCLCLK); printk("using GPIO %d as MCLCMD.\n", MCLCMD); IFX_SD_PIN_RESERVE(MCLCMD); IFX_SD_DIR_IN(MCLCMD); IFX_SD_ALTSEL0_CLR(MCLCMD); IFX_SD_ALTSEL1_SET(MCLCMD); IFX_SD_OD_SET(MCLCMD); IFX_SD_PUDSEL_SET(MCLCMD); IFX_SD_PUDEN_SET(MCLCMD); #if MCLDATA0 == 17 //GPIO17 (P1.1): DIR=0,ALT0=0,ALT1=1,OUT=1, OD=1 printk("using GPIO %d as DATA0.\n", MCLDATA0); IFX_SD_PIN_RESERVE(MCLDATA0); IFX_SD_ALTSEL0_CLR(MCLDATA0); #elif MCLDATA0 == 28 //GPIO28 (P1.12): DIR=0,ALT0=1,ALT1=1,OUT=1, OD=1 printk("using GPIO %d as DATA0.\n", MCLDATA0); IFX_SD_PIN_RESERVE(MCLDATA0); IFX_SD_ALTSEL0_SET(MCLDATA0); #else #error MCLDATA0 not defined #endif // IFX_SD_DIR_IN(MCLDATA0); IFX_SD_ALTSEL1_SET(MCLDATA0); IFX_SD_OD_SET(MCLDATA0); IFX_SD_PUDSEL_SET(MCLDATA0); IFX_SD_PUDEN_SET(MCLDATA0); printk("using GPIO %d as DATA1.\n", MCLDATA1); IFX_SD_PIN_RESERVE(MCLDATA1); // IFX_SD_DIR_IN(MCLDATA1); IFX_SD_ALTSEL0_CLR(MCLDATA1); IFX_SD_ALTSEL1_SET(MCLDATA1); IFX_SD_OD_SET(MCLDATA1); IFX_SD_PUDSEL_SET(MCLDATA1); IFX_SD_PUDEN_SET(MCLDATA1); #if MCLDATA2 == 16 //GPIO16 (P1.0): DIR=0,ALT0=0,ALT1=1,OUT=1, OD=1 printk("using GPIO %d as DATA2.\n", MCLDATA2); IFX_SD_PIN_RESERVE(MCLDATA2); // IFX_SD_DIR_OUT(MCLDATA2); IFX_SD_ALTSEL0_CLR(MCLDATA2); #elif MCLDATA2 == 26 //GPIO26 (P1.10): DIR=0,ALT0=1,ALT1=1,OUT=1, OD=1 printk("using GPIO %d as DATA2.\n", MCLDATA2); IFX_SD_PIN_RESERVE(MCLDATA2); // IFX_SD_DIR_IN(MCLDATA2); IFX_SD_ALTSEL0_SET(MCLDATA2); #else #error MCLDATA2 not defined #endif IFX_SD_ALTSEL1_SET(MCLDATA2); IFX_SD_OD_SET(MCLDATA2); IFX_SD_PUDSEL_SET(MCLDATA2); IFX_SD_PUDEN_SET(MCLDATA2); printk("using GPIO %d as DATA3.\n", MCLDATA3); IFX_SD_PIN_RESERVE(MCLDATA3); // IFX_SD_DIR_IN(MCLDATA3); IFX_SD_ALTSEL0_CLR(MCLDATA3); IFX_SD_ALTSEL1_SET(MCLDATA3); IFX_SD_OD_SET(MCLDATA3); IFX_SD_PUDSEL_SET(MCLDATA3); IFX_SD_PUDEN_SET(MCLDATA3); return 0; } /* GPIO is connected to the WLAN chip */ int ifx_sdio_set_ext_reset(int gpio, int active) { IFX_SD_PIN_RESERVE(gpio); IFX_SD_ALTSEL0_CLR(gpio); IFX_SD_ALTSEL1_CLR(gpio); if(active) IFX_SD_OUTPUT_CLR(gpio); else IFX_SD_OUTPUT_SET(gpio); IFX_SD_DIR_OUT(gpio); IFX_SD_OD_SET(gpio); IFX_SD_PUDSEL_SET(gpio); IFX_SD_PUDEN_SET(gpio); return 0; } int dump_data_and_time(void *buffer, int length) { // uint32_t *pData = buffer; printk("Cmd crc error counter (rx+tx+cmd): 0x%08x\n", cmd_crc_error_counter); printk("Cmd crc Rx error counter: 0x%08x\n", cmd_crc_rx_error_counter); printk("Cmd crc Tx error counter: 0x%08x\n", cmd_crc_tx_error_counter); printk("Data read counter: 0x%08x\n",data_read_counter ); printk("Data read counter: 0x%08x\n",data_read_async_counter ); printk("Data read total size: 0x%08x\n", data_read_total_size); printk("Data read total size: 0x%08x\n", data_read_async_total_size); printk("Data write counter: 0x%08x\n", data_write_counter); printk("Data write counter: 0x%08x\n", data_write_async_counter); printk("Data write total size: 0x%08x\n", data_write_total_size); printk("Data write total size: 0x%08x\n", data_write_async_total_size); printk("Data Tx error counter: 0x%08x\n", data_tx_error_counter); printk("Data Rx error counter: 0x%08x\n", rx_or_error_counter); printk("Data error counter (rx+tx): 0x%08x\n", data_error_counter); return 0; } /** * \fn int ifx_sdio_controller_set_ops (int type, uint32_t data) * \ingroup AMAZON_S_SDIO_FUNCTIONS * \brief set sdio controller operations * \param type sdio operation type * \param data set data value * \return On Success return OK otherwise error codes */ int ifx_sdio_controller_set_ops (int type, uint32_t data) { uint32_t reg = 0; uint32_t div = 0; switch (type) { case SD_SET_FREQENCY: reg = MMC_READ_REG32(MCI_CLK); reg &= ~(MCI_CLK_BYPASS | 0xFF); /*BYPASS, CLK_DIV */ if (data == 0) { /* printk("Warning! The clock is 0.\n"); */ reg &= ~(MCI_CLK_ENABLE); MMC_WRITE_REG32(reg, MCI_CLK); ifx_priv->current_speed = 0; break; } if (data >= ( ifx_priv->mclk_speed)) { if (data > (ifx_priv->mclk_speed)) /* printk("Error! Clock is too large. clock = %d, mclk_speed=%d\n", data, (ifx_priv-> mclk_speed)); */ div = MCI_CLK_BYPASS; } else { div = ifx_priv->mclk_speed / (data * 2); div -= 1; if (((ifx_priv->mclk_speed) / ((div + 1) * 2)) > data) div++; } /* printk("div=%d,mclk_speed=%d\n", div, ifx_priv->mclk_speed); */ reg |= div; reg |= MCI_CLK_ENABLE; MMC_WRITE_REG32(reg, MCI_CLK); if (div == MCI_CLK_BYPASS) ifx_priv->current_speed =( ifx_priv->mclk_speed); else ifx_priv->current_speed =( ifx_priv->mclk_speed) / ((div + 1) * 2); /* printk("current_speed = %08X\n", ifx_priv->current_speed); */ break; case SD_SET_BUS_WIDTH: reg = MMC_READ_REG32(MCI_CLK); switch (data) { case SD_BUS_4BITS: reg |= MCI_CLK_WD; break; case SD_BUS_1BITS: reg &= ~(MCI_CLK_WD); break; default: return -EINVAL; } MMC_WRITE_REG32(reg, MCI_CLK); /* printk("MCI_CLK=%08X\n", MMC_READ_REG32(MCI_CLK)); */ break; default: return -EINVAL; } return OK; } /* ** Poll the command status ** Input: ** x_IFX_sdio_cmd_t *pCmd --> Ptr to the command structure ** Output: ** Return: OK->Wake up, -1-> continue to poll */ static int ifx_sdio_cmd_status_poll (x_IFX_sdio_cmd_t *pCmd) { u32 status, reg_im0, status1; int ret = -1; status = MMC_READ_REG32 (MCI_STAT); status1=status; reg_im0 = MMC_READ_REG32 (MCI_IM0); status &= reg_im0; /* printk("Status:0x%08x\n",status); */ /*Command was sent but No response required. */ if (status & MCI_CMDSENT) { if (pCmd->response_type == MMC_RSP_NONE) { ret = OK; } } /* Command response received. CRC check passed.*/ if (status & MCI_CMDRESPEND) { if (MMC_READ_REG32(MCI_REPCMD) == pCmd->op_code) { switch (pCmd->response_type) { case MMC_RSP_R1: case MMC_RSP_R1B: case MMC_RSP_R3: pCmd->response[0] = MMC_READ_REG32(MCI_REP0); cmd_response_counter++; /* printk("poll got interrupt,resp=%08X, Status: 0x%08x, IM0: 0x%08x\n", pCmd->response[0], status1,reg_im0); */ ret = OK; break; default: pCmd->error = ERROR_WRONG_RESPONSE_TYPE; break; } } else { if (MMC_READ_REG32(MCI_REPCMD) == 0x3F && pCmd->response_type == MMC_RSP_R2) /* Response CMD of Response R2 is 3F */ { pCmd->response[0] = MMC_READ_REG32 (MCI_REP0); pCmd->response[1] = MMC_READ_REG32 (MCI_REP1); pCmd->response[2] = MMC_READ_REG32 (MCI_REP2); pCmd->response[3] = MMC_READ_REG32 (MCI_REP3); cmd_response_counter++; /* printk("got interrupt,resp[0]=0x%8X,resp[1]=0x%08X,resp[2]=0x%08X,resp[3]=0x%08X\n",pCmd->response[0], pCmd->response[1],pCmd->response[2], pCmd->response[3]); */ ret = OK; } else { pCmd->error = ERROR_WRONG_RESPONSE_CMD; } } } /* Command time-out */ if (status & MCI_CMDTIMEOUT) { pCmd->error = MMC_ERR_TIMEOUT; ret = MMC_ERR_TIMEOUT; printk("%s[%d] : Command Time out, Status: 0x%08x\n",__func__,__LINE__,status1); } /*Command response received, CRC check failed. */ if (status & MCI_CMDCRCFAIL) { if (pCmd->response_type == MMC_RSP_R3 && MMC_READ_REG32(MCI_REPCMD) == 0x3F) /* Response R3 CRC is always 7F, Response CMD is always 3F*/ { pCmd->response[0] = MMC_READ_REG32(MCI_REP0); /* printk("poll1 got interrupt,resp=%08X\n", pCmd->response[0]); */ ret = OK; } else { pCmd->error = MMC_ERR_BADCRC; cmd_crc_error_counter++; ret = MMC_ERR_BADCRC; } } MMC_WRITE_REG32(status, MCI_CL); return ret; } /** * \fn int ifx_sdio_send_cmd (x_IFX_sdio_cmd_t *pCmd) * \ingroup AMAZON_S_SDIO_FUNCTIONS * \brief Send a command * \param pCmd pointer to a structure command * \return On Success return OK otherwise error codes */ int ifx_sdio_send_cmd (x_IFX_sdio_cmd_t *pCmd) { uint32_t cmd = 0, imc; uint32_t reg = 0, counter; int ret; /* printk("Sending command :op_code=%d,type=0x%X args=0x%X\n", pCmd->op_code, pCmd->response_type, pCmd->args); */ MMC_WRITE_REG32(pCmd->args, MCI_ARG); cmd = pCmd->op_code; switch (pCmd->response_type) { case MMC_RSP_R1: case MMC_RSP_R1B: case MMC_RSP_R3: cmd |= MCI_CMD_SHORT_RSP; MMC_WRITE_REG32(MMC_READ_REG32 (MCI_IM0) | MCI_CMDRESPENDMASK | MCI_CMDTIMEOUTMASK | MCI_CMDCRCFAILMASK , MCI_IM0); break; case MMC_RSP_R2: cmd |= MCI_CMD_LONG_RSP; MMC_WRITE_REG32 (MMC_READ_REG32 (MCI_IM0) | MCI_CMDRESPENDMASK | MCI_CMDTIMEOUTMASK | MCI_CMDCRCFAILMASK , MCI_IM0); break; default: MMC_WRITE_REG32 (MMC_READ_REG32 (MCI_IM0) | MCI_CMDSENTMASK, MCI_IM0); break; } pCmd->error = OK; cmd |= MCI_CPSM_ENABLE; MMC_WRITE_REG32 (cmd, MCI_CMD); cmd_counter++; imc = MMC_READ_REG32 (SDIO_IMC); imc &= ~(SDIO_IMC_INTR0); /*Disable INTR0 */ MMC_WRITE_REG32 (imc, SDIO_IMC); counter = 500; do { ret = ifx_sdio_cmd_status_poll(pCmd); } while (((ret == -1) && counter--) ); if(counter == 0) printk("c "); if(ret == -1 ) printk("%s[%d]: ERROR!!! status:0x%08x \n",__func__,__LINE__,MMC_READ_REG32(MCI_STAT)); pCmd->error = ret; reg = MMC_READ_REG32 (MCI_IM0); reg &= ~(MCI_CMDRESPENDMASK | MCI_CMDTIMEOUTMASK | MCI_CMDCRCFAILMASK | MCI_CMDSENTMASK); MMC_WRITE_REG32 (reg, MCI_IM0); return pCmd->error; } /** * \fn int ifx_sdio_write_stream_data_fifo_mode(x_IFX_sdio_cmd_t *pCmd, void *buffer, int length, unsigned long *status, unsigned long timeout) * \ingroup AMAZON_S_SDIO_FUNCTIONS * \brief Write data using sdio FIFO's (stream mode) * \param pCmd pointer to the x_IFX_sdio_cmd_t * \param buffer pointer to the buffer * \param length data length * \param status sdio status * \param timeout timeout value * \return On Success return OK otherwise error codes */ int ifx_sdio_write_stream_data_fifo_mode(x_IFX_sdio_cmd_t *pCmd, void *buffer, int length, unsigned long *status, unsigned long timeout) { int i, ret, cnt; uint32_t *pData = buffer , counter; unsigned long flags; local_irq_save(flags); ret = ifx_sdio_send_cmd(pCmd); // if (pCmd->error == MMC_ERR_TIMEOUT) if (pCmd->error != OK) { local_irq_restore(flags); MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ printk("%s[%d]: Tx Command Time out!!!\n",__func__,__LINE__); return ret; } MMC_WRITE_REG32 (0x7ff, MCI_CL); MMC_WRITE_REG32 (0xfffff, MCI_DTIM); MMC_WRITE_REG32 (length, MCI_DLGTH); MMC_WRITE_REG32 (((0x6<<4)| 0x5), MCI_DCTRL);/*dma disable, stream mode, dir->from controller to card */ for (i=0;(ifrom card to from controller */ ret = ifx_sdio_send_cmd(pCmd); // if (pCmd->error == MMC_ERR_TIMEOUT) { if (pCmd->error != OK) { local_irq_restore(flags); MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ printk("%s[%d]: Rx Command Time out\n",__func__,__LINE__); return ret; } i=0; counter=0xfffff; temp = length; while((temp!=0)&& counter-- ) { cnt = *(volatile uint32_t *)MCI_DCNT; while ((((temp-cnt) >= 8) || ((temp == 4) && (cnt == 0))) && (!(*(volatile uint32_t *)MCI_STAT & MCI_RXFIFOEMPTY)) ) { *pData++ = * (volatile uint32_t *)((uint32_t)MCI_DF0); temp -=4; } } local_irq_restore(flags); data_read_counter++; counter=0xffff; cnt = MMC_READ_REG32 (MCI_DCNT); if(cnt!=0 ) { /*Rx over run condition */ data_error_counter++; MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ printk("%s[%d]: Status:0x%08x cnt:%d\n",__func__,__LINE__,MMC_READ_REG32(MCI_STAT),cnt); }else { data_read_total_size += length; } MMC_WRITE_REG32(0x7ff, MCI_CL); /* clear all status bits */ return ret; } /** * \fn int ifx_sdio_write_block_data_fifo_mode(x_IFX_sdio_cmd_t *pCmd, void *buffer, int block_size_shift, unsigned long *status, unsigned long timeout) * \ingroup AMAZON_S_SDIO_FUNCTIONS * \brief Write data using sdio FIFO's (block mode) * \param pCmd pointer to the struct x_IFX_sdio_cmd_t * \param buffer pointer to the buffer * \param block_size_shift data length * \param status sdio status * \param timeout timeout value * \return On Success return OK otherwise error codes */ int ifx_sdio_write_block_data_fifo_mode(x_IFX_sdio_cmd_t *pCmd, void *buffer, int block_size_shift, unsigned long *status, unsigned long timeout) { int i, ret, cnt, length; uint32_t *pData = buffer, counter ; unsigned long flags; length = (1<error == MMC_ERR_TIMEOUT) { if (pCmd->error != OK ) { local_irq_restore(flags); MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ printk("%s[%d]: Tx Command Time out\n",__func__,__LINE__); return ret; } MMC_WRITE_REG32 (0x7ff, MCI_CL); MMC_WRITE_REG32 (0xfffff, MCI_DTIM); MMC_WRITE_REG32 (length, MCI_DLGTH); /*block mode, dir->controller to card, dma->disable */ MMC_WRITE_REG32 (((0x6<<4)| 0x1), MCI_DCTRL); for (i=0;(ierror == MMC_ERR_TIMEOUT) { if (pCmd->error != OK) { local_irq_restore(flags); MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ printk("%s[%d]: Rx Command Time out\n",__func__,__LINE__); return ret; } i=0; counter=0xfffff; temp = (1<< block_size_shift); while((temp!=0)&& counter-- ) { cnt = *(volatile uint32_t *)MCI_DCNT; while ((((temp-cnt) >= 8) || ((temp == 4) && (cnt == 0))) && (!(*(volatile uint32_t *)MCI_STAT & MCI_RXFIFOEMPTY)) ) { *pData++ = * (volatile uint32_t *)((uint32_t)MCI_DF0); temp -=4; } } local_irq_restore(flags); data_read_counter++; counter=0xffff; cnt = MMC_READ_REG32 (MCI_DCNT); if(cnt!=0 ) { data_error_counter++; MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ printk("%s[%d]:, Status:0x%08x \n",__func__,__LINE__,MMC_READ_REG32(MCI_STAT)); } else { data_read_total_size += (1<dma_device; dma_dev->current_tx_chan = 0; MMC_WRITE_REG32 ((SDIO_DMACON_TXON ), SDIO_DMACON); local_irq_save(flags); ret = ifx_sdio_send_cmd(pCmd); local_irq_restore(flags); // if (pCmd->error == MMC_ERR_TIMEOUT) { if (pCmd->error != OK) { MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ printk("%s[%d]: Tx Command Time out\n",__func__,__LINE__); status = (unsigned long )pCmd->error; if (g_sdio_completion_callback != 0) g_sdio_completion_callback(status); return ret; } #if 1 MMC_WRITE_REG32 (0x7ff, MCI_CL); MMC_WRITE_REG32 (0xfffff, MCI_DTIM); MMC_WRITE_REG32 (length, MCI_DLGTH); MMC_WRITE_REG32 (((0x6<<4)|0x0d), MCI_DCTRL); if (dma_device_write (dma_dev, (uint8_t*)buffer, length, NULL) != length) { printk("ERROR: buffer full(async operation)!!!\n"); data_error_counter++; data_tx_error_counter++; ret |= MMC_ERR_TIMEOUT; /*Error condition */ status = (unsigned long )ret; if (g_sdio_completion_callback != 0) g_sdio_completion_callback(status); return ret; } local_irq_save(flags); counter=0xfffff; regVal= MMC_READ_REG32 (MCI_STAT); while ( regVal & MCI_TXACTIVE ) { regVal= MMC_READ_REG32 (MCI_STAT); counter--; if(counter == 0) break; } /* 8 clock cycles of delay is required before the return */ udelay(1); // ndelay(350); /* @25MHz, 1 cycle = 40ns, min 40*(32/4)=320ns*/ // ndelay(650); /* @12.5MHz, 1 cycle = 80ns, min 80*(32/4)=640ns*/ local_irq_restore(flags); cnt= MMC_READ_REG32 (MCI_DCNT); if(cnt != 0) { MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ if(counter == 0) printk(" response[0] = %08x Status:%08x\n", pCmd->response[0], (*(volatile uint32_t *)MCI_STAT) ); data_error_counter++; data_tx_error_counter++; } else { data_write_total_size += length; data_write_async_total_size += length; } data_write_counter++; data_write_async_counter++; data_write_total_size += length; data_write_async_total_size += length; status = (unsigned long )ret; MMC_WRITE_REG32 (0x7ff, MCI_CL); if (g_sdio_completion_callback != 0) g_sdio_completion_callback(status); return ret; #else MMC_WRITE_REG32 (0x7ff, MCI_CL); MMC_WRITE_REG32 (0xfffff, MCI_DTIM); MMC_WRITE_REG32 (length, MCI_DLGTH); MMC_WRITE_REG32 ( MCI_DATATIMEOUTMASK | MCI_DATACRCFAILMASK | MCI_TXUNDERRUNMASK | MCI_DATAENDMASK , MCI_IM1); MMC_WRITE_REG32 (((0x6<<4)|0x0d), MCI_DCTRL); if (dma_device_write (dma_dev, (uint8_t*)buffer, length, NULL) != length) { data_error_counter++; data_tx_error_counter++; printk("%s[%d]: Dma device write fail \n",__func__,__LINE__); } return ret; #endif } /** * \fn int ifx_sdio_read_stream_data_dma_mode (x_IFX_sdio_cmd_t *pCmd, void *buffer, int length); * \ingroup AMAZON_S_SDIO_FUNCTIONS * \brief Read data using DMA (stream mode) * \param pCmd pointer to the struct x_IFX_sdio_cmd_t * \param buffer pointer to the buffer * \param length data length * \return On Success return OK otherwise error codes */ int ifx_sdio_read_stream_data_dma_mode (x_IFX_sdio_cmd_t *pCmd, void *buffer, int length) { int ret=0; unsigned long flags , status=0 ; uint32_t tmplen, reg; // struct dma_device_info *dma_dev = ifx_priv->dma_device; #if 0 memset (buffer, 0, length); #endif tmplen = length; reg = MMC_READ_REG32(SDIO_DMACON); if(!(reg & SDIO_DMACON_RXON) ) /*enable dma rx path if its disabled*/ MMC_WRITE_REG32 ((reg | SDIO_DMACON_RXON), SDIO_DMACON); g_sdio_async_receive_buffer = buffer; g_data_len_offset= 0; g_data_recv_len=0; g_data_len= tmplen; MMC_WRITE_REG32 (0x7ff, MCI_CL); MMC_WRITE_REG32 (( MCI_DATATIMEOUTMASK | MCI_DATABLOCKENDMASK | MCI_DATACRCFAILMASK |MCI_STARTBITERRMASK|MCI_RXOVERRUNMASK ), MCI_IM1); MMC_WRITE_REG32 (0xfffff, MCI_DTIM); MMC_WRITE_REG32 (tmplen, MCI_DLGTH); local_irq_save(flags); MMC_WRITE_REG32 (((0x6<<4)|0xF), MCI_DCTRL); ret = ifx_sdio_send_cmd(pCmd); local_irq_restore(flags); // if (pCmd->error == MMC_ERR_TIMEOUT) { if (pCmd->error != OK) { MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ printk("%s[%d]: Rx Command Time out\n",__func__,__LINE__); status = (unsigned long )pCmd->error; if (g_sdio_completion_callback != 0) g_sdio_completion_callback(status); return ret; } data_read_counter++; data_read_async_counter++; return ret; } /** * \fn int ifx_sdio_write_block_data_dma_mode (x_IFX_sdio_cmd_t *pCmd, x_IFX_sdio_block_request_t *pRequest, unsigned long timeout) * \ingroup AMAZON_S_SDIO_FUNCTIONS * \brief Write data using DMA (block mode) * \param pCmd pointer to the struct x_IFX_sdio_cmd_t * \param pRequest pointer to request buffer * \param timeout sdio timeout * \return On Success return OK otherwise error codes */ int ifx_sdio_write_block_data_dma_mode (x_IFX_sdio_cmd_t *pCmd, x_IFX_sdio_block_request_t *pRequest, unsigned long timeout) { int data_len, ret; uint8_t *pData = NULL; uint32_t data_ctrl_reg ; unsigned long flags, status=0; struct dma_device_info *dma_dev = ifx_priv->dma_device; dma_dev->current_tx_chan = 0; data_len = pRequest->nBlocks*(1<block_size_shift); data_ctrl_reg = (pRequest->block_size_shift << 4) | 0x9; /*blksize,dir=ctrl to card,dma enable */ pData= pRequest->pData_buffer; MMC_WRITE_REG32 ((SDIO_DMACON_TXON ), SDIO_DMACON); local_irq_save(flags); ret = ifx_sdio_send_cmd(pCmd); local_irq_restore(flags); if (pCmd->error != OK) { MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ printk("%s[%d]: Tx Command Time out\n",__func__,__LINE__); status = (unsigned long )pCmd->error; if (g_sdio_completion_callback != 0) g_sdio_completion_callback(status); return ret; } MMC_WRITE_REG32 (0x7ff, MCI_CL); MMC_WRITE_REG32 ( MCI_DATATIMEOUTMASK | MCI_DATABLOCKENDMASK | MCI_DATACRCFAILMASK | MCI_TXUNDERRUNMASK , MCI_IM1); MMC_WRITE_REG32 (0xfffff, MCI_DTIM); MMC_WRITE_REG32 (data_len, MCI_DLGTH); MMC_WRITE_REG32 (data_ctrl_reg, MCI_DCTRL); data_write_async_counter++; if (dma_device_write (dma_dev, pData, data_len, NULL) != data_len) { data_error_counter++; data_tx_error_counter++; // printk("%s[%d]: Dma device write fail \n",__func__,__LINE__); ret|= MMC_ERR_TIMEOUT; /*Error condition */ status = (unsigned long )ret; if (g_sdio_completion_callback != 0) g_sdio_completion_callback(status); } return ret; } /** * \fn int ifx_sdio_read_block_data_dma_mode (x_IFX_sdio_cmd_t *pCmd, x_IFX_sdio_block_request_t *pRequest, unsigned long timeout) * \ingroup AMAZON_S_SDIO_FUNCTIONS * \brief Read data using DMA (block mode) * \param pCmd pointer to the struct x_IFX_sdio_cmd_t * \param pRequest pointer to request buffer * \param timeout sdio timeout * \return On Success return OK otherwise error codes */ int ifx_sdio_read_block_data_dma_mode (x_IFX_sdio_cmd_t *pCmd, x_IFX_sdio_block_request_t *pRequest, unsigned long timeout) { int ret=0; unsigned long flags,status=0 ; uint32_t data_ctrl_reg, tmplen; #if 0 memset (buffer, 0, length); #endif tmplen= pRequest->nBlocks * (1 << pRequest->block_size_shift); data_ctrl_reg = (pRequest->block_size_shift << 4) | 0x9 | (1<<1); /*blksize,dir=card to ctrl,dma enable */ MMC_WRITE_REG32 (MMC_READ_REG32 (SDIO_DMACON)|(SDIO_DMACON_RXON), SDIO_DMACON); g_sdio_async_receive_buffer = pRequest->pData_buffer; g_data_len_offset= 0; g_data_recv_len=0; g_data_len= tmplen; MMC_WRITE_REG32 (0x7ff, MCI_CL); MMC_WRITE_REG32 (( MCI_DATATIMEOUTMASK | MCI_DATACRCFAILMASK |MCI_STARTBITERRMASK|MCI_RXOVERRUNMASK ), MCI_IM1); MMC_WRITE_REG32 (0xfffff, MCI_DTIM); MMC_WRITE_REG32 (tmplen, MCI_DLGTH); local_irq_save(flags); MMC_WRITE_REG32 (data_ctrl_reg, MCI_DCTRL); ret = ifx_sdio_send_cmd(pCmd); local_irq_restore(flags); // if (pCmd->error == MMC_ERR_TIMEOUT) { if (pCmd->error != OK) { MMC_WRITE_REG32 (0, MCI_DCTRL); /*disable state machine*/ printk("%s[%d]: Rx Command Time out\n",__func__,__LINE__); status = (unsigned long )pCmd->error; if (g_sdio_completion_callback != 0) g_sdio_completion_callback(status); return ret; } data_read_counter++; data_read_async_counter++; return ret; } void *ifx_sdio_controller_register_callback(void (*fct)(unsigned long)) { void *ret = (void *)g_sdio_completion_callback; g_sdio_completion_callback = fct; return ret; } static int ifx_sdio_ioctl (struct inode *ino, struct file *fil, unsigned int command, unsigned long lon) { int ret = 0; #if 0 struct sd_cmd sdio_ioctl_cmd = { 0 }; switch (command) { case AMAZON_S_SDIO_SEND_CMD: copy_from_user ((char *) &sdio_ioctl_cmd, (char *) lon, sizeof (struct sd_cmd)); ret = ifx_sdio_send_cmd ( &sdio_ioctl_cmd); copy_to_user ((char *) lon, (char *) &sdio_ioctl_cmd, sizeof (struct sd_cmd)); break; case AMAZON_S_SDIO_READ_DATA_SYNC: /* TO DO */ break; case AMAZON_S_SDIO_READ_DATA_ASYNC: /* TO DO */ break; case AMAZON_S_SDIO_SEND_DATA_SYNC: /*TO DO*/ break; case AMAZON_S_SDIO_SEND_DATA_ASYNC: /*TO DO*/ break; case AMAZON_S_SDIO_SET_OPS_WBUS: if (lon == 0) { ret = ifx_sd_controller_set_ops (SD_SET_BUS_WIDTH, SD_BUS_1BITS); } else if (lon == 1) { ret = ifx_sd_controller_set_ops (SD_SET_BUS_WIDTH, SD_BUS_4BITS); } else { ret = -EINVAL; } break; case AMAZON_S_SDIO_SET_OPS_FREQUENCY: ret = ifx_sd_controller_set_ops (SD_SET_FREQENCY, lon); break; case AMAZON_S_SDIO_GET_OPS_WBUS: break; default: return -ENOIOCTLCMD; } #endif return ret; } static int ifx_sdio_proc_read (struct file *file, char *buf, size_t nbytes, loff_t * ppos) { int i_ino = (file->f_dentry->d_inode)->i_ino; char outputbuf[128]; int count = 0; int i; reg_entry_t *current_reg = NULL; for (i = 0; i < NUM_OF_REG_ENTRY; i++) { if (regs[i].low_ino == (unsigned short)i_ino) { current_reg = ®s[i]; break; } } if (current_reg == NULL) return -EINVAL; if (current_reg->flag == (int *) 1) { ///proc/ifx_sdio/gpio if (*ppos > 0) /* Assume reading completed in previous read */ return 0; // indicates end of file count = sprintf (outputbuf, "\nFunction\tGPIO Number\n"); count += sprintf (outputbuf + count, "Command:\t%d\nClock :\t%d\nDATA 0 :\t%d\nDATA 1 :\t%d\nDATA 2 :\t%d\nDATA 3 :\t%d\n", MCLCMD, MCLCLK, MCLDATA0, MCLDATA1, MCLDATA2, MCLDATA3); *ppos += count; } else if (current_reg->flag == (int *) 2) { ///proc/ifx_sdio/frequency if (*ppos > 0) /* Assume reading completed in previous read */ return 0; // indicates end of file #if 1 count += sprintf (outputbuf + count, "Frequency: %d\n", ifx_priv->current_speed); #endif *ppos += count; } else if (current_reg->flag == (int *) 3) { uint32_t reg; if (*ppos > 0) /* Assume reading completed in previous read */ return 0; // indicates end of fil reg = MMC_READ_REG32(MCI_CLK); if (reg & MCI_CLK_WD) { count += sprintf (outputbuf + count, "4 Bits\n"); } else { count += sprintf (outputbuf + count, "1 Bit\n"); } *ppos += count; } else { if (*ppos > 0) /* Assume reading completed in previous read */ return 0; // indicates end of file count = sprintf (outputbuf, "0x%08X\n\n", *(current_reg->flag)); *ppos += count; if (count > nbytes) /* Assume output can be read at one time */ return -EINVAL; } if (copy_to_user (buf, outputbuf, count)) { return -EFAULT; } return count; } static u8 * dma_buffer_alloc (int len, int *byte_offset, void **opt) { u8 *buffer = NULL; buffer = kmalloc (len, GFP_ATOMIC); if (buffer == NULL) { printk("%s:buffer alloc Failed: line:%d\n",__FUNCTION__,__LINE__); return NULL; } *byte_offset = 0; return buffer; } static int dma_buffer_free (u8 * dataptr, void *opt) { if (dataptr == NULL) { } else { //kfree(dataptr); } return OK; } static int ifx_data_recv (struct dma_device_info *dma_dev) { uint8_t *buf = NULL; unsigned int len; unsigned long status=0; len = dma_device_read (dma_dev, &buf, NULL); if(len) memcpy (g_sdio_async_receive_buffer+g_data_recv_len, ((uint8_t *) buf),(len-g_data_len_offset)); if(buf) kfree (buf); g_data_recv_len += len; if( g_data_recv_len >= g_data_len ) { MMC_WRITE_REG32(MMC_READ_REG32(SDIO_DMACON) & ~SDIO_DMACON_RXON, SDIO_DMACON); data_read_total_size += g_data_recv_len; data_read_async_total_size += g_data_recv_len; if (g_sdio_completion_callback != 0) g_sdio_completion_callback(status); } /* int k; for ( k=0; k<(len-g_data_len_offset);k++) { if (k%16 == 0) printk("\n"); printk("%2x ",(uint8_t)g_sdio_async_receive_buffer[k]); } */ return OK; } static int dma_intr_handler (struct dma_device_info *dma_dev, int status) { // printk("got dma interrupt!\n"); switch (status) { case RCV_INT: ifx_data_recv (dma_dev); break; case TX_BUF_FULL_INT: // printk("TX_BUF_FULL_INT\n"); break; } return OK; } static int setup_dma_driver (ifx_sdio_controller_priv_t * priv) { int i = 0; int ret; priv->dma_device = dma_device_reserve ("SDIO"); if (!priv->dma_device) return -ENOMEM; priv->dma_device->intr_handler = dma_intr_handler; priv->dma_device->buffer_alloc = dma_buffer_alloc; priv->dma_device->buffer_free = dma_buffer_free; priv->dma_device->num_rx_chan = 1; /*turn on all the receive channels */ for (i = 0; i < priv->dma_device->num_rx_chan; i++) { priv->dma_device->rx_chan[i]->packet_size = 1 << 9 ; //ifx_sd_controller.blklen; priv->dma_device->rx_chan[i]->control = AMAZON_S_DMA_CH_ON; } ret = dma_device_register (priv->dma_device); for (i = 0; i < priv->dma_device->num_rx_chan; i++) priv->dma_device->rx_chan[i]->open (priv->dma_device-> rx_chan[i]); return ret; } static int cleanup_dma_driver (ifx_sdio_controller_priv_t * priv) { if (priv == NULL) return -EINVAL; if (priv->dma_device) { dma_device_unregister (priv->dma_device); kfree (priv->dma_device); } return 0; } /* * PIO data transfer IRQ handler. */ static irqreturn_t ifx_sdio_pio_irq(int irq, void *dev_id) { u32 status; int ret = 0; status = MMC_READ_REG32(MCI_STAT); status &= MMC_READ_REG32(MCI_IM1); MMC_WRITE_REG32(0, MCI_IM1); MMC_WRITE_REG32(status, MCI_CL); /* printk("%s[%d]: Status: 0x%08x \n",__func__,__LINE__,status);*/ if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN| MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND| MCI_DATASTARTBITERR) ){ if(status & (MCI_DATAEND | MCI_DATABLOCKEND)) { ret = 0; udelay(10); /* stream mode */ } else { ret = MMC_ERR_TIMEOUT; MMC_WRITE_REG32 (0, MCI_DCTRL); MMC_WRITE_REG32(0, SDIO_DMACON); } status = (unsigned long )ret; if (g_sdio_completion_callback != 0) g_sdio_completion_callback(status); } return IRQ_HANDLED; } #if ENABLE_TIMER static void ifx_sdio_card_check_status(unsigned long data) { struct ifx_sdio_host *host = (struct ifx_sdio_host *)data; unsigned int status ; status = MMC_READ_REG32(MCI_STAT); if(status){ /* printk("%s[%d] : status:0x%08x\n",__FUNCTION__,__LINE__,status); */ } // else // printk("%s[%d] : status:0x%08x\n",__FUNCTION__,__LINE__,status); host->oldstat = status; mod_timer(&host->timer, jiffies + HZ); } #endif static int __init ifx_sdio_init(void) { uint32_t sdio_id = 0; struct ifx_sdio_host *host=NULL; int retval=0,i,j=0; struct proc_dir_entry *entry; printk ("Amazon_S_sdio_controller WLAN driver Version:%s\n", AMAZON_S_SDIO_WLAN_VERSION); sdio_id = MMC_READ_REG32(SDIO_ID); if (sdio_id != 0xF041C030) { printk("Amazon-S SDIO Controller not found!!\n"); return -ENODEV; } /* power on SDIO module */ SDIO_PMU_SETUP(PMU_ENABLE); ifx_sdio_gpio_configure (); ifx_priv = kmalloc (sizeof (ifx_sdio_controller_priv_t), GFP_ATOMIC); if (ifx_priv == NULL) { return -ENOMEM; } memset (ifx_priv, 0, sizeof (ifx_sdio_controller_priv_t)); ifx_priv->mclk_speed = 25000000; setup_dma_driver (ifx_priv); /* SDIO clock 100MHz / 4 = 25MHz */ MMC_WRITE_REG32(0x400, SDIO_CLC); MMC_WRITE_REG32(MCI_PWR_ON , MCI_PWR); MMC_WRITE_REG32(SDIO_CTRL_SDIOEN , SDIO_CTRL); #ifdef CONFIG_DEVFS_FS if (devfs_register_chrdev (major, AMAZON_S_SDIO_PROC_DIRNAME , &ifx_sdio_operations) != 0) { printk ("%s[%d] unable to register major for ifx_sdio!!!\n",__FUNCTION__,__LINE__); retval = -1; goto free_mem; } #else if (register_chrdev (major, AMAZON_S_SDIO_PROC_DIRNAME, &ifx_sdio_operations) != 0) { printk ("%s[%d] unable to register major for ifx_sdio!!!\n",__FUNCTION__,__LINE__); retval = -1; goto free_mem; } #endif /* procfs */ ifx_sdio_dir = proc_mkdir (AMAZON_S_SDIO_PROC_DIRNAME, NULL); if (ifx_sdio_dir == NULL) { printk (KERN_ERR ": can't create /proc/" AMAZON_S_SDIO_PROC_DIRNAME "\n\n"); retval = -ENOMEM; goto free_cdev_mem; } memcpy ((char *) regs, (char *) regs_temp, sizeof (regs_temp)); for (i = 0; i < NUM_OF_REG_ENTRY; i++) { entry = create_proc_entry (regs[i].name, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, ifx_sdio_dir); if (entry) { regs[i].low_ino = entry->low_ino; entry->proc_fops = &proc_operations; } else { printk (KERN_ERR ": can't create /proc/" AMAZON_S_SDIO_PROC_DIRNAME "/%s\n\n", regs[i].name); retval = -ENOMEM; j=i; goto free_proc_mem; } } spin_lock_init(&host->lock); MMC_WRITE_REG32(0, MCI_IM0); MMC_WRITE_REG32(0, MCI_IM1); MMC_WRITE_REG32(0xfff, MCI_CL); printk("%s[%d]: MMC_TNET_1350\n",__FUNCTION__,__LINE__); ifx_sdio_controller_set_ops (SD_SET_FREQENCY, 0); udelay(100); ifx_sdio_controller_set_ops (SD_SET_FREQENCY, 25000000); udelay(100); // ifx_sdio_controller_set_ops (SD_SET_FREQENCY, 12500000); ifx_sdio_controller_set_ops (SD_SET_BUS_WIDTH, SD_BUS_4BITS); retval = request_irq(IFX_MMC_CONTROLLER_INTR1_IRQ, ifx_sdio_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host); if (retval) { goto free_proc_mem; } MMC_WRITE_REG32(MCI_IRQENABLE, MCI_IM0); #if ENABLE_TIMER init_timer(&host->timer); host->timer.data = (unsigned long)host; host->timer.function = ifx_sdio_card_check_status; host->timer.expires = jiffies + HZ; add_timer(&host->timer); #endif MMC_WRITE_REG32(SDIO_IMC_INTR0| SDIO_IMC_INTR1, SDIO_IMC); return 0; free_proc_mem: #ifdef CONFIG_PROC_FS for (i = 0; i < j; i++) remove_proc_entry (regs[i].name, ifx_sdio_dir); remove_proc_entry (AMAZON_S_SDIO_PROC_DIRNAME, &proc_root); #endif //CONFIG_PROC_FS free_cdev_mem: #ifdef CONFIG_DEVFS_FS devfs_unregister_chrdev (major, AMAZON_S_SDIO_PROC_DIRNAME); #else unregister_chrdev (major, AMAZON_S_SDIO_PROC_DIRNAME); #endif free_mem: if(ifx_priv) cleanup_dma_driver (ifx_priv); if(ifx_priv ) kfree (ifx_priv); return -1; } static void __exit ifx_sdio_exit(void) { #ifdef CONFIG_PROC_FS int i = 0; #endif printk("Module ifx_sdio_controller exit\n"); #ifdef CONFIG_PROC_FS for (i = 0; i < NUM_OF_REG_ENTRY; i++) remove_proc_entry (regs[i].name, ifx_sdio_dir); remove_proc_entry (AMAZON_S_SDIO_PROC_DIRNAME, &proc_root); #endif //CONFIG_PROC_FS #ifdef CONFIG_DEVFS_FS devfs_unregister_chrdev (major, "ifx_sdio"); #else unregister_chrdev (major, "ifx_sdio"); #endif free_irq (IFX_MMC_CONTROLLER_INTR1_IRQ, NULL); if(ifx_priv) cleanup_dma_driver (ifx_priv); if(ifx_priv) kfree (ifx_priv); } module_init(ifx_sdio_init); module_exit(ifx_sdio_exit); module_param(fmax, uint, 0444); MODULE_DESCRIPTION("Amazon-S SDIO interface driver");