aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/iwmc3200top/main.c
diff options
context:
space:
mode:
authorTomas Winkler <tomas.winkler@intel.com>2009-10-17 05:09:34 -0400
committerDavid S. Miller <davem@davemloft.net>2009-10-20 02:22:50 -0400
commitab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3bae (patch)
treed5ed0f7d99c516c2feba03db5c70afbf79f20a48 /drivers/misc/iwmc3200top/main.c
parent7e75f93eda027d9f9e0203ee6ffd210ea92e98f3 (diff)
iwmc3200top: Add Intel Wireless MultiCom 3200 top driver.
This patch adds Intel Wireless MultiCom 3200 top driver. IWMC3200 is 4Wireless Com CHIP (GPS/BT/WiFi/WiMAX). Top driver is responsible for device initialization and firmware download. Firmware handled by top is responsible for top itself and as well as bluetooth and GPS coms. (Wifi and WiMax provide their own firmware) In addition top driver is used to retrieve firmware logs and supports other debugging features Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/misc/iwmc3200top/main.c')
-rw-r--r--drivers/misc/iwmc3200top/main.c699
1 files changed, 699 insertions, 0 deletions
diff --git a/drivers/misc/iwmc3200top/main.c b/drivers/misc/iwmc3200top/main.c
new file mode 100644
index 000000000000..6e4e49113ab4
--- /dev/null
+++ b/drivers/misc/iwmc3200top/main.c
@@ -0,0 +1,699 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/main.c
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/kernel.h>
30#include <linux/debugfs.h>
31#include <linux/mmc/sdio_ids.h>
32#include <linux/mmc/sdio_func.h>
33#include <linux/mmc/sdio.h>
34
35#include "iwmc3200top.h"
36#include "log.h"
37#include "fw-msg.h"
38#include "debugfs.h"
39
40
41#define DRIVER_DESCRIPTION "Intel(R) IWMC 3200 Top Driver"
42#define DRIVER_COPYRIGHT "Copyright (c) 2008 Intel Corporation."
43
44#define IWMCT_VERSION "0.1.62"
45
46#ifdef REPOSITORY_LABEL
47#define RL REPOSITORY_LABEL
48#else
49#define RL local
50#endif
51
52#ifdef CONFIG_IWMC3200TOP_DEBUG
53#define VD "-d"
54#else
55#define VD
56#endif
57
58#define DRIVER_VERSION IWMCT_VERSION "-" __stringify(RL) VD
59
60MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
61MODULE_VERSION(DRIVER_VERSION);
62MODULE_LICENSE("GPL");
63MODULE_AUTHOR(DRIVER_COPYRIGHT);
64
65
66/* FIXME: These can be found in sdio_ids.h in newer kernels */
67#ifndef SDIO_INTEL_VENDOR_ID
68#define SDIO_INTEL_VENDOR_ID 0x0089
69#endif
70#ifndef SDIO_DEVICE_ID_INTEL_IWMC3200TOP
71#define SDIO_DEVICE_ID_INTEL_IWMC3200TOP 0x1404
72#endif
73
74/*
75 * This workers main task is to wait for OP_OPR_ALIVE
76 * from TOP FW until ALIVE_MSG_TIMOUT timeout is elapsed.
77 * When OP_OPR_ALIVE received it will issue
78 * a call to "bus_rescan_devices".
79 */
80static void iwmct_rescan_worker(struct work_struct *ws)
81{
82 struct iwmct_priv *priv;
83 int ret;
84
85 priv = container_of(ws, struct iwmct_priv, bus_rescan_worker);
86
87 LOG_INFO(priv, FW_MSG, "Calling bus_rescan\n");
88
89 ret = bus_rescan_devices(priv->func->dev.bus);
90 if (ret < 0)
91 LOG_INFO(priv, FW_DOWNLOAD, "bus_rescan_devices FAILED!!!\n");
92}
93
94static void op_top_message(struct iwmct_priv *priv, struct top_msg *msg)
95{
96 switch (msg->hdr.opcode) {
97 case OP_OPR_ALIVE:
98 LOG_INFO(priv, FW_MSG, "Got ALIVE from device, wake rescan\n");
99 queue_work(priv->bus_rescan_wq, &priv->bus_rescan_worker);
100 break;
101 default:
102 LOG_INFO(priv, FW_MSG, "Received msg opcode 0x%X\n",
103 msg->hdr.opcode);
104 break;
105 }
106}
107
108
109static void handle_top_message(struct iwmct_priv *priv, u8 *buf, int len)
110{
111 struct top_msg *msg;
112
113 msg = (struct top_msg *)buf;
114
115 if (msg->hdr.type != COMM_TYPE_D2H) {
116 LOG_ERROR(priv, FW_MSG,
117 "Message from TOP with invalid message type 0x%X\n",
118 msg->hdr.type);
119 return;
120 }
121
122 if (len < sizeof(msg->hdr)) {
123 LOG_ERROR(priv, FW_MSG,
124 "Message from TOP is too short for message header "
125 "received %d bytes, expected at least %zd bytes\n",
126 len, sizeof(msg->hdr));
127 return;
128 }
129
130 if (len < le16_to_cpu(msg->hdr.length) + sizeof(msg->hdr)) {
131 LOG_ERROR(priv, FW_MSG,
132 "Message length (%d bytes) is shorter than "
133 "in header (%d bytes)\n",
134 len, le16_to_cpu(msg->hdr.length));
135 return;
136 }
137
138 switch (msg->hdr.category) {
139 case COMM_CATEGORY_OPERATIONAL:
140 op_top_message(priv, (struct top_msg *)buf);
141 break;
142
143 case COMM_CATEGORY_DEBUG:
144 case COMM_CATEGORY_TESTABILITY:
145 case COMM_CATEGORY_DIAGNOSTICS:
146 iwmct_log_top_message(priv, buf, len);
147 break;
148
149 default:
150 LOG_ERROR(priv, FW_MSG,
151 "Message from TOP with unknown category 0x%X\n",
152 msg->hdr.category);
153 break;
154 }
155}
156
157int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len)
158{
159 int ret;
160 u8 *buf;
161
162 LOG_INFOEX(priv, FW_MSG, "Sending hcmd:\n");
163
164 /* add padding to 256 for IWMC */
165 ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256;
166
167 LOG_HEXDUMP(FW_MSG, cmd, len);
168
169 if (len > FW_HCMD_BLOCK_SIZE) {
170 LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n",
171 len, FW_HCMD_BLOCK_SIZE);
172 return -1;
173 }
174
175 buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL);
176 if (!buf) {
177 LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n",
178 FW_HCMD_BLOCK_SIZE);
179 return -1;
180 }
181
182 memcpy(buf, cmd, len);
183
184 sdio_claim_host(priv->func);
185 ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, buf,
186 FW_HCMD_BLOCK_SIZE);
187 sdio_release_host(priv->func);
188
189 kfree(buf);
190 return ret;
191}
192
193int iwmct_tx(struct iwmct_priv *priv, unsigned int addr,
194 void *src, int count)
195{
196 int ret;
197
198 sdio_claim_host(priv->func);
199 ret = sdio_memcpy_toio(priv->func, addr, src, count);
200 sdio_release_host(priv->func);
201
202 return ret;
203}
204
205static void iwmct_irq_read_worker(struct work_struct *ws)
206{
207 struct iwmct_priv *priv;
208 struct iwmct_work_struct *read_req;
209 __le32 *buf = NULL;
210 int ret;
211 int iosize;
212 u32 barker;
213 bool is_barker;
214
215 priv = container_of(ws, struct iwmct_priv, isr_worker);
216
217 LOG_INFO(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws);
218
219 /* --------------------- Handshake with device -------------------- */
220 sdio_claim_host(priv->func);
221
222 /* all list manipulations have to be protected by
223 * sdio_claim_host/sdio_release_host */
224 if (list_empty(&priv->read_req_list)) {
225 LOG_ERROR(priv, IRQ, "read_req_list empty in read worker\n");
226 goto exit_release;
227 }
228
229 read_req = list_entry(priv->read_req_list.next,
230 struct iwmct_work_struct, list);
231
232 list_del(&read_req->list);
233 iosize = read_req->iosize;
234 kfree(read_req);
235
236 buf = kzalloc(iosize, GFP_KERNEL);
237 if (!buf) {
238 LOG_ERROR(priv, IRQ, "kzalloc error, buf size %d\n", iosize);
239 goto exit_release;
240 }
241
242 LOG_INFO(priv, IRQ, "iosize=%d, buf=%p, func=%d\n",
243 iosize, buf, priv->func->num);
244
245 /* read from device */
246 ret = sdio_memcpy_fromio(priv->func, buf, IWMC_SDIO_DATA_ADDR, iosize);
247 if (ret) {
248 LOG_ERROR(priv, IRQ, "error %d reading buffer\n", ret);
249 goto exit_release;
250 }
251
252 LOG_HEXDUMP(IRQ, (u8 *)buf, iosize);
253
254 barker = le32_to_cpu(buf[0]);
255
256 /* Verify whether it's a barker and if not - treat as regular Rx */
257 if (barker == IWMC_BARKER_ACK ||
258 (barker & BARKER_DNLOAD_BARKER_MSK) == IWMC_BARKER_REBOOT) {
259
260 /* Valid Barker is equal on first 4 dwords */
261 is_barker = (buf[1] == buf[0]) &&
262 (buf[2] == buf[0]) &&
263 (buf[3] == buf[0]);
264
265 if (!is_barker) {
266 LOG_WARNING(priv, IRQ,
267 "Potentially inconsistent barker "
268 "%08X_%08X_%08X_%08X\n",
269 le32_to_cpu(buf[0]), le32_to_cpu(buf[1]),
270 le32_to_cpu(buf[2]), le32_to_cpu(buf[3]));
271 }
272 } else {
273 is_barker = false;
274 }
275
276 /* Handle Top CommHub message */
277 if (!is_barker) {
278 sdio_release_host(priv->func);
279 handle_top_message(priv, (u8 *)buf, iosize);
280 goto exit;
281 } else if (barker == IWMC_BARKER_ACK) { /* Handle barkers */
282 if (atomic_read(&priv->dev_sync) == 0) {
283 LOG_ERROR(priv, IRQ,
284 "ACK barker arrived out-of-sync\n");
285 goto exit_release;
286 }
287
288 /* Continuing to FW download (after Sync is completed)*/
289 atomic_set(&priv->dev_sync, 0);
290 LOG_INFO(priv, IRQ, "ACK barker arrived "
291 "- starting FW download\n");
292 } else { /* REBOOT barker */
293 LOG_INFO(priv, IRQ, "Recieved reboot barker: %x\n", barker);
294 priv->barker = barker;
295
296 if (barker & BARKER_DNLOAD_SYNC_MSK) {
297 /* Send the same barker back */
298 ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR,
299 buf, iosize);
300 if (ret) {
301 LOG_ERROR(priv, IRQ,
302 "error %d echoing barker\n", ret);
303 goto exit_release;
304 }
305 LOG_INFO(priv, IRQ, "Echoing barker to device\n");
306 atomic_set(&priv->dev_sync, 1);
307 goto exit_release;
308 }
309
310 /* Continuing to FW download (without Sync) */
311 LOG_INFO(priv, IRQ, "No sync requested "
312 "- starting FW download\n");
313 }
314
315 sdio_release_host(priv->func);
316
317
318 LOG_INFO(priv, IRQ, "barker download request 0x%x is:\n", priv->barker);
319 LOG_INFO(priv, IRQ, "******* Top FW %s requested ********\n",
320 (priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not");
321 LOG_INFO(priv, IRQ, "******* GPS FW %s requested ********\n",
322 (priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not");
323 LOG_INFO(priv, IRQ, "******* BT FW %s requested ********\n",
324 (priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not");
325
326 if (priv->dbg.fw_download)
327 iwmct_fw_load(priv);
328 else
329 LOG_ERROR(priv, IRQ, "FW download not allowed\n");
330
331 goto exit;
332
333exit_release:
334 sdio_release_host(priv->func);
335exit:
336 kfree(buf);
337 LOG_INFO(priv, IRQ, "exit iwmct_irq_read_worker\n");
338}
339
340static void iwmct_irq(struct sdio_func *func)
341{
342 struct iwmct_priv *priv;
343 int val, ret;
344 int iosize;
345 int addr = IWMC_SDIO_INTR_GET_SIZE_ADDR;
346 struct iwmct_work_struct *read_req;
347
348 priv = sdio_get_drvdata(func);
349
350 LOG_INFO(priv, IRQ, "enter iwmct_irq\n");
351
352 /* read the function's status register */
353 val = sdio_readb(func, IWMC_SDIO_INTR_STATUS_ADDR, &ret);
354
355 LOG_INFO(priv, IRQ, "iir value = %d, ret=%d\n", val, ret);
356
357 if (!val) {
358 LOG_ERROR(priv, IRQ, "iir = 0, exiting ISR\n");
359 goto exit_clear_intr;
360 }
361
362
363 /*
364 * read 2 bytes of the transaction size
365 * IMPORTANT: sdio transaction size has to be read before clearing
366 * sdio interrupt!!!
367 */
368 val = sdio_readb(priv->func, addr++, &ret);
369 iosize = val;
370 val = sdio_readb(priv->func, addr++, &ret);
371 iosize += val << 8;
372
373 LOG_INFO(priv, IRQ, "READ size %d\n", iosize);
374
375 if (iosize == 0) {
376 LOG_ERROR(priv, IRQ, "READ size %d, exiting ISR\n", iosize);
377 goto exit_clear_intr;
378 }
379
380 /* allocate a work structure to pass iosize to the worker */
381 read_req = kzalloc(sizeof(struct iwmct_work_struct), GFP_KERNEL);
382 if (!read_req) {
383 LOG_ERROR(priv, IRQ, "failed to allocate read_req, exit ISR\n");
384 goto exit_clear_intr;
385 }
386
387 INIT_LIST_HEAD(&read_req->list);
388 read_req->iosize = iosize;
389
390 list_add_tail(&priv->read_req_list, &read_req->list);
391
392 /* clear the function's interrupt request bit (write 1 to clear) */
393 sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
394
395 queue_work(priv->wq, &priv->isr_worker);
396
397 LOG_INFO(priv, IRQ, "exit iwmct_irq\n");
398
399 return;
400
401exit_clear_intr:
402 /* clear the function's interrupt request bit (write 1 to clear) */
403 sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
404}
405
406
407static int blocks;
408module_param(blocks, int, 0604);
409MODULE_PARM_DESC(blocks, "max_blocks_to_send");
410
411static int dump;
412module_param(dump, bool, 0604);
413MODULE_PARM_DESC(dump, "dump_hex_content");
414
415static int jump = 1;
416module_param(jump, bool, 0604);
417
418static int direct = 1;
419module_param(direct, bool, 0604);
420
421static int checksum = 1;
422module_param(checksum, bool, 0604);
423
424static int fw_download = 1;
425module_param(fw_download, bool, 0604);
426
427static int block_size = IWMC_SDIO_BLK_SIZE;
428module_param(block_size, int, 0404);
429
430static int download_trans_blks = IWMC_DEFAULT_TR_BLK;
431module_param(download_trans_blks, int, 0604);
432
433static int rubbish_barker;
434module_param(rubbish_barker, bool, 0604);
435
436#ifdef CONFIG_IWMC3200TOP_DEBUG
437static int log_level[LOG_SRC_MAX];
438static unsigned int log_level_argc;
439module_param_array(log_level, int, &log_level_argc, 0604);
440MODULE_PARM_DESC(log_level, "log_level");
441
442static int log_level_fw[FW_LOG_SRC_MAX];
443static unsigned int log_level_fw_argc;
444module_param_array(log_level_fw, int, &log_level_fw_argc, 0604);
445MODULE_PARM_DESC(log_level_fw, "log_level_fw");
446#endif
447
448void iwmct_dbg_init_params(struct iwmct_priv *priv)
449{
450#ifdef CONFIG_IWMC3200TOP_DEBUG
451 int i;
452
453 for (i = 0; i < log_level_argc; i++) {
454 dev_notice(&priv->func->dev, "log_level[%d]=0x%X\n",
455 i, log_level[i]);
456 iwmct_log_set_filter((log_level[i] >> 8) & 0xFF,
457 log_level[i] & 0xFF);
458 }
459 for (i = 0; i < log_level_fw_argc; i++) {
460 dev_notice(&priv->func->dev, "log_level_fw[%d]=0x%X\n",
461 i, log_level_fw[i]);
462 iwmct_log_set_fw_filter((log_level_fw[i] >> 8) & 0xFF,
463 log_level_fw[i] & 0xFF);
464 }
465#endif
466
467 priv->dbg.blocks = blocks;
468 LOG_INFO(priv, INIT, "blocks=%d\n", blocks);
469 priv->dbg.dump = (bool)dump;
470 LOG_INFO(priv, INIT, "dump=%d\n", dump);
471 priv->dbg.jump = (bool)jump;
472 LOG_INFO(priv, INIT, "jump=%d\n", jump);
473 priv->dbg.direct = (bool)direct;
474 LOG_INFO(priv, INIT, "direct=%d\n", direct);
475 priv->dbg.checksum = (bool)checksum;
476 LOG_INFO(priv, INIT, "checksum=%d\n", checksum);
477 priv->dbg.fw_download = (bool)fw_download;
478 LOG_INFO(priv, INIT, "fw_download=%d\n", fw_download);
479 priv->dbg.block_size = block_size;
480 LOG_INFO(priv, INIT, "block_size=%d\n", block_size);
481 priv->dbg.download_trans_blks = download_trans_blks;
482 LOG_INFO(priv, INIT, "download_trans_blks=%d\n", download_trans_blks);
483}
484
485/*****************************************************************************
486 *
487 * sysfs attributes
488 *
489 *****************************************************************************/
490static ssize_t show_iwmct_fw_version(struct device *d,
491 struct device_attribute *attr, char *buf)
492{
493 struct iwmct_priv *priv = dev_get_drvdata(d);
494 return sprintf(buf, "%s\n", priv->dbg.label_fw);
495}
496static DEVICE_ATTR(cc_label_fw, S_IRUGO, show_iwmct_fw_version, NULL);
497
498#ifdef CONFIG_IWMC3200TOP_DEBUG
499static DEVICE_ATTR(log_level, S_IWUSR | S_IRUGO,
500 show_iwmct_log_level, store_iwmct_log_level);
501static DEVICE_ATTR(log_level_fw, S_IWUSR | S_IRUGO,
502 show_iwmct_log_level_fw, store_iwmct_log_level_fw);
503#endif
504
505static struct attribute *iwmct_sysfs_entries[] = {
506 &dev_attr_cc_label_fw.attr,
507#ifdef CONFIG_IWMC3200TOP_DEBUG
508 &dev_attr_log_level.attr,
509 &dev_attr_log_level_fw.attr,
510#endif
511 NULL
512};
513
514static struct attribute_group iwmct_attribute_group = {
515 .name = NULL, /* put in device directory */
516 .attrs = iwmct_sysfs_entries,
517};
518
519
520static int iwmct_probe(struct sdio_func *func,
521 const struct sdio_device_id *id)
522{
523 struct iwmct_priv *priv;
524 int ret;
525 int val = 1;
526 int addr = IWMC_SDIO_INTR_ENABLE_ADDR;
527
528 dev_dbg(&func->dev, "enter iwmct_probe\n");
529
530 dev_dbg(&func->dev, "IRQ polling period id %u msecs, HZ is %d\n",
531 jiffies_to_msecs(2147483647), HZ);
532
533 priv = kzalloc(sizeof(struct iwmct_priv), GFP_KERNEL);
534 if (!priv) {
535 dev_err(&func->dev, "kzalloc error\n");
536 return -ENOMEM;
537 }
538 priv->func = func;
539 sdio_set_drvdata(func, priv);
540
541
542 /* create drivers work queue */
543 priv->wq = create_workqueue(DRV_NAME "_wq");
544 priv->bus_rescan_wq = create_workqueue(DRV_NAME "_rescan_wq");
545 INIT_WORK(&priv->bus_rescan_worker, iwmct_rescan_worker);
546 INIT_WORK(&priv->isr_worker, iwmct_irq_read_worker);
547
548 init_waitqueue_head(&priv->wait_q);
549
550 sdio_claim_host(func);
551 /* FIXME: Remove after it is fixed in the Boot ROM upgrade */
552 func->enable_timeout = 10;
553
554 /* In our HW, setting the block size also wakes up the boot rom. */
555 ret = sdio_set_block_size(func, priv->dbg.block_size);
556 if (ret) {
557 LOG_ERROR(priv, INIT,
558 "sdio_set_block_size() failure: %d\n", ret);
559 goto error_sdio_enable;
560 }
561
562 ret = sdio_enable_func(func);
563 if (ret) {
564 LOG_ERROR(priv, INIT, "sdio_enable_func() failure: %d\n", ret);
565 goto error_sdio_enable;
566 }
567
568 /* init reset and dev_sync states */
569 atomic_set(&priv->reset, 0);
570 atomic_set(&priv->dev_sync, 0);
571
572 /* init read req queue */
573 INIT_LIST_HEAD(&priv->read_req_list);
574
575 /* process configurable parameters */
576 iwmct_dbg_init_params(priv);
577 ret = sysfs_create_group(&func->dev.kobj, &iwmct_attribute_group);
578 if (ret) {
579 LOG_ERROR(priv, INIT, "Failed to register attributes and "
580 "initialize module_params\n");
581 goto error_dev_attrs;
582 }
583
584 iwmct_dbgfs_register(priv, DRV_NAME);
585
586 if (!priv->dbg.direct && priv->dbg.download_trans_blks > 8) {
587 LOG_INFO(priv, INIT,
588 "Reducing transaction to 8 blocks = 2K (from %d)\n",
589 priv->dbg.download_trans_blks);
590 priv->dbg.download_trans_blks = 8;
591 }
592 priv->trans_len = priv->dbg.download_trans_blks * priv->dbg.block_size;
593 LOG_INFO(priv, INIT, "Transaction length = %d\n", priv->trans_len);
594
595 ret = sdio_claim_irq(func, iwmct_irq);
596 if (ret) {
597 LOG_ERROR(priv, INIT, "sdio_claim_irq() failure: %d\n", ret);
598 goto error_claim_irq;
599 }
600
601
602 /* Enable function's interrupt */
603 sdio_writeb(priv->func, val, addr, &ret);
604 if (ret) {
605 LOG_ERROR(priv, INIT, "Failure writing to "
606 "Interrupt Enable Register (%d): %d\n", addr, ret);
607 goto error_enable_int;
608 }
609
610 sdio_release_host(func);
611
612 LOG_INFO(priv, INIT, "exit iwmct_probe\n");
613
614 return ret;
615
616error_enable_int:
617 sdio_release_irq(func);
618error_claim_irq:
619 sdio_disable_func(func);
620error_dev_attrs:
621 iwmct_dbgfs_unregister(priv->dbgfs);
622 sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
623error_sdio_enable:
624 sdio_release_host(func);
625 return ret;
626}
627
628static void iwmct_remove(struct sdio_func *func)
629{
630 struct iwmct_work_struct *read_req;
631 struct iwmct_priv *priv = sdio_get_drvdata(func);
632
633 priv = sdio_get_drvdata(func);
634
635 LOG_INFO(priv, INIT, "enter\n");
636
637 sdio_claim_host(func);
638 sdio_release_irq(func);
639 sdio_release_host(func);
640
641 /* Safely destroy osc workqueue */
642 destroy_workqueue(priv->bus_rescan_wq);
643 destroy_workqueue(priv->wq);
644
645 sdio_claim_host(func);
646 sdio_disable_func(func);
647 sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
648 iwmct_dbgfs_unregister(priv->dbgfs);
649 sdio_release_host(func);
650
651 /* free read requests */
652 while (!list_empty(&priv->read_req_list)) {
653 read_req = list_entry(priv->read_req_list.next,
654 struct iwmct_work_struct, list);
655
656 list_del(&read_req->list);
657 kfree(read_req);
658 }
659
660 kfree(priv);
661}
662
663
664static const struct sdio_device_id iwmct_ids[] = {
665 { SDIO_DEVICE(SDIO_INTEL_VENDOR_ID, SDIO_DEVICE_ID_INTEL_IWMC3200TOP)},
666 { /* end: all zeroes */ },
667};
668
669MODULE_DEVICE_TABLE(sdio, iwmct_ids);
670
671static struct sdio_driver iwmct_driver = {
672 .probe = iwmct_probe,
673 .remove = iwmct_remove,
674 .name = DRV_NAME,
675 .id_table = iwmct_ids,
676};
677
678static int __init iwmct_init(void)
679{
680 int rc;
681
682 /* Default log filter settings */
683 iwmct_log_set_filter(LOG_SRC_ALL, LOG_SEV_FILTER_RUNTIME);
684 iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FILTER_ALL);
685 iwmct_log_set_fw_filter(LOG_SRC_ALL, FW_LOG_SEV_FILTER_RUNTIME);
686
687 rc = sdio_register_driver(&iwmct_driver);
688
689 return rc;
690}
691
692static void __exit iwmct_exit(void)
693{
694 sdio_unregister_driver(&iwmct_driver);
695}
696
697module_init(iwmct_init);
698module_exit(iwmct_exit);
699