Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
0da6bc8c VB |
2 | /* Driver for TI CC2520 802.15.4 Wireless-PAN Networking controller |
3 | * | |
4 | * Copyright (C) 2014 Varka Bhadram <varkab@cdac.in> | |
5 | * Md.Jamal Mohiuddin <mjmohiuddin@cdac.in> | |
6 | * P Sowjanya <sowjanyap@cdac.in> | |
0da6bc8c VB |
7 | */ |
8 | #include <linux/kernel.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/gpio.h> | |
11 | #include <linux/delay.h> | |
12 | #include <linux/spi/spi.h> | |
13 | #include <linux/spi/cc2520.h> | |
14 | #include <linux/workqueue.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/skbuff.h> | |
0da6bc8c | 17 | #include <linux/of_gpio.h> |
4ca24aca | 18 | #include <linux/ieee802154.h> |
59869ebf BC |
19 | #include <linux/crc-ccitt.h> |
20 | #include <asm/unaligned.h> | |
0da6bc8c VB |
21 | |
22 | #include <net/mac802154.h> | |
5ad60d36 | 23 | #include <net/cfg802154.h> |
0da6bc8c VB |
24 | |
25 | #define SPI_COMMAND_BUFFER 3 | |
26 | #define HIGH 1 | |
27 | #define LOW 0 | |
28 | #define STATE_IDLE 0 | |
29 | #define RSSI_VALID 0 | |
30 | #define RSSI_OFFSET 78 | |
31 | ||
32 | #define CC2520_RAM_SIZE 640 | |
33 | #define CC2520_FIFO_SIZE 128 | |
34 | ||
35 | #define CC2520RAM_TXFIFO 0x100 | |
36 | #define CC2520RAM_RXFIFO 0x180 | |
37 | #define CC2520RAM_IEEEADDR 0x3EA | |
38 | #define CC2520RAM_PANID 0x3F2 | |
39 | #define CC2520RAM_SHORTADDR 0x3F4 | |
40 | ||
41 | #define CC2520_FREG_MASK 0x3F | |
42 | ||
43 | /* status byte values */ | |
908edc54 MJ |
44 | #define CC2520_STATUS_XOSC32M_STABLE BIT(7) |
45 | #define CC2520_STATUS_RSSI_VALID BIT(6) | |
46 | #define CC2520_STATUS_TX_UNDERFLOW BIT(3) | |
0da6bc8c VB |
47 | |
48 | /* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */ | |
49 | #define CC2520_MINCHANNEL 11 | |
50 | #define CC2520_MAXCHANNEL 26 | |
51 | #define CC2520_CHANNEL_SPACING 5 | |
52 | ||
53 | /* command strobes */ | |
54 | #define CC2520_CMD_SNOP 0x00 | |
55 | #define CC2520_CMD_IBUFLD 0x02 | |
56 | #define CC2520_CMD_SIBUFEX 0x03 | |
57 | #define CC2520_CMD_SSAMPLECCA 0x04 | |
58 | #define CC2520_CMD_SRES 0x0f | |
59 | #define CC2520_CMD_MEMORY_MASK 0x0f | |
60 | #define CC2520_CMD_MEMORY_READ 0x10 | |
61 | #define CC2520_CMD_MEMORY_WRITE 0x20 | |
62 | #define CC2520_CMD_RXBUF 0x30 | |
63 | #define CC2520_CMD_RXBUFCP 0x38 | |
64 | #define CC2520_CMD_RXBUFMOV 0x32 | |
65 | #define CC2520_CMD_TXBUF 0x3A | |
66 | #define CC2520_CMD_TXBUFCP 0x3E | |
67 | #define CC2520_CMD_RANDOM 0x3C | |
68 | #define CC2520_CMD_SXOSCON 0x40 | |
69 | #define CC2520_CMD_STXCAL 0x41 | |
70 | #define CC2520_CMD_SRXON 0x42 | |
71 | #define CC2520_CMD_STXON 0x43 | |
72 | #define CC2520_CMD_STXONCCA 0x44 | |
73 | #define CC2520_CMD_SRFOFF 0x45 | |
74 | #define CC2520_CMD_SXOSCOFF 0x46 | |
75 | #define CC2520_CMD_SFLUSHRX 0x47 | |
76 | #define CC2520_CMD_SFLUSHTX 0x48 | |
77 | #define CC2520_CMD_SACK 0x49 | |
78 | #define CC2520_CMD_SACKPEND 0x4A | |
79 | #define CC2520_CMD_SNACK 0x4B | |
80 | #define CC2520_CMD_SRXMASKBITSET 0x4C | |
81 | #define CC2520_CMD_SRXMASKBITCLR 0x4D | |
82 | #define CC2520_CMD_RXMASKAND 0x4E | |
83 | #define CC2520_CMD_RXMASKOR 0x4F | |
84 | #define CC2520_CMD_MEMCP 0x50 | |
85 | #define CC2520_CMD_MEMCPR 0x52 | |
86 | #define CC2520_CMD_MEMXCP 0x54 | |
87 | #define CC2520_CMD_MEMXWR 0x56 | |
88 | #define CC2520_CMD_BCLR 0x58 | |
89 | #define CC2520_CMD_BSET 0x59 | |
90 | #define CC2520_CMD_CTR_UCTR 0x60 | |
91 | #define CC2520_CMD_CBCMAC 0x64 | |
92 | #define CC2520_CMD_UCBCMAC 0x66 | |
93 | #define CC2520_CMD_CCM 0x68 | |
94 | #define CC2520_CMD_UCCM 0x6A | |
95 | #define CC2520_CMD_ECB 0x70 | |
96 | #define CC2520_CMD_ECBO 0x72 | |
97 | #define CC2520_CMD_ECBX 0x74 | |
98 | #define CC2520_CMD_INC 0x78 | |
99 | #define CC2520_CMD_ABORT 0x7F | |
100 | #define CC2520_CMD_REGISTER_READ 0x80 | |
101 | #define CC2520_CMD_REGISTER_WRITE 0xC0 | |
102 | ||
103 | /* status registers */ | |
104 | #define CC2520_CHIPID 0x40 | |
105 | #define CC2520_VERSION 0x42 | |
106 | #define CC2520_EXTCLOCK 0x44 | |
107 | #define CC2520_MDMCTRL0 0x46 | |
108 | #define CC2520_MDMCTRL1 0x47 | |
109 | #define CC2520_FREQEST 0x48 | |
110 | #define CC2520_RXCTRL 0x4A | |
111 | #define CC2520_FSCTRL 0x4C | |
112 | #define CC2520_FSCAL0 0x4E | |
113 | #define CC2520_FSCAL1 0x4F | |
114 | #define CC2520_FSCAL2 0x50 | |
115 | #define CC2520_FSCAL3 0x51 | |
116 | #define CC2520_AGCCTRL0 0x52 | |
117 | #define CC2520_AGCCTRL1 0x53 | |
118 | #define CC2520_AGCCTRL2 0x54 | |
119 | #define CC2520_AGCCTRL3 0x55 | |
120 | #define CC2520_ADCTEST0 0x56 | |
121 | #define CC2520_ADCTEST1 0x57 | |
122 | #define CC2520_ADCTEST2 0x58 | |
123 | #define CC2520_MDMTEST0 0x5A | |
124 | #define CC2520_MDMTEST1 0x5B | |
125 | #define CC2520_DACTEST0 0x5C | |
126 | #define CC2520_DACTEST1 0x5D | |
127 | #define CC2520_ATEST 0x5E | |
128 | #define CC2520_DACTEST2 0x5F | |
129 | #define CC2520_PTEST0 0x60 | |
130 | #define CC2520_PTEST1 0x61 | |
131 | #define CC2520_RESERVED 0x62 | |
132 | #define CC2520_DPUBIST 0x7A | |
133 | #define CC2520_ACTBIST 0x7C | |
134 | #define CC2520_RAMBIST 0x7E | |
135 | ||
136 | /* frame registers */ | |
137 | #define CC2520_FRMFILT0 0x00 | |
138 | #define CC2520_FRMFILT1 0x01 | |
139 | #define CC2520_SRCMATCH 0x02 | |
140 | #define CC2520_SRCSHORTEN0 0x04 | |
141 | #define CC2520_SRCSHORTEN1 0x05 | |
142 | #define CC2520_SRCSHORTEN2 0x06 | |
143 | #define CC2520_SRCEXTEN0 0x08 | |
144 | #define CC2520_SRCEXTEN1 0x09 | |
145 | #define CC2520_SRCEXTEN2 0x0A | |
146 | #define CC2520_FRMCTRL0 0x0C | |
147 | #define CC2520_FRMCTRL1 0x0D | |
148 | #define CC2520_RXENABLE0 0x0E | |
149 | #define CC2520_RXENABLE1 0x0F | |
150 | #define CC2520_EXCFLAG0 0x10 | |
151 | #define CC2520_EXCFLAG1 0x11 | |
152 | #define CC2520_EXCFLAG2 0x12 | |
153 | #define CC2520_EXCMASKA0 0x14 | |
154 | #define CC2520_EXCMASKA1 0x15 | |
155 | #define CC2520_EXCMASKA2 0x16 | |
156 | #define CC2520_EXCMASKB0 0x18 | |
157 | #define CC2520_EXCMASKB1 0x19 | |
158 | #define CC2520_EXCMASKB2 0x1A | |
159 | #define CC2520_EXCBINDX0 0x1C | |
160 | #define CC2520_EXCBINDX1 0x1D | |
161 | #define CC2520_EXCBINDY0 0x1E | |
162 | #define CC2520_EXCBINDY1 0x1F | |
163 | #define CC2520_GPIOCTRL0 0x20 | |
164 | #define CC2520_GPIOCTRL1 0x21 | |
165 | #define CC2520_GPIOCTRL2 0x22 | |
166 | #define CC2520_GPIOCTRL3 0x23 | |
167 | #define CC2520_GPIOCTRL4 0x24 | |
168 | #define CC2520_GPIOCTRL5 0x25 | |
169 | #define CC2520_GPIOPOLARITY 0x26 | |
170 | #define CC2520_GPIOCTRL 0x28 | |
171 | #define CC2520_DPUCON 0x2A | |
172 | #define CC2520_DPUSTAT 0x2C | |
173 | #define CC2520_FREQCTRL 0x2E | |
174 | #define CC2520_FREQTUNE 0x2F | |
175 | #define CC2520_TXPOWER 0x30 | |
176 | #define CC2520_TXCTRL 0x31 | |
177 | #define CC2520_FSMSTAT0 0x32 | |
178 | #define CC2520_FSMSTAT1 0x33 | |
179 | #define CC2520_FIFOPCTRL 0x34 | |
180 | #define CC2520_FSMCTRL 0x35 | |
181 | #define CC2520_CCACTRL0 0x36 | |
182 | #define CC2520_CCACTRL1 0x37 | |
183 | #define CC2520_RSSI 0x38 | |
184 | #define CC2520_RSSISTAT 0x39 | |
185 | #define CC2520_RXFIRST 0x3C | |
186 | #define CC2520_RXFIFOCNT 0x3E | |
187 | #define CC2520_TXFIFOCNT 0x3F | |
188 | ||
59869ebf BC |
189 | /* CC2520_FRMFILT0 */ |
190 | #define FRMFILT0_FRAME_FILTER_EN BIT(0) | |
191 | #define FRMFILT0_PAN_COORDINATOR BIT(1) | |
192 | ||
193 | /* CC2520_FRMCTRL0 */ | |
194 | #define FRMCTRL0_AUTOACK BIT(5) | |
195 | #define FRMCTRL0_AUTOCRC BIT(6) | |
196 | ||
197 | /* CC2520_FRMCTRL1 */ | |
198 | #define FRMCTRL1_SET_RXENMASK_ON_TX BIT(0) | |
199 | #define FRMCTRL1_IGNORE_TX_UNDERF BIT(1) | |
200 | ||
0da6bc8c VB |
201 | /* Driver private information */ |
202 | struct cc2520_private { | |
203 | struct spi_device *spi; /* SPI device structure */ | |
5a504397 | 204 | struct ieee802154_hw *hw; /* IEEE-802.15.4 device */ |
0da6bc8c VB |
205 | u8 *buf; /* SPI TX/Rx data buffer */ |
206 | struct mutex buffer_mutex; /* SPI buffer mutex */ | |
207 | bool is_tx; /* Flag for sync b/w Tx and Rx */ | |
1a1bc59c | 208 | bool amplified; /* Flag for CC2591 */ |
0da6bc8c VB |
209 | int fifo_pin; /* FIFO GPIO pin number */ |
210 | struct work_struct fifop_irqwork;/* Workqueue for FIFOP */ | |
211 | spinlock_t lock; /* Lock for is_tx*/ | |
212 | struct completion tx_complete; /* Work completion for Tx */ | |
59869ebf | 213 | bool promiscuous; /* Flag for promiscuous mode */ |
0da6bc8c VB |
214 | }; |
215 | ||
216 | /* Generic Functions */ | |
217 | static int | |
218 | cc2520_cmd_strobe(struct cc2520_private *priv, u8 cmd) | |
219 | { | |
220 | int ret; | |
221 | u8 status = 0xff; | |
222 | struct spi_message msg; | |
223 | struct spi_transfer xfer = { | |
224 | .len = 0, | |
225 | .tx_buf = priv->buf, | |
226 | .rx_buf = priv->buf, | |
227 | }; | |
228 | ||
229 | spi_message_init(&msg); | |
230 | spi_message_add_tail(&xfer, &msg); | |
231 | ||
232 | mutex_lock(&priv->buffer_mutex); | |
233 | priv->buf[xfer.len++] = cmd; | |
234 | dev_vdbg(&priv->spi->dev, | |
235 | "command strobe buf[0] = %02x\n", | |
236 | priv->buf[0]); | |
237 | ||
238 | ret = spi_sync(priv->spi, &msg); | |
239 | if (!ret) | |
240 | status = priv->buf[0]; | |
241 | dev_vdbg(&priv->spi->dev, | |
242 | "buf[0] = %02x\n", priv->buf[0]); | |
243 | mutex_unlock(&priv->buffer_mutex); | |
244 | ||
245 | return ret; | |
246 | } | |
247 | ||
248 | static int | |
249 | cc2520_get_status(struct cc2520_private *priv, u8 *status) | |
250 | { | |
251 | int ret; | |
252 | struct spi_message msg; | |
253 | struct spi_transfer xfer = { | |
254 | .len = 0, | |
255 | .tx_buf = priv->buf, | |
256 | .rx_buf = priv->buf, | |
257 | }; | |
258 | ||
259 | spi_message_init(&msg); | |
260 | spi_message_add_tail(&xfer, &msg); | |
261 | ||
262 | mutex_lock(&priv->buffer_mutex); | |
263 | priv->buf[xfer.len++] = CC2520_CMD_SNOP; | |
264 | dev_vdbg(&priv->spi->dev, | |
265 | "get status command buf[0] = %02x\n", priv->buf[0]); | |
266 | ||
267 | ret = spi_sync(priv->spi, &msg); | |
268 | if (!ret) | |
269 | *status = priv->buf[0]; | |
270 | dev_vdbg(&priv->spi->dev, | |
271 | "buf[0] = %02x\n", priv->buf[0]); | |
272 | mutex_unlock(&priv->buffer_mutex); | |
273 | ||
274 | return ret; | |
275 | } | |
276 | ||
277 | static int | |
278 | cc2520_write_register(struct cc2520_private *priv, u8 reg, u8 value) | |
279 | { | |
280 | int status; | |
281 | struct spi_message msg; | |
282 | struct spi_transfer xfer = { | |
283 | .len = 0, | |
284 | .tx_buf = priv->buf, | |
285 | .rx_buf = priv->buf, | |
286 | }; | |
287 | ||
288 | spi_message_init(&msg); | |
289 | spi_message_add_tail(&xfer, &msg); | |
290 | ||
291 | mutex_lock(&priv->buffer_mutex); | |
292 | ||
293 | if (reg <= CC2520_FREG_MASK) { | |
294 | priv->buf[xfer.len++] = CC2520_CMD_REGISTER_WRITE | reg; | |
295 | priv->buf[xfer.len++] = value; | |
296 | } else { | |
297 | priv->buf[xfer.len++] = CC2520_CMD_MEMORY_WRITE; | |
298 | priv->buf[xfer.len++] = reg; | |
299 | priv->buf[xfer.len++] = value; | |
300 | } | |
301 | status = spi_sync(priv->spi, &msg); | |
302 | if (msg.status) | |
303 | status = msg.status; | |
304 | ||
305 | mutex_unlock(&priv->buffer_mutex); | |
306 | ||
307 | return status; | |
308 | } | |
309 | ||
310 | static int | |
311 | cc2520_write_ram(struct cc2520_private *priv, u16 reg, u8 len, u8 *data) | |
312 | { | |
313 | int status; | |
314 | struct spi_message msg; | |
315 | struct spi_transfer xfer_head = { | |
316 | .len = 0, | |
317 | .tx_buf = priv->buf, | |
318 | .rx_buf = priv->buf, | |
319 | }; | |
320 | ||
321 | struct spi_transfer xfer_buf = { | |
322 | .len = len, | |
323 | .tx_buf = data, | |
324 | }; | |
325 | ||
326 | mutex_lock(&priv->buffer_mutex); | |
327 | priv->buf[xfer_head.len++] = (CC2520_CMD_MEMORY_WRITE | | |
328 | ((reg >> 8) & 0xff)); | |
329 | priv->buf[xfer_head.len++] = reg & 0xff; | |
330 | ||
331 | spi_message_init(&msg); | |
332 | spi_message_add_tail(&xfer_head, &msg); | |
333 | spi_message_add_tail(&xfer_buf, &msg); | |
334 | ||
335 | status = spi_sync(priv->spi, &msg); | |
336 | dev_dbg(&priv->spi->dev, "spi status = %d\n", status); | |
337 | if (msg.status) | |
338 | status = msg.status; | |
339 | ||
340 | mutex_unlock(&priv->buffer_mutex); | |
341 | return status; | |
342 | } | |
343 | ||
344 | static int | |
345 | cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data) | |
346 | { | |
347 | int status; | |
348 | struct spi_message msg; | |
349 | struct spi_transfer xfer1 = { | |
350 | .len = 0, | |
351 | .tx_buf = priv->buf, | |
352 | .rx_buf = priv->buf, | |
353 | }; | |
354 | ||
355 | struct spi_transfer xfer2 = { | |
356 | .len = 1, | |
357 | .rx_buf = data, | |
358 | }; | |
359 | ||
360 | spi_message_init(&msg); | |
361 | spi_message_add_tail(&xfer1, &msg); | |
362 | spi_message_add_tail(&xfer2, &msg); | |
363 | ||
364 | mutex_lock(&priv->buffer_mutex); | |
365 | priv->buf[xfer1.len++] = CC2520_CMD_MEMORY_READ; | |
366 | priv->buf[xfer1.len++] = reg; | |
367 | ||
368 | status = spi_sync(priv->spi, &msg); | |
369 | dev_dbg(&priv->spi->dev, | |
370 | "spi status = %d\n", status); | |
371 | if (msg.status) | |
372 | status = msg.status; | |
373 | ||
374 | mutex_unlock(&priv->buffer_mutex); | |
375 | ||
376 | return status; | |
377 | } | |
378 | ||
379 | static int | |
59869ebf | 380 | cc2520_write_txfifo(struct cc2520_private *priv, u8 pkt_len, u8 *data, u8 len) |
0da6bc8c VB |
381 | { |
382 | int status; | |
383 | ||
384 | /* length byte must include FCS even | |
385 | * if it is calculated in the hardware | |
386 | */ | |
59869ebf | 387 | int len_byte = pkt_len; |
0da6bc8c VB |
388 | |
389 | struct spi_message msg; | |
390 | ||
391 | struct spi_transfer xfer_head = { | |
392 | .len = 0, | |
393 | .tx_buf = priv->buf, | |
394 | .rx_buf = priv->buf, | |
395 | }; | |
396 | struct spi_transfer xfer_len = { | |
397 | .len = 1, | |
398 | .tx_buf = &len_byte, | |
399 | }; | |
400 | struct spi_transfer xfer_buf = { | |
401 | .len = len, | |
402 | .tx_buf = data, | |
403 | }; | |
404 | ||
405 | spi_message_init(&msg); | |
406 | spi_message_add_tail(&xfer_head, &msg); | |
407 | spi_message_add_tail(&xfer_len, &msg); | |
408 | spi_message_add_tail(&xfer_buf, &msg); | |
409 | ||
410 | mutex_lock(&priv->buffer_mutex); | |
411 | priv->buf[xfer_head.len++] = CC2520_CMD_TXBUF; | |
412 | dev_vdbg(&priv->spi->dev, | |
413 | "TX_FIFO cmd buf[0] = %02x\n", priv->buf[0]); | |
414 | ||
415 | status = spi_sync(priv->spi, &msg); | |
416 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | |
417 | if (msg.status) | |
418 | status = msg.status; | |
419 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | |
420 | dev_vdbg(&priv->spi->dev, "buf[0] = %02x\n", priv->buf[0]); | |
421 | mutex_unlock(&priv->buffer_mutex); | |
422 | ||
423 | return status; | |
424 | } | |
425 | ||
426 | static int | |
59869ebf | 427 | cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len) |
0da6bc8c VB |
428 | { |
429 | int status; | |
430 | struct spi_message msg; | |
431 | ||
432 | struct spi_transfer xfer_head = { | |
433 | .len = 0, | |
434 | .tx_buf = priv->buf, | |
435 | .rx_buf = priv->buf, | |
436 | }; | |
437 | struct spi_transfer xfer_buf = { | |
438 | .len = len, | |
439 | .rx_buf = data, | |
440 | }; | |
441 | ||
442 | spi_message_init(&msg); | |
443 | spi_message_add_tail(&xfer_head, &msg); | |
444 | spi_message_add_tail(&xfer_buf, &msg); | |
445 | ||
446 | mutex_lock(&priv->buffer_mutex); | |
447 | priv->buf[xfer_head.len++] = CC2520_CMD_RXBUF; | |
448 | ||
449 | dev_vdbg(&priv->spi->dev, "read rxfifo buf[0] = %02x\n", priv->buf[0]); | |
450 | dev_vdbg(&priv->spi->dev, "buf[1] = %02x\n", priv->buf[1]); | |
451 | ||
452 | status = spi_sync(priv->spi, &msg); | |
453 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | |
454 | if (msg.status) | |
455 | status = msg.status; | |
456 | dev_vdbg(&priv->spi->dev, "status = %d\n", status); | |
457 | dev_vdbg(&priv->spi->dev, | |
458 | "return status buf[0] = %02x\n", priv->buf[0]); | |
459 | dev_vdbg(&priv->spi->dev, "length buf[1] = %02x\n", priv->buf[1]); | |
460 | ||
461 | mutex_unlock(&priv->buffer_mutex); | |
462 | ||
463 | return status; | |
464 | } | |
465 | ||
5a504397 | 466 | static int cc2520_start(struct ieee802154_hw *hw) |
0da6bc8c | 467 | { |
5a504397 | 468 | return cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRXON); |
0da6bc8c VB |
469 | } |
470 | ||
5a504397 | 471 | static void cc2520_stop(struct ieee802154_hw *hw) |
0da6bc8c | 472 | { |
5a504397 | 473 | cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRFOFF); |
0da6bc8c VB |
474 | } |
475 | ||
476 | static int | |
5a504397 | 477 | cc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb) |
0da6bc8c | 478 | { |
5a504397 | 479 | struct cc2520_private *priv = hw->priv; |
0da6bc8c VB |
480 | unsigned long flags; |
481 | int rc; | |
482 | u8 status = 0; | |
59869ebf BC |
483 | u8 pkt_len; |
484 | ||
485 | /* In promiscuous mode we disable AUTOCRC so we can get the raw CRC | |
486 | * values on RX. This means we need to manually add the CRC on TX. | |
487 | */ | |
488 | if (priv->promiscuous) { | |
489 | u16 crc = crc_ccitt(0, skb->data, skb->len); | |
490 | ||
491 | put_unaligned_le16(crc, skb_put(skb, 2)); | |
492 | pkt_len = skb->len; | |
493 | } else { | |
494 | pkt_len = skb->len + 2; | |
495 | } | |
0da6bc8c VB |
496 | |
497 | rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); | |
498 | if (rc) | |
499 | goto err_tx; | |
500 | ||
59869ebf | 501 | rc = cc2520_write_txfifo(priv, pkt_len, skb->data, skb->len); |
0da6bc8c VB |
502 | if (rc) |
503 | goto err_tx; | |
504 | ||
505 | rc = cc2520_get_status(priv, &status); | |
506 | if (rc) | |
507 | goto err_tx; | |
508 | ||
509 | if (status & CC2520_STATUS_TX_UNDERFLOW) { | |
510 | dev_err(&priv->spi->dev, "cc2520 tx underflow exception\n"); | |
511 | goto err_tx; | |
512 | } | |
513 | ||
514 | spin_lock_irqsave(&priv->lock, flags); | |
cd3a21b5 | 515 | WARN_ON(priv->is_tx); |
0da6bc8c VB |
516 | priv->is_tx = 1; |
517 | spin_unlock_irqrestore(&priv->lock, flags); | |
518 | ||
519 | rc = cc2520_cmd_strobe(priv, CC2520_CMD_STXONCCA); | |
520 | if (rc) | |
521 | goto err; | |
522 | ||
523 | rc = wait_for_completion_interruptible(&priv->tx_complete); | |
524 | if (rc < 0) | |
525 | goto err; | |
526 | ||
527 | cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); | |
528 | cc2520_cmd_strobe(priv, CC2520_CMD_SRXON); | |
529 | ||
530 | return rc; | |
531 | err: | |
532 | spin_lock_irqsave(&priv->lock, flags); | |
533 | priv->is_tx = 0; | |
534 | spin_unlock_irqrestore(&priv->lock, flags); | |
535 | err_tx: | |
536 | return rc; | |
537 | } | |
538 | ||
0da6bc8c VB |
539 | static int cc2520_rx(struct cc2520_private *priv) |
540 | { | |
541 | u8 len = 0, lqi = 0, bytes = 1; | |
542 | struct sk_buff *skb; | |
543 | ||
59869ebf BC |
544 | /* Read single length byte from the radio. */ |
545 | cc2520_read_rxfifo(priv, &len, bytes); | |
0da6bc8c | 546 | |
59869ebf BC |
547 | if (!ieee802154_is_valid_psdu_len(len)) { |
548 | /* Corrupted frame received, clear frame buffer by | |
549 | * reading entire buffer. | |
550 | */ | |
551 | dev_dbg(&priv->spi->dev, "corrupted frame received\n"); | |
552 | len = IEEE802154_MTU; | |
553 | } | |
0da6bc8c | 554 | |
61a22814 | 555 | skb = dev_alloc_skb(len); |
0da6bc8c VB |
556 | if (!skb) |
557 | return -ENOMEM; | |
558 | ||
59869ebf | 559 | if (cc2520_read_rxfifo(priv, skb_put(skb, len), len)) { |
0da6bc8c VB |
560 | dev_dbg(&priv->spi->dev, "frame reception failed\n"); |
561 | kfree_skb(skb); | |
562 | return -EINVAL; | |
563 | } | |
564 | ||
59869ebf BC |
565 | /* In promiscuous mode, we configure the radio to include the |
566 | * CRC (AUTOCRC==0) and we pass on the packet unconditionally. If not | |
567 | * in promiscuous mode, we check the CRC here, but leave the | |
568 | * RSSI/LQI/CRC_OK bytes as they will get removed in the mac layer. | |
569 | */ | |
570 | if (!priv->promiscuous) { | |
571 | bool crc_ok; | |
572 | ||
573 | /* Check if the CRC is valid. With AUTOCRC set, the most | |
574 | * significant bit of the last byte returned from the CC2520 | |
575 | * is CRC_OK flag. See section 20.3.4 of the datasheet. | |
576 | */ | |
577 | crc_ok = skb->data[len - 1] & BIT(7); | |
578 | ||
579 | /* If we failed CRC drop the packet in the driver layer. */ | |
580 | if (!crc_ok) { | |
581 | dev_dbg(&priv->spi->dev, "CRC check failed\n"); | |
582 | kfree_skb(skb); | |
583 | return -EINVAL; | |
584 | } | |
585 | ||
586 | /* To calculate LQI, the lower 7 bits of the last byte (the | |
587 | * correlation value provided by the radio) must be scaled to | |
588 | * the range 0-255. According to section 20.6, the correlation | |
589 | * value ranges from 50-110. Ideally this would be calibrated | |
590 | * per hardware design, but we use roughly the datasheet values | |
591 | * to get close enough while avoiding floating point. | |
592 | */ | |
593 | lqi = skb->data[len - 1] & 0x7f; | |
594 | if (lqi < 50) | |
595 | lqi = 50; | |
596 | else if (lqi > 113) | |
597 | lqi = 113; | |
598 | lqi = (lqi - 50) * 4; | |
599 | } | |
0da6bc8c | 600 | |
5a504397 | 601 | ieee802154_rx_irqsafe(priv->hw, skb, lqi); |
0da6bc8c VB |
602 | |
603 | dev_vdbg(&priv->spi->dev, "RXFIFO: %x %x\n", len, lqi); | |
604 | ||
605 | return 0; | |
606 | } | |
607 | ||
608 | static int | |
5a504397 | 609 | cc2520_ed(struct ieee802154_hw *hw, u8 *level) |
0da6bc8c | 610 | { |
5a504397 | 611 | struct cc2520_private *priv = hw->priv; |
0da6bc8c VB |
612 | u8 status = 0xff; |
613 | u8 rssi; | |
614 | int ret; | |
615 | ||
3251ca33 | 616 | ret = cc2520_read_register(priv, CC2520_RSSISTAT, &status); |
0da6bc8c VB |
617 | if (ret) |
618 | return ret; | |
619 | ||
620 | if (status != RSSI_VALID) | |
621 | return -EINVAL; | |
622 | ||
3251ca33 | 623 | ret = cc2520_read_register(priv, CC2520_RSSI, &rssi); |
0da6bc8c VB |
624 | if (ret) |
625 | return ret; | |
626 | ||
627 | /* level = RSSI(rssi) - OFFSET [dBm] : offset is 76dBm */ | |
628 | *level = rssi - RSSI_OFFSET; | |
629 | ||
630 | return 0; | |
631 | } | |
632 | ||
633 | static int | |
e37d2ec8 | 634 | cc2520_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) |
0da6bc8c | 635 | { |
5a504397 | 636 | struct cc2520_private *priv = hw->priv; |
0da6bc8c VB |
637 | int ret; |
638 | ||
0da6bc8c VB |
639 | dev_dbg(&priv->spi->dev, "trying to set channel\n"); |
640 | ||
cd3a21b5 SS |
641 | WARN_ON(page != 0); |
642 | WARN_ON(channel < CC2520_MINCHANNEL); | |
643 | WARN_ON(channel > CC2520_MAXCHANNEL); | |
0da6bc8c VB |
644 | |
645 | ret = cc2520_write_register(priv, CC2520_FREQCTRL, | |
3ee0275d | 646 | 11 + 5 * (channel - 11)); |
0da6bc8c VB |
647 | |
648 | return ret; | |
649 | } | |
650 | ||
651 | static int | |
5a504397 | 652 | cc2520_filter(struct ieee802154_hw *hw, |
0da6bc8c VB |
653 | struct ieee802154_hw_addr_filt *filt, unsigned long changed) |
654 | { | |
5a504397 | 655 | struct cc2520_private *priv = hw->priv; |
c9d44203 | 656 | int ret = 0; |
0da6bc8c | 657 | |
57205c14 | 658 | if (changed & IEEE802154_AFILT_PANID_CHANGED) { |
0da6bc8c VB |
659 | u16 panid = le16_to_cpu(filt->pan_id); |
660 | ||
a8ab042c | 661 | dev_vdbg(&priv->spi->dev, "%s called for pan id\n", __func__); |
c9d44203 SS |
662 | ret = cc2520_write_ram(priv, CC2520RAM_PANID, |
663 | sizeof(panid), (u8 *)&panid); | |
0da6bc8c VB |
664 | } |
665 | ||
57205c14 | 666 | if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { |
0da6bc8c | 667 | dev_vdbg(&priv->spi->dev, |
a8ab042c | 668 | "%s called for IEEE addr\n", __func__); |
c9d44203 SS |
669 | ret = cc2520_write_ram(priv, CC2520RAM_IEEEADDR, |
670 | sizeof(filt->ieee_addr), | |
671 | (u8 *)&filt->ieee_addr); | |
0da6bc8c VB |
672 | } |
673 | ||
57205c14 | 674 | if (changed & IEEE802154_AFILT_SADDR_CHANGED) { |
0da6bc8c VB |
675 | u16 addr = le16_to_cpu(filt->short_addr); |
676 | ||
a8ab042c | 677 | dev_vdbg(&priv->spi->dev, "%s called for saddr\n", __func__); |
c9d44203 SS |
678 | ret = cc2520_write_ram(priv, CC2520RAM_SHORTADDR, |
679 | sizeof(addr), (u8 *)&addr); | |
0da6bc8c VB |
680 | } |
681 | ||
57205c14 | 682 | if (changed & IEEE802154_AFILT_PANC_CHANGED) { |
59869ebf BC |
683 | u8 frmfilt0; |
684 | ||
0da6bc8c | 685 | dev_vdbg(&priv->spi->dev, |
a8ab042c | 686 | "%s called for panc change\n", __func__); |
59869ebf BC |
687 | |
688 | cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0); | |
689 | ||
0da6bc8c | 690 | if (filt->pan_coord) |
59869ebf | 691 | frmfilt0 |= FRMFILT0_PAN_COORDINATOR; |
0da6bc8c | 692 | else |
59869ebf BC |
693 | frmfilt0 &= ~FRMFILT0_PAN_COORDINATOR; |
694 | ||
695 | ret = cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0); | |
0da6bc8c VB |
696 | } |
697 | ||
c9d44203 | 698 | return ret; |
0da6bc8c VB |
699 | } |
700 | ||
e10c1674 VB |
701 | static inline int cc2520_set_tx_power(struct cc2520_private *priv, s32 mbm) |
702 | { | |
703 | u8 power; | |
704 | ||
705 | switch (mbm) { | |
706 | case 500: | |
707 | power = 0xF7; | |
708 | break; | |
709 | case 300: | |
710 | power = 0xF2; | |
711 | break; | |
712 | case 200: | |
713 | power = 0xAB; | |
714 | break; | |
715 | case 100: | |
716 | power = 0x13; | |
717 | break; | |
718 | case 0: | |
719 | power = 0x32; | |
720 | break; | |
721 | case -200: | |
722 | power = 0x81; | |
723 | break; | |
724 | case -400: | |
725 | power = 0x88; | |
726 | break; | |
727 | case -700: | |
728 | power = 0x2C; | |
729 | break; | |
730 | case -1800: | |
731 | power = 0x03; | |
732 | break; | |
733 | default: | |
734 | return -EINVAL; | |
735 | } | |
736 | ||
737 | return cc2520_write_register(priv, CC2520_TXPOWER, power); | |
738 | } | |
739 | ||
740 | static inline int cc2520_cc2591_set_tx_power(struct cc2520_private *priv, | |
741 | s32 mbm) | |
742 | { | |
743 | u8 power; | |
744 | ||
745 | switch (mbm) { | |
746 | case 1700: | |
747 | power = 0xF9; | |
748 | break; | |
749 | case 1600: | |
750 | power = 0xF0; | |
751 | break; | |
752 | case 1400: | |
753 | power = 0xA0; | |
754 | break; | |
755 | case 1100: | |
756 | power = 0x2C; | |
757 | break; | |
758 | case -100: | |
759 | power = 0x03; | |
760 | break; | |
761 | case -800: | |
762 | power = 0x01; | |
763 | break; | |
764 | default: | |
765 | return -EINVAL; | |
766 | } | |
767 | ||
768 | return cc2520_write_register(priv, CC2520_TXPOWER, power); | |
769 | } | |
770 | ||
771 | #define CC2520_MAX_TX_POWERS 0x8 | |
772 | static const s32 cc2520_powers[CC2520_MAX_TX_POWERS + 1] = { | |
773 | 500, 300, 200, 100, 0, -200, -400, -700, -1800, | |
774 | }; | |
775 | ||
776 | #define CC2520_CC2591_MAX_TX_POWERS 0x5 | |
777 | static const s32 cc2520_cc2591_powers[CC2520_CC2591_MAX_TX_POWERS + 1] = { | |
778 | 1700, 1600, 1400, 1100, -100, -800, | |
779 | }; | |
780 | ||
781 | static int | |
782 | cc2520_set_txpower(struct ieee802154_hw *hw, s32 mbm) | |
783 | { | |
784 | struct cc2520_private *priv = hw->priv; | |
785 | ||
786 | if (!priv->amplified) | |
787 | return cc2520_set_tx_power(priv, mbm); | |
788 | ||
789 | return cc2520_cc2591_set_tx_power(priv, mbm); | |
790 | } | |
791 | ||
59869ebf BC |
792 | static int |
793 | cc2520_set_promiscuous_mode(struct ieee802154_hw *hw, bool on) | |
794 | { | |
795 | struct cc2520_private *priv = hw->priv; | |
796 | u8 frmfilt0; | |
797 | ||
798 | dev_dbg(&priv->spi->dev, "%s : mode %d\n", __func__, on); | |
799 | ||
800 | priv->promiscuous = on; | |
801 | ||
802 | cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0); | |
803 | ||
804 | if (on) { | |
805 | /* Disable automatic ACK, automatic CRC, and frame filtering. */ | |
806 | cc2520_write_register(priv, CC2520_FRMCTRL0, 0); | |
807 | frmfilt0 &= ~FRMFILT0_FRAME_FILTER_EN; | |
808 | } else { | |
809 | cc2520_write_register(priv, CC2520_FRMCTRL0, FRMCTRL0_AUTOACK | | |
810 | FRMCTRL0_AUTOCRC); | |
811 | frmfilt0 |= FRMFILT0_FRAME_FILTER_EN; | |
812 | } | |
813 | return cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0); | |
814 | } | |
815 | ||
16301861 | 816 | static const struct ieee802154_ops cc2520_ops = { |
0da6bc8c VB |
817 | .owner = THIS_MODULE, |
818 | .start = cc2520_start, | |
819 | .stop = cc2520_stop, | |
ed0a5dce | 820 | .xmit_sync = cc2520_tx, |
0da6bc8c VB |
821 | .ed = cc2520_ed, |
822 | .set_channel = cc2520_set_channel, | |
823 | .set_hw_addr_filt = cc2520_filter, | |
e10c1674 | 824 | .set_txpower = cc2520_set_txpower, |
59869ebf | 825 | .set_promiscuous_mode = cc2520_set_promiscuous_mode, |
0da6bc8c VB |
826 | }; |
827 | ||
828 | static int cc2520_register(struct cc2520_private *priv) | |
829 | { | |
830 | int ret = -ENOMEM; | |
831 | ||
5a504397 AA |
832 | priv->hw = ieee802154_alloc_hw(sizeof(*priv), &cc2520_ops); |
833 | if (!priv->hw) | |
0da6bc8c VB |
834 | goto err_ret; |
835 | ||
5a504397 AA |
836 | priv->hw->priv = priv; |
837 | priv->hw->parent = &priv->spi->dev; | |
838 | priv->hw->extra_tx_headroom = 0; | |
7fc1b2d5 | 839 | ieee802154_random_extended_addr(&priv->hw->phy->perm_extended_addr); |
0da6bc8c VB |
840 | |
841 | /* We do support only 2.4 Ghz */ | |
72f655e4 | 842 | priv->hw->phy->supported.channels[0] = 0x7FFF800; |
59869ebf BC |
843 | priv->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | |
844 | IEEE802154_HW_PROMISCUOUS; | |
0da6bc8c | 845 | |
e10c1674 VB |
846 | priv->hw->phy->flags = WPAN_PHY_FLAG_TXPOWER; |
847 | ||
848 | if (!priv->amplified) { | |
849 | priv->hw->phy->supported.tx_powers = cc2520_powers; | |
850 | priv->hw->phy->supported.tx_powers_size = ARRAY_SIZE(cc2520_powers); | |
322fe2d1 | 851 | priv->hw->phy->transmit_power = priv->hw->phy->supported.tx_powers[4]; |
e10c1674 VB |
852 | } else { |
853 | priv->hw->phy->supported.tx_powers = cc2520_cc2591_powers; | |
854 | priv->hw->phy->supported.tx_powers_size = ARRAY_SIZE(cc2520_cc2591_powers); | |
322fe2d1 | 855 | priv->hw->phy->transmit_power = priv->hw->phy->supported.tx_powers[0]; |
e10c1674 VB |
856 | } |
857 | ||
d6d244d4 VB |
858 | priv->hw->phy->current_channel = 11; |
859 | ||
0da6bc8c | 860 | dev_vdbg(&priv->spi->dev, "registered cc2520\n"); |
5a504397 | 861 | ret = ieee802154_register_hw(priv->hw); |
0da6bc8c VB |
862 | if (ret) |
863 | goto err_free_device; | |
864 | ||
865 | return 0; | |
866 | ||
867 | err_free_device: | |
5a504397 | 868 | ieee802154_free_hw(priv->hw); |
0da6bc8c VB |
869 | err_ret: |
870 | return ret; | |
871 | } | |
872 | ||
873 | static void cc2520_fifop_irqwork(struct work_struct *work) | |
874 | { | |
875 | struct cc2520_private *priv | |
876 | = container_of(work, struct cc2520_private, fifop_irqwork); | |
877 | ||
878 | dev_dbg(&priv->spi->dev, "fifop interrupt received\n"); | |
879 | ||
880 | if (gpio_get_value(priv->fifo_pin)) | |
881 | cc2520_rx(priv); | |
882 | else | |
883 | dev_dbg(&priv->spi->dev, "rxfifo overflow\n"); | |
884 | ||
885 | cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX); | |
886 | cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX); | |
887 | } | |
888 | ||
889 | static irqreturn_t cc2520_fifop_isr(int irq, void *data) | |
890 | { | |
891 | struct cc2520_private *priv = data; | |
892 | ||
893 | schedule_work(&priv->fifop_irqwork); | |
894 | ||
895 | return IRQ_HANDLED; | |
896 | } | |
897 | ||
898 | static irqreturn_t cc2520_sfd_isr(int irq, void *data) | |
899 | { | |
900 | struct cc2520_private *priv = data; | |
901 | unsigned long flags; | |
902 | ||
903 | spin_lock_irqsave(&priv->lock, flags); | |
904 | if (priv->is_tx) { | |
905 | priv->is_tx = 0; | |
906 | spin_unlock_irqrestore(&priv->lock, flags); | |
907 | dev_dbg(&priv->spi->dev, "SFD for TX\n"); | |
908 | complete(&priv->tx_complete); | |
909 | } else { | |
910 | spin_unlock_irqrestore(&priv->lock, flags); | |
911 | dev_dbg(&priv->spi->dev, "SFD for RX\n"); | |
912 | } | |
913 | ||
914 | return IRQ_HANDLED; | |
915 | } | |
916 | ||
0db055c9 BC |
917 | static int cc2520_get_platform_data(struct spi_device *spi, |
918 | struct cc2520_platform_data *pdata) | |
919 | { | |
920 | struct device_node *np = spi->dev.of_node; | |
921 | struct cc2520_private *priv = spi_get_drvdata(spi); | |
922 | ||
923 | if (!np) { | |
924 | struct cc2520_platform_data *spi_pdata = spi->dev.platform_data; | |
3ee0275d | 925 | |
0db055c9 BC |
926 | if (!spi_pdata) |
927 | return -ENOENT; | |
928 | *pdata = *spi_pdata; | |
85998229 | 929 | priv->fifo_pin = pdata->fifo; |
0db055c9 BC |
930 | return 0; |
931 | } | |
932 | ||
933 | pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0); | |
934 | priv->fifo_pin = pdata->fifo; | |
935 | ||
936 | pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0); | |
937 | ||
938 | pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0); | |
939 | pdata->cca = of_get_named_gpio(np, "cca-gpio", 0); | |
940 | pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0); | |
941 | pdata->reset = of_get_named_gpio(np, "reset-gpio", 0); | |
942 | ||
1a1bc59c VB |
943 | /* CC2591 front end for CC2520 */ |
944 | if (of_property_read_bool(np, "amplified")) | |
945 | priv->amplified = true; | |
f0b7d43c | 946 | |
0db055c9 BC |
947 | return 0; |
948 | } | |
949 | ||
0da6bc8c VB |
950 | static int cc2520_hw_init(struct cc2520_private *priv) |
951 | { | |
952 | u8 status = 0, state = 0xff; | |
953 | int ret; | |
954 | int timeout = 100; | |
f0b7d43c BC |
955 | struct cc2520_platform_data pdata; |
956 | ||
957 | ret = cc2520_get_platform_data(priv->spi, &pdata); | |
958 | if (ret) | |
959 | goto err_ret; | |
0da6bc8c VB |
960 | |
961 | ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state); | |
962 | if (ret) | |
963 | goto err_ret; | |
964 | ||
965 | if (state != STATE_IDLE) | |
966 | return -EINVAL; | |
967 | ||
968 | do { | |
969 | ret = cc2520_get_status(priv, &status); | |
970 | if (ret) | |
971 | goto err_ret; | |
972 | ||
973 | if (timeout-- <= 0) { | |
974 | dev_err(&priv->spi->dev, "oscillator start failed!\n"); | |
975 | return ret; | |
976 | } | |
977 | udelay(1); | |
978 | } while (!(status & CC2520_STATUS_XOSC32M_STABLE)); | |
979 | ||
980 | dev_vdbg(&priv->spi->dev, "oscillator brought up\n"); | |
981 | ||
f0b7d43c BC |
982 | /* If the CC2520 is connected to a CC2591 amplifier, we must both |
983 | * configure GPIOs on the CC2520 to correctly configure the CC2591 | |
984 | * and change a couple settings of the CC2520 to work with the | |
985 | * amplifier. See section 8 page 17 of TI application note AN065. | |
986 | * http://www.ti.com/lit/an/swra229a/swra229a.pdf | |
987 | */ | |
1a1bc59c | 988 | if (priv->amplified) { |
f0b7d43c BC |
989 | ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x16); |
990 | if (ret) | |
991 | goto err_ret; | |
992 | ||
993 | ret = cc2520_write_register(priv, CC2520_GPIOCTRL0, 0x46); | |
994 | if (ret) | |
995 | goto err_ret; | |
996 | ||
997 | ret = cc2520_write_register(priv, CC2520_GPIOCTRL5, 0x47); | |
998 | if (ret) | |
999 | goto err_ret; | |
1000 | ||
1001 | ret = cc2520_write_register(priv, CC2520_GPIOPOLARITY, 0x1e); | |
1002 | if (ret) | |
1003 | goto err_ret; | |
1004 | ||
1005 | ret = cc2520_write_register(priv, CC2520_TXCTRL, 0xc1); | |
1006 | if (ret) | |
1007 | goto err_ret; | |
1008 | } else { | |
f0b7d43c BC |
1009 | ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11); |
1010 | if (ret) | |
1011 | goto err_ret; | |
1012 | } | |
0da6bc8c | 1013 | |
f0b7d43c | 1014 | /* Registers default value: section 28.1 in Datasheet */ |
59869ebf BC |
1015 | |
1016 | /* Set the CCA threshold to -50 dBm. This seems to have been copied | |
1017 | * from the TinyOS CC2520 driver and is much higher than the -84 dBm | |
1018 | * threshold suggested in the datasheet. | |
1019 | */ | |
0da6bc8c VB |
1020 | ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A); |
1021 | if (ret) | |
1022 | goto err_ret; | |
1023 | ||
1024 | ret = cc2520_write_register(priv, CC2520_MDMCTRL0, 0x85); | |
1025 | if (ret) | |
1026 | goto err_ret; | |
1027 | ||
1028 | ret = cc2520_write_register(priv, CC2520_MDMCTRL1, 0x14); | |
1029 | if (ret) | |
1030 | goto err_ret; | |
1031 | ||
1032 | ret = cc2520_write_register(priv, CC2520_RXCTRL, 0x3f); | |
1033 | if (ret) | |
1034 | goto err_ret; | |
1035 | ||
1036 | ret = cc2520_write_register(priv, CC2520_FSCTRL, 0x5a); | |
1037 | if (ret) | |
1038 | goto err_ret; | |
1039 | ||
1040 | ret = cc2520_write_register(priv, CC2520_FSCAL1, 0x2b); | |
1041 | if (ret) | |
1042 | goto err_ret; | |
1043 | ||
0da6bc8c VB |
1044 | ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10); |
1045 | if (ret) | |
1046 | goto err_ret; | |
1047 | ||
1048 | ret = cc2520_write_register(priv, CC2520_ADCTEST1, 0x0e); | |
1049 | if (ret) | |
1050 | goto err_ret; | |
1051 | ||
1052 | ret = cc2520_write_register(priv, CC2520_ADCTEST2, 0x03); | |
1053 | if (ret) | |
1054 | goto err_ret; | |
1055 | ||
59869ebf BC |
1056 | /* Configure registers correctly for this driver. */ |
1057 | ret = cc2520_write_register(priv, CC2520_FRMCTRL1, | |
1058 | FRMCTRL1_SET_RXENMASK_ON_TX | | |
1059 | FRMCTRL1_IGNORE_TX_UNDERF); | |
0da6bc8c VB |
1060 | if (ret) |
1061 | goto err_ret; | |
1062 | ||
1063 | ret = cc2520_write_register(priv, CC2520_FIFOPCTRL, 127); | |
1064 | if (ret) | |
1065 | goto err_ret; | |
1066 | ||
1067 | return 0; | |
1068 | ||
1069 | err_ret: | |
1070 | return ret; | |
1071 | } | |
1072 | ||
0da6bc8c VB |
1073 | static int cc2520_probe(struct spi_device *spi) |
1074 | { | |
1075 | struct cc2520_private *priv; | |
0db055c9 | 1076 | struct cc2520_platform_data pdata; |
0da6bc8c VB |
1077 | int ret; |
1078 | ||
5eb9f8ca | 1079 | priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); |
f50f1c37 VB |
1080 | if (!priv) |
1081 | return -ENOMEM; | |
0da6bc8c VB |
1082 | |
1083 | spi_set_drvdata(spi, priv); | |
1084 | ||
0db055c9 BC |
1085 | ret = cc2520_get_platform_data(spi, &pdata); |
1086 | if (ret < 0) { | |
0da6bc8c VB |
1087 | dev_err(&spi->dev, "no platform data\n"); |
1088 | return -EINVAL; | |
1089 | } | |
1090 | ||
1091 | priv->spi = spi; | |
1092 | ||
1093 | priv->buf = devm_kzalloc(&spi->dev, | |
1094 | SPI_COMMAND_BUFFER, GFP_KERNEL); | |
f50f1c37 VB |
1095 | if (!priv->buf) |
1096 | return -ENOMEM; | |
0da6bc8c VB |
1097 | |
1098 | mutex_init(&priv->buffer_mutex); | |
1099 | INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork); | |
1100 | spin_lock_init(&priv->lock); | |
1101 | init_completion(&priv->tx_complete); | |
1102 | ||
1a1bc59c VB |
1103 | /* Assumption that CC2591 is not connected */ |
1104 | priv->amplified = false; | |
1105 | ||
0da6bc8c | 1106 | /* Request all the gpio's */ |
0db055c9 | 1107 | if (!gpio_is_valid(pdata.fifo)) { |
0da6bc8c VB |
1108 | dev_err(&spi->dev, "fifo gpio is not valid\n"); |
1109 | ret = -EINVAL; | |
1110 | goto err_hw_init; | |
1111 | } | |
1112 | ||
0db055c9 | 1113 | ret = devm_gpio_request_one(&spi->dev, pdata.fifo, |
0da6bc8c VB |
1114 | GPIOF_IN, "fifo"); |
1115 | if (ret) | |
1116 | goto err_hw_init; | |
1117 | ||
0db055c9 | 1118 | if (!gpio_is_valid(pdata.cca)) { |
0da6bc8c VB |
1119 | dev_err(&spi->dev, "cca gpio is not valid\n"); |
1120 | ret = -EINVAL; | |
1121 | goto err_hw_init; | |
1122 | } | |
1123 | ||
0db055c9 | 1124 | ret = devm_gpio_request_one(&spi->dev, pdata.cca, |
0da6bc8c VB |
1125 | GPIOF_IN, "cca"); |
1126 | if (ret) | |
1127 | goto err_hw_init; | |
1128 | ||
0db055c9 | 1129 | if (!gpio_is_valid(pdata.fifop)) { |
0da6bc8c VB |
1130 | dev_err(&spi->dev, "fifop gpio is not valid\n"); |
1131 | ret = -EINVAL; | |
1132 | goto err_hw_init; | |
1133 | } | |
1134 | ||
0db055c9 | 1135 | ret = devm_gpio_request_one(&spi->dev, pdata.fifop, |
0da6bc8c VB |
1136 | GPIOF_IN, "fifop"); |
1137 | if (ret) | |
1138 | goto err_hw_init; | |
1139 | ||
0db055c9 | 1140 | if (!gpio_is_valid(pdata.sfd)) { |
0da6bc8c VB |
1141 | dev_err(&spi->dev, "sfd gpio is not valid\n"); |
1142 | ret = -EINVAL; | |
1143 | goto err_hw_init; | |
1144 | } | |
1145 | ||
0db055c9 | 1146 | ret = devm_gpio_request_one(&spi->dev, pdata.sfd, |
0da6bc8c VB |
1147 | GPIOF_IN, "sfd"); |
1148 | if (ret) | |
1149 | goto err_hw_init; | |
1150 | ||
0db055c9 | 1151 | if (!gpio_is_valid(pdata.reset)) { |
0da6bc8c VB |
1152 | dev_err(&spi->dev, "reset gpio is not valid\n"); |
1153 | ret = -EINVAL; | |
1154 | goto err_hw_init; | |
1155 | } | |
1156 | ||
0db055c9 | 1157 | ret = devm_gpio_request_one(&spi->dev, pdata.reset, |
0da6bc8c VB |
1158 | GPIOF_OUT_INIT_LOW, "reset"); |
1159 | if (ret) | |
1160 | goto err_hw_init; | |
1161 | ||
0db055c9 | 1162 | if (!gpio_is_valid(pdata.vreg)) { |
0da6bc8c VB |
1163 | dev_err(&spi->dev, "vreg gpio is not valid\n"); |
1164 | ret = -EINVAL; | |
1165 | goto err_hw_init; | |
1166 | } | |
1167 | ||
0db055c9 | 1168 | ret = devm_gpio_request_one(&spi->dev, pdata.vreg, |
0da6bc8c VB |
1169 | GPIOF_OUT_INIT_LOW, "vreg"); |
1170 | if (ret) | |
1171 | goto err_hw_init; | |
1172 | ||
0db055c9 | 1173 | gpio_set_value(pdata.vreg, HIGH); |
0da6bc8c VB |
1174 | usleep_range(100, 150); |
1175 | ||
0db055c9 | 1176 | gpio_set_value(pdata.reset, HIGH); |
0da6bc8c VB |
1177 | usleep_range(200, 250); |
1178 | ||
1179 | ret = cc2520_hw_init(priv); | |
1180 | if (ret) | |
1181 | goto err_hw_init; | |
1182 | ||
1183 | /* Set up fifop interrupt */ | |
1184 | ret = devm_request_irq(&spi->dev, | |
0db055c9 | 1185 | gpio_to_irq(pdata.fifop), |
0da6bc8c VB |
1186 | cc2520_fifop_isr, |
1187 | IRQF_TRIGGER_RISING, | |
1188 | dev_name(&spi->dev), | |
1189 | priv); | |
1190 | if (ret) { | |
1191 | dev_err(&spi->dev, "could not get fifop irq\n"); | |
1192 | goto err_hw_init; | |
1193 | } | |
1194 | ||
1195 | /* Set up sfd interrupt */ | |
1196 | ret = devm_request_irq(&spi->dev, | |
0db055c9 | 1197 | gpio_to_irq(pdata.sfd), |
0da6bc8c VB |
1198 | cc2520_sfd_isr, |
1199 | IRQF_TRIGGER_FALLING, | |
1200 | dev_name(&spi->dev), | |
1201 | priv); | |
1202 | if (ret) { | |
1203 | dev_err(&spi->dev, "could not get sfd irq\n"); | |
1204 | goto err_hw_init; | |
1205 | } | |
1206 | ||
1207 | ret = cc2520_register(priv); | |
1208 | if (ret) | |
1209 | goto err_hw_init; | |
1210 | ||
1211 | return 0; | |
1212 | ||
1213 | err_hw_init: | |
1214 | mutex_destroy(&priv->buffer_mutex); | |
1215 | flush_work(&priv->fifop_irqwork); | |
0da6bc8c VB |
1216 | return ret; |
1217 | } | |
1218 | ||
1219 | static int cc2520_remove(struct spi_device *spi) | |
1220 | { | |
1221 | struct cc2520_private *priv = spi_get_drvdata(spi); | |
1222 | ||
1223 | mutex_destroy(&priv->buffer_mutex); | |
1224 | flush_work(&priv->fifop_irqwork); | |
1225 | ||
5a504397 AA |
1226 | ieee802154_unregister_hw(priv->hw); |
1227 | ieee802154_free_hw(priv->hw); | |
0da6bc8c VB |
1228 | |
1229 | return 0; | |
1230 | } | |
1231 | ||
1232 | static const struct spi_device_id cc2520_ids[] = { | |
1233 | {"cc2520", }, | |
1234 | {}, | |
1235 | }; | |
1236 | MODULE_DEVICE_TABLE(spi, cc2520_ids); | |
1237 | ||
1238 | static const struct of_device_id cc2520_of_ids[] = { | |
1239 | {.compatible = "ti,cc2520", }, | |
1240 | {}, | |
1241 | }; | |
1242 | MODULE_DEVICE_TABLE(of, cc2520_of_ids); | |
1243 | ||
1244 | /* SPI driver structure */ | |
1245 | static struct spi_driver cc2520_driver = { | |
1246 | .driver = { | |
1247 | .name = "cc2520", | |
0da6bc8c VB |
1248 | .of_match_table = of_match_ptr(cc2520_of_ids), |
1249 | }, | |
1250 | .id_table = cc2520_ids, | |
1251 | .probe = cc2520_probe, | |
1252 | .remove = cc2520_remove, | |
1253 | }; | |
1254 | module_spi_driver(cc2520_driver); | |
1255 | ||
1256 | MODULE_AUTHOR("Varka Bhadram <varkab@cdac.in>"); | |
1257 | MODULE_DESCRIPTION("CC2520 Transceiver Driver"); | |
1258 | MODULE_LICENSE("GPL v2"); |