IEEE 802.15.4 subsystem
Sign in or create your account | Project List | Help
IEEE 802.15.4 subsystem Git Source Tree
Root/
Source at commit 624caeb00652f6118dc64698b7dad6fa3e5bdcc2 created 7 years 5 days ago. By Werner Almesberger, atusb/: use VR, POWERED, and LED from kicad-libs | |
---|---|
1 | /* |
2 | * lib/atben.c - ATRF access functions library (Ben 8:10 card version) |
3 | * |
4 | * Written 2010-2011, 2013 by Werner Almesberger |
5 | * Copyright 2010-2011, 2013 Werner Almesberger |
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 | #include <stdint.h> |
15 | #include <stdlib.h> |
16 | #include <stdio.h> |
17 | #include <unistd.h> |
18 | #include <fcntl.h> |
19 | #include <dirent.h> |
20 | #include <fnmatch.h> |
21 | #include <sys/mman.h> |
22 | |
23 | #include "at86rf230.h" |
24 | #include "timeout.h" |
25 | #include "driver.h" |
26 | |
27 | |
28 | enum { |
29 | VDD_OFF = 1 << 2, /* VDD disable, PD02 */ |
30 | MOSI = 1 << 8, /* CMD, PD08 */ |
31 | #ifdef OLD |
32 | CLK = 1 << 9, /* CLK, PD09 */ |
33 | #else |
34 | SLP_TR = 1 << 9, /* CLK, PD09 */ |
35 | #endif |
36 | MISO = 1 << 10, /* DAT0, PD10 */ |
37 | SCLK = 1 << 11, /* DAT1, PD11 */ |
38 | IRQ = 1 << 12, /* DAT2, PD12 */ |
39 | nSEL = 1 << 13, /* DAT3/CD, PD13 */ |
40 | }; |
41 | |
42 | |
43 | #define MMC_PATH "/sys/bus/platform/drivers/jz4740-mmc" |
44 | #define AT86RF230_PATH "/sys/bus/spi/drivers/at86rf230" |
45 | |
46 | |
47 | #define SOC_BASE 0x10000000 |
48 | |
49 | #define REG(n) (*(volatile uint32_t *) (dsc->mem+(n))) |
50 | |
51 | #define CGU(n) REG(0x00000+(n)) |
52 | #define GPIO(n) REG(0x10000+(n)) |
53 | #define MSC(n) REG(0x21000+(n)) |
54 | |
55 | #define PDPIN GPIO(0x300) /* port D pin level */ |
56 | #define PDDATS GPIO(0x314) /* port D data set */ |
57 | #define PDDATC GPIO(0x318) /* port D data clear */ |
58 | #define PDFUNS GPIO(0x344) /* port D function set */ |
59 | #define PDFUNC GPIO(0x348) /* port D function clear */ |
60 | #define PDDIRS GPIO(0x364) /* port D direction set */ |
61 | #define PDDIRC GPIO(0x368) /* port D direction clear */ |
62 | |
63 | #define MSC_STRPCL MSC(0x00) /* Start/stop MMC/SD clock */ |
64 | #define MSC_CLKRT MSC(0x08) /* MSC Clock Rate */ |
65 | |
66 | #define CLKGR CGU(0x0020) /* Clock Gate */ |
67 | #define MSCCDR CGU(0x0068) /* MSC device clock divider */ |
68 | |
69 | |
70 | #define PAGE_SIZE 4096 |
71 | |
72 | |
73 | struct atben_dsc { |
74 | int fd; |
75 | void *mem; |
76 | }; |
77 | |
78 | |
79 | /* ----- Reset functions --------------------------------------------------- */ |
80 | |
81 | |
82 | static void wait_for_power(void) |
83 | { |
84 | /* |
85 | * Give power time to stabilize and the chip time to reset. |
86 | * Power takes about 2 ms to ramp up. We wait 10 ms to be sure. |
87 | */ |
88 | usleep(10*1000); |
89 | } |
90 | |
91 | |
92 | static void atben_cycle(struct atben_dsc *dsc) |
93 | { |
94 | /* stop the MMC bus clock */ |
95 | MSC_STRPCL = 1; |
96 | |
97 | /* drive all outputs low (including the MMC bus clock) */ |
98 | #ifdef OLD |
99 | PDDATC = MOSI | CLK | SCLK | nSEL; |
100 | |
101 | /* make the MMC bus clock a regular output */ |
102 | PDFUNC = CLK; |
103 | #else |
104 | PDDATC = MOSI | SLP_TR | SCLK | nSEL; |
105 | PDFUNC = SLP_TR; |
106 | #endif |
107 | |
108 | /* cut the power */ |
109 | PDDATS = VDD_OFF; |
110 | |
111 | /* Power drains within about 20 ms. Wait 100 ms to be sure. */ |
112 | usleep(100*1000); |
113 | |
114 | /* drive MOSI and nSS high */ |
115 | PDDATS = MOSI | nSEL; |
116 | |
117 | /* precharge the capacitors to avoid current surge */ |
118 | wait_for_power(); |
119 | |
120 | #ifdef OLD |
121 | |
122 | /* return the bus clock output to the MMC controller */ |
123 | PDFUNS = CLK; |
124 | |
125 | /* start MMC clock output */ |
126 | MSC_STRPCL = 2; |
127 | #endif |
128 | |
129 | /* supply power */ |
130 | PDDATC = VDD_OFF; |
131 | |
132 | wait_for_power(); |
133 | } |
134 | |
135 | |
136 | /* ----- Low-level SPI operations ------------------------------------------ */ |
137 | |
138 | |
139 | static void spi_begin(struct atben_dsc *dsc) |
140 | { |
141 | PDDATC = nSEL; |
142 | } |
143 | |
144 | |
145 | static void spi_end(struct atben_dsc *dsc) |
146 | { |
147 | PDDATS = nSEL; |
148 | } |
149 | |
150 | |
151 | static void spi_send(struct atben_dsc *dsc, uint8_t v) |
152 | { |
153 | uint8_t mask; |
154 | |
155 | for (mask = 0x80; mask; mask >>= 1) { |
156 | if (v & mask) |
157 | PDDATS = MOSI; |
158 | else |
159 | PDDATC = MOSI; |
160 | PDDATS = SCLK; |
161 | PDDATC = SCLK; |
162 | } |
163 | } |
164 | |
165 | |
166 | static uint8_t spi_recv(struct atben_dsc *dsc) |
167 | { |
168 | uint8_t res = 0; |
169 | uint8_t mask; |
170 | |
171 | for (mask = 0x80; mask; mask >>= 1) { |
172 | if (PDPIN & MISO) |
173 | res |= mask; |
174 | PDDATS = SCLK; |
175 | PDDATC = SCLK; |
176 | } |
177 | return res; |
178 | } |
179 | |
180 | |
181 | /* ----- Driver operations ------------------------------------------------- */ |
182 | |
183 | |
184 | static void atben_reset_rf(void *handle) |
185 | { |
186 | struct atben_dsc *dsc = handle; |
187 | |
188 | atben_cycle(dsc); |
189 | wait_for_power(); |
190 | } |
191 | |
192 | |
193 | static int find_file(const char *path, const char *pattern) |
194 | { |
195 | DIR *dir; |
196 | const struct dirent *de; |
197 | |
198 | dir = opendir(path); |
199 | if (!dir) |
200 | return 0; |
201 | while ((de = readdir(dir))) |
202 | if (!fnmatch(pattern, de->d_name, 0)) |
203 | break; |
204 | if (closedir(dir) < 0) { |
205 | perror("closedir"); |
206 | exit(1); |
207 | } |
208 | return !!de; |
209 | } |
210 | |
211 | |
212 | static int slot_in_use(void) |
213 | { |
214 | if (find_file(MMC_PATH, "*.0")) { |
215 | fprintf(stderr, |
216 | "The 8:10 card slot is currently used by the MMC controller.\n" |
217 | "You can try to detach it with:\n" |
218 | "# echo jz4740-mmc.0 >/sys/bus/platform/drivers/jz4740-mmc/unbind\n"); |
219 | return 1; |
220 | } |
221 | if (find_file(AT86RF230_PATH, "*.0")) { |
222 | fprintf(stderr, |
223 | "The 8:10 card slot is currently used by the AT86RF230 kernel driver.\n" |
224 | "You can try to detach it with:\n" |
225 | "# cd /sys/bus/spi/drivers/at86rf230\n" |
226 | "# echo spi*.0 >unbind\n"); |
227 | return 1; |
228 | } |
229 | return 0; |
230 | } |
231 | |
232 | |
233 | static void *atben_open(const char *arg) |
234 | { |
235 | struct atben_dsc *dsc; |
236 | |
237 | if (slot_in_use()) |
238 | return NULL; |
239 | |
240 | dsc = malloc(sizeof(*dsc)); |
241 | if (!dsc) { |
242 | perror("malloc"); |
243 | exit(1); |
244 | } |
245 | |
246 | dsc->fd = open("/dev/mem", O_RDWR | O_SYNC); |
247 | if (dsc->fd < 0) { |
248 | perror("/dev/mem"); |
249 | exit(1); |
250 | } |
251 | dsc->mem = mmap(NULL, PAGE_SIZE*3*16, PROT_READ | PROT_WRITE, |
252 | MAP_SHARED, dsc->fd, SOC_BASE); |
253 | if (dsc->mem == MAP_FAILED) { |
254 | perror("mmap"); |
255 | exit(1); |
256 | } |
257 | |
258 | /* set the output levels */ |
259 | PDDATS = nSEL | VDD_OFF; |
260 | PDDATC = SCLK; |
261 | |
262 | /* take the GPIOs away from the MMC controller */ |
263 | #ifdef OLD |
264 | PDFUNC = MOSI | MISO | SCLK | IRQ | nSEL; |
265 | PDFUNS = CLK; |
266 | #else |
267 | PDFUNC = MOSI | MISO | SCLK | IRQ | nSEL | SLP_TR; |
268 | #endif |
269 | |
270 | /* set the pin directions */ |
271 | PDDIRC = MISO | IRQ; |
272 | #ifdef OLD |
273 | PDDIRS = MOSI | CLK | SCLK | nSEL; |
274 | #else |
275 | PDDIRS = MOSI | SLP_TR | SCLK | nSEL; |
276 | #endif |
277 | |
278 | /* let capacitors precharge */ |
279 | wait_for_power(); |
280 | |
281 | /* enable power */ |
282 | PDDATC = VDD_OFF; |
283 | |
284 | #ifdef OLD |
285 | /* set the MSC clock to 336 MHz / 21 = 16 MHz */ |
286 | MSCCDR = 20; |
287 | /* |
288 | * Enable the MSC clock. We need to do this before accessing any |
289 | * registers of the MSC block ! |
290 | */ |
291 | CLKGR &= ~(1 << 7); |
292 | /* bus clock = MSC clock / 1 */ |
293 | MSC_CLKRT = 0; |
294 | /* start MMC clock output */ |
295 | MSC_STRPCL = 2; |
296 | #endif |
297 | |
298 | wait_for_power(); |
299 | atben_reset_rf(dsc); |
300 | |
301 | return dsc; |
302 | } |
303 | |
304 | |
305 | static void atben_close(void *arg) |
306 | { |
307 | struct atben_dsc *dsc = arg; |
308 | |
309 | /* stop the MMC bus clock */ |
310 | MSC_STRPCL = 1; |
311 | |
312 | /* cut the power */ |
313 | PDDATS = VDD_OFF; |
314 | |
315 | /* make all MMC pins inputs */ |
316 | #ifdef OLD |
317 | PDDIRC = MOSI | MISO | CLK | SCLK | IRQ | nSEL; |
318 | #else |
319 | PDDIRC = MOSI | MISO | SLP_TR | SCLK | IRQ | nSEL; |
320 | #endif |
321 | } |
322 | |
323 | |
324 | static void atben_reg_write(void *handle, uint8_t reg, uint8_t v) |
325 | { |
326 | struct atben_dsc *dsc = handle; |
327 | |
328 | spi_begin(dsc); |
329 | spi_send(dsc, AT86RF230_REG_WRITE | reg); |
330 | spi_send(dsc, v); |
331 | spi_end(dsc); |
332 | } |
333 | |
334 | |
335 | static uint8_t atben_reg_read(void *handle, uint8_t reg) |
336 | { |
337 | struct atben_dsc *dsc = handle; |
338 | uint8_t res; |
339 | |
340 | spi_begin(dsc); |
341 | spi_send(dsc, AT86RF230_REG_READ | reg); |
342 | res = spi_recv(dsc); |
343 | spi_end(dsc); |
344 | return res; |
345 | } |
346 | |
347 | |
348 | static void atben_buf_write(void *handle, const void *buf, int size) |
349 | { |
350 | struct atben_dsc *dsc = handle; |
351 | |
352 | spi_begin(dsc); |
353 | spi_send(dsc, AT86RF230_BUF_WRITE); |
354 | spi_send(dsc, size); |
355 | while (size--) |
356 | spi_send(dsc, *(uint8_t *) buf++); |
357 | spi_end(dsc); |
358 | } |
359 | |
360 | |
361 | static int atben_buf_read(void *handle, void *buf, int size) |
362 | { |
363 | struct atben_dsc *dsc = handle; |
364 | uint8_t len, i; |
365 | |
366 | spi_begin(dsc); |
367 | spi_send(dsc, AT86RF230_BUF_READ); |
368 | len = spi_recv(dsc); |
369 | len++; /* LQI */ |
370 | if (len > size) |
371 | len = size; |
372 | for (i = 0; i != len; i++) |
373 | *(uint8_t *) buf++ = spi_recv(dsc); |
374 | spi_end(dsc); |
375 | return len; |
376 | } |
377 | |
378 | |
379 | static void atben_sram_write(void *handle, uint8_t addr, uint8_t v) |
380 | { |
381 | struct atben_dsc *dsc = handle; |
382 | |
383 | spi_begin(dsc); |
384 | spi_send(dsc, AT86RF230_SRAM_WRITE); |
385 | spi_send(dsc, addr); |
386 | spi_send(dsc, v); |
387 | spi_end(dsc); |
388 | } |
389 | |
390 | |
391 | static uint8_t atben_sram_read(void *handle, uint8_t addr) |
392 | { |
393 | struct atben_dsc *dsc = handle; |
394 | uint8_t res; |
395 | |
396 | spi_begin(dsc); |
397 | spi_send(dsc, AT86RF230_SRAM_READ); |
398 | spi_send(dsc, addr); |
399 | res = spi_recv(dsc); |
400 | spi_end(dsc); |
401 | return res; |
402 | } |
403 | |
404 | |
405 | /* ----- SLP_TR ------------------------------------------------------------ */ |
406 | |
407 | |
408 | static void atben_slp_tr(void *handle, int on, int pulse) |
409 | { |
410 | struct atben_dsc *dsc = handle; |
411 | |
412 | if (on) |
413 | PDDATS = SLP_TR; |
414 | else |
415 | PDDATC = SLP_TR; |
416 | if (!pulse) |
417 | return; |
418 | if (on) |
419 | PDDATC = SLP_TR; |
420 | else |
421 | PDDATS = SLP_TR; |
422 | } |
423 | |
424 | |
425 | /* ----- RF interrupt ------------------------------------------------------ */ |
426 | |
427 | |
428 | static int atben_interrupt(void *handle) |
429 | { |
430 | struct atben_dsc *dsc = handle; |
431 | |
432 | return !!(PDPIN & IRQ); |
433 | } |
434 | |
435 | |
436 | int atben_interrupt_wait(void *handle, int timeout_ms) |
437 | { |
438 | struct timeout to; |
439 | int timedout = 0; |
440 | uint8_t irq; |
441 | |
442 | if (timeout_ms > 0) |
443 | timeout_start(&to, timeout_ms); |
444 | while (1) { |
445 | if (timeout_ms > 0) |
446 | timedout = timeout_reached(&to); |
447 | if (atben_interrupt(handle)) { |
448 | irq = atben_reg_read(handle, REG_IRQ_STATUS); |
449 | if (irq) |
450 | return irq; |
451 | fprintf(stderr, "ignoring stray interrupt\n"); |
452 | } |
453 | if (timedout) |
454 | return 0; |
455 | if (timeout_ms >= 0) |
456 | usleep(1000); |
457 | } |
458 | |
459 | return 0; |
460 | } |
461 | |
462 | |
463 | /* ----- Driver-specific hacks --------------------------------------------- */ |
464 | |
465 | |
466 | void *atben_regs(void *handle) |
467 | { |
468 | struct atben_dsc *dsc = handle; |
469 | |
470 | return dsc->mem; |
471 | } |
472 | |
473 | |
474 | /* ----- Driver interface -------------------------------------------------- */ |
475 | |
476 | |
477 | struct atrf_driver atben_driver = { |
478 | .name = "ben", |
479 | .open = atben_open, |
480 | .close = atben_close, |
481 | .reset = NULL, |
482 | .reset_rf = atben_reset_rf, |
483 | .test_mode = NULL, |
484 | .slp_tr = atben_slp_tr, |
485 | .reg_write = atben_reg_write, |
486 | .reg_read = atben_reg_read, |
487 | .buf_write = atben_buf_write, |
488 | .buf_read = atben_buf_read, |
489 | .sram_write = atben_sram_write, |
490 | .sram_read = atben_sram_read, |
491 | .interrupt_wait = atben_interrupt_wait, |
492 | }; |
493 |