1a0a954b1SSergio Paracuellos // SPDX-License-Identifier: GPL-2.0 213a9930dSWolfram Sang /* 313a9930dSWolfram Sang * Driver for KeyStream, KS7010 based SDIO cards. 413a9930dSWolfram Sang * 513a9930dSWolfram Sang * Copyright (C) 2006-2008 KeyStream Corp. 613a9930dSWolfram Sang * Copyright (C) 2009 Renesas Technology Corp. 7c5d9a030SWolfram Sang * Copyright (C) 2016 Sang Engineering, Wolfram Sang 813a9930dSWolfram Sang */ 913a9930dSWolfram Sang 10fa3fd846SSergio Paracuellos #include <linux/atomic.h> 111c013a5cSWolfram Sang #include <linux/firmware.h> 12fa3fd846SSergio Paracuellos #include <linux/jiffies.h> 1313a9930dSWolfram Sang #include <linux/mmc/card.h> 1413a9930dSWolfram Sang #include <linux/mmc/sdio_func.h> 15fa3fd846SSergio Paracuellos #include <linux/module.h> 161c013a5cSWolfram Sang #include <linux/workqueue.h> 1713a9930dSWolfram Sang #include "ks_wlan.h" 1813a9930dSWolfram Sang #include "ks_hostif.h" 1913a9930dSWolfram Sang 202c54ee54SSergio Paracuellos #define ROM_FILE "ks7010sd.rom" 2148ecb225SSergio Paracuellos 2248ecb225SSergio Paracuellos /* SDIO KeyStream vendor and device */ 2348ecb225SSergio Paracuellos #define SDIO_VENDOR_ID_KS_CODE_A 0x005b 2448ecb225SSergio Paracuellos #define SDIO_VENDOR_ID_KS_CODE_B 0x0023 2548ecb225SSergio Paracuellos 2648ecb225SSergio Paracuellos /* Older sources suggest earlier versions were named 7910 or 79xx */ 2748ecb225SSergio Paracuellos #define SDIO_DEVICE_ID_KS_7010 0x7910 2848ecb225SSergio Paracuellos 2948ecb225SSergio Paracuellos /* Read/Write Status Register */ 30a704a1bcSSergio Paracuellos #define READ_STATUS_REG 0x000000 31a704a1bcSSergio Paracuellos #define WRITE_STATUS_REG 0x00000C 3248ecb225SSergio Paracuellos enum reg_status_type { 3348ecb225SSergio Paracuellos REG_STATUS_BUSY, 3448ecb225SSergio Paracuellos REG_STATUS_IDLE 3548ecb225SSergio Paracuellos }; 3648ecb225SSergio Paracuellos 3748ecb225SSergio Paracuellos /* Read Index Register */ 38a704a1bcSSergio Paracuellos #define READ_INDEX_REG 0x000004 3948ecb225SSergio Paracuellos 4048ecb225SSergio Paracuellos /* Read Data Size Register */ 41a704a1bcSSergio Paracuellos #define READ_DATA_SIZE_REG 0x000008 4248ecb225SSergio Paracuellos 4348ecb225SSergio Paracuellos /* Write Index Register */ 44a704a1bcSSergio Paracuellos #define WRITE_INDEX_REG 0x000010 4548ecb225SSergio Paracuellos 46004e43c2SSergio Paracuellos /* 47004e43c2SSergio Paracuellos * Write Status/Read Data Size Register 4848ecb225SSergio Paracuellos * for network packet (less than 2048 bytes data) 4948ecb225SSergio Paracuellos */ 50a704a1bcSSergio Paracuellos #define WSTATUS_RSIZE_REG 0x000014 51004e43c2SSergio Paracuellos 52004e43c2SSergio Paracuellos /* Write Status Register value */ 53004e43c2SSergio Paracuellos #define WSTATUS_MASK 0x80 54004e43c2SSergio Paracuellos 55004e43c2SSergio Paracuellos /* Read Data Size Register value [10:4] */ 56004e43c2SSergio Paracuellos #define RSIZE_MASK 0x7F 5748ecb225SSergio Paracuellos 5848ecb225SSergio Paracuellos /* ARM to SD interrupt Enable */ 59a704a1bcSSergio Paracuellos #define INT_ENABLE_REG 0x000020 6048ecb225SSergio Paracuellos /* ARM to SD interrupt Pending */ 61a704a1bcSSergio Paracuellos #define INT_PENDING_REG 0x000024 6248ecb225SSergio Paracuellos 6348ecb225SSergio Paracuellos #define INT_GCR_B BIT(7) 6448ecb225SSergio Paracuellos #define INT_GCR_A BIT(6) 6548ecb225SSergio Paracuellos #define INT_WRITE_STATUS BIT(5) 6648ecb225SSergio Paracuellos #define INT_WRITE_INDEX BIT(4) 6748ecb225SSergio Paracuellos #define INT_WRITE_SIZE BIT(3) 6848ecb225SSergio Paracuellos #define INT_READ_STATUS BIT(2) 6948ecb225SSergio Paracuellos #define INT_READ_INDEX BIT(1) 7048ecb225SSergio Paracuellos #define INT_READ_SIZE BIT(0) 7148ecb225SSergio Paracuellos 7248ecb225SSergio Paracuellos /* General Communication Register A */ 73a704a1bcSSergio Paracuellos #define GCR_A_REG 0x000028 7448ecb225SSergio Paracuellos enum gen_com_reg_a { 7548ecb225SSergio Paracuellos GCR_A_INIT, 7648ecb225SSergio Paracuellos GCR_A_REMAP, 7748ecb225SSergio Paracuellos GCR_A_RUN 7848ecb225SSergio Paracuellos }; 7948ecb225SSergio Paracuellos 8048ecb225SSergio Paracuellos /* General Communication Register B */ 81a704a1bcSSergio Paracuellos #define GCR_B_REG 0x00002C 8248ecb225SSergio Paracuellos enum gen_com_reg_b { 8348ecb225SSergio Paracuellos GCR_B_ACTIVE, 8448ecb225SSergio Paracuellos GCR_B_DOZE 8548ecb225SSergio Paracuellos }; 8648ecb225SSergio Paracuellos 8748ecb225SSergio Paracuellos /* Wakeup Register */ 88a704a1bcSSergio Paracuellos #define WAKEUP_REG 0x008018 8948ecb225SSergio Paracuellos #define WAKEUP_REQ 0x5a 9048ecb225SSergio Paracuellos 9148ecb225SSergio Paracuellos /* AHB Data Window 0x010000-0x01FFFF */ 9248ecb225SSergio Paracuellos #define DATA_WINDOW 0x010000 9348ecb225SSergio Paracuellos #define WINDOW_SIZE (64 * 1024) 9448ecb225SSergio Paracuellos 9548ecb225SSergio Paracuellos #define KS7010_IRAM_ADDRESS 0x06000000 9648ecb225SSergio Paracuellos 9713a9930dSWolfram Sang #define KS7010_IO_BLOCK_SIZE 512 9813a9930dSWolfram Sang 9907511629SSergio Paracuellos /** 10007511629SSergio Paracuellos * struct ks_sdio_card - SDIO device data. 10107511629SSergio Paracuellos * 10207511629SSergio Paracuellos * Structure is used as the &struct sdio_func private data. 10307511629SSergio Paracuellos * 10407511629SSergio Paracuellos * @func: Pointer to the SDIO function device. 10507511629SSergio Paracuellos * @priv: Pointer to the &struct net_device private data. 10607511629SSergio Paracuellos */ 10707511629SSergio Paracuellos struct ks_sdio_card { 10807511629SSergio Paracuellos struct sdio_func *func; 10907511629SSergio Paracuellos struct ks_wlan_private *priv; 11007511629SSergio Paracuellos }; 11107511629SSergio Paracuellos 11207511629SSergio Paracuellos static struct sdio_func *ks7010_to_func(struct ks_wlan_private *priv) 11307511629SSergio Paracuellos { 11407511629SSergio Paracuellos struct ks_sdio_card *ks_sdio = priv->if_hw; 11507511629SSergio Paracuellos 11607511629SSergio Paracuellos return ks_sdio->func; 11707511629SSergio Paracuellos } 11807511629SSergio Paracuellos 119f1e79f4bSTobin C. Harding /* Read single byte from device address into byte (CMD52) */ 120e0ba53a4SSergio Paracuellos static int ks7010_sdio_readb(struct ks_wlan_private *priv, 121e0ba53a4SSergio Paracuellos u32 address, u8 *byte) 122f1e79f4bSTobin C. Harding { 12307511629SSergio Paracuellos struct sdio_func *func = ks7010_to_func(priv); 124f1e79f4bSTobin C. Harding int ret; 125f1e79f4bSTobin C. Harding 126f1e79f4bSTobin C. Harding *byte = sdio_readb(func, address, &ret); 127f1e79f4bSTobin C. Harding 128f1e79f4bSTobin C. Harding return ret; 129f1e79f4bSTobin C. Harding } 130f1e79f4bSTobin C. Harding 131f1e79f4bSTobin C. Harding /* Read length bytes from device address into buffer (CMD53) */ 132e0ba53a4SSergio Paracuellos static int ks7010_sdio_read(struct ks_wlan_private *priv, u32 address, 133e0ba53a4SSergio Paracuellos u8 *buffer, unsigned int length) 1344c0d46d2SWolfram Sang { 13507511629SSergio Paracuellos struct sdio_func *func = ks7010_to_func(priv); 136f1e79f4bSTobin C. Harding 137f1e79f4bSTobin C. Harding return sdio_memcpy_fromio(func, buffer, address, length); 138f1e79f4bSTobin C. Harding } 139f1e79f4bSTobin C. Harding 140f1e79f4bSTobin C. Harding /* Write single byte to device address (CMD52) */ 141f1e79f4bSTobin C. Harding static int ks7010_sdio_writeb(struct ks_wlan_private *priv, 142e0ba53a4SSergio Paracuellos u32 address, u8 byte) 143f1e79f4bSTobin C. Harding { 14407511629SSergio Paracuellos struct sdio_func *func = ks7010_to_func(priv); 1451770ae9dSTobin C. Harding int ret; 1464c0d46d2SWolfram Sang 147f1e79f4bSTobin C. Harding sdio_writeb(func, byte, address, &ret); 1484c0d46d2SWolfram Sang 1491770ae9dSTobin C. Harding return ret; 1501770ae9dSTobin C. Harding } 1514c0d46d2SWolfram Sang 152f1e79f4bSTobin C. Harding /* Write length bytes to device address from buffer (CMD53) */ 153e0ba53a4SSergio Paracuellos static int ks7010_sdio_write(struct ks_wlan_private *priv, u32 address, 154e0ba53a4SSergio Paracuellos u8 *buffer, unsigned int length) 1554c0d46d2SWolfram Sang { 15607511629SSergio Paracuellos struct sdio_func *func = ks7010_to_func(priv); 1574c0d46d2SWolfram Sang 158f1e79f4bSTobin C. Harding return sdio_memcpy_toio(func, address, buffer, length); 1594c0d46d2SWolfram Sang } 1604c0d46d2SWolfram Sang 1614433459aSSergio Paracuellos static void ks_wlan_hw_sleep_doze_request(struct ks_wlan_private *priv) 16213a9930dSWolfram Sang { 1631770ae9dSTobin C. Harding int ret; 16413a9930dSWolfram Sang 16513a9930dSWolfram Sang /* clear request */ 16613a9930dSWolfram Sang atomic_set(&priv->sleepstatus.doze_request, 0); 16713a9930dSWolfram Sang 16813a9930dSWolfram Sang if (atomic_read(&priv->sleepstatus.status) == 0) { 169a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, GCR_B_REG, GCR_B_DOZE); 1701770ae9dSTobin C. Harding if (ret) { 171156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write GCR_B_REG\n"); 172f283dd69STobin C. Harding goto set_sleep_mode; 17313a9930dSWolfram Sang } 17413a9930dSWolfram Sang atomic_set(&priv->sleepstatus.status, 1); 17513a9930dSWolfram Sang priv->last_doze = jiffies; 17613a9930dSWolfram Sang } 17713a9930dSWolfram Sang 178f283dd69STobin C. Harding set_sleep_mode: 17913a9930dSWolfram Sang priv->sleep_mode = atomic_read(&priv->sleepstatus.status); 18013a9930dSWolfram Sang } 18113a9930dSWolfram Sang 1824433459aSSergio Paracuellos static void ks_wlan_hw_sleep_wakeup_request(struct ks_wlan_private *priv) 18313a9930dSWolfram Sang { 1841770ae9dSTobin C. Harding int ret; 18513a9930dSWolfram Sang 18613a9930dSWolfram Sang /* clear request */ 18713a9930dSWolfram Sang atomic_set(&priv->sleepstatus.wakeup_request, 0); 18813a9930dSWolfram Sang 18913a9930dSWolfram Sang if (atomic_read(&priv->sleepstatus.status) == 1) { 190a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, WAKEUP_REG, WAKEUP_REQ); 1911770ae9dSTobin C. Harding if (ret) { 192156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write WAKEUP_REG\n"); 193f283dd69STobin C. Harding goto set_sleep_mode; 19413a9930dSWolfram Sang } 19513a9930dSWolfram Sang atomic_set(&priv->sleepstatus.status, 0); 19613a9930dSWolfram Sang priv->last_wakeup = jiffies; 19713a9930dSWolfram Sang ++priv->wakeup_count; 19813a9930dSWolfram Sang } 19913a9930dSWolfram Sang 200f283dd69STobin C. Harding set_sleep_mode: 20113a9930dSWolfram Sang priv->sleep_mode = atomic_read(&priv->sleepstatus.status); 20213a9930dSWolfram Sang } 20313a9930dSWolfram Sang 204feedcf1aSWolfram Sang void ks_wlan_hw_wakeup_request(struct ks_wlan_private *priv) 20513a9930dSWolfram Sang { 2061770ae9dSTobin C. Harding int ret; 20713a9930dSWolfram Sang 20813a9930dSWolfram Sang if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { 209a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, WAKEUP_REG, WAKEUP_REQ); 2101770ae9dSTobin C. Harding if (ret) 211156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write WAKEUP_REG\n"); 21253638cefSsayli karnik 21313a9930dSWolfram Sang priv->last_wakeup = jiffies; 21413a9930dSWolfram Sang ++priv->wakeup_count; 21513a9930dSWolfram Sang } 21613a9930dSWolfram Sang } 21713a9930dSWolfram Sang 218fa740a9eSTobin C. Harding static void _ks_wlan_hw_power_save(struct ks_wlan_private *priv) 21913a9930dSWolfram Sang { 2209d418fa8SSergio Paracuellos u8 byte; 221f7172487STobin C. Harding int ret; 22213a9930dSWolfram Sang 2238fb8e05cSTobin C. Harding if (priv->reg.power_mgmt == POWER_MGMT_ACTIVE) 224fa740a9eSTobin C. Harding return; 22513a9930dSWolfram Sang 226482c03c7STobin C. Harding if (priv->reg.operation_mode != MODE_INFRASTRUCTURE) 227482c03c7STobin C. Harding return; 228482c03c7STobin C. Harding 2290e24eb8aSTobin C. Harding if (!is_connect_status(priv->connect_status)) 230fa740a9eSTobin C. Harding return; 231d5f1db31STobin C. Harding 232d5f1db31STobin C. Harding if (priv->dev_state != DEVICE_STATE_SLEEP) 233fa740a9eSTobin C. Harding return; 234d5f1db31STobin C. Harding 2353188bc09STobin C. Harding if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) 236fa740a9eSTobin C. Harding return; 2373188bc09STobin C. Harding 238832ec535SSergio Paracuellos netdev_dbg(priv->net_dev, 239832ec535SSergio Paracuellos "STATUS:\n" 240832ec535SSergio Paracuellos "- psstatus.status = %d\n" 241832ec535SSergio Paracuellos "- psstatus.confirm_wait = %d\n" 242832ec535SSergio Paracuellos "- psstatus.snooze_guard = %d\n" 243832ec535SSergio Paracuellos "- txq_count = %d\n", 24413a9930dSWolfram Sang atomic_read(&priv->psstatus.status), 24513a9930dSWolfram Sang atomic_read(&priv->psstatus.confirm_wait), 24613a9930dSWolfram Sang atomic_read(&priv->psstatus.snooze_guard), 247e6bc5053SSergio Paracuellos txq_count(priv)); 24813a9930dSWolfram Sang 249fa740a9eSTobin C. Harding if (atomic_read(&priv->psstatus.confirm_wait) || 250fa740a9eSTobin C. Harding atomic_read(&priv->psstatus.snooze_guard) || 251e6bc5053SSergio Paracuellos txq_has_space(priv)) { 25218bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 0); 253fa740a9eSTobin C. Harding return; 254fa740a9eSTobin C. Harding } 255fa740a9eSTobin C. Harding 256a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, INT_PENDING_REG, &byte); 257f7172487STobin C. Harding if (ret) { 258156f2703SSergio Paracuellos netdev_err(priv->net_dev, "read INT_PENDING_REG\n"); 259f8641485STobin C. Harding goto queue_delayed_work; 26013a9930dSWolfram Sang } 261f1e79f4bSTobin C. Harding if (byte) 262f8641485STobin C. Harding goto queue_delayed_work; 263fa740a9eSTobin C. Harding 264a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, GCR_B_REG, GCR_B_DOZE); 265f7172487STobin C. Harding if (ret) { 266156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write GCR_B_REG\n"); 267f8641485STobin C. Harding goto queue_delayed_work; 26813a9930dSWolfram Sang } 2699f9d7030STobin C. Harding atomic_set(&priv->psstatus.status, PS_SNOOZE); 27013a9930dSWolfram Sang 271fa740a9eSTobin C. Harding return; 272f8641485STobin C. Harding 273f8641485STobin C. Harding queue_delayed_work: 27418bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 1); 27513a9930dSWolfram Sang } 27613a9930dSWolfram Sang 277feedcf1aSWolfram Sang int ks_wlan_hw_power_save(struct ks_wlan_private *priv) 27813a9930dSWolfram Sang { 27918bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 1); 28013a9930dSWolfram Sang return 0; 28113a9930dSWolfram Sang } 28213a9930dSWolfram Sang 283cdf6ecc5SWolfram Sang static int enqueue_txdev(struct ks_wlan_private *priv, unsigned char *p, 284cdf6ecc5SWolfram Sang unsigned long size, 285055da4f9STobin C. Harding void (*complete_handler)(struct ks_wlan_private *priv, 286055da4f9STobin C. Harding struct sk_buff *skb), 287055da4f9STobin C. Harding struct sk_buff *skb) 28813a9930dSWolfram Sang { 28913a9930dSWolfram Sang struct tx_device_buffer *sp; 2901770ae9dSTobin C. Harding int ret; 29113a9930dSWolfram Sang 29213a9930dSWolfram Sang if (priv->dev_state < DEVICE_STATE_BOOT) { 2931770ae9dSTobin C. Harding ret = -EPERM; 294aa6ca807STobin C. Harding goto err_complete; 29513a9930dSWolfram Sang } 29613a9930dSWolfram Sang 297e6bc5053SSergio Paracuellos if ((TX_DEVICE_BUFF_SIZE - 1) <= txq_count(priv)) { 2985259b329SSergio Paracuellos netdev_err(priv->net_dev, "tx buffer overflow\n"); 2991770ae9dSTobin C. Harding ret = -EOVERFLOW; 300aa6ca807STobin C. Harding goto err_complete; 30113a9930dSWolfram Sang } 30213a9930dSWolfram Sang 30313a9930dSWolfram Sang sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qtail]; 30413a9930dSWolfram Sang sp->sendp = p; 30513a9930dSWolfram Sang sp->size = size; 30613a9930dSWolfram Sang sp->complete_handler = complete_handler; 307055da4f9STobin C. Harding sp->skb = skb; 30813a9930dSWolfram Sang inc_txqtail(priv); 30913a9930dSWolfram Sang 31013a9930dSWolfram Sang return 0; 311aa6ca807STobin C. Harding 312aa6ca807STobin C. Harding err_complete: 313aa6ca807STobin C. Harding kfree(p); 314aa6ca807STobin C. Harding if (complete_handler) 315055da4f9STobin C. Harding (*complete_handler)(priv, skb); 316aa6ca807STobin C. Harding 3171770ae9dSTobin C. Harding return ret; 31813a9930dSWolfram Sang } 31913a9930dSWolfram Sang 32013a9930dSWolfram Sang /* write data */ 32149705f9aSSergio Paracuellos static int write_to_device(struct ks_wlan_private *priv, u8 *buffer, 322cdf6ecc5SWolfram Sang unsigned long size) 32313a9930dSWolfram Sang { 32413a9930dSWolfram Sang struct hostif_hdr *hdr; 3251770ae9dSTobin C. Harding int ret; 326697f9f7fSMuraru Mihaela 32713a9930dSWolfram Sang hdr = (struct hostif_hdr *)buffer; 32813a9930dSWolfram Sang 329d9d1ffd4SCezary Gapinski if (le16_to_cpu(hdr->event) < HIF_DATA_REQ || 330d9d1ffd4SCezary Gapinski le16_to_cpu(hdr->event) > HIF_REQ_MAX) { 3315259b329SSergio Paracuellos netdev_err(priv->net_dev, "unknown event=%04X\n", hdr->event); 33213a9930dSWolfram Sang return 0; 33313a9930dSWolfram Sang } 33413a9930dSWolfram Sang 3351770ae9dSTobin C. Harding ret = ks7010_sdio_write(priv, DATA_WINDOW, buffer, size); 3361770ae9dSTobin C. Harding if (ret) { 337156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write DATA_WINDOW\n"); 3381770ae9dSTobin C. Harding return ret; 33913a9930dSWolfram Sang } 34013a9930dSWolfram Sang 341a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, WRITE_STATUS_REG, REG_STATUS_BUSY); 3421770ae9dSTobin C. Harding if (ret) { 343156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write WRITE_STATUS_REG\n"); 3441770ae9dSTobin C. Harding return ret; 34513a9930dSWolfram Sang } 34613a9930dSWolfram Sang 34713a9930dSWolfram Sang return 0; 34813a9930dSWolfram Sang } 34913a9930dSWolfram Sang 3505141e9c6STobin C. Harding static void tx_device_task(struct ks_wlan_private *priv) 35113a9930dSWolfram Sang { 35213a9930dSWolfram Sang struct tx_device_buffer *sp; 35303b02449STobin C. Harding int ret; 35413a9930dSWolfram Sang 355e6bc5053SSergio Paracuellos if (!txq_has_space(priv) || 356638a75b6STobin C. Harding atomic_read(&priv->psstatus.status) == PS_SNOOZE) 357638a75b6STobin C. Harding return; 358638a75b6STobin C. Harding 35913a9930dSWolfram Sang sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead]; 36013a9930dSWolfram Sang if (priv->dev_state >= DEVICE_STATE_BOOT) { 36103b02449STobin C. Harding ret = write_to_device(priv, sp->sendp, sp->size); 36203b02449STobin C. Harding if (ret) { 3637dd51ea1SSergio Paracuellos netdev_err(priv->net_dev, 3647dd51ea1SSergio Paracuellos "write_to_device error !!(%d)\n", ret); 36518bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 1); 36613a9930dSWolfram Sang return; 36713a9930dSWolfram Sang } 36813a9930dSWolfram Sang } 369638a75b6STobin C. Harding kfree(sp->sendp); 370c7e65f4dSsayli karnik if (sp->complete_handler) /* TX Complete */ 371055da4f9STobin C. Harding (*sp->complete_handler)(priv, sp->skb); 37213a9930dSWolfram Sang inc_txqhead(priv); 37313a9930dSWolfram Sang 374e6bc5053SSergio Paracuellos if (txq_has_space(priv)) 37518bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 0); 37613a9930dSWolfram Sang } 37713a9930dSWolfram Sang 378feedcf1aSWolfram Sang int ks_wlan_hw_tx(struct ks_wlan_private *priv, void *p, unsigned long size, 379055da4f9STobin C. Harding void (*complete_handler)(struct ks_wlan_private *priv, 380055da4f9STobin C. Harding struct sk_buff *skb), 381055da4f9STobin C. Harding struct sk_buff *skb) 38213a9930dSWolfram Sang { 38313a9930dSWolfram Sang int result = 0; 38413a9930dSWolfram Sang struct hostif_hdr *hdr; 385697f9f7fSMuraru Mihaela 38613a9930dSWolfram Sang hdr = (struct hostif_hdr *)p; 38713a9930dSWolfram Sang 388d9d1ffd4SCezary Gapinski if (le16_to_cpu(hdr->event) < HIF_DATA_REQ || 389d9d1ffd4SCezary Gapinski le16_to_cpu(hdr->event) > HIF_REQ_MAX) { 3905259b329SSergio Paracuellos netdev_err(priv->net_dev, "unknown event=%04X\n", hdr->event); 39113a9930dSWolfram Sang return 0; 39213a9930dSWolfram Sang } 39313a9930dSWolfram Sang 39413a9930dSWolfram Sang /* add event to hostt buffer */ 395d9d1ffd4SCezary Gapinski priv->hostt.buff[priv->hostt.qtail] = le16_to_cpu(hdr->event); 39613a9930dSWolfram Sang priv->hostt.qtail = (priv->hostt.qtail + 1) % SME_EVENT_BUFF_SIZE; 39713a9930dSWolfram Sang 39813a9930dSWolfram Sang spin_lock(&priv->tx_dev.tx_dev_lock); 399055da4f9STobin C. Harding result = enqueue_txdev(priv, p, size, complete_handler, skb); 40013a9930dSWolfram Sang spin_unlock(&priv->tx_dev.tx_dev_lock); 40113a9930dSWolfram Sang 402e6bc5053SSergio Paracuellos if (txq_has_space(priv)) 40318bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 0); 40418bd6dd1STobin C. Harding 40513a9930dSWolfram Sang return result; 40613a9930dSWolfram Sang } 40713a9930dSWolfram Sang 40813a9930dSWolfram Sang static void rx_event_task(unsigned long dev) 40913a9930dSWolfram Sang { 410feedcf1aSWolfram Sang struct ks_wlan_private *priv = (struct ks_wlan_private *)dev; 41113a9930dSWolfram Sang struct rx_device_buffer *rp; 41213a9930dSWolfram Sang 413e6bc5053SSergio Paracuellos if (rxq_has_space(priv) && priv->dev_state >= DEVICE_STATE_BOOT) { 41413a9930dSWolfram Sang rp = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qhead]; 41513a9930dSWolfram Sang hostif_receive(priv, rp->data, rp->size); 41613a9930dSWolfram Sang inc_rxqhead(priv); 41713a9930dSWolfram Sang 418e6bc5053SSergio Paracuellos if (rxq_has_space(priv)) 419321dabdcSTobin C. Harding tasklet_schedule(&priv->rx_bh_task); 42013a9930dSWolfram Sang } 42113a9930dSWolfram Sang } 42213a9930dSWolfram Sang 42389467e74SSergio Paracuellos static void ks_wlan_hw_rx(struct ks_wlan_private *priv, size_t size) 42413a9930dSWolfram Sang { 425f7172487STobin C. Harding int ret; 42613a9930dSWolfram Sang struct rx_device_buffer *rx_buffer; 42713a9930dSWolfram Sang struct hostif_hdr *hdr; 4283c6b7591SSergio Paracuellos u16 event = 0; 42913a9930dSWolfram Sang 43013a9930dSWolfram Sang /* receive data */ 431e6bc5053SSergio Paracuellos if (rxq_count(priv) >= (RX_DEVICE_BUFF_SIZE - 1)) { 4325259b329SSergio Paracuellos netdev_err(priv->net_dev, "rx buffer overflow\n"); 43313b05e46STobin C. Harding return; 43413a9930dSWolfram Sang } 43513a9930dSWolfram Sang rx_buffer = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qtail]; 43613a9930dSWolfram Sang 437f7172487STobin C. Harding ret = ks7010_sdio_read(priv, DATA_WINDOW, &rx_buffer->data[0], 438cdf6ecc5SWolfram Sang hif_align_size(size)); 439f7172487STobin C. Harding if (ret) 44013b05e46STobin C. Harding return; 44113a9930dSWolfram Sang 44213a9930dSWolfram Sang /* length check */ 44313a9930dSWolfram Sang if (size > 2046 || size == 0) { 44431ce0d86SSergio Paracuellos #ifdef DEBUG 445cdf6ecc5SWolfram Sang print_hex_dump_bytes("INVALID DATA dump: ", 446cdf6ecc5SWolfram Sang DUMP_PREFIX_OFFSET, 4473215bb1aSWolfram Sang rx_buffer->data, 32); 4483215bb1aSWolfram Sang #endif 449a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, READ_STATUS_REG, REG_STATUS_IDLE); 450f7172487STobin C. Harding if (ret) 451156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write READ_STATUS_REG\n"); 45253638cefSsayli karnik 45313b05e46STobin C. Harding /* length check fail */ 45413b05e46STobin C. Harding return; 45513a9930dSWolfram Sang } 45613a9930dSWolfram Sang 45713a9930dSWolfram Sang hdr = (struct hostif_hdr *)&rx_buffer->data[0]; 45813a9930dSWolfram Sang rx_buffer->size = le16_to_cpu(hdr->size) + sizeof(hdr->size); 459d9d1ffd4SCezary Gapinski event = le16_to_cpu(hdr->event); 46013a9930dSWolfram Sang inc_rxqtail(priv); 46113a9930dSWolfram Sang 462a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, READ_STATUS_REG, REG_STATUS_IDLE); 463f7172487STobin C. Harding if (ret) 464156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write READ_STATUS_REG\n"); 46513a9930dSWolfram Sang 4667570d757SSergio Paracuellos if (atomic_read(&priv->psstatus.confirm_wait) && is_hif_conf(event)) { 4675259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "IS_HIF_CONF true !!\n"); 46813a9930dSWolfram Sang atomic_dec(&priv->psstatus.confirm_wait); 46913a9930dSWolfram Sang } 47013a9930dSWolfram Sang 471321dabdcSTobin C. Harding tasklet_schedule(&priv->rx_bh_task); 47213a9930dSWolfram Sang } 47313a9930dSWolfram Sang 47413a9930dSWolfram Sang static void ks7010_rw_function(struct work_struct *work) 47513a9930dSWolfram Sang { 47607511629SSergio Paracuellos struct ks_wlan_private *priv = container_of(work, 47707511629SSergio Paracuellos struct ks_wlan_private, 47807511629SSergio Paracuellos rw_dwork.work); 47907511629SSergio Paracuellos struct sdio_func *func = ks7010_to_func(priv); 480a51333d1SSergio Paracuellos u8 byte; 4811770ae9dSTobin C. Harding int ret; 48213a9930dSWolfram Sang 483e61e73d7STobin C. Harding /* wait after DOZE */ 4846adc30b1SSergio Paracuellos if (time_after(priv->last_doze + msecs_to_jiffies(30), jiffies)) { 4855259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "wait after DOZE\n"); 48618bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 1); 48713a9930dSWolfram Sang return; 48813a9930dSWolfram Sang } 48913a9930dSWolfram Sang 490e61e73d7STobin C. Harding /* wait after WAKEUP */ 4916adc30b1SSergio Paracuellos while (time_after(priv->last_wakeup + msecs_to_jiffies(30), jiffies)) { 4925259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "wait after WAKEUP\n"); 49307511629SSergio Paracuellos dev_info(&func->dev, "wake: %lu %lu\n", 4946adc30b1SSergio Paracuellos priv->last_wakeup + msecs_to_jiffies(30), jiffies); 49513a9930dSWolfram Sang msleep(30); 49613a9930dSWolfram Sang } 49713a9930dSWolfram Sang 49807511629SSergio Paracuellos sdio_claim_host(func); 49913a9930dSWolfram Sang 50013a9930dSWolfram Sang /* power save wakeup */ 50113a9930dSWolfram Sang if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { 502e6bc5053SSergio Paracuellos if (txq_has_space(priv)) { 50313a9930dSWolfram Sang ks_wlan_hw_wakeup_request(priv); 50418bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 1); 50513a9930dSWolfram Sang } 5060dd30a74STobin C. Harding goto release_host; 50713a9930dSWolfram Sang } 50813a9930dSWolfram Sang 50913a9930dSWolfram Sang /* sleep mode doze */ 51013a9930dSWolfram Sang if (atomic_read(&priv->sleepstatus.doze_request) == 1) { 51113a9930dSWolfram Sang ks_wlan_hw_sleep_doze_request(priv); 5120dd30a74STobin C. Harding goto release_host; 51313a9930dSWolfram Sang } 51413a9930dSWolfram Sang /* sleep mode wakeup */ 51513a9930dSWolfram Sang if (atomic_read(&priv->sleepstatus.wakeup_request) == 1) { 51613a9930dSWolfram Sang ks_wlan_hw_sleep_wakeup_request(priv); 5170dd30a74STobin C. Harding goto release_host; 51813a9930dSWolfram Sang } 51913a9930dSWolfram Sang 52013a9930dSWolfram Sang /* read (WriteStatus/ReadDataSize FN1:00_0014) */ 521a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, WSTATUS_RSIZE_REG, &byte); 5221770ae9dSTobin C. Harding if (ret) { 523156f2703SSergio Paracuellos netdev_err(priv->net_dev, "read WSTATUS_RSIZE_REG psstatus=%d\n", 524cdf6ecc5SWolfram Sang atomic_read(&priv->psstatus.status)); 5250dd30a74STobin C. Harding goto release_host; 52613a9930dSWolfram Sang } 52713a9930dSWolfram Sang 528f1e79f4bSTobin C. Harding if (byte & RSIZE_MASK) { /* Read schedule */ 52989467e74SSergio Paracuellos ks_wlan_hw_rx(priv, (size_t)((byte & RSIZE_MASK) << 4)); 53013a9930dSWolfram Sang } 531f1e79f4bSTobin C. Harding if ((byte & WSTATUS_MASK)) 5325141e9c6STobin C. Harding tx_device_task(priv); 53353638cefSsayli karnik 53413a9930dSWolfram Sang _ks_wlan_hw_power_save(priv); 53513a9930dSWolfram Sang 5360dd30a74STobin C. Harding release_host: 53707511629SSergio Paracuellos sdio_release_host(func); 53813a9930dSWolfram Sang } 53913a9930dSWolfram Sang 54013a9930dSWolfram Sang static void ks_sdio_interrupt(struct sdio_func *func) 54113a9930dSWolfram Sang { 542f7172487STobin C. Harding int ret; 54313a9930dSWolfram Sang struct ks_sdio_card *card; 544feedcf1aSWolfram Sang struct ks_wlan_private *priv; 54529699193SSergio Paracuellos u8 status, rsize, byte; 54613a9930dSWolfram Sang 54713a9930dSWolfram Sang card = sdio_get_drvdata(func); 54813a9930dSWolfram Sang priv = card->priv; 54913a9930dSWolfram Sang 550638a75b6STobin C. Harding if (priv->dev_state < DEVICE_STATE_BOOT) 551638a75b6STobin C. Harding goto queue_delayed_work; 552638a75b6STobin C. Harding 553a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, INT_PENDING_REG, &status); 554f7172487STobin C. Harding if (ret) { 555156f2703SSergio Paracuellos netdev_err(priv->net_dev, "read INT_PENDING_REG\n"); 556f283dd69STobin C. Harding goto queue_delayed_work; 55713a9930dSWolfram Sang } 55813a9930dSWolfram Sang 55913a9930dSWolfram Sang /* schedule task for interrupt status */ 56013a9930dSWolfram Sang /* bit7 -> Write General Communication B register */ 56113a9930dSWolfram Sang /* read (General Communication B register) */ 56213a9930dSWolfram Sang /* bit5 -> Write Status Idle */ 56313a9930dSWolfram Sang /* bit2 -> Read Status Busy */ 564ddd10774SXiangyang Zhang if (status & INT_GCR_B || 565ddd10774SXiangyang Zhang atomic_read(&priv->psstatus.status) == PS_SNOOZE) { 566a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, GCR_B_REG, &byte); 567f7172487STobin C. Harding if (ret) { 568156f2703SSergio Paracuellos netdev_err(priv->net_dev, "read GCR_B_REG\n"); 569f283dd69STobin C. Harding goto queue_delayed_work; 57013a9930dSWolfram Sang } 571f1e79f4bSTobin C. Harding if (byte == GCR_B_ACTIVE) { 572638a75b6STobin C. Harding if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { 573638a75b6STobin C. Harding atomic_set(&priv->psstatus.status, PS_WAKEUP); 57413a9930dSWolfram Sang priv->wakeup_count = 0; 57513a9930dSWolfram Sang } 57613a9930dSWolfram Sang complete(&priv->psstatus.wakeup_wait); 57713a9930dSWolfram Sang } 57813a9930dSWolfram Sang } 57913a9930dSWolfram Sang 58013a9930dSWolfram Sang do { 58113a9930dSWolfram Sang /* read (WriteStatus/ReadDataSize FN1:00_0014) */ 582a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, WSTATUS_RSIZE_REG, &byte); 583f7172487STobin C. Harding if (ret) { 584156f2703SSergio Paracuellos netdev_err(priv->net_dev, "read WSTATUS_RSIZE_REG\n"); 585f283dd69STobin C. Harding goto queue_delayed_work; 58613a9930dSWolfram Sang } 587f1e79f4bSTobin C. Harding rsize = byte & RSIZE_MASK; 5885141e9c6STobin C. Harding if (rsize != 0) /* Read schedule */ 58989467e74SSergio Paracuellos ks_wlan_hw_rx(priv, (size_t)(rsize << 4)); 5905141e9c6STobin C. Harding 591f1e79f4bSTobin C. Harding if (byte & WSTATUS_MASK) { 59213a9930dSWolfram Sang if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { 593e6bc5053SSergio Paracuellos if (txq_has_space(priv)) { 59413a9930dSWolfram Sang ks_wlan_hw_wakeup_request(priv); 5957dd51ea1SSergio Paracuellos queue_delayed_work(priv->wq, 5967dd51ea1SSergio Paracuellos &priv->rw_dwork, 1); 59713a9930dSWolfram Sang return; 59813a9930dSWolfram Sang } 599cdf6ecc5SWolfram Sang } else { 6005141e9c6STobin C. Harding tx_device_task(priv); 60113a9930dSWolfram Sang } 60213a9930dSWolfram Sang } 60313a9930dSWolfram Sang } while (rsize); 60413a9930dSWolfram Sang 605f283dd69STobin C. Harding queue_delayed_work: 60618bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 0); 60713a9930dSWolfram Sang } 60813a9930dSWolfram Sang 609feedcf1aSWolfram Sang static int trx_device_init(struct ks_wlan_private *priv) 61013a9930dSWolfram Sang { 61120358d13SNick Rosbrook priv->tx_dev.qhead = 0; 61220358d13SNick Rosbrook priv->tx_dev.qtail = 0; 61313a9930dSWolfram Sang 61420358d13SNick Rosbrook priv->rx_dev.qhead = 0; 61520358d13SNick Rosbrook priv->rx_dev.qtail = 0; 61613a9930dSWolfram Sang 61713a9930dSWolfram Sang spin_lock_init(&priv->tx_dev.tx_dev_lock); 61813a9930dSWolfram Sang spin_lock_init(&priv->rx_dev.rx_dev_lock); 61913a9930dSWolfram Sang 620321dabdcSTobin C. Harding tasklet_init(&priv->rx_bh_task, rx_event_task, (unsigned long)priv); 62113a9930dSWolfram Sang 62213a9930dSWolfram Sang return 0; 62313a9930dSWolfram Sang } 62413a9930dSWolfram Sang 625feedcf1aSWolfram Sang static void trx_device_exit(struct ks_wlan_private *priv) 62613a9930dSWolfram Sang { 62713a9930dSWolfram Sang struct tx_device_buffer *sp; 62813a9930dSWolfram Sang 62913a9930dSWolfram Sang /* tx buffer clear */ 630e6bc5053SSergio Paracuellos while (txq_has_space(priv)) { 63113a9930dSWolfram Sang sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead]; 632e61e73d7STobin C. Harding kfree(sp->sendp); 633c7e65f4dSsayli karnik if (sp->complete_handler) /* TX Complete */ 634055da4f9STobin C. Harding (*sp->complete_handler)(priv, sp->skb); 63513a9930dSWolfram Sang inc_txqhead(priv); 63613a9930dSWolfram Sang } 63713a9930dSWolfram Sang 638321dabdcSTobin C. Harding tasklet_kill(&priv->rx_bh_task); 63913a9930dSWolfram Sang } 640cdf6ecc5SWolfram Sang 641feedcf1aSWolfram Sang static int ks7010_sdio_update_index(struct ks_wlan_private *priv, u32 index) 64213a9930dSWolfram Sang { 6431770ae9dSTobin C. Harding int ret; 64413a9930dSWolfram Sang unsigned char *data_buf; 64513a9930dSWolfram Sang 64647bda74aSJi-Hun Kim data_buf = kmemdup(&index, sizeof(u32), GFP_KERNEL); 647aa6ca807STobin C. Harding if (!data_buf) 648aa6ca807STobin C. Harding return -ENOMEM; 64913a9930dSWolfram Sang 650a704a1bcSSergio Paracuellos ret = ks7010_sdio_write(priv, WRITE_INDEX_REG, data_buf, sizeof(index)); 6511770ae9dSTobin C. Harding if (ret) 652f283dd69STobin C. Harding goto err_free_data_buf; 65313a9930dSWolfram Sang 654a704a1bcSSergio Paracuellos ret = ks7010_sdio_write(priv, READ_INDEX_REG, data_buf, sizeof(index)); 6551770ae9dSTobin C. Harding if (ret) 656f283dd69STobin C. Harding goto err_free_data_buf; 657aa6ca807STobin C. Harding 658aa6ca807STobin C. Harding return 0; 659aa6ca807STobin C. Harding 660f283dd69STobin C. Harding err_free_data_buf: 661cdf6ecc5SWolfram Sang kfree(data_buf); 662aa6ca807STobin C. Harding 6631770ae9dSTobin C. Harding return ret; 66413a9930dSWolfram Sang } 66513a9930dSWolfram Sang 66613a9930dSWolfram Sang #define ROM_BUFF_SIZE (64 * 1024) 667feedcf1aSWolfram Sang static int ks7010_sdio_data_compare(struct ks_wlan_private *priv, u32 address, 668a7360b18SSergio Paracuellos u8 *data, unsigned int size) 66913a9930dSWolfram Sang { 6701770ae9dSTobin C. Harding int ret; 671a7360b18SSergio Paracuellos u8 *read_buf; 672eeed92c0SMarkus Elfring 67313a9930dSWolfram Sang read_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL); 674aa6ca807STobin C. Harding if (!read_buf) 675aa6ca807STobin C. Harding return -ENOMEM; 67613a9930dSWolfram Sang 6771770ae9dSTobin C. Harding ret = ks7010_sdio_read(priv, address, read_buf, size); 6781770ae9dSTobin C. Harding if (ret) 679f283dd69STobin C. Harding goto err_free_read_buf; 680aa6ca807STobin C. Harding 68158af272bSTobin C. Harding if (memcmp(data, read_buf, size) != 0) { 68258af272bSTobin C. Harding ret = -EIO; 6835259b329SSergio Paracuellos netdev_err(priv->net_dev, "data compare error (%d)\n", ret); 684f283dd69STobin C. Harding goto err_free_read_buf; 68513a9930dSWolfram Sang } 686aa6ca807STobin C. Harding 687aa6ca807STobin C. Harding return 0; 688aa6ca807STobin C. Harding 689f283dd69STobin C. Harding err_free_read_buf: 690cdf6ecc5SWolfram Sang kfree(read_buf); 691aa6ca807STobin C. Harding 6921770ae9dSTobin C. Harding return ret; 69313a9930dSWolfram Sang } 694cdf6ecc5SWolfram Sang 69567082c22SSergio Paracuellos static int ks7010_copy_firmware(struct ks_wlan_private *priv, 69667082c22SSergio Paracuellos const struct firmware *fw_entry) 69713a9930dSWolfram Sang { 698881f76b9STobin C. Harding unsigned int length; 69967082c22SSergio Paracuellos unsigned int size; 70067082c22SSergio Paracuellos unsigned int offset; 70167082c22SSergio Paracuellos unsigned int n = 0; 702a7360b18SSergio Paracuellos u8 *rom_buf; 70367082c22SSergio Paracuellos int ret; 70413a9930dSWolfram Sang 70513a9930dSWolfram Sang rom_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL); 706369e1b69SSudip Mukherjee if (!rom_buf) 707aa6ca807STobin C. Harding return -ENOMEM; 70813a9930dSWolfram Sang 70913a9930dSWolfram Sang length = fw_entry->size; 71013a9930dSWolfram Sang 71113a9930dSWolfram Sang do { 71213a9930dSWolfram Sang if (length >= ROM_BUFF_SIZE) { 71313a9930dSWolfram Sang size = ROM_BUFF_SIZE; 71413a9930dSWolfram Sang length = length - ROM_BUFF_SIZE; 715cdf6ecc5SWolfram Sang } else { 71613a9930dSWolfram Sang size = length; 71713a9930dSWolfram Sang length = 0; 71813a9930dSWolfram Sang } 719cdf6ecc5SWolfram Sang if (size == 0) 720cdf6ecc5SWolfram Sang break; 721bd6dad98SSergio Paracuellos 72213a9930dSWolfram Sang memcpy(rom_buf, fw_entry->data + n, size); 723e61e73d7STobin C. Harding 72413a9930dSWolfram Sang offset = n; 725bd6dad98SSergio Paracuellos ret = ks7010_sdio_update_index(priv, 726bd6dad98SSergio Paracuellos KS7010_IRAM_ADDRESS + offset); 7271770ae9dSTobin C. Harding if (ret) 72867082c22SSergio Paracuellos goto free_rom_buf; 72913a9930dSWolfram Sang 7301770ae9dSTobin C. Harding ret = ks7010_sdio_write(priv, DATA_WINDOW, rom_buf, size); 7311770ae9dSTobin C. Harding if (ret) 73267082c22SSergio Paracuellos goto free_rom_buf; 73313a9930dSWolfram Sang 734bd6dad98SSergio Paracuellos ret = ks7010_sdio_data_compare(priv, 735bd6dad98SSergio Paracuellos DATA_WINDOW, rom_buf, size); 7361770ae9dSTobin C. Harding if (ret) 73767082c22SSergio Paracuellos goto free_rom_buf; 738aa6ca807STobin C. Harding 73913a9930dSWolfram Sang n += size; 74013a9930dSWolfram Sang 74113a9930dSWolfram Sang } while (size); 74213a9930dSWolfram Sang 743a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, GCR_A_REG, GCR_A_REMAP); 74467082c22SSergio Paracuellos 74567082c22SSergio Paracuellos free_rom_buf: 74667082c22SSergio Paracuellos kfree(rom_buf); 74767082c22SSergio Paracuellos return ret; 74867082c22SSergio Paracuellos } 74967082c22SSergio Paracuellos 75067082c22SSergio Paracuellos static int ks7010_upload_firmware(struct ks_sdio_card *card) 75167082c22SSergio Paracuellos { 75267082c22SSergio Paracuellos struct ks_wlan_private *priv = card->priv; 75307511629SSergio Paracuellos struct sdio_func *func = ks7010_to_func(priv); 75467082c22SSergio Paracuellos unsigned int n; 75562a37b72SSergio Paracuellos u8 byte = 0; 75667082c22SSergio Paracuellos int ret; 75767082c22SSergio Paracuellos const struct firmware *fw_entry = NULL; 75867082c22SSergio Paracuellos 75907511629SSergio Paracuellos sdio_claim_host(func); 76067082c22SSergio Paracuellos 76167082c22SSergio Paracuellos /* Firmware running ? */ 762a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, GCR_A_REG, &byte); 76367082c22SSergio Paracuellos if (ret) 76467082c22SSergio Paracuellos goto release_host; 76567082c22SSergio Paracuellos if (byte == GCR_A_RUN) { 76667082c22SSergio Paracuellos netdev_dbg(priv->net_dev, "MAC firmware running ...\n"); 76767082c22SSergio Paracuellos ret = -EBUSY; 76867082c22SSergio Paracuellos goto release_host; 76967082c22SSergio Paracuellos } 77067082c22SSergio Paracuellos 77167082c22SSergio Paracuellos ret = request_firmware(&fw_entry, ROM_FILE, 77207511629SSergio Paracuellos &func->dev); 77367082c22SSergio Paracuellos if (ret) 77467082c22SSergio Paracuellos goto release_host; 77567082c22SSergio Paracuellos 77667082c22SSergio Paracuellos ret = ks7010_copy_firmware(priv, fw_entry); 7771770ae9dSTobin C. Harding if (ret) 778aa6ca807STobin C. Harding goto release_firmware; 779aa6ca807STobin C. Harding 78013a9930dSWolfram Sang /* Firmware running check */ 78113a9930dSWolfram Sang for (n = 0; n < 50; ++n) { 78295751f19SJia-Ju Bai usleep_range(10000, 11000); /* wait_ms(10); */ 783a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, GCR_A_REG, &byte); 7841770ae9dSTobin C. Harding if (ret) 785aa6ca807STobin C. Harding goto release_firmware; 786aa6ca807STobin C. Harding 787f1e79f4bSTobin C. Harding if (byte == GCR_A_RUN) 788cdf6ecc5SWolfram Sang break; 78913a9930dSWolfram Sang } 79013a9930dSWolfram Sang if ((50) <= n) { 7915259b329SSergio Paracuellos netdev_err(priv->net_dev, "firmware can't start\n"); 7921770ae9dSTobin C. Harding ret = -EIO; 793aa6ca807STobin C. Harding goto release_firmware; 79413a9930dSWolfram Sang } 79513a9930dSWolfram Sang 7961770ae9dSTobin C. Harding ret = 0; 79713a9930dSWolfram Sang 798aa6ca807STobin C. Harding release_firmware: 79913a9930dSWolfram Sang release_firmware(fw_entry); 80067082c22SSergio Paracuellos release_host: 80107511629SSergio Paracuellos sdio_release_host(func); 802aa6ca807STobin C. Harding 8031770ae9dSTobin C. Harding return ret; 80413a9930dSWolfram Sang } 80513a9930dSWolfram Sang 8066e6156f6SSergio Paracuellos static void ks7010_sme_enqueue_events(struct ks_wlan_private *priv) 80713a9930dSWolfram Sang { 808f0d5a975SSergio Paracuellos static const u16 init_events[] = { 809f0d5a975SSergio Paracuellos SME_GET_EEPROM_CKSUM, SME_STOP_REQUEST, 810f0d5a975SSergio Paracuellos SME_RTS_THRESHOLD_REQUEST, SME_FRAGMENTATION_THRESHOLD_REQUEST, 811f0d5a975SSergio Paracuellos SME_WEP_INDEX_REQUEST, SME_WEP_KEY1_REQUEST, 812f0d5a975SSergio Paracuellos SME_WEP_KEY2_REQUEST, SME_WEP_KEY3_REQUEST, 813f0d5a975SSergio Paracuellos SME_WEP_KEY4_REQUEST, SME_WEP_FLAG_REQUEST, 814f0d5a975SSergio Paracuellos SME_RSN_ENABLED_REQUEST, SME_MODE_SET_REQUEST, 815f0d5a975SSergio Paracuellos SME_START_REQUEST 816f0d5a975SSergio Paracuellos }; 817f0d5a975SSergio Paracuellos int ev; 81813a9930dSWolfram Sang 819f0d5a975SSergio Paracuellos for (ev = 0; ev < ARRAY_SIZE(init_events); ev++) 820f0d5a975SSergio Paracuellos hostif_sme_enqueue(priv, init_events[ev]); 8216e6156f6SSergio Paracuellos } 8226e6156f6SSergio Paracuellos 8236e6156f6SSergio Paracuellos static void ks7010_card_init(struct ks_wlan_private *priv) 8246e6156f6SSergio Paracuellos { 8256e6156f6SSergio Paracuellos init_completion(&priv->confirm_wait); 8266e6156f6SSergio Paracuellos 8276e6156f6SSergio Paracuellos /* get mac address & firmware version */ 8286e6156f6SSergio Paracuellos hostif_sme_enqueue(priv, SME_START); 8296e6156f6SSergio Paracuellos 8306e6156f6SSergio Paracuellos if (!wait_for_completion_interruptible_timeout 8316e6156f6SSergio Paracuellos (&priv->confirm_wait, 5 * HZ)) { 8326e6156f6SSergio Paracuellos netdev_dbg(priv->net_dev, "wait time out!! SME_START\n"); 8336e6156f6SSergio Paracuellos } 8346e6156f6SSergio Paracuellos 8356e6156f6SSergio Paracuellos if (priv->mac_address_valid && priv->version_size != 0) 8366e6156f6SSergio Paracuellos priv->dev_state = DEVICE_STATE_PREINIT; 8376e6156f6SSergio Paracuellos 8386e6156f6SSergio Paracuellos ks7010_sme_enqueue_events(priv); 83913a9930dSWolfram Sang 840cdf6ecc5SWolfram Sang if (!wait_for_completion_interruptible_timeout 841cdf6ecc5SWolfram Sang (&priv->confirm_wait, 5 * HZ)) { 8425259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "wait time out!! wireless parameter set\n"); 84313a9930dSWolfram Sang } 84413a9930dSWolfram Sang 84513a9930dSWolfram Sang if (priv->dev_state >= DEVICE_STATE_PREINIT) { 8465259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "DEVICE READY!!\n"); 84713a9930dSWolfram Sang priv->dev_state = DEVICE_STATE_READY; 84813a9930dSWolfram Sang } 84913a9930dSWolfram Sang } 85013a9930dSWolfram Sang 8516ee9169bSWolfram Sang static void ks7010_init_defaults(struct ks_wlan_private *priv) 8526ee9169bSWolfram Sang { 8536ee9169bSWolfram Sang priv->reg.tx_rate = TX_RATE_AUTO; 8546ee9169bSWolfram Sang priv->reg.preamble = LONG_PREAMBLE; 8558fb8e05cSTobin C. Harding priv->reg.power_mgmt = POWER_MGMT_ACTIVE; 8566ee9169bSWolfram Sang priv->reg.scan_type = ACTIVE_SCAN; 8576ee9169bSWolfram Sang priv->reg.beacon_lost_count = 20; 8586ee9169bSWolfram Sang priv->reg.rts = 2347UL; 8596ee9169bSWolfram Sang priv->reg.fragment = 2346UL; 8606ee9169bSWolfram Sang priv->reg.phy_type = D_11BG_COMPATIBLE_MODE; 8616ee9169bSWolfram Sang priv->reg.cts_mode = CTS_MODE_FALSE; 8626ee9169bSWolfram Sang priv->reg.rate_set.body[11] = TX_RATE_54M; 8636ee9169bSWolfram Sang priv->reg.rate_set.body[10] = TX_RATE_48M; 8646ee9169bSWolfram Sang priv->reg.rate_set.body[9] = TX_RATE_36M; 8656ee9169bSWolfram Sang priv->reg.rate_set.body[8] = TX_RATE_18M; 8666ee9169bSWolfram Sang priv->reg.rate_set.body[7] = TX_RATE_9M; 8676ee9169bSWolfram Sang priv->reg.rate_set.body[6] = TX_RATE_24M | BASIC_RATE; 8686ee9169bSWolfram Sang priv->reg.rate_set.body[5] = TX_RATE_12M | BASIC_RATE; 8696ee9169bSWolfram Sang priv->reg.rate_set.body[4] = TX_RATE_6M | BASIC_RATE; 8706ee9169bSWolfram Sang priv->reg.rate_set.body[3] = TX_RATE_11M | BASIC_RATE; 8716ee9169bSWolfram Sang priv->reg.rate_set.body[2] = TX_RATE_5M | BASIC_RATE; 8726ee9169bSWolfram Sang priv->reg.rate_set.body[1] = TX_RATE_2M | BASIC_RATE; 8736ee9169bSWolfram Sang priv->reg.rate_set.body[0] = TX_RATE_1M | BASIC_RATE; 8746ee9169bSWolfram Sang priv->reg.tx_rate = TX_RATE_FULL_AUTO; 8756ee9169bSWolfram Sang priv->reg.rate_set.size = 12; 8766ee9169bSWolfram Sang } 8776ee9169bSWolfram Sang 8788d4ab8f7SSergio Paracuellos static int ks7010_sdio_setup_irqs(struct sdio_func *func) 8798d4ab8f7SSergio Paracuellos { 8808d4ab8f7SSergio Paracuellos int ret; 8818d4ab8f7SSergio Paracuellos 8828d4ab8f7SSergio Paracuellos /* interrupt disable */ 883a704a1bcSSergio Paracuellos sdio_writeb(func, 0, INT_ENABLE_REG, &ret); 8848d4ab8f7SSergio Paracuellos if (ret) 8858d4ab8f7SSergio Paracuellos goto irq_error; 8868d4ab8f7SSergio Paracuellos 887a704a1bcSSergio Paracuellos sdio_writeb(func, 0xff, INT_PENDING_REG, &ret); 8888d4ab8f7SSergio Paracuellos if (ret) 8898d4ab8f7SSergio Paracuellos goto irq_error; 8908d4ab8f7SSergio Paracuellos 8918d4ab8f7SSergio Paracuellos /* setup interrupt handler */ 8928d4ab8f7SSergio Paracuellos ret = sdio_claim_irq(func, ks_sdio_interrupt); 8938d4ab8f7SSergio Paracuellos 8948d4ab8f7SSergio Paracuellos irq_error: 8958d4ab8f7SSergio Paracuellos return ret; 8968d4ab8f7SSergio Paracuellos } 8978d4ab8f7SSergio Paracuellos 8980e80e31aSSergio Paracuellos static void ks7010_sdio_init_irqs(struct sdio_func *func, 8990e80e31aSSergio Paracuellos struct ks_wlan_private *priv) 9000e80e31aSSergio Paracuellos { 90158f8128eSSergio Paracuellos u8 byte; 9020e80e31aSSergio Paracuellos int ret; 9030e80e31aSSergio Paracuellos 9040e80e31aSSergio Paracuellos /* 9050e80e31aSSergio Paracuellos * interrupt setting 9060e80e31aSSergio Paracuellos * clear Interrupt status write 9070e80e31aSSergio Paracuellos * (ARMtoSD_InterruptPending FN1:00_0024) 9080e80e31aSSergio Paracuellos */ 9090e80e31aSSergio Paracuellos sdio_claim_host(func); 910a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, INT_PENDING_REG, 0xff); 9110e80e31aSSergio Paracuellos sdio_release_host(func); 9120e80e31aSSergio Paracuellos if (ret) 913156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write INT_PENDING_REG\n"); 9140e80e31aSSergio Paracuellos 9150e80e31aSSergio Paracuellos /* enable ks7010sdio interrupt */ 9160e80e31aSSergio Paracuellos byte = (INT_GCR_B | INT_READ_STATUS | INT_WRITE_STATUS); 9170e80e31aSSergio Paracuellos sdio_claim_host(func); 918a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, INT_ENABLE_REG, byte); 9190e80e31aSSergio Paracuellos sdio_release_host(func); 9200e80e31aSSergio Paracuellos if (ret) 921156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write INT_ENABLE_REG\n"); 9220e80e31aSSergio Paracuellos } 9230e80e31aSSergio Paracuellos 9245df835e8SSergio Paracuellos static void ks7010_private_init(struct ks_wlan_private *priv, 9255df835e8SSergio Paracuellos struct ks_sdio_card *card, 9265df835e8SSergio Paracuellos struct net_device *netdev) 9275df835e8SSergio Paracuellos { 9285df835e8SSergio Paracuellos /* private memory initialize */ 92907511629SSergio Paracuellos priv->if_hw = card; 9305df835e8SSergio Paracuellos 9315df835e8SSergio Paracuellos priv->dev_state = DEVICE_STATE_PREBOOT; 9325df835e8SSergio Paracuellos priv->net_dev = netdev; 9335df835e8SSergio Paracuellos priv->firmware_version[0] = '\0'; 9345df835e8SSergio Paracuellos priv->version_size = 0; 9355df835e8SSergio Paracuellos priv->last_doze = jiffies; 9365df835e8SSergio Paracuellos priv->last_wakeup = jiffies; 9375df835e8SSergio Paracuellos memset(&priv->nstats, 0, sizeof(priv->nstats)); 9385df835e8SSergio Paracuellos memset(&priv->wstats, 0, sizeof(priv->wstats)); 9395df835e8SSergio Paracuellos 9405df835e8SSergio Paracuellos /* sleep mode */ 9415df835e8SSergio Paracuellos atomic_set(&priv->sleepstatus.doze_request, 0); 9425df835e8SSergio Paracuellos atomic_set(&priv->sleepstatus.wakeup_request, 0); 9435df835e8SSergio Paracuellos atomic_set(&priv->sleepstatus.wakeup_request, 0); 9445df835e8SSergio Paracuellos 9455df835e8SSergio Paracuellos trx_device_init(priv); 9465df835e8SSergio Paracuellos hostif_init(priv); 9475df835e8SSergio Paracuellos ks_wlan_net_start(netdev); 9485df835e8SSergio Paracuellos ks7010_init_defaults(priv); 9495df835e8SSergio Paracuellos } 9505df835e8SSergio Paracuellos 951c4730a92SWolfram Sang static int ks7010_sdio_probe(struct sdio_func *func, 952cdf6ecc5SWolfram Sang const struct sdio_device_id *device) 95313a9930dSWolfram Sang { 9542c3f8945SSergio Paracuellos struct ks_wlan_private *priv = NULL; 9552c3f8945SSergio Paracuellos struct net_device *netdev = NULL; 95613a9930dSWolfram Sang struct ks_sdio_card *card; 9572801d7a2SWolfram Sang int ret; 95813a9930dSWolfram Sang 9592d738bd2SSandhya Bankar card = kzalloc(sizeof(*card), GFP_KERNEL); 96013a9930dSWolfram Sang if (!card) 96113a9930dSWolfram Sang return -ENOMEM; 96213a9930dSWolfram Sang 96313a9930dSWolfram Sang card->func = func; 96413a9930dSWolfram Sang 96513a9930dSWolfram Sang sdio_claim_host(func); 96613a9930dSWolfram Sang 96713a9930dSWolfram Sang ret = sdio_set_block_size(func, KS7010_IO_BLOCK_SIZE); 96831d7b1b1SSergio Paracuellos if (ret) 96931d7b1b1SSergio Paracuellos goto err_free_card; 97031d7b1b1SSergio Paracuellos 9715259b329SSergio Paracuellos dev_dbg(&card->func->dev, "multi_block=%d sdio_set_block_size()=%d %d\n", 972cdf6ecc5SWolfram Sang func->card->cccr.multi_block, func->cur_blksize, ret); 97313a9930dSWolfram Sang 97413a9930dSWolfram Sang ret = sdio_enable_func(func); 97513a9930dSWolfram Sang if (ret) 976f283dd69STobin C. Harding goto err_free_card; 97713a9930dSWolfram Sang 9788d4ab8f7SSergio Paracuellos ret = ks7010_sdio_setup_irqs(func); 97913a9930dSWolfram Sang if (ret) 980f283dd69STobin C. Harding goto err_disable_func; 98113a9930dSWolfram Sang 98213a9930dSWolfram Sang sdio_release_host(func); 98313a9930dSWolfram Sang 98413a9930dSWolfram Sang sdio_set_drvdata(func, card); 98513a9930dSWolfram Sang 9865259b329SSergio Paracuellos dev_dbg(&card->func->dev, "class = 0x%X, vendor = 0x%X, device = 0x%X\n", 9877c1c4361STobin C. Harding func->class, func->vendor, func->device); 98813a9930dSWolfram Sang 98913a9930dSWolfram Sang /* private memory allocate */ 99013a9930dSWolfram Sang netdev = alloc_etherdev(sizeof(*priv)); 991c7e65f4dSsayli karnik if (!netdev) { 992156f2703SSergio Paracuellos dev_err(&card->func->dev, "Unable to alloc new net device\n"); 993f283dd69STobin C. Harding goto err_release_irq; 99413a9930dSWolfram Sang } 99506176b87SSergio Paracuellos 99606176b87SSergio Paracuellos ret = dev_alloc_name(netdev, "wlan%d"); 99706176b87SSergio Paracuellos if (ret < 0) { 998156f2703SSergio Paracuellos dev_err(&card->func->dev, "Couldn't get name!\n"); 999f283dd69STobin C. Harding goto err_free_netdev; 100013a9930dSWolfram Sang } 100113a9930dSWolfram Sang 100213a9930dSWolfram Sang priv = netdev_priv(netdev); 100313a9930dSWolfram Sang 100413a9930dSWolfram Sang card->priv = priv; 1005e4844d6fSSergio Paracuellos SET_NETDEV_DEV(netdev, &card->func->dev); 100613a9930dSWolfram Sang 10075df835e8SSergio Paracuellos ks7010_private_init(priv, card, netdev); 100813a9930dSWolfram Sang 1009ed246b9eSTobin C. Harding ret = ks7010_upload_firmware(card); 101013a9930dSWolfram Sang if (ret) { 10115259b329SSergio Paracuellos netdev_err(priv->net_dev, 1012156f2703SSergio Paracuellos "firmware load failed !! ret = %d\n", ret); 1013cf10e78eSTobin C. Harding goto err_free_netdev; 101413a9930dSWolfram Sang } 101513a9930dSWolfram Sang 10160e80e31aSSergio Paracuellos ks7010_sdio_init_irqs(func, priv); 101753638cefSsayli karnik 101813a9930dSWolfram Sang priv->dev_state = DEVICE_STATE_BOOT; 101913a9930dSWolfram Sang 10204487cf88SSergio Paracuellos priv->wq = alloc_workqueue("wq", WQ_MEM_RECLAIM, 1); 102118bd6dd1STobin C. Harding if (!priv->wq) { 10225259b329SSergio Paracuellos netdev_err(priv->net_dev, "create_workqueue failed !!\n"); 1023cf10e78eSTobin C. Harding goto err_free_netdev; 102413a9930dSWolfram Sang } 102513a9930dSWolfram Sang 102618bd6dd1STobin C. Harding INIT_DELAYED_WORK(&priv->rw_dwork, ks7010_rw_function); 1027e8593a8aSWolfram Sang ks7010_card_init(priv); 102813a9930dSWolfram Sang 10293fb54d75SWolfram Sang ret = register_netdev(priv->net_dev); 10303fb54d75SWolfram Sang if (ret) 1031cf10e78eSTobin C. Harding goto err_free_netdev; 10323fb54d75SWolfram Sang 103313a9930dSWolfram Sang return 0; 103413a9930dSWolfram Sang 1035f283dd69STobin C. Harding err_free_netdev: 10369962d86dSGustavo A. R. Silva free_netdev(netdev); 1037f283dd69STobin C. Harding err_release_irq: 103813a9930dSWolfram Sang sdio_claim_host(func); 103913a9930dSWolfram Sang sdio_release_irq(func); 1040f283dd69STobin C. Harding err_disable_func: 104113a9930dSWolfram Sang sdio_disable_func(func); 1042f283dd69STobin C. Harding err_free_card: 104313a9930dSWolfram Sang sdio_release_host(func); 104413a9930dSWolfram Sang sdio_set_drvdata(func, NULL); 104513a9930dSWolfram Sang kfree(card); 10462801d7a2SWolfram Sang 104713a9930dSWolfram Sang return -ENODEV; 104813a9930dSWolfram Sang } 104913a9930dSWolfram Sang 10502ab6fd59STobin C. Harding /* send stop request to MAC */ 10512ab6fd59STobin C. Harding static int send_stop_request(struct sdio_func *func) 10522ab6fd59STobin C. Harding { 10539f2ae0a4SQuytelda Kahja struct hostif_stop_request *pp; 10542ab6fd59STobin C. Harding struct ks_sdio_card *card; 10552ab6fd59STobin C. Harding size_t size; 10562ab6fd59STobin C. Harding 10572ab6fd59STobin C. Harding card = sdio_get_drvdata(func); 10582ab6fd59STobin C. Harding 10592ab6fd59STobin C. Harding pp = kzalloc(hif_align_size(sizeof(*pp)), GFP_KERNEL); 1060cea78d61SSergio Paracuellos if (!pp) 10612ab6fd59STobin C. Harding return -ENOMEM; 10622ab6fd59STobin C. Harding 10632ab6fd59STobin C. Harding size = sizeof(*pp) - sizeof(pp->header.size); 1064b2d187ccSSergio Paracuellos pp->header.size = cpu_to_le16(size); 1065b2d187ccSSergio Paracuellos pp->header.event = cpu_to_le16(HIF_STOP_REQ); 10662ab6fd59STobin C. Harding 10672ab6fd59STobin C. Harding sdio_claim_host(func); 106849705f9aSSergio Paracuellos write_to_device(card->priv, (u8 *)pp, hif_align_size(sizeof(*pp))); 10692ab6fd59STobin C. Harding sdio_release_host(func); 10702ab6fd59STobin C. Harding 10712ab6fd59STobin C. Harding kfree(pp); 10722ab6fd59STobin C. Harding return 0; 10732ab6fd59STobin C. Harding } 10742ab6fd59STobin C. Harding 1075c4730a92SWolfram Sang static void ks7010_sdio_remove(struct sdio_func *func) 107613a9930dSWolfram Sang { 107713a9930dSWolfram Sang int ret; 107813a9930dSWolfram Sang struct ks_sdio_card *card; 107913a9930dSWolfram Sang struct ks_wlan_private *priv; 1080697f9f7fSMuraru Mihaela 108113a9930dSWolfram Sang card = sdio_get_drvdata(func); 108213a9930dSWolfram Sang 1083c7e65f4dSsayli karnik if (!card) 108413a9930dSWolfram Sang return; 108513a9930dSWolfram Sang 108613a9930dSWolfram Sang priv = card->priv; 10870cc053ddSSergio Paracuellos if (!priv) 10880cc053ddSSergio Paracuellos goto err_free_card; 1089803394d0SColin Ian King 10900cc053ddSSergio Paracuellos ks_wlan_net_stop(priv->net_dev); 109113a9930dSWolfram Sang 109213a9930dSWolfram Sang /* interrupt disable */ 109313a9930dSWolfram Sang sdio_claim_host(func); 1094a704a1bcSSergio Paracuellos sdio_writeb(func, 0, INT_ENABLE_REG, &ret); 1095a704a1bcSSergio Paracuellos sdio_writeb(func, 0xff, INT_PENDING_REG, &ret); 109613a9930dSWolfram Sang sdio_release_host(func); 109713a9930dSWolfram Sang 10982ab6fd59STobin C. Harding ret = send_stop_request(func); 10992ab6fd59STobin C. Harding if (ret) /* memory allocation failure */ 11000cc053ddSSergio Paracuellos goto err_free_card; 1101697f9f7fSMuraru Mihaela 110218bd6dd1STobin C. Harding if (priv->wq) { 110318bd6dd1STobin C. Harding flush_workqueue(priv->wq); 110418bd6dd1STobin C. Harding destroy_workqueue(priv->wq); 110513a9930dSWolfram Sang } 110613a9930dSWolfram Sang 110713a9930dSWolfram Sang hostif_exit(priv); 110813a9930dSWolfram Sang 11090cc053ddSSergio Paracuellos unregister_netdev(priv->net_dev); 111013a9930dSWolfram Sang 111113a9930dSWolfram Sang trx_device_exit(priv); 111213a9930dSWolfram Sang free_netdev(priv->net_dev); 111313a9930dSWolfram Sang card->priv = NULL; 111413a9930dSWolfram Sang 111513a9930dSWolfram Sang sdio_claim_host(func); 111613a9930dSWolfram Sang sdio_release_irq(func); 111713a9930dSWolfram Sang sdio_disable_func(func); 111813a9930dSWolfram Sang sdio_release_host(func); 11190cc053ddSSergio Paracuellos err_free_card: 112013a9930dSWolfram Sang sdio_set_drvdata(func, NULL); 112113a9930dSWolfram Sang kfree(card); 112213a9930dSWolfram Sang } 112313a9930dSWolfram Sang 112478c74a5fSSergio Paracuellos static const struct sdio_device_id ks7010_sdio_ids[] = { 112578c74a5fSSergio Paracuellos {SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_A, SDIO_DEVICE_ID_KS_7010)}, 112678c74a5fSSergio Paracuellos {SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_B, SDIO_DEVICE_ID_KS_7010)}, 112778c74a5fSSergio Paracuellos { /* all zero */ } 112878c74a5fSSergio Paracuellos }; 112978c74a5fSSergio Paracuellos MODULE_DEVICE_TABLE(sdio, ks7010_sdio_ids); 113078c74a5fSSergio Paracuellos 11314c0d46d2SWolfram Sang static struct sdio_driver ks7010_sdio_driver = { 11324c0d46d2SWolfram Sang .name = "ks7010_sdio", 11334c0d46d2SWolfram Sang .id_table = ks7010_sdio_ids, 11344c0d46d2SWolfram Sang .probe = ks7010_sdio_probe, 11354c0d46d2SWolfram Sang .remove = ks7010_sdio_remove, 11364c0d46d2SWolfram Sang }; 11374c0d46d2SWolfram Sang 11386b0cb0b0SWolfram Sang module_driver(ks7010_sdio_driver, sdio_register_driver, sdio_unregister_driver); 1139e1240140SWolfram Sang MODULE_AUTHOR("Sang Engineering, Qi-Hardware, KeyStream"); 1140e1240140SWolfram Sang MODULE_DESCRIPTION("Driver for KeyStream KS7010 based SDIO cards"); 1141e1240140SWolfram Sang MODULE_LICENSE("GPL v2"); 1142e1240140SWolfram Sang MODULE_FIRMWARE(ROM_FILE); 1143