Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
/*
 * 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 <http://www.gnu.org/licenses/>.
 */

#include <linux/irq.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/platform_device.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include "sdio_def.h"
#include <linux/pm_runtime.h>
#include <linux/version.h>
#include <linux/firmware.h>
#include <linux/reboot.h>
#ifdef CONFIG_FW_ALIGNMENT_CHECK
#include <linux/skbuff.h>
#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 <linux/printk.h>
#else
#include <linux/kernel.h>
#endif
#include <ssv6200.h>
#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<jblk;j++)
                    {
                        ret = ssv6xxx_sdio_write_sram(child, sram_addr, (u8 *)(fw_buffer+j*128), 128);
                        if (ret){
                            printk("ssv6xxx_sdio_write_sram failed!!\n");
                            break;
                        }
                        sram_addr += 128;
                    }
                }else{
                    ret = ssv6xxx_sdio_write_sram(child, sram_addr, (u8 *)fw_buffer, FW_BLOCK_SIZE);
                    if (ret)
                        break;
                    sram_addr += FW_BLOCK_SIZE;
                }
                word_count = (len / sizeof(u32));
                fw_data32 = (u32 *)fw_buffer;
                for (i = 0; i < word_count; i++){
                    checksum += fw_data32[i];
                }
                memset(fw_buffer, 0xA5, FW_BLOCK_SIZE);
            }
            if(res_size)
            {
                u32 cks_blk_cnt,cks_blk_res;
                cks_blk_cnt = res_size / CHECKSUM_BLOCK_SIZE;
                cks_blk_res = res_size % CHECKSUM_BLOCK_SIZE;
                if(0)
                {
                    jblk = ((cks_blk_cnt+1)*CHECKSUM_BLOCK_SIZE)/128;
                    for(j=0;j<jblk;j++){
                        ret = ssv6xxx_sdio_write_sram(child, sram_addr, (u8 *)(fw_buffer+j*128), 128);
                        sram_addr += 128;
                    }
                }else{
                    ret = ssv6xxx_sdio_write_sram(child, sram_addr, (u8 *)fw_buffer, (cks_blk_cnt+1)*CHECKSUM_BLOCK_SIZE);
                }
                word_count = (cks_blk_cnt * CHECKSUM_BLOCK_SIZE / sizeof(u32));
                fw_data32 = (u32 *)fw_buffer;
                for (i = 0; i < word_count; i++)
                    checksum += *fw_data32++;
                if(cks_blk_res)
                {
                    word_count = (CHECKSUM_BLOCK_SIZE / sizeof(u32));
                    for (i = 0; i < word_count; i++) {
                        checksum += *fw_data32++;
                    }
                }
            }
            checksum = ((checksum >> 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 <linux/pci.h>
#include <linux/pci-aspm.h>
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");