/* * Copyright (c) 2015 South Silicon Valley Microelectronics Inc. * Copyright (c) 2015 iComm Corporation * * 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 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "sdio_def.h" #include #include #include #include #ifdef CONFIG_FW_ALIGNMENT_CHECK #include #endif #define SDIO_USE_SLOW_CLOCK #define LOW_SPEED_SDIO_CLOCK (25000000) #define HIGH_SPEED_SDIO_CLOCK (50000000) static struct ssv6xxx_platform_data wlan_data; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) #include #else #include #endif #include #define MAX_RX_FRAME_SIZE 0x900 #define SSV_VENDOR_ID 0x3030 #define SSV_CABRIO_DEVID 0x3030 #define ENABLE_FW_SELF_CHECK 1 #define FW_BLOCK_SIZE 0x8000 #define CHECKSUM_BLOCK_SIZE 1024 #define FW_CHECKSUM_INIT (0x12345678) #define FW_STATUS_REG ADR_TX_SEG #define FW_STATUS_MASK (0x00FF0000) #ifdef CONFIG_PM static int ssv6xxx_sdio_trigger_pmu(struct device *dev); static void ssv6xxx_sdio_reset(struct device *child); #else static void ssv6xxx_sdio_reset(struct device *child) { ; } #endif static void ssv6xxx_high_sdio_clk(struct sdio_func *func); static void ssv6xxx_low_sdio_clk(struct sdio_func *func); extern void *ssv6xxx_ifdebug_info[]; extern int ssv_devicetype; extern void ssv6xxx_deinit_prepare(void); static int ssv6xxx_sdio_status = 0; u32 sdio_sr_bhvr = SUSPEND_RESUME_0; EXPORT_SYMBOL(sdio_sr_bhvr); static DEFINE_MUTEX(reboot_lock); u32 shutdown_flags = SSV_SYS_REBOOT; struct ssv6xxx_sdio_glue { struct device *dev; struct platform_device *core; #ifdef CONFIG_FW_ALIGNMENT_CHECK struct sk_buff *dmaSkb; #endif #ifdef CONFIG_PM struct sk_buff *cmd_skb; #endif unsigned int dataIOPort; unsigned int regIOPort; irq_handler_t irq_handler; void *irq_dev; bool dev_ready; }; #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) static const struct sdio_device_id ssv6xxx_sdio_devices[] __devinitconst = #else static const struct sdio_device_id ssv6xxx_sdio_devices[] = #endif { { SDIO_DEVICE(SSV_VENDOR_ID, SSV_CABRIO_DEVID) }, {} }; MODULE_DEVICE_TABLE(sdio, ssv6xxx_sdio_devices); static bool ssv6xxx_is_ready (struct device *child) { struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return false; return glue->dev_ready; } static int ssv6xxx_sdio_cmd52_read(struct device *child, u32 addr, u32 *value) { int ret = -1; struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return ret; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); *value = sdio_readb(func, addr, &ret); sdio_release_host(func); } return ret; } static int ssv6xxx_sdio_cmd52_write(struct device *child, u32 addr, u32 value) { int ret = -1; struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return ret; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); sdio_writeb(func, value, addr, &ret); sdio_release_host(func); } return ret; } static int __must_check ssv6xxx_sdio_read_reg(struct device *child, u32 addr, u32 *buf) { int ret = (-1); struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func ; u8 data[4]; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return ret; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); data[0] = (addr >> ( 0 )) &0xff; data[1] = (addr >> ( 8 )) &0xff; data[2] = (addr >> ( 16 )) &0xff; data[3] = (addr >> ( 24 )) &0xff; ret = sdio_memcpy_toio(func, glue->regIOPort, data, 4); if (WARN_ON(ret)) { dev_err(child->parent, "sdio read reg write address failed (%d)\n", ret); goto io_err; } ret = sdio_memcpy_fromio(func, data, glue->regIOPort, 4); if (WARN_ON(ret)) { dev_err(child->parent, "sdio read reg from I/O failed (%d)\n",ret); goto io_err; } if(ret == 0) { *buf = (data[0]&0xff); *buf = *buf | ((data[1]&0xff)<<( 8 )); *buf = *buf | ((data[2]&0xff)<<( 16 )); *buf = *buf | ((data[3]&0xff)<<( 24 )); } else *buf = 0xffffffff; io_err: sdio_release_host(func); } else { dev_err(child->parent, "sdio read reg glue == NULL!!!\n"); } return ret; } #ifdef ENABLE_WAKE_IO_ISR_WHEN_HCI_ENQUEUE static int ssv6xxx_sdio_trigger_tx_rx (struct device *child) { int ret = (-1); struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; struct mmc_host *host; if (glue == NULL) return ret; func = dev_to_sdio_func(glue->dev); host = func->card->host; mmc_signal_sdio_irq(host); return 0; } #endif static int __must_check ssv6xxx_sdio_write_reg(struct device *child, u32 addr, u32 buf) { int ret = (-1); struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; u8 data[8]; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return ret; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); dev_dbg(child->parent, "sdio write reg addr 0x%x, 0x%x\n",addr, buf); sdio_claim_host(func); data[0] = (addr >> ( 0 )) &0xff; data[1] = (addr >> ( 8 )) &0xff; data[2] = (addr >> ( 16 )) &0xff; data[3] = (addr >> ( 24 )) &0xff; data[4] = (buf >> ( 0 )) &0xff; data[5] = (buf >> ( 8 )) &0xff; data[6] = (buf >> ( 16 )) &0xff; data[7] = (buf >> ( 24 )) &0xff; ret = sdio_memcpy_toio(func, glue->regIOPort, data, 8); sdio_release_host(func); #ifdef __x86_64 udelay(50); #endif } else { dev_err(child->parent, "sdio write reg glue == NULL!!!\n"); } return ret; } static int ssv6xxx_sdio_write_sram(struct device *child, u32 addr, u8 *data, u32 size) { int ret = -1; struct ssv6xxx_sdio_glue *glue; struct sdio_func *func=NULL; glue = dev_get_drvdata(child->parent); if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return ret; func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); do { if (ssv6xxx_sdio_write_reg(child,0xc0000860,addr)) ; sdio_writeb(func, 0x2, REG_Fn1_STATUS, &ret); if (unlikely(ret)) break; ret = sdio_memcpy_toio(func, glue->dataIOPort, data, size); if (unlikely(ret)) return ret; sdio_writeb(func, 0, REG_Fn1_STATUS, &ret); if (unlikely(ret)) return ret; } while (0); sdio_release_host(func); return ret; } void * ssv6xxx_open_firmware(char *user_mainfw) { struct file *fp; fp = filp_open(user_mainfw, O_RDONLY, 0); if (IS_ERR(fp)) { printk("ssv6xxx_open_firmware failed!!\n"); fp = NULL; } return fp; } int ssv6xxx_read_fw_block(char *buf, int len, void *image) { struct file *fp = (struct file *)image; int rdlen; if (!image) return 0; rdlen = kernel_read(fp, fp->f_pos, buf, len); if (rdlen > 0) fp->f_pos += rdlen; return rdlen; } void ssv6xxx_close_firmware(void *image) { if (image) filp_close((struct file *)image, NULL); } static int ssv6xxx_sdio_load_firmware_openfile(struct device *child, u8 *firmware_name) { int ret = 0; struct ssv6xxx_sdio_glue *glue; u8 *fw_buffer = NULL; u32 sram_addr = 0x00000000; u32 block_count = 0; u32 res_size=0,len=0,tolen=0; void *fw_fp=NULL; #ifdef ENABLE_FW_SELF_CHECK u32 checksum = FW_CHECKSUM_INIT; u32 fw_checksum,fw_clkcnt; u32 retry_count = 3; u32 *fw_data32; #else int writesize=0; u32 retry_count = 1; #endif u32 word_count,i; u32 j,jblk; #ifndef SDIO_USE_SLOW_CLOCK struct sdio_func *func=NULL; struct mmc_card *card = NULL; struct mmc_host *host = NULL; #endif glue = dev_get_drvdata(child->parent); if ( (wlan_data.is_enabled != false) || (glue != NULL) || (glue->dev_ready != false)) { #ifndef SDIO_USE_SLOW_CLOCK func = dev_to_sdio_func(glue->dev); card = func->card; host = card->host; #endif fw_fp = ssv6xxx_open_firmware(firmware_name); if (!fw_fp) { printk("failed to find firmware (%s)\n", firmware_name); ret = -1; goto out; } fw_buffer = (u8 *)kzalloc(FW_BLOCK_SIZE, GFP_KERNEL); if (fw_buffer == NULL) { printk("Failed to allocate buffer for firmware.\n"); goto out; } do { u32 clk_en; if(1){ if(ssv6xxx_sdio_write_reg(child, ADR_BRG_SW_RST, 0x0)); if(ssv6xxx_sdio_write_reg(child, ADR_BOOT, 0x01)); if(ssv6xxx_sdio_read_reg(child, ADR_PLATFORM_CLOCK_ENABLE, &clk_en)); if(ssv6xxx_sdio_write_reg(child, ADR_PLATFORM_CLOCK_ENABLE, clk_en | (1 << 2))); } printk("Writing firmware to SSV6XXX...\n"); memset(fw_buffer, 0xA5, FW_BLOCK_SIZE); while ((len = ssv6xxx_read_fw_block((char*)fw_buffer, FW_BLOCK_SIZE, fw_fp))) { tolen += len; if(len < FW_BLOCK_SIZE){ res_size = len; break; } if(0) { jblk = len / 128; for(j=0;j> 24) + (checksum >> 16) + (checksum >> 8) + checksum) & 0x0FF; checksum <<= 16; if (ret == 0) { block_count = tolen / CHECKSUM_BLOCK_SIZE; res_size = tolen % CHECKSUM_BLOCK_SIZE; if(res_size) block_count++; if(ssv6xxx_sdio_write_reg(child, FW_STATUS_REG, (block_count << 16))); if(ssv6xxx_sdio_read_reg(child, FW_STATUS_REG, &fw_clkcnt)); printk("(block_count << 16) = %x,reg =%x\n",(block_count << 16),fw_clkcnt); if(ssv6xxx_sdio_write_reg(child, ADR_BRG_SW_RST, 0x1)); printk("Firmware \"%s\" loaded\n", firmware_name); msleep(50); if(ssv6xxx_sdio_read_reg(child, FW_STATUS_REG, &fw_checksum)); fw_checksum = fw_checksum & FW_STATUS_MASK; if (fw_checksum == checksum) { if(ssv6xxx_sdio_write_reg(child, FW_STATUS_REG, (~checksum & FW_STATUS_MASK))); ret = 0; printk("Firmware check OK.%04x = %04x\n", fw_checksum, checksum); break; } else { printk("FW checksum error: %04x != %04x\n", fw_checksum, checksum); ret = -1; } } else { printk("Firmware \"%s\" download failed. (%d)\n", firmware_name, ret); ret = -1; } } while (--retry_count); if (ret) goto out; ret = 0; } out: if(fw_fp) ssv6xxx_close_firmware(fw_fp); if (fw_buffer != NULL) kfree(fw_buffer); msleep(50); return ret; } int ssv6xxx_get_firmware(struct device *dev, char *user_mainfw, const struct firmware **mainfw) { int ret; BUG_ON(mainfw == NULL); if (*user_mainfw) { ret = request_firmware(mainfw, user_mainfw, dev); if (ret) { dev_err(dev, "couldn't find main firmware %s\n",user_mainfw); goto fail; } if (*mainfw) return 0; } fail: if (*mainfw) { release_firmware(*mainfw); *mainfw = NULL; } return -ENOENT; } static int ssv6xxx_sdio_load_firmware_request(struct device *child ,u8 *firmware_name) { int ret = 0; const struct firmware *ssv6xxx_fw = NULL; struct ssv6xxx_sdio_glue *glue; u8 *fw_buffer = NULL; u32 sram_addr = 0x00000000; u32 block_count = 0; u32 block_idx = 0; u32 res_size; u8 *fw_data; #ifdef ENABLE_FW_SELF_CHECK u32 checksum = FW_CHECKSUM_INIT; u32 fw_checksum; u32 retry_count = 3; u32 *fw_data32; #else int writesize=0; u32 retry_count = 1; #endif #ifndef SDIO_USE_SLOW_CLOCK struct sdio_func *func=NULL; struct mmc_card *card = NULL; struct mmc_host *host = NULL; #endif glue = dev_get_drvdata(child->parent); if ( (wlan_data.is_enabled != false) || (glue != NULL) || (glue->dev_ready != false)) { #ifndef SDIO_USE_SLOW_CLOCK func = dev_to_sdio_func(glue->dev); card = func->card; host = card->host; #endif ret = ssv6xxx_get_firmware(glue->dev, firmware_name, &ssv6xxx_fw); if (ret) { pr_err("failed to find firmware (%d)\n", ret); goto out; } fw_buffer = (u8 *)kzalloc(FW_BLOCK_SIZE, GFP_KERNEL); if (fw_buffer == NULL) { pr_err("Failed to allocate buffer for firmware.\n"); goto out; } #ifdef ENABLE_FW_SELF_CHECK block_count = ssv6xxx_fw->size / CHECKSUM_BLOCK_SIZE; res_size = ssv6xxx_fw->size % CHECKSUM_BLOCK_SIZE; { int word_count = (int)(block_count * CHECKSUM_BLOCK_SIZE / sizeof(u32)); int i; fw_data32 = (u32 *)ssv6xxx_fw->data; for (i = 0; i < word_count; i++) checksum += fw_data32[i]; if(res_size) { memset(fw_buffer, 0xA5, CHECKSUM_BLOCK_SIZE); memcpy(fw_buffer, &ssv6xxx_fw->data[block_count * CHECKSUM_BLOCK_SIZE], res_size); word_count = (int)(CHECKSUM_BLOCK_SIZE / sizeof(u32)); fw_data32 = (u32 *)fw_buffer; for (i = 0; i < word_count; i++) { checksum += fw_data32[i]; } } } checksum = ((checksum >> 24) + (checksum >> 16) + (checksum >> 8) + checksum) & 0x0FF; checksum <<= 16; #endif do { u32 clk_en; if(ssv6xxx_sdio_write_reg(child, ADR_BRG_SW_RST, 0x0)); if(ssv6xxx_sdio_write_reg(child, ADR_BOOT, 0x01)); if(ssv6xxx_sdio_read_reg(child, ADR_PLATFORM_CLOCK_ENABLE, &clk_en)); if(ssv6xxx_sdio_write_reg(child, ADR_PLATFORM_CLOCK_ENABLE, clk_en | (1 << 2))); #ifdef ENABLE_FW_SELF_CHECK block_count = ssv6xxx_fw->size / FW_BLOCK_SIZE; res_size = ssv6xxx_fw->size % FW_BLOCK_SIZE; printk("Writing %d blocks to SSV6XXX...", block_count); for (block_idx = 0, fw_data = (u8 *)ssv6xxx_fw->data, sram_addr = 0;block_idx < block_count; block_idx++, fw_data += FW_BLOCK_SIZE, sram_addr += FW_BLOCK_SIZE) { memcpy(fw_buffer, fw_data, FW_BLOCK_SIZE); ret = ssv6xxx_sdio_write_sram(child, sram_addr, (u8 *)fw_buffer, FW_BLOCK_SIZE); if (ret) break; } if(res_size) { memset(fw_buffer, 0xA5, FW_BLOCK_SIZE); memcpy(fw_buffer, &ssv6xxx_fw->data[block_count * FW_BLOCK_SIZE], res_size); ret = ssv6xxx_sdio_write_sram(child, sram_addr, (u8 *)fw_buffer, ((res_size/CHECKSUM_BLOCK_SIZE)+1)*CHECKSUM_BLOCK_SIZE); } #else block_count = ssv6xxx_fw->size / FW_BLOCK_SIZE; res_size = ssv6xxx_fw->size % FW_BLOCK_SIZE; writesize = sdio_align_size(func,res_size); printk("Writing %d blocks to SSV6XXX...", block_count); for (block_idx = 0, fw_data = (u8 *)ssv6xxx_fw->data, sram_addr = 0;block_idx < block_count; block_idx++, fw_data += FW_BLOCK_SIZE, sram_addr += FW_BLOCK_SIZE) { memcpy(fw_buffer, fw_data, FW_BLOCK_SIZE); ret = ssv6xxx_sdio_write_sram(child, sram_addr, (u8 *)fw_buffer, FW_BLOCK_SIZE); if (ret) break; } if(res_size) { memcpy(fw_buffer, &ssv6xxx_fw->data[block_count * FW_BLOCK_SIZE], res_size); ret = ssv6xxx_sdio_write_sram(child, sram_addr, (u8 *)fw_buffer, writesize); } #endif if (ret == 0) { #ifdef ENABLE_FW_SELF_CHECK block_count = ssv6xxx_fw->size / CHECKSUM_BLOCK_SIZE; res_size = ssv6xxx_fw->size % CHECKSUM_BLOCK_SIZE; if(res_size) block_count++; if(ssv6xxx_sdio_write_reg(child, FW_STATUS_REG, (block_count << 16))); #endif if(ssv6xxx_sdio_write_reg(child, ADR_BRG_SW_RST, 0x1)); printk("Firmware \"%s\" loaded\n", firmware_name); #ifdef ENABLE_FW_SELF_CHECK msleep(50); if(ssv6xxx_sdio_read_reg(child, FW_STATUS_REG, &fw_checksum)); fw_checksum = fw_checksum & FW_STATUS_MASK; if (fw_checksum == checksum) { if(ssv6xxx_sdio_write_reg(child, FW_STATUS_REG, (~checksum & FW_STATUS_MASK))); ret = 0; printk("Firmware check OK.\n"); break; } else { printk("FW checksum error: %04x != %04x\n", fw_checksum, checksum); ret = -1; } #endif } else { printk("Firmware \"%s\" download failed. (%d)\n", firmware_name, ret); ret = -1; } } while (--retry_count); if (ret) goto out; ret = 0; } out: if (ssv6xxx_fw) release_firmware(ssv6xxx_fw); if (fw_buffer != NULL) kfree(fw_buffer); msleep(50); return ret; } static int ssv6xxx_sdio_load_firmware(struct device *child ,u8 *firmware_name, u8 openfile) { int ret = -1; struct ssv6xxx_sdio_glue *glue; struct sdio_func *func; glue = dev_get_drvdata(child->parent); if(openfile) ret = ssv6xxx_sdio_load_firmware_openfile(child,firmware_name); else ret = ssv6xxx_sdio_load_firmware_request(child,firmware_name); if(glue != NULL) { func = dev_to_sdio_func(glue->dev); ssv6xxx_high_sdio_clk(func); } return ret; } static int ssv6xxx_sdio_irq_getstatus(struct device *child,int *status) { int ret = (-1); struct ssv6xxx_sdio_glue *glue; struct sdio_func *func; glue = dev_get_drvdata(child->parent); if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return ret; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); *status = sdio_readb(func, REG_INT_STATUS, &ret); sdio_release_host(func); } return ret; } #if 0 static void _sdio_hexdump(const u8 *buf, size_t len) { size_t i; printk("\n-----------------------------\n"); printk("hexdump(len=%lu):\n", (unsigned long) len); { for (i = 0; i < len; i++){ printk(" %02x", buf[i]); if((i+1)%40 ==0) printk("\n"); } } printk("\n-----------------------------\n"); } #endif static int __must_check ssv6xxx_sdio_read(struct device *child, void *buf, size_t *size) { int ret = (-1), readsize = 0; struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func ; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return ret; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); *size = (uint)sdio_readb(func, REG_CARD_PKT_LEN_0, &ret); if (ret) dev_err(child->parent, "sdio read hight len failed ret[%d]\n",ret); if (ret == 0) { *size = *size | ((uint)sdio_readb(func, REG_CARD_PKT_LEN_1, &ret)<<0x8); if (ret) dev_err(child->parent, "sdio read low len failed ret[%d]\n",ret); } if (ret == 0) { readsize = sdio_align_size(func,*size); ret = sdio_memcpy_fromio(func, buf, glue->dataIOPort, readsize); if (ret) dev_err(child->parent, "sdio read failed size ret[%d]\n",ret); } sdio_release_host(func); } #if 0 if(*size > 1500) _sdio_hexdump(buf,*size); #endif return ret; } static int __must_check ssv6xxx_sdio_write(struct device *child, void *buf, size_t len,u8 queue_num) { int ret = (-1); struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; int writesize; void *tempPointer; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return ret; if ( glue != NULL ) { #ifdef CONFIG_FW_ALIGNMENT_CHECK #ifdef CONFIG_ARM64 if (((u64)buf) & 3) { #else if (((u32)buf) & 3) { #endif memcpy(glue->dmaSkb->data,buf,len); tempPointer = glue->dmaSkb->data; } else #endif tempPointer = buf; #if 0 if(len > 1500) _sdio_hexdump(buf,len); #endif func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); writesize = sdio_align_size(func,len); do { ret = sdio_memcpy_toio(func, glue->dataIOPort, tempPointer, writesize); if ( ret == -EILSEQ || ret == -ETIMEDOUT ) { ret = -1; break; } else { if(ret) dev_err(glue->dev,"Unexpected return value ret=[%d]\n",ret); } } while( ret == -EILSEQ || ret == -ETIMEDOUT); sdio_release_host(func); if (ret) dev_err(glue->dev, "sdio write failed (%d)\n", ret); } return ret; } static void ssv6xxx_sdio_irq_handler(struct sdio_func *func) { int status; struct ssv6xxx_sdio_glue *glue = sdio_get_drvdata(func); struct ssv6xxx_platform_data *pwlan_data = &wlan_data; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return; if ( glue != NULL && glue->irq_handler != NULL ) { atomic_set(&pwlan_data->irq_handling, 1); sdio_release_host(func); if ( glue->irq_handler != NULL ) status = glue->irq_handler(0,glue->irq_dev); sdio_claim_host(func); atomic_set(&pwlan_data->irq_handling, 0); } } static void ssv6xxx_sdio_irq_setmask(struct device *child,int mask) { int err_ret; struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); sdio_writeb(func,mask, REG_INT_MASK, &err_ret); sdio_release_host(func); } } static void ssv6xxx_sdio_irq_trigger(struct device *child) { int err_ret; struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); sdio_writeb(func,0x2, REG_INT_TRIGGER, &err_ret); sdio_release_host(func); } } static int ssv6xxx_sdio_irq_getmask(struct device *child, u32 *mask) { u8 imask = 0; int ret = (-1); struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return ret; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); imask = sdio_readb(func,REG_INT_MASK, &ret); *mask = imask; sdio_release_host(func); } return ret; } static void ssv6xxx_sdio_irq_enable(struct device *child) { struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; int ret; struct ssv6xxx_platform_data *pwlan_data = &wlan_data; if ( (pwlan_data->is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); sdio_claim_host(func); ret = sdio_claim_irq(func, ssv6xxx_sdio_irq_handler); if (ret) dev_err(child->parent, "Failed to claim sdio irq: %d\n", ret); sdio_release_host(func); } printk("ssv6xxx_sdio_irq_enable\n"); } static void ssv6xxx_sdio_irq_disable(struct device *child,bool iswaitirq) { struct ssv6xxx_sdio_glue *glue = NULL; struct sdio_func *func; struct ssv6xxx_platform_data *pwlan_data = &wlan_data; int ret; printk("ssv6xxx_sdio_irq_disable\n"); if ( (wlan_data.is_enabled == false) || (child->parent == NULL)) return; glue = dev_get_drvdata(child->parent); if ( (glue == NULL) || (glue->dev_ready == false) || (glue->dev == NULL)) return; { func = dev_to_sdio_func(glue->dev); if(func == NULL){ printk("func == NULL\n"); return; } sdio_claim_host(func); while(atomic_read(&pwlan_data->irq_handling)){ sdio_release_host(func); schedule_timeout(HZ / 10); sdio_claim_host(func); } ret = sdio_release_irq(func); if (ret) dev_err(child->parent, "Failed to release sdio irq: %d\n", ret); sdio_release_host(func); } } static void ssv6xxx_sdio_irq_request(struct device *child,irq_handler_t irq_handler,void *irq_dev) { struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; bool isIrqEn = false; if ( (wlan_data.is_enabled == false) || (glue == NULL) || (glue->dev_ready == false)) return; if ( glue != NULL ) { func = dev_to_sdio_func(glue->dev); glue->irq_handler = irq_handler; glue->irq_dev = irq_dev; if (isIrqEn ) { ssv6xxx_sdio_irq_enable(child); } } } static void ssv6xxx_sdio_read_parameter(struct sdio_func *func, struct ssv6xxx_sdio_glue *glue) { int err_ret; sdio_claim_host(func); glue->dataIOPort = 0; glue->dataIOPort = glue->dataIOPort | (sdio_readb(func, REG_DATA_IO_PORT_0, &err_ret) << ( 8*0 )); glue->dataIOPort = glue->dataIOPort | (sdio_readb(func, REG_DATA_IO_PORT_1, &err_ret) << ( 8*1 )); glue->dataIOPort = glue->dataIOPort | (sdio_readb(func, REG_DATA_IO_PORT_2, &err_ret) << ( 8*2 )); glue->regIOPort = 0; glue->regIOPort = glue->regIOPort | (sdio_readb(func, REG_REG_IO_PORT_0, &err_ret) << ( 8*0 )); glue->regIOPort = glue->regIOPort | (sdio_readb(func, REG_REG_IO_PORT_1, &err_ret) << ( 8*1 )); glue->regIOPort = glue->regIOPort | (sdio_readb(func, REG_REG_IO_PORT_2, &err_ret) << ( 8*2 )); dev_err(&func->dev, "dataIOPort 0x%x regIOPort 0x%x\n",glue->dataIOPort,glue->regIOPort); #ifdef CONFIG_PLATFORM_SDIO_BLOCK_SIZE err_ret = sdio_set_block_size(func,CONFIG_PLATFORM_SDIO_BLOCK_SIZE); #else err_ret = sdio_set_block_size(func,SDIO_DEF_BLOCK_SIZE); #endif if (err_ret != 0) { printk("SDIO setting SDIO_DEF_BLOCK_SIZE fail!!\n"); } #ifdef CONFIG_PLATFORM_SDIO_OUTPUT_TIMING sdio_writeb(func, CONFIG_PLATFORM_SDIO_OUTPUT_TIMING,REG_OUTPUT_TIMING_REG, &err_ret); #else sdio_writeb(func, SDIO_DEF_OUTPUT_TIMING,REG_OUTPUT_TIMING_REG, &err_ret); #endif sdio_writeb(func, 0x00,REG_Fn1_STATUS, &err_ret); #if 0 sdio_writeb(func,SDIO_TX_ALLOC_SIZE_SHIFT|SDIO_TX_ALLOC_ENABLE,REG_SDIO_TX_ALLOC_SHIFT, &err_ret); #endif sdio_release_host(func); } static void ssv6xxx_do_sdio_wakeup(struct sdio_func *func) { int err_ret; if(func != NULL) { sdio_claim_host(func); sdio_writeb(func, 0x01, REG_PMU_WAKEUP, &err_ret); mdelay(10); sdio_writeb(func, 0x00, REG_PMU_WAKEUP, &err_ret); sdio_release_host(func); } } static void ssv6xxx_sdio_pmu_wakeup(struct device *child) { struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; if (glue != NULL) { func = dev_to_sdio_func(glue->dev); ssv6xxx_do_sdio_wakeup(func); } } static bool ssv6xxx_sdio_support_scatter(struct device *child) { struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; bool support = false; do{ if(!glue){ dev_err(child->parent, "ssv6xxx_sdio_enable_scatter glue == NULL!!!\n"); break; } func = dev_to_sdio_func(glue->dev); #if LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0) if (func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) { dev_err(child->parent, "host controller only supports scatter of :%d entries, driver need: %d\n", func->card->host->max_segs, MAX_SCATTER_ENTRIES_PER_REQ); break; } support = true; #endif }while(0); return support; } static void ssv6xxx_sdio_setup_scat_data(struct sdio_scatter_req *scat_req, struct mmc_data *data) { struct scatterlist *sg; int i; data->blksz = SDIO_DEF_BLOCK_SIZE; data->blocks = scat_req->len / SDIO_DEF_BLOCK_SIZE; printk("scatter: (%s) (block len: %d, block count: %d) , (tot:%d,sg:%d)\n", (scat_req->req & SDIO_WRITE) ? "WR" : "RD", data->blksz, data->blocks, scat_req->len, scat_req->scat_entries); data->flags = (scat_req->req & SDIO_WRITE) ? MMC_DATA_WRITE : MMC_DATA_READ; sg = scat_req->sgentries; sg_init_table(sg, scat_req->scat_entries); for (i = 0; i < scat_req->scat_entries; i++, sg++) { printk("%d: addr:0x%p, len:%d\n", i, scat_req->scat_list[i].buf, scat_req->scat_list[i].len); sg_set_buf(sg, scat_req->scat_list[i].buf, scat_req->scat_list[i].len); } data->sg = scat_req->sgentries; data->sg_len = scat_req->scat_entries; } static inline void ssv6xxx_sdio_set_cmd53_arg(u32 *arg, u8 rw, u8 func, u8 mode, u8 opcode, u32 addr, u16 blksz) { *arg = (((rw & 1) << 31) | ((func & 0x7) << 28) | ((mode & 1) << 27) | ((opcode & 1) << 26) | ((addr & 0x1FFFF) << 9) | (blksz & 0x1FF)); } static int ssv6xxx_sdio_rw_scatter(struct device *child, struct sdio_scatter_req *scat_req) { struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func; struct mmc_request mmc_req; struct mmc_command cmd; struct mmc_data data; u8 opcode, rw; int status = 1; do{ if(!glue){ dev_err(child->parent, "ssv6xxx_sdio_enable_scatter glue == NULL!!!\n"); break; } func = dev_to_sdio_func(glue->dev); memset(&mmc_req, 0, sizeof(struct mmc_request)); memset(&cmd, 0, sizeof(struct mmc_command)); memset(&data, 0, sizeof(struct mmc_data)); ssv6xxx_sdio_setup_scat_data(scat_req, &data); opcode = 0; rw = (scat_req->req & SDIO_WRITE) ? CMD53_ARG_WRITE : CMD53_ARG_READ; ssv6xxx_sdio_set_cmd53_arg(&cmd.arg, rw, func->num, CMD53_ARG_BLOCK_BASIS, opcode, glue->dataIOPort, data.blocks); cmd.opcode = SD_IO_RW_EXTENDED; cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; mmc_req.cmd = &cmd; mmc_req.data = &data; mmc_set_data_timeout(&data, func->card); mmc_wait_for_req(func->card->host, &mmc_req); status = cmd.error ? cmd.error : data.error; if (cmd.error) return cmd.error; if (data.error) return data.error; }while(0); return status; } static void ssv6xxx_set_sdio_clk(struct sdio_func *func,u32 sdio_hz) { struct mmc_host *host; host = func->card->host; if(sdio_hz < host->f_min ) sdio_hz = host->f_min; else if(sdio_hz > host->f_max) sdio_hz = host->f_max; printk("%s:set sdio clk %dHz\n", __FUNCTION__,sdio_hz); sdio_claim_host(func); host->ios.clock = sdio_hz; host->ops->set_ios(host, &host->ios); mdelay(20); sdio_release_host(func); } static void ssv6xxx_low_sdio_clk(struct sdio_func *func) { ssv6xxx_set_sdio_clk(func,LOW_SPEED_SDIO_CLOCK); } static void ssv6xxx_high_sdio_clk(struct sdio_func *func) { #ifndef SDIO_USE_SLOW_CLOCK ssv6xxx_set_sdio_clk(func,HIGH_SPEED_SDIO_CLOCK); #endif } static struct ssv6xxx_hwif_ops sdio_ops = { .read = ssv6xxx_sdio_read, .write = ssv6xxx_sdio_write, .readreg = ssv6xxx_sdio_read_reg, .writereg = ssv6xxx_sdio_write_reg, #ifdef ENABLE_WAKE_IO_ISR_WHEN_HCI_ENQUEUE .trigger_tx_rx = ssv6xxx_sdio_trigger_tx_rx, #endif .irq_getmask = ssv6xxx_sdio_irq_getmask, .irq_setmask = ssv6xxx_sdio_irq_setmask, .irq_enable = ssv6xxx_sdio_irq_enable, .irq_disable = ssv6xxx_sdio_irq_disable, .irq_getstatus = ssv6xxx_sdio_irq_getstatus, .irq_request = ssv6xxx_sdio_irq_request, .irq_trigger = ssv6xxx_sdio_irq_trigger, .pmu_wakeup = ssv6xxx_sdio_pmu_wakeup, .load_fw = ssv6xxx_sdio_load_firmware, .cmd52_read = ssv6xxx_sdio_cmd52_read, .cmd52_write = ssv6xxx_sdio_cmd52_write, .support_scatter = ssv6xxx_sdio_support_scatter, .rw_scatter = ssv6xxx_sdio_rw_scatter, .is_ready = ssv6xxx_is_ready, .write_sram = ssv6xxx_sdio_write_sram, .interface_reset = ssv6xxx_sdio_reset, }; #ifdef CONFIG_PCIEASPM #include #include static int cabrio_sdio_pm_check(struct sdio_func *func) { struct pci_dev *pci_dev = NULL; struct mmc_card *card = func->card; struct mmc_host *host = card->host; if (strcmp(host->parent->bus->name, "pci")) { dev_info(&func->dev, "SDIO host is not PCI device, but \"%s\".", host->parent->bus->name); return 0; } for_each_pci_dev(pci_dev) { if ( ((pci_dev->class >> 8) != PCI_CLASS_SYSTEM_SDHCI) && ( (pci_dev->driver == NULL) || (strcmp(pci_dev->driver->name, "sdhci-pci") != 0))) continue; if (pci_is_pcie(pci_dev)) { u8 aspm; int pos; pos = pci_pcie_cap(pci_dev); if (pos) { struct pci_dev *parent = pci_dev->bus->self; pci_read_config_byte(pci_dev, pos + PCI_EXP_LNKCTL, &aspm); aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); pci_write_config_byte(pci_dev, pos + PCI_EXP_LNKCTL, aspm); pos = pci_pcie_cap(parent); pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm); aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); pci_write_config_byte(parent, pos + PCI_EXP_LNKCTL, aspm); dev_info(&pci_dev->dev, "Clear PCI-E device and its parent link state L0S and L1 and CLKPM.\n"); } } } return 0; } #endif static int ssv6xxx_sdio_power_on(struct ssv6xxx_platform_data * pdata, struct sdio_func *func) { int ret = 0; if (pdata->is_enabled == true) return 0; printk("ssv6xxx_sdio_power_on\n"); sdio_claim_host(func); ret = sdio_enable_func(func); sdio_release_host(func); if (ret) { printk("Unable to enable sdio func: %d)\n", ret); return ret; } msleep(10); pdata->is_enabled = true; return ret; } static int ssv6xxx_sdio_power_off(struct ssv6xxx_platform_data * pdata, struct sdio_func *func) { int ret; if (pdata->is_enabled == false) return 0; printk("ssv6xxx_sdio_power_off\n"); sdio_claim_host(func); ret = sdio_disable_func(func); sdio_release_host(func); if (ret) return ret; pdata->is_enabled = false; return ret; } int ssv6xxx_get_dev_status(void) { return ssv6xxx_sdio_status; } EXPORT_SYMBOL(ssv6xxx_get_dev_status); #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) static int __devinit ssv6xxx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) #else static int ssv6xxx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) #endif { struct ssv6xxx_platform_data *pwlan_data = &wlan_data; struct ssv6xxx_sdio_glue *glue; int ret = -ENOMEM; const char *chip_family = "ssv6200"; if (ssv_devicetype != 0) { printk(KERN_INFO "Not using SSV6200 normal SDIO driver.\n"); return -ENODEV; } printk(KERN_INFO "=======================================\n"); printk(KERN_INFO "== RUN SDIO ==\n"); printk(KERN_INFO "=======================================\n"); if (func->num != 0x01) return -ENODEV; glue = kzalloc(sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&func->dev, "can't allocate glue\n"); goto out; } ssv6xxx_sdio_status = 1; ssv6xxx_low_sdio_clk(func); #ifdef CONFIG_FW_ALIGNMENT_CHECK glue->dmaSkb=__dev_alloc_skb(SDIO_DMA_BUFFER_LEN , GFP_KERNEL); #endif #ifdef CONFIG_PM glue->cmd_skb=__dev_alloc_skb(SDIO_COMMAND_BUFFER_LEN , GFP_KERNEL); #endif memset(pwlan_data, 0, sizeof(struct ssv6xxx_platform_data)); atomic_set(&pwlan_data->irq_handling, 0); glue->dev = &func->dev; func->card->quirks |= MMC_QUIRK_LENIENT_FN0; func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; glue->dev_ready = true; pwlan_data->vendor = func->vendor; pwlan_data->device = func->device; dev_err(glue->dev, "vendor = 0x%x device = 0x%x\n", pwlan_data->vendor,pwlan_data->device); #ifdef CONFIG_PCIEASPM cabrio_sdio_pm_check(func); #endif pwlan_data->ops = &sdio_ops; sdio_set_drvdata(func, glue); #ifdef CONFIG_PM ssv6xxx_do_sdio_wakeup(func); #endif ssv6xxx_sdio_power_on(pwlan_data, func); ssv6xxx_sdio_read_parameter(func,glue); glue->core = platform_device_alloc(chip_family, -1); if (!glue->core) { dev_err(glue->dev, "can't allocate platform_device"); ret = -ENOMEM; goto out_free_glue; } glue->core->dev.parent = &func->dev; ret = platform_device_add_data(glue->core, pwlan_data, sizeof(*pwlan_data)); if (ret) { dev_err(glue->dev, "can't add platform data\n"); goto out_dev_put; } ret = platform_device_add(glue->core); if (ret) { dev_err(glue->dev, "can't add platform device\n"); goto out_dev_put; } ssv6xxx_sdio_irq_setmask(&glue->core->dev,0xff); #if 0 ssv6xxx_sdio_irq_enable(&glue->core->dev); #else #endif #if 0 glue->dev->platform_data = (void *)pwlan_data; ret = ssv6xxx_dev_probe(glue->dev); if (ret) { dev_err(glue->dev, "failed to initial ssv6xxx device !!\n"); platform_device_del(glue->core); goto out_dev_put; } #endif ssv6xxx_ifdebug_info[0] = (void *)&glue->core->dev; ssv6xxx_ifdebug_info[1] = (void *)glue->core; ssv6xxx_ifdebug_info[2] = (void *)&sdio_ops; return 0; out_dev_put: platform_device_put(glue->core); out_free_glue: kfree(glue); out: return ret; } #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) static void __devexit ssv6xxx_sdio_remove(struct sdio_func *func) #else static void ssv6xxx_sdio_remove(struct sdio_func *func) #endif { struct ssv6xxx_sdio_glue *glue = sdio_get_drvdata(func); struct ssv6xxx_platform_data *pwlan_data = &wlan_data; printk("ssv6xxx_sdio_remove..........\n"); ssv6xxx_sdio_status = 0; if ( glue ) { printk("ssv6xxx_sdio_remove - ssv6xxx_sdio_irq_disable\n"); ssv6xxx_sdio_irq_disable(&glue->core->dev,false); glue->dev_ready = false; #if 0 ssv6xxx_dev_remove(glue->dev); #endif ssv6xxx_low_sdio_clk(func); #ifdef CONFIG_FW_ALIGNMENT_CHECK if(glue->dmaSkb != NULL) dev_kfree_skb(glue->dmaSkb); #endif printk("ssv6xxx_sdio_remove - disable mask\n"); ssv6xxx_sdio_irq_setmask(&glue->core->dev,0xff); #ifdef CONFIG_PM ssv6xxx_sdio_trigger_pmu(glue->dev); if(glue->cmd_skb != NULL) dev_kfree_skb(glue->cmd_skb); #endif ssv6xxx_sdio_power_off(pwlan_data, func); printk("platform_device_del \n"); platform_device_del(glue->core); printk("platform_device_put \n"); platform_device_put(glue->core); kfree(glue); } sdio_set_drvdata(func, NULL); printk("ssv6xxx_sdio_remove leave..........\n"); } #ifdef CONFIG_PM static int ssv6xxx_sdio_trigger_pmu(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct ssv6xxx_sdio_glue *glue = sdio_get_drvdata(func); struct cfg_host_cmd *host_cmd; int writesize; int ret = 0; void *tempPointer; #ifdef SSV_WAKEUP_HOST if(ssv6xxx_sdio_write_reg(dev, ADR_RX_FLOW_MNG, M_ENG_MACRX|(M_ENG_CPU<<4)|(M_ENG_HWHCI<<8))); if(ssv6xxx_sdio_write_reg(dev, ADR_RX_FLOW_DATA, M_ENG_MACRX|(M_ENG_CPU<<4)|(M_ENG_HWHCI<<8))); if(ssv6xxx_sdio_write_reg(dev, ADR_MRX_FLT_TB0+6*4, (sc->mac_deci_tbl[6]|1))); #else if(ssv6xxx_sdio_write_reg(dev, ADR_RX_FLOW_MNG, M_ENG_MACRX|(M_ENG_TRASH_CAN<<4))); if(ssv6xxx_sdio_write_reg(dev, ADR_RX_FLOW_DATA, M_ENG_MACRX|(M_ENG_TRASH_CAN<<4))); if(ssv6xxx_sdio_write_reg(dev, ADR_RX_FLOW_CTRL, M_ENG_MACRX|(M_ENG_TRASH_CAN<<4))); #endif host_cmd = (struct cfg_host_cmd *)glue->cmd_skb->data; host_cmd->c_type = HOST_CMD; host_cmd->RSVD0 = 0; host_cmd->h_cmd = (u8)SSV6XXX_HOST_CMD_PS; host_cmd->len = sizeof(struct cfg_host_cmd); #ifdef SSV_WAKEUP_HOST host_cmd->dummy = sc->ps_aid; #else host_cmd->dummy = 0; #endif { tempPointer = glue->cmd_skb->data; sdio_claim_host(func); writesize = sdio_align_size(func,sizeof(struct cfg_host_cmd)); do { ret = sdio_memcpy_toio(func, glue->dataIOPort, tempPointer, writesize); if ( ret == -EILSEQ || ret == -ETIMEDOUT ) { ret = -1; break; } else { if(ret) dev_err(glue->dev,"Unexpected return value ret=[%d]\n",ret); } } while( ret == -EILSEQ || ret == -ETIMEDOUT); sdio_release_host(func); if (ret) dev_err(glue->dev, "sdio write failed (%d)\n", ret); } return ret; } static void ssv6xxx_sdio_reset(struct device *child) { struct ssv6xxx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func = dev_to_sdio_func(glue->dev); printk("%s\n", __FUNCTION__); if(glue == NULL || glue->dev == NULL || func == NULL) return; ssv6xxx_sdio_trigger_pmu(glue->dev); ssv6xxx_do_sdio_wakeup( func); } #ifdef AML_WIFI_MAC extern void sdio_reinit(void); extern void extern_wifi_set_enable(int is_on); #endif static int ssv6xxx_sdio_suspend(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); mmc_pm_flag_t flags = sdio_get_host_pm_caps(func); #ifdef AML_WIFI_MAC if (!(flags & MMC_PM_KEEP_POWER) || sdio_sr_bhvr == SUSPEND_RESUME_1) { printk("%s : module will not get power support during suspend. %u\n", __func__, sdio_sr_bhvr); ssv6xxx_deinit_prepare(); ssv6xxx_sdio_remove(func); mdelay(100); extern_wifi_set_enable(0); mdelay(10); return 0; }else #endif { int ret = 0; dev_info(dev, "%s: suspend: PM flags = 0x%x\n", sdio_func_id(func), flags); ssv6xxx_low_sdio_clk(func); ret = ssv6xxx_sdio_trigger_pmu(dev); if (ret) printk("ssv6xxx_sdio_trigger_pmu fail!!\n"); if (!(flags & MMC_PM_KEEP_POWER)) { dev_err(dev, "%s: cannot remain alive while host is suspended\n", sdio_func_id(func)); } ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (ret) return ret; mdelay(10); #ifdef SSV_WAKEUP_HOST ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); #endif #if 0 if (softc->wow_enabled) { sdio_flags = sdio_get_host_pm_caps(func); if (!(sdio_flags & MMC_PM_KEEP_POWER)) { dev_err(dev, "can't keep power while host " "is suspended\n"); ret = -EINVAL; goto out; } ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (ret) { dev_err(dev, "error while trying to keep power\n"); goto out; } }else{ ssv6xxx_sdio_irq_disable(&glue->core->dev,true); } #endif return ret; } } static int ssv6xxx_sdio_resume(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); #ifdef AML_WIFI_MAC mmc_pm_flag_t flags = sdio_get_host_pm_caps(func); mdelay(100); if (!(flags & MMC_PM_KEEP_POWER) || sdio_sr_bhvr == SUSPEND_RESUME_1) { printk("%s : module is reset, run probe now !! %u\n", __func__, sdio_sr_bhvr); extern_wifi_set_enable(1); mdelay(100); sdio_reinit(); mdelay(150); ssv6xxx_sdio_probe(func, NULL); return 0; }else #endif { printk("ssv6xxx_sdio_resume\n"); { ssv6xxx_do_sdio_wakeup(func); mdelay(10); ssv6xxx_high_sdio_clk(func); mdelay(10); } } return 0; } static const struct dev_pm_ops ssv6xxx_sdio_pm_ops = { .suspend = ssv6xxx_sdio_suspend, .resume = ssv6xxx_sdio_resume, }; #endif #ifdef AML_WIFI_MAC static void ssv6xxx_sdio_shutdown(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); printk("%s shutdown_flags:%d \n", __func__,shutdown_flags); switch(shutdown_flags){ case SSV_SYS_REBOOT : case SSV_SYS_HALF: printk("%s ,system reboot ..\n", __func__); break; case SSV_SYS_POWER_OFF : printk("%s ,system shutdown .. \n", __func__); ssv6xxx_deinit_prepare(); ssv6xxx_sdio_remove(func); mdelay(100); extern_wifi_set_enable(0); mdelay(100); break; default: printk("%s,unknown event code ..", __func__); } } static int ssv6xxx_reboot_notify(struct notifier_block *nb, unsigned long event, void *p) { printk("%s, code = %ld \n",__FUNCTION__,event); switch (event){ case SYS_DOWN: shutdown_flags = SYS_DOWN; break; case SYS_HALT: shutdown_flags = SYS_HALT; break; case SYS_POWER_OFF: shutdown_flags = SYS_POWER_OFF; break; default: shutdown_flags = event; break; } return NOTIFY_DONE; } static struct notifier_block ssv6xxx_reboot_notifier = { .notifier_call = ssv6xxx_reboot_notify, .next = NULL, .priority = 0, }; #endif struct sdio_driver ssv6xxx_sdio_driver = { .name = "SSV6XXX_SDIO", .id_table = ssv6xxx_sdio_devices, .probe = ssv6xxx_sdio_probe, #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) .remove = __devexit_p(ssv6xxx_sdio_remove), #else .remove = ssv6xxx_sdio_remove, #endif #ifdef CONFIG_PM .drv = { .pm = &ssv6xxx_sdio_pm_ops, #ifdef AML_WIFI_MAC .shutdown = ssv6xxx_sdio_shutdown, #endif }, #endif }; EXPORT_SYMBOL(ssv6xxx_sdio_driver); #if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO)) int ssv6xxx_sdio_init(void) #else static int __init ssv6xxx_sdio_init(void) #endif { printk(KERN_INFO "ssv6xxx_sdio_init\n"); #ifdef AML_WIFI_MAC register_reboot_notifier(&ssv6xxx_reboot_notifier); #endif return sdio_register_driver(&ssv6xxx_sdio_driver); } #if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO)) void ssv6xxx_sdio_exit(void) #else static void __exit ssv6xxx_sdio_exit(void) #endif { printk(KERN_INFO "ssv6xxx_sdio_exit\n"); #ifdef AML_WIFI_MAC unregister_reboot_notifier(&ssv6xxx_reboot_notifier); #endif sdio_unregister_driver(&ssv6xxx_sdio_driver); } #if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO)) EXPORT_SYMBOL(ssv6xxx_sdio_init); EXPORT_SYMBOL(ssv6xxx_sdio_exit); #else module_init(ssv6xxx_sdio_init); module_exit(ssv6xxx_sdio_exit); #endif MODULE_LICENSE("GPL");