113a9930dSWolfram Sang /* 213a9930dSWolfram Sang * Driver for KeyStream, KS7010 based SDIO cards. 313a9930dSWolfram Sang * 413a9930dSWolfram Sang * Copyright (C) 2006-2008 KeyStream Corp. 513a9930dSWolfram Sang * Copyright (C) 2009 Renesas Technology Corp. 6c5d9a030SWolfram Sang * Copyright (C) 2016 Sang Engineering, Wolfram Sang 713a9930dSWolfram Sang * 813a9930dSWolfram Sang * This program is free software; you can redistribute it and/or modify 9c5d9a030SWolfram Sang * it under the terms of the GNU General Public License version 2 as 10c5d9a030SWolfram Sang * published by the Free Software Foundation. 1113a9930dSWolfram Sang */ 1213a9930dSWolfram Sang 13fa3fd846SSergio Paracuellos #include <linux/atomic.h> 14e6bc5053SSergio Paracuellos #include <linux/circ_buf.h> 151c013a5cSWolfram Sang #include <linux/firmware.h> 16fa3fd846SSergio Paracuellos #include <linux/jiffies.h> 1713a9930dSWolfram Sang #include <linux/mmc/card.h> 1813a9930dSWolfram Sang #include <linux/mmc/sdio_func.h> 19fa3fd846SSergio Paracuellos #include <linux/module.h> 201c013a5cSWolfram Sang #include <linux/workqueue.h> 2113a9930dSWolfram Sang #include "ks_wlan.h" 2213a9930dSWolfram Sang #include "ks_hostif.h" 2313a9930dSWolfram Sang #include "ks7010_sdio.h" 2413a9930dSWolfram Sang 252c54ee54SSergio Paracuellos #define ROM_FILE "ks7010sd.rom" 2648ecb225SSergio Paracuellos 2748ecb225SSergio Paracuellos /* SDIO KeyStream vendor and device */ 2848ecb225SSergio Paracuellos #define SDIO_VENDOR_ID_KS_CODE_A 0x005b 2948ecb225SSergio Paracuellos #define SDIO_VENDOR_ID_KS_CODE_B 0x0023 3048ecb225SSergio Paracuellos 3148ecb225SSergio Paracuellos /* Older sources suggest earlier versions were named 7910 or 79xx */ 3248ecb225SSergio Paracuellos #define SDIO_DEVICE_ID_KS_7010 0x7910 3348ecb225SSergio Paracuellos 3448ecb225SSergio Paracuellos /* Read/Write Status Register */ 35a704a1bcSSergio Paracuellos #define READ_STATUS_REG 0x000000 36a704a1bcSSergio Paracuellos #define WRITE_STATUS_REG 0x00000C 3748ecb225SSergio Paracuellos enum reg_status_type { 3848ecb225SSergio Paracuellos REG_STATUS_BUSY, 3948ecb225SSergio Paracuellos REG_STATUS_IDLE 4048ecb225SSergio Paracuellos }; 4148ecb225SSergio Paracuellos 4248ecb225SSergio Paracuellos /* Read Index Register */ 43a704a1bcSSergio Paracuellos #define READ_INDEX_REG 0x000004 4448ecb225SSergio Paracuellos 4548ecb225SSergio Paracuellos /* Read Data Size Register */ 46a704a1bcSSergio Paracuellos #define READ_DATA_SIZE_REG 0x000008 4748ecb225SSergio Paracuellos 4848ecb225SSergio Paracuellos /* Write Index Register */ 49a704a1bcSSergio Paracuellos #define WRITE_INDEX_REG 0x000010 5048ecb225SSergio Paracuellos 51004e43c2SSergio Paracuellos /* 52004e43c2SSergio Paracuellos * Write Status/Read Data Size Register 5348ecb225SSergio Paracuellos * for network packet (less than 2048 bytes data) 5448ecb225SSergio Paracuellos */ 55a704a1bcSSergio Paracuellos #define WSTATUS_RSIZE_REG 0x000014 56004e43c2SSergio Paracuellos 57004e43c2SSergio Paracuellos /* Write Status Register value */ 58004e43c2SSergio Paracuellos #define WSTATUS_MASK 0x80 59004e43c2SSergio Paracuellos 60004e43c2SSergio Paracuellos /* Read Data Size Register value [10:4] */ 61004e43c2SSergio Paracuellos #define RSIZE_MASK 0x7F 6248ecb225SSergio Paracuellos 6348ecb225SSergio Paracuellos /* ARM to SD interrupt Enable */ 64a704a1bcSSergio Paracuellos #define INT_ENABLE_REG 0x000020 6548ecb225SSergio Paracuellos /* ARM to SD interrupt Pending */ 66a704a1bcSSergio Paracuellos #define INT_PENDING_REG 0x000024 6748ecb225SSergio Paracuellos 6848ecb225SSergio Paracuellos #define INT_GCR_B BIT(7) 6948ecb225SSergio Paracuellos #define INT_GCR_A BIT(6) 7048ecb225SSergio Paracuellos #define INT_WRITE_STATUS BIT(5) 7148ecb225SSergio Paracuellos #define INT_WRITE_INDEX BIT(4) 7248ecb225SSergio Paracuellos #define INT_WRITE_SIZE BIT(3) 7348ecb225SSergio Paracuellos #define INT_READ_STATUS BIT(2) 7448ecb225SSergio Paracuellos #define INT_READ_INDEX BIT(1) 7548ecb225SSergio Paracuellos #define INT_READ_SIZE BIT(0) 7648ecb225SSergio Paracuellos 7748ecb225SSergio Paracuellos /* General Communication Register A */ 78a704a1bcSSergio Paracuellos #define GCR_A_REG 0x000028 7948ecb225SSergio Paracuellos enum gen_com_reg_a { 8048ecb225SSergio Paracuellos GCR_A_INIT, 8148ecb225SSergio Paracuellos GCR_A_REMAP, 8248ecb225SSergio Paracuellos GCR_A_RUN 8348ecb225SSergio Paracuellos }; 8448ecb225SSergio Paracuellos 8548ecb225SSergio Paracuellos /* General Communication Register B */ 86a704a1bcSSergio Paracuellos #define GCR_B_REG 0x00002C 8748ecb225SSergio Paracuellos enum gen_com_reg_b { 8848ecb225SSergio Paracuellos GCR_B_ACTIVE, 8948ecb225SSergio Paracuellos GCR_B_DOZE 9048ecb225SSergio Paracuellos }; 9148ecb225SSergio Paracuellos 9248ecb225SSergio Paracuellos /* Wakeup Register */ 93a704a1bcSSergio Paracuellos #define WAKEUP_REG 0x008018 9448ecb225SSergio Paracuellos #define WAKEUP_REQ 0x5a 9548ecb225SSergio Paracuellos 9648ecb225SSergio Paracuellos /* AHB Data Window 0x010000-0x01FFFF */ 9748ecb225SSergio Paracuellos #define DATA_WINDOW 0x010000 9848ecb225SSergio Paracuellos #define WINDOW_SIZE (64 * 1024) 9948ecb225SSergio Paracuellos 10048ecb225SSergio Paracuellos #define KS7010_IRAM_ADDRESS 0x06000000 10148ecb225SSergio Paracuellos 10213a9930dSWolfram Sang #define KS7010_IO_BLOCK_SIZE 512 10313a9930dSWolfram Sang 104f9b5bd05SWolfram Sang static const struct sdio_device_id ks7010_sdio_ids[] = { 10513a9930dSWolfram Sang {SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_A, SDIO_DEVICE_ID_KS_7010)}, 10613a9930dSWolfram Sang {SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_B, SDIO_DEVICE_ID_KS_7010)}, 10713a9930dSWolfram Sang { /* all zero */ } 10813a9930dSWolfram Sang }; 109f9b5bd05SWolfram Sang MODULE_DEVICE_TABLE(sdio, ks7010_sdio_ids); 11013a9930dSWolfram Sang 1112ec999e5SQuytelda Kahja static inline void inc_txqhead(struct ks_wlan_private *priv) 1122ec999e5SQuytelda Kahja { 1132ec999e5SQuytelda Kahja priv->tx_dev.qhead = (priv->tx_dev.qhead + 1) % TX_DEVICE_BUFF_SIZE; 1142ec999e5SQuytelda Kahja } 11513a9930dSWolfram Sang 1162ec999e5SQuytelda Kahja static inline void inc_txqtail(struct ks_wlan_private *priv) 1172ec999e5SQuytelda Kahja { 1182ec999e5SQuytelda Kahja priv->tx_dev.qtail = (priv->tx_dev.qtail + 1) % TX_DEVICE_BUFF_SIZE; 1192ec999e5SQuytelda Kahja } 1202ec999e5SQuytelda Kahja 121e6bc5053SSergio Paracuellos static inline bool txq_has_space(struct ks_wlan_private *priv) 1222ec999e5SQuytelda Kahja { 123e6bc5053SSergio Paracuellos return (CIRC_SPACE(priv->tx_dev.qhead, priv->tx_dev.qtail, 124e6bc5053SSergio Paracuellos TX_DEVICE_BUFF_SIZE) > 0); 1252ec999e5SQuytelda Kahja } 1262ec999e5SQuytelda Kahja 1272ec999e5SQuytelda Kahja static inline void inc_rxqhead(struct ks_wlan_private *priv) 1282ec999e5SQuytelda Kahja { 1292ec999e5SQuytelda Kahja priv->rx_dev.qhead = (priv->rx_dev.qhead + 1) % RX_DEVICE_BUFF_SIZE; 1302ec999e5SQuytelda Kahja } 1312ec999e5SQuytelda Kahja 1322ec999e5SQuytelda Kahja static inline void inc_rxqtail(struct ks_wlan_private *priv) 1332ec999e5SQuytelda Kahja { 1342ec999e5SQuytelda Kahja priv->rx_dev.qtail = (priv->rx_dev.qtail + 1) % RX_DEVICE_BUFF_SIZE; 1352ec999e5SQuytelda Kahja } 1362ec999e5SQuytelda Kahja 137e6bc5053SSergio Paracuellos static inline bool rxq_has_space(struct ks_wlan_private *priv) 1382ec999e5SQuytelda Kahja { 139e6bc5053SSergio Paracuellos return (CIRC_SPACE(priv->rx_dev.qhead, priv->rx_dev.qtail, 140e6bc5053SSergio Paracuellos RX_DEVICE_BUFF_SIZE) > 0); 141e6bc5053SSergio Paracuellos } 1422ec999e5SQuytelda Kahja 143e6bc5053SSergio Paracuellos static inline unsigned int txq_count(struct ks_wlan_private *priv) 144e6bc5053SSergio Paracuellos { 145e6bc5053SSergio Paracuellos return CIRC_CNT_TO_END(priv->tx_dev.qhead, priv->tx_dev.qtail, 146e6bc5053SSergio Paracuellos TX_DEVICE_BUFF_SIZE); 147e6bc5053SSergio Paracuellos } 148e6bc5053SSergio Paracuellos 149e6bc5053SSergio Paracuellos static inline unsigned int rxq_count(struct ks_wlan_private *priv) 150e6bc5053SSergio Paracuellos { 151e6bc5053SSergio Paracuellos return CIRC_CNT_TO_END(priv->rx_dev.qhead, priv->rx_dev.qtail, 152e6bc5053SSergio Paracuellos RX_DEVICE_BUFF_SIZE); 1532ec999e5SQuytelda Kahja } 15413a9930dSWolfram Sang 155f1e79f4bSTobin C. Harding /* Read single byte from device address into byte (CMD52) */ 156e0ba53a4SSergio Paracuellos static int ks7010_sdio_readb(struct ks_wlan_private *priv, 157e0ba53a4SSergio Paracuellos u32 address, u8 *byte) 158f1e79f4bSTobin C. Harding { 159f1e79f4bSTobin C. Harding struct sdio_func *func = priv->ks_sdio_card->func; 160f1e79f4bSTobin C. Harding int ret; 161f1e79f4bSTobin C. Harding 162f1e79f4bSTobin C. Harding *byte = sdio_readb(func, address, &ret); 163f1e79f4bSTobin C. Harding 164f1e79f4bSTobin C. Harding return ret; 165f1e79f4bSTobin C. Harding } 166f1e79f4bSTobin C. Harding 167f1e79f4bSTobin C. Harding /* Read length bytes from device address into buffer (CMD53) */ 168e0ba53a4SSergio Paracuellos static int ks7010_sdio_read(struct ks_wlan_private *priv, u32 address, 169e0ba53a4SSergio Paracuellos u8 *buffer, unsigned int length) 1704c0d46d2SWolfram Sang { 171f1e79f4bSTobin C. Harding struct sdio_func *func = priv->ks_sdio_card->func; 172f1e79f4bSTobin C. Harding 173f1e79f4bSTobin C. Harding return sdio_memcpy_fromio(func, buffer, address, length); 174f1e79f4bSTobin C. Harding } 175f1e79f4bSTobin C. Harding 176f1e79f4bSTobin C. Harding /* Write single byte to device address (CMD52) */ 177f1e79f4bSTobin C. Harding static int ks7010_sdio_writeb(struct ks_wlan_private *priv, 178e0ba53a4SSergio Paracuellos u32 address, u8 byte) 179f1e79f4bSTobin C. Harding { 180f1e79f4bSTobin C. Harding struct sdio_func *func = priv->ks_sdio_card->func; 1811770ae9dSTobin C. Harding int ret; 1824c0d46d2SWolfram Sang 183f1e79f4bSTobin C. Harding sdio_writeb(func, byte, address, &ret); 1844c0d46d2SWolfram Sang 1851770ae9dSTobin C. Harding return ret; 1861770ae9dSTobin C. Harding } 1874c0d46d2SWolfram Sang 188f1e79f4bSTobin C. Harding /* Write length bytes to device address from buffer (CMD53) */ 189e0ba53a4SSergio Paracuellos static int ks7010_sdio_write(struct ks_wlan_private *priv, u32 address, 190e0ba53a4SSergio Paracuellos u8 *buffer, unsigned int length) 1914c0d46d2SWolfram Sang { 192f1e79f4bSTobin C. Harding struct sdio_func *func = priv->ks_sdio_card->func; 1934c0d46d2SWolfram Sang 194f1e79f4bSTobin C. Harding return sdio_memcpy_toio(func, address, buffer, length); 1954c0d46d2SWolfram Sang } 1964c0d46d2SWolfram Sang 1974433459aSSergio Paracuellos static void ks_wlan_hw_sleep_doze_request(struct ks_wlan_private *priv) 19813a9930dSWolfram Sang { 1991770ae9dSTobin C. Harding int ret; 20013a9930dSWolfram Sang 20113a9930dSWolfram Sang /* clear request */ 20213a9930dSWolfram Sang atomic_set(&priv->sleepstatus.doze_request, 0); 20313a9930dSWolfram Sang 20413a9930dSWolfram Sang if (atomic_read(&priv->sleepstatus.status) == 0) { 205a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, GCR_B_REG, GCR_B_DOZE); 2061770ae9dSTobin C. Harding if (ret) { 207156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write GCR_B_REG\n"); 208f283dd69STobin C. Harding goto set_sleep_mode; 20913a9930dSWolfram Sang } 21013a9930dSWolfram Sang atomic_set(&priv->sleepstatus.status, 1); 21113a9930dSWolfram Sang priv->last_doze = jiffies; 21213a9930dSWolfram Sang } 21313a9930dSWolfram Sang 214f283dd69STobin C. Harding set_sleep_mode: 21513a9930dSWolfram Sang priv->sleep_mode = atomic_read(&priv->sleepstatus.status); 21613a9930dSWolfram Sang } 21713a9930dSWolfram Sang 2184433459aSSergio Paracuellos static void ks_wlan_hw_sleep_wakeup_request(struct ks_wlan_private *priv) 21913a9930dSWolfram Sang { 2201770ae9dSTobin C. Harding int ret; 22113a9930dSWolfram Sang 22213a9930dSWolfram Sang /* clear request */ 22313a9930dSWolfram Sang atomic_set(&priv->sleepstatus.wakeup_request, 0); 22413a9930dSWolfram Sang 22513a9930dSWolfram Sang if (atomic_read(&priv->sleepstatus.status) == 1) { 226a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, WAKEUP_REG, WAKEUP_REQ); 2271770ae9dSTobin C. Harding if (ret) { 228156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write WAKEUP_REG\n"); 229f283dd69STobin C. Harding goto set_sleep_mode; 23013a9930dSWolfram Sang } 23113a9930dSWolfram Sang atomic_set(&priv->sleepstatus.status, 0); 23213a9930dSWolfram Sang priv->last_wakeup = jiffies; 23313a9930dSWolfram Sang ++priv->wakeup_count; 23413a9930dSWolfram Sang } 23513a9930dSWolfram Sang 236f283dd69STobin C. Harding set_sleep_mode: 23713a9930dSWolfram Sang priv->sleep_mode = atomic_read(&priv->sleepstatus.status); 23813a9930dSWolfram Sang } 23913a9930dSWolfram Sang 240feedcf1aSWolfram Sang void ks_wlan_hw_wakeup_request(struct ks_wlan_private *priv) 24113a9930dSWolfram Sang { 2421770ae9dSTobin C. Harding int ret; 24313a9930dSWolfram Sang 24413a9930dSWolfram Sang if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { 245a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, WAKEUP_REG, WAKEUP_REQ); 2461770ae9dSTobin C. Harding if (ret) 247156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write WAKEUP_REG\n"); 24853638cefSsayli karnik 24913a9930dSWolfram Sang priv->last_wakeup = jiffies; 25013a9930dSWolfram Sang ++priv->wakeup_count; 25113a9930dSWolfram Sang } 25213a9930dSWolfram Sang } 25313a9930dSWolfram Sang 254fa740a9eSTobin C. Harding static void _ks_wlan_hw_power_save(struct ks_wlan_private *priv) 25513a9930dSWolfram Sang { 256f1e79f4bSTobin C. Harding unsigned char byte; 257f7172487STobin C. Harding int ret; 25813a9930dSWolfram Sang 2598fb8e05cSTobin C. Harding if (priv->reg.power_mgmt == POWER_MGMT_ACTIVE) 260fa740a9eSTobin C. Harding return; 26113a9930dSWolfram Sang 262482c03c7STobin C. Harding if (priv->reg.operation_mode != MODE_INFRASTRUCTURE) 263482c03c7STobin C. Harding return; 264482c03c7STobin C. Harding 2650e24eb8aSTobin C. Harding if (!is_connect_status(priv->connect_status)) 266fa740a9eSTobin C. Harding return; 267d5f1db31STobin C. Harding 268d5f1db31STobin C. Harding if (priv->dev_state != DEVICE_STATE_SLEEP) 269fa740a9eSTobin C. Harding return; 270d5f1db31STobin C. Harding 2713188bc09STobin C. Harding if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) 272fa740a9eSTobin C. Harding return; 2733188bc09STobin C. Harding 2745259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "\npsstatus.status=%d\npsstatus.confirm_wait=%d\npsstatus.snooze_guard=%d\ncnt_txqbody=%d\n", 27513a9930dSWolfram Sang atomic_read(&priv->psstatus.status), 27613a9930dSWolfram Sang atomic_read(&priv->psstatus.confirm_wait), 27713a9930dSWolfram Sang atomic_read(&priv->psstatus.snooze_guard), 278e6bc5053SSergio Paracuellos txq_count(priv)); 27913a9930dSWolfram Sang 280fa740a9eSTobin C. Harding if (atomic_read(&priv->psstatus.confirm_wait) || 281fa740a9eSTobin C. Harding atomic_read(&priv->psstatus.snooze_guard) || 282e6bc5053SSergio Paracuellos txq_has_space(priv)) { 28318bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 0); 284fa740a9eSTobin C. Harding return; 285fa740a9eSTobin C. Harding } 286fa740a9eSTobin C. Harding 287a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, INT_PENDING_REG, &byte); 288f7172487STobin C. Harding if (ret) { 289156f2703SSergio Paracuellos netdev_err(priv->net_dev, "read INT_PENDING_REG\n"); 290f8641485STobin C. Harding goto queue_delayed_work; 29113a9930dSWolfram Sang } 292f1e79f4bSTobin C. Harding if (byte) 293f8641485STobin C. Harding goto queue_delayed_work; 294fa740a9eSTobin C. Harding 295a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, GCR_B_REG, GCR_B_DOZE); 296f7172487STobin C. Harding if (ret) { 297156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write GCR_B_REG\n"); 298f8641485STobin C. Harding goto queue_delayed_work; 29913a9930dSWolfram Sang } 3009f9d7030STobin C. Harding atomic_set(&priv->psstatus.status, PS_SNOOZE); 30113a9930dSWolfram Sang 302fa740a9eSTobin C. Harding return; 303f8641485STobin C. Harding 304f8641485STobin C. Harding queue_delayed_work: 30518bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 1); 30613a9930dSWolfram Sang } 30713a9930dSWolfram Sang 308feedcf1aSWolfram Sang int ks_wlan_hw_power_save(struct ks_wlan_private *priv) 30913a9930dSWolfram Sang { 31018bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 1); 31113a9930dSWolfram Sang return 0; 31213a9930dSWolfram Sang } 31313a9930dSWolfram Sang 314cdf6ecc5SWolfram Sang static int enqueue_txdev(struct ks_wlan_private *priv, unsigned char *p, 315cdf6ecc5SWolfram Sang unsigned long size, 316055da4f9STobin C. Harding void (*complete_handler)(struct ks_wlan_private *priv, 317055da4f9STobin C. Harding struct sk_buff *skb), 318055da4f9STobin C. Harding struct sk_buff *skb) 31913a9930dSWolfram Sang { 32013a9930dSWolfram Sang struct tx_device_buffer *sp; 3211770ae9dSTobin C. Harding int ret; 32213a9930dSWolfram Sang 32313a9930dSWolfram Sang if (priv->dev_state < DEVICE_STATE_BOOT) { 3241770ae9dSTobin C. Harding ret = -EPERM; 325aa6ca807STobin C. Harding goto err_complete; 32613a9930dSWolfram Sang } 32713a9930dSWolfram Sang 328e6bc5053SSergio Paracuellos if ((TX_DEVICE_BUFF_SIZE - 1) <= txq_count(priv)) { 3295259b329SSergio Paracuellos netdev_err(priv->net_dev, "tx buffer overflow\n"); 3301770ae9dSTobin C. Harding ret = -EOVERFLOW; 331aa6ca807STobin C. Harding goto err_complete; 33213a9930dSWolfram Sang } 33313a9930dSWolfram Sang 33413a9930dSWolfram Sang sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qtail]; 33513a9930dSWolfram Sang sp->sendp = p; 33613a9930dSWolfram Sang sp->size = size; 33713a9930dSWolfram Sang sp->complete_handler = complete_handler; 338055da4f9STobin C. Harding sp->skb = skb; 33913a9930dSWolfram Sang inc_txqtail(priv); 34013a9930dSWolfram Sang 34113a9930dSWolfram Sang return 0; 342aa6ca807STobin C. Harding 343aa6ca807STobin C. Harding err_complete: 344aa6ca807STobin C. Harding kfree(p); 345aa6ca807STobin C. Harding if (complete_handler) 346055da4f9STobin C. Harding (*complete_handler)(priv, skb); 347aa6ca807STobin C. Harding 3481770ae9dSTobin C. Harding return ret; 34913a9930dSWolfram Sang } 35013a9930dSWolfram Sang 35113a9930dSWolfram Sang /* write data */ 352cdf6ecc5SWolfram Sang static int write_to_device(struct ks_wlan_private *priv, unsigned char *buffer, 353cdf6ecc5SWolfram Sang unsigned long size) 35413a9930dSWolfram Sang { 35513a9930dSWolfram Sang struct hostif_hdr *hdr; 3561770ae9dSTobin C. Harding int ret; 357697f9f7fSMuraru Mihaela 35813a9930dSWolfram Sang hdr = (struct hostif_hdr *)buffer; 35913a9930dSWolfram Sang 360d9d1ffd4SCezary Gapinski if (le16_to_cpu(hdr->event) < HIF_DATA_REQ || 361d9d1ffd4SCezary Gapinski le16_to_cpu(hdr->event) > HIF_REQ_MAX) { 3625259b329SSergio Paracuellos netdev_err(priv->net_dev, "unknown event=%04X\n", hdr->event); 36313a9930dSWolfram Sang return 0; 36413a9930dSWolfram Sang } 36513a9930dSWolfram Sang 3661770ae9dSTobin C. Harding ret = ks7010_sdio_write(priv, DATA_WINDOW, buffer, size); 3671770ae9dSTobin C. Harding if (ret) { 368156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write DATA_WINDOW\n"); 3691770ae9dSTobin C. Harding return ret; 37013a9930dSWolfram Sang } 37113a9930dSWolfram Sang 372a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, WRITE_STATUS_REG, REG_STATUS_BUSY); 3731770ae9dSTobin C. Harding if (ret) { 374156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write WRITE_STATUS_REG\n"); 3751770ae9dSTobin C. Harding return ret; 37613a9930dSWolfram Sang } 37713a9930dSWolfram Sang 37813a9930dSWolfram Sang return 0; 37913a9930dSWolfram Sang } 38013a9930dSWolfram Sang 3815141e9c6STobin C. Harding static void tx_device_task(struct ks_wlan_private *priv) 38213a9930dSWolfram Sang { 38313a9930dSWolfram Sang struct tx_device_buffer *sp; 38403b02449STobin C. Harding int ret; 38513a9930dSWolfram Sang 386e6bc5053SSergio Paracuellos if (!txq_has_space(priv) || 387638a75b6STobin C. Harding atomic_read(&priv->psstatus.status) == PS_SNOOZE) 388638a75b6STobin C. Harding return; 389638a75b6STobin C. Harding 39013a9930dSWolfram Sang sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead]; 39113a9930dSWolfram Sang if (priv->dev_state >= DEVICE_STATE_BOOT) { 39203b02449STobin C. Harding ret = write_to_device(priv, sp->sendp, sp->size); 39303b02449STobin C. Harding if (ret) { 3947dd51ea1SSergio Paracuellos netdev_err(priv->net_dev, 3957dd51ea1SSergio Paracuellos "write_to_device error !!(%d)\n", ret); 39618bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 1); 39713a9930dSWolfram Sang return; 39813a9930dSWolfram Sang } 39913a9930dSWolfram Sang } 400638a75b6STobin C. Harding kfree(sp->sendp); 401c7e65f4dSsayli karnik if (sp->complete_handler) /* TX Complete */ 402055da4f9STobin C. Harding (*sp->complete_handler)(priv, sp->skb); 40313a9930dSWolfram Sang inc_txqhead(priv); 40413a9930dSWolfram Sang 405e6bc5053SSergio Paracuellos if (txq_has_space(priv)) 40618bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 0); 40713a9930dSWolfram Sang } 40813a9930dSWolfram Sang 409feedcf1aSWolfram Sang int ks_wlan_hw_tx(struct ks_wlan_private *priv, void *p, unsigned long size, 410055da4f9STobin C. Harding void (*complete_handler)(struct ks_wlan_private *priv, 411055da4f9STobin C. Harding struct sk_buff *skb), 412055da4f9STobin C. Harding struct sk_buff *skb) 41313a9930dSWolfram Sang { 41413a9930dSWolfram Sang int result = 0; 41513a9930dSWolfram Sang struct hostif_hdr *hdr; 416697f9f7fSMuraru Mihaela 41713a9930dSWolfram Sang hdr = (struct hostif_hdr *)p; 41813a9930dSWolfram Sang 419d9d1ffd4SCezary Gapinski if (le16_to_cpu(hdr->event) < HIF_DATA_REQ || 420d9d1ffd4SCezary Gapinski le16_to_cpu(hdr->event) > HIF_REQ_MAX) { 4215259b329SSergio Paracuellos netdev_err(priv->net_dev, "unknown event=%04X\n", hdr->event); 42213a9930dSWolfram Sang return 0; 42313a9930dSWolfram Sang } 42413a9930dSWolfram Sang 42513a9930dSWolfram Sang /* add event to hostt buffer */ 426d9d1ffd4SCezary Gapinski priv->hostt.buff[priv->hostt.qtail] = le16_to_cpu(hdr->event); 42713a9930dSWolfram Sang priv->hostt.qtail = (priv->hostt.qtail + 1) % SME_EVENT_BUFF_SIZE; 42813a9930dSWolfram Sang 42913a9930dSWolfram Sang spin_lock(&priv->tx_dev.tx_dev_lock); 430055da4f9STobin C. Harding result = enqueue_txdev(priv, p, size, complete_handler, skb); 43113a9930dSWolfram Sang spin_unlock(&priv->tx_dev.tx_dev_lock); 43213a9930dSWolfram Sang 433e6bc5053SSergio Paracuellos if (txq_has_space(priv)) 43418bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 0); 43518bd6dd1STobin C. Harding 43613a9930dSWolfram Sang return result; 43713a9930dSWolfram Sang } 43813a9930dSWolfram Sang 43913a9930dSWolfram Sang static void rx_event_task(unsigned long dev) 44013a9930dSWolfram Sang { 441feedcf1aSWolfram Sang struct ks_wlan_private *priv = (struct ks_wlan_private *)dev; 44213a9930dSWolfram Sang struct rx_device_buffer *rp; 44313a9930dSWolfram Sang 444e6bc5053SSergio Paracuellos if (rxq_has_space(priv) && priv->dev_state >= DEVICE_STATE_BOOT) { 44513a9930dSWolfram Sang rp = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qhead]; 44613a9930dSWolfram Sang hostif_receive(priv, rp->data, rp->size); 44713a9930dSWolfram Sang inc_rxqhead(priv); 44813a9930dSWolfram Sang 449e6bc5053SSergio Paracuellos if (rxq_has_space(priv)) 450321dabdcSTobin C. Harding tasklet_schedule(&priv->rx_bh_task); 45113a9930dSWolfram Sang } 45213a9930dSWolfram Sang } 45313a9930dSWolfram Sang 4545141e9c6STobin C. Harding static void ks_wlan_hw_rx(struct ks_wlan_private *priv, uint16_t size) 45513a9930dSWolfram Sang { 456f7172487STobin C. Harding int ret; 45713a9930dSWolfram Sang struct rx_device_buffer *rx_buffer; 45813a9930dSWolfram Sang struct hostif_hdr *hdr; 45913a9930dSWolfram Sang unsigned short event = 0; 46013a9930dSWolfram Sang 46113a9930dSWolfram Sang /* receive data */ 462e6bc5053SSergio Paracuellos if (rxq_count(priv) >= (RX_DEVICE_BUFF_SIZE - 1)) { 4635259b329SSergio Paracuellos netdev_err(priv->net_dev, "rx buffer overflow\n"); 46413b05e46STobin C. Harding return; 46513a9930dSWolfram Sang } 46613a9930dSWolfram Sang rx_buffer = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qtail]; 46713a9930dSWolfram Sang 468f7172487STobin C. Harding ret = ks7010_sdio_read(priv, DATA_WINDOW, &rx_buffer->data[0], 469cdf6ecc5SWolfram Sang hif_align_size(size)); 470f7172487STobin C. Harding if (ret) 47113b05e46STobin C. Harding return; 47213a9930dSWolfram Sang 47313a9930dSWolfram Sang /* length check */ 47413a9930dSWolfram Sang if (size > 2046 || size == 0) { 47531ce0d86SSergio Paracuellos #ifdef DEBUG 476cdf6ecc5SWolfram Sang print_hex_dump_bytes("INVALID DATA dump: ", 477cdf6ecc5SWolfram Sang DUMP_PREFIX_OFFSET, 4783215bb1aSWolfram Sang rx_buffer->data, 32); 4793215bb1aSWolfram Sang #endif 480a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, READ_STATUS_REG, REG_STATUS_IDLE); 481f7172487STobin C. Harding if (ret) 482156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write READ_STATUS_REG\n"); 48353638cefSsayli karnik 48413b05e46STobin C. Harding /* length check fail */ 48513b05e46STobin C. Harding return; 48613a9930dSWolfram Sang } 48713a9930dSWolfram Sang 48813a9930dSWolfram Sang hdr = (struct hostif_hdr *)&rx_buffer->data[0]; 48913a9930dSWolfram Sang rx_buffer->size = le16_to_cpu(hdr->size) + sizeof(hdr->size); 490d9d1ffd4SCezary Gapinski event = le16_to_cpu(hdr->event); 49113a9930dSWolfram Sang inc_rxqtail(priv); 49213a9930dSWolfram Sang 493a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, READ_STATUS_REG, REG_STATUS_IDLE); 494f7172487STobin C. Harding if (ret) 495156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write READ_STATUS_REG\n"); 49613a9930dSWolfram Sang 4977570d757SSergio Paracuellos if (atomic_read(&priv->psstatus.confirm_wait) && is_hif_conf(event)) { 4985259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "IS_HIF_CONF true !!\n"); 49913a9930dSWolfram Sang atomic_dec(&priv->psstatus.confirm_wait); 50013a9930dSWolfram Sang } 50113a9930dSWolfram Sang 502321dabdcSTobin C. Harding tasklet_schedule(&priv->rx_bh_task); 50313a9930dSWolfram Sang } 50413a9930dSWolfram Sang 50513a9930dSWolfram Sang static void ks7010_rw_function(struct work_struct *work) 50613a9930dSWolfram Sang { 50713a9930dSWolfram Sang struct ks_wlan_private *priv; 508f1e79f4bSTobin C. Harding unsigned char byte; 5091770ae9dSTobin C. Harding int ret; 51013a9930dSWolfram Sang 51118bd6dd1STobin C. Harding priv = container_of(work, struct ks_wlan_private, rw_dwork.work); 51213a9930dSWolfram Sang 513e61e73d7STobin C. Harding /* wait after DOZE */ 5146adc30b1SSergio Paracuellos if (time_after(priv->last_doze + msecs_to_jiffies(30), jiffies)) { 5155259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "wait after DOZE\n"); 51618bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 1); 51713a9930dSWolfram Sang return; 51813a9930dSWolfram Sang } 51913a9930dSWolfram Sang 520e61e73d7STobin C. Harding /* wait after WAKEUP */ 5216adc30b1SSergio Paracuellos while (time_after(priv->last_wakeup + msecs_to_jiffies(30), jiffies)) { 5225259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "wait after WAKEUP\n"); 52318bd6dd1STobin C. Harding dev_info(&priv->ks_sdio_card->func->dev, 5249887b5e5SSabitha George "wake: %lu %lu\n", 5256adc30b1SSergio Paracuellos priv->last_wakeup + msecs_to_jiffies(30), jiffies); 52613a9930dSWolfram Sang msleep(30); 52713a9930dSWolfram Sang } 52813a9930dSWolfram Sang 52918bd6dd1STobin C. Harding sdio_claim_host(priv->ks_sdio_card->func); 53013a9930dSWolfram Sang 53113a9930dSWolfram Sang /* power save wakeup */ 53213a9930dSWolfram Sang if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { 533e6bc5053SSergio Paracuellos if (txq_has_space(priv)) { 53413a9930dSWolfram Sang ks_wlan_hw_wakeup_request(priv); 53518bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 1); 53613a9930dSWolfram Sang } 5370dd30a74STobin C. Harding goto release_host; 53813a9930dSWolfram Sang } 53913a9930dSWolfram Sang 54013a9930dSWolfram Sang /* sleep mode doze */ 54113a9930dSWolfram Sang if (atomic_read(&priv->sleepstatus.doze_request) == 1) { 54213a9930dSWolfram Sang ks_wlan_hw_sleep_doze_request(priv); 5430dd30a74STobin C. Harding goto release_host; 54413a9930dSWolfram Sang } 54513a9930dSWolfram Sang /* sleep mode wakeup */ 54613a9930dSWolfram Sang if (atomic_read(&priv->sleepstatus.wakeup_request) == 1) { 54713a9930dSWolfram Sang ks_wlan_hw_sleep_wakeup_request(priv); 5480dd30a74STobin C. Harding goto release_host; 54913a9930dSWolfram Sang } 55013a9930dSWolfram Sang 55113a9930dSWolfram Sang /* read (WriteStatus/ReadDataSize FN1:00_0014) */ 552a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, WSTATUS_RSIZE_REG, &byte); 5531770ae9dSTobin C. Harding if (ret) { 554156f2703SSergio Paracuellos netdev_err(priv->net_dev, "read WSTATUS_RSIZE_REG psstatus=%d\n", 555cdf6ecc5SWolfram Sang atomic_read(&priv->psstatus.status)); 5560dd30a74STobin C. Harding goto release_host; 55713a9930dSWolfram Sang } 55813a9930dSWolfram Sang 559f1e79f4bSTobin C. Harding if (byte & RSIZE_MASK) { /* Read schedule */ 560f1e79f4bSTobin C. Harding ks_wlan_hw_rx(priv, (uint16_t)((byte & RSIZE_MASK) << 4)); 56113a9930dSWolfram Sang } 562f1e79f4bSTobin C. Harding if ((byte & WSTATUS_MASK)) 5635141e9c6STobin C. Harding tx_device_task(priv); 56453638cefSsayli karnik 56513a9930dSWolfram Sang _ks_wlan_hw_power_save(priv); 56613a9930dSWolfram Sang 5670dd30a74STobin C. Harding release_host: 56818bd6dd1STobin C. Harding sdio_release_host(priv->ks_sdio_card->func); 56913a9930dSWolfram Sang } 57013a9930dSWolfram Sang 57113a9930dSWolfram Sang static void ks_sdio_interrupt(struct sdio_func *func) 57213a9930dSWolfram Sang { 573f7172487STobin C. Harding int ret; 57413a9930dSWolfram Sang struct ks_sdio_card *card; 575feedcf1aSWolfram Sang struct ks_wlan_private *priv; 576f1e79f4bSTobin C. Harding unsigned char status, rsize, byte; 57713a9930dSWolfram Sang 57813a9930dSWolfram Sang card = sdio_get_drvdata(func); 57913a9930dSWolfram Sang priv = card->priv; 58013a9930dSWolfram Sang 581638a75b6STobin C. Harding if (priv->dev_state < DEVICE_STATE_BOOT) 582638a75b6STobin C. Harding goto queue_delayed_work; 583638a75b6STobin C. Harding 584a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, INT_PENDING_REG, &status); 585f7172487STobin C. Harding if (ret) { 586156f2703SSergio Paracuellos netdev_err(priv->net_dev, "read INT_PENDING_REG\n"); 587f283dd69STobin C. Harding goto queue_delayed_work; 58813a9930dSWolfram Sang } 58913a9930dSWolfram Sang 59013a9930dSWolfram Sang /* schedule task for interrupt status */ 59113a9930dSWolfram Sang /* bit7 -> Write General Communication B register */ 59213a9930dSWolfram Sang /* read (General Communication B register) */ 59313a9930dSWolfram Sang /* bit5 -> Write Status Idle */ 59413a9930dSWolfram Sang /* bit2 -> Read Status Busy */ 595ddd10774SXiangyang Zhang if (status & INT_GCR_B || 596ddd10774SXiangyang Zhang atomic_read(&priv->psstatus.status) == PS_SNOOZE) { 597a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, GCR_B_REG, &byte); 598f7172487STobin C. Harding if (ret) { 599156f2703SSergio Paracuellos netdev_err(priv->net_dev, "read GCR_B_REG\n"); 600f283dd69STobin C. Harding goto queue_delayed_work; 60113a9930dSWolfram Sang } 602f1e79f4bSTobin C. Harding if (byte == GCR_B_ACTIVE) { 603638a75b6STobin C. Harding if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { 604638a75b6STobin C. Harding atomic_set(&priv->psstatus.status, PS_WAKEUP); 60513a9930dSWolfram Sang priv->wakeup_count = 0; 60613a9930dSWolfram Sang } 60713a9930dSWolfram Sang complete(&priv->psstatus.wakeup_wait); 60813a9930dSWolfram Sang } 60913a9930dSWolfram Sang } 61013a9930dSWolfram Sang 61113a9930dSWolfram Sang do { 61213a9930dSWolfram Sang /* read (WriteStatus/ReadDataSize FN1:00_0014) */ 613a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, WSTATUS_RSIZE_REG, &byte); 614f7172487STobin C. Harding if (ret) { 615156f2703SSergio Paracuellos netdev_err(priv->net_dev, "read WSTATUS_RSIZE_REG\n"); 616f283dd69STobin C. Harding goto queue_delayed_work; 61713a9930dSWolfram Sang } 618f1e79f4bSTobin C. Harding rsize = byte & RSIZE_MASK; 6195141e9c6STobin C. Harding if (rsize != 0) /* Read schedule */ 6205141e9c6STobin C. Harding ks_wlan_hw_rx(priv, (uint16_t)(rsize << 4)); 6215141e9c6STobin C. Harding 622f1e79f4bSTobin C. Harding if (byte & WSTATUS_MASK) { 62313a9930dSWolfram Sang if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { 624e6bc5053SSergio Paracuellos if (txq_has_space(priv)) { 62513a9930dSWolfram Sang ks_wlan_hw_wakeup_request(priv); 6267dd51ea1SSergio Paracuellos queue_delayed_work(priv->wq, 6277dd51ea1SSergio Paracuellos &priv->rw_dwork, 1); 62813a9930dSWolfram Sang return; 62913a9930dSWolfram Sang } 630cdf6ecc5SWolfram Sang } else { 6315141e9c6STobin C. Harding tx_device_task(priv); 63213a9930dSWolfram Sang } 63313a9930dSWolfram Sang } 63413a9930dSWolfram Sang } while (rsize); 63513a9930dSWolfram Sang 636f283dd69STobin C. Harding queue_delayed_work: 63718bd6dd1STobin C. Harding queue_delayed_work(priv->wq, &priv->rw_dwork, 0); 63813a9930dSWolfram Sang } 63913a9930dSWolfram Sang 640feedcf1aSWolfram Sang static int trx_device_init(struct ks_wlan_private *priv) 64113a9930dSWolfram Sang { 64220358d13SNick Rosbrook priv->tx_dev.qhead = 0; 64320358d13SNick Rosbrook priv->tx_dev.qtail = 0; 64413a9930dSWolfram Sang 64520358d13SNick Rosbrook priv->rx_dev.qhead = 0; 64620358d13SNick Rosbrook priv->rx_dev.qtail = 0; 64713a9930dSWolfram Sang 64813a9930dSWolfram Sang spin_lock_init(&priv->tx_dev.tx_dev_lock); 64913a9930dSWolfram Sang spin_lock_init(&priv->rx_dev.rx_dev_lock); 65013a9930dSWolfram Sang 651321dabdcSTobin C. Harding tasklet_init(&priv->rx_bh_task, rx_event_task, (unsigned long)priv); 65213a9930dSWolfram Sang 65313a9930dSWolfram Sang return 0; 65413a9930dSWolfram Sang } 65513a9930dSWolfram Sang 656feedcf1aSWolfram Sang static void trx_device_exit(struct ks_wlan_private *priv) 65713a9930dSWolfram Sang { 65813a9930dSWolfram Sang struct tx_device_buffer *sp; 65913a9930dSWolfram Sang 66013a9930dSWolfram Sang /* tx buffer clear */ 661e6bc5053SSergio Paracuellos while (txq_has_space(priv)) { 66213a9930dSWolfram Sang sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead]; 663e61e73d7STobin C. Harding kfree(sp->sendp); 664c7e65f4dSsayli karnik if (sp->complete_handler) /* TX Complete */ 665055da4f9STobin C. Harding (*sp->complete_handler)(priv, sp->skb); 66613a9930dSWolfram Sang inc_txqhead(priv); 66713a9930dSWolfram Sang } 66813a9930dSWolfram Sang 669321dabdcSTobin C. Harding tasklet_kill(&priv->rx_bh_task); 67013a9930dSWolfram Sang } 671cdf6ecc5SWolfram Sang 672feedcf1aSWolfram Sang static int ks7010_sdio_update_index(struct ks_wlan_private *priv, u32 index) 67313a9930dSWolfram Sang { 6741770ae9dSTobin C. Harding int ret; 67513a9930dSWolfram Sang unsigned char *data_buf; 67613a9930dSWolfram Sang 67747bda74aSJi-Hun Kim data_buf = kmemdup(&index, sizeof(u32), GFP_KERNEL); 678aa6ca807STobin C. Harding if (!data_buf) 679aa6ca807STobin C. Harding return -ENOMEM; 68013a9930dSWolfram Sang 681a704a1bcSSergio Paracuellos ret = ks7010_sdio_write(priv, WRITE_INDEX_REG, data_buf, sizeof(index)); 6821770ae9dSTobin C. Harding if (ret) 683f283dd69STobin C. Harding goto err_free_data_buf; 68413a9930dSWolfram Sang 685a704a1bcSSergio Paracuellos ret = ks7010_sdio_write(priv, READ_INDEX_REG, data_buf, sizeof(index)); 6861770ae9dSTobin C. Harding if (ret) 687f283dd69STobin C. Harding goto err_free_data_buf; 688aa6ca807STobin C. Harding 689aa6ca807STobin C. Harding return 0; 690aa6ca807STobin C. Harding 691f283dd69STobin C. Harding err_free_data_buf: 692cdf6ecc5SWolfram Sang kfree(data_buf); 693aa6ca807STobin C. Harding 6941770ae9dSTobin C. Harding return ret; 69513a9930dSWolfram Sang } 69613a9930dSWolfram Sang 69713a9930dSWolfram Sang #define ROM_BUFF_SIZE (64 * 1024) 698feedcf1aSWolfram Sang static int ks7010_sdio_data_compare(struct ks_wlan_private *priv, u32 address, 69913a9930dSWolfram Sang unsigned char *data, unsigned int size) 70013a9930dSWolfram Sang { 7011770ae9dSTobin C. Harding int ret; 70213a9930dSWolfram Sang unsigned char *read_buf; 703eeed92c0SMarkus Elfring 70413a9930dSWolfram Sang read_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL); 705aa6ca807STobin C. Harding if (!read_buf) 706aa6ca807STobin C. Harding return -ENOMEM; 70713a9930dSWolfram Sang 7081770ae9dSTobin C. Harding ret = ks7010_sdio_read(priv, address, read_buf, size); 7091770ae9dSTobin C. Harding if (ret) 710f283dd69STobin C. Harding goto err_free_read_buf; 711aa6ca807STobin C. Harding 71258af272bSTobin C. Harding if (memcmp(data, read_buf, size) != 0) { 71358af272bSTobin C. Harding ret = -EIO; 7145259b329SSergio Paracuellos netdev_err(priv->net_dev, "data compare error (%d)\n", ret); 715f283dd69STobin C. Harding goto err_free_read_buf; 71613a9930dSWolfram Sang } 717aa6ca807STobin C. Harding 718aa6ca807STobin C. Harding return 0; 719aa6ca807STobin C. Harding 720f283dd69STobin C. Harding err_free_read_buf: 721cdf6ecc5SWolfram Sang kfree(read_buf); 722aa6ca807STobin C. Harding 7231770ae9dSTobin C. Harding return ret; 72413a9930dSWolfram Sang } 725cdf6ecc5SWolfram Sang 72667082c22SSergio Paracuellos static int ks7010_copy_firmware(struct ks_wlan_private *priv, 72767082c22SSergio Paracuellos const struct firmware *fw_entry) 72813a9930dSWolfram Sang { 729881f76b9STobin C. Harding unsigned int length; 73067082c22SSergio Paracuellos unsigned int size; 73167082c22SSergio Paracuellos unsigned int offset; 73267082c22SSergio Paracuellos unsigned int n = 0; 73367082c22SSergio Paracuellos unsigned char *rom_buf; 73467082c22SSergio Paracuellos int ret; 73513a9930dSWolfram Sang 73613a9930dSWolfram Sang rom_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL); 737369e1b69SSudip Mukherjee if (!rom_buf) 738aa6ca807STobin C. Harding return -ENOMEM; 73913a9930dSWolfram Sang 74013a9930dSWolfram Sang length = fw_entry->size; 74113a9930dSWolfram Sang 74213a9930dSWolfram Sang do { 74313a9930dSWolfram Sang if (length >= ROM_BUFF_SIZE) { 74413a9930dSWolfram Sang size = ROM_BUFF_SIZE; 74513a9930dSWolfram Sang length = length - ROM_BUFF_SIZE; 746cdf6ecc5SWolfram Sang } else { 74713a9930dSWolfram Sang size = length; 74813a9930dSWolfram Sang length = 0; 74913a9930dSWolfram Sang } 750cdf6ecc5SWolfram Sang if (size == 0) 751cdf6ecc5SWolfram Sang break; 752bd6dad98SSergio Paracuellos 75313a9930dSWolfram Sang memcpy(rom_buf, fw_entry->data + n, size); 754e61e73d7STobin C. Harding 75513a9930dSWolfram Sang offset = n; 756bd6dad98SSergio Paracuellos ret = ks7010_sdio_update_index(priv, 757bd6dad98SSergio Paracuellos KS7010_IRAM_ADDRESS + offset); 7581770ae9dSTobin C. Harding if (ret) 75967082c22SSergio Paracuellos goto free_rom_buf; 76013a9930dSWolfram Sang 7611770ae9dSTobin C. Harding ret = ks7010_sdio_write(priv, DATA_WINDOW, rom_buf, size); 7621770ae9dSTobin C. Harding if (ret) 76367082c22SSergio Paracuellos goto free_rom_buf; 76413a9930dSWolfram Sang 765bd6dad98SSergio Paracuellos ret = ks7010_sdio_data_compare(priv, 766bd6dad98SSergio Paracuellos DATA_WINDOW, rom_buf, size); 7671770ae9dSTobin C. Harding if (ret) 76867082c22SSergio Paracuellos goto free_rom_buf; 769aa6ca807STobin C. Harding 77013a9930dSWolfram Sang n += size; 77113a9930dSWolfram Sang 77213a9930dSWolfram Sang } while (size); 77313a9930dSWolfram Sang 774a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, GCR_A_REG, GCR_A_REMAP); 77567082c22SSergio Paracuellos 77667082c22SSergio Paracuellos free_rom_buf: 77767082c22SSergio Paracuellos kfree(rom_buf); 77867082c22SSergio Paracuellos return ret; 77967082c22SSergio Paracuellos } 78067082c22SSergio Paracuellos 78167082c22SSergio Paracuellos static int ks7010_upload_firmware(struct ks_sdio_card *card) 78267082c22SSergio Paracuellos { 78367082c22SSergio Paracuellos struct ks_wlan_private *priv = card->priv; 78467082c22SSergio Paracuellos unsigned int n; 78567082c22SSergio Paracuellos unsigned char byte = 0; 78667082c22SSergio Paracuellos int ret; 78767082c22SSergio Paracuellos const struct firmware *fw_entry = NULL; 78867082c22SSergio Paracuellos 78967082c22SSergio Paracuellos 79067082c22SSergio Paracuellos sdio_claim_host(card->func); 79167082c22SSergio Paracuellos 79267082c22SSergio Paracuellos /* Firmware running ? */ 793a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, GCR_A_REG, &byte); 79467082c22SSergio Paracuellos if (ret) 79567082c22SSergio Paracuellos goto release_host; 79667082c22SSergio Paracuellos if (byte == GCR_A_RUN) { 79767082c22SSergio Paracuellos netdev_dbg(priv->net_dev, "MAC firmware running ...\n"); 79867082c22SSergio Paracuellos ret = -EBUSY; 79967082c22SSergio Paracuellos goto release_host; 80067082c22SSergio Paracuellos } 80167082c22SSergio Paracuellos 80267082c22SSergio Paracuellos ret = request_firmware(&fw_entry, ROM_FILE, 80367082c22SSergio Paracuellos &priv->ks_sdio_card->func->dev); 80467082c22SSergio Paracuellos if (ret) 80567082c22SSergio Paracuellos goto release_host; 80667082c22SSergio Paracuellos 80767082c22SSergio Paracuellos ret = ks7010_copy_firmware(priv, fw_entry); 8081770ae9dSTobin C. Harding if (ret) 809aa6ca807STobin C. Harding goto release_firmware; 810aa6ca807STobin C. Harding 81113a9930dSWolfram Sang /* Firmware running check */ 81213a9930dSWolfram Sang for (n = 0; n < 50; ++n) { 81395751f19SJia-Ju Bai usleep_range(10000, 11000); /* wait_ms(10); */ 814a704a1bcSSergio Paracuellos ret = ks7010_sdio_readb(priv, GCR_A_REG, &byte); 8151770ae9dSTobin C. Harding if (ret) 816aa6ca807STobin C. Harding goto release_firmware; 817aa6ca807STobin C. Harding 818f1e79f4bSTobin C. Harding if (byte == GCR_A_RUN) 819cdf6ecc5SWolfram Sang break; 82013a9930dSWolfram Sang } 82113a9930dSWolfram Sang if ((50) <= n) { 8225259b329SSergio Paracuellos netdev_err(priv->net_dev, "firmware can't start\n"); 8231770ae9dSTobin C. Harding ret = -EIO; 824aa6ca807STobin C. Harding goto release_firmware; 82513a9930dSWolfram Sang } 82613a9930dSWolfram Sang 8271770ae9dSTobin C. Harding ret = 0; 82813a9930dSWolfram Sang 829aa6ca807STobin C. Harding release_firmware: 83013a9930dSWolfram Sang release_firmware(fw_entry); 83167082c22SSergio Paracuellos release_host: 83213a9930dSWolfram Sang sdio_release_host(card->func); 833aa6ca807STobin C. Harding 8341770ae9dSTobin C. Harding return ret; 83513a9930dSWolfram Sang } 83613a9930dSWolfram Sang 8376e6156f6SSergio Paracuellos static void ks7010_sme_enqueue_events(struct ks_wlan_private *priv) 83813a9930dSWolfram Sang { 83913a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_GET_EEPROM_CKSUM); 84013a9930dSWolfram Sang 84113a9930dSWolfram Sang /* load initial wireless parameter */ 84213a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_STOP_REQUEST); 84313a9930dSWolfram Sang 84413a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_RTS_THRESHOLD_REQUEST); 84513a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_FRAGMENTATION_THRESHOLD_REQUEST); 84613a9930dSWolfram Sang 84713a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_WEP_INDEX_REQUEST); 84813a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_WEP_KEY1_REQUEST); 84913a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_WEP_KEY2_REQUEST); 85013a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_WEP_KEY3_REQUEST); 85113a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_WEP_KEY4_REQUEST); 85213a9930dSWolfram Sang 85313a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_WEP_FLAG_REQUEST); 85413a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_RSN_ENABLED_REQUEST); 85513a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_MODE_SET_REQUEST); 85613a9930dSWolfram Sang hostif_sme_enqueue(priv, SME_START_REQUEST); 8576e6156f6SSergio Paracuellos } 8586e6156f6SSergio Paracuellos 8596e6156f6SSergio Paracuellos static void ks7010_card_init(struct ks_wlan_private *priv) 8606e6156f6SSergio Paracuellos { 8616e6156f6SSergio Paracuellos init_completion(&priv->confirm_wait); 8626e6156f6SSergio Paracuellos 8636e6156f6SSergio Paracuellos /* get mac address & firmware version */ 8646e6156f6SSergio Paracuellos hostif_sme_enqueue(priv, SME_START); 8656e6156f6SSergio Paracuellos 8666e6156f6SSergio Paracuellos if (!wait_for_completion_interruptible_timeout 8676e6156f6SSergio Paracuellos (&priv->confirm_wait, 5 * HZ)) { 8686e6156f6SSergio Paracuellos netdev_dbg(priv->net_dev, "wait time out!! SME_START\n"); 8696e6156f6SSergio Paracuellos } 8706e6156f6SSergio Paracuellos 8716e6156f6SSergio Paracuellos if (priv->mac_address_valid && priv->version_size != 0) 8726e6156f6SSergio Paracuellos priv->dev_state = DEVICE_STATE_PREINIT; 8736e6156f6SSergio Paracuellos 8746e6156f6SSergio Paracuellos ks7010_sme_enqueue_events(priv); 87513a9930dSWolfram Sang 876cdf6ecc5SWolfram Sang if (!wait_for_completion_interruptible_timeout 877cdf6ecc5SWolfram Sang (&priv->confirm_wait, 5 * HZ)) { 8785259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "wait time out!! wireless parameter set\n"); 87913a9930dSWolfram Sang } 88013a9930dSWolfram Sang 88113a9930dSWolfram Sang if (priv->dev_state >= DEVICE_STATE_PREINIT) { 8825259b329SSergio Paracuellos netdev_dbg(priv->net_dev, "DEVICE READY!!\n"); 88313a9930dSWolfram Sang priv->dev_state = DEVICE_STATE_READY; 88413a9930dSWolfram Sang } 88513a9930dSWolfram Sang } 88613a9930dSWolfram Sang 8876ee9169bSWolfram Sang static void ks7010_init_defaults(struct ks_wlan_private *priv) 8886ee9169bSWolfram Sang { 8896ee9169bSWolfram Sang priv->reg.tx_rate = TX_RATE_AUTO; 8906ee9169bSWolfram Sang priv->reg.preamble = LONG_PREAMBLE; 8918fb8e05cSTobin C. Harding priv->reg.power_mgmt = POWER_MGMT_ACTIVE; 8926ee9169bSWolfram Sang priv->reg.scan_type = ACTIVE_SCAN; 8936ee9169bSWolfram Sang priv->reg.beacon_lost_count = 20; 8946ee9169bSWolfram Sang priv->reg.rts = 2347UL; 8956ee9169bSWolfram Sang priv->reg.fragment = 2346UL; 8966ee9169bSWolfram Sang priv->reg.phy_type = D_11BG_COMPATIBLE_MODE; 8976ee9169bSWolfram Sang priv->reg.cts_mode = CTS_MODE_FALSE; 8986ee9169bSWolfram Sang priv->reg.rate_set.body[11] = TX_RATE_54M; 8996ee9169bSWolfram Sang priv->reg.rate_set.body[10] = TX_RATE_48M; 9006ee9169bSWolfram Sang priv->reg.rate_set.body[9] = TX_RATE_36M; 9016ee9169bSWolfram Sang priv->reg.rate_set.body[8] = TX_RATE_18M; 9026ee9169bSWolfram Sang priv->reg.rate_set.body[7] = TX_RATE_9M; 9036ee9169bSWolfram Sang priv->reg.rate_set.body[6] = TX_RATE_24M | BASIC_RATE; 9046ee9169bSWolfram Sang priv->reg.rate_set.body[5] = TX_RATE_12M | BASIC_RATE; 9056ee9169bSWolfram Sang priv->reg.rate_set.body[4] = TX_RATE_6M | BASIC_RATE; 9066ee9169bSWolfram Sang priv->reg.rate_set.body[3] = TX_RATE_11M | BASIC_RATE; 9076ee9169bSWolfram Sang priv->reg.rate_set.body[2] = TX_RATE_5M | BASIC_RATE; 9086ee9169bSWolfram Sang priv->reg.rate_set.body[1] = TX_RATE_2M | BASIC_RATE; 9096ee9169bSWolfram Sang priv->reg.rate_set.body[0] = TX_RATE_1M | BASIC_RATE; 9106ee9169bSWolfram Sang priv->reg.tx_rate = TX_RATE_FULL_AUTO; 9116ee9169bSWolfram Sang priv->reg.rate_set.size = 12; 9126ee9169bSWolfram Sang } 9136ee9169bSWolfram Sang 9148d4ab8f7SSergio Paracuellos static int ks7010_sdio_setup_irqs(struct sdio_func *func) 9158d4ab8f7SSergio Paracuellos { 9168d4ab8f7SSergio Paracuellos int ret; 9178d4ab8f7SSergio Paracuellos 9188d4ab8f7SSergio Paracuellos /* interrupt disable */ 919a704a1bcSSergio Paracuellos sdio_writeb(func, 0, INT_ENABLE_REG, &ret); 9208d4ab8f7SSergio Paracuellos if (ret) 9218d4ab8f7SSergio Paracuellos goto irq_error; 9228d4ab8f7SSergio Paracuellos 923a704a1bcSSergio Paracuellos sdio_writeb(func, 0xff, INT_PENDING_REG, &ret); 9248d4ab8f7SSergio Paracuellos if (ret) 9258d4ab8f7SSergio Paracuellos goto irq_error; 9268d4ab8f7SSergio Paracuellos 9278d4ab8f7SSergio Paracuellos /* setup interrupt handler */ 9288d4ab8f7SSergio Paracuellos ret = sdio_claim_irq(func, ks_sdio_interrupt); 9298d4ab8f7SSergio Paracuellos 9308d4ab8f7SSergio Paracuellos irq_error: 9318d4ab8f7SSergio Paracuellos return ret; 9328d4ab8f7SSergio Paracuellos } 9338d4ab8f7SSergio Paracuellos 9340e80e31aSSergio Paracuellos static void ks7010_sdio_init_irqs(struct sdio_func *func, 9350e80e31aSSergio Paracuellos struct ks_wlan_private *priv) 9360e80e31aSSergio Paracuellos { 9370e80e31aSSergio Paracuellos unsigned char byte; 9380e80e31aSSergio Paracuellos int ret; 9390e80e31aSSergio Paracuellos 9400e80e31aSSergio Paracuellos /* 9410e80e31aSSergio Paracuellos * interrupt setting 9420e80e31aSSergio Paracuellos * clear Interrupt status write 9430e80e31aSSergio Paracuellos * (ARMtoSD_InterruptPending FN1:00_0024) 9440e80e31aSSergio Paracuellos */ 9450e80e31aSSergio Paracuellos sdio_claim_host(func); 946a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, INT_PENDING_REG, 0xff); 9470e80e31aSSergio Paracuellos sdio_release_host(func); 9480e80e31aSSergio Paracuellos if (ret) 949156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write INT_PENDING_REG\n"); 9500e80e31aSSergio Paracuellos 9510e80e31aSSergio Paracuellos /* enable ks7010sdio interrupt */ 9520e80e31aSSergio Paracuellos byte = (INT_GCR_B | INT_READ_STATUS | INT_WRITE_STATUS); 9530e80e31aSSergio Paracuellos sdio_claim_host(func); 954a704a1bcSSergio Paracuellos ret = ks7010_sdio_writeb(priv, INT_ENABLE_REG, byte); 9550e80e31aSSergio Paracuellos sdio_release_host(func); 9560e80e31aSSergio Paracuellos if (ret) 957156f2703SSergio Paracuellos netdev_err(priv->net_dev, "write INT_ENABLE_REG\n"); 9580e80e31aSSergio Paracuellos } 9590e80e31aSSergio Paracuellos 9605df835e8SSergio Paracuellos static void ks7010_private_init(struct ks_wlan_private *priv, 9615df835e8SSergio Paracuellos struct ks_sdio_card *card, 9625df835e8SSergio Paracuellos struct net_device *netdev) 9635df835e8SSergio Paracuellos { 9645df835e8SSergio Paracuellos /* private memory initialize */ 9655df835e8SSergio Paracuellos priv->ks_sdio_card = card; 9665df835e8SSergio Paracuellos 9675df835e8SSergio Paracuellos priv->dev_state = DEVICE_STATE_PREBOOT; 9685df835e8SSergio Paracuellos priv->net_dev = netdev; 9695df835e8SSergio Paracuellos priv->firmware_version[0] = '\0'; 9705df835e8SSergio Paracuellos priv->version_size = 0; 9715df835e8SSergio Paracuellos priv->last_doze = jiffies; 9725df835e8SSergio Paracuellos priv->last_wakeup = jiffies; 9735df835e8SSergio Paracuellos memset(&priv->nstats, 0, sizeof(priv->nstats)); 9745df835e8SSergio Paracuellos memset(&priv->wstats, 0, sizeof(priv->wstats)); 9755df835e8SSergio Paracuellos 9765df835e8SSergio Paracuellos /* sleep mode */ 9775df835e8SSergio Paracuellos atomic_set(&priv->sleepstatus.doze_request, 0); 9785df835e8SSergio Paracuellos atomic_set(&priv->sleepstatus.wakeup_request, 0); 9795df835e8SSergio Paracuellos atomic_set(&priv->sleepstatus.wakeup_request, 0); 9805df835e8SSergio Paracuellos 9815df835e8SSergio Paracuellos trx_device_init(priv); 9825df835e8SSergio Paracuellos hostif_init(priv); 9835df835e8SSergio Paracuellos ks_wlan_net_start(netdev); 9845df835e8SSergio Paracuellos ks7010_init_defaults(priv); 9855df835e8SSergio Paracuellos } 9865df835e8SSergio Paracuellos 987c4730a92SWolfram Sang static int ks7010_sdio_probe(struct sdio_func *func, 988cdf6ecc5SWolfram Sang const struct sdio_device_id *device) 98913a9930dSWolfram Sang { 9902c3f8945SSergio Paracuellos struct ks_wlan_private *priv = NULL; 9912c3f8945SSergio Paracuellos struct net_device *netdev = NULL; 99213a9930dSWolfram Sang struct ks_sdio_card *card; 9932801d7a2SWolfram Sang int ret; 99413a9930dSWolfram Sang 9952d738bd2SSandhya Bankar card = kzalloc(sizeof(*card), GFP_KERNEL); 99613a9930dSWolfram Sang if (!card) 99713a9930dSWolfram Sang return -ENOMEM; 99813a9930dSWolfram Sang 99913a9930dSWolfram Sang card->func = func; 100013a9930dSWolfram Sang 100113a9930dSWolfram Sang sdio_claim_host(func); 100213a9930dSWolfram Sang 100313a9930dSWolfram Sang ret = sdio_set_block_size(func, KS7010_IO_BLOCK_SIZE); 10045259b329SSergio Paracuellos dev_dbg(&card->func->dev, "multi_block=%d sdio_set_block_size()=%d %d\n", 1005cdf6ecc5SWolfram Sang func->card->cccr.multi_block, func->cur_blksize, ret); 100613a9930dSWolfram Sang 100713a9930dSWolfram Sang ret = sdio_enable_func(func); 100813a9930dSWolfram Sang if (ret) 1009f283dd69STobin C. Harding goto err_free_card; 101013a9930dSWolfram Sang 10118d4ab8f7SSergio Paracuellos ret = ks7010_sdio_setup_irqs(func); 101213a9930dSWolfram Sang if (ret) 1013f283dd69STobin C. Harding goto err_disable_func; 101413a9930dSWolfram Sang 101513a9930dSWolfram Sang sdio_release_host(func); 101613a9930dSWolfram Sang 101713a9930dSWolfram Sang sdio_set_drvdata(func, card); 101813a9930dSWolfram Sang 10195259b329SSergio Paracuellos dev_dbg(&card->func->dev, "class = 0x%X, vendor = 0x%X, device = 0x%X\n", 10207c1c4361STobin C. Harding func->class, func->vendor, func->device); 102113a9930dSWolfram Sang 102213a9930dSWolfram Sang /* private memory allocate */ 102313a9930dSWolfram Sang netdev = alloc_etherdev(sizeof(*priv)); 1024c7e65f4dSsayli karnik if (!netdev) { 1025156f2703SSergio Paracuellos dev_err(&card->func->dev, "Unable to alloc new net device\n"); 1026f283dd69STobin C. Harding goto err_release_irq; 102713a9930dSWolfram Sang } 10286634cff1SWolfram Sang if (dev_alloc_name(netdev, "wlan%d") < 0) { 1029156f2703SSergio Paracuellos dev_err(&card->func->dev, "Couldn't get name!\n"); 1030f283dd69STobin C. Harding goto err_free_netdev; 103113a9930dSWolfram Sang } 103213a9930dSWolfram Sang 103313a9930dSWolfram Sang priv = netdev_priv(netdev); 103413a9930dSWolfram Sang 103513a9930dSWolfram Sang card->priv = priv; 1036e4844d6fSSergio Paracuellos SET_NETDEV_DEV(netdev, &card->func->dev); 103713a9930dSWolfram Sang 10385df835e8SSergio Paracuellos ks7010_private_init(priv, card, netdev); 103913a9930dSWolfram Sang 1040ed246b9eSTobin C. Harding ret = ks7010_upload_firmware(card); 104113a9930dSWolfram Sang if (ret) { 10425259b329SSergio Paracuellos netdev_err(priv->net_dev, 1043156f2703SSergio Paracuellos "firmware load failed !! ret = %d\n", ret); 1044cf10e78eSTobin C. Harding goto err_free_netdev; 104513a9930dSWolfram Sang } 104613a9930dSWolfram Sang 10470e80e31aSSergio Paracuellos ks7010_sdio_init_irqs(func, priv); 104853638cefSsayli karnik 104913a9930dSWolfram Sang priv->dev_state = DEVICE_STATE_BOOT; 105013a9930dSWolfram Sang 105118bd6dd1STobin C. Harding priv->wq = create_workqueue("wq"); 105218bd6dd1STobin C. Harding if (!priv->wq) { 10535259b329SSergio Paracuellos netdev_err(priv->net_dev, "create_workqueue failed !!\n"); 1054cf10e78eSTobin C. Harding goto err_free_netdev; 105513a9930dSWolfram Sang } 105613a9930dSWolfram Sang 105718bd6dd1STobin C. Harding INIT_DELAYED_WORK(&priv->rw_dwork, ks7010_rw_function); 1058e8593a8aSWolfram Sang ks7010_card_init(priv); 105913a9930dSWolfram Sang 10603fb54d75SWolfram Sang ret = register_netdev(priv->net_dev); 10613fb54d75SWolfram Sang if (ret) 1062cf10e78eSTobin C. Harding goto err_free_netdev; 10633fb54d75SWolfram Sang 106413a9930dSWolfram Sang return 0; 106513a9930dSWolfram Sang 1066f283dd69STobin C. Harding err_free_netdev: 10679962d86dSGustavo A. R. Silva free_netdev(netdev); 1068f283dd69STobin C. Harding err_release_irq: 106913a9930dSWolfram Sang sdio_claim_host(func); 107013a9930dSWolfram Sang sdio_release_irq(func); 1071f283dd69STobin C. Harding err_disable_func: 107213a9930dSWolfram Sang sdio_disable_func(func); 1073f283dd69STobin C. Harding err_free_card: 107413a9930dSWolfram Sang sdio_release_host(func); 107513a9930dSWolfram Sang sdio_set_drvdata(func, NULL); 107613a9930dSWolfram Sang kfree(card); 10772801d7a2SWolfram Sang 107813a9930dSWolfram Sang return -ENODEV; 107913a9930dSWolfram Sang } 108013a9930dSWolfram Sang 10812ab6fd59STobin C. Harding /* send stop request to MAC */ 10822ab6fd59STobin C. Harding static int send_stop_request(struct sdio_func *func) 10832ab6fd59STobin C. Harding { 10849f2ae0a4SQuytelda Kahja struct hostif_stop_request *pp; 10852ab6fd59STobin C. Harding struct ks_sdio_card *card; 10862ab6fd59STobin C. Harding size_t size; 10872ab6fd59STobin C. Harding 10882ab6fd59STobin C. Harding card = sdio_get_drvdata(func); 10892ab6fd59STobin C. Harding 10902ab6fd59STobin C. Harding pp = kzalloc(hif_align_size(sizeof(*pp)), GFP_KERNEL); 1091cea78d61SSergio Paracuellos if (!pp) 10922ab6fd59STobin C. Harding return -ENOMEM; 10932ab6fd59STobin C. Harding 10942ab6fd59STobin C. Harding size = sizeof(*pp) - sizeof(pp->header.size); 10952ab6fd59STobin C. Harding pp->header.size = cpu_to_le16((uint16_t)size); 10962ab6fd59STobin C. Harding pp->header.event = cpu_to_le16((uint16_t)HIF_STOP_REQ); 10972ab6fd59STobin C. Harding 10982ab6fd59STobin C. Harding sdio_claim_host(func); 10992ab6fd59STobin C. Harding write_to_device(card->priv, (unsigned char *)pp, 11002ab6fd59STobin C. Harding hif_align_size(sizeof(*pp))); 11012ab6fd59STobin C. Harding sdio_release_host(func); 11022ab6fd59STobin C. Harding 11032ab6fd59STobin C. Harding kfree(pp); 11042ab6fd59STobin C. Harding return 0; 11052ab6fd59STobin C. Harding } 11062ab6fd59STobin C. Harding 1107c4730a92SWolfram Sang static void ks7010_sdio_remove(struct sdio_func *func) 110813a9930dSWolfram Sang { 110913a9930dSWolfram Sang int ret; 111013a9930dSWolfram Sang struct ks_sdio_card *card; 111113a9930dSWolfram Sang struct ks_wlan_private *priv; 1112697f9f7fSMuraru Mihaela 111313a9930dSWolfram Sang card = sdio_get_drvdata(func); 111413a9930dSWolfram Sang 1115c7e65f4dSsayli karnik if (!card) 111613a9930dSWolfram Sang return; 111713a9930dSWolfram Sang 111813a9930dSWolfram Sang priv = card->priv; 111913a9930dSWolfram Sang if (priv) { 1120803394d0SColin Ian King struct net_device *netdev = priv->net_dev; 1121803394d0SColin Ian King 112213a9930dSWolfram Sang ks_wlan_net_stop(netdev); 112313a9930dSWolfram Sang 112413a9930dSWolfram Sang /* interrupt disable */ 112513a9930dSWolfram Sang sdio_claim_host(func); 1126a704a1bcSSergio Paracuellos sdio_writeb(func, 0, INT_ENABLE_REG, &ret); 1127a704a1bcSSergio Paracuellos sdio_writeb(func, 0xff, INT_PENDING_REG, &ret); 112813a9930dSWolfram Sang sdio_release_host(func); 112913a9930dSWolfram Sang 11302ab6fd59STobin C. Harding ret = send_stop_request(func); 11312ab6fd59STobin C. Harding if (ret) /* memory allocation failure */ 11322ab6fd59STobin C. Harding return; 1133697f9f7fSMuraru Mihaela 113418bd6dd1STobin C. Harding if (priv->wq) { 113518bd6dd1STobin C. Harding flush_workqueue(priv->wq); 113618bd6dd1STobin C. Harding destroy_workqueue(priv->wq); 113713a9930dSWolfram Sang } 113813a9930dSWolfram Sang 113913a9930dSWolfram Sang hostif_exit(priv); 114013a9930dSWolfram Sang 114113a9930dSWolfram Sang unregister_netdev(netdev); 114213a9930dSWolfram Sang 114313a9930dSWolfram Sang trx_device_exit(priv); 114413a9930dSWolfram Sang free_netdev(priv->net_dev); 114513a9930dSWolfram Sang card->priv = NULL; 114613a9930dSWolfram Sang } 114713a9930dSWolfram Sang 114813a9930dSWolfram Sang sdio_claim_host(func); 114913a9930dSWolfram Sang sdio_release_irq(func); 115013a9930dSWolfram Sang sdio_disable_func(func); 115113a9930dSWolfram Sang sdio_release_host(func); 115213a9930dSWolfram Sang sdio_set_drvdata(func, NULL); 115313a9930dSWolfram Sang kfree(card); 115413a9930dSWolfram Sang } 115513a9930dSWolfram Sang 11564c0d46d2SWolfram Sang static struct sdio_driver ks7010_sdio_driver = { 11574c0d46d2SWolfram Sang .name = "ks7010_sdio", 11584c0d46d2SWolfram Sang .id_table = ks7010_sdio_ids, 11594c0d46d2SWolfram Sang .probe = ks7010_sdio_probe, 11604c0d46d2SWolfram Sang .remove = ks7010_sdio_remove, 11614c0d46d2SWolfram Sang }; 11624c0d46d2SWolfram Sang 11636b0cb0b0SWolfram Sang module_driver(ks7010_sdio_driver, sdio_register_driver, sdio_unregister_driver); 1164e1240140SWolfram Sang MODULE_AUTHOR("Sang Engineering, Qi-Hardware, KeyStream"); 1165e1240140SWolfram Sang MODULE_DESCRIPTION("Driver for KeyStream KS7010 based SDIO cards"); 1166e1240140SWolfram Sang MODULE_LICENSE("GPL v2"); 1167e1240140SWolfram Sang MODULE_FIRMWARE(ROM_FILE); 1168