]>
Commit | Line | Data |
---|---|---|
0da6bc8c VB |
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> | |
0da6bc8c | 22 | #include <linux/of_gpio.h> |
4ca24aca | 23 | #include <linux/ieee802154.h> |
0da6bc8c VB |
24 | |
25 | #include <net/mac802154.h> | |
5ad60d36 | 26 | #include <net/cfg802154.h> |
0da6bc8c VB |
27 | |
28 | #define SPI_COMMAND_BUFFER 3 | |
29 | #define HIGH 1 | |
30 | #define LOW 0 | |
31 | #define STATE_IDLE 0 | |
32 | #define RSSI_VALID 0 | |
33 | #define RSSI_OFFSET 78 | |
34 | ||
35 | #define CC2520_RAM_SIZE 640 | |
36 | #define CC2520_FIFO_SIZE 128 | |
37 | ||
38 | #define CC2520RAM_TXFIFO 0x100 | |
39 | #define CC2520RAM_RXFIFO 0x180 | |
40 | #define CC2520RAM_IEEEADDR 0x3EA | |
41 | #define CC2520RAM_PANID 0x3F2 | |
42 | #define CC2520RAM_SHORTADDR 0x3F4 | |
43 | ||
44 | #define CC2520_FREG_MASK 0x3F | |
45 | ||
46 | /* status byte values */ | |
908edc54 MJ |
47 | #define CC2520_STATUS_XOSC32M_STABLE BIT(7) |
48 | #define CC2520_STATUS_RSSI_VALID BIT(6) | |
49 | #define CC2520_STATUS_TX_UNDERFLOW BIT(3) | |
0da6bc8c VB |
50 | |
51 | /* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */ | |
52 | #define CC2520_MINCHANNEL 11 | |
53 | #define CC2520_MAXCHANNEL 26 | |
54 | #define CC2520_CHANNEL_SPACING 5 | |
55 | ||
56 | /* command strobes */ | |
57 | #define CC2520_CMD_SNOP 0x00 | |
58 | #define CC2520_CMD_IBUFLD 0x02 | |
59 | #define CC2520_CMD_SIBUFEX 0x03 | |
60 | #define CC2520_CMD_SSAMPLECCA 0x04 | |
61 | #define CC2520_CMD_SRES 0x0f | |
62 | #define CC2520_CMD_MEMORY_MASK 0x0f | |
63 | #define CC2520_CMD_MEMORY_READ 0x10 | |
64 | #define CC2520_CMD_MEMORY_WRITE 0x20 | |
65 | #define CC2520_CMD_RXBUF 0x30 | |
66 | #define CC2520_CMD_RXBUFCP 0x38 | |
67 | #define CC2520_CMD_RXBUFMOV 0x32 | |
68 | #define CC2520_CMD_TXBUF 0x3A | |
69 | #define CC2520_CMD_TXBUFCP 0x3E | |
70 | #define CC2520_CMD_RANDOM 0x3C | |
71 | #define CC2520_CMD_SXOSCON 0x40 | |
72 | #define CC2520_CMD_STXCAL 0x41 | |
73 | #define CC2520_CMD_SRXON 0x42 | |
74 | #define CC2520_CMD_STXON 0x43 | |
75 | #define CC2520_CMD_STXONCCA 0x44 | |
76 | #define CC2520_CMD_SRFOFF 0x45 | |
77 | #define CC2520_CMD_SXOSCOFF 0x46 | |
78 | #define CC2520_CMD_SFLUSHRX 0x47 | |
79 | #define CC2520_CMD_SFLUSHTX 0x48 | |
80 | #define CC2520_CMD_SACK 0x49 | |
81 | #define CC2520_CMD_SACKPEND 0x4A | |
82 | #define CC2520_CMD_SNACK 0x4B | |
83 | #define CC2520_CMD_SRXMASKBITSET 0x4C | |
84 | #define CC2520_CMD_SRXMASKBITCLR 0x4D | |
85 | #define CC2520_CMD_RXMASKAND 0x4E | |
86 | #define CC2520_CMD_RXMASKOR 0x4F | |
87 | #define CC2520_CMD_MEMCP 0x50 | |
88 | #define CC2520_CMD_MEMCPR 0x52 | |
89 | #define CC2520_CMD_MEMXCP 0x54 | |
90 | #define CC2520_CMD_MEMXWR 0x56 | |
91 | #define CC2520_CMD_BCLR 0x58 | |
92 | #define CC2520_CMD_BSET 0x59 | |
93 | #define CC2520_CMD_CTR_UCTR 0x60 | |
94 | #define CC2520_CMD_CBCMAC 0x64 | |
95 | #define CC2520_CMD_UCBCMAC 0x66 | |
96 | #define CC2520_CMD_CCM 0x68 | |
97 | #define CC2520_CMD_UCCM 0x6A | |
98 | #define CC2520_CMD_ECB 0x70 | |
99 | #define CC2520_CMD_ECBO 0x72 | |
100 | #define CC2520_CMD_ECBX 0x74 | |
101 | #define CC2520_CMD_INC 0x78 | |
102 | #define CC2520_CMD_ABORT 0x7F | |
103 | #define CC2520_CMD_REGISTER_READ 0x80 | |
104 | #define CC2520_CMD_REGISTER_WRITE 0xC0 | |
105 | ||
106 | /* status registers */ | |
107 | #define CC2520_CHIPID 0x40 | |
108 | #define CC2520_VERSION 0x42 | |
109 | #define CC2520_EXTCLOCK 0x44 | |
110 | #define CC2520_MDMCTRL0 0x46 | |
111 | #define CC2520_MDMCTRL1 0x47 | |
112 | #define CC2520_FREQEST 0x48 | |
113 | #define CC2520_RXCTRL 0x4A | |
114 | #define CC2520_FSCTRL 0x4C | |
115 | #define CC2520_FSCAL0 0x4E | |
116 | #define CC2520_FSCAL1 0x4F | |
117 | #define CC2520_FSCAL2 0x50 | |
118 | #define CC2520_FSCAL3 0x51 | |
119 | #define CC2520_AGCCTRL0 0x52 | |
120 | #define CC2520_AGCCTRL1 0x53 | |
121 | #define CC2520_AGCCTRL2 0x54 | |
122 | #define CC2520_AGCCTRL3 0x55 | |
123 | #define CC2520_ADCTEST0 0x56 | |
124 | #define CC2520_ADCTEST1 0x57 | |
125 | #define CC2520_ADCTEST2 0x58 | |
126 | #define CC2520_MDMTEST0 0x5A | |
127 | #define CC2520_MDMTEST1 0x5B | |
128 | #define CC2520_DACTEST0 0x5C | |
129 | #define CC2520_DACTEST1 0x5D | |
130 | #define CC2520_ATEST 0x5E | |
131 | #define CC2520_DACTEST2 0x5F | |
132 | #define CC2520_PTEST0 0x60 | |
133 | #define CC2520_PTEST1 0x61 | |
134 | #define CC2520_RESERVED 0x62 | |
135 | #define CC2520_DPUBIST 0x7A | |
136 | #define CC2520_ACTBIST 0x7C | |
137 | #define CC2520_RAMBIST 0x7E | |
138 | ||
139 | /* frame registers */ | |
140 | #define CC2520_FRMFILT0 0x00 | |
141 | #define CC2520_FRMFILT1 0x01 | |
142 | #define CC2520_SRCMATCH 0x02 | |
143 | #define CC2520_SRCSHORTEN0 0x04 | |
144 | #define CC2520_SRCSHORTEN1 0x05 | |
145 | #define CC2520_SRCSHORTEN2 0x06 | |
146 | #define CC2520_SRCEXTEN0 0x08 | |
147 | #define CC2520_SRCEXTEN1 0x09 | |
148 | #define CC2520_SRCEXTEN2 0x0A | |
149 | #define CC2520_FRMCTRL0 0x0C | |
150 | #define CC2520_FRMCTRL1 0x0D | |
151 | #define CC2520_RXENABLE0 0x0E | |
152 | #define CC2520_RXENABLE1 0x0F | |
153 | #define CC2520_EXCFLAG0 0x10 | |
154 | #define CC2520_EXCFLAG1 0x11 | |
155 | #define CC2520_EXCFLAG2 0x12 | |
156 | #define CC2520_EXCMASKA0 0x14 | |
157 | #define CC2520_EXCMASKA1 0x15 | |
158 | #define CC2520_EXCMASKA2 0x16 | |
159 | #define CC2520_EXCMASKB0 0x18 | |
160 | #define CC2520_EXCMASKB1 0x19 | |
161 | #define CC2520_EXCMASKB2 0x1A | |
162 | #define CC2520_EXCBINDX0 0x1C | |
163 | #define CC2520_EXCBINDX1 0x1D | |
164 | #define CC2520_EXCBINDY0 0x1E | |
165 | #define CC2520_EXCBINDY1 0x1F | |
166 | #define CC2520_GPIOCTRL0 0x20 | |
167 | #define CC2520_GPIOCTRL1 0x21 | |
168 | #define CC2520_GPIOCTRL2 0x22 | |
169 | #define CC2520_GPIOCTRL3 0x23 | |
170 | #define CC2520_GPIOCTRL4 0x24 | |
171 | #define CC2520_GPIOCTRL5 0x25 | |
172 | #define CC2520_GPIOPOLARITY 0x26 | |
173 | #define CC2520_GPIOCTRL 0x28 | |
174 | #define CC2520_DPUCON 0x2A | |
175 | #define CC2520_DPUSTAT 0x2C | |
176 | #define CC2520_FREQCTRL 0x2E | |
177 | #define CC2520_FREQTUNE 0x2F | |
178 | #define CC2520_TXPOWER 0x30 | |
179 | #define CC2520_TXCTRL 0x31 | |
180 | #define CC2520_FSMSTAT0 0x32 | |
181 | #define CC2520_FSMSTAT1 0x33 | |
182 | #define CC2520_FIFOPCTRL 0x34 | |
183 | #define CC2520_FSMCTRL 0x35 | |
184 | #define CC2520_CCACTRL0 0x36 | |
185 | #define CC2520_CCACTRL1 0x37 | |
186 | #define CC2520_RSSI 0x38 | |
187 | #define CC2520_RSSISTAT 0x39 | |
188 | #define CC2520_RXFIRST 0x3C | |
189 | #define CC2520_RXFIFOCNT 0x3E | |
190 | #define CC2520_TXFIFOCNT 0x3F | |
191 | ||
192 | /* Driver private information */ | |
193 | struct cc2520_private { | |
194 | struct spi_device *spi; /* SPI device structure */ | |
5a504397 | 195 | struct ieee802154_hw *hw; /* IEEE-802.15.4 device */ |
0da6bc8c VB |
196 | u8 *buf; /* SPI TX/Rx data buffer */ |
197 | struct mutex buffer_mutex; /* SPI buffer mutex */ | |
198 | bool is_tx; /* Flag for sync b/w Tx and Rx */ | |
199 | int fifo_pin; /* FIFO GPIO pin number */ | |
200 | struct work_struct fifop_irqwork;/* Workqueue for FIFOP */ | |
201 | spinlock_t lock; /* Lock for is_tx*/ | |
202 | struct completion tx_complete; /* Work completion for Tx */ | |
203 | }; | |
204 | ||
205 | /* Generic Functions */ | |
206 | static int | |
207 | cc2520_cmd_strobe(struct cc2520_private *priv, u8 cmd) | |
208 | { | |
209 | int ret; | |
210 | u8 status = 0xff; | |
211 | struct spi_message msg; | |
212 | struct spi_transfer xfer = { | |
213 | .len = 0, | |
214 | .tx_buf = priv->buf, | |
215 | .rx_buf = priv->buf, | |
216 | }; | |
217 | ||
218 | spi_message_init(&msg); | |
219 | spi_message_add_tail(&xfer, &msg); | |
220 | ||
221 | mutex_lock(&priv->buffer_mutex); | |
222 | priv->buf[xfer.len++] = cmd; | |
223 | dev_vdbg(&priv->spi->dev, | |
224 | "command strobe buf[0] = %02x\n", | |
225 | priv->buf[0]); | |
226 | ||
227 | ret = spi_sync(priv->spi, &msg); | |
228 | if (!ret) | |
229 | status = priv->buf[0]; | |
230 | dev_vdbg(&priv->spi->dev, | |
231 | "buf[0] = %02x\n", priv->buf[0]); | |
232 | mutex_unlock(&priv->buffer_mutex); | |
233 | ||
234 | return ret; | |
235 | } | |
236 | ||
237 | static int | |
238 | cc2520_get_status(struct cc2520_private *priv, u8 *status) | |
239 | { | |
240 | int ret; | |
241 | struct spi_message msg; | |
242 | struct spi_transfer xfer = { | |
243 | .len = 0, | |
244 | .tx_buf = priv->buf, | |
245 | .rx_buf = priv->buf, | |
246 | }; | |
247 | ||
248 | spi_message_init(&msg); | |
249 | spi_message_add_tail(&xfer, &msg); | |
250 | ||
251 | mutex_lock(&priv->buffer_mutex); | |
252 | priv->buf[xfer.len++] = CC2520_CMD_SNOP; | |
253 | dev_vdbg(&priv->spi->dev, | |
254 | "get status command buf[0] = %02x\n", priv->buf[0]); | |
255 | ||
256 | ret = spi_sync(priv->spi, &msg); | |
257 | if (!ret) | |
258 | *status = priv->buf[0]; | |
259 | dev_vdbg(&priv->spi->dev, | |
260 | "buf[0] = %02x\n", priv->buf[0]); | |
261 | mutex_unlock(&priv->buffer_mutex); | |
262 | ||
263 | return ret; | |
264 | } | |
265 | ||
266 | static int | |
267 | cc2520_write_register(struct cc2520_private *priv, u8 reg, u8 value) | |
268 | { | |
269 | int status; | |
270 | struct spi_message msg; | |
271 | struct spi_transfer xfer = { | |
272 | .len = 0, | |
273 | .tx_buf = priv->buf, | |
274 | .rx_buf = priv->buf, | |
275 | }; | |
276 | ||
277 | spi_message_init(&msg); | |
278 | spi_message_add_tail(&xfer, &msg); | |
279 | ||
280 | mutex_lock(&priv->buffer_mutex); | |
281 | ||
282 | if (reg <= CC2520_FREG_MASK) { | |
283 | priv->buf[xfer.len++] = CC2520_CMD_REGISTER_WRITE | reg; | |
284 | priv->buf[xfer.len++] = value; | |
285 | } else { | |
286 | priv->buf[xfer.len++] = CC2520_CMD_MEMORY_WRITE; | |
287 | priv->buf[xfer.len++] = reg; | |
288 | priv->buf[xfer.len++] = value; | |
289 | } | |
290 | status = spi_sync(priv->spi, &msg); | |
291 | if (msg.status) | |
292 | status = msg.status; | |
293 | ||
294 | mutex_unlock(&priv->buffer_mutex); | |
295 | ||
296 | return status; | |
297 | } | |
298 | ||
299 | static int | |
300 | cc2520_write_ram(struct cc2520_private *priv, u16 reg, u8 len, u8 *data) | |
301 | { | |
302 | int status; | |
303 | struct spi_message msg; | |
304 | struct spi_transfer xfer_head = { | |
305 | .len = 0, | |
306 | .tx_buf = priv->buf, | |
307 | .rx_buf = priv->buf, | |
308 | }; | |
309 | ||
310 | struct spi_transfer xfer_buf = { | |
311 | .len = len, | |
312 | .tx_buf = data, | |
313 | }; | |
314 | ||
315 | mutex_lock(&priv->buffer_mutex); | |
316 | priv->buf[xfer_head.len++] = (CC2520_CMD_MEMORY_WRITE | | |
317 | ((reg >> 8) & 0xff)); | |
318 | priv->buf[xfer_head.len++] = reg & 0xff; | |
319 | ||
320 | spi_message_init(&msg); | |
321 | spi_message_add_tail(&xfer_head, &msg); | |
322 | spi_message_add_tail(&xfer_buf, &msg); | |
323 | ||
324 | status = spi_sync(priv->spi, &msg); | |
325 | dev_dbg(&priv->spi->dev, "spi status = %d\n", status); | |
326 | if (msg.status) | |
327 | status = msg.status; | |
328 | ||
329 | mutex_unlock(&priv->buffer_mutex); | |
330 | return status; | |
331 | } | |
332 | ||
333 | static int | |
334 | cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data) | |
335 | { | |
336 | int status; | |
337 | struct spi_message msg; | |
338 | struct spi_transfer xfer1 = { | |
339 | .len = 0, | |
340 | .tx_buf = priv->buf, | |
341 | .rx_buf = priv->buf, | |
342 | }; | |
343 | ||
344 | struct spi_transfer xfer2 = { | |
345 | .len = 1, | |
346 | .rx_buf = data, | |
347 | }; | |
348 | ||
349 | spi_message_init(&msg); | |
350 | spi_message_add_tail(&xfer1, &msg); | |
351 | spi_message_add_tail(&xfer2, &msg); | |
352 | ||
353 | mutex_lock(&priv->buffer_mutex); | |
354 | priv->buf[xfer1.len++] = CC2520_CMD_MEMORY_READ; | |
355 | priv->buf[xfer1.len++] = reg; | |
356 | ||
357 | status = spi_sync(priv->spi, &msg); | |
358 | dev_dbg(&priv->spi->dev, | |
359 | "spi status = %d\n", status); | |
360 | if (msg.status) | |
361 | status = msg.status; | |
362 | ||
363 | mutex_unlock(&priv->buffer_mutex); | |
364 | ||
365 | return status; | |
366 | } | |
367 | ||
368 | static int | |
369 | cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len) | |
370 | { | |
371 | int status; | |
372 | ||
373 | /* length byte must include FCS even | |
374 | * if it is calculated in the hardware | |
375 | */ | |
376 | int len_byte = len + 2; | |
377 | ||
378 | struct spi_message msg; | |
379 | ||
380 | struct spi_transfer xfer_head = { | |
381 | .len = 0, | |
382 | .tx_buf = priv->buf, | |
383 | .rx_buf = priv->buf, | |
384 | }; | |
385 | struct spi_transfer xfer_len = { | |
386 | .len = 1, | |
387 | .tx_buf = &len_byte, | |
388 | }; | |
389 | struct spi_transfer xfer_buf = { | |
390 | .len = len, | |
391 | .tx_buf = data, | |
392 | }; | |
393 | ||
394 | spi_message_init(&msg); | |
395 | spi_message_add_tail(&xfer_head, &msg); | |
396 | spi_message_add_tail(&xfer_len, &msg); | |
397 | spi_message_add_tail(&xfer_buf, &msg); | |
398 | ||
399 | mutex_lock(&priv->buffer_mutex); | |
400 | priv->buf[xfer_head.len++] = CC2520_CMD_TXBUF; | |
401 | dev_vdbg(&priv->spi->dev, | |
402 | "TX_FIFO cmd buf[0] = %02x\n", priv->buf[0]); | |
403 | ||
404 | status = spi_sync(priv->spi, &msg); | |
405 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | |
406 | if (msg.status) | |
407 | status = msg.status; | |
408 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | |
409 | dev_vdbg(&priv->spi->dev, "buf[0] = %02x\n", priv->buf[0]); | |
410 | mutex_unlock(&priv->buffer_mutex); | |
411 | ||
412 | return status; | |
413 | } | |
414 | ||
415 | static int | |
416 | cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len, u8 *lqi) | |
417 | { | |
418 | int status; | |
419 | struct spi_message msg; | |
420 | ||
421 | struct spi_transfer xfer_head = { | |
422 | .len = 0, | |
423 | .tx_buf = priv->buf, | |
424 | .rx_buf = priv->buf, | |
425 | }; | |
426 | struct spi_transfer xfer_buf = { | |
427 | .len = len, | |
428 | .rx_buf = data, | |
429 | }; | |
430 | ||
431 | spi_message_init(&msg); | |
432 | spi_message_add_tail(&xfer_head, &msg); | |
433 | spi_message_add_tail(&xfer_buf, &msg); | |
434 | ||
435 | mutex_lock(&priv->buffer_mutex); | |
436 | priv->buf[xfer_head.len++] = CC2520_CMD_RXBUF; | |
437 | ||
438 | dev_vdbg(&priv->spi->dev, "read rxfifo buf[0] = %02x\n", priv->buf[0]); | |
439 | dev_vdbg(&priv->spi->dev, "buf[1] = %02x\n", priv->buf[1]); | |
440 | ||
441 | status = spi_sync(priv->spi, &msg); | |
442 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | |
443 | if (msg.status) | |
444 | status = msg.status; | |
445 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | |
446 | dev_vdbg(&priv->spi->dev, | |
447 | "return status buf[0] = %02x\n", priv->buf[0]); | |
448 | dev_vdbg(&priv->spi->dev, "length buf[1] = %02x\n", priv->buf[1]); | |
449 | ||
450 | mutex_unlock(&priv->buffer_mutex); | |
451 | ||
452 | return status; | |
453 | } | |
454 | ||
5a504397 | 455 | static int cc2520_start(struct ieee802154_hw *hw) |
0da6bc8c | 456 | { |
5a504397 | 457 | return cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRXON); |
0da6bc8c VB |
458 | } |
459 | ||
5a504397 | 460 | static void cc2520_stop(struct ieee802154_hw *hw) |
0da6bc8c | 461 | { |
5a504397 | 462 | cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRFOFF); |
0da6bc8c VB |
463 | } |
464 | ||
465 | static int | |
5a504397 | 466 | cc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb) |
0da6bc8c | 467 | { |
5a504397 | 468 | struct cc2520_private *priv = hw->priv; |
0da6bc8c VB |
469 | unsigned long flags; |
470 | int rc; | |
471 | u8 status = 0; | |
472 | ||
473 | rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); | |
474 | if (rc) | |
475 | goto err_tx; | |
476 | ||
477 | rc = cc2520_write_txfifo(priv, skb->data, skb->len); | |
478 | if (rc) | |
479 | goto err_tx; | |
480 | ||
481 | rc = cc2520_get_status(priv, &status); | |
482 | if (rc) | |
483 | goto err_tx; | |
484 | ||
485 | if (status & CC2520_STATUS_TX_UNDERFLOW) { | |
486 | dev_err(&priv->spi->dev, "cc2520 tx underflow exception\n"); | |
487 | goto err_tx; | |
488 | } | |
489 | ||
490 | spin_lock_irqsave(&priv->lock, flags); | |
491 | BUG_ON(priv->is_tx); | |
492 | priv->is_tx = 1; | |
493 | spin_unlock_irqrestore(&priv->lock, flags); | |
494 | ||
495 | rc = cc2520_cmd_strobe(priv, CC2520_CMD_STXONCCA); | |
496 | if (rc) | |
497 | goto err; | |
498 | ||
499 | rc = wait_for_completion_interruptible(&priv->tx_complete); | |
500 | if (rc < 0) | |
501 | goto err; | |
502 | ||
503 | cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); | |
504 | cc2520_cmd_strobe(priv, CC2520_CMD_SRXON); | |
505 | ||
506 | return rc; | |
507 | err: | |
508 | spin_lock_irqsave(&priv->lock, flags); | |
509 | priv->is_tx = 0; | |
510 | spin_unlock_irqrestore(&priv->lock, flags); | |
511 | err_tx: | |
512 | return rc; | |
513 | } | |
514 | ||
0da6bc8c VB |
515 | static int cc2520_rx(struct cc2520_private *priv) |
516 | { | |
517 | u8 len = 0, lqi = 0, bytes = 1; | |
518 | struct sk_buff *skb; | |
519 | ||
520 | cc2520_read_rxfifo(priv, &len, bytes, &lqi); | |
521 | ||
522 | if (len < 2 || len > IEEE802154_MTU) | |
523 | return -EINVAL; | |
524 | ||
61a22814 | 525 | skb = dev_alloc_skb(len); |
0da6bc8c VB |
526 | if (!skb) |
527 | return -ENOMEM; | |
528 | ||
529 | if (cc2520_read_rxfifo(priv, skb_put(skb, len), len, &lqi)) { | |
530 | dev_dbg(&priv->spi->dev, "frame reception failed\n"); | |
531 | kfree_skb(skb); | |
532 | return -EINVAL; | |
533 | } | |
534 | ||
535 | skb_trim(skb, skb->len - 2); | |
536 | ||
5a504397 | 537 | ieee802154_rx_irqsafe(priv->hw, skb, lqi); |
0da6bc8c VB |
538 | |
539 | dev_vdbg(&priv->spi->dev, "RXFIFO: %x %x\n", len, lqi); | |
540 | ||
541 | return 0; | |
542 | } | |
543 | ||
544 | static int | |
5a504397 | 545 | cc2520_ed(struct ieee802154_hw *hw, u8 *level) |
0da6bc8c | 546 | { |
5a504397 | 547 | struct cc2520_private *priv = hw->priv; |
0da6bc8c VB |
548 | u8 status = 0xff; |
549 | u8 rssi; | |
550 | int ret; | |
551 | ||
3251ca33 | 552 | ret = cc2520_read_register(priv, CC2520_RSSISTAT, &status); |
0da6bc8c VB |
553 | if (ret) |
554 | return ret; | |
555 | ||
556 | if (status != RSSI_VALID) | |
557 | return -EINVAL; | |
558 | ||
3251ca33 | 559 | ret = cc2520_read_register(priv, CC2520_RSSI, &rssi); |
0da6bc8c VB |
560 | if (ret) |
561 | return ret; | |
562 | ||
563 | /* level = RSSI(rssi) - OFFSET [dBm] : offset is 76dBm */ | |
564 | *level = rssi - RSSI_OFFSET; | |
565 | ||
566 | return 0; | |
567 | } | |
568 | ||
569 | static int | |
e37d2ec8 | 570 | cc2520_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) |
0da6bc8c | 571 | { |
5a504397 | 572 | struct cc2520_private *priv = hw->priv; |
0da6bc8c VB |
573 | int ret; |
574 | ||
0da6bc8c VB |
575 | dev_dbg(&priv->spi->dev, "trying to set channel\n"); |
576 | ||
577 | BUG_ON(page != 0); | |
578 | BUG_ON(channel < CC2520_MINCHANNEL); | |
579 | BUG_ON(channel > CC2520_MAXCHANNEL); | |
580 | ||
581 | ret = cc2520_write_register(priv, CC2520_FREQCTRL, | |
582 | 11 + 5*(channel - 11)); | |
583 | ||
584 | return ret; | |
585 | } | |
586 | ||
587 | static int | |
5a504397 | 588 | cc2520_filter(struct ieee802154_hw *hw, |
0da6bc8c VB |
589 | struct ieee802154_hw_addr_filt *filt, unsigned long changed) |
590 | { | |
5a504397 | 591 | struct cc2520_private *priv = hw->priv; |
0da6bc8c | 592 | |
57205c14 | 593 | if (changed & IEEE802154_AFILT_PANID_CHANGED) { |
0da6bc8c VB |
594 | u16 panid = le16_to_cpu(filt->pan_id); |
595 | ||
596 | dev_vdbg(&priv->spi->dev, | |
597 | "cc2520_filter called for pan id\n"); | |
598 | cc2520_write_ram(priv, CC2520RAM_PANID, | |
599 | sizeof(panid), (u8 *)&panid); | |
600 | } | |
601 | ||
57205c14 | 602 | if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { |
0da6bc8c VB |
603 | dev_vdbg(&priv->spi->dev, |
604 | "cc2520_filter called for IEEE addr\n"); | |
605 | cc2520_write_ram(priv, CC2520RAM_IEEEADDR, | |
606 | sizeof(filt->ieee_addr), | |
607 | (u8 *)&filt->ieee_addr); | |
608 | } | |
609 | ||
57205c14 | 610 | if (changed & IEEE802154_AFILT_SADDR_CHANGED) { |
0da6bc8c VB |
611 | u16 addr = le16_to_cpu(filt->short_addr); |
612 | ||
613 | dev_vdbg(&priv->spi->dev, | |
614 | "cc2520_filter called for saddr\n"); | |
615 | cc2520_write_ram(priv, CC2520RAM_SHORTADDR, | |
616 | sizeof(addr), (u8 *)&addr); | |
617 | } | |
618 | ||
57205c14 | 619 | if (changed & IEEE802154_AFILT_PANC_CHANGED) { |
0da6bc8c VB |
620 | dev_vdbg(&priv->spi->dev, |
621 | "cc2520_filter called for panc change\n"); | |
622 | if (filt->pan_coord) | |
623 | cc2520_write_register(priv, CC2520_FRMFILT0, 0x02); | |
624 | else | |
625 | cc2520_write_register(priv, CC2520_FRMFILT0, 0x00); | |
626 | } | |
627 | ||
628 | return 0; | |
629 | } | |
630 | ||
16301861 | 631 | static const struct ieee802154_ops cc2520_ops = { |
0da6bc8c VB |
632 | .owner = THIS_MODULE, |
633 | .start = cc2520_start, | |
634 | .stop = cc2520_stop, | |
ed0a5dce | 635 | .xmit_sync = cc2520_tx, |
0da6bc8c VB |
636 | .ed = cc2520_ed, |
637 | .set_channel = cc2520_set_channel, | |
638 | .set_hw_addr_filt = cc2520_filter, | |
639 | }; | |
640 | ||
641 | static int cc2520_register(struct cc2520_private *priv) | |
642 | { | |
643 | int ret = -ENOMEM; | |
644 | ||
5a504397 AA |
645 | priv->hw = ieee802154_alloc_hw(sizeof(*priv), &cc2520_ops); |
646 | if (!priv->hw) | |
0da6bc8c VB |
647 | goto err_ret; |
648 | ||
5a504397 AA |
649 | priv->hw->priv = priv; |
650 | priv->hw->parent = &priv->spi->dev; | |
651 | priv->hw->extra_tx_headroom = 0; | |
7c118c1a | 652 | priv->hw->vif_data_size = sizeof(*priv); |
7fc1b2d5 | 653 | ieee802154_random_extended_addr(&priv->hw->phy->perm_extended_addr); |
0da6bc8c VB |
654 | |
655 | /* We do support only 2.4 Ghz */ | |
5a504397 | 656 | priv->hw->phy->channels_supported[0] = 0x7FFF800; |
c8fc84ed AA |
657 | priv->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK | |
658 | IEEE802154_HW_AFILT; | |
0da6bc8c VB |
659 | |
660 | dev_vdbg(&priv->spi->dev, "registered cc2520\n"); | |
5a504397 | 661 | ret = ieee802154_register_hw(priv->hw); |
0da6bc8c VB |
662 | if (ret) |
663 | goto err_free_device; | |
664 | ||
665 | return 0; | |
666 | ||
667 | err_free_device: | |
5a504397 | 668 | ieee802154_free_hw(priv->hw); |
0da6bc8c VB |
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; | |
0da6bc8c VB |
844 | struct cc2520_platform_data *pdata; |
845 | int ret; | |
846 | ||
5eb9f8ca | 847 | priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); |
f50f1c37 VB |
848 | if (!priv) |
849 | return -ENOMEM; | |
0da6bc8c VB |
850 | |
851 | spi_set_drvdata(spi, priv); | |
852 | ||
0da6bc8c VB |
853 | pdata = cc2520_get_platform_data(spi); |
854 | if (!pdata) { | |
855 | dev_err(&spi->dev, "no platform data\n"); | |
856 | return -EINVAL; | |
857 | } | |
858 | ||
859 | priv->spi = spi; | |
860 | ||
861 | priv->buf = devm_kzalloc(&spi->dev, | |
862 | SPI_COMMAND_BUFFER, GFP_KERNEL); | |
f50f1c37 VB |
863 | if (!priv->buf) |
864 | return -ENOMEM; | |
0da6bc8c VB |
865 | |
866 | mutex_init(&priv->buffer_mutex); | |
867 | INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork); | |
868 | spin_lock_init(&priv->lock); | |
869 | init_completion(&priv->tx_complete); | |
870 | ||
871 | /* Request all the gpio's */ | |
872 | if (!gpio_is_valid(pdata->fifo)) { | |
873 | dev_err(&spi->dev, "fifo gpio is not valid\n"); | |
874 | ret = -EINVAL; | |
875 | goto err_hw_init; | |
876 | } | |
877 | ||
878 | ret = devm_gpio_request_one(&spi->dev, pdata->fifo, | |
879 | GPIOF_IN, "fifo"); | |
880 | if (ret) | |
881 | goto err_hw_init; | |
882 | ||
883 | if (!gpio_is_valid(pdata->cca)) { | |
884 | dev_err(&spi->dev, "cca gpio is not valid\n"); | |
885 | ret = -EINVAL; | |
886 | goto err_hw_init; | |
887 | } | |
888 | ||
889 | ret = devm_gpio_request_one(&spi->dev, pdata->cca, | |
890 | GPIOF_IN, "cca"); | |
891 | if (ret) | |
892 | goto err_hw_init; | |
893 | ||
894 | if (!gpio_is_valid(pdata->fifop)) { | |
895 | dev_err(&spi->dev, "fifop gpio is not valid\n"); | |
896 | ret = -EINVAL; | |
897 | goto err_hw_init; | |
898 | } | |
899 | ||
900 | ret = devm_gpio_request_one(&spi->dev, pdata->fifop, | |
901 | GPIOF_IN, "fifop"); | |
902 | if (ret) | |
903 | goto err_hw_init; | |
904 | ||
905 | if (!gpio_is_valid(pdata->sfd)) { | |
906 | dev_err(&spi->dev, "sfd gpio is not valid\n"); | |
907 | ret = -EINVAL; | |
908 | goto err_hw_init; | |
909 | } | |
910 | ||
911 | ret = devm_gpio_request_one(&spi->dev, pdata->sfd, | |
912 | GPIOF_IN, "sfd"); | |
913 | if (ret) | |
914 | goto err_hw_init; | |
915 | ||
916 | if (!gpio_is_valid(pdata->reset)) { | |
917 | dev_err(&spi->dev, "reset gpio is not valid\n"); | |
918 | ret = -EINVAL; | |
919 | goto err_hw_init; | |
920 | } | |
921 | ||
922 | ret = devm_gpio_request_one(&spi->dev, pdata->reset, | |
923 | GPIOF_OUT_INIT_LOW, "reset"); | |
924 | if (ret) | |
925 | goto err_hw_init; | |
926 | ||
927 | if (!gpio_is_valid(pdata->vreg)) { | |
928 | dev_err(&spi->dev, "vreg gpio is not valid\n"); | |
929 | ret = -EINVAL; | |
930 | goto err_hw_init; | |
931 | } | |
932 | ||
933 | ret = devm_gpio_request_one(&spi->dev, pdata->vreg, | |
934 | GPIOF_OUT_INIT_LOW, "vreg"); | |
935 | if (ret) | |
936 | goto err_hw_init; | |
937 | ||
0da6bc8c VB |
938 | gpio_set_value(pdata->vreg, HIGH); |
939 | usleep_range(100, 150); | |
940 | ||
941 | gpio_set_value(pdata->reset, HIGH); | |
942 | usleep_range(200, 250); | |
943 | ||
944 | ret = cc2520_hw_init(priv); | |
945 | if (ret) | |
946 | goto err_hw_init; | |
947 | ||
948 | /* Set up fifop interrupt */ | |
949 | ret = devm_request_irq(&spi->dev, | |
950 | gpio_to_irq(pdata->fifop), | |
951 | cc2520_fifop_isr, | |
952 | IRQF_TRIGGER_RISING, | |
953 | dev_name(&spi->dev), | |
954 | priv); | |
955 | if (ret) { | |
956 | dev_err(&spi->dev, "could not get fifop irq\n"); | |
957 | goto err_hw_init; | |
958 | } | |
959 | ||
960 | /* Set up sfd interrupt */ | |
961 | ret = devm_request_irq(&spi->dev, | |
962 | gpio_to_irq(pdata->sfd), | |
963 | cc2520_sfd_isr, | |
964 | IRQF_TRIGGER_FALLING, | |
965 | dev_name(&spi->dev), | |
966 | priv); | |
967 | if (ret) { | |
968 | dev_err(&spi->dev, "could not get sfd irq\n"); | |
969 | goto err_hw_init; | |
970 | } | |
971 | ||
972 | ret = cc2520_register(priv); | |
973 | if (ret) | |
974 | goto err_hw_init; | |
975 | ||
976 | return 0; | |
977 | ||
978 | err_hw_init: | |
979 | mutex_destroy(&priv->buffer_mutex); | |
980 | flush_work(&priv->fifop_irqwork); | |
0da6bc8c VB |
981 | return ret; |
982 | } | |
983 | ||
984 | static int cc2520_remove(struct spi_device *spi) | |
985 | { | |
986 | struct cc2520_private *priv = spi_get_drvdata(spi); | |
987 | ||
988 | mutex_destroy(&priv->buffer_mutex); | |
989 | flush_work(&priv->fifop_irqwork); | |
990 | ||
5a504397 AA |
991 | ieee802154_unregister_hw(priv->hw); |
992 | ieee802154_free_hw(priv->hw); | |
0da6bc8c VB |
993 | |
994 | return 0; | |
995 | } | |
996 | ||
997 | static const struct spi_device_id cc2520_ids[] = { | |
998 | {"cc2520", }, | |
999 | {}, | |
1000 | }; | |
1001 | MODULE_DEVICE_TABLE(spi, cc2520_ids); | |
1002 | ||
1003 | static const struct of_device_id cc2520_of_ids[] = { | |
1004 | {.compatible = "ti,cc2520", }, | |
1005 | {}, | |
1006 | }; | |
1007 | MODULE_DEVICE_TABLE(of, cc2520_of_ids); | |
1008 | ||
1009 | /* SPI driver structure */ | |
1010 | static struct spi_driver cc2520_driver = { | |
1011 | .driver = { | |
1012 | .name = "cc2520", | |
1013 | .bus = &spi_bus_type, | |
1014 | .owner = THIS_MODULE, | |
1015 | .of_match_table = of_match_ptr(cc2520_of_ids), | |
1016 | }, | |
1017 | .id_table = cc2520_ids, | |
1018 | .probe = cc2520_probe, | |
1019 | .remove = cc2520_remove, | |
1020 | }; | |
1021 | module_spi_driver(cc2520_driver); | |
1022 | ||
1023 | MODULE_AUTHOR("Varka Bhadram <varkab@cdac.in>"); | |
1024 | MODULE_DESCRIPTION("CC2520 Transceiver Driver"); | |
1025 | MODULE_LICENSE("GPL v2"); |