~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/net/wireless/iwmc3200wifi/sdio.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/module.h>
 
67
#include <linux/slab.h>
 
68
#include <linux/netdevice.h>
 
69
#include <linux/debugfs.h>
 
70
#include <linux/mmc/sdio_ids.h>
 
71
#include <linux/mmc/sdio.h>
 
72
#include <linux/mmc/sdio_func.h>
 
73
 
 
74
#include "iwm.h"
 
75
#include "debug.h"
 
76
#include "bus.h"
 
77
#include "sdio.h"
 
78
 
 
79
static void iwm_sdio_isr_worker(struct work_struct *work)
 
80
{
 
81
        struct iwm_sdio_priv *hw;
 
82
        struct iwm_priv *iwm;
 
83
        struct iwm_rx_info *rx_info;
 
84
        struct sk_buff *skb;
 
85
        u8 *rx_buf;
 
86
        unsigned long rx_size;
 
87
 
 
88
        hw = container_of(work, struct iwm_sdio_priv, isr_worker);
 
89
        iwm = hw_to_iwm(hw);
 
90
 
 
91
        while (!skb_queue_empty(&iwm->rx_list)) {
 
92
                skb = skb_dequeue(&iwm->rx_list);
 
93
                rx_info = skb_to_rx_info(skb);
 
94
                rx_size = rx_info->rx_size;
 
95
                rx_buf = skb->data;
 
96
 
 
97
                IWM_HEXDUMP(iwm, DBG, SDIO, "RX: ", rx_buf, rx_size);
 
98
                if (iwm_rx_handle(iwm, rx_buf, rx_size) < 0)
 
99
                        IWM_WARN(iwm, "RX error\n");
 
100
 
 
101
                kfree_skb(skb);
 
102
        }
 
103
}
 
104
 
 
105
static void iwm_sdio_isr(struct sdio_func *func)
 
106
{
 
107
        struct iwm_priv *iwm;
 
108
        struct iwm_sdio_priv *hw;
 
109
        struct iwm_rx_info *rx_info;
 
110
        struct sk_buff *skb;
 
111
        unsigned long buf_size, read_size;
 
112
        int ret;
 
113
        u8 val;
 
114
 
 
115
        hw = sdio_get_drvdata(func);
 
116
        iwm = hw_to_iwm(hw);
 
117
 
 
118
        buf_size = hw->blk_size;
 
119
 
 
120
        /* We're checking the status */
 
121
        val = sdio_readb(func, IWM_SDIO_INTR_STATUS_ADDR, &ret);
 
122
        if (val == 0 || ret < 0) {
 
123
                IWM_ERR(iwm, "Wrong INTR_STATUS\n");
 
124
                return;
 
125
        }
 
126
 
 
127
        /* See if we have free buffers */
 
128
        if (skb_queue_len(&iwm->rx_list) > IWM_RX_LIST_SIZE) {
 
129
                IWM_ERR(iwm, "No buffer for more Rx frames\n");
 
130
                return;
 
131
        }
 
132
 
 
133
        /* We first read the transaction size */
 
134
        read_size = sdio_readb(func, IWM_SDIO_INTR_GET_SIZE_ADDR + 1, &ret);
 
135
        read_size = read_size << 8;
 
136
 
 
137
        if (ret < 0) {
 
138
                IWM_ERR(iwm, "Couldn't read the xfer size\n");
 
139
                return;
 
140
        }
 
141
 
 
142
        /* We need to clear the INT register */
 
143
        sdio_writeb(func, 1, IWM_SDIO_INTR_CLEAR_ADDR, &ret);
 
144
        if (ret < 0) {
 
145
                IWM_ERR(iwm, "Couldn't clear the INT register\n");
 
146
                return;
 
147
        }
 
148
 
 
149
        while (buf_size < read_size)
 
150
                buf_size <<= 1;
 
151
 
 
152
        skb = dev_alloc_skb(buf_size);
 
153
        if (!skb) {
 
154
                IWM_ERR(iwm, "Couldn't alloc RX skb\n");
 
155
                return;
 
156
        }
 
157
        rx_info = skb_to_rx_info(skb);
 
158
        rx_info->rx_size = read_size;
 
159
        rx_info->rx_buf_size = buf_size;
 
160
 
 
161
        /* Now we can read the actual buffer */
 
162
        ret = sdio_memcpy_fromio(func, skb_put(skb, read_size),
 
163
                                 IWM_SDIO_DATA_ADDR, read_size);
 
164
 
 
165
        /* The skb is put on a driver's specific Rx SKB list */
 
166
        skb_queue_tail(&iwm->rx_list, skb);
 
167
 
 
168
        /* We can now schedule the actual worker */
 
169
        queue_work(hw->isr_wq, &hw->isr_worker);
 
170
}
 
