/****************************************************************************** ** ** 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 #include #include #include #include #include #include #define IFX_SDIO_BASE_ADDR 0x1e102000 #define ENABLE_TIMER 1 #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 10 static reg_entry_t regs[PROC_ITEMS]; /* total ino. 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; uint32_t data_write_counter = 0; uint32_t data_error_counter = 0; uint32_t cmd_crc_error_counter=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_write_counter, "data_write_counter", "data write command counter", 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}, }; 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_controller_set_ops (int type, uint32_t data); static void ifx_sdio_request(struct mmc_host *mmc, struct mmc_request *mrq); #define DMA_CLASS 0x0 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, }; #include 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; struct mmc_host *ifx_mmc; static u64 sdio_dmamask = (u32)0x1fffffff; static void release_platform_dev(struct device * dev) { printk("IFX sdio platform_dev release\n"); dev->parent = NULL; } static struct platform_device platform_dev = { .id = -1, .dev = { .release = release_platform_dev, .dma_mask = &sdio_dmamask, }, }; #define AMAZON_S_SDIO_VERSION "1.0.0" #define DRIVER_NAME "ifx_sdio_host" #define DBG(host,fmt,args...) \ pr_debug("%s: %s: " fmt, ifx_mmc_hostname(host->mmc), __func__ , args) static unsigned int fmax = 25000000; #if 0 /*MCLCMD GPIO 3 or 20 */ #define MCLCMD 3 /*MCLCLK GPIO 0 or 19 */ #define MCLCLK 0 //19 /*MCLDATA0 GPIO 17 or 28 */ #define MCLDATA0 28 //17 /*MCLDATA1 GPIO 18 or 27 */ #define MCLDATA1 27 //18 /*MCLDATA2 GPIO 16 or 26 */ #define MCLDATA2 26 //16 /*MCLDATA3 GPIO 15 or 25 */ #define MCLDATA3 25 //15 #define MAX_PIN_PER_PORT 16 /* Every port has 16 pins, up to 4 ports from 0~3 */ #define PIN2PORT(pin) ((((pin) >> 4) & 0x3)) #define PIN2PORTPIN(pin) ((pin) % (MAX_PIN_PER_PORT)) #define IFX_SD_PIN_RESERVE(pin) \ bsp_port_reserve_pin((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_DIR_OUT(pin) \ bsp_port_set_dir_out((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_DIR_IN(pin) \ bsp_port_set_dir_in((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_OUTPUT_SET(pin) \ bsp_port_set_output((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_OUTPUT_CLR(pin) \ bsp_port_clear_output((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_ALTSEL0_SET(pin) \ bsp_port_set_altsel0((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_ALTSEL0_CLR(pin) \ bsp_port_clear_altsel0((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_OD_SET(pin) \ bsp_port_set_open_drain((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_OD_CLR(pin) \ bsp_port_clear_open_drain((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_ALTSEL1_SET(pin) \ bsp_port_set_altsel1((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_ALTSEL1_CLR(pin) \ bsp_port_clear_altsel1((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_PUDSEL_SET(pin) \ bsp_port_set_pudsel((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #define IFX_SD_PUDEN_SET(pin) \ bsp_port_set_puden((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_SDIO)) #endif 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; } /** * \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; } static int ifx_sdio_ioctl (struct inode *ino, struct file *fil, unsigned int command, unsigned long lon) { int ret = 0; #if 1 switch (command) { case AMAZON_S_SDIO_SET_OPS_WBUS: if (lon == 0) { ret = ifx_sdio_controller_set_ops (SD_SET_BUS_WIDTH, SD_BUS_1BITS); } else if (lon == 1) { ret = ifx_sdio_controller_set_ops (SD_SET_BUS_WIDTH, SD_BUS_4BITS); } else { ret = -EINVAL; } break; case AMAZON_S_SDIO_SET_OPS_FREQUENCY: ret = ifx_sdio_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; *byte_offset = 0; buffer = kmalloc (len, GFP_ATOMIC); if (buffer == NULL) { printk("%s:buffer alloc Failed: line:%d\n",__FUNCTION__,__LINE__); return NULL; } 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; len = dma_device_read (dma_dev, &buf, NULL); /* * Map the current scatter buffer. */ unsigned long flags; unsigned int remain; uint8_t *buffer; struct ifx_sdio_host *host = ifx_priv->host; buffer = ifx_sdio_kmap_atomic(host, &flags) + host->sg_off; remain = host->sg_ptr->length - host->sg_off; memcpy (buffer, ((uint8_t *) buf) ,len); /* * Unmap the buffer. */ ifx_sdio_kunmap_atomic(host, buffer, &flags); host->sg_off += len; host->size -= len; remain -= len; // printk("Rx %s[%d] len:%d, remain:%d, host->size:%d, buffer:0x%08x\n",__FUNCTION__,__LINE__,len,remain,host->size,(uint32_t)buffer); // if(!remain ) // flush_dcache_page(host->sg_ptr->page); if(buf) kfree(buf); if (host->size == 0) { MMC_WRITE_REG32(0, MCI_IM1); MMC_WRITE_REG32(MMC_READ_REG32(MCI_IM0) | MCI_DATAENDMASK, MCI_IM0); } 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; } static void ifx_sdio_request_end(struct ifx_sdio_host *host, struct mmc_request *mrq) { MMC_WRITE_REG32(0, MCI_CMD); BUG_ON(host->data); host->mrq = NULL; host->cmd = NULL; if (mrq->data) mrq->data->bytes_xfered = host->data_xfered; MMC_WRITE_REG32(0x0 , SDIO_DMACON); /* * Need to drop the host lock here; ifx_mmc_request_done may call * back into the driver... */ spin_unlock(&host->lock); ifx_mmc_request_done(host->mmc, mrq); spin_lock(&host->lock); } static void ifx_sdio_stop_data(struct ifx_sdio_host *host) { MMC_WRITE_REG32(0, MCI_DCTRL); MMC_WRITE_REG32(0, MCI_IM1); host->data = NULL; } static void ifx_sdio_start_data(struct ifx_sdio_host *host, struct mmc_data *data) { unsigned int datactrl, timeout, irqmask=0; unsigned long long clks; int blksz_bits, write_flag; DBG(host, "blksz %04x blks %04x flags %08x\n", data->blksz, data->blocks, data->flags); host->data = data; // host->size = data->blksz; host->size = data->blocks*data->blksz; /*for multiblock mode */ host->data_xfered = 0; ifx_sdio_init_sg(host, data); clks = (unsigned long long)data->timeout_ns * host->cclk; do_div(clks, 1000000000UL); timeout = data->timeout_clks + (unsigned int)clks; MMC_WRITE_REG32(timeout, MCI_DTIM); MMC_WRITE_REG32(host->size, MCI_DLGTH); blksz_bits = ffs(data->blksz) - 1; BUG_ON(1 << blksz_bits != data->blksz); datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; if (data->flags & MMC_DATA_READ) { datactrl |= MCI_DPSM_DIRECTION; /* * If we have less than a FIFOSIZE of bytes to transfer, * trigger a PIO interrupt as soon as any data is available. */ write_flag=0; datactrl |= (1<< 3); /*enable dma mode*/ MMC_WRITE_REG32(0x1 , SDIO_DMACON); } else { /* * We don't actually need to include "FIFO empty" here * since its implicit in "FIFO half empty". */ irqmask = MCI_TXFIFOHALFEMPTYMASK; write_flag= 1; MMC_WRITE_REG32(0x2 , SDIO_DMACON); } MMC_WRITE_REG32(datactrl, MCI_DCTRL); MMC_WRITE_REG32(MMC_READ_REG32(MCI_IM0) & ~MCI_DATAENDMASK, MCI_IM0); if(write_flag) { write_flag=0; MMC_WRITE_REG32(irqmask, MCI_IM1); } } static void ifx_sdio_start_command(struct ifx_sdio_host *host, struct mmc_command *cmd, u32 c) { DBG(host, "op %02x arg %08x flags %08x\n", cmd->opcode, cmd->arg, cmd->flags); if (MMC_READ_REG32(MCI_CMD) & MCI_CPSM_ENABLE) { MMC_WRITE_REG32(0, MCI_CMD); udelay(1); } c |= cmd->opcode | MCI_CPSM_ENABLE; if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) c |= MCI_CPSM_LONGRSP; c |= MCI_CPSM_RESPONSE; } if (/*interrupt*/0) c |= MCI_CPSM_INTERRUPT; host->cmd = cmd; MMC_WRITE_REG32(cmd->arg, MCI_ARG); MMC_WRITE_REG32(c, MCI_CMD); cmd_counter++; } static void ifx_sdio_data_irq(struct ifx_sdio_host *host, struct mmc_data *data, unsigned int status) { if (status & MCI_DATABLOCKEND) { host->data_xfered += data->blksz; } if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { if (status & MCI_DATACRCFAIL) { /* printk("%s[%d] Data crc err!!!\n",__FUNCTION__,__LINE__); */ data->error = MMC_ERR_BADCRC; } else if (status & MCI_DATATIMEOUT) { /* printk("%s[%d] Data time out err!!!\n",__FUNCTION__,__LINE__); */ data->error = MMC_ERR_TIMEOUT; } else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) { /* printk("%s[%d] Data fifo err!!!\n",__FUNCTION__,__LINE__);*/ data->error = MMC_ERR_FIFO; } else if (status & MCI_DATASTARTBITERR ) { /* printk("%s[%d] Data fifo err!!!\n",__FUNCTION__,__LINE__); */ data->error = MMC_ERR_STARTBIT; } status |= MCI_DATAEND; /* * We hit an error condition. Ensure that any data * partially written to a page is properly coherent. */ if (host->sg_len && data->flags & MMC_DATA_READ) flush_dcache_page(host->sg_ptr->page); } if (status & MCI_DATAEND) { ifx_sdio_stop_data(host); if (!data->stop) { ifx_sdio_request_end(host, data->mrq); } else { ifx_sdio_start_command(host, data->stop, 0); } } } static void ifx_sdio_cmd_irq(struct ifx_sdio_host *host, struct mmc_command *cmd, unsigned int status) { host->cmd = NULL; cmd->resp[0] = MMC_READ_REG32(MCI_REP0); cmd->resp[1] = MMC_READ_REG32(MCI_REP1); cmd->resp[2] = MMC_READ_REG32(MCI_REP2); cmd->resp[3] = MMC_READ_REG32(MCI_REP3); if (status & MCI_CMDTIMEOUT) { cmd->error = MMC_ERR_TIMEOUT; cmd_error_counter++; } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { cmd->error = MMC_ERR_BADCRC; cmd_error_counter++; } if (!cmd->data || cmd->error != MMC_ERR_NONE) { if (host->data) ifx_sdio_stop_data(host); ifx_sdio_request_end(host, cmd->mrq); } else if (!(cmd->data->flags & MMC_DATA_READ)) { data_write_counter++; ifx_sdio_start_data(host, cmd->data); } cmd_response_counter++; } /* * PIO data transfer IRQ handler. */ static irqreturn_t ifx_sdio_pio_irq(int irq, void *dev_id) { u32 status; struct ifx_sdio_host *host = dev_id; struct dma_device_info *dma_dev = ifx_priv->dma_device; unsigned long flags; unsigned int remain, len; char *buffer; status = MMC_READ_REG32(MCI_STAT); DBG(host, "irq1 %08x\n", status); /* * once we get the ready for data transmit, then disable all interrupts on MASK1 */ MMC_WRITE_REG32(0, MCI_IM1); /* * Map the current scatter buffer. */ buffer = ifx_sdio_kmap_atomic(host, &flags) + host->sg_off; remain = host->sg_ptr->length - host->sg_off; len = 0; len = dma_device_write(dma_dev, buffer, host->size, NULL); if(len != host->size){ printk("%s[%d] dma device full!!! len:0x%08x....\n",__FUNCTION__,__LINE__,len); } /* * Unmap the buffer. */ ifx_sdio_kunmap_atomic(host, buffer, &flags); host->sg_off += len; host->size -= len; remain -= len; if(!remain ) flush_dcache_page(host->sg_ptr->page); /* * If we run out of data, disable the data IRQs; this * prevents a race where the FIFO becomes empty before * the chip itself has disabled the data path, and * stops us racing with our data end IRQ. */ if (host->size == 0) { // MMC_WRITE_REG32(0, MCI_IM1); MMC_WRITE_REG32(MMC_READ_REG32(MCI_IM0) | MCI_DATAENDMASK, MCI_IM0); } return IRQ_HANDLED; } /* * Handle completion of command and data transfers. */ static irqreturn_t ifx_sdio_irq(int irq, void *dev_id) { struct ifx_sdio_host *host = dev_id; u32 status; int ret = 0; spin_lock(&host->lock); do { struct mmc_command *cmd; struct mmc_data *data; status = MMC_READ_REG32(MCI_STAT); status &= MMC_READ_REG32(MCI_IM0); MMC_WRITE_REG32(status, MCI_CL); DBG(host, "irq0 %08x\n", status); data = host->data; if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN| MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data) ifx_sdio_data_irq(host, data, status); cmd = host->cmd; if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd) ifx_sdio_cmd_irq(host, cmd, status); ret = 1; } while (status); spin_unlock(&host->lock); return IRQ_RETVAL(ret); } static void ifx_sdio_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct ifx_sdio_host *host = ifx_mmc_priv(mmc); ifx_priv->host = host; WARN_ON(host->mrq != NULL); spin_lock_irq(&host->lock); host->mrq = mrq; if (mrq->data && mrq->data->flags & MMC_DATA_READ){ data_read_counter++; ifx_sdio_start_data(host, mrq->data); } ifx_sdio_start_command(host, mrq->cmd, 0); spin_unlock_irq(&host->lock); } static void ifx_sdio_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct ifx_sdio_host *host = ifx_mmc_priv(mmc); u32 clk = 0, bus_width; /* printk("%s[%d] host: 0x%08x\n",__func__,__LINE__,host); */ if (ios->clock) { if (ios->clock >= host->mclk) { clk = MCI_CLK_BYPASS; host->cclk = host->mclk; ifx_priv->current_speed = host->cclk; } else { clk = host->mclk / (2 * ios->clock) - 1; if (clk > 256) clk = 255; host->cclk = host->mclk / (2 * (clk + 1)); ifx_priv->current_speed = host->cclk; } clk |= MCI_CLK_ENABLE; MMC_WRITE_REG32(clk, MCI_CLK); } if(ios->bus_width == MMC_BUS_WIDTH_1) { bus_width = MMC_READ_REG32(MCI_CLK); bus_width &=~(1<<11); MMC_WRITE_REG32(bus_width, MCI_CLK); } else if(ios->bus_width == MMC_BUS_WIDTH_4) { bus_width = MMC_READ_REG32(MCI_CLK); bus_width |= (1<<11); MMC_WRITE_REG32(bus_width, MCI_CLK); } } static const struct mmc_host_ops ifx_sdio_ops = { .request = ifx_sdio_request, .set_ios = ifx_sdio_set_ios, }; #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); */ ifx_mmc_detect_change(host->mmc, 0); } // else // printk("%s[%d] : status:0x%08x\n",__FUNCTION__,__LINE__,status); host->oldstat = status; mod_timer(&host->timer, jiffies + HZ); } #endif #define SD_OCR ( MMC_VDD_32_33 | MMC_VDD_33_34 ) static int ifx_sdio_probe(struct device *dev) { struct mmc_host *mmc = ifx_mmc; unsigned int reg; int ret; mmc = ifx_mmc_alloc_host(mmc, dev); if (!mmc) { printk("%s[%d]: Error!!!\n",__FUNCTION__,__LINE__); ret = -ENOMEM; return ret; } dev_set_drvdata(dev, mmc); ifx_mmc_add_host(mmc); /* printk("%s[%d] End\n",__FUNCTION__,__LINE__); */ return 0; } static int ifx_sdio_remove(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); /* printk("%s[%d] ....\n",__FUNCTION__,__LINE__); */ dev_set_drvdata(dev, NULL); if (mmc) { struct ifx_sdio_host *host = ifx_mmc_priv(mmc); #if ENABLE_TIMER del_timer_sync(&host->timer); #endif ifx_mmc_remove_host(mmc); MMC_WRITE_REG32(0, MCI_IM0); MMC_WRITE_REG32(0, MCI_IM1); MMC_WRITE_REG32(0, MCI_CMD); MMC_WRITE_REG32(0, MCI_DCTRL); free_irq(IFX_MMC_CONTROLLER_INTR0_IRQ, host); free_irq(IFX_MMC_CONTROLLER_INTR1_IRQ, host); ifx_mmc_free_host(mmc); } return 0; } static struct device_driver ifx_sdio_driver= { .name = DRIVER_NAME, .bus = &platform_bus_type, .probe = ifx_sdio_probe, .remove = ifx_sdio_remove, }; int ifx_sdio_hc_init(unsigned long base_addr ) { if (platform_dev.dev.parent) { printk("%s[%d] ....\n",__FUNCTION__,__LINE__); return -EBUSY; } /* The driver core will probe for us. */ platform_dev.name = DRIVER_NAME; return platform_device_register(&platform_dev); } void ifx_sdio_hw_remove(void) { platform_device_unregister(&platform_dev); } 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 Version:%s\n", AMAZON_S_SDIO_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; } ifx_mmc = kmalloc(sizeof(struct mmc_host) + sizeof(struct ifx_sdio_host), GFP_KERNEL); if (ifx_mmc == NULL){ kfree(ifx_priv); return -ENOMEM; } memset (ifx_priv, 0, sizeof (ifx_sdio_controller_priv_t)); memset (ifx_mmc, 0, (sizeof (struct mmc_host)+sizeof(struct ifx_sdio_host))); 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; } } host = ifx_mmc_priv(ifx_mmc); host->mclk = 25000000; host->mmc = ifx_mmc; host->base = MMCI_ADDR_SPACE; ifx_mmc->ops = &ifx_sdio_ops; ifx_mmc->f_min = 400000; ifx_mmc->f_max = host->mclk; ifx_mmc->ocr_avail = SD_OCR; ifx_mmc->caps = MMC_CAP_MULTIWRITE | MMC_CAP_4_BIT_DATA; ifx_mmc->max_hw_segs = 1; ifx_mmc->max_phys_segs = 1; //NR_SG; ifx_mmc->max_sectors = 8; /* * Set the maximum segment size. */ ifx_mmc->max_seg_size =4096; spin_lock_init(&host->lock); MMC_WRITE_REG32(0, MCI_IM0); MMC_WRITE_REG32(0, MCI_IM1); MMC_WRITE_REG32(0xfff, MCI_CL); retval = request_irq(IFX_MMC_CONTROLLER_INTR0_IRQ, ifx_sdio_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); if (retval) { goto free_proc_mem; } retval = request_irq(IFX_MMC_CONTROLLER_INTR1_IRQ, ifx_sdio_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host); if (retval) { goto free_irq0; } 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); retval = driver_register(&ifx_sdio_driver); if (retval < 0) { printk(KERN_ERR "%s[%d] retval=%d\n", __func__, __LINE__,retval); goto timer_del; } retval = ifx_sdio_hc_init(IFX_SDIO_BASE_ADDR); if(retval < 0) { printk(KERN_ERR "%s[%d] retval=%d\n", __func__, __LINE__,retval); goto del_reg; } printk("%s[%d] End\n",__FUNCTION__,__LINE__); return 0; del_reg: driver_unregister(&ifx_sdio_driver); timer_del: #if ENABLE_TIMER del_timer_sync(&host->timer); #endif free_irq0: free_irq(IFX_MMC_CONTROLLER_INTR0_IRQ, host); 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); if(ifx_mmc) kfree (ifx_mmc); 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 driver_unregister(&ifx_sdio_driver); ifx_sdio_hw_remove(); if(ifx_priv) cleanup_dma_driver (ifx_priv); if(ifx_priv) kfree (ifx_priv); MMC_WRITE_REG32(0, MCI_IM0); MMC_WRITE_REG32(0, MCI_IM1); MMC_WRITE_REG32(0, MCI_CMD); MMC_WRITE_REG32(0, MCI_DCTRL); free_irq(IFX_MMC_CONTROLLER_INTR0_IRQ, NULL); free_irq(IFX_MMC_CONTROLLER_INTR1_IRQ, NULL); } module_init(ifx_sdio_init); module_exit(ifx_sdio_exit); module_param(fmax, uint, 0444); MODULE_DESCRIPTION("Amazon-S SDIO interface driver");