1/* 2 * Intel Wireless Multicomm 3200 WiFi driver 3 * 4 * Copyright (C) 2009 Intel Corporation. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * * Neither the name of Intel Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 * 32 * 33 * Intel Corporation <ilw@linux.intel.com> 34 * Samuel Ortiz <samuel.ortiz@intel.com> 35 * Zhu Yi <yi.zhu@intel.com> 36 * 37 */ 38 39/* 40 * This is the SDIO bus specific hooks for iwm. 41 * It also is the module's entry point. 42 * 43 * Interesting code paths: 44 * iwm_sdio_probe() (Called by an SDIO bus scan) 45 * -> iwm_if_alloc() (netdev.c) 46 * -> iwm_wdev_alloc() (cfg80211.c, allocates and register our wiphy) 47 * -> wiphy_new() 48 * -> wiphy_register() 49 * -> alloc_netdev_mq() 50 * -> register_netdev() 51 * 52 * iwm_sdio_remove() 53 * -> iwm_if_free() (netdev.c) 54 * -> unregister_netdev() 55 * -> iwm_wdev_free() (cfg80211.c) 56 * -> wiphy_unregister() 57 * -> wiphy_free() 58 * 59 * iwm_sdio_isr() (called in process context from the SDIO core code) 60 * -> queue_work(.., isr_worker) 61 * -- [async] --> iwm_sdio_isr_worker() 62 * -> iwm_rx_handle() 63 */ 64 65#include <linux/kernel.h> 66#include <linux/slab.h> 67#include <linux/netdevice.h> 68#include <linux/debugfs.h> 69#include <linux/mmc/sdio_ids.h> 70#include <linux/mmc/sdio.h> 71#include <linux/mmc/sdio_func.h> 72 73#include "iwm.h" 74#include "debug.h" 75#include "bus.h" 76#include "sdio.h" 77 78static void iwm_sdio_isr_worker(struct work_struct *work) 79{ 80 struct iwm_sdio_priv *hw; 81 struct iwm_priv *iwm; 82 struct iwm_rx_info *rx_info; 83 struct sk_buff *skb; 84 u8 *rx_buf; 85 unsigned long rx_size; 86 87 hw = container_of(work, struct iwm_sdio_priv, isr_worker); 88 iwm = hw_to_iwm(hw); 89 90 while (!skb_queue_empty(&iwm->rx_list)) { 91 skb = skb_dequeue(&iwm->rx_list); 92 rx_info = skb_to_rx_info(skb); 93 rx_size = rx_info->rx_size; 94 rx_buf = skb->data; 95 96 IWM_HEXDUMP(iwm, DBG, SDIO, "RX: ", rx_buf, rx_size); 97 if (iwm_rx_handle(iwm, rx_buf, rx_size) < 0) 98 IWM_WARN(iwm, "RX error\n"); 99 100 kfree_skb(skb); 101 } 102} 103 104static void iwm_sdio_isr(struct sdio_func *func) 105{ 106 struct iwm_priv *iwm; 107 struct iwm_sdio_priv *hw; 108 struct iwm_rx_info *rx_info; 109 struct sk_buff *skb; 110 unsigned long buf_size, read_size; 111 int ret; 112 u8 val; 113 114 hw = sdio_get_drvdata(func); 115 iwm = hw_to_iwm(hw); 116 117 buf_size = hw->blk_size; 118 119 /* We're checking the status */ 120 val = sdio_readb(func, IWM_SDIO_INTR_STATUS_ADDR, &ret); 121 if (val == 0 || ret < 0) { 122 IWM_ERR(iwm, "Wrong INTR_STATUS\n"); 123 return; 124 } 125 126 /* See if we have free buffers */ 127 if (skb_queue_len(&iwm->rx_list) > IWM_RX_LIST_SIZE) { 128 IWM_ERR(iwm, "No buffer for more Rx frames\n"); 129 return; 130 } 131 132 /* We first read the transaction size */ 133 read_size = sdio_readb(func, IWM_SDIO_INTR_GET_SIZE_ADDR + 1, &ret); 134 read_size = read_size << 8; 135 136 if (ret < 0) { 137 IWM_ERR(iwm, "Couldn't read the xfer size\n"); 138 return; 139 } 140 141 /* We need to clear the INT register */ 142 sdio_writeb(func, 1, IWM_SDIO_INTR_CLEAR_ADDR, &ret); 143 if (ret < 0) { 144 IWM_ERR(iwm, "Couldn't clear the INT register\n"); 145 return; 146 } 147 148 while (buf_size < read_size) 149 buf_size <<= 1; 150 151 skb = dev_alloc_skb(buf_size); 152 if (!skb) { 153 IWM_ERR(iwm, "Couldn't alloc RX skb\n"); 154 return; 155 } 156 rx_info = skb_to_rx_info(skb); 157 rx_info->rx_size = read_size; 158 rx_info->rx_buf_size = buf_size; 159 160 /* Now we can read the actual buffer */ 161 ret = sdio_memcpy_fromio(func, skb_put(skb, read_size), 162 IWM_SDIO_DATA_ADDR, read_size); 163 164 /* The skb is put on a driver's specific Rx SKB list */ 165 skb_queue_tail(&iwm->rx_list, skb); 166 167 /* We can now schedule the actual worker */ 168 queue_work(hw->isr_wq, &hw->isr_worker); 169} 170 171static void iwm_sdio_rx_free(struct iwm_sdio_priv *hw) 172{ 173 struct iwm_priv *iwm = hw_to_iwm(hw); 174 175 flush_workqueue(hw->isr_wq); 176 177 skb_queue_purge(&iwm->rx_list); 178} 179 180/* Bus ops */ 181static int if_sdio_enable(struct iwm_priv *iwm) 182{ 183 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); 184 int ret; 185 186 sdio_claim_host(hw->func); 187 188 ret = sdio_enable_func(hw->func); 189 if (ret) { 190 IWM_ERR(iwm, "Couldn't enable the device: is TOP driver " 191 "loaded and functional?\n"); 192 goto release_host; 193 } 194 195 iwm_reset(iwm); 196 197 ret = sdio_claim_irq(hw->func, iwm_sdio_isr); 198 if (ret) { 199 IWM_ERR(iwm, "Failed to claim irq: %d\n", ret); 200 goto release_host; 201 } 202 203 sdio_writeb(hw->func, 1, IWM_SDIO_INTR_ENABLE_ADDR, &ret); 204 if (ret < 0) { 205 IWM_ERR(iwm, "Couldn't enable INTR: %d\n", ret); 206 goto release_irq; 207 } 208 209 sdio_release_host(hw->func); 210 211 IWM_DBG_SDIO(iwm, INFO, "IWM SDIO enable\n"); 212 213 return 0; 214 215 release_irq: 216 sdio_release_irq(hw->func); 217 release_host: 218 sdio_release_host(hw->func); 219 220 return ret; 221} 222 223static int if_sdio_disable(struct iwm_priv *iwm) 224{ 225 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); 226 int ret; 227 228 sdio_claim_host(hw->func); 229 sdio_writeb(hw->func, 0, IWM_SDIO_INTR_ENABLE_ADDR, &ret); 230 if (ret < 0) 231 IWM_WARN(iwm, "Couldn't disable INTR: %d\n", ret); 232 233 sdio_release_irq(hw->func); 234 sdio_disable_func(hw->func); 235 sdio_release_host(hw->func); 236 237 iwm_sdio_rx_free(hw); 238 239 iwm_reset(iwm); 240 241 IWM_DBG_SDIO(iwm, INFO, "IWM SDIO disable\n"); 242 243 return 0; 244} 245 246static int if_sdio_send_chunk(struct iwm_priv *iwm, u8 *buf, int count) 247{ 248 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); 249 int aligned_count = ALIGN(count, hw->blk_size); 250 int ret; 251 252 if ((unsigned long)buf & 0x3) { 253 IWM_ERR(iwm, "buf <%p> is not dword aligned\n", buf); 254 /* TODO: Is this a hardware limitation? use get_unligned */ 255 return -EINVAL; 256 } 257 258 sdio_claim_host(hw->func); 259 ret = sdio_memcpy_toio(hw->func, IWM_SDIO_DATA_ADDR, buf, 260 aligned_count); 261 sdio_release_host(hw->func); 262 263 return ret; 264} 265 266/* debugfs hooks */ 267static int iwm_debugfs_sdio_open(struct inode *inode, struct file *filp) 268{ 269 filp->private_data = inode->i_private; 270 return 0; 271} 272 273static ssize_t iwm_debugfs_sdio_read(struct file *filp, char __user *buffer, 274 size_t count, loff_t *ppos) 275{ 276 struct iwm_priv *iwm = filp->private_data; 277 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); 278 char *buf; 279 u8 cccr; 280 int buf_len = 4096, ret; 281 size_t len = 0; 282 283 if (*ppos != 0) 284 return 0; 285 if (count < sizeof(buf)) 286 return -ENOSPC; 287 288 buf = kzalloc(buf_len, GFP_KERNEL); 289 if (!buf) 290 return -ENOMEM; 291 292 sdio_claim_host(hw->func); 293 294 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IOEx, &ret); 295 if (ret) { 296 IWM_ERR(iwm, "Could not read SDIO_CCCR_IOEx\n"); 297 goto err; 298 } 299 len += snprintf(buf + len, buf_len - len, "CCCR_IOEx: 0x%x\n", cccr); 300 301 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IORx, &ret); 302 if (ret) { 303 IWM_ERR(iwm, "Could not read SDIO_CCCR_IORx\n"); 304 goto err; 305 } 306 len += snprintf(buf + len, buf_len - len, "CCCR_IORx: 0x%x\n", cccr); 307 308 309 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IENx, &ret); 310 if (ret) { 311 IWM_ERR(iwm, "Could not read SDIO_CCCR_IENx\n"); 312 goto err; 313 } 314 len += snprintf(buf + len, buf_len - len, "CCCR_IENx: 0x%x\n", cccr); 315 316 317 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_INTx, &ret); 318 if (ret) { 319 IWM_ERR(iwm, "Could not read SDIO_CCCR_INTx\n"); 320 goto err; 321 } 322 len += snprintf(buf + len, buf_len - len, "CCCR_INTx: 0x%x\n", cccr); 323 324 325 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_ABORT, &ret); 326 if (ret) { 327 IWM_ERR(iwm, "Could not read SDIO_CCCR_ABORTx\n"); 328 goto err; 329 } 330 len += snprintf(buf + len, buf_len - len, "CCCR_ABORT: 0x%x\n", cccr); 331 332 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IF, &ret); 333 if (ret) { 334 IWM_ERR(iwm, "Could not read SDIO_CCCR_IF\n"); 335 goto err; 336 } 337 len += snprintf(buf + len, buf_len - len, "CCCR_IF: 0x%x\n", cccr); 338 339 340 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CAPS, &ret); 341 if (ret) { 342 IWM_ERR(iwm, "Could not read SDIO_CCCR_CAPS\n"); 343 goto err; 344 } 345 len += snprintf(buf + len, buf_len - len, "CCCR_CAPS: 0x%x\n", cccr); 346 347 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CIS, &ret); 348 if (ret) { 349 IWM_ERR(iwm, "Could not read SDIO_CCCR_CIS\n"); 350 goto err; 351 } 352 len += snprintf(buf + len, buf_len - len, "CCCR_CIS: 0x%x\n", cccr); 353 354 ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); 355err: 356 sdio_release_host(hw->func); 357 358 kfree(buf); 359 360 return ret; 361} 362 363static const struct file_operations iwm_debugfs_sdio_fops = { 364 .owner = THIS_MODULE, 365 .open = iwm_debugfs_sdio_open, 366 .read = iwm_debugfs_sdio_read, 367}; 368 369static void if_sdio_debugfs_init(struct iwm_priv *iwm, struct dentry *parent_dir) 370{ 371 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); 372 373 hw->cccr_dentry = debugfs_create_file("cccr", 0200, 374 parent_dir, iwm, 375 &iwm_debugfs_sdio_fops); 376} 377 378static void if_sdio_debugfs_exit(struct iwm_priv *iwm) 379{ 380 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); 381 382 debugfs_remove(hw->cccr_dentry); 383} 384 385static struct iwm_if_ops if_sdio_ops = { 386 .enable = if_sdio_enable, 387 .disable = if_sdio_disable, 388 .send_chunk = if_sdio_send_chunk, 389 .debugfs_init = if_sdio_debugfs_init, 390 .debugfs_exit = if_sdio_debugfs_exit, 391 .umac_name = "iwmc3200wifi-umac-sdio.bin", 392 .calib_lmac_name = "iwmc3200wifi-calib-sdio.bin", 393 .lmac_name = "iwmc3200wifi-lmac-sdio.bin", 394}; 395MODULE_FIRMWARE("iwmc3200wifi-umac-sdio.bin"); 396MODULE_FIRMWARE("iwmc3200wifi-calib-sdio.bin"); 397MODULE_FIRMWARE("iwmc3200wifi-lmac-sdio.bin"); 398 399static int iwm_sdio_probe(struct sdio_func *func, 400 const struct sdio_device_id *id) 401{ 402 struct iwm_priv *iwm; 403 struct iwm_sdio_priv *hw; 404 struct device *dev = &func->dev; 405 int ret; 406 407 /* check if TOP has already initialized the card */ 408 sdio_claim_host(func); 409 ret = sdio_enable_func(func); 410 if (ret) { 411 dev_err(dev, "wait for TOP to enable the device\n"); 412 sdio_release_host(func); 413 return ret; 414 } 415 416 ret = sdio_set_block_size(func, IWM_SDIO_BLK_SIZE); 417 418 sdio_disable_func(func); 419 sdio_release_host(func); 420 421 if (ret < 0) { 422 dev_err(dev, "Failed to set block size: %d\n", ret); 423 return ret; 424 } 425 426 iwm = iwm_if_alloc(sizeof(struct iwm_sdio_priv), dev, &if_sdio_ops); 427 if (IS_ERR(iwm)) { 428 dev_err(dev, "allocate SDIO interface failed\n"); 429 return PTR_ERR(iwm); 430 } 431 432 hw = iwm_private(iwm); 433 hw->iwm = iwm; 434 435 iwm_debugfs_init(iwm); 436 437 sdio_set_drvdata(func, hw); 438 439 hw->func = func; 440 hw->blk_size = IWM_SDIO_BLK_SIZE; 441 442 hw->isr_wq = create_singlethread_workqueue(KBUILD_MODNAME "_sdio"); 443 if (!hw->isr_wq) { 444 ret = -ENOMEM; 445 goto debugfs_exit; 446 } 447 448 INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker); 449 450 ret = iwm_if_add(iwm); 451 if (ret) { 452 dev_err(dev, "add SDIO interface failed\n"); 453 goto destroy_wq; 454 } 455 456 dev_info(dev, "IWM SDIO probe\n"); 457 458 return 0; 459 460 destroy_wq: 461 destroy_workqueue(hw->isr_wq); 462 debugfs_exit: 463 iwm_debugfs_exit(iwm); 464 iwm_if_free(iwm); 465 return ret; 466} 467 468static void iwm_sdio_remove(struct sdio_func *func) 469{ 470 struct iwm_sdio_priv *hw = sdio_get_drvdata(func); 471 struct iwm_priv *iwm = hw_to_iwm(hw); 472 struct device *dev = &func->dev; 473 474 iwm_if_remove(iwm); 475 destroy_workqueue(hw->isr_wq); 476 iwm_debugfs_exit(iwm); 477 iwm_if_free(iwm); 478 479 sdio_set_drvdata(func, NULL); 480 481 dev_info(dev, "IWM SDIO remove\n"); 482} 483 484static const struct sdio_device_id iwm_sdio_ids[] = { 485 /* Global/AGN SKU */ 486 { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1403) }, 487 /* BGN SKU */ 488 { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1408) }, 489 { /* end: all zeroes */ }, 490}; 491MODULE_DEVICE_TABLE(sdio, iwm_sdio_ids); 492 493static struct sdio_driver iwm_sdio_driver = { 494 .name = "iwm_sdio", 495 .id_table = iwm_sdio_ids, 496 .probe = iwm_sdio_probe, 497 .remove = iwm_sdio_remove, 498}; 499 500static int __init iwm_sdio_init_module(void) 501{ 502 return sdio_register_driver(&iwm_sdio_driver); 503} 504 505static void __exit iwm_sdio_exit_module(void) 506{ 507 sdio_unregister_driver(&iwm_sdio_driver); 508} 509 510module_init(iwm_sdio_init_module); 511module_exit(iwm_sdio_exit_module); 512 513MODULE_LICENSE("GPL"); 514MODULE_AUTHOR(IWM_COPYRIGHT " " IWM_AUTHOR); 515