171
 
 
172
static void iwm_sdio_rx_free(struct iwm_sdio_priv *hw)
 
173
{
 
174
        struct iwm_priv *iwm = hw_to_iwm(hw);
 
175
 
 
176
        flush_workqueue(hw->isr_wq);
 
177
 
 
178
        skb_queue_purge(&iwm->rx_list);
 
179
}
 
180
 
 
181
/* Bus ops */
 
182
static int if_sdio_enable(struct iwm_priv *iwm)
 
183
{
 
184
        struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
 
185
        int ret;
 
186
 
 
187
        sdio_claim_host(hw->func);
 
188
 
 
189
        ret = sdio_enable_func(hw->func);
 
190
        if (ret) {
 
191
                IWM_ERR(iwm, "Couldn't enable the device: is TOP driver "
 
192
                        "loaded and functional?\n");
 
193
                goto release_host;
 
194
        }
 
195
 
 
196
        iwm_reset(iwm);
 
197
 
 
198
        ret = sdio_claim_irq(hw->func, iwm_sdio_isr);
 
199
        if (ret) {
 
200
                IWM_ERR(iwm, "Failed to claim irq: %d\n", ret);
 
201
                goto release_host;
 
202
        }
 
203
 
 
204
        sdio_writeb(hw->func, 1, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
 
205
        if (ret < 0) {
 
206
                IWM_ERR(iwm, "Couldn't enable INTR: %d\n", ret);
 
207
                goto release_irq;
 
208
        }
 
209
 
 
210
        sdio_release_host(hw->func);
 
211
 
 
212
        IWM_DBG_SDIO(iwm, INFO, "IWM SDIO enable\n");
 
213
 
 
214
        return 0;
 
215
 
 
216
 release_irq:
 
217
        sdio_release_irq(hw->func);
 
218
 release_host:
 
219
        sdio_release_host(hw->func);
 
220
 
 
221
        return ret;
 
222
}
 
223
 
 
224
static int if_sdio_disable(struct iwm_priv *iwm)
 
225
{
 
226
        struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
 
227
        int ret;
 
228
 
 
229
        sdio_claim_host(hw->func);
 
230
        sdio_writeb(hw->func, 0, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
 
231
        if (ret < 0)
 
232
                IWM_WARN(iwm, "Couldn't disable INTR: %d\n", ret);
 
233
 
 
234
        sdio_release_irq(hw->func);
 
235
        sdio_disable_func(hw->func);
 
236
        sdio_release_host(hw->func);
 
237
 
 
238
        iwm_sdio_rx_free(hw);
 
239
 
 
240
        iwm_reset(iwm);
 
241
 
 
242
        IWM_DBG_SDIO(iwm, INFO, "IWM SDIO disable\n");
 
243
 
 
244
        return 0;
 
245
}
 
246
 
 
247
static int if_sdio_send_chunk(struct iwm_priv *iwm, u8 *buf, int count)
 
248
{
 
249
        struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
 
250
        int aligned_count = ALIGN(count, hw->blk_size);
 
251
        int ret;
 
252
 
 
253
        if ((unsigned long)buf & 0x3) {
 
254
                IWM_ERR(iwm, "buf <%p> is not dword aligned\n", buf);
 
255
                /* TODO: Is this a hardware limitation? use get_unligned */
 
256
                return -EINVAL;
 
257
        }
 
258
 
 
259
        sdio_claim_host(hw->func);
 
260
        ret = sdio_memcpy_toio(hw->func, IWM_SDIO_DATA_ADDR, buf,
 
261
                               aligned_count);
 
262
        sdio_release_host(hw->func);
 
263
 
 
264
        return ret;
 
265
}
 
266
 
 
267
/* debugfs hooks */
 
268
static int iwm_debugfs_sdio_open(struct inode *inode, struct file *filp)
 
269
{
 
270
        filp->private_data = inode->i_private;
 
271
        return 0;
 
272
}
 
273
 
 
274
static ssize_t iwm_debugfs_sdio_read(struct file *filp, char __user *buffer,
 
275
                                     size_t count, loff_t *ppos)
 
276
{
 
277
        struct iwm_priv *iwm = filp->private_data;
 
278
        struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
 
279
        char *buf;
 
280
        u8 cccr;
 
281
        int buf_len = 4096, ret;
 
282
        size_t len = 0;
 
283
 
 
284
        if (*ppos != 0)
 
285
                return 0;
 
286
        if (count < sizeof(buf))
 
287
                return -ENOSPC;
 
288
 
 
289
        buf = kzalloc(buf_len, GFP_KERNEL);
 
290
        if (!buf)
 
291
                return -ENOMEM;
 
292
 
 
293
        sdio_claim_host(hw->func);
 
294
 
 
295
        cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_IOEx, &ret);
 
296
        if (ret) {
 
297
                IWM_ERR(iwm, "Could not read SDIO_CCCR_IOEx\n");
 
298
                goto err;
 
299
        }
 
300
        len += snprintf(buf + len, buf_len - len, "CCCR_IOEx:  0x%x\n", cccr);
 
301
 
 
302
        cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_IORx, &ret);
 
303
        if (ret) {
 
304
                IWM_ERR(iwm, "Could not read SDIO_CCCR_IORx\n");
 
305
                goto err;
 
306
        }
 
307
        len += snprintf(buf + len, buf_len - len, "CCCR_IORx:  0x%x\n", cccr);
 
308
 
 
309
 
 
310
        cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_IENx, &ret);
 
