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