diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/wl1271_spi.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_spi.c | 267 |
1 files changed, 255 insertions, 12 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 67a82934f36e..ed285fec2a08 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c | |||
@@ -21,17 +21,69 @@ | |||
21 | * | 21 | * |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/irq.h> | ||
24 | #include <linux/module.h> | 25 | #include <linux/module.h> |
25 | #include <linux/platform_device.h> | 26 | #include <linux/platform_device.h> |
26 | #include <linux/crc7.h> | 27 | #include <linux/crc7.h> |
27 | #include <linux/spi/spi.h> | 28 | #include <linux/spi/spi.h> |
29 | #include <linux/spi/wl12xx.h> | ||
28 | 30 | ||
29 | #include "wl1271.h" | 31 | #include "wl1271.h" |
30 | #include "wl12xx_80211.h" | 32 | #include "wl12xx_80211.h" |
31 | #include "wl1271_spi.h" | 33 | #include "wl1271_io.h" |
34 | |||
35 | #include "wl1271_reg.h" | ||
36 | |||
37 | #define WSPI_CMD_READ 0x40000000 | ||
38 | #define WSPI_CMD_WRITE 0x00000000 | ||
39 | #define WSPI_CMD_FIXED 0x20000000 | ||
40 | #define WSPI_CMD_BYTE_LENGTH 0x1FFE0000 | ||
41 | #define WSPI_CMD_BYTE_LENGTH_OFFSET 17 | ||
42 | #define WSPI_CMD_BYTE_ADDR 0x0001FFFF | ||
43 | |||
44 | #define WSPI_INIT_CMD_CRC_LEN 5 | ||
45 | |||
46 | #define WSPI_INIT_CMD_START 0x00 | ||
47 | #define WSPI_INIT_CMD_TX 0x40 | ||
48 | /* the extra bypass bit is sampled by the TNET as '1' */ | ||
49 | #define WSPI_INIT_CMD_BYPASS_BIT 0x80 | ||
50 | #define WSPI_INIT_CMD_FIXEDBUSY_LEN 0x07 | ||
51 | #define WSPI_INIT_CMD_EN_FIXEDBUSY 0x80 | ||
52 | #define WSPI_INIT_CMD_DIS_FIXEDBUSY 0x00 | ||
53 | #define WSPI_INIT_CMD_IOD 0x40 | ||
54 | #define WSPI_INIT_CMD_IP 0x20 | ||
55 | #define WSPI_INIT_CMD_CS 0x10 | ||
56 | #define WSPI_INIT_CMD_WS 0x08 | ||
57 | #define WSPI_INIT_CMD_WSPI 0x01 | ||
58 | #define WSPI_INIT_CMD_END 0x01 | ||
59 | |||
60 | #define WSPI_INIT_CMD_LEN 8 | ||
61 | |||
62 | #define HW_ACCESS_WSPI_FIXED_BUSY_LEN \ | ||
63 | ((WL1271_BUSY_WORD_LEN - 4) / sizeof(u32)) | ||
64 | #define HW_ACCESS_WSPI_INIT_CMD_MASK 0 | ||
65 | |||
66 | static inline struct spi_device *wl_to_spi(struct wl1271 *wl) | ||
67 | { | ||
68 | return wl->if_priv; | ||
69 | } | ||
70 | |||
71 | static struct device *wl1271_spi_wl_to_dev(struct wl1271 *wl) | ||
72 | { | ||
73 | return &(wl_to_spi(wl)->dev); | ||
74 | } | ||
32 | 75 | ||
76 | static void wl1271_spi_disable_interrupts(struct wl1271 *wl) | ||
77 | { | ||
78 | disable_irq(wl->irq); | ||
79 | } | ||
80 | |||
81 | static void wl1271_spi_enable_interrupts(struct wl1271 *wl) | ||
82 | { | ||
83 | enable_irq(wl->irq); | ||
84 | } | ||
33 | 85 | ||
34 | void wl1271_spi_reset(struct wl1271 *wl) | 86 | static void wl1271_spi_reset(struct wl1271 *wl) |
35 | { | 87 | { |
36 | u8 *cmd; | 88 | u8 *cmd; |
37 | struct spi_transfer t; | 89 | struct spi_transfer t; |
@@ -52,12 +104,12 @@ void wl1271_spi_reset(struct wl1271 *wl) | |||
52 | t.len = WSPI_INIT_CMD_LEN; | 104 | t.len = WSPI_INIT_CMD_LEN; |
53 | spi_message_add_tail(&t, &m); | 105 | spi_message_add_tail(&t, &m); |
54 | 106 | ||
55 | spi_sync(wl->spi, &m); | 107 | spi_sync(wl_to_spi(wl), &m); |
56 | 108 | ||
57 | wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); | 109 | wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); |
58 | } | 110 | } |
59 | 111 | ||
60 | void wl1271_spi_init(struct wl1271 *wl) | 112 | static void wl1271_spi_init(struct wl1271 *wl) |
61 | { | 113 | { |
62 | u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; | 114 | u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; |
63 | struct spi_transfer t; | 115 | struct spi_transfer t; |
@@ -106,7 +158,7 @@ void wl1271_spi_init(struct wl1271 *wl) | |||
106 | t.len = WSPI_INIT_CMD_LEN; | 158 | t.len = WSPI_INIT_CMD_LEN; |
107 | spi_message_add_tail(&t, &m); | 159 | spi_message_add_tail(&t, &m); |
108 | 160 | ||
109 | spi_sync(wl->spi, &m); | 161 | spi_sync(wl_to_spi(wl), &m); |
110 | 162 | ||
111 | wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); | 163 | wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); |
112 | } | 164 | } |
@@ -138,7 +190,7 @@ static void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len) | |||
138 | t[0].rx_buf = buf + (len - num_busy_bytes); | 190 | t[0].rx_buf = buf + (len - num_busy_bytes); |
139 | t[0].len = num_busy_bytes; | 191 | t[0].len = num_busy_bytes; |
140 | spi_message_add_tail(&t[0], &m); | 192 | spi_message_add_tail(&t[0], &m); |
141 | spi_sync(wl->spi, &m); | 193 | spi_sync(wl_to_spi(wl), &m); |
142 | return; | 194 | return; |
143 | } | 195 | } |
144 | } | 196 | } |
@@ -158,7 +210,7 @@ static void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len) | |||
158 | t[0].rx_buf = busy_buf; | 210 | t[0].rx_buf = busy_buf; |
159 | t[0].len = sizeof(u32); | 211 | t[0].len = sizeof(u32); |
160 | spi_message_add_tail(&t[0], &m); | 212 | spi_message_add_tail(&t[0], &m); |
161 | spi_sync(wl->spi, &m); | 213 | spi_sync(wl_to_spi(wl), &m); |
162 | 214 | ||
163 | if (*busy_buf & 0x1) { | 215 | if (*busy_buf & 0x1) { |
164 | spi_message_init(&m); | 216 | spi_message_init(&m); |
@@ -166,7 +218,7 @@ static void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len) | |||
166 | t[0].rx_buf = buf; | 218 | t[0].rx_buf = buf; |
167 | t[0].len = len; | 219 | t[0].len = len; |
168 | spi_message_add_tail(&t[0], &m); | 220 | spi_message_add_tail(&t[0], &m); |
169 | spi_sync(wl->spi, &m); | 221 | spi_sync(wl_to_spi(wl), &m); |
170 | return; | 222 | return; |
171 | } | 223 | } |
172 | } | 224 | } |
@@ -177,7 +229,7 @@ static void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len) | |||
177 | } | 229 | } |
178 | #endif | 230 | #endif |
179 | 231 | ||
180 | void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, | 232 | static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, |
181 | size_t len, bool fixed) | 233 | size_t len, bool fixed) |
182 | { | 234 | { |
183 | struct spi_transfer t[3]; | 235 | struct spi_transfer t[3]; |
@@ -212,7 +264,7 @@ void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, | |||
212 | t[2].len = len; | 264 | t[2].len = len; |
213 | spi_message_add_tail(&t[2], &m); | 265 | spi_message_add_tail(&t[2], &m); |
214 | 266 | ||
215 | spi_sync(wl->spi, &m); | 267 | spi_sync(wl_to_spi(wl), &m); |
216 | 268 | ||
217 | /* FIXME: Check busy words, removed due to SPI bug */ | 269 | /* FIXME: Check busy words, removed due to SPI bug */ |
218 | /* if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1)) | 270 | /* if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1)) |
@@ -222,7 +274,7 @@ void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, | |||
222 | wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); | 274 | wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); |
223 | } | 275 | } |
224 | 276 | ||
225 | void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, | 277 | static void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, |
226 | size_t len, bool fixed) | 278 | size_t len, bool fixed) |
227 | { | 279 | { |
228 | struct spi_transfer t[2]; | 280 | struct spi_transfer t[2]; |
@@ -250,8 +302,199 @@ void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, | |||
250 | t[1].len = len; | 302 | t[1].len = len; |
251 | spi_message_add_tail(&t[1], &m); | 303 | spi_message_add_tail(&t[1], &m); |
252 | 304 | ||
253 | spi_sync(wl->spi, &m); | 305 | spi_sync(wl_to_spi(wl), &m); |
254 | 306 | ||
255 | wl1271_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); | 307 | wl1271_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); |
256 | wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); | 308 | wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); |
257 | } | 309 | } |
310 | |||
311 | static irqreturn_t wl1271_irq(int irq, void *cookie) | ||
312 | { | ||
313 | struct wl1271 *wl; | ||
314 | unsigned long flags; | ||
315 | |||
316 | wl1271_debug(DEBUG_IRQ, "IRQ"); | ||
317 | |||
318 | wl = cookie; | ||
319 | |||
320 | /* complete the ELP completion */ | ||
321 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
322 | if (wl->elp_compl) { | ||
323 | complete(wl->elp_compl); | ||
324 | wl->elp_compl = NULL; | ||
325 | } | ||
326 | |||
327 | if (!test_and_set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags)) | ||
328 | ieee80211_queue_work(wl->hw, &wl->irq_work); | ||
329 | set_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags); | ||
330 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
331 | |||
332 | return IRQ_HANDLED; | ||
333 | } | ||
334 | |||
335 | static void wl1271_device_release(struct device *dev) | ||
336 | { | ||
337 | |||
338 | } | ||
339 | |||
340 | static struct platform_device wl1271_device = { | ||
341 | .name = "wl1271", | ||
342 | .id = -1, | ||
343 | |||
344 | /* device model insists to have a release function */ | ||
345 | .dev = { | ||
346 | .release = wl1271_device_release, | ||
347 | }, | ||
348 | }; | ||
349 | |||
350 | static struct wl1271_if_operations spi_ops = { | ||
351 | .read = wl1271_spi_raw_read, | ||
352 | .write = wl1271_spi_raw_write, | ||
353 | .reset = wl1271_spi_reset, | ||
354 | .init = wl1271_spi_init, | ||
355 | .dev = wl1271_spi_wl_to_dev, | ||
356 | .enable_irq = wl1271_spi_enable_interrupts, | ||
357 | .disable_irq = wl1271_spi_disable_interrupts | ||
358 | }; | ||
359 | |||
360 | static int __devinit wl1271_probe(struct spi_device *spi) | ||
361 | { | ||
362 | struct wl12xx_platform_data *pdata; | ||
363 | struct ieee80211_hw *hw; | ||
364 | struct wl1271 *wl; | ||
365 | int ret; | ||
366 | |||
367 | pdata = spi->dev.platform_data; | ||
368 | if (!pdata) { | ||
369 | wl1271_error("no platform data"); | ||
370 | return -ENODEV; | ||
371 | } | ||
372 | |||
373 | hw = wl1271_alloc_hw(); | ||
374 | if (IS_ERR(hw)) | ||
375 | return PTR_ERR(hw); | ||
376 | |||
377 | wl = hw->priv; | ||
378 | |||
379 | dev_set_drvdata(&spi->dev, wl); | ||
380 | wl->if_priv = spi; | ||
381 | |||
382 | wl->if_ops = &spi_ops; | ||
383 | |||
384 | /* This is the only SPI value that we need to set here, the rest | ||
385 | * comes from the board-peripherals file */ | ||
386 | spi->bits_per_word = 32; | ||
387 | |||
388 | ret = spi_setup(spi); | ||
389 | if (ret < 0) { | ||
390 | wl1271_error("spi_setup failed"); | ||
391 | goto out_free; | ||
392 | } | ||
393 | |||
394 | wl->set_power = pdata->set_power; | ||
395 | if (!wl->set_power) { | ||
396 | wl1271_error("set power function missing in platform data"); | ||
397 | ret = -ENODEV; | ||
398 | goto out_free; | ||
399 | } | ||
400 | |||
401 | wl->irq = spi->irq; | ||
402 | if (wl->irq < 0) { | ||
403 | wl1271_error("irq missing in platform data"); | ||
404 | ret = -ENODEV; | ||
405 | goto out_free; | ||
406 | } | ||
407 | |||
408 | ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl); | ||
409 | if (ret < 0) { | ||
410 | wl1271_error("request_irq() failed: %d", ret); | ||
411 | goto out_free; | ||
412 | } | ||
413 | |||
414 | set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); | ||
415 | |||
416 | disable_irq(wl->irq); | ||
417 | |||
418 | ret = platform_device_register(&wl1271_device); | ||
419 | if (ret) { | ||
420 | wl1271_error("couldn't register platform device"); | ||
421 | goto out_irq; | ||
422 | } | ||
423 | dev_set_drvdata(&wl1271_device.dev, wl); | ||
424 | |||
425 | ret = wl1271_init_ieee80211(wl); | ||
426 | if (ret) | ||
427 | goto out_platform; | ||
428 | |||
429 | ret = wl1271_register_hw(wl); | ||
430 | if (ret) | ||
431 | goto out_platform; | ||
432 | |||
433 | wl1271_notice("initialized"); | ||
434 | |||
435 | return 0; | ||
436 | |||
437 | out_platform: | ||
438 | platform_device_unregister(&wl1271_device); | ||
439 | |||
440 | out_irq: | ||
441 | free_irq(wl->irq, wl); | ||
442 | |||
443 | out_free: | ||
444 | ieee80211_free_hw(hw); | ||
445 | |||
446 | return ret; | ||
447 | } | ||
448 | |||
449 | static int __devexit wl1271_remove(struct spi_device *spi) | ||
450 | { | ||
451 | struct wl1271 *wl = dev_get_drvdata(&spi->dev); | ||
452 | |||
453 | platform_device_unregister(&wl1271_device); | ||
454 | free_irq(wl->irq, wl); | ||
455 | |||
456 | wl1271_free_hw(wl); | ||
457 | |||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | |||
462 | static struct spi_driver wl1271_spi_driver = { | ||
463 | .driver = { | ||
464 | .name = "wl1271", | ||
465 | .bus = &spi_bus_type, | ||
466 | .owner = THIS_MODULE, | ||
467 | }, | ||
468 | |||
469 | .probe = wl1271_probe, | ||
470 | .remove = __devexit_p(wl1271_remove), | ||
471 | }; | ||
472 | |||
473 | static int __init wl1271_init(void) | ||
474 | { | ||
475 | int ret; | ||
476 | |||
477 | ret = spi_register_driver(&wl1271_spi_driver); | ||
478 | if (ret < 0) { | ||
479 | wl1271_error("failed to register spi driver: %d", ret); | ||
480 | goto out; | ||
481 | } | ||
482 | |||
483 | out: | ||
484 | return ret; | ||
485 | } | ||
486 | |||
487 | static void __exit wl1271_exit(void) | ||
488 | { | ||
489 | spi_unregister_driver(&wl1271_spi_driver); | ||
490 | |||
491 | wl1271_notice("unloaded"); | ||
492 | } | ||
493 | |||
494 | module_init(wl1271_init); | ||
495 | module_exit(wl1271_exit); | ||
496 | |||
497 | MODULE_LICENSE("GPL"); | ||
498 | MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); | ||
499 | MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); | ||
500 | MODULE_FIRMWARE(WL1271_FW_NAME); | ||