311
        if (ret) {
 
312
                IWM_ERR(iwm, "Could not read SDIO_CCCR_IENx\n");
 
313
                goto err;
 
314
        }
 
315
        len += snprintf(buf + len, buf_len - len, "CCCR_IENx:  0x%x\n", cccr);
 
316
 
 
317
 
 
318
        cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_INTx, &ret);
 
319
        if (ret) {
 
320
                IWM_ERR(iwm, "Could not read SDIO_CCCR_INTx\n");
 
321
                goto err;
 
322
        }
 
323
        len += snprintf(buf + len, buf_len - len, "CCCR_INTx:  0x%x\n", cccr);
 
324
 
 
325
 
 
326
        cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_ABORT, &ret);
 
327
        if (ret) {
 
328
                IWM_ERR(iwm, "Could not read SDIO_CCCR_ABORTx\n");
 
329
                goto err;
 
330
        }
 
331
        len += snprintf(buf + len, buf_len - len, "CCCR_ABORT: 0x%x\n", cccr);
 
332
 
 
333
        cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_IF, &ret);
 
334
        if (ret) {
 
335
                IWM_ERR(iwm, "Could not read SDIO_CCCR_IF\n");
 
336
                goto err;
 
337
        }
 
338
        len += snprintf(buf + len, buf_len - len, "CCCR_IF:    0x%x\n", cccr);
 
339
 
 
340
 
 
341
        cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_CAPS, &ret);
 
342
        if (ret) {
 
343
                IWM_ERR(iwm, "Could not read SDIO_CCCR_CAPS\n");
 
344
                goto err;
 
345
        }
 
346
        len += snprintf(buf + len, buf_len - len, "CCCR_CAPS:  0x%x\n", cccr);
 
347
 
 
348
        cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_CIS, &ret);
 
349
        if (ret) {
 
350
                IWM_ERR(iwm, "Could not read SDIO_CCCR_CIS\n");
 
351
                goto err;
 
352
        }
 
353
        len += snprintf(buf + len, buf_len - len, "CCCR_CIS:   0x%x\n", cccr);
 
354
 
 
355
        ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
 
356
err:
 
357
        sdio_release_host(hw->func);
 
358
 
 
359
        kfree(buf);
 
360
 
 
361
        return ret;
 
362
}
 
363
 
 
364
static const struct file_operations iwm_debugfs_sdio_fops = {
 
365
        .owner =        THIS_MODULE,
 
366
        .open =         iwm_debugfs_sdio_open,
 
367
        .read =         iwm_debugfs_sdio_read,
 
368
        .llseek =       default_llseek,
 
369
};
 
370
 
 
371
static void if_sdio_debugfs_init(struct iwm_priv *iwm, struct dentry *parent_dir)
 
372
{
 
373
        struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
 
374
 
 
375
        hw->cccr_dentry = debugfs_create_file("cccr", 0200,
 
376
                                              parent_dir, iwm,
 
377
                                              &iwm_debugfs_sdio_fops);
 
378
}
 
379
 
 
380
static void if_sdio_debugfs_exit(struct iwm_priv *iwm)
 
381
{
 
382
        struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
 
383
 
 
384
        debugfs_remove(hw->cccr_dentry);
 
385
}
 
386
 
 
387
static struct iwm_if_ops if_sdio_ops = {
 
388
        .enable = if_sdio_enable,
 
389
        .disable = if_sdio_disable,
 
390
        .send_chunk = if_sdio_send_chunk,
 
391
        .debugfs_init = if_sdio_debugfs_init,
 
392
        .debugfs_exit = if_sdio_debugfs_exit,
 
393
        .umac_name = "iwmc3200wifi-umac-sdio.bin",
 
394
        .calib_lmac_name = "iwmc3200wifi-calib-sdio.bin",
 
395
        .lmac_name = "iwmc3200wifi-lmac-sdio.bin",
 
396
};
 
397
MODULE_FIRMWARE("iwmc3200wifi-umac-sdio.bin");
 
398
MODULE_FIRMWARE("iwmc3200wifi-calib-sdio.bin");
 
399
MODULE_FIRMWARE("iwmc3200wifi-lmac-sdio.bin");
 
