Date: | 2011-07-08 05:32:25 (12 years 8 months ago) |
---|---|
Author: | Werner Almesberger |
Commit: | 6c17a31f1fc515425221067cb3ece599c09dbc5d |
Message: | atusb, atben: moved from spi/ to ieee802154/; renamed atusb to
spi_atusb Also made both drivers depend on CONFIG_IEEE802154_AT86RF230 |
Files: |
drivers/ieee802154/Kconfig (1 diff) drivers/ieee802154/Makefile (1 diff) drivers/ieee802154/spi_atben.c (1 diff) drivers/ieee802154/spi_atusb.c (1 diff) drivers/spi/Kconfig (1 diff) drivers/spi/Makefile (1 diff) drivers/spi/atusb.c (1 diff) drivers/spi/spi_atben.c (1 diff) |
Change Details
drivers/ieee802154/Kconfig | ||
---|---|---|
48 | 48 | tristate "AT86RF230 transceiver driver" |
49 | 49 | depends on SPI |
50 | 50 | |
51 | config SPI_ATBEN | |
52 | tristate "ATBEN 8:10 SPI interface" | |
53 | depends on JZ4740_QI_LB60 && IEEE802154_AT86RF230 | |
54 | help | |
55 | Bit-banging SPI driver for the 8:10 interface of the Ben NanoNote | |
56 | when equipped with an ATBEN board. | |
57 | ||
58 | config SPI_ATUSB | |
59 | tristate "ATUSB SPI interface" | |
60 | depends on USB && SPI && IEEE802154_AT86RF230 | |
61 | help | |
62 | SPI-over-USB driver for the ATUSB IEEE 802.15.4 board. | |
63 | ||
51 | 64 | config IEEE802154_CC2420 |
52 | 65 | tristate "CC2420 driver" |
53 | 66 | depends on SPI |
drivers/ieee802154/Makefile | ||
---|---|---|
4 | 4 | obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o |
5 | 5 | obj-$(CONFIG_IEEE802154_CC2420) += cc2420.o |
6 | 6 | obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o |
7 | obj-$(CONFIG_SPI_ATUSB) += spi_atusb.o | |
8 | obj-$(CONFIG_SPI_ATBEN) += spi_atben.o | |
7 | 9 | |
8 | 10 | ccflags-$(CONFIG_IEEE802154_DRIVER_DEBUG) += -DDEBUG |
9 | 11 | ccflags-y += -DCONFIG_FFD |
drivers/ieee802154/spi_atben.c | ||
---|---|---|
1 | /* | |
2 | * spi_atben.c - SPI host look-alike for ATBEN | |
3 | * | |
4 | * Written 2011 by Werner Almesberger | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 | |
8 | * as published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/kernel.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/gpio.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/spi/spi.h> | |
17 | #include <linux/spi/at86rf230.h> | |
18 | #include <asm/mach-jz4740/base.h> | |
19 | ||
20 | #include "../ieee802154/at86rf230.h" /* dirty */ | |
21 | ||
22 | ||
23 | enum { | |
24 | VDD_OFF = 1 << 2, /* VDD disable, PD02 */ | |
25 | MOSI = 1 << 8, /* CMD, PD08 */ | |
26 | SLP_TR = 1 << 9, /* CLK, PD09 */ | |
27 | MISO = 1 << 10, /* DAT0, PD10 */ | |
28 | SCLK = 1 << 11, /* DAT1, PD11 */ | |
29 | IRQ = 1 << 12, /* DAT2, PD12 */ | |
30 | nSEL = 1 << 13, /* DAT3/CD, PD13 */ | |
31 | }; | |
32 | ||
33 | #define PDPIN (prv->regs) | |
34 | #define PDDATS (prv->regs+0x14) | |
35 | #define PDDATC (prv->regs+0x18) | |
36 | ||
37 | ||
38 | struct atben_prv { | |
39 | struct device *dev; | |
40 | void __iomem *regs; | |
41 | struct resource *ioarea; | |
42 | struct at86rf230_platform_data | |
43 | platform_data; | |
44 | /* copy platform_data so that we can adapt .reset_data */ | |
45 | }; | |
46 | ||
47 | ||
48 | /* ----- ATBEN reset ------------------------------------------------------- */ | |
49 | ||
50 | ||
51 | static void atben_reset(void *reset_data) | |
52 | { | |
53 | struct atben_prv *prv = reset_data; | |
54 | const int charge = nSEL | MOSI | SLP_TR | SCLK; | |
55 | const int discharge = charge | IRQ | MISO; | |
56 | ||
57 | dev_info(prv->dev, "atben_reset\n"); | |
58 | jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 1 << 2, 1 << 2); | |
59 | jz_gpio_port_direction_output(JZ_GPIO_PORTD(0), discharge); | |
60 | jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 0, discharge); | |
61 | msleep(100); /* let power drop */ | |
62 | ||
63 | /* | |
64 | * Hack: PD12/DAT2/IRQ is an active-high interrupt input, which is | |
65 | * indicated by setting its direction bit to 1. We thus must not | |
66 | * configure it as an "input". | |
67 | */ | |
68 | jz_gpio_port_direction_input(JZ_GPIO_PORTD(0), MISO); | |
69 | jz_gpio_port_set_value(JZ_GPIO_PORTD(0), charge, charge); | |
70 | msleep(10); /* precharge caps */ | |
71 | ||
72 | jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 0, VDD_OFF | SLP_TR | SCLK); | |
73 | msleep(10); | |
74 | } | |
75 | ||
76 | ||
77 | /* ----- SPI transfers ----------------------------------------------------- */ | |
78 | ||
79 | ||
80 | static void rx_only(const struct atben_prv *prv, uint8_t *buf, int len) | |
81 | { | |
82 | uint8_t v; | |
83 | ||
84 | while (len--) { | |
85 | writel(SCLK, PDDATS); | |
86 | v = readl(PDPIN) & MISO ? 0x80 : 0; | |
87 | writel(SCLK, PDDATC); | |
88 | ||
89 | #define DO_BIT(m) \ | |
90 | writel(SCLK, PDDATS); \ | |
91 | if (readl(PDPIN) & MISO) \ | |
92 | v |= (m); \ | |
93 | writel(SCLK, PDDATC) | |
94 | ||
95 | DO_BIT(0x40); | |
96 | DO_BIT(0x20); | |
97 | DO_BIT(0x10); | |
98 | DO_BIT(0x08); | |
99 | DO_BIT(0x04); | |
100 | DO_BIT(0x02); | |
101 | DO_BIT(0x01); | |
102 | ||
103 | #undef DO_BIT | |
104 | ||
105 | *buf++ = v; | |
106 | } | |
107 | } | |
108 | ||
109 | ||
110 | static void tx_only(const struct atben_prv *prv, const uint8_t *buf, int len) | |
111 | { | |
112 | uint8_t tv; | |
113 | ||
114 | while (len--) { | |
115 | tv = *buf++; | |
116 | ||
117 | if (tv & 0x80) { | |
118 | writel(MOSI, PDDATS); | |
119 | goto b6_1; | |
120 | } else { | |
121 | writel(MOSI, PDDATC); | |
122 | goto b6_0; | |
123 | } | |
124 | ||
125 | #define DO_BIT(m, this, next) \ | |
126 | this##_1: \ | |
127 | writel(SCLK, PDDATS); \ | |
128 | if (tv & (m)) { \ | |
129 | writel(SCLK, PDDATC); \ | |
130 | goto next##_1; \ | |
131 | } else { \ | |
132 | writel(MOSI | SCLK, PDDATC); \ | |
133 | goto next##_0; \ | |
134 | } \ | |
135 | this##_0: \ | |
136 | writel(SCLK, PDDATS); \ | |
137 | writel(SCLK, PDDATC); \ | |
138 | if (tv & (m)) { \ | |
139 | writel(MOSI, PDDATS); \ | |
140 | goto next##_1; \ | |
141 | } else { \ | |
142 | goto next##_0; \ | |
143 | } | |
144 | ||
145 | DO_BIT(0x40, b6, b5); | |
146 | DO_BIT(0x20, b5, b4); | |
147 | DO_BIT(0x10, b4, b3); | |
148 | DO_BIT(0x08, b3, b2); | |
149 | DO_BIT(0x04, b2, b1); | |
150 | DO_BIT(0x02, b1, b0); | |
151 | DO_BIT(0x01, b0, done); | |
152 | ||
153 | #undef DO_BIT | |
154 | ||
155 | done_1: | |
156 | done_0: | |
157 | writel(SCLK, PDDATS); | |
158 | writel(SCLK, PDDATC); | |
159 | writel(SCLK, PDDATC); /* delay to meet t5 timing */ | |
160 | } | |
161 | } | |
162 | ||
163 | ||
164 | static void bidir(const struct atben_prv *prv, const uint8_t *tx, uint8_t *rx, | |
165 | int len) | |
166 | { | |
167 | uint8_t mask, tv, rv; | |
168 | ||
169 | while (len--) { | |
170 | tv = *tx++; | |
171 | for (mask = 0x80; mask; mask >>= 1) { | |
172 | if (tv & mask) | |
173 | writel(MOSI, PDDATS); | |
174 | else | |
175 | writel(MOSI, PDDATC); | |
176 | writel(SCLK, PDDATS); | |
177 | if (readl(PDPIN) & MISO) | |
178 | rv |= mask; | |
179 | writel(SCLK, PDDATC); | |
180 | } | |
181 | *rx++ = rv; | |
182 | } | |
183 | } | |
184 | ||
185 | ||
186 | static int atben_transfer(struct spi_device *spi, struct spi_message *msg) | |
187 | { | |
188 | struct atben_prv *prv = spi_master_get_devdata(spi->master); | |
189 | struct spi_transfer *xfer; | |
190 | const uint8_t *tx; | |
191 | uint8_t *rx; | |
192 | ||
193 | if (unlikely(list_empty(&msg->transfers))) { | |
194 | dev_err(prv->dev, "transfer is empty\n"); | |
195 | return -EINVAL; | |
196 | } | |
197 | ||
198 | msg->actual_length = 0; | |
199 | ||
200 | writel(nSEL, PDDATC); | |
201 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | |
202 | tx = xfer->tx_buf; | |
203 | rx = xfer->rx_buf; | |
204 | msg->actual_length += xfer->len; | |
205 | ||
206 | if (!tx) | |
207 | rx_only(prv, rx, xfer->len); | |
208 | else if (!rx) | |
209 | tx_only(prv, tx, xfer->len); | |
210 | else | |
211 | bidir(prv, tx, rx, xfer->len); | |
212 | } | |
213 | writel(nSEL, PDDATS); | |
214 | ||
215 | msg->status = 0; | |
216 | msg->complete(msg->context); | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
221 | static int atben_setup(struct spi_device *spi) | |
222 | { | |
223 | return 0; | |
224 | } | |
225 | ||
226 | ||
227 | /* ----- SPI master creation/removal --------------------------------------- */ | |
228 | ||
229 | ||
230 | const static struct at86rf230_platform_data at86rf230_platform_data = { | |
231 | .rstn = -1, | |
232 | .slp_tr = JZ_GPIO_PORTD(9), | |
233 | .dig2 = -1, | |
234 | .reset = atben_reset, | |
235 | /* set .reset_data later */ | |
236 | }; | |
237 | ||
238 | static int __devinit atben_probe(struct platform_device *pdev) | |
239 | { | |
240 | struct spi_board_info board_info = { | |
241 | .modalias = "at86rf230", | |
242 | /* set .irq later */ | |
243 | .chip_select = 0, | |
244 | .bus_num = -1, | |
245 | .max_speed_hz = 8 * 1000 * 1000, | |
246 | }; | |
247 | ||
248 | struct spi_master *master; | |
249 | struct atben_prv *prv; | |
250 | struct resource *regs; | |
251 | struct spi_device *spi; | |
252 | int err = -ENXIO; | |
253 | ||
254 | master = spi_alloc_master(&pdev->dev, sizeof(*prv)); | |
255 | if (!master) | |
256 | return -ENOMEM; | |
257 | ||
258 | prv = spi_master_get_devdata(master); | |
259 | prv->dev = &pdev->dev; | |
260 | platform_set_drvdata(pdev, spi_master_get(master)); | |
261 | ||
262 | master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; | |
263 | master->bus_num = pdev->id; | |
264 | master->num_chipselect = 1; | |
265 | master->setup = atben_setup; | |
266 | master->transfer = atben_transfer; | |
267 | ||
268 | dev_dbg(prv->dev, "Setting up ATBEN SPI\n"); | |
269 | ||
270 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
271 | if (!regs) { | |
272 | dev_err(prv->dev, "no IORESOURCE_MEM\n"); | |
273 | err = -ENOENT; | |
274 | goto out_master; | |
275 | } | |
276 | prv->ioarea = request_mem_region(regs->start, resource_size(regs), | |
277 | pdev->name); | |
278 | if (!prv->ioarea) { | |
279 | dev_err(prv->dev, "can't request ioarea\n"); | |
280 | goto out_master; | |
281 | } | |
282 | ||
283 | prv->regs = ioremap(regs->start, resource_size(regs)); | |
284 | if (!prv->regs) { | |
285 | dev_err(prv->dev, "can't ioremap\n"); | |
286 | goto out_ioarea; | |
287 | } | |
288 | ||
289 | board_info.irq = platform_get_irq(pdev, 0); | |
290 | if (board_info.irq < 0) { | |
291 | dev_err(prv->dev, "can't get GPIO irq\n"); | |
292 | err = -ENOENT; | |
293 | goto out_regs; | |
294 | } | |
295 | ||
296 | err = spi_register_master(master); | |
297 | if (err) { | |
298 | dev_err(prv->dev, "can't register master\n"); | |
299 | goto out_regs; | |
300 | } | |
301 | ||
302 | prv->platform_data = at86rf230_platform_data; | |
303 | prv->platform_data.reset_data = prv; | |
304 | board_info.platform_data = &prv->platform_data; | |
305 | ||
306 | spi = spi_new_device(master, &board_info); | |
307 | if (!spi) { | |
308 | dev_err(&pdev->dev, "can't create new device for %s\n", | |
309 | board_info.modalias); | |
310 | err = -ENXIO; | |
311 | goto out_registered; | |
312 | } | |
313 | ||
314 | dev_info(&spi->dev, "ATBEN ready for mischief (IRQ %d)\n", | |
315 | board_info.irq); | |
316 | ||
317 | return 0; | |
318 | ||
319 | out_registered: | |
320 | spi_unregister_master(master); | |
321 | ||
322 | out_regs: | |
323 | iounmap(prv->regs); | |
324 | ||
325 | out_ioarea: | |
326 | release_resource(prv->ioarea); | |
327 | kfree(prv->ioarea); | |
328 | ||
329 | out_master: | |
330 | platform_set_drvdata(pdev, NULL); | |
331 | spi_master_put(master); | |
332 | ||
333 | return err; | |
334 | } | |
335 | ||
336 | static int __devexit atben_remove(struct platform_device *pdev) | |
337 | { | |
338 | struct spi_master *master = platform_get_drvdata(pdev); | |
339 | struct atben_prv *prv = spi_master_get_devdata(master); | |
340 | ||
341 | // restore GPIOs | |
342 | ||
343 | spi_unregister_master(master); | |
344 | ||
345 | iounmap(prv->regs); | |
346 | ||
347 | release_resource(prv->ioarea); | |
348 | kfree(prv->ioarea); | |
349 | ||
350 | platform_set_drvdata(pdev, NULL); | |
351 | spi_master_put(master); | |
352 | ||
353 | return 0; | |
354 | } | |
355 | ||
356 | static struct platform_driver atben_driver = { | |
357 | .driver = { | |
358 | .name = "spi_atben", | |
359 | .owner = THIS_MODULE, | |
360 | }, | |
361 | .remove = __devexit_p(atben_remove), | |
362 | }; | |
363 | ||
364 | static struct resource atben_resources[] = { | |
365 | { | |
366 | .start = JZ4740_GPIO_BASE_ADDR+0x300, | |
367 | .end = JZ4740_GPIO_BASE_ADDR+0x3ff, | |
368 | .flags = IORESOURCE_MEM, | |
369 | }, | |
370 | { | |
371 | /* set start and end later */ | |
372 | .flags = IORESOURCE_IRQ, | |
373 | }, | |
374 | }; | |
375 | ||
376 | static struct platform_device atben_device = { | |
377 | .name = "spi_atben", | |
378 | .id = -1, | |
379 | .num_resources = ARRAY_SIZE(atben_resources), | |
380 | .resource = atben_resources, | |
381 | }; | |
382 | ||
383 | /* | |
384 | * Registering the platform device just to probe it immediately afterwards | |
385 | * seems a little circuitous. Need to see if there's a better way. | |
386 | * | |
387 | * What we actually should do is this: | |
388 | * - in module init, register the device | |
389 | * - maybe probe as well, but keep the device also if the probe fails | |
390 | * (due to a conflicting driver already occupying the 8:10 slot) | |
391 | * - have a means for user space to kick off driver probing, e.g., when | |
392 | * anything about the 8:10 slot changes | |
393 | */ | |
394 | ||
395 | static int __init atben_init(void) | |
396 | { | |
397 | int err; | |
398 | ||
399 | err = platform_device_register(&atben_device); | |
400 | if (err) | |
401 | return err; | |
402 | ||
403 | atben_resources[1].start = atben_resources[1].end = | |
404 | gpio_to_irq(JZ_GPIO_PORTD(12)); | |
405 | ||
406 | return platform_driver_probe(&atben_driver, atben_probe); | |
407 | } | |
408 | ||
409 | static void __exit atben_exit(void) | |
410 | { | |
411 | platform_driver_unregister(&atben_driver); | |
412 | platform_device_unregister(&atben_device); | |
413 | } | |
414 | ||
415 | module_init(atben_init); | |
416 | module_exit(atben_exit); | |
417 | ||
418 | ||
419 | MODULE_DESCRIPTION("ATBEN SPI Controller Driver"); | |
420 | MODULE_AUTHOR("Werner Almesberger <werner@almesberger.net>"); | |
421 | MODULE_LICENSE("GPL"); |
drivers/ieee802154/spi_atusb.c | ||
---|---|---|
1 | /* | |
2 | * atusb - SPI host look-alike for ATSUB | |
3 | * | |
4 | * Copyright (c) 2011 Richard Sharpe <realrichardsharpe@gmail.com> | |
5 | * Copyright (c) 2011 Stefan Schmidt <stefan@datenfreihafen.org> | |
6 | * Copyright (c) 2011 Werner Almesberger <werner@almesberger.net> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License as | |
10 | * published by the Free Software Foundation, version 2 | |
11 | * | |
12 | */ | |
13 | ||
14 | /* | |
15 | * - implement more robust interrupt synchronization | |
16 | * - check URB killing in atusb_disconnect for races | |
17 | * - switch from bulk to interrupt endpoint | |
18 | * - implement buffer read without extra copy | |
19 | * - harmonize indentation style | |
20 | * - mv atusb.c ../ieee802.15.4/spi_atusb.c, or maybe atrf_atusb.c or such | |
21 | * - check module load/unload | |
22 | * - review dev_* severity levels | |
23 | */ | |
24 | ||
25 | #include <linux/kernel.h> | |
26 | #include <linux/module.h> | |
27 | #include <linux/platform_device.h> | |
28 | #include <linux/jiffies.h> | |
29 | #include <linux/timer.h> | |
30 | #include <linux/interrupt.h> | |
31 | #include <linux/usb.h> | |
32 | #include <linux/spi/spi.h> | |
33 | #include <linux/spi/at86rf230.h> | |
34 | ||
35 | #include "../ieee802154/at86rf230.h" /* dirty */ | |
36 | ||
37 | ||
38 | #define SYNC_TIMEOUT_MS 50 /* assume interrupt has been synced after | |
39 | waiting this long */ | |
40 | ||
41 | #define VENDOR_ID 0x20b7 | |
42 | #define PRODUCT_ID 0x1540 | |
43 | ||
44 | /* The devices we work with */ | |
45 | static const struct usb_device_id atusb_device_table[] = { | |
46 | { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, | |
47 | { }, | |
48 | }; | |
49 | MODULE_DEVICE_TABLE(usb, atusb_device_table); | |
50 | ||
51 | #define ATUSB_BUILD_SIZE 256 | |
52 | struct atusb_local { | |
53 | struct usb_device * udev; | |
54 | /* The interface to the RF part info, if applicable */ | |
55 | uint8_t ep0_atusb_major; | |
56 | uint8_t ep0_atusb_minor; | |
57 | uint8_t atusb_hw_type; | |
58 | struct spi_master *master; | |
59 | int slave_irq; | |
60 | struct urb *irq_urb; | |
61 | uint8_t irq_buf; /* receive irq serial here*/ | |
62 | uint8_t irq_seen; /* last irq serial from bulk */ | |
63 | uint8_t irq_sync; /* last irq serial from WRITE2_SYNC */ | |
64 | struct tasklet_struct task; /* interrupt delivery tasklet */ | |
65 | struct timer_list timer; /* delay, for interrupt synch */ | |
66 | struct at86rf230_platform_data platform_data; | |
67 | /* copy platform_data so that we can adapt .reset_data */ | |
68 | struct spi_device *spi; | |
69 | // unsigned char buffer[3]; | |
70 | unsigned char buffer[260]; /* XXL, just in case */ | |
71 | struct spi_message *msg; | |
72 | }; | |
73 | ||
74 | /* Commands to our device. Make sure this is synced with the firmware */ | |
75 | enum atspi_requests { | |
76 | ATUSB_ID = 0x00, /* system status/control grp */ | |
77 | ATUSB_BUILD, | |
78 | ATUSB_RESET, | |
79 | ATUSB_RF_RESET = 0x10, /* debug/test group */ | |
80 | ATUSB_POLL_INT, | |
81 | ATUSB_TEST, /* atusb-sil only */ | |
82 | ATUSB_TIMER, | |
83 | ATUSB_GPIO, | |
84 | ATUSB_SLP_TR, | |
85 | ATUSB_GPIO_CLEANUP, | |
86 | ATUSB_REG_WRITE = 0x20, /* transceiver group */ | |
87 | ATUSB_REG_READ, | |
88 | ATUSB_BUF_WRITE, | |
89 | ATUSB_BUF_READ, | |
90 | ATUSB_SRAM_WRITE, | |
91 | ATUSB_SRAM_READ, | |
92 | ATUSB_SPI_WRITE = 0x30, /* SPI group */ | |
93 | ATUSB_SPI_READ1, | |
94 | ATUSB_SPI_READ2, | |
95 | ATUSB_SPI_WRITE2_SYNC, | |
96 | }; | |
97 | ||
98 | /* | |
99 | * Direction bRequest wValue wIndex wLength | |
100 | * | |
101 | * ->host ATUSB_ID - - 3 | |
102 | * ->host ATUSB_BUILD - - #bytes | |
103 | * host-> ATUSB_RESET - - 0 | |
104 | * | |
105 | * host-> ATUSB_RF_RESET - - 0 | |
106 | * ->host ATUSB_POLL_INT - - 1 | |
107 | * host-> ATUSB_TEST - - 0 | |
108 | * ->host ATUSB_TIMER - - #bytes (6) | |
109 | * ->host ATUSB_GPIO dir+data mask+p# 3 | |
110 | * host-> ATUSB_SLP_TR - - 0 | |
111 | * host-> ATUSB_GPIO_CLEANUP - - 0 | |
112 | * | |
113 | * host-> ATUSB_REG_WRITE value addr 0 | |
114 | * ->host ATUSB_REG_READ - addr 1 | |
115 | * host-> ATUSB_BUF_WRITE - - #bytes | |
116 | * ->host ATUSB_BUF_READ - - #bytes | |
117 | * host-> ATUSB_SRAM_WRITE - addr #bytes | |
118 | * ->host ATUSB_SRAM_READ - addr #bytes | |
119 | * | |
120 | * host-> ATUSB_SPI_WRITE byte0 byte1 #bytes | |
121 | * ->host ATUSB_SPI_READ1 byte0 - #bytes | |
122 | * ->host ATUSB_SPI_READ2 byte0 byte1 #bytes | |
123 | * ->host ATUSB_SPI_WRITE2_SYNC byte0 byte1 0/1 | |
124 | */ | |
125 | ||
126 | #define ATUSB_FROM_DEV (USB_TYPE_VENDOR | USB_DIR_IN) | |
127 | #define ATUSB_TO_DEV (USB_TYPE_VENDOR | USB_DIR_OUT) | |
128 | ||
129 | ||
130 | /* ----- Control transfers ------------------------------------------------- */ | |
131 | ||
132 | ||
133 | static int atusb_async_errchk(struct urb *urb) | |
134 | { | |
135 | struct atusb_local *atusb = urb->context; | |
136 | struct spi_message *msg = atusb->msg; | |
137 | struct usb_device *dev = atusb->udev; | |
138 | ||
139 | if (!urb->status) { | |
140 | dev_dbg(&dev->dev, "atusb_async_errchk OK len %d\n", | |
141 | urb->actual_length); | |
142 | return 0; | |
143 | } | |
144 | ||
145 | if (urb->status != -ENOENT && urb->status != -ECONNRESET && | |
146 | urb->status != -ESHUTDOWN) | |
147 | dev_info(&dev->dev, "atusb_async_errchk FAIL error %d\n", | |
148 | urb->status); | |
149 | ||
150 | msg->actual_length = 0; | |
151 | ||
152 | return urb->status; | |
153 | } | |
154 | ||
155 | static void atusb_async_finish(struct urb *urb) | |
156 | { | |
157 | struct atusb_local *atusb = urb->context; | |
158 | struct spi_message *msg = atusb->msg; | |
159 | ||
160 | msg->status = urb->status; | |
161 | msg->complete(msg->context); | |
162 | ||
163 | kfree(urb->setup_packet); | |
164 | usb_free_urb(urb); | |
165 | } | |
166 | ||
167 | static void atusb_ctrl_cb(struct urb *urb) | |
168 | { | |
169 | atusb_async_errchk(urb); | |
170 | atusb_async_finish(urb); | |
171 | } | |
172 | ||
173 | static void atusb_timer(unsigned long data) | |
174 | { | |
175 | struct urb *urb = (void *) data; | |
176 | ||
177 | dev_warn(&urb->dev->dev, "atusb_timer\n"); | |
178 | atusb_async_finish(urb); | |
179 | } | |
180 | ||
181 | static void atusb_ctrl_cb_sync(struct urb *urb) | |
182 | { | |
183 | struct atusb_local *atusb = urb->context; | |
184 | ||
185 | /* @@@ needs locking/atomic */ | |
186 | if (atusb_async_errchk(urb) || atusb->irq_sync == atusb->irq_seen) { | |
187 | atusb_async_finish(urb); | |
188 | return; | |
189 | } | |
190 | ||
191 | BUG_ON(timer_pending(&atusb->timer)); | |
192 | atusb->timer.expires = jiffies+msecs_to_jiffies(SYNC_TIMEOUT_MS); | |
193 | atusb->timer.data = (unsigned long) urb; | |
194 | add_timer(&atusb->timer); | |
195 | } | |
196 | ||
197 | static void atusb_read_fb_cb(struct urb *urb) | |
198 | { | |
199 | struct atusb_local *atusb = urb->context; | |
200 | struct spi_message *msg = atusb->msg; | |
201 | const struct spi_transfer *xfer; | |
202 | uint8_t *rx; | |
203 | ||
204 | if (!atusb_async_errchk(urb)) { | |
205 | BUG_ON(!urb->actual_length); | |
206 | ||
207 | xfer = list_first_entry(&msg->transfers, struct spi_transfer, | |
208 | transfer_list); | |
209 | rx = xfer->rx_buf; | |
210 | rx[1] = atusb->buffer[0]; | |
211 | ||
212 | xfer = list_entry(xfer->transfer_list.next, | |
213 | struct spi_transfer, transfer_list); | |
214 | memcpy(xfer->rx_buf, atusb->buffer+1, urb->actual_length-1); | |
215 | } | |
216 | ||
217 | atusb_async_finish(urb); | |
218 | } | |
219 | ||
220 | static int submit_control_msg(struct atusb_local *atusb, | |
221 | __u8 request, __u8 requesttype, __u16 value, __u16 index, | |
222 | void *data, __u16 size, usb_complete_t complete_fn, void *context) | |
223 | { | |
224 | struct usb_device *dev = atusb->udev; | |
225 | struct usb_ctrlrequest *req; | |
226 | struct urb *urb; | |
227 | int retval = -ENOMEM; | |
228 | ||
229 | req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); | |
230 | if (!req) | |
231 | return -ENOMEM; | |
232 | ||
233 | req->bRequest = request; | |
234 | req->bRequestType = requesttype; | |
235 | req->wValue = cpu_to_le16(value); | |
236 | req->wIndex = cpu_to_le16(index); | |
237 | req->wLength = cpu_to_le16(size); | |
238 | ||
239 | urb = usb_alloc_urb(0, GFP_KERNEL); | |
240 | if (!urb) | |
241 | goto out_nourb; | |
242 | ||
243 | usb_fill_control_urb(urb, dev, | |
244 | requesttype == ATUSB_FROM_DEV ? | |
245 | usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0), | |
246 | (unsigned char *) req, data, size, complete_fn, context); | |
247 | ||
248 | retval = usb_submit_urb(urb, GFP_KERNEL); | |
249 | if (!retval) | |
250 | return 0; | |
251 | dev_warn(&dev->dev, "failed submitting read urb, error %d", | |
252 | retval); | |
253 | retval = retval == -ENOMEM ? retval : -EIO; | |
254 | ||
255 | usb_free_urb(urb); | |
256 | out_nourb: | |
257 | kfree(req); | |
258 | ||
259 | return retval; | |
260 | } | |
261 | ||
262 | ||
263 | /* ----- SPI transfers ----------------------------------------------------- */ | |
264 | ||
265 | ||
266 | static int atusb_read1(struct atusb_local *atusb, | |
267 | uint8_t tx, uint8_t *rx, int len) | |
268 | { | |
269 | dev_dbg(&atusb->udev->dev, "atusb_read1: tx = 0x%x\n", tx); | |
270 | return submit_control_msg(atusb, | |
271 | ATUSB_SPI_READ1, ATUSB_FROM_DEV, tx, 0, | |
272 | rx, 1, atusb_ctrl_cb, atusb); | |
273 | } | |
274 | ||
275 | static int atusb_read_fb(struct atusb_local *atusb, | |
276 | uint8_t tx, uint8_t *rx0, uint8_t *rx, int len) | |
277 | { | |
278 | dev_dbg(&atusb->udev->dev, "atusb_read_fb: tx = 0x%x\n", tx); | |
279 | return submit_control_msg(atusb, | |
280 | ATUSB_SPI_READ1, ATUSB_FROM_DEV, tx, 0, | |
281 | atusb->buffer, len+1, atusb_read_fb_cb, atusb); | |
282 | } | |
283 | ||
284 | static int atusb_write(struct atusb_local *atusb, | |
285 | uint8_t tx0, uint8_t tx1, const uint8_t *tx, int len) | |
286 | { | |
287 | dev_dbg(&atusb->udev->dev, | |
288 | "atusb_write: tx0 = 0x%x tx1 = 0x%x\n", tx0, tx1); | |
289 | ||
290 | /* | |
291 | * The AT86RF230 driver sometimes requires a transceiver state | |
292 | * transition to be an interrupt barrier. This is the case after | |
293 | * writing FORCE_TX_ON to the TRX_CMD field in the TRX_STATE register. | |
294 | * | |
295 | * Since there is no other means of notification, we just decode the | |
296 | * transfer and do a bit of pattern matching. | |
297 | */ | |
298 | if (tx0 == (CMD_REG | CMD_WRITE | RG_TRX_STATE) && | |
299 | (tx1 & 0x1f) == STATE_FORCE_TX_ON) | |
300 | return submit_control_msg(atusb, | |
301 | ATUSB_SPI_WRITE2_SYNC, ATUSB_FROM_DEV, tx0, tx1, | |
302 | &atusb->irq_sync, 1, atusb_ctrl_cb_sync, atusb); | |
303 | else | |
304 | return submit_control_msg(atusb, | |
305 | ATUSB_SPI_WRITE, ATUSB_TO_DEV, tx0, tx1, | |
306 | (uint8_t *) tx, len, atusb_ctrl_cb, atusb); | |
307 | } | |
308 | ||
309 | static int atusb_transfer(struct spi_device *spi, struct spi_message *msg) | |
310 | { | |
311 | struct atusb_local *atusb = spi_master_get_devdata(spi->master); | |
312 | struct spi_transfer *xfer; | |
313 | struct spi_transfer *x[2]; | |
314 | int n; | |
315 | const uint8_t *tx; | |
316 | uint8_t *rx; | |
317 | int len; | |
318 | int retval = 0; | |
319 | ||
320 | if (unlikely(list_empty(&msg->transfers))) { | |
321 | dev_err(&atusb->udev->dev, "transfer is empty\n"); | |
322 | return -EINVAL; | |
323 | } | |
324 | ||
325 | atusb->msg = msg; | |
326 | ||
327 | /* Classify the request */ | |
328 | n = 0; | |
329 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | |
330 | if (n == ARRAY_SIZE(x)) { | |
331 | dev_err(&atusb->udev->dev, "too many transfers\n"); | |
332 | return -EINVAL; | |
333 | } | |
334 | x[n] = xfer; | |
335 | n++; | |
336 | } | |
337 | ||
338 | tx = x[0]->tx_buf; | |
339 | rx = x[0]->rx_buf; | |
340 | len = x[0]->len; | |
341 | ||
342 | msg->actual_length = len; | |
343 | ||
344 | if (!tx || len != 2) | |
345 | goto bad_req; | |
346 | if (n == 1) { | |
347 | if (rx) { | |
348 | dev_dbg(&atusb->udev->dev, "read 1\n"); | |
349 | retval = atusb_read1(atusb, tx[0], rx+1, len-1); | |
350 | } else { | |
351 | dev_dbg(&atusb->udev->dev, "write 2\n"); | |
352 | /* | |
353 | * Don't take our clock away !! ;-) | |
354 | */ | |
355 | if (tx[0] == (CMD_REG | CMD_WRITE | RG_TRX_CTRL_0)) { | |
356 | msg->status = 0; | |
357 | msg->complete(msg->context); | |
358 | } else { | |
359 | retval = atusb_write(atusb, | |
360 | tx[0], tx[1], NULL, 0); | |
361 | } | |
362 | } | |
363 | } else { | |
364 | if (x[0]->rx_buf) { | |
365 | if (x[1]->tx_buf || !x[1]->rx_buf) | |
366 | goto bad_req; | |
367 | dev_dbg(&atusb->udev->dev, "read 1+\n"); | |
368 | retval = atusb_read_fb(atusb, tx[0], rx+1, | |
369 | x[1]->rx_buf, x[1]->len); | |
370 | } else { | |
371 | if (!x[1]->tx_buf ||x[1]->rx_buf) | |
372 | goto bad_req; | |
373 | dev_dbg(&atusb->udev->dev, "write 2+n\n"); | |
374 | retval = atusb_write(atusb, tx[0], tx[1], | |
375 | x[1]->tx_buf, x[1]->len); | |
376 | } | |
377 | } | |
378 | return retval; | |
379 | ||
380 | bad_req: | |
381 | dev_err(&atusb->udev->dev, "unrecognized request:\n"); | |
382 | list_for_each_entry(xfer, &msg->transfers, transfer_list) | |
383 | dev_err(&atusb->udev->dev, "%stx %srx len %u\n", | |
384 | xfer->tx_buf ? "" : "!", xfer->rx_buf ? " " : "!", | |
385 | xfer->len); | |
386 | return -EINVAL; | |
387 | } | |
388 | ||
389 | static int atusb_setup(struct spi_device *spi) | |
390 | { | |
391 | return 0; | |
392 | } | |
393 | ||
394 | ||
395 | /* ----- Interrupt handling ------------------------------------------------ */ | |
396 | ||
397 | ||
398 | static void atusb_tasklet(unsigned long data) | |
399 | { | |
400 | struct atusb_local *atusb = (void *) data; | |
401 | ||
402 | generic_handle_irq(atusb->slave_irq); | |
403 | } | |
404 | ||
405 | static void atusb_irq(struct urb *urb) | |
406 | { | |
407 | struct atusb_local *atusb = urb->context; | |
408 | ||
409 | dev_dbg(&urb->dev->dev, "atusb_irq (%d), seen %d sync %d\n", | |
410 | urb->status, atusb->irq_buf, atusb->irq_sync); | |
411 | if (!urb->status) { | |
412 | atusb->irq_seen = atusb->irq_buf; | |
413 | if (atusb->irq_sync == atusb->irq_seen && | |
414 | try_to_del_timer_sync(&atusb->timer) == 1) | |
415 | atusb_async_finish((struct urb *) atusb->timer.data); | |
416 | } | |
417 | usb_free_urb(urb); | |
418 | atusb->irq_urb = NULL; | |
419 | tasklet_schedule(&atusb->task); | |
420 | } | |
421 | ||
422 | static int atusb_arm_interrupt(struct atusb_local *atusb) | |
423 | { | |
424 | struct usb_device *dev = atusb->udev; | |
425 | struct urb *urb; | |
426 | int retval = -ENOMEM; | |
427 | ||
428 | BUG_ON(atusb->irq_urb); | |
429 | ||
430 | dev_vdbg(&dev->dev, "atusb_arm_interrupt\n"); | |
431 | urb = usb_alloc_urb(0, GFP_KERNEL); | |
432 | if (!urb) { | |
433 | dev_err(&dev->dev, | |
434 | "atusb_arm_interrupt: usb_alloc_urb failed\n"); | |
435 | return -ENOMEM; | |
436 | } | |
437 | ||
438 | usb_fill_bulk_urb(urb, dev, usb_rcvbulkpipe(dev, 1), | |
439 | &atusb->irq_buf, 1, atusb_irq, atusb); | |
440 | atusb->irq_urb = urb; | |
441 | retval = usb_submit_urb(urb, GFP_KERNEL); | |
442 | if (!retval) | |
443 | return 0; | |
444 | ||
445 | dev_err(&dev->dev, "failed submitting bulk urb, error %d\n", retval); | |
446 | retval = retval == -ENOMEM ? retval : -EIO; | |
447 | ||
448 | usb_free_urb(urb); | |
449 | ||
450 | return retval; | |
451 | } | |
452 | ||
453 | static void atusb_irq_mask(struct irq_data *data) | |
454 | { | |
455 | struct atusb_local *atusb = irq_data_get_irq_chip_data(data); | |
456 | ||
457 | dev_vdbg(&atusb->udev->dev, "atusb_irq_mask\n"); | |
458 | tasklet_disable_nosync(&atusb->task); | |
459 | } | |
460 | ||
461 | static void atusb_irq_unmask(struct irq_data *data) | |
462 | { | |
463 | struct atusb_local *atusb = irq_data_get_irq_chip_data(data); | |
464 | ||
465 | dev_vdbg(&atusb->udev->dev, "atusb_irq_unmask\n"); | |
466 | tasklet_enable(&atusb->task); | |
467 | } | |
468 | ||
469 | static void atusb_irq_ack(struct irq_data *data) | |
470 | { | |
471 | struct atusb_local *atusb = irq_data_get_irq_chip_data(data); | |
472 | ||
473 | dev_vdbg(&atusb->udev->dev, "atusb_irq_ack\n"); | |
474 | atusb_arm_interrupt(atusb); | |
475 | } | |
476 | ||
477 | static struct irq_chip atusb_irq_chip = { | |
478 | .name = "atusb-slave", | |
479 | .irq_mask = atusb_irq_mask, | |
480 | .irq_unmask = atusb_irq_unmask, | |
481 | .irq_ack = atusb_irq_ack, | |
482 | }; | |
483 | ||
484 | ||
485 | /* ----- Transceiver reset ------------------------------------------------- */ | |
486 | ||
487 | ||
488 | static void atusb_reset(void *reset_data) | |
489 | { | |
490 | int retval; | |
491 | struct atusb_local *atusb = reset_data; | |
492 | ||
493 | retval = usb_control_msg(atusb->udev, | |
494 | usb_rcvctrlpipe(atusb->udev, 0), | |
495 | ATUSB_RF_RESET, ATUSB_TO_DEV, 0, 0, | |
496 | NULL, 0, 1000); | |
497 | if (retval < 0) { | |
498 | dev_err(&atusb->udev->dev, | |
499 | "%s: error doing reset retval = %d\n", | |
500 | __func__, retval); | |
501 | } | |
502 | } | |
503 | ||
504 | ||
505 | /* ----- Firmware version information -------------------------------------- */ | |
506 | ||
507 | ||
508 | static int atusb_get_and_show_revision(struct atusb_local *atusb) | |
509 | { | |
510 | struct usb_device *dev = atusb->udev; | |
511 | int retval; | |
512 | ||
513 | /* Get a couple of the ATMega Firmware values */ | |
514 | retval = usb_control_msg(dev, | |
515 | usb_rcvctrlpipe(dev, 0), | |
516 | ATUSB_ID, ATUSB_FROM_DEV, 0, 0, | |
517 | atusb->buffer, 3, 1000); | |
518 | if (retval < 0) { | |
519 | dev_info(&dev->dev, | |
520 | "failed submitting urb for ATUSB_ID, error %d\n", retval); | |
521 | return retval == -ENOMEM ? retval : -EIO; | |
522 | } | |
523 | ||
524 | atusb->ep0_atusb_major = atusb->buffer[0]; | |
525 | atusb->ep0_atusb_minor = atusb->buffer[1]; | |
526 | atusb->atusb_hw_type = atusb->buffer[2]; | |
527 | dev_info(&dev->dev, | |
528 | "Firmware: major: %u, minor: %u, hardware type: %u\n", | |
529 | atusb->ep0_atusb_major, atusb->ep0_atusb_minor, | |
530 | atusb->atusb_hw_type); | |
531 | ||
532 | return 0; | |
533 | } | |
534 | ||
535 | static int atusb_get_and_show_build(struct atusb_local *atusb) | |
536 | { | |
537 | struct usb_device *dev = atusb->udev; | |
538 | char build[ATUSB_BUILD_SIZE+1]; | |
539 | int retval; | |
540 | ||
541 | retval = usb_control_msg(dev, | |
542 | usb_rcvctrlpipe(atusb->udev, 0), | |
543 | ATUSB_BUILD, ATUSB_FROM_DEV, 0, 0, | |
544 | build, ATUSB_BUILD_SIZE, 1000); | |
545 | if (retval < 0) { | |
546 | dev_err(&dev->dev, | |
547 | "failed submitting urb for ATUSB_BUILD, error %d\n", | |
548 | retval); | |
549 | return retval == -ENOMEM ? retval : -EIO; | |
550 | } | |
551 | ||
552 | build[retval] = 0; | |
553 | dev_info(&dev->dev, "Firmware: build %s\n", build); | |
554 | ||
555 | return 0; | |
556 | } | |
557 | ||
558 | ||
559 | /* ----- Setup ------------------------------------------------------------- */ | |
560 | ||
561 | ||
562 | struct at86rf230_platform_data at86rf230_platform_data = { | |
563 | .rstn = -1, | |
564 | .slp_tr = -1, | |
565 | .dig2 = -1, | |
566 | .reset = atusb_reset, | |
567 | /* set .reset_data later */ | |
568 | }; | |
569 | ||
570 | static int atusb_probe(struct usb_interface *interface, | |
571 | const struct usb_device_id *id) | |
572 | { | |
573 | struct spi_board_info board_info = { | |
574 | .modalias = "at86rf230", | |
575 | /* set .irq later */ | |
576 | .chip_select = 0, | |
577 | .bus_num = -1, | |
578 | .max_speed_hz = 8 * 1000 * 1000, | |
579 | }; | |
580 | ||
581 | struct usb_device *udev = interface_to_usbdev(interface); | |
582 | struct atusb_local *atusb = NULL; | |
583 | struct spi_master *master; | |
584 | int retval; | |
585 | ||
586 | /* | |
587 | * Ignore all interfaces used for DFU, i.e., everything while in the | |
588 | * boot loader, and interface #1 when in the application. | |
589 | */ | |
590 | if (interface->cur_altsetting->desc.bInterfaceClass != | |
591 | USB_CLASS_VENDOR_SPEC) { | |
592 | dev_dbg(&udev->dev, | |
593 | "Ignoring interface with class 0x%02x\n", | |
594 | interface->cur_altsetting->desc.bInterfaceClass); | |
595 | return -ENODEV; | |
596 | } | |
597 | ||
598 | master = spi_alloc_master(&udev->dev, sizeof(*atusb)); | |
599 | if (!master) | |
600 | return -ENOMEM; | |
601 | ||
602 | atusb = spi_master_get_devdata(master); | |
603 | ||
604 | atusb->udev = usb_get_dev(udev); | |
605 | usb_set_intfdata(interface, atusb); | |
606 | ||
607 | atusb->master = spi_master_get(master); | |
608 | ||
609 | master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; | |
610 | master->bus_num = -1; | |
611 | master->num_chipselect = 1; | |
612 | master->setup = atusb_setup; | |
613 | master->transfer = atusb_transfer; | |
614 | ||
615 | atusb->slave_irq = irq_alloc_desc(numa_node_id()); | |
616 | if (atusb->slave_irq < 0) { | |
617 | dev_err(&udev->dev, "can't allocate slave irq\n"); | |
618 | retval = -ENXIO; | |
619 | goto err_free; | |
620 | } | |
621 | ||
622 | set_irq_chip_data(atusb->slave_irq, atusb); | |
623 | set_irq_chip_and_handler(atusb->slave_irq, &atusb_irq_chip, | |
624 | handle_level_irq); | |
625 | ||
626 | /* FIXME prepare USB IRQ */ | |
627 | ||
628 | retval = spi_register_master(master); | |
629 | if (retval < 0) { | |
630 | dev_err(&udev->dev, "can't register spi master\n"); | |
631 | goto err_slave_irq; | |
632 | } | |
633 | ||
634 | atusb->platform_data = at86rf230_platform_data; | |
635 | atusb->platform_data.reset_data = atusb; | |
636 | board_info.platform_data = &atusb->platform_data; | |
637 | board_info.irq = atusb->slave_irq; | |
638 | ||
639 | init_timer(&atusb->timer); | |
640 | atusb->timer.function = atusb_timer; | |
641 | ||
642 | tasklet_init(&atusb->task, atusb_tasklet, (unsigned long) atusb); | |
643 | tasklet_disable(&atusb->task); | |
644 | atusb_arm_interrupt(atusb); | |
645 | ||
646 | if (atusb_get_and_show_revision(atusb) < 0) | |
647 | goto err_master; | |
648 | if (atusb_get_and_show_build(atusb) < 0) | |
649 | goto err_master; | |
650 | ||
651 | atusb->spi = spi_new_device(master, &board_info); | |
652 | if (!atusb->spi) { | |
653 | dev_err(&udev->dev, "can't create new device for %s\n", | |
654 | board_info.modalias); | |
655 | goto err_master; | |
656 | } | |
657 | ||
658 | dev_info(&atusb->spi->dev, | |
659 | "ATUSB ready for mischief (IRQ %d)\n", board_info.irq); | |
660 | ||
661 | return 0; | |
662 | ||
663 | err_master: | |
664 | /* | |
665 | * If we come here from a partially successful driver initialization, | |
666 | * we don't really know how much it has done. In particular, it may | |
667 | * have triggered an interrupt and thus removed the interrupt URB and | |
668 | * maybe scheduled the tasklet. | |
669 | */ | |
670 | tasklet_disable(&atusb->task); | |
671 | if (atusb->irq_urb) | |
672 | usb_kill_urb(atusb->irq_urb); | |
673 | spi_master_put(atusb->master); | |
674 | err_slave_irq: | |
675 | set_irq_chained_handler(atusb->slave_irq, NULL); | |
676 | set_irq_chip_data(atusb->slave_irq, NULL); | |
677 | irq_free_desc(atusb->slave_irq); | |
678 | err_free: | |
679 | return retval; | |
680 | } | |
681 | ||
682 | static void atusb_disconnect(struct usb_interface *interface) | |
683 | { | |
684 | struct atusb_local *atusb = usb_get_intfdata(interface); | |
685 | struct spi_master *master = atusb->master; | |
686 | ||
687 | tasklet_disable(&atusb->task); | |
688 | /* @@@ this needs some extra protecion - wa */ | |
689 | if (atusb->irq_urb) | |
690 | usb_kill_urb(atusb->irq_urb); | |
691 | ||
692 | BUG_ON(timer_pending(&atusb->timer)); | |
693 | ||
694 | usb_set_intfdata(interface, NULL); | |
695 | usb_put_dev(atusb->udev); | |
696 | ||
697 | spi_dev_put(atusb->spi); | |
698 | ||
699 | spi_unregister_master(master); | |
700 | ||
701 | set_irq_chained_handler(atusb->slave_irq, NULL); | |
702 | set_irq_chip_data(atusb->slave_irq, NULL); | |
703 | irq_free_desc(atusb->slave_irq); | |
704 | ||
705 | spi_master_put(master); | |
706 | } | |
707 | ||
708 | void atusb_release(struct device *dev) | |
709 | { | |
710 | return; | |
711 | } | |
712 | ||
713 | static struct usb_driver atusb_driver = { | |
714 | .name = "atusb_ben-wpan", | |
715 | .probe = atusb_probe, | |
716 | .disconnect = atusb_disconnect, | |
717 | .id_table = atusb_device_table, | |
718 | }; | |
719 | ||
720 | static struct platform_device atusb_device = { | |
721 | .name = "spi_atusb", | |
722 | .id = -1, | |
723 | .dev.release = atusb_release, | |
724 | }; | |
725 | ||
726 | static int __init atusb_init(void) | |
727 | { | |
728 | int retval; | |
729 | ||
730 | retval = platform_device_register(&atusb_device); | |
731 | if (retval) | |
732 | return retval; | |
733 | ||
734 | return usb_register(&atusb_driver); | |
735 | } | |
736 | ||
737 | static void __exit atusb_exit(void) | |
738 | { | |
739 | usb_deregister(&atusb_driver); | |
740 | platform_device_unregister(&atusb_device); | |
741 | } | |
742 | ||
743 | module_init (atusb_init); | |
744 | module_exit (atusb_exit); | |
745 | ||
746 | MODULE_AUTHOR("Richard Sharpe <realrichardsharpe@gmail.com>"); | |
747 | MODULE_AUTHOR("Stefan Schmidt <stefan@datenfreihafen.org>"); | |
748 | MODULE_AUTHOR("Werner Almesberger <werner@almesberger.net>"); | |
749 | MODULE_DESCRIPTION("ATUSB ben-wpan Driver"); | |
750 | MODULE_LICENSE("GPL"); |
drivers/spi/Kconfig | ||
---|---|---|
382 | 382 | help |
383 | 383 | SPI driver for Nuvoton NUC900 series ARM SoCs |
384 | 384 | |
385 | config SPI_ATUSB | |
386 | tristate "ATUSB SPI interface" | |
387 | depends on USB && SPI | |
388 | help | |
389 | SPI-over-USB driver for the ATUSB IEEE 802.15.4 board. | |
390 | ||
391 | config SPI_ATBEN | |
392 | tristate "ATBEN 8:10 SPI interface" | |
393 | depends on JZ4740_QI_LB60 && EXPERIMENTAL | |
394 | help | |
395 | Bit-banging SPI driver for the 8:10 interface of the Ben NanoNote | |
396 | when equipped with an ATBEN board. | |
397 | ||
398 | 385 | # |
399 | 386 | # Add new SPI master controllers in alphabetical order above this line |
400 | 387 | # |
drivers/spi/Makefile | ||
---|---|---|
50 | 50 | obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o |
51 | 51 | obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o |
52 | 52 | obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o |
53 | obj-$(CONFIG_SPI_ATUSB) += atusb.o | |
54 | obj-$(CONFIG_SPI_ATBEN) += spi_atben.o | |
55 | 53 | |
56 | 54 | # special build for s3c24xx spi driver with fiq support |
57 | 55 | spi_s3c24xx_hw-y := spi_s3c24xx.o |
drivers/spi/atusb.c | ||
---|---|---|
1 | /* | |
2 | * atusb - SPI host look-alike for ATSUB | |
3 | * | |
4 | * Copyright (c) 2011 Richard Sharpe <realrichardsharpe@gmail.com> | |
5 | * Copyright (c) 2011 Stefan Schmidt <stefan@datenfreihafen.org> | |
6 | * Copyright (c) 2011 Werner Almesberger <werner@almesberger.net> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License as | |
10 | * published by the Free Software Foundation, version 2 | |
11 | * | |
12 | */ | |
13 | ||
14 | /* | |
15 | * - implement more robust interrupt synchronization | |
16 | * - check URB killing in atusb_disconnect for races | |
17 | * - switch from bulk to interrupt endpoint | |
18 | * - implement buffer read without extra copy | |
19 | * - harmonize indentation style | |
20 | * - mv atusb.c ../ieee802.15.4/spi_atusb.c, or maybe atrf_atusb.c or such | |
21 | * - check module load/unload | |
22 | * - review dev_* severity levels | |
23 | */ | |
24 | ||
25 | #include <linux/kernel.h> | |
26 | #include <linux/module.h> | |
27 | #include <linux/platform_device.h> | |
28 | #include <linux/jiffies.h> | |
29 | #include <linux/timer.h> | |
30 | #include <linux/interrupt.h> | |
31 | #include <linux/usb.h> | |
32 | #include <linux/spi/spi.h> | |
33 | #include <linux/spi/at86rf230.h> | |
34 | ||
35 | #include "../ieee802154/at86rf230.h" /* dirty */ | |
36 | ||
37 | ||
38 | #define SYNC_TIMEOUT_MS 50 /* assume interrupt has been synced after | |
39 | waiting this long */ | |
40 | ||
41 | #define VENDOR_ID 0x20b7 | |
42 | #define PRODUCT_ID 0x1540 | |
43 | ||
44 | /* The devices we work with */ | |
45 | static const struct usb_device_id atusb_device_table[] = { | |
46 | { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, | |
47 | { }, | |
48 | }; | |
49 | MODULE_DEVICE_TABLE(usb, atusb_device_table); | |
50 | ||
51 | #define ATUSB_BUILD_SIZE 256 | |
52 | struct atusb_local { | |
53 | struct usb_device * udev; | |
54 | /* The interface to the RF part info, if applicable */ | |
55 | uint8_t ep0_atusb_major; | |
56 | uint8_t ep0_atusb_minor; | |
57 | uint8_t atusb_hw_type; | |
58 | struct spi_master *master; | |
59 | int slave_irq; | |
60 | struct urb *irq_urb; | |
61 | uint8_t irq_buf; /* receive irq serial here*/ | |
62 | uint8_t irq_seen; /* last irq serial from bulk */ | |
63 | uint8_t irq_sync; /* last irq serial from WRITE2_SYNC */ | |
64 | struct tasklet_struct task; /* interrupt delivery tasklet */ | |
65 | struct timer_list timer; /* delay, for interrupt synch */ | |
66 | struct at86rf230_platform_data platform_data; | |
67 | /* copy platform_data so that we can adapt .reset_data */ | |
68 | struct spi_device *spi; | |
69 | // unsigned char buffer[3]; | |
70 | unsigned char buffer[260]; /* XXL, just in case */ | |
71 | struct spi_message *msg; | |
72 | }; | |
73 | ||
74 | /* Commands to our device. Make sure this is synced with the firmware */ | |
75 | enum atspi_requests { | |
76 | ATUSB_ID = 0x00, /* system status/control grp */ | |
77 | ATUSB_BUILD, | |
78 | ATUSB_RESET, | |
79 | ATUSB_RF_RESET = 0x10, /* debug/test group */ | |
80 | ATUSB_POLL_INT, | |
81 | ATUSB_TEST, /* atusb-sil only */ | |
82 | ATUSB_TIMER, | |
83 | ATUSB_GPIO, | |
84 | ATUSB_SLP_TR, | |
85 | ATUSB_GPIO_CLEANUP, | |
86 | ATUSB_REG_WRITE = 0x20, /* transceiver group */ | |
87 | ATUSB_REG_READ, | |
88 | ATUSB_BUF_WRITE, | |
89 | ATUSB_BUF_READ, | |
90 | ATUSB_SRAM_WRITE, | |
91 | ATUSB_SRAM_READ, | |
92 | ATUSB_SPI_WRITE = 0x30, /* SPI group */ | |
93 | ATUSB_SPI_READ1, | |
94 | ATUSB_SPI_READ2, | |
95 | ATUSB_SPI_WRITE2_SYNC, | |
96 | }; | |
97 | ||
98 | /* | |
99 | * Direction bRequest wValue wIndex wLength | |
100 | * | |
101 | * ->host ATUSB_ID - - 3 | |
102 | * ->host ATUSB_BUILD - - #bytes | |
103 | * host-> ATUSB_RESET - - 0 | |
104 | * | |
105 | * host-> ATUSB_RF_RESET - - 0 | |
106 | * ->host ATUSB_POLL_INT - - 1 | |
107 | * host-> ATUSB_TEST - - 0 | |
108 | * ->host ATUSB_TIMER - - #bytes (6) | |
109 | * ->host ATUSB_GPIO dir+data mask+p# 3 | |
110 | * host-> ATUSB_SLP_TR - - 0 | |
111 | * host-> ATUSB_GPIO_CLEANUP - - 0 | |
112 | * | |
113 | * host-> ATUSB_REG_WRITE value addr 0 | |
114 | * ->host ATUSB_REG_READ - addr 1 | |
115 | * host-> ATUSB_BUF_WRITE - - #bytes | |
116 | * ->host ATUSB_BUF_READ - - #bytes | |
117 | * host-> ATUSB_SRAM_WRITE - addr #bytes | |
118 | * ->host ATUSB_SRAM_READ - addr #bytes | |
119 | * | |
120 | * host-> ATUSB_SPI_WRITE byte0 byte1 #bytes | |
121 | * ->host ATUSB_SPI_READ1 byte0 - #bytes | |
122 | * ->host ATUSB_SPI_READ2 byte0 byte1 #bytes | |
123 | * ->host ATUSB_SPI_WRITE2_SYNC byte0 byte1 0/1 | |
124 | */ | |
125 | ||
126 | #define ATUSB_FROM_DEV (USB_TYPE_VENDOR | USB_DIR_IN) | |
127 | #define ATUSB_TO_DEV (USB_TYPE_VENDOR | USB_DIR_OUT) | |
128 | ||
129 | ||
130 | /* ----- Control transfers ------------------------------------------------- */ | |
131 | ||
132 | ||
133 | static int atusb_async_errchk(struct urb *urb) | |
134 | { | |
135 | struct atusb_local *atusb = urb->context; | |
136 | struct spi_message *msg = atusb->msg; | |
137 | struct usb_device *dev = atusb->udev; | |
138 | ||
139 | if (!urb->status) { | |
140 | dev_dbg(&dev->dev, "atusb_async_errchk OK len %d\n", | |
141 | urb->actual_length); | |
142 | return 0; | |
143 | } | |
144 | ||
145 | if (urb->status != -ENOENT && urb->status != -ECONNRESET && | |
146 | urb->status != -ESHUTDOWN) | |
147 | dev_info(&dev->dev, "atusb_async_errchk FAIL error %d\n", | |
148 | urb->status); | |
149 | ||
150 | msg->actual_length = 0; | |
151 | ||
152 | return urb->status; | |
153 | } | |
154 | ||
155 | static void atusb_async_finish(struct urb *urb) | |
156 | { | |
157 | struct atusb_local *atusb = urb->context; | |
158 | struct spi_message *msg = atusb->msg; | |
159 | ||
160 | msg->status = urb->status; | |
161 | msg->complete(msg->context); | |
162 | ||
163 | kfree(urb->setup_packet); | |
164 | usb_free_urb(urb); | |
165 | } | |
166 | ||
167 | static void atusb_ctrl_cb(struct urb *urb) | |
168 | { | |
169 | atusb_async_errchk(urb); | |
170 | atusb_async_finish(urb); | |
171 | } | |
172 | ||
173 | static void atusb_timer(unsigned long data) | |
174 | { | |
175 | struct urb *urb = (void *) data; | |
176 | ||
177 | dev_warn(&urb->dev->dev, "atusb_timer\n"); | |
178 | atusb_async_finish(urb); | |
179 | } | |
180 | ||
181 | static void atusb_ctrl_cb_sync(struct urb *urb) | |
182 | { | |
183 | struct atusb_local *atusb = urb->context; | |
184 | ||
185 | /* @@@ needs locking/atomic */ | |
186 | if (atusb_async_errchk(urb) || atusb->irq_sync == atusb->irq_seen) { | |
187 | atusb_async_finish(urb); | |
188 | return; | |
189 | } | |
190 | ||
191 | BUG_ON(timer_pending(&atusb->timer)); | |
192 | atusb->timer.expires = jiffies+msecs_to_jiffies(SYNC_TIMEOUT_MS); | |
193 | atusb->timer.data = (unsigned long) urb; | |
194 | add_timer(&atusb->timer); | |
195 | } | |
196 | ||
197 | static void atusb_read_fb_cb(struct urb *urb) | |
198 | { | |
199 | struct atusb_local *atusb = urb->context; | |
200 | struct spi_message *msg = atusb->msg; | |
201 | const struct spi_transfer *xfer; | |
202 | uint8_t *rx; | |
203 | ||
204 | if (!atusb_async_errchk(urb)) { | |
205 | BUG_ON(!urb->actual_length); | |
206 | ||
207 | xfer = list_first_entry(&msg->transfers, struct spi_transfer, | |
208 | transfer_list); | |
209 | rx = xfer->rx_buf; | |
210 | rx[1] = atusb->buffer[0]; | |
211 | ||
212 | xfer = list_entry(xfer->transfer_list.next, | |
213 | struct spi_transfer, transfer_list); | |
214 | memcpy(xfer->rx_buf, atusb->buffer+1, urb->actual_length-1); | |
215 | } | |
216 | ||
217 | atusb_async_finish(urb); | |
218 | } | |
219 | ||
220 | static int submit_control_msg(struct atusb_local *atusb, | |
221 | __u8 request, __u8 requesttype, __u16 value, __u16 index, | |
222 | void *data, __u16 size, usb_complete_t complete_fn, void *context) | |
223 | { | |
224 | struct usb_device *dev = atusb->udev; | |
225 | struct usb_ctrlrequest *req; | |
226 | struct urb *urb; | |
227 | int retval = -ENOMEM; | |
228 | ||
229 | req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); | |
230 | if (!req) | |
231 | return -ENOMEM; | |
232 | ||
233 | req->bRequest = request; | |
234 | req->bRequestType = requesttype; | |
235 | req->wValue = cpu_to_le16(value); | |
236 | req->wIndex = cpu_to_le16(index); | |
237 | req->wLength = cpu_to_le16(size); | |
238 | ||
239 | urb = usb_alloc_urb(0, GFP_KERNEL); | |
240 | if (!urb) | |
241 | goto out_nourb; | |
242 | ||
243 | usb_fill_control_urb(urb, dev, | |
244 | requesttype == ATUSB_FROM_DEV ? | |
245 | usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0), | |
246 | (unsigned char *) req, data, size, complete_fn, context); | |
247 | ||
248 | retval = usb_submit_urb(urb, GFP_KERNEL); | |
249 | if (!retval) | |
250 | return 0; | |
251 | dev_warn(&dev->dev, "failed submitting read urb, error %d", | |
252 | retval); | |
253 | retval = retval == -ENOMEM ? retval : -EIO; | |
254 | ||
255 | usb_free_urb(urb); | |
256 | out_nourb: | |
257 | kfree(req); | |
258 | ||
259 | return retval; | |
260 | } | |
261 | ||
262 | ||
263 | /* ----- SPI transfers ----------------------------------------------------- */ | |
264 | ||
265 | ||
266 | static int atusb_read1(struct atusb_local *atusb, | |
267 | uint8_t tx, uint8_t *rx, int len) | |
268 | { | |
269 | dev_dbg(&atusb->udev->dev, "atusb_read1: tx = 0x%x\n", tx); | |
270 | return submit_control_msg(atusb, | |
271 | ATUSB_SPI_READ1, ATUSB_FROM_DEV, tx, 0, | |
272 | rx, 1, atusb_ctrl_cb, atusb); | |
273 | } | |
274 | ||
275 | static int atusb_read_fb(struct atusb_local *atusb, | |
276 | uint8_t tx, uint8_t *rx0, uint8_t *rx, int len) | |
277 | { | |
278 | dev_dbg(&atusb->udev->dev, "atusb_read_fb: tx = 0x%x\n", tx); | |
279 | return submit_control_msg(atusb, | |
280 | ATUSB_SPI_READ1, ATUSB_FROM_DEV, tx, 0, | |
281 | atusb->buffer, len+1, atusb_read_fb_cb, atusb); | |
282 | } | |
283 | ||
284 | static int atusb_write(struct atusb_local *atusb, | |
285 | uint8_t tx0, uint8_t tx1, const uint8_t *tx, int len) | |
286 | { | |
287 | dev_dbg(&atusb->udev->dev, | |
288 | "atusb_write: tx0 = 0x%x tx1 = 0x%x\n", tx0, tx1); | |
289 | ||
290 | /* | |
291 | * The AT86RF230 driver sometimes requires a transceiver state | |
292 | * transition to be an interrupt barrier. This is the case after | |
293 | * writing FORCE_TX_ON to the TRX_CMD field in the TRX_STATE register. | |
294 | * | |
295 | * Since there is no other means of notification, we just decode the | |
296 | * transfer and do a bit of pattern matching. | |
297 | */ | |
298 | if (tx0 == (CMD_REG | CMD_WRITE | RG_TRX_STATE) && | |
299 | (tx1 & 0x1f) == STATE_FORCE_TX_ON) | |
300 | return submit_control_msg(atusb, | |
301 | ATUSB_SPI_WRITE2_SYNC, ATUSB_FROM_DEV, tx0, tx1, | |
302 | &atusb->irq_sync, 1, atusb_ctrl_cb_sync, atusb); | |
303 | else | |
304 | return submit_control_msg(atusb, | |
305 | ATUSB_SPI_WRITE, ATUSB_TO_DEV, tx0, tx1, | |
306 | (uint8_t *) tx, len, atusb_ctrl_cb, atusb); | |
307 | } | |
308 | ||
309 | static int atusb_transfer(struct spi_device *spi, struct spi_message *msg) | |
310 | { | |
311 | struct atusb_local *atusb = spi_master_get_devdata(spi->master); | |
312 | struct spi_transfer *xfer; | |
313 | struct spi_transfer *x[2]; | |
314 | int n; | |
315 | const uint8_t *tx; | |
316 | uint8_t *rx; | |
317 | int len; | |
318 | int retval = 0; | |
319 | ||
320 | if (unlikely(list_empty(&msg->transfers))) { | |
321 | dev_err(&atusb->udev->dev, "transfer is empty\n"); | |
322 | return -EINVAL; | |
323 | } | |
324 | ||
325 | atusb->msg = msg; | |
326 | ||
327 | /* Classify the request */ | |
328 | n = 0; | |
329 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | |
330 | if (n == ARRAY_SIZE(x)) { | |
331 | dev_err(&atusb->udev->dev, "too many transfers\n"); | |
332 | return -EINVAL; | |
333 | } | |
334 | x[n] = xfer; | |
335 | n++; | |
336 | } | |
337 | ||
338 | tx = x[0]->tx_buf; | |
339 | rx = x[0]->rx_buf; | |
340 | len = x[0]->len; | |
341 | ||
342 | msg->actual_length = len; | |
343 | ||
344 | if (!tx || len != 2) | |
345 | goto bad_req; | |
346 | if (n == 1) { | |
347 | if (rx) { | |
348 | dev_dbg(&atusb->udev->dev, "read 1\n"); | |
349 | retval = atusb_read1(atusb, tx[0], rx+1, len-1); | |
350 | } else { | |
351 | dev_dbg(&atusb->udev->dev, "write 2\n"); | |
352 | /* | |
353 | * Don't take our clock away !! ;-) | |
354 | */ | |
355 | if (tx[0] == (CMD_REG | CMD_WRITE | RG_TRX_CTRL_0)) { | |
356 | msg->status = 0; | |
357 | msg->complete(msg->context); | |
358 | } else { | |
359 | retval = atusb_write(atusb, | |
360 | tx[0], tx[1], NULL, 0); | |
361 | } | |
362 | } | |
363 | } else { | |
364 | if (x[0]->rx_buf) { | |
365 | if (x[1]->tx_buf || !x[1]->rx_buf) | |
366 | goto bad_req; | |
367 | dev_dbg(&atusb->udev->dev, "read 1+\n"); | |
368 | retval = atusb_read_fb(atusb, tx[0], rx+1, | |
369 | x[1]->rx_buf, x[1]->len); | |
370 | } else { | |
371 | if (!x[1]->tx_buf ||x[1]->rx_buf) | |
372 | goto bad_req; | |
373 | dev_dbg(&atusb->udev->dev, "write 2+n\n"); | |
374 | retval = atusb_write(atusb, tx[0], tx[1], | |
375 | x[1]->tx_buf, x[1]->len); | |
376 | } | |
377 | } | |
378 | return retval; | |
379 | ||
380 | bad_req: | |
381 | dev_err(&atusb->udev->dev, "unrecognized request:\n"); | |
382 | list_for_each_entry(xfer, &msg->transfers, transfer_list) | |
383 | dev_err(&atusb->udev->dev, "%stx %srx len %u\n", | |
384 | xfer->tx_buf ? "" : "!", xfer->rx_buf ? " " : "!", | |
385 | xfer->len); | |
386 | return -EINVAL; | |
387 | } | |
388 | ||
389 | static int atusb_setup(struct spi_device *spi) | |
390 | { | |
391 | return 0; | |
392 | } | |
393 | ||
394 | ||
395 | /* ----- Interrupt handling ------------------------------------------------ */ | |
396 | ||
397 | ||
398 | static void atusb_tasklet(unsigned long data) | |
399 | { | |
400 | struct atusb_local *atusb = (void *) data; | |
401 | ||
402 | generic_handle_irq(atusb->slave_irq); | |
403 | } | |
404 | ||
405 | static void atusb_irq(struct urb *urb) | |
406 | { | |
407 | struct atusb_local *atusb = urb->context; | |
408 | ||
409 | dev_dbg(&urb->dev->dev, "atusb_irq (%d), seen %d sync %d\n", | |
410 | urb->status, atusb->irq_buf, atusb->irq_sync); | |
411 | if (!urb->status) { | |
412 | atusb->irq_seen = atusb->irq_buf; | |
413 | if (atusb->irq_sync == atusb->irq_seen && | |
414 | try_to_del_timer_sync(&atusb->timer) == 1) | |
415 | atusb_async_finish((struct urb *) atusb->timer.data); | |
416 | } | |
417 | usb_free_urb(urb); | |
418 | atusb->irq_urb = NULL; | |
419 | tasklet_schedule(&atusb->task); | |
420 | } | |
421 | ||
422 | static int atusb_arm_interrupt(struct atusb_local *atusb) | |
423 | { | |
424 | struct usb_device *dev = atusb->udev; | |
425 | struct urb *urb; | |
426 | int retval = -ENOMEM; | |
427 | ||
428 | BUG_ON(atusb->irq_urb); | |
429 | ||
430 | dev_vdbg(&dev->dev, "atusb_arm_interrupt\n"); | |
431 | urb = usb_alloc_urb(0, GFP_KERNEL); | |
432 | if (!urb) { | |
433 | dev_err(&dev->dev, | |
434 | "atusb_arm_interrupt: usb_alloc_urb failed\n"); | |
435 | return -ENOMEM; | |
436 | } | |
437 | ||
438 | usb_fill_bulk_urb(urb, dev, usb_rcvbulkpipe(dev, 1), | |
439 | &atusb->irq_buf, 1, atusb_irq, atusb); | |
440 | atusb->irq_urb = urb; | |
441 | retval = usb_submit_urb(urb, GFP_KERNEL); | |
442 | if (!retval) | |
443 | return 0; | |
444 | ||
445 | dev_err(&dev->dev, "failed submitting bulk urb, error %d\n", retval); | |
446 | retval = retval == -ENOMEM ? retval : -EIO; | |
447 | ||
448 | usb_free_urb(urb); | |
449 | ||
450 | return retval; | |
451 | } | |
452 | ||
453 | static void atusb_irq_mask(struct irq_data *data) | |
454 | { | |
455 | struct atusb_local *atusb = irq_data_get_irq_chip_data(data); | |
456 | ||
457 | dev_vdbg(&atusb->udev->dev, "atusb_irq_mask\n"); | |
458 | tasklet_disable_nosync(&atusb->task); | |
459 | } | |
460 | ||
461 | static void atusb_irq_unmask(struct irq_data *data) | |
462 | { | |
463 | struct atusb_local *atusb = irq_data_get_irq_chip_data(data); | |
464 | ||
465 | dev_vdbg(&atusb->udev->dev, "atusb_irq_unmask\n"); | |
466 | tasklet_enable(&atusb->task); | |
467 | } | |
468 | ||
469 | static void atusb_irq_ack(struct irq_data *data) | |
470 | { | |
471 | struct atusb_local *atusb = irq_data_get_irq_chip_data(data); | |
472 | ||
473 | dev_vdbg(&atusb->udev->dev, "atusb_irq_ack\n"); | |
474 | atusb_arm_interrupt(atusb); | |
475 | } | |
476 | ||
477 | static struct irq_chip atusb_irq_chip = { | |
478 | .name = "atusb-slave", | |
479 | .irq_mask = atusb_irq_mask, | |
480 | .irq_unmask = atusb_irq_unmask, | |
481 | .irq_ack = atusb_irq_ack, | |
482 | }; | |
483 | ||
484 | ||
485 | /* ----- Transceiver reset ------------------------------------------------- */ | |
486 | ||
487 | ||
488 | static void atusb_reset(void *reset_data) | |
489 | { | |
490 | int retval; | |
491 | struct atusb_local *atusb = reset_data; | |
492 | ||
493 | retval = usb_control_msg(atusb->udev, | |
494 | usb_rcvctrlpipe(atusb->udev, 0), | |
495 | ATUSB_RF_RESET, ATUSB_TO_DEV, 0, 0, | |
496 | NULL, 0, 1000); | |
497 | if (retval < 0) { | |
498 | dev_err(&atusb->udev->dev, | |
499 | "%s: error doing reset retval = %d\n", | |
500 | __func__, retval); | |
501 | } | |
502 | } | |
503 | ||
504 | ||
505 | /* ----- Firmware version information -------------------------------------- */ | |
506 | ||
507 | ||
508 | static int atusb_get_and_show_revision(struct atusb_local *atusb) | |
509 | { | |
510 | struct usb_device *dev = atusb->udev; | |
511 | int retval; | |
512 | ||
513 | /* Get a couple of the ATMega Firmware values */ | |
514 | retval = usb_control_msg(dev, | |
515 | usb_rcvctrlpipe(dev, 0), | |
516 | ATUSB_ID, ATUSB_FROM_DEV, 0, 0, | |
517 | atusb->buffer, 3, 1000); | |
518 | if (retval < 0) { | |
519 | dev_info(&dev->dev, | |
520 | "failed submitting urb for ATUSB_ID, error %d\n", retval); | |
521 | return retval == -ENOMEM ? retval : -EIO; | |
522 | } | |
523 | ||
524 | atusb->ep0_atusb_major = atusb->buffer[0]; | |
525 | atusb->ep0_atusb_minor = atusb->buffer[1]; | |
526 | atusb->atusb_hw_type = atusb->buffer[2]; | |
527 | dev_info(&dev->dev, | |
528 | "Firmware: major: %u, minor: %u, hardware type: %u\n", | |
529 | atusb->ep0_atusb_major, atusb->ep0_atusb_minor, | |
530 | atusb->atusb_hw_type); | |
531 | ||
532 | return 0; | |
533 | } | |
534 | ||
535 | static int atusb_get_and_show_build(struct atusb_local *atusb) | |
536 | { | |
537 | struct usb_device *dev = atusb->udev; | |
538 | char build[ATUSB_BUILD_SIZE+1]; | |
539 | int retval; | |
540 | ||
541 | retval = usb_control_msg(dev, | |
542 | usb_rcvctrlpipe(atusb->udev, 0), | |
543 | ATUSB_BUILD, ATUSB_FROM_DEV, 0, 0, | |
544 | build, ATUSB_BUILD_SIZE, 1000); | |
545 | if (retval < 0) { | |
546 | dev_err(&dev->dev, | |
547 | "failed submitting urb for ATUSB_BUILD, error %d\n", | |
548 | retval); | |
549 | return retval == -ENOMEM ? retval : -EIO; | |
550 | } | |
551 | ||
552 | build[retval] = 0; | |
553 | dev_info(&dev->dev, "Firmware: build %s\n", build); | |
554 | ||
555 | return 0; | |
556 | } | |
557 | ||
558 | ||
559 | /* ----- Setup ------------------------------------------------------------- */ | |
560 | ||
561 | ||
562 | struct at86rf230_platform_data at86rf230_platform_data = { | |
563 | .rstn = -1, | |
564 | .slp_tr = -1, | |
565 | .dig2 = -1, | |
566 | .reset = atusb_reset, | |
567 | /* set .reset_data later */ | |
568 | }; | |
569 | ||
570 | static int atusb_probe(struct usb_interface *interface, | |
571 | const struct usb_device_id *id) | |
572 | { | |
573 | struct spi_board_info board_info = { | |
574 | .modalias = "at86rf230", | |
575 | /* set .irq later */ | |
576 | .chip_select = 0, | |
577 | .bus_num = -1, | |
578 | .max_speed_hz = 8 * 1000 * 1000, | |
579 | }; | |
580 | ||
581 | struct usb_device *udev = interface_to_usbdev(interface); | |
582 | struct atusb_local *atusb = NULL; | |
583 | struct spi_master *master; | |
584 | int retval; | |
585 | ||
586 | /* | |
587 | * Ignore all interfaces used for DFU, i.e., everything while in the | |
588 | * boot loader, and interface #1 when in the application. | |
589 | */ | |
590 | if (interface->cur_altsetting->desc.bInterfaceClass != | |
591 | USB_CLASS_VENDOR_SPEC) { | |
592 | dev_dbg(&udev->dev, | |
593 | "Ignoring interface with class 0x%02x\n", | |
594 | interface->cur_altsetting->desc.bInterfaceClass); | |
595 | return -ENODEV; | |
596 | } | |
597 | ||
598 | master = spi_alloc_master(&udev->dev, sizeof(*atusb)); | |
599 | if (!master) | |
600 | return -ENOMEM; | |
601 | ||
602 | atusb = spi_master_get_devdata(master); | |
603 | ||
604 | atusb->udev = usb_get_dev(udev); | |
605 | usb_set_intfdata(interface, atusb); | |
606 | ||
607 | atusb->master = spi_master_get(master); | |
608 | ||
609 | master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; | |
610 | master->bus_num = -1; | |
611 | master->num_chipselect = 1; | |
612 | master->setup = atusb_setup; | |
613 | master->transfer = atusb_transfer; | |
614 | ||
615 | atusb->slave_irq = irq_alloc_desc(numa_node_id()); | |
616 | if (atusb->slave_irq < 0) { | |
617 | dev_err(&udev->dev, "can't allocate slave irq\n"); | |
618 | retval = -ENXIO; | |
619 | goto err_free; | |
620 | } | |
621 | ||
622 | set_irq_chip_data(atusb->slave_irq, atusb); | |
623 | set_irq_chip_and_handler(atusb->slave_irq, &atusb_irq_chip, | |
624 | handle_level_irq); | |
625 | ||
626 | /* FIXME prepare USB IRQ */ | |
627 | ||
628 | retval = spi_register_master(master); | |
629 | if (retval < 0) { | |
630 | dev_err(&udev->dev, "can't register spi master\n"); | |
631 | goto err_slave_irq; | |
632 | } | |
633 | ||
634 | atusb->platform_data = at86rf230_platform_data; | |
635 | atusb->platform_data.reset_data = atusb; | |
636 | board_info.platform_data = &atusb->platform_data; | |
637 | board_info.irq = atusb->slave_irq; | |
638 | ||
639 | init_timer(&atusb->timer); | |
640 | atusb->timer.function = atusb_timer; | |
641 | ||
642 | tasklet_init(&atusb->task, atusb_tasklet, (unsigned long) atusb); | |
643 | tasklet_disable(&atusb->task); | |
644 | atusb_arm_interrupt(atusb); | |
645 | ||
646 | if (atusb_get_and_show_revision(atusb) < 0) | |
647 | goto err_master; | |
648 | if (atusb_get_and_show_build(atusb) < 0) | |
649 | goto err_master; | |
650 | ||
651 | atusb->spi = spi_new_device(master, &board_info); | |
652 | if (!atusb->spi) { | |
653 | dev_err(&udev->dev, "can't create new device for %s\n", | |
654 | board_info.modalias); | |
655 | goto err_master; | |
656 | } | |
657 | ||
658 | dev_info(&atusb->spi->dev, | |
659 | "ATUSB ready for mischief (IRQ %d)\n", board_info.irq); | |
660 | ||
661 | return 0; | |
662 | ||
663 | err_master: | |
664 | /* | |
665 | * If we come here from a partially successful driver initialization, | |
666 | * we don't really know how much it has done. In particular, it may | |
667 | * have triggered an interrupt and thus removed the interrupt URB and | |
668 | * maybe scheduled the tasklet. | |
669 | */ | |
670 | tasklet_disable(&atusb->task); | |
671 | if (atusb->irq_urb) | |
672 | usb_kill_urb(atusb->irq_urb); | |
673 | spi_master_put(atusb->master); | |
674 | err_slave_irq: | |
675 | set_irq_chained_handler(atusb->slave_irq, NULL); | |
676 | set_irq_chip_data(atusb->slave_irq, NULL); | |
677 | irq_free_desc(atusb->slave_irq); | |
678 | err_free: | |
679 | return retval; | |
680 | } | |
681 | ||
682 | static void atusb_disconnect(struct usb_interface *interface) | |
683 | { | |
684 | struct atusb_local *atusb = usb_get_intfdata(interface); | |
685 | struct spi_master *master = atusb->master; | |
686 | ||
687 | tasklet_disable(&atusb->task); | |
688 | /* @@@ this needs some extra protecion - wa */ | |
689 | if (atusb->irq_urb) | |
690 | usb_kill_urb(atusb->irq_urb); | |
691 | ||
692 | BUG_ON(timer_pending(&atusb->timer)); | |
693 | ||
694 | usb_set_intfdata(interface, NULL); | |
695 | usb_put_dev(atusb->udev); | |
696 | ||
697 | spi_dev_put(atusb->spi); | |
698 | ||
699 | spi_unregister_master(master); | |
700 | ||
701 | set_irq_chained_handler(atusb->slave_irq, NULL); | |
702 | set_irq_chip_data(atusb->slave_irq, NULL); | |
703 | irq_free_desc(atusb->slave_irq); | |
704 | ||
705 | spi_master_put(master); | |
706 | } | |
707 | ||
708 | void atusb_release(struct device *dev) | |
709 | { | |
710 | return; | |
711 | } | |
712 | ||
713 | static struct usb_driver atusb_driver = { | |
714 | .name = "atusb_ben-wpan", | |
715 | .probe = atusb_probe, | |
716 | .disconnect = atusb_disconnect, | |
717 | .id_table = atusb_device_table, | |
718 | }; | |
719 | ||
720 | static struct platform_device atusb_device = { | |
721 | .name = "spi_atusb", | |
722 | .id = -1, | |
723 | .dev.release = atusb_release, | |
724 | }; | |
725 | ||
726 | static int __init atusb_init(void) | |
727 | { | |
728 | int retval; | |
729 | ||
730 | retval = platform_device_register(&atusb_device); | |
731 | if (retval) | |
732 | return retval; | |
733 | ||
734 | return usb_register(&atusb_driver); | |
735 | } | |
736 | ||
737 | static void __exit atusb_exit(void) | |
738 | { | |
739 | usb_deregister(&atusb_driver); | |
740 | platform_device_unregister(&atusb_device); | |
741 | } | |
742 | ||
743 | module_init (atusb_init); | |
744 | module_exit (atusb_exit); | |
745 | ||
746 | MODULE_AUTHOR("Richard Sharpe <realrichardsharpe@gmail.com>"); | |
747 | MODULE_AUTHOR("Stefan Schmidt <stefan@datenfreihafen.org>"); | |
748 | MODULE_AUTHOR("Werner Almesberger <werner@almesberger.net>"); | |
749 | MODULE_DESCRIPTION("ATUSB ben-wpan Driver"); | |
750 | MODULE_LICENSE("GPL"); |
drivers/spi/spi_atben.c | ||
---|---|---|
1 | /* | |
2 | * spi_atben.c - SPI host look-alike for ATBEN | |
3 | * | |
4 | * Written 2011 by Werner Almesberger | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 | |
8 | * as published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/kernel.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/gpio.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/spi/spi.h> | |
17 | #include <linux/spi/at86rf230.h> | |
18 | #include <asm/mach-jz4740/base.h> | |
19 | ||
20 | #include "../ieee802154/at86rf230.h" /* dirty */ | |
21 | ||
22 | ||
23 | enum { | |
24 | VDD_OFF = 1 << 2, /* VDD disable, PD02 */ | |
25 | MOSI = 1 << 8, /* CMD, PD08 */ | |
26 | SLP_TR = 1 << 9, /* CLK, PD09 */ | |
27 | MISO = 1 << 10, /* DAT0, PD10 */ | |
28 | SCLK = 1 << 11, /* DAT1, PD11 */ | |
29 | IRQ = 1 << 12, /* DAT2, PD12 */ | |
30 | nSEL = 1 << 13, /* DAT3/CD, PD13 */ | |
31 | }; | |
32 | ||
33 | #define PDPIN (prv->regs) | |
34 | #define PDDATS (prv->regs+0x14) | |
35 | #define PDDATC (prv->regs+0x18) | |
36 | ||
37 | ||
38 | struct atben_prv { | |
39 | struct device *dev; | |
40 | void __iomem *regs; | |
41 | struct resource *ioarea; | |
42 | struct at86rf230_platform_data | |
43 | platform_data; | |
44 | /* copy platform_data so that we can adapt .reset_data */ | |
45 | }; | |
46 | ||
47 | ||
48 | /* ----- ATBEN reset ------------------------------------------------------- */ | |
49 | ||
50 | ||
51 | static void atben_reset(void *reset_data) | |
52 | { | |
53 | struct atben_prv *prv = reset_data; | |
54 | const int charge = nSEL | MOSI | SLP_TR | SCLK; | |
55 | const int discharge = charge | IRQ | MISO; | |
56 | ||
57 | dev_info(prv->dev, "atben_reset\n"); | |
58 | jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 1 << 2, 1 << 2); | |
59 | jz_gpio_port_direction_output(JZ_GPIO_PORTD(0), discharge); | |
60 | jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 0, discharge); | |
61 | msleep(100); /* let power drop */ | |
62 | ||
63 | /* | |
64 | * Hack: PD12/DAT2/IRQ is an active-high interrupt input, which is | |
65 | * indicated by setting its direction bit to 1. We thus must not | |
66 | * configure it as an "input". | |
67 | */ | |
68 | jz_gpio_port_direction_input(JZ_GPIO_PORTD(0), MISO); | |
69 | jz_gpio_port_set_value(JZ_GPIO_PORTD(0), charge, charge); | |
70 | msleep(10); /* precharge caps */ | |
71 | ||
72 | jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 0, VDD_OFF | SLP_TR | SCLK); | |
73 | msleep(10); | |
74 | } | |
75 | ||
76 | ||
77 | /* ----- SPI transfers ----------------------------------------------------- */ | |
78 | ||
79 | ||
80 | static void rx_only(const struct atben_prv *prv, uint8_t *buf, int len) | |
81 | { | |
82 | uint8_t v; | |
83 | ||
84 | while (len--) { | |
85 | writel(SCLK, PDDATS); | |
86 | v = readl(PDPIN) & MISO ? 0x80 : 0; | |
87 | writel(SCLK, PDDATC); | |
88 | ||
89 | #define DO_BIT(m) \ | |
90 | writel(SCLK, PDDATS); \ | |
91 | if (readl(PDPIN) & MISO) \ | |
92 | v |= (m); \ | |
93 | writel(SCLK, PDDATC) | |
94 | ||
95 | DO_BIT(0x40); | |
96 | DO_BIT(0x20); | |
97 | DO_BIT(0x10); | |
98 | DO_BIT(0x08); | |
99 | DO_BIT(0x04); | |
100 | DO_BIT(0x02); | |
101 | DO_BIT(0x01); | |
102 | ||
103 | #undef DO_BIT | |
104 | ||
105 | *buf++ = v; | |
106 | } | |
107 | } | |
108 | ||
109 | ||
110 | static void tx_only(const struct atben_prv *prv, const uint8_t *buf, int len) | |
111 | { | |
112 | uint8_t tv; | |
113 | ||
114 | while (len--) { | |
115 | tv = *buf++; | |
116 | ||
117 | if (tv & 0x80) { | |
118 | writel(MOSI, PDDATS); | |
119 | goto b6_1; | |
120 | } else { | |
121 | writel(MOSI, PDDATC); | |
122 | goto b6_0; | |
123 | } | |
124 | ||
125 | #define DO_BIT(m, this, next) \ | |
126 | this##_1: \ | |
127 | writel(SCLK, PDDATS); \ | |
128 | if (tv & (m)) { \ | |
129 | writel(SCLK, PDDATC); \ | |
130 | goto next##_1; \ | |
131 | } else { \ | |
132 | writel(MOSI | SCLK, PDDATC); \ | |
133 | goto next##_0; \ | |
134 | } \ | |
135 | this##_0: \ | |
136 | writel(SCLK, PDDATS); \ | |
137 | writel(SCLK, PDDATC); \ | |
138 | if (tv & (m)) { \ | |
139 | writel(MOSI, PDDATS); \ | |
140 | goto next##_1; \ | |
141 | } else { \ | |
142 | goto next##_0; \ | |
143 | } | |
144 | ||
145 | DO_BIT(0x40, b6, b5); | |
146 | DO_BIT(0x20, b5, b4); | |
147 | DO_BIT(0x10, b4, b3); | |
148 | DO_BIT(0x08, b3, b2); | |
149 | DO_BIT(0x04, b2, b1); | |
150 | DO_BIT(0x02, b1, b0); | |
151 | DO_BIT(0x01, b0, done); | |
152 | ||
153 | #undef DO_BIT | |
154 | ||
155 | done_1: | |
156 | done_0: | |
157 | writel(SCLK, PDDATS); | |
158 | writel(SCLK, PDDATC); | |
159 | writel(SCLK, PDDATC); /* delay to meet t5 timing */ | |
160 | } | |
161 | } | |
162 | ||
163 | ||
164 | static void bidir(const struct atben_prv *prv, const uint8_t *tx, uint8_t *rx, | |
165 | int len) | |
166 | { | |
167 | uint8_t mask, tv, rv; | |
168 | ||
169 | while (len--) { | |
170 | tv = *tx++; | |
171 | for (mask = 0x80; mask; mask >>= 1) { | |
172 | if (tv & mask) | |
173 | writel(MOSI, PDDATS); | |
174 | else | |
175 | writel(MOSI, PDDATC); | |
176 | writel(SCLK, PDDATS); | |
177 | if (readl(PDPIN) & MISO) | |
178 | rv |= mask; | |
179 | writel(SCLK, PDDATC); | |
180 | } | |
181 | *rx++ = rv; | |
182 | } | |
183 | } | |
184 | ||
185 | ||
186 | static int atben_transfer(struct spi_device *spi, struct spi_message *msg) | |
187 | { | |
188 | struct atben_prv *prv = spi_master_get_devdata(spi->master); | |
189 | struct spi_transfer *xfer; | |
190 | const uint8_t *tx; | |
191 | uint8_t *rx; | |
192 | ||
193 | if (unlikely(list_empty(&msg->transfers))) { | |
194 | dev_err(prv->dev, "transfer is empty\n"); | |
195 | return -EINVAL; | |
196 | } | |
197 | ||
198 | msg->actual_length = 0; | |
199 | ||
200 | writel(nSEL, PDDATC); | |
201 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | |
202 | tx = xfer->tx_buf; | |
203 | rx = xfer->rx_buf; | |
204 | msg->actual_length += xfer->len; | |
205 | ||
206 | if (!tx) | |
207 | rx_only(prv, rx, xfer->len); | |
208 | else if (!rx) | |
209 | tx_only(prv, tx, xfer->len); | |
210 | else | |
211 | bidir(prv, tx, rx, xfer->len); | |
212 | } | |
213 | writel(nSEL, PDDATS); | |
214 | ||
215 | msg->status = 0; | |
216 | msg->complete(msg->context); | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
221 | static int atben_setup(struct spi_device *spi) | |
222 | { | |
223 | return 0; | |
224 | } | |
225 | ||
226 | ||
227 | /* ----- SPI master creation/removal --------------------------------------- */ | |
228 | ||
229 | ||
230 | const static struct at86rf230_platform_data at86rf230_platform_data = { | |
231 | .rstn = -1, | |
232 | .slp_tr = JZ_GPIO_PORTD(9), | |
233 | .dig2 = -1, | |
234 | .reset = atben_reset, | |
235 | /* set .reset_data later */ | |
236 | }; | |
237 | ||
238 | static int __devinit atben_probe(struct platform_device *pdev) | |
239 | { | |
240 | struct spi_board_info board_info = { | |
241 | .modalias = "at86rf230", | |
242 | /* set .irq later */ | |
243 | .chip_select = 0, | |
244 | .bus_num = -1, | |
245 | .max_speed_hz = 8 * 1000 * 1000, | |
246 | }; | |
247 | ||
248 | struct spi_master *master; | |
249 | struct atben_prv *prv; | |
250 | struct resource *regs; | |
251 | struct spi_device *spi; | |
252 | int err = -ENXIO; | |
253 | ||
254 | master = spi_alloc_master(&pdev->dev, sizeof(*prv)); | |
255 | if (!master) | |
256 | return -ENOMEM; | |
257 | ||
258 | prv = spi_master_get_devdata(master); | |
259 | prv->dev = &pdev->dev; | |
260 | platform_set_drvdata(pdev, spi_master_get(master)); | |
261 | ||
262 | master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; | |
263 | master->bus_num = pdev->id; | |
264 | master->num_chipselect = 1; | |
265 | master->setup = atben_setup; | |
266 | master->transfer = atben_transfer; | |
267 | ||
268 | dev_dbg(prv->dev, "Setting up ATBEN SPI\n"); | |
269 | ||
270 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
271 | if (!regs) { | |
272 | dev_err(prv->dev, "no IORESOURCE_MEM\n"); | |
273 | err = -ENOENT; | |
274 | goto out_master; | |
275 | } | |
276 | prv->ioarea = request_mem_region(regs->start, resource_size(regs), | |
277 | pdev->name); | |
278 | if (!prv->ioarea) { | |
279 | dev_err(prv->dev, "can't request ioarea\n"); | |
280 | goto out_master; | |
281 | } | |
282 | ||
283 | prv->regs = ioremap(regs->start, resource_size(regs)); | |
284 | if (!prv->regs) { | |
285 | dev_err(prv->dev, "can't ioremap\n"); | |
286 | goto out_ioarea; | |
287 | } | |
288 | ||
289 | board_info.irq = platform_get_irq(pdev, 0); | |
290 | if (board_info.irq < 0) { | |
291 | dev_err(prv->dev, "can't get GPIO irq\n"); | |
292 | err = -ENOENT; | |
293 | goto out_regs; | |
294 | } | |
295 | ||
296 | err = spi_register_master(master); | |
297 | if (err) { | |
298 | dev_err(prv->dev, "can't register master\n"); | |
299 | goto out_regs; | |
300 | } | |
301 | ||
302 | prv->platform_data = at86rf230_platform_data; | |
303 | prv->platform_data.reset_data = prv; | |
304 | board_info.platform_data = &prv->platform_data; | |
305 | ||
306 | spi = spi_new_device(master, &board_info); | |
307 | if (!spi) { | |
308 | dev_err(&pdev->dev, "can't create new device for %s\n", | |
309 | board_info.modalias); | |
310 | err = -ENXIO; | |
311 | goto out_registered; | |
312 | } | |
313 | ||
314 | dev_info(&spi->dev, "ATBEN ready for mischief (IRQ %d)\n", | |
315 | board_info.irq); | |
316 | ||
317 | return 0; | |
318 | ||
319 | out_registered: | |
320 | spi_unregister_master(master); | |
321 | ||
322 | out_regs: | |
323 | iounmap(prv->regs); | |
324 | ||
325 | out_ioarea: | |
326 | release_resource(prv->ioarea); | |
327 | kfree(prv->ioarea); | |
328 | ||
329 | out_master: | |
330 | platform_set_drvdata(pdev, NULL); | |
331 | spi_master_put(master); | |
332 | ||
333 | return err; | |
334 | } | |
335 | ||
336 | static int __devexit atben_remove(struct platform_device *pdev) | |
337 | { | |
338 | struct spi_master *master = platform_get_drvdata(pdev); | |
339 | struct atben_prv *prv = spi_master_get_devdata(master); | |
340 | ||
341 | // restore GPIOs | |
342 | ||
343 | spi_unregister_master(master); | |
344 | ||
345 | iounmap(prv->regs); | |
346 | ||
347 | release_resource(prv->ioarea); | |
348 | kfree(prv->ioarea); | |
349 | ||
350 | platform_set_drvdata(pdev, NULL); | |
351 | spi_master_put(master); | |
352 | ||
353 | return 0; | |
354 | } | |
355 | ||
356 | static struct platform_driver atben_driver = { | |
357 | .driver = { | |
358 | .name = "spi_atben", | |
359 | .owner = THIS_MODULE, | |
360 | }, | |
361 | .remove = __devexit_p(atben_remove), | |
362 | }; | |
363 | ||
364 | static struct resource atben_resources[] = { | |
365 | { | |
366 | .start = JZ4740_GPIO_BASE_ADDR+0x300, | |
367 | .end = JZ4740_GPIO_BASE_ADDR+0x3ff, | |
368 | .flags = IORESOURCE_MEM, | |
369 | }, | |
370 | { | |
371 | /* set start and end later */ | |
372 | .flags = IORESOURCE_IRQ, | |
373 | }, | |
374 | }; | |
375 | ||
376 | static struct platform_device atben_device = { | |
377 | .name = "spi_atben", | |
378 | .id = -1, | |
379 | .num_resources = ARRAY_SIZE(atben_resources), | |
380 | .resource = atben_resources, | |
381 | }; | |
382 | ||
383 | /* | |
384 | * Registering the platform device just to probe it immediately afterwards | |
385 | * seems a little circuitous. Need to see if there's a better way. | |
386 | * | |
387 | * What we actually should do is this: | |
388 | * - in module init, register the device | |
389 | * - maybe probe as well, but keep the device also if the probe fails | |
390 | * (due to a conflicting driver already occupying the 8:10 slot) | |
391 | * - have a means for user space to kick off driver probing, e.g., when | |
392 | * anything about the 8:10 slot changes | |
393 | */ | |
394 | ||
395 | static int __init atben_init(void) | |
396 | { | |
397 | int err; | |
398 | ||
399 | err = platform_device_register(&atben_device); | |
400 | if (err) | |
401 | return err; | |
402 | ||
403 | atben_resources[1].start = atben_resources[1].end = | |
404 | gpio_to_irq(JZ_GPIO_PORTD(12)); | |
405 | ||
406 | return platform_driver_probe(&atben_driver, atben_probe); | |
407 | } | |
408 | ||
409 | static void __exit atben_exit(void) | |
410 | { | |
411 | platform_driver_unregister(&atben_driver); | |
412 | platform_device_unregister(&atben_device); | |
413 | } | |
414 | ||
415 | module_init(atben_init); | |
416 | module_exit(atben_exit); | |
417 | ||
418 | ||
419 | MODULE_DESCRIPTION("ATBEN SPI Controller Driver"); | |
420 | MODULE_AUTHOR("Werner Almesberger <werner@almesberger.net>"); | |
421 | MODULE_LICENSE("GPL"); |
Branches:
ben-wpan
ben-wpan-stefan
5396a9238205f20f811ea57898980d3ca82df0b6
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9