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 32de4b6ad4b428b1aa13791fdb52af7b21618d11 created 10 years 1 month ago. By Werner Almesberger, great atusd -> atben renaming: renamed tools/atusd.c and Makefile reference | |
---|---|
1 | /* |
2 | * lib/atusd.c - ATRF access functions library (uSD 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 "driver.h" |
23 | |
24 | |
25 | enum { |
26 | VDD_OFF = 1 << 2, /* VDD disable, PD02 */ |
27 | MOSI = 1 << 8, /* CMD, PD08 */ |
28 | #ifdef OLD |
29 | CLK = 1 << 9, /* CLK, PD09 */ |
30 | #else |
31 | SLP_TR = 1 << 9, /* CLK, PD09 */ |
32 | #endif |
33 | MISO = 1 << 10, /* DAT0, PD10 */ |
34 | SCLK = 1 << 11, /* DAT1, PD11 */ |
35 | IRQ = 1 << 12, /* DAT2, PD12 */ |
36 | nSEL = 1 << 13, /* DAT3/CD, PD13 */ |
37 | }; |
38 | |
39 | |
40 | #define SOC_BASE 0x10000000 |
41 | |
42 | #define REG(n) (*(volatile uint32_t *) (dsc->mem+(n))) |
43 | |
44 | #define CGU(n) REG(0x00000+(n)) |
45 | #define GPIO(n) REG(0x10000+(n)) |
46 | #define MSC(n) REG(0x21000+(n)) |
47 | |
48 | #define PDPIN GPIO(0x300) /* port D pin level */ |
49 | #define PDDATS GPIO(0x314) /* port D data set */ |
50 | #define PDDATC GPIO(0x318) /* port D data clear */ |
51 | #define PDFUNS GPIO(0x344) /* port D function set */ |
52 | #define PDFUNC GPIO(0x348) /* port D function clear */ |
53 | #define PDDIRS GPIO(0x364) /* port D direction set */ |
54 | #define PDDIRC GPIO(0x368) /* port D direction clear */ |
55 | |
56 | #define MSC_STRPCL MSC(0x00) /* Start/stop MMC/SD clock */ |
57 | #define MSC_CLKRT MSC(0x08) /* MSC Clock Rate */ |
58 | |
59 | #define CLKGR CGU(0x0020) /* Clock Gate */ |
60 | #define MSCCDR CGU(0x0068) /* MSC device clock divider */ |
61 | |
62 | |
63 | #define PAGE_SIZE 4096 |
64 | |
65 | |
66 | struct atusd_dsc { |
67 | int fd; |
68 | void *mem; |
69 | }; |
70 | |
71 | |
72 | /* ----- Reset functions --------------------------------------------------- */ |
73 | |
74 | |
75 | static void wait_for_power(void) |
76 | { |
77 | /* |
78 | * Give power time to stabilize and the chip time to reset. |
79 | * Power takes about 2 ms to ramp up. We wait 10 ms to be sure. |
80 | */ |
81 | usleep(10*1000); |
82 | } |
83 | |
84 | |
85 | static void atusd_cycle(struct atusd_dsc *dsc) |
86 | { |
87 | /* stop the MMC bus clock */ |
88 | MSC_STRPCL = 1; |
89 | |
90 | /* drive all outputs low (including the MMC bus clock) */ |
91 | #ifdef OLD |
92 | PDDATC = MOSI | CLK | SCLK | nSEL; |
93 | |
94 | /* make the MMC bus clock a regular output */ |
95 | PDFUNC = CLK; |
96 | #else |
97 | PDDATC = MOSI | SLP_TR | SCLK | nSEL; |
98 | PDFUNC = SLP_TR; |
99 | #endif |
100 | |
101 | /* cut the power */ |
102 | PDDATS = VDD_OFF; |
103 | |
104 | /* Power drains within about 20 ms. Wait 100 ms to be sure. */ |
105 | usleep(100*1000); |
106 | |
107 | /* drive MOSI and nSS high */ |
108 | PDDATS = MOSI | nSEL; |
109 | |
110 | /* precharge the capacitors to avoid current surge */ |
111 | wait_for_power(); |
112 | |
113 | #ifdef OLD |
114 | |
115 | /* return the bus clock output to the MMC controller */ |
116 | PDFUNS = CLK; |
117 | |
118 | /* start MMC clock output */ |
119 | MSC_STRPCL = 2; |
120 | #endif |
121 | |
122 | /* supply power */ |
123 | PDDATC = VDD_OFF; |
124 | |
125 | wait_for_power(); |
126 | } |
127 | |
128 | |
129 | /* ----- Low-level SPI operations ------------------------------------------ */ |
130 | |
131 | |
132 | static void spi_begin(struct atusd_dsc *dsc) |
133 | { |
134 | PDDATC = nSEL; |
135 | } |
136 | |
137 | |
138 | static void spi_end(struct atusd_dsc *dsc) |
139 | { |
140 | PDDATS = nSEL; |
141 | } |
142 | |
143 | |
144 | static void spi_send(struct atusd_dsc *dsc, uint8_t v) |
145 | { |
146 | uint8_t mask; |
147 | |
148 | for (mask = 0x80; mask; mask >>= 1) { |
149 | if (v & mask) |
150 | PDDATS = MOSI; |
151 | else |
152 | PDDATC = MOSI; |
153 | PDDATS = SCLK; |
154 | PDDATC = SCLK; |
155 | } |
156 | } |
157 | |
158 | |
159 | static uint8_t spi_recv(struct atusd_dsc *dsc) |
160 | { |
161 | uint8_t res = 0; |
162 | uint8_t mask; |
163 | |
164 | for (mask = 0x80; mask; mask >>= 1) { |
165 | if (PDPIN & MISO) |
166 | res |= mask; |
167 | PDDATS = SCLK; |
168 | PDDATC = SCLK; |
169 | } |
170 | return res; |
171 | } |
172 | |
173 | |
174 | /* ----- Driver operations ------------------------------------------------- */ |
175 | |
176 | |
177 | static void atusd_reset_rf(void *handle) |
178 | { |
179 | struct atusd_dsc *dsc = handle; |
180 | |
181 | atusd_cycle(dsc); |
182 | wait_for_power(); |
183 | } |
184 | |
185 | |
186 | static void *atusd_open(void) |
187 | { |
188 | struct atusd_dsc *dsc; |
189 | |
190 | dsc = malloc(sizeof(*dsc)); |
191 | if (!dsc) { |
192 | perror("malloc"); |
193 | exit(1); |
194 | } |
195 | |
196 | dsc->fd = open("/dev/mem", O_RDWR | O_SYNC); |
197 | if (dsc->fd < 0) { |
198 | perror("/dev/mem"); |
199 | exit(1); |
200 | } |
201 | dsc->mem = mmap(NULL, PAGE_SIZE*3*16, PROT_READ | PROT_WRITE, |
202 | MAP_SHARED, dsc->fd, SOC_BASE); |
203 | if (dsc->mem == MAP_FAILED) { |
204 | perror("mmap"); |
205 | exit(1); |
206 | } |
207 | |
208 | /* set the output levels */ |
209 | PDDATS = nSEL | VDD_OFF; |
210 | PDDATC = SCLK; |
211 | |
212 | /* take the GPIOs away from the MMC controller */ |
213 | #ifdef OLD |
214 | PDFUNC = MOSI | MISO | SCLK | IRQ | nSEL; |
215 | PDFUNS = CLK; |
216 | #else |
217 | PDFUNC = MOSI | MISO | SCLK | IRQ | nSEL | SLP_TR; |
218 | #endif |
219 | |
220 | /* set the pin directions */ |
221 | PDDIRC = MISO | IRQ; |
222 | #ifdef OLD |
223 | PDDIRS = MOSI | CLK | SCLK | nSEL; |
224 | #else |
225 | PDDIRS = MOSI | SLP_TR | SCLK | nSEL; |
226 | #endif |
227 | |
228 | /* let capacitors precharge */ |
229 | wait_for_power(); |
230 | |
231 | /* enable power */ |
232 | PDDATC = VDD_OFF; |
233 | |
234 | #ifdef OLD |
235 | /* set the MSC clock to 316 MHz / 21 = 16 MHz */ |
236 | MSCCDR = 20; |
237 | /* |
238 | * Enable the MSC clock. We need to do this before accessing any |
239 | * registers of the MSC block ! |
240 | */ |
241 | CLKGR &= ~(1 << 7); |
242 | /* bus clock = MSC clock / 1 */ |
243 | MSC_CLKRT = 0; |
244 | /* start MMC clock output */ |
245 | MSC_STRPCL = 2; |
246 | #endif |
247 | |
248 | wait_for_power(); |
249 | atusd_reset_rf(dsc); |
250 | |
251 | return dsc; |
252 | } |
253 | |
254 | |
255 | static void atusd_close(void *arg) |
256 | { |
257 | struct atusd_dsc *dsc = arg; |
258 | |
259 | /* stop the MMC bus clock */ |
260 | MSC_STRPCL = 1; |
261 | |
262 | /* cut the power */ |
263 | PDDATS = VDD_OFF; |
264 | |
265 | /* make all MMC pins inputs */ |
266 | #ifdef OLD |
267 | PDDIRC = MOSI | MISO | CLK | SCLK | IRQ | nSEL; |
268 | #else |
269 | PDDIRC = MOSI | MISO | SLP_TR | SCLK | IRQ | nSEL; |
270 | #endif |
271 | } |
272 | |
273 | |
274 | static void atusd_reg_write(void *handle, uint8_t reg, uint8_t v) |
275 | { |
276 | struct atusd_dsc *dsc = handle; |
277 | |
278 | spi_begin(dsc); |
279 | spi_send(dsc, AT86RF230_REG_WRITE | reg); |
280 | spi_send(dsc, v); |
281 | spi_end(dsc); |
282 | } |
283 | |
284 | |
285 | static uint8_t atusd_reg_read(void *handle, uint8_t reg) |
286 | { |
287 | struct atusd_dsc *dsc = handle; |
288 | uint8_t res; |
289 | |
290 | spi_begin(dsc); |
291 | spi_send(dsc, AT86RF230_REG_READ | reg); |
292 | res = spi_recv(dsc); |
293 | spi_end(dsc); |
294 | return res; |
295 | } |
296 | |
297 | |
298 | static void atusd_buf_write(void *handle, const void *buf, int size) |
299 | { |
300 | struct atusd_dsc *dsc = handle; |
301 | |
302 | spi_begin(dsc); |
303 | spi_send(dsc, AT86RF230_BUF_WRITE); |
304 | spi_send(dsc, size); |
305 | while (size--) |
306 | spi_send(dsc, *(uint8_t *) buf++); |
307 | spi_end(dsc); |
308 | } |
309 | |
310 | |
311 | static int atusd_buf_read(void *handle, void *buf, int size) |
312 | { |
313 | struct atusd_dsc *dsc = handle; |
314 | uint8_t len, i; |
315 | |
316 | spi_begin(dsc); |
317 | spi_send(dsc, AT86RF230_BUF_READ); |
318 | len = spi_recv(dsc); |
319 | len++; /* LQI */ |
320 | if (len > size) |
321 | len = size; |
322 | for (i = 0; i != len; i++) |
323 | *(uint8_t *) buf++ = spi_recv(dsc); |
324 | spi_end(dsc); |
325 | return len; |
326 | } |
327 | |
328 | |
329 | /* ----- RF interrupt ------------------------------------------------------ */ |
330 | |
331 | |
332 | static int atusd_interrupt(void *handle) |
333 | { |
334 | struct atusd_dsc *dsc = handle; |
335 | |
336 | return !!(PDPIN & IRQ); |
337 | } |
338 | |
339 | |
340 | /* ----- Driver interface -------------------------------------------------- */ |
341 | |
342 | |
343 | struct atrf_driver atusd_driver = { |
344 | .name = "uSD", |
345 | .open = atusd_open, |
346 | .close = atusd_close, |
347 | .reset = NULL, |
348 | .reset_rf = atusd_reset_rf, |
349 | .test_mode = NULL, |
350 | .reg_write = atusd_reg_write, |
351 | .reg_read = atusd_reg_read, |
352 | .buf_write = atusd_buf_write, |
353 | .buf_read = atusd_buf_read, |
354 | .interrupt = atusd_interrupt, |
355 | }; |
356 |