400
 
 
401
static int iwm_sdio_probe(struct sdio_func *func,
 
402
                          const struct sdio_device_id *id)
 
403
{
 
404
        struct iwm_priv *iwm;
 
405
        struct iwm_sdio_priv *hw;
 
406
        struct device *dev = &func->dev;
 
407
        int ret;
 
408
 
 
409
        /* check if TOP has already initialized the card */
 
410
        sdio_claim_host(func);
 
411
        ret = sdio_enable_func(func);
 
412
        if (ret) {
 
413
                dev_err(dev, "wait for TOP to enable the device\n");
 
414
                sdio_release_host(func);
 
415
                return ret;
 
416
        }
 
417
 
 
418
        ret = sdio_set_block_size(func, IWM_SDIO_BLK_SIZE);
 
419
 
 
420
        sdio_disable_func(func);
 
421
        sdio_release_host(func);
 
422
 
 
423
        if (ret < 0) {
 
424
                dev_err(dev, "Failed to set block size: %d\n", ret);
 
425
                return ret;
 
426
        }
 
427
 
 
428
        iwm = iwm_if_alloc(sizeof(struct iwm_sdio_priv), dev, &if_sdio_ops);
 
429
        if (IS_ERR(iwm)) {
 
430
                dev_err(dev, "allocate SDIO interface failed\n");
 
431
                return PTR_ERR(iwm);
 
432
        }
 
433
 
 
434
        hw = iwm_private(iwm);
 
435
        hw->iwm = iwm;
 
436
 
 
437
        iwm_debugfs_init(iwm);
 
438
 
 
439
        sdio_set_drvdata(func, hw);
 
440
 
 
441
        hw->func = func;
 
442
        hw->blk_size = IWM_SDIO_BLK_SIZE;
 
443
 
 
444
        hw->isr_wq = create_singlethread_workqueue(KBUILD_MODNAME "_sdio");
 
445
        if (!hw->isr_wq) {
 
446
                ret = -ENOMEM;
 
447
                goto debugfs_exit;
 
448
        }
 
449
 
 
450
        INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker);
 
451
 
 
452
        ret = iwm_if_add(iwm);
 
453
        if (ret) {
 
454
                dev_err(dev, "add SDIO interface failed\n");
 
455
                goto destroy_wq;
 
456
        }
 
457
 
 
458
        dev_info(dev, "IWM SDIO probe\n");
 
459
 
 
460
        return 0;
 
461
 
 
462
 destroy_wq:
 
463
        destroy_workqueue(hw->isr_wq);
 
464
 debugfs_exit:
 
465
        iwm_debugfs_exit(iwm);
 
466
        iwm_if_free(iwm);
 
467
        return ret;
 
468
}
 
469
 
 
470
static void iwm_sdio_remove(struct sdio_func *func)
 
471
{
 
472
        struct iwm_sdio_priv *hw = sdio_get_drvdata(func);
 
473
        struct iwm_priv *iwm = hw_to_iwm(hw);
 
474
        struct device *dev = &func->dev;
 
475
 
 
476
        iwm_if_remove(iwm);
 
477
        destroy_workqueue(hw->isr_wq);
 
478
        iwm_debugfs_exit(iwm);
 
479
        iwm_if_free(iwm);
 
480
 
 
481
        sdio_set_drvdata(func, NULL);
 
482
 
 
483
        dev_info(dev, "IWM SDIO remove\n");
 
484
}
 
485
 
 
486
static const struct sdio_device_id iwm_sdio_ids[] = {
 
487
        /* Global/AGN SKU */
 
488
        { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1403) },
 
489
        /* BGN SKU */
 
490
        { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1408) },
 
491
        { /* end: all zeroes */ },
 
492
};
 
493
MODULE_DEVICE_TABLE(sdio, iwm_sdio_ids);
 
494
 
 
495
static struct sdio_driver iwm_sdio_driver = {
 
496
        .name           = "iwm_sdio",
 
497
        .id_table       = iwm_sdio_ids,
 
498
        .probe          = iwm_sdio_probe,
 
499
        .remove         = iwm_sdio_remove,
 
500
};
 
501
 
 
502
static int __init iwm_sdio_init_module(void)
 
503
{
 
504
        return sdio_register_driver(&iwm_sdio_driver);
 
505
}
 
506
 
 
507
static void __exit iwm_sdio_exit_module(void)
 
508
{
 
509
        sdio_unregister_driver(&iwm_sdio_driver);
 
510
}
 
511
 
 
512
module_init(iwm_sdio_init_module);
 
513
module_exit(iwm_sdio_exit_module);
 
514
 
 
515
MODULE_LICENSE("GPL");
 
516
MODULE_AUTHOR(IWM_COPYRIGHT " " IWM_AUTHOR);