diff options
-rw-r--r-- | Documentation/devicetree/bindings/net/ieee802154/cc2520.txt | 29 | ||||
-rw-r--r-- | drivers/net/ieee802154/Kconfig | 11 | ||||
-rw-r--r-- | drivers/net/ieee802154/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ieee802154/cc2520.c | 1039 | ||||
-rw-r--r-- | include/linux/spi/cc2520.h | 26 |
5 files changed, 1106 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt b/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt new file mode 100644 index 000000000000..0071883c08d8 --- /dev/null +++ b/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt | |||
@@ -0,0 +1,29 @@ | |||
1 | *CC2520 IEEE 802.15.4 Compatible Radio* | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: should be "ti,cc2520" | ||
5 | - spi-max-frequency: maximal bus speed (8000000), should be set to 4000000 depends | ||
6 | sync or async operation mode | ||
7 | - reg: the chipselect index | ||
8 | - pinctrl-0: pin control group to be used for this controller. | ||
9 | - pinctrl-names: must contain a "default" entry. | ||
10 | - fifo-gpio: GPIO spec for the FIFO pin | ||
11 | - fifop-gpio: GPIO spec for the FIFOP pin | ||
12 | - sfd-gpio: GPIO spec for the SFD pin | ||
13 | - cca-gpio: GPIO spec for the CCA pin | ||
14 | - vreg-gpio: GPIO spec for the VREG pin | ||
15 | - reset-gpio: GPIO spec for the RESET pin | ||
16 | Example: | ||
17 | cc2520@0 { | ||
18 | compatible = "ti,cc2520"; | ||
19 | reg = <0>; | ||
20 | spi-max-frequency = <4000000>; | ||
21 | pinctrl-names = "default"; | ||
22 | pinctrl-0 = <&cc2520_cape_pins>; | ||
23 | fifo-gpio = <&gpio1 18 0>; | ||
24 | fifop-gpio = <&gpio1 19 0>; | ||
25 | sfd-gpio = <&gpio1 13 0>; | ||
26 | cca-gpio = <&gpio1 16 0>; | ||
27 | vreg-gpio = <&gpio0 31 0>; | ||
28 | reset-gpio = <&gpio1 12 0>; | ||
29 | }; | ||
diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index 3e89beab64fd..8b7ae51f30f5 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig | |||
@@ -51,3 +51,14 @@ config IEEE802154_MRF24J40 | |||
51 | 51 | ||
52 | This driver can also be built as a module. To do so, say M here. | 52 | This driver can also be built as a module. To do so, say M here. |
53 | the module will be called 'mrf24j40'. | 53 | the module will be called 'mrf24j40'. |
54 | |||
55 | config IEEE802154_CC2520 | ||
56 | depends on IEEE802154_DRIVERS && MAC802154 | ||
57 | tristate "CC2520 transceiver driver" | ||
58 | depends on SPI | ||
59 | ---help--- | ||
60 | Say Y here to enable the CC2520 SPI 802.15.4 wireless | ||
61 | controller. | ||
62 | |||
63 | This driver can also be built as a module. To do so, say M here. | ||
64 | the module will be called 'cc2520'. | ||
diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile index abb0c08decb0..655cb95e6e24 100644 --- a/drivers/net/ieee802154/Makefile +++ b/drivers/net/ieee802154/Makefile | |||
@@ -2,3 +2,4 @@ obj-$(CONFIG_IEEE802154_FAKEHARD) += fakehard.o | |||
2 | obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o | 2 | obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o |
3 | obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o | 3 | obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o |
4 | obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o | 4 | obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o |
5 | obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o | ||
diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c new file mode 100644 index 000000000000..8a5ac7ab2300 --- /dev/null +++ b/drivers/net/ieee802154/cc2520.c | |||
@@ -0,0 +1,1039 @@ | |||
1 | /* Driver for TI CC2520 802.15.4 Wireless-PAN Networking controller | ||
2 | * | ||
3 | * Copyright (C) 2014 Varka Bhadram <varkab@cdac.in> | ||
4 | * Md.Jamal Mohiuddin <mjmohiuddin@cdac.in> | ||
5 | * P Sowjanya <sowjanyap@cdac.in> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/gpio.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/spi/spi.h> | ||
18 | #include <linux/spi/cc2520.h> | ||
19 | #include <linux/workqueue.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/skbuff.h> | ||
22 | #include <linux/pinctrl/consumer.h> | ||
23 | #include <linux/of_gpio.h> | ||
24 | |||
25 | #include <net/mac802154.h> | ||
26 | #include <net/wpan-phy.h> | ||
27 | #include <net/ieee802154.h> | ||
28 | |||
29 | #define SPI_COMMAND_BUFFER 3 | ||
30 | #define HIGH 1 | ||
31 | #define LOW 0 | ||
32 | #define STATE_IDLE 0 | ||
33 | #define RSSI_VALID 0 | ||
34 | #define RSSI_OFFSET 78 | ||
35 | |||
36 | #define CC2520_RAM_SIZE 640 | ||
37 | #define CC2520_FIFO_SIZE 128 | ||
38 | |||
39 | #define CC2520RAM_TXFIFO 0x100 | ||
40 | #define CC2520RAM_RXFIFO 0x180 | ||
41 | #define CC2520RAM_IEEEADDR 0x3EA | ||
42 | #define CC2520RAM_PANID 0x3F2 | ||
43 | #define CC2520RAM_SHORTADDR 0x3F4 | ||
44 | |||
45 | #define CC2520_FREG_MASK 0x3F | ||
46 | |||
47 | /* status byte values */ | ||
48 | #define CC2520_STATUS_XOSC32M_STABLE (1 << 7) | ||
49 | #define CC2520_STATUS_RSSI_VALID (1 << 6) | ||
50 | #define CC2520_STATUS_TX_UNDERFLOW (1 << 3) | ||
51 | |||
52 | /* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */ | ||
53 | #define CC2520_MINCHANNEL 11 | ||
54 | #define CC2520_MAXCHANNEL 26 | ||
55 | #define CC2520_CHANNEL_SPACING 5 | ||
56 | |||
57 | /* command strobes */ | ||
58 | #define CC2520_CMD_SNOP 0x00 | ||
59 | #define CC2520_CMD_IBUFLD 0x02 | ||
60 | #define CC2520_CMD_SIBUFEX 0x03 | ||
61 | #define CC2520_CMD_SSAMPLECCA 0x04 | ||
62 | #define CC2520_CMD_SRES 0x0f | ||
63 | #define CC2520_CMD_MEMORY_MASK 0x0f | ||
64 | #define CC2520_CMD_MEMORY_READ 0x10 | ||
65 | #define CC2520_CMD_MEMORY_WRITE 0x20 | ||
66 | #define CC2520_CMD_RXBUF 0x30 | ||
67 | #define CC2520_CMD_RXBUFCP 0x38 | ||
68 | #define CC2520_CMD_RXBUFMOV 0x32 | ||
69 | #define CC2520_CMD_TXBUF 0x3A | ||
70 | #define CC2520_CMD_TXBUFCP 0x3E | ||
71 | #define CC2520_CMD_RANDOM 0x3C | ||
72 | #define CC2520_CMD_SXOSCON 0x40 | ||
73 | #define CC2520_CMD_STXCAL 0x41 | ||
74 | #define CC2520_CMD_SRXON 0x42 | ||
75 | #define CC2520_CMD_STXON 0x43 | ||
76 | #define CC2520_CMD_STXONCCA 0x44 | ||
77 | #define CC2520_CMD_SRFOFF 0x45 | ||
78 | #define CC2520_CMD_SXOSCOFF 0x46 | ||
79 | #define CC2520_CMD_SFLUSHRX 0x47 | ||
80 | #define CC2520_CMD_SFLUSHTX 0x48 | ||
81 | #define CC2520_CMD_SACK 0x49 | ||
82 | #define CC2520_CMD_SACKPEND 0x4A | ||
83 | #define CC2520_CMD_SNACK 0x4B | ||
84 | #define CC2520_CMD_SRXMASKBITSET 0x4C | ||
85 | #define CC2520_CMD_SRXMASKBITCLR 0x4D | ||
86 | #define CC2520_CMD_RXMASKAND 0x4E | ||
87 | #define CC2520_CMD_RXMASKOR 0x4F | ||
88 | #define CC2520_CMD_MEMCP 0x50 | ||
89 | #define CC2520_CMD_MEMCPR 0x52 | ||
90 | #define CC2520_CMD_MEMXCP 0x54 | ||
91 | #define CC2520_CMD_MEMXWR 0x56 | ||
92 | #define CC2520_CMD_BCLR 0x58 | ||
93 | #define CC2520_CMD_BSET 0x59 | ||
94 | #define CC2520_CMD_CTR_UCTR 0x60 | ||
95 | #define CC2520_CMD_CBCMAC 0x64 | ||
96 | #define CC2520_CMD_UCBCMAC 0x66 | ||
97 | #define CC2520_CMD_CCM 0x68 | ||
98 | #define CC2520_CMD_UCCM 0x6A | ||
99 | #define CC2520_CMD_ECB 0x70 | ||
100 | #define CC2520_CMD_ECBO 0x72 | ||
101 | #define CC2520_CMD_ECBX 0x74 | ||
102 | #define CC2520_CMD_INC 0x78 | ||
103 | #define CC2520_CMD_ABORT 0x7F | ||
104 | #define CC2520_CMD_REGISTER_READ 0x80 | ||
105 | #define CC2520_CMD_REGISTER_WRITE 0xC0 | ||
106 | |||
107 | /* status registers */ | ||
108 | #define CC2520_CHIPID 0x40 | ||
109 | #define CC2520_VERSION 0x42 | ||
110 | #define CC2520_EXTCLOCK 0x44 | ||
111 | #define CC2520_MDMCTRL0 0x46 | ||
112 | #define CC2520_MDMCTRL1 0x47 | ||
113 | #define CC2520_FREQEST 0x48 | ||
114 | #define CC2520_RXCTRL 0x4A | ||
115 | #define CC2520_FSCTRL 0x4C | ||
116 | #define CC2520_FSCAL0 0x4E | ||
117 | #define CC2520_FSCAL1 0x4F | ||
118 | #define CC2520_FSCAL2 0x50 | ||
119 | #define CC2520_FSCAL3 0x51 | ||
120 | #define CC2520_AGCCTRL0 0x52 | ||
121 | #define CC2520_AGCCTRL1 0x53 | ||
122 | #define CC2520_AGCCTRL2 0x54 | ||
123 | #define CC2520_AGCCTRL3 0x55 | ||
124 | #define CC2520_ADCTEST0 0x56 | ||
125 | #define CC2520_ADCTEST1 0x57 | ||
126 | #define CC2520_ADCTEST2 0x58 | ||
127 | #define CC2520_MDMTEST0 0x5A | ||
128 | #define CC2520_MDMTEST1 0x5B | ||
129 | #define CC2520_DACTEST0 0x5C | ||
130 | #define CC2520_DACTEST1 0x5D | ||
131 | #define CC2520_ATEST 0x5E | ||
132 | #define CC2520_DACTEST2 0x5F | ||
133 | #define CC2520_PTEST0 0x60 | ||
134 | #define CC2520_PTEST1 0x61 | ||
135 | #define CC2520_RESERVED 0x62 | ||
136 | #define CC2520_DPUBIST 0x7A | ||
137 | #define CC2520_ACTBIST 0x7C | ||
138 | #define CC2520_RAMBIST 0x7E | ||
139 | |||
140 | /* frame registers */ | ||
141 | #define CC2520_FRMFILT0 0x00 | ||
142 | #define CC2520_FRMFILT1 0x01 | ||
143 | #define CC2520_SRCMATCH 0x02 | ||
144 | #define CC2520_SRCSHORTEN0 0x04 | ||
145 | #define CC2520_SRCSHORTEN1 0x05 | ||
146 | #define CC2520_SRCSHORTEN2 0x06 | ||
147 | #define CC2520_SRCEXTEN0 0x08 | ||
148 | #define CC2520_SRCEXTEN1 0x09 | ||
149 | #define CC2520_SRCEXTEN2 0x0A | ||
150 | #define CC2520_FRMCTRL0 0x0C | ||
151 | #define CC2520_FRMCTRL1 0x0D | ||
152 | #define CC2520_RXENABLE0 0x0E | ||
153 | #define CC2520_RXENABLE1 0x0F | ||
154 | #define CC2520_EXCFLAG0 0x10 | ||
155 | #define CC2520_EXCFLAG1 0x11 | ||
156 | #define CC2520_EXCFLAG2 0x12 | ||
157 | #define CC2520_EXCMASKA0 0x14 | ||
158 | #define CC2520_EXCMASKA1 0x15 | ||
159 | #define CC2520_EXCMASKA2 0x16 | ||
160 | #define CC2520_EXCMASKB0 0x18 | ||
161 | #define CC2520_EXCMASKB1 0x19 | ||
162 | #define CC2520_EXCMASKB2 0x1A | ||
163 | #define CC2520_EXCBINDX0 0x1C | ||
164 | #define CC2520_EXCBINDX1 0x1D | ||
165 | #define CC2520_EXCBINDY0 0x1E | ||
166 | #define CC2520_EXCBINDY1 0x1F | ||
167 | #define CC2520_GPIOCTRL0 0x20 | ||
168 | #define CC2520_GPIOCTRL1 0x21 | ||
169 | #define CC2520_GPIOCTRL2 0x22 | ||
170 | #define CC2520_GPIOCTRL3 0x23 | ||
171 | #define CC2520_GPIOCTRL4 0x24 | ||
172 | #define CC2520_GPIOCTRL5 0x25 | ||
173 | #define CC2520_GPIOPOLARITY 0x26 | ||
174 | #define CC2520_GPIOCTRL 0x28 | ||
175 | #define CC2520_DPUCON 0x2A | ||
176 | #define CC2520_DPUSTAT 0x2C | ||
177 | #define CC2520_FREQCTRL 0x2E | ||
178 | #define CC2520_FREQTUNE 0x2F | ||
179 | #define CC2520_TXPOWER 0x30 | ||
180 | #define CC2520_TXCTRL 0x31 | ||
181 | #define CC2520_FSMSTAT0 0x32 | ||
182 | #define CC2520_FSMSTAT1 0x33 | ||
183 | #define CC2520_FIFOPCTRL 0x34 | ||
184 | #define CC2520_FSMCTRL 0x35 | ||
185 | #define CC2520_CCACTRL0 0x36 | ||
186 | #define CC2520_CCACTRL1 0x37 | ||
187 | #define CC2520_RSSI 0x38 | ||
188 | #define CC2520_RSSISTAT 0x39 | ||
189 | #define CC2520_RXFIRST 0x3C | ||
190 | #define CC2520_RXFIFOCNT 0x3E | ||
191 | #define CC2520_TXFIFOCNT 0x3F | ||
192 | |||
193 | /* Driver private information */ | ||
194 | struct cc2520_private { | ||
195 | struct spi_device *spi; /* SPI device structure */ | ||
196 | struct ieee802154_dev *dev; /* IEEE-802.15.4 device */ | ||
197 | u8 *buf; /* SPI TX/Rx data buffer */ | ||
198 | struct mutex buffer_mutex; /* SPI buffer mutex */ | ||
199 | bool is_tx; /* Flag for sync b/w Tx and Rx */ | ||
200 | int fifo_pin; /* FIFO GPIO pin number */ | ||
201 | struct work_struct fifop_irqwork;/* Workqueue for FIFOP */ | ||
202 | spinlock_t lock; /* Lock for is_tx*/ | ||
203 | struct completion tx_complete; /* Work completion for Tx */ | ||
204 | }; | ||
205 | |||
206 | /* Generic Functions */ | ||
207 | static int | ||
208 | cc2520_cmd_strobe(struct cc2520_private *priv, u8 cmd) | ||
209 | { | ||
210 | int ret; | ||
211 | u8 status = 0xff; | ||
212 | struct spi_message msg; | ||
213 | struct spi_transfer xfer = { | ||
214 | .len = 0, | ||
215 | .tx_buf = priv->buf, | ||
216 | .rx_buf = priv->buf, | ||
217 | }; | ||
218 | |||
219 | spi_message_init(&msg); | ||
220 | spi_message_add_tail(&xfer, &msg); | ||
221 | |||
222 | mutex_lock(&priv->buffer_mutex); | ||
223 | priv->buf[xfer.len++] = cmd; | ||
224 | dev_vdbg(&priv->spi->dev, | ||
225 | "command strobe buf[0] = %02x\n", | ||
226 | priv->buf[0]); | ||
227 | |||
228 | ret = spi_sync(priv->spi, &msg); | ||
229 | if (!ret) | ||
230 | status = priv->buf[0]; | ||
231 | dev_vdbg(&priv->spi->dev, | ||
232 | "buf[0] = %02x\n", priv->buf[0]); | ||
233 | mutex_unlock(&priv->buffer_mutex); | ||
234 | |||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | static int | ||
239 | cc2520_get_status(struct cc2520_private *priv, u8 *status) | ||
240 | { | ||
241 | int ret; | ||
242 | struct spi_message msg; | ||
243 | struct spi_transfer xfer = { | ||
244 | .len = 0, | ||
245 | .tx_buf = priv->buf, | ||
246 | .rx_buf = priv->buf, | ||
247 | }; | ||
248 | |||
249 | spi_message_init(&msg); | ||
250 | spi_message_add_tail(&xfer, &msg); | ||
251 | |||
252 | mutex_lock(&priv->buffer_mutex); | ||
253 | priv->buf[xfer.len++] = CC2520_CMD_SNOP; | ||
254 | dev_vdbg(&priv->spi->dev, | ||
255 | "get status command buf[0] = %02x\n", priv->buf[0]); | ||
256 | |||
257 | ret = spi_sync(priv->spi, &msg); | ||
258 | if (!ret) | ||
259 | *status = priv->buf[0]; | ||
260 | dev_vdbg(&priv->spi->dev, | ||
261 | "buf[0] = %02x\n", priv->buf[0]); | ||
262 | mutex_unlock(&priv->buffer_mutex); | ||
263 | |||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | static int | ||
268 | cc2520_write_register(struct cc2520_private *priv, u8 reg, u8 value) | ||
269 | { | ||
270 | int status; | ||
271 | struct spi_message msg; | ||
272 | struct spi_transfer xfer = { | ||
273 | .len = 0, | ||
274 | .tx_buf = priv->buf, | ||
275 | .rx_buf = priv->buf, | ||
276 | }; | ||
277 | |||
278 | spi_message_init(&msg); | ||
279 | spi_message_add_tail(&xfer, &msg); | ||
280 | |||
281 | mutex_lock(&priv->buffer_mutex); | ||
282 | |||
283 | if (reg <= CC2520_FREG_MASK) { | ||
284 | priv->buf[xfer.len++] = CC2520_CMD_REGISTER_WRITE | reg; | ||
285 | priv->buf[xfer.len++] = value; | ||
286 | } else { | ||
287 | priv->buf[xfer.len++] = CC2520_CMD_MEMORY_WRITE; | ||
288 | priv->buf[xfer.len++] = reg; | ||
289 | priv->buf[xfer.len++] = value; | ||
290 | } | ||
291 | status = spi_sync(priv->spi, &msg); | ||
292 | if (msg.status) | ||
293 | status = msg.status; | ||
294 | |||
295 | mutex_unlock(&priv->buffer_mutex); | ||
296 | |||
297 | return status; | ||
298 | } | ||
299 | |||
300 | static int | ||
301 | cc2520_write_ram(struct cc2520_private *priv, u16 reg, u8 len, u8 *data) | ||
302 | { | ||
303 | int status; | ||
304 | struct spi_message msg; | ||
305 | struct spi_transfer xfer_head = { | ||
306 | .len = 0, | ||
307 | .tx_buf = priv->buf, | ||
308 | .rx_buf = priv->buf, | ||
309 | }; | ||
310 | |||
311 | struct spi_transfer xfer_buf = { | ||
312 | .len = len, | ||
313 | .tx_buf = data, | ||
314 | }; | ||
315 | |||
316 | mutex_lock(&priv->buffer_mutex); | ||
317 | priv->buf[xfer_head.len++] = (CC2520_CMD_MEMORY_WRITE | | ||
318 | ((reg >> 8) & 0xff)); | ||
319 | priv->buf[xfer_head.len++] = reg & 0xff; | ||
320 | |||
321 | spi_message_init(&msg); | ||
322 | spi_message_add_tail(&xfer_head, &msg); | ||
323 | spi_message_add_tail(&xfer_buf, &msg); | ||
324 | |||
325 | status = spi_sync(priv->spi, &msg); | ||
326 | dev_dbg(&priv->spi->dev, "spi status = %d\n", status); | ||
327 | if (msg.status) | ||
328 | status = msg.status; | ||
329 | |||
330 | mutex_unlock(&priv->buffer_mutex); | ||
331 | return status; | ||
332 | } | ||
333 | |||
334 | static int | ||
335 | cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data) | ||
336 | { | ||
337 | int status; | ||
338 | struct spi_message msg; | ||
339 | struct spi_transfer xfer1 = { | ||
340 | .len = 0, | ||
341 | .tx_buf = priv->buf, | ||
342 | .rx_buf = priv->buf, | ||
343 | }; | ||
344 | |||
345 | struct spi_transfer xfer2 = { | ||
346 | .len = 1, | ||
347 | .rx_buf = data, | ||
348 | }; | ||
349 | |||
350 | spi_message_init(&msg); | ||
351 | spi_message_add_tail(&xfer1, &msg); | ||
352 | spi_message_add_tail(&xfer2, &msg); | ||
353 | |||
354 | mutex_lock(&priv->buffer_mutex); | ||
355 | priv->buf[xfer1.len++] = CC2520_CMD_MEMORY_READ; | ||
356 | priv->buf[xfer1.len++] = reg; | ||
357 | |||
358 | status = spi_sync(priv->spi, &msg); | ||
359 | dev_dbg(&priv->spi->dev, | ||
360 | "spi status = %d\n", status); | ||
361 | if (msg.status) | ||
362 | status = msg.status; | ||
363 | |||
364 | mutex_unlock(&priv->buffer_mutex); | ||
365 | |||
366 | return status; | ||
367 | } | ||
368 | |||
369 | static int | ||
370 | cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len) | ||
371 | { | ||
372 | int status; | ||
373 | |||
374 | /* length byte must include FCS even | ||
375 | * if it is calculated in the hardware | ||
376 | */ | ||
377 | int len_byte = len + 2; | ||
378 | |||
379 | struct spi_message msg; | ||
380 | |||
381 | struct spi_transfer xfer_head = { | ||
382 | .len = 0, | ||
383 | .tx_buf = priv->buf, | ||
384 | .rx_buf = priv->buf, | ||
385 | }; | ||
386 | struct spi_transfer xfer_len = { | ||
387 | .len = 1, | ||
388 | .tx_buf = &len_byte, | ||
389 | }; | ||
390 | struct spi_transfer xfer_buf = { | ||
391 | .len = len, | ||
392 | .tx_buf = data, | ||
393 | }; | ||
394 | |||
395 | spi_message_init(&msg); | ||
396 | spi_message_add_tail(&xfer_head, &msg); | ||
397 | spi_message_add_tail(&xfer_len, &msg); | ||
398 | spi_message_add_tail(&xfer_buf, &msg); | ||
399 | |||
400 | mutex_lock(&priv->buffer_mutex); | ||
401 | priv->buf[xfer_head.len++] = CC2520_CMD_TXBUF; | ||
402 | dev_vdbg(&priv->spi->dev, | ||
403 | "TX_FIFO cmd buf[0] = %02x\n", priv->buf[0]); | ||
404 | |||
405 | status = spi_sync(priv->spi, &msg); | ||
406 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | ||
407 | if (msg.status) | ||
408 | status = msg.status; | ||
409 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | ||
410 | dev_vdbg(&priv->spi->dev, "buf[0] = %02x\n", priv->buf[0]); | ||
411 | mutex_unlock(&priv->buffer_mutex); | ||
412 | |||
413 | return status; | ||
414 | } | ||
415 | |||
416 | static int | ||
417 | cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len, u8 *lqi) | ||
418 | { | ||
419 | int status; | ||
420 | struct spi_message msg; | ||
421 | |||
422 | struct spi_transfer xfer_head = { | ||
423 | .len = 0, | ||
424 | .tx_buf = priv->buf, | ||
425 | .rx_buf = priv->buf, | ||
426 | }; | ||
427 | struct spi_transfer xfer_buf = { | ||
428 | .len = len, | ||
429 | .rx_buf = data, | ||
430 | }; | ||
431 | |||
432 | spi_message_init(&msg); | ||
433 | spi_message_add_tail(&xfer_head, &msg); | ||
434 | spi_message_add_tail(&xfer_buf, &msg); | ||
435 | |||
436 | mutex_lock(&priv->buffer_mutex); | ||
437 | priv->buf[xfer_head.len++] = CC2520_CMD_RXBUF; | ||
438 | |||
439 | dev_vdbg(&priv->spi->dev, "read rxfifo buf[0] = %02x\n", priv->buf[0]); | ||
440 | dev_vdbg(&priv->spi->dev, "buf[1] = %02x\n", priv->buf[1]); | ||
441 | |||
442 | status = spi_sync(priv->spi, &msg); | ||
443 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | ||
444 | if (msg.status) | ||
445 | status = msg.status; | ||
446 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | ||
447 | dev_vdbg(&priv->spi->dev, | ||
448 | "return status buf[0] = %02x\n", priv->buf[0]); | ||
449 | dev_vdbg(&priv->spi->dev, "length buf[1] = %02x\n", priv->buf[1]); | ||
450 | |||
451 | mutex_unlock(&priv->buffer_mutex); | ||
452 | |||
453 | return status; | ||
454 | } | ||
455 | |||
456 | static int cc2520_start(struct ieee802154_dev *dev) | ||
457 | { | ||
458 | return cc2520_cmd_strobe(dev->priv, CC2520_CMD_SRXON); | ||
459 | } | ||
460 | |||
461 | static void cc2520_stop(struct ieee802154_dev *dev) | ||
462 | { | ||
463 | cc2520_cmd_strobe(dev->priv, CC2520_CMD_SRFOFF); | ||
464 | } | ||
465 | |||
466 | static int | ||
467 | cc2520_tx(struct ieee802154_dev *dev, struct sk_buff *skb) | ||
468 | { | ||
469 | struct cc2520_private *priv = dev->priv; | ||
470 | unsigned long flags; | ||
471 | int rc; | ||
472 | u8 status = 0; | ||
473 | |||
474 | rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); | ||
475 | if (rc) | ||
476 | goto err_tx; | ||
477 | |||
478 | rc = cc2520_write_txfifo(priv, skb->data, skb->len); | ||
479 | if (rc) | ||
480 | goto err_tx; | ||
481 | |||
482 | rc = cc2520_get_status(priv, &status); | ||
483 | if (rc) | ||
484 | goto err_tx; | ||
485 | |||
486 | if (status & CC2520_STATUS_TX_UNDERFLOW) { | ||
487 | dev_err(&priv->spi->dev, "cc2520 tx underflow exception\n"); | ||
488 | goto err_tx; | ||
489 | } | ||
490 | |||
491 | spin_lock_irqsave(&priv->lock, flags); | ||
492 | BUG_ON(priv->is_tx); | ||
493 | priv->is_tx = 1; | ||
494 | spin_unlock_irqrestore(&priv->lock, flags); | ||
495 | |||
496 | rc = cc2520_cmd_strobe(priv, CC2520_CMD_STXONCCA); | ||
497 | if (rc) | ||
498 | goto err; | ||
499 | |||
500 | rc = wait_for_completion_interruptible(&priv->tx_complete); | ||
501 | if (rc < 0) | ||
502 | goto err; | ||
503 | |||
504 | cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); | ||
505 | cc2520_cmd_strobe(priv, CC2520_CMD_SRXON); | ||
506 | |||
507 | return rc; | ||
508 | err: | ||
509 | spin_lock_irqsave(&priv->lock, flags); | ||
510 | priv->is_tx = 0; | ||
511 | spin_unlock_irqrestore(&priv->lock, flags); | ||
512 | err_tx: | ||
513 | return rc; | ||
514 | } | ||
515 | |||
516 | |||
517 | static int cc2520_rx(struct cc2520_private *priv) | ||
518 | { | ||
519 | u8 len = 0, lqi = 0, bytes = 1; | ||
520 | struct sk_buff *skb; | ||
521 | |||
522 | cc2520_read_rxfifo(priv, &len, bytes, &lqi); | ||
523 | |||
524 | if (len < 2 || len > IEEE802154_MTU) | ||
525 | return -EINVAL; | ||
526 | |||
527 | skb = alloc_skb(len, GFP_KERNEL); | ||
528 | if (!skb) | ||
529 | return -ENOMEM; | ||
530 | |||
531 | if (cc2520_read_rxfifo(priv, skb_put(skb, len), len, &lqi)) { | ||
532 | dev_dbg(&priv->spi->dev, "frame reception failed\n"); | ||
533 | kfree_skb(skb); | ||
534 | return -EINVAL; | ||
535 | } | ||
536 | |||
537 | skb_trim(skb, skb->len - 2); | ||
538 | |||
539 | ieee802154_rx_irqsafe(priv->dev, skb, lqi); | ||
540 | |||
541 | dev_vdbg(&priv->spi->dev, "RXFIFO: %x %x\n", len, lqi); | ||
542 | |||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | static int | ||
547 | cc2520_ed(struct ieee802154_dev *dev, u8 *level) | ||
548 | { | ||
549 | struct cc2520_private *priv = dev->priv; | ||
550 | u8 status = 0xff; | ||
551 | u8 rssi; | ||
552 | int ret; | ||
553 | |||
554 | ret = cc2520_read_register(priv , CC2520_RSSISTAT, &status); | ||
555 | if (ret) | ||
556 | return ret; | ||
557 | |||
558 | if (status != RSSI_VALID) | ||
559 | return -EINVAL; | ||
560 | |||
561 | ret = cc2520_read_register(priv , CC2520_RSSI, &rssi); | ||
562 | if (ret) | ||
563 | return ret; | ||
564 | |||
565 | /* level = RSSI(rssi) - OFFSET [dBm] : offset is 76dBm */ | ||
566 | *level = rssi - RSSI_OFFSET; | ||
567 | |||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | static int | ||
572 | cc2520_set_channel(struct ieee802154_dev *dev, int page, int channel) | ||
573 | { | ||
574 | struct cc2520_private *priv = dev->priv; | ||
575 | int ret; | ||
576 | |||
577 | might_sleep(); | ||
578 | dev_dbg(&priv->spi->dev, "trying to set channel\n"); | ||
579 | |||
580 | BUG_ON(page != 0); | ||
581 | BUG_ON(channel < CC2520_MINCHANNEL); | ||
582 | BUG_ON(channel > CC2520_MAXCHANNEL); | ||
583 | |||
584 | ret = cc2520_write_register(priv, CC2520_FREQCTRL, | ||
585 | 11 + 5*(channel - 11)); | ||
586 | |||
587 | return ret; | ||
588 | } | ||
589 | |||
590 | static int | ||
591 | cc2520_filter(struct ieee802154_dev *dev, | ||
592 | struct ieee802154_hw_addr_filt *filt, unsigned long changed) | ||
593 | { | ||
594 | struct cc2520_private *priv = dev->priv; | ||
595 | |||
596 | if (changed & IEEE802515_AFILT_PANID_CHANGED) { | ||
597 | u16 panid = le16_to_cpu(filt->pan_id); | ||
598 | |||
599 | dev_vdbg(&priv->spi->dev, | ||
600 | "cc2520_filter called for pan id\n"); | ||
601 | cc2520_write_ram(priv, CC2520RAM_PANID, | ||
602 | sizeof(panid), (u8 *)&panid); | ||
603 | } | ||
604 | |||
605 | if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) { | ||
606 | dev_vdbg(&priv->spi->dev, | ||
607 | "cc2520_filter called for IEEE addr\n"); | ||
608 | cc2520_write_ram(priv, CC2520RAM_IEEEADDR, | ||
609 | sizeof(filt->ieee_addr), | ||
610 | (u8 *)&filt->ieee_addr); | ||
611 | } | ||
612 | |||
613 | if (changed & IEEE802515_AFILT_SADDR_CHANGED) { | ||
614 | u16 addr = le16_to_cpu(filt->short_addr); | ||
615 | |||
616 | dev_vdbg(&priv->spi->dev, | ||
617 | "cc2520_filter called for saddr\n"); | ||
618 | cc2520_write_ram(priv, CC2520RAM_SHORTADDR, | ||
619 | sizeof(addr), (u8 *)&addr); | ||
620 | } | ||
621 | |||
622 | if (changed & IEEE802515_AFILT_PANC_CHANGED) { | ||
623 | dev_vdbg(&priv->spi->dev, | ||
624 | "cc2520_filter called for panc change\n"); | ||
625 | if (filt->pan_coord) | ||
626 | cc2520_write_register(priv, CC2520_FRMFILT0, 0x02); | ||
627 | else | ||
628 | cc2520_write_register(priv, CC2520_FRMFILT0, 0x00); | ||
629 | } | ||
630 | |||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static struct ieee802154_ops cc2520_ops = { | ||
635 | .owner = THIS_MODULE, | ||
636 | .start = cc2520_start, | ||
637 | .stop = cc2520_stop, | ||
638 | .xmit = cc2520_tx, | ||
639 | .ed = cc2520_ed, | ||
640 | .set_channel = cc2520_set_channel, | ||
641 | .set_hw_addr_filt = cc2520_filter, | ||
642 | }; | ||
643 | |||
644 | static int cc2520_register(struct cc2520_private *priv) | ||
645 | { | ||
646 | int ret = -ENOMEM; | ||
647 | |||
648 | priv->dev = ieee802154_alloc_device(sizeof(*priv), &cc2520_ops); | ||
649 | if (!priv->dev) | ||
650 | goto err_ret; | ||
651 | |||
652 | priv->dev->priv = priv; | ||
653 | priv->dev->parent = &priv->spi->dev; | ||
654 | priv->dev->extra_tx_headroom = 0; | ||
655 | |||
656 | /* We do support only 2.4 Ghz */ | ||
657 | priv->dev->phy->channels_supported[0] = 0x7FFF800; | ||
658 | priv->dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK; | ||
659 | |||
660 | dev_vdbg(&priv->spi->dev, "registered cc2520\n"); | ||
661 | ret = ieee802154_register_device(priv->dev); | ||
662 | if (ret) | ||
663 | goto err_free_device; | ||
664 | |||
665 | return 0; | ||
666 | |||
667 | err_free_device: | ||
668 | ieee802154_free_device(priv->dev); | ||
669 | err_ret: | ||
670 | return ret; | ||
671 | } | ||
672 | |||
673 | static void cc2520_fifop_irqwork(struct work_struct *work) | ||
674 | { | ||
675 | struct cc2520_private *priv | ||
676 | = container_of(work, struct cc2520_private, fifop_irqwork); | ||
677 | |||
678 | dev_dbg(&priv->spi->dev, "fifop interrupt received\n"); | ||
679 | |||
680 | if (gpio_get_value(priv->fifo_pin)) | ||
681 | cc2520_rx(priv); | ||
682 | else | ||
683 | dev_dbg(&priv->spi->dev, "rxfifo overflow\n"); | ||
684 | |||
685 | cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX); | ||
686 | cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX); | ||
687 | } | ||
688 | |||
689 | static irqreturn_t cc2520_fifop_isr(int irq, void *data) | ||
690 | { | ||
691 | struct cc2520_private *priv = data; | ||
692 | |||
693 | schedule_work(&priv->fifop_irqwork); | ||
694 | |||
695 | return IRQ_HANDLED; | ||
696 | } | ||
697 | |||
698 | static irqreturn_t cc2520_sfd_isr(int irq, void *data) | ||
699 | { | ||
700 | struct cc2520_private *priv = data; | ||
701 | unsigned long flags; | ||
702 | |||
703 | spin_lock_irqsave(&priv->lock, flags); | ||
704 | if (priv->is_tx) { | ||
705 | priv->is_tx = 0; | ||
706 | spin_unlock_irqrestore(&priv->lock, flags); | ||
707 | dev_dbg(&priv->spi->dev, "SFD for TX\n"); | ||
708 | complete(&priv->tx_complete); | ||
709 | } else { | ||
710 | spin_unlock_irqrestore(&priv->lock, flags); | ||
711 | dev_dbg(&priv->spi->dev, "SFD for RX\n"); | ||
712 | } | ||
713 | |||
714 | return IRQ_HANDLED; | ||
715 | } | ||
716 | |||
717 | static int cc2520_hw_init(struct cc2520_private *priv) | ||
718 | { | ||
719 | u8 status = 0, state = 0xff; | ||
720 | int ret; | ||
721 | int timeout = 100; | ||
722 | |||
723 | ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state); | ||
724 | if (ret) | ||
725 | goto err_ret; | ||
726 | |||
727 | if (state != STATE_IDLE) | ||
728 | return -EINVAL; | ||
729 | |||
730 | do { | ||
731 | ret = cc2520_get_status(priv, &status); | ||
732 | if (ret) | ||
733 | goto err_ret; | ||
734 | |||
735 | if (timeout-- <= 0) { | ||
736 | dev_err(&priv->spi->dev, "oscillator start failed!\n"); | ||
737 | return ret; | ||
738 | } | ||
739 | udelay(1); | ||
740 | } while (!(status & CC2520_STATUS_XOSC32M_STABLE)); | ||
741 | |||
742 | dev_vdbg(&priv->spi->dev, "oscillator brought up\n"); | ||
743 | |||
744 | /* Registers default value: section 28.1 in Datasheet */ | ||
745 | ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7); | ||
746 | if (ret) | ||
747 | goto err_ret; | ||
748 | |||
749 | ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A); | ||
750 | if (ret) | ||
751 | goto err_ret; | ||
752 | |||
753 | ret = cc2520_write_register(priv, CC2520_MDMCTRL0, 0x85); | ||
754 | if (ret) | ||
755 | goto err_ret; | ||
756 | |||
757 | ret = cc2520_write_register(priv, CC2520_MDMCTRL1, 0x14); | ||
758 | if (ret) | ||
759 | goto err_ret; | ||
760 | |||
761 | ret = cc2520_write_register(priv, CC2520_RXCTRL, 0x3f); | ||
762 | if (ret) | ||
763 | goto err_ret; | ||
764 | |||
765 | ret = cc2520_write_register(priv, CC2520_FSCTRL, 0x5a); | ||
766 | if (ret) | ||
767 | goto err_ret; | ||
768 | |||
769 | ret = cc2520_write_register(priv, CC2520_FSCAL1, 0x2b); | ||
770 | if (ret) | ||
771 | goto err_ret; | ||
772 | |||
773 | ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11); | ||
774 | if (ret) | ||
775 | goto err_ret; | ||
776 | |||
777 | ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10); | ||
778 | if (ret) | ||
779 | goto err_ret; | ||
780 | |||
781 | ret = cc2520_write_register(priv, CC2520_ADCTEST1, 0x0e); | ||
782 | if (ret) | ||
783 | goto err_ret; | ||
784 | |||
785 | ret = cc2520_write_register(priv, CC2520_ADCTEST2, 0x03); | ||
786 | if (ret) | ||
787 | goto err_ret; | ||
788 | |||
789 | ret = cc2520_write_register(priv, CC2520_FRMCTRL0, 0x60); | ||
790 | if (ret) | ||
791 | goto err_ret; | ||
792 | |||
793 | ret = cc2520_write_register(priv, CC2520_FRMCTRL1, 0x03); | ||
794 | if (ret) | ||
795 | goto err_ret; | ||
796 | |||
797 | ret = cc2520_write_register(priv, CC2520_FRMFILT0, 0x00); | ||
798 | if (ret) | ||
799 | goto err_ret; | ||
800 | |||
801 | ret = cc2520_write_register(priv, CC2520_FIFOPCTRL, 127); | ||
802 | if (ret) | ||
803 | goto err_ret; | ||
804 | |||
805 | return 0; | ||
806 | |||
807 | err_ret: | ||
808 | return ret; | ||
809 | } | ||
810 | |||
811 | static struct cc2520_platform_data * | ||
812 | cc2520_get_platform_data(struct spi_device *spi) | ||
813 | { | ||
814 | struct cc2520_platform_data *pdata; | ||
815 | struct device_node *np = spi->dev.of_node; | ||
816 | struct cc2520_private *priv = spi_get_drvdata(spi); | ||
817 | |||
818 | if (!np) | ||
819 | return spi->dev.platform_data; | ||
820 | |||
821 | pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL); | ||
822 | if (!pdata) | ||
823 | goto done; | ||
824 | |||
825 | pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0); | ||
826 | priv->fifo_pin = pdata->fifo; | ||
827 | |||
828 | pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0); | ||
829 | |||
830 | pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0); | ||
831 | pdata->cca = of_get_named_gpio(np, "cca-gpio", 0); | ||
832 | pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0); | ||
833 | pdata->reset = of_get_named_gpio(np, "reset-gpio", 0); | ||
834 | |||
835 | spi->dev.platform_data = pdata; | ||
836 | |||
837 | done: | ||
838 | return pdata; | ||
839 | } | ||
840 | |||
841 | static int cc2520_probe(struct spi_device *spi) | ||
842 | { | ||
843 | struct cc2520_private *priv; | ||
844 | struct pinctrl *pinctrl; | ||
845 | struct cc2520_platform_data *pdata; | ||
846 | int ret; | ||
847 | |||
848 | priv = devm_kzalloc(&spi->dev, | ||
849 | sizeof(struct cc2520_private), GFP_KERNEL); | ||
850 | if (!priv) { | ||
851 | ret = -ENOMEM; | ||
852 | goto err_ret; | ||
853 | } | ||
854 | |||
855 | spi_set_drvdata(spi, priv); | ||
856 | |||
857 | pinctrl = devm_pinctrl_get_select_default(&spi->dev); | ||
858 | if (IS_ERR(pinctrl)) | ||
859 | dev_warn(&spi->dev, | ||
860 | "pinctrl pins are not configured"); | ||
861 | |||
862 | pdata = cc2520_get_platform_data(spi); | ||
863 | if (!pdata) { | ||
864 | dev_err(&spi->dev, "no platform data\n"); | ||
865 | return -EINVAL; | ||
866 | } | ||
867 | |||
868 | priv->spi = spi; | ||
869 | |||
870 | priv->buf = devm_kzalloc(&spi->dev, | ||
871 | SPI_COMMAND_BUFFER, GFP_KERNEL); | ||
872 | if (!priv->buf) { | ||
873 | ret = -ENOMEM; | ||
874 | goto err_ret; | ||
875 | } | ||
876 | |||
877 | mutex_init(&priv->buffer_mutex); | ||
878 | INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork); | ||
879 | spin_lock_init(&priv->lock); | ||
880 | init_completion(&priv->tx_complete); | ||
881 | |||
882 | /* Request all the gpio's */ | ||
883 | if (!gpio_is_valid(pdata->fifo)) { | ||
884 | dev_err(&spi->dev, "fifo gpio is not valid\n"); | ||
885 | ret = -EINVAL; | ||
886 | goto err_hw_init; | ||
887 | } | ||
888 | |||
889 | ret = devm_gpio_request_one(&spi->dev, pdata->fifo, | ||
890 | GPIOF_IN, "fifo"); | ||
891 | if (ret) | ||
892 | goto err_hw_init; | ||
893 | |||
894 | if (!gpio_is_valid(pdata->cca)) { | ||
895 | dev_err(&spi->dev, "cca gpio is not valid\n"); | ||
896 | ret = -EINVAL; | ||
897 | goto err_hw_init; | ||
898 | } | ||
899 | |||
900 | ret = devm_gpio_request_one(&spi->dev, pdata->cca, | ||
901 | GPIOF_IN, "cca"); | ||
902 | if (ret) | ||
903 | goto err_hw_init; | ||
904 | |||
905 | if (!gpio_is_valid(pdata->fifop)) { | ||
906 | dev_err(&spi->dev, "fifop gpio is not valid\n"); | ||
907 | ret = -EINVAL; | ||
908 | goto err_hw_init; | ||
909 | } | ||
910 | |||
911 | ret = devm_gpio_request_one(&spi->dev, pdata->fifop, | ||
912 | GPIOF_IN, "fifop"); | ||
913 | if (ret) | ||
914 | goto err_hw_init; | ||
915 | |||
916 | if (!gpio_is_valid(pdata->sfd)) { | ||
917 | dev_err(&spi->dev, "sfd gpio is not valid\n"); | ||
918 | ret = -EINVAL; | ||
919 | goto err_hw_init; | ||
920 | } | ||
921 | |||
922 | ret = devm_gpio_request_one(&spi->dev, pdata->sfd, | ||
923 | GPIOF_IN, "sfd"); | ||
924 | if (ret) | ||
925 | goto err_hw_init; | ||
926 | |||
927 | if (!gpio_is_valid(pdata->reset)) { | ||
928 | dev_err(&spi->dev, "reset gpio is not valid\n"); | ||
929 | ret = -EINVAL; | ||
930 | goto err_hw_init; | ||
931 | } | ||
932 | |||
933 | ret = devm_gpio_request_one(&spi->dev, pdata->reset, | ||
934 | GPIOF_OUT_INIT_LOW, "reset"); | ||
935 | if (ret) | ||
936 | goto err_hw_init; | ||
937 | |||
938 | if (!gpio_is_valid(pdata->vreg)) { | ||
939 | dev_err(&spi->dev, "vreg gpio is not valid\n"); | ||
940 | ret = -EINVAL; | ||
941 | goto err_hw_init; | ||
942 | } | ||
943 | |||
944 | ret = devm_gpio_request_one(&spi->dev, pdata->vreg, | ||
945 | GPIOF_OUT_INIT_LOW, "vreg"); | ||
946 | if (ret) | ||
947 | goto err_hw_init; | ||
948 | |||
949 | |||
950 | gpio_set_value(pdata->vreg, HIGH); | ||
951 | usleep_range(100, 150); | ||
952 | |||
953 | gpio_set_value(pdata->reset, HIGH); | ||
954 | usleep_range(200, 250); | ||
955 | |||
956 | ret = cc2520_hw_init(priv); | ||
957 | if (ret) | ||
958 | goto err_hw_init; | ||
959 | |||
960 | /* Set up fifop interrupt */ | ||
961 | ret = devm_request_irq(&spi->dev, | ||
962 | gpio_to_irq(pdata->fifop), | ||
963 | cc2520_fifop_isr, | ||
964 | IRQF_TRIGGER_RISING, | ||
965 | dev_name(&spi->dev), | ||
966 | priv); | ||
967 | if (ret) { | ||
968 | dev_err(&spi->dev, "could not get fifop irq\n"); | ||
969 | goto err_hw_init; | ||
970 | } | ||
971 | |||
972 | /* Set up sfd interrupt */ | ||
973 | ret = devm_request_irq(&spi->dev, | ||
974 | gpio_to_irq(pdata->sfd), | ||
975 | cc2520_sfd_isr, | ||
976 | IRQF_TRIGGER_FALLING, | ||
977 | dev_name(&spi->dev), | ||
978 | priv); | ||
979 | if (ret) { | ||
980 | dev_err(&spi->dev, "could not get sfd irq\n"); | ||
981 | goto err_hw_init; | ||
982 | } | ||
983 | |||
984 | ret = cc2520_register(priv); | ||
985 | if (ret) | ||
986 | goto err_hw_init; | ||
987 | |||
988 | return 0; | ||
989 | |||
990 | err_hw_init: | ||
991 | mutex_destroy(&priv->buffer_mutex); | ||
992 | flush_work(&priv->fifop_irqwork); | ||
993 | |||
994 | err_ret: | ||
995 | return ret; | ||
996 | } | ||
997 | |||
998 | static int cc2520_remove(struct spi_device *spi) | ||
999 | { | ||
1000 | struct cc2520_private *priv = spi_get_drvdata(spi); | ||
1001 | |||
1002 | mutex_destroy(&priv->buffer_mutex); | ||
1003 | flush_work(&priv->fifop_irqwork); | ||
1004 | |||
1005 | ieee802154_unregister_device(priv->dev); | ||
1006 | ieee802154_free_device(priv->dev); | ||
1007 | |||
1008 | return 0; | ||
1009 | } | ||
1010 | |||
1011 | static const struct spi_device_id cc2520_ids[] = { | ||
1012 | {"cc2520", }, | ||
1013 | {}, | ||
1014 | }; | ||
1015 | MODULE_DEVICE_TABLE(spi, cc2520_ids); | ||
1016 | |||
1017 | static const struct of_device_id cc2520_of_ids[] = { | ||
1018 | {.compatible = "ti,cc2520", }, | ||
1019 | {}, | ||
1020 | }; | ||
1021 | MODULE_DEVICE_TABLE(of, cc2520_of_ids); | ||
1022 | |||
1023 | /* SPI driver structure */ | ||
1024 | static struct spi_driver cc2520_driver = { | ||
1025 | .driver = { | ||
1026 | .name = "cc2520", | ||
1027 | .bus = &spi_bus_type, | ||
1028 | .owner = THIS_MODULE, | ||
1029 | .of_match_table = of_match_ptr(cc2520_of_ids), | ||
1030 | }, | ||
1031 | .id_table = cc2520_ids, | ||
1032 | .probe = cc2520_probe, | ||
1033 | .remove = cc2520_remove, | ||
1034 | }; | ||
1035 | module_spi_driver(cc2520_driver); | ||
1036 | |||
1037 | MODULE_AUTHOR("Varka Bhadram <varkab@cdac.in>"); | ||
1038 | MODULE_DESCRIPTION("CC2520 Transceiver Driver"); | ||
1039 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/include/linux/spi/cc2520.h b/include/linux/spi/cc2520.h new file mode 100644 index 000000000000..85b8ee67e937 --- /dev/null +++ b/include/linux/spi/cc2520.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /* Header file for cc2520 radio driver | ||
2 | * | ||
3 | * Copyright (C) 2014 Varka Bhadram <varkab@cdac.in> | ||
4 | * Md.Jamal Mohiuddin <mjmohiuddin@cdac.in> | ||
5 | * P Sowjanya <sowjanyap@cdac.in> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #ifndef __CC2520_H | ||
15 | #define __CC2520_H | ||
16 | |||
17 | struct cc2520_platform_data { | ||
18 | int fifo; | ||
19 | int fifop; | ||
20 | int cca; | ||
21 | int sfd; | ||
22 | int reset; | ||
23 | int vreg; | ||
24 | }; | ||
25 | |||
26 | #endif | ||