aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorRoger Quadros <roger.quadros@nokia.com>2010-12-01 04:58:54 -0500
committerLuciano Coelho <luciano.coelho@nokia.com>2010-12-15 08:03:17 -0500
commitb69eb80bf7a6922fef8056d42b06124a7de31501 (patch)
tree6c792d2bd69997057abb915a3056d0d52f1fa2e9 /drivers/net
parentfb6a6819fad0d71b47577a51709440a9f8441f0a (diff)
wl1271_sdio_test: Add module for sdio RX/TX testing
This module enables individually generating RX and TX traffic over the SDIO bus on which the WL1271 chipset is connected. This is required to perform RF interference testing. The module takes 2 module parameters 'rx' and 'tx'. To generate RX traffic: modprobe wl1271_sdio_test rx=1 To generate TX traffic: modprobe wl1271_sdio_test tx=1 To generate both RX & TX traffic, set both rx and tx to 1. You can change the testing configuration at runtime by changing the rx & tx values at /sys/modules/wl1271_sdio_test/ To stop testing simply unload the module. Signed-off-by: Roger Quadros <roger.quadros@nokia.com> Reviewed-by: Carlos Chinea <carlos.chinea@nokia.com> Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/wl12xx/Kconfig8
-rw-r--r--drivers/net/wireless/wl12xx/Makefile2
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_sdio_test.c510
3 files changed, 520 insertions, 0 deletions
diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig
index d2adeb1f72b7..085bc44d814b 100644
--- a/drivers/net/wireless/wl12xx/Kconfig
+++ b/drivers/net/wireless/wl12xx/Kconfig
@@ -52,6 +52,14 @@ config WL12XX_SDIO
52 If you choose to build a module, it'll be called wl12xx_sdio. 52 If you choose to build a module, it'll be called wl12xx_sdio.
53 Say N if unsure. 53 Say N if unsure.
54 54
55config WL1271_SDIO_TEST
56 tristate "TI wl1271 SDIO testing support"
57 depends on WL1271 && MMC
58 ---help---
59 This module adds support for the SDIO bus testing with the
60 TI wl1271 chipset. Select this if your platform is using
61 the SDIO bus.
62
55config WL12XX_PLATFORM_DATA 63config WL12XX_PLATFORM_DATA
56 bool 64 bool
57 depends on WL12XX_SDIO != n || WL1251_SDIO != n 65 depends on WL12XX_SDIO != n || WL1251_SDIO != n
diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile
index 005a758174d9..187678503887 100644
--- a/drivers/net/wireless/wl12xx/Makefile
+++ b/drivers/net/wireless/wl12xx/Makefile
@@ -9,5 +9,7 @@ obj-$(CONFIG_WL12XX) += wl12xx.o
9obj-$(CONFIG_WL12XX_SPI) += wl12xx_spi.o 9obj-$(CONFIG_WL12XX_SPI) += wl12xx_spi.o
10obj-$(CONFIG_WL12XX_SDIO) += wl12xx_sdio.o 10obj-$(CONFIG_WL12XX_SDIO) += wl12xx_sdio.o
11 11
12obj-$(CONFIG_WL1271_SDIO_TEST) += wl1271_sdio_test.o
13
12# small builtin driver bit 14# small builtin driver bit
13obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o 15obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o
diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio_test.c b/drivers/net/wireless/wl12xx/wl1271_sdio_test.c
new file mode 100644
index 000000000000..42d13144f645
--- /dev/null
+++ b/drivers/net/wireless/wl12xx/wl1271_sdio_test.c
@@ -0,0 +1,510 @@
1/*
2 * wl1271_sdio_test.c - SDIO testing driver for wl1271
3 *
4 * Copyright (C) 2010 Nokia Corporation
5 *
6 * Contact: Roger Quadros <roger.quadros@nokia.com>
7 *
8 * wl1271 read/write routines taken from wl1271_sdio.c
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * version 2 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA
23 *
24 */
25
26#include <linux/irq.h>
27#include <linux/module.h>
28#include <linux/crc7.h>
29#include <linux/vmalloc.h>
30#include <linux/mmc/sdio_func.h>
31#include <linux/mmc/sdio_ids.h>
32#include <linux/mmc/card.h>
33#include <linux/gpio.h>
34#include <linux/wl12xx.h>
35#include <linux/kthread.h>
36#include <linux/firmware.h>
37
38#include "wl1271.h"
39#include "wl1271_io.h"
40#include "wl1271_boot.h"
41
42#ifndef SDIO_VENDOR_ID_TI
43#define SDIO_VENDOR_ID_TI 0x0097
44#endif
45
46#ifndef SDIO_DEVICE_ID_TI_WL1271
47#define SDIO_DEVICE_ID_TI_WL1271 0x4076
48#endif
49
50static bool rx, tx;
51
52module_param(rx, bool, S_IRUGO | S_IWUSR);
53MODULE_PARM_DESC(rx, "Perform rx test. Default (0). "
54 "This test continuously reads data from the SDIO device.\n");
55
56module_param(tx, bool, S_IRUGO | S_IWUSR);
57MODULE_PARM_DESC(tx, "Perform tx test. Default (0). "
58 "This test continuously writes data to the SDIO device.\n");
59
60struct wl1271_test {
61 struct wl1271 wl;
62 struct task_struct *test_task;
63};
64
65static const struct sdio_device_id wl1271_devices[] = {
66 { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) },
67 {}
68};
69
70static inline struct sdio_func *wl_to_func(struct wl1271 *wl)
71{
72 return wl->if_priv;
73}
74
75static struct device *wl1271_sdio_wl_to_dev(struct wl1271 *wl)
76{
77 return &(wl_to_func(wl)->dev);
78}
79
80static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
81 size_t len, bool fixed)
82{
83 int ret = 0;
84 struct sdio_func *func = wl_to_func(wl);
85
86 if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
87 ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
88 wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x",
89 addr, ((u8 *)buf)[0]);
90 } else {
91 if (fixed)
92 ret = sdio_readsb(func, buf, addr, len);
93 else
94 ret = sdio_memcpy_fromio(func, buf, addr, len);
95
96 wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %zu bytes",
97 addr, len);
98 wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
99 }
100
101 if (ret)
102 wl1271_error("sdio read failed (%d)", ret);
103}
104
105static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
106 size_t len, bool fixed)
107{
108 int ret = 0;
109 struct sdio_func *func = wl_to_func(wl);
110
111 if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
112 sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
113 wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x",
114 addr, ((u8 *)buf)[0]);
115 } else {
116 wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %zu bytes",
117 addr, len);
118 wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
119
120 if (fixed)
121 ret = sdio_writesb(func, addr, buf, len);
122 else
123 ret = sdio_memcpy_toio(func, addr, buf, len);
124 }
125 if (ret)
126 wl1271_error("sdio write failed (%d)", ret);
127
128}
129
130static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
131{
132 struct sdio_func *func = wl_to_func(wl);
133
134 /* Let the SDIO stack handle wlan_enable control, so we
135 * keep host claimed while wlan is in use to keep wl1271
136 * alive.
137 */
138 if (enable) {
139 sdio_claim_power(func);
140 sdio_claim_host(func);
141 sdio_enable_func(func);
142 } else {
143 sdio_disable_func(func);
144 sdio_release_host(func);
145 sdio_release_power(func);
146 }
147
148 return 0;
149}
150
151static void wl1271_sdio_disable_interrupts(struct wl1271 *wl)
152{
153}
154
155static void wl1271_sdio_enable_interrupts(struct wl1271 *wl)
156{
157}
158
159
160static struct wl1271_if_operations sdio_ops = {
161 .read = wl1271_sdio_raw_read,
162 .write = wl1271_sdio_raw_write,
163 .power = wl1271_sdio_set_power,
164 .dev = wl1271_sdio_wl_to_dev,
165 .enable_irq = wl1271_sdio_enable_interrupts,
166 .disable_irq = wl1271_sdio_disable_interrupts,
167};
168
169static void wl1271_fw_wakeup(struct wl1271 *wl)
170{
171 u32 elp_reg;
172
173 elp_reg = ELPCTRL_WAKE_UP;
174 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
175}
176
177static int wl1271_fetch_firmware(struct wl1271 *wl)
178{
179 const struct firmware *fw;
180 int ret;
181
182 ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl));
183
184 if (ret < 0) {
185 wl1271_error("could not get firmware: %d", ret);
186 return ret;
187 }
188
189 if (fw->size % 4) {
190 wl1271_error("firmware size is not multiple of 32 bits: %zu",
191 fw->size);
192 ret = -EILSEQ;
193 goto out;
194 }
195
196 wl->fw_len = fw->size;
197 wl->fw = vmalloc(wl->fw_len);
198
199 if (!wl->fw) {
200 wl1271_error("could not allocate memory for the firmware");
201 ret = -ENOMEM;
202 goto out;
203 }
204
205 memcpy(wl->fw, fw->data, wl->fw_len);
206
207 ret = 0;
208
209out:
210 release_firmware(fw);
211
212 return ret;
213}
214
215static int wl1271_fetch_nvs(struct wl1271 *wl)
216{
217 const struct firmware *fw;
218 int ret;
219
220 ret = request_firmware(&fw, WL1271_NVS_NAME, wl1271_wl_to_dev(wl));
221
222 if (ret < 0) {
223 wl1271_error("could not get nvs file: %d", ret);
224 return ret;
225 }
226
227 wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL);
228
229 if (!wl->nvs) {
230 wl1271_error("could not allocate memory for the nvs file");
231 ret = -ENOMEM;
232 goto out;
233 }
234
235 wl->nvs_len = fw->size;
236
237out:
238 release_firmware(fw);
239
240 return ret;
241}
242
243static int wl1271_chip_wakeup(struct wl1271 *wl)
244{
245 struct wl1271_partition_set partition;
246 int ret;
247
248 msleep(WL1271_PRE_POWER_ON_SLEEP);
249 ret = wl1271_power_on(wl);
250 if (ret)
251 return ret;
252
253 msleep(WL1271_POWER_ON_SLEEP);
254
255 /* We don't need a real memory partition here, because we only want
256 * to use the registers at this point. */
257 memset(&partition, 0, sizeof(partition));
258 partition.reg.start = REGISTERS_BASE;
259 partition.reg.size = REGISTERS_DOWN_SIZE;
260 wl1271_set_partition(wl, &partition);
261
262 /* ELP module wake up */
263 wl1271_fw_wakeup(wl);
264
265 /* whal_FwCtrl_BootSm() */
266
267 /* 0. read chip id from CHIP_ID */
268 wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
269
270 /* 1. check if chip id is valid */
271
272 switch (wl->chip.id) {
273 case CHIP_ID_1271_PG10:
274 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
275 wl->chip.id);
276 break;
277 case CHIP_ID_1271_PG20:
278 wl1271_notice("chip id 0x%x (1271 PG20)",
279 wl->chip.id);
280 break;
281 default:
282 wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
283 return -ENODEV;
284 }
285
286 return ret;
287}
288
289static struct wl1271_partition_set part_down = {
290 .mem = {
291 .start = 0x00000000,
292 .size = 0x000177c0
293 },
294 .reg = {
295 .start = REGISTERS_BASE,
296 .size = 0x00008800
297 },
298 .mem2 = {
299 .start = 0x00000000,
300 .size = 0x00000000
301 },
302 .mem3 = {
303 .start = 0x00000000,
304 .size = 0x00000000
305 },
306};
307
308static int tester(void *data)
309{
310 struct wl1271 *wl = data;
311 struct sdio_func *func = wl_to_func(wl);
312 struct device *pdev = &func->dev;
313 int ret = 0;
314 bool rx_started = 0;
315 bool tx_started = 0;
316 uint8_t *tx_buf, *rx_buf;
317 int test_size = PAGE_SIZE;
318 u32 addr = 0;
319 struct wl1271_partition_set partition;
320
321 /* We assume chip is powered up and firmware fetched */
322
323 memcpy(&partition, &part_down, sizeof(partition));
324 partition.mem.start = addr;
325 wl1271_set_partition(wl, &partition);
326
327 tx_buf = kmalloc(test_size, GFP_KERNEL);
328 rx_buf = kmalloc(test_size, GFP_KERNEL);
329 if (!tx_buf || !rx_buf) {
330 dev_err(pdev,
331 "Could not allocate memory. Test will not run.\n");
332 ret = -ENOMEM;
333 goto free;
334 }
335
336 memset(tx_buf, 0x5a, test_size);
337
338 /* write something in data area so we can read it back */
339 wl1271_write(wl, addr, tx_buf, test_size, false);
340
341 while (!kthread_should_stop()) {
342 if (rx && !rx_started) {
343 dev_info(pdev, "starting rx test\n");
344 rx_started = 1;
345 } else if (!rx && rx_started) {
346 dev_info(pdev, "stopping rx test\n");
347 rx_started = 0;
348 }
349
350 if (tx && !tx_started) {
351 dev_info(pdev, "starting tx test\n");
352 tx_started = 1;
353 } else if (!tx && tx_started) {
354 dev_info(pdev, "stopping tx test\n");
355 tx_started = 0;
356 }
357
358 if (rx_started)
359 wl1271_read(wl, addr, rx_buf, test_size, false);
360
361 if (tx_started)
362 wl1271_write(wl, addr, tx_buf, test_size, false);
363
364 if (!rx_started && !tx_started)
365 msleep(100);
366 }
367
368free:
369 kfree(tx_buf);
370 kfree(rx_buf);
371 return ret;
372}
373
374static int __devinit wl1271_probe(struct sdio_func *func,
375 const struct sdio_device_id *id)
376{
377 const struct wl12xx_platform_data *wlan_data;
378 struct wl1271 *wl;
379 struct wl1271_test *wl_test;
380 int ret = 0;
381
382 /* wl1271 has 2 sdio functions we handle just the wlan part */
383 if (func->num != 0x02)
384 return -ENODEV;
385
386 wl_test = kzalloc(sizeof(struct wl1271_test), GFP_KERNEL);
387 if (!wl_test) {
388 dev_err(&func->dev, "Could not allocate memory\n");
389 return -ENOMEM;
390 }
391
392 wl = &wl_test->wl;
393
394 wl->if_priv = func;
395 wl->if_ops = &sdio_ops;
396
397 /* Grab access to FN0 for ELP reg. */
398 func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
399
400 wlan_data = wl12xx_get_platform_data();
401 if (IS_ERR(wlan_data)) {
402 ret = PTR_ERR(wlan_data);
403 dev_err(&func->dev, "missing wlan platform data: %d\n", ret);
404 goto out_free;
405 }
406
407 wl->irq = wlan_data->irq;
408 wl->ref_clock = wlan_data->board_ref_clock;
409
410 sdio_set_drvdata(func, wl_test);
411
412
413 /* power up the device */
414 ret = wl1271_chip_wakeup(wl);
415 if (ret) {
416 dev_err(&func->dev, "could not wake up chip\n");
417 goto out_free;
418 }
419
420 if (wl->fw == NULL) {
421 ret = wl1271_fetch_firmware(wl);
422 if (ret < 0) {
423 dev_err(&func->dev, "firmware fetch error\n");
424 goto out_off;
425 }
426 }
427
428 /* fetch NVS */
429 if (wl->nvs == NULL) {
430 ret = wl1271_fetch_nvs(wl);
431 if (ret < 0) {
432 dev_err(&func->dev, "NVS fetch error\n");
433 goto out_off;
434 }
435 }
436
437 ret = wl1271_load_firmware(wl);
438 if (ret < 0) {
439 dev_err(&func->dev, "firmware load error: %d\n", ret);
440 goto out_free;
441 }
442
443 dev_info(&func->dev, "initialized\n");
444
445 /* I/O testing will be done in the tester thread */
446
447 wl_test->test_task = kthread_run(tester, wl, "sdio_tester");
448 if (IS_ERR(wl_test->test_task)) {
449 dev_err(&func->dev, "unable to create kernel thread\n");
450 ret = PTR_ERR(wl_test->test_task);
451 goto out_free;
452 }
453
454 return 0;
455
456out_off:
457 /* power off the chip */
458 wl1271_power_off(wl);
459
460out_free:
461 kfree(wl_test);
462 return ret;
463}
464
465static void __devexit wl1271_remove(struct sdio_func *func)
466{
467 struct wl1271_test *wl_test = sdio_get_drvdata(func);
468
469 /* stop the I/O test thread */
470 kthread_stop(wl_test->test_task);
471
472 /* power off the chip */
473 wl1271_power_off(&wl_test->wl);
474
475 vfree(wl_test->wl.fw);
476 wl_test->wl.fw = NULL;
477 kfree(wl_test->wl.nvs);
478 wl_test->wl.nvs = NULL;
479
480 kfree(wl_test);
481}
482
483static struct sdio_driver wl1271_sdio_driver = {
484 .name = "wl1271_sdio_test",
485 .id_table = wl1271_devices,
486 .probe = wl1271_probe,
487 .remove = __devexit_p(wl1271_remove),
488};
489
490static int __init wl1271_init(void)
491{
492 int ret;
493
494 ret = sdio_register_driver(&wl1271_sdio_driver);
495 if (ret < 0)
496 pr_err("failed to register sdio driver: %d\n", ret);
497
498 return ret;
499}
500module_init(wl1271_init);
501
502static void __exit wl1271_exit(void)
503{
504 sdio_unregister_driver(&wl1271_sdio_driver);
505}
506module_exit(wl1271_exit);
507
508MODULE_LICENSE("GPL");
509MODULE_AUTHOR("Roger Quadros <roger.quadros@nokia.com>");
510