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 406517baa24266d2c86f9e6621bca1b56952d831 created 9 years 3 months ago. By Werner Almesberger, - tools/lib/atusd.c (atusd_cycle): we never turned power back on ? - tools/lib/atusd.c (atusd_open): need to power cycle the board to bring it up properly | |
---|---|
1 | /* |
2 | * lib/atusd.c - ATSPI access functions library (uSD version) |
3 | * |
4 | * Written 2010 by Werner Almesberger |
5 | * Copyright 2010 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 << 6, /* VDD disable, PD06 */ |
27 | MxSx = 1 << 8, /* CMD, PD08 */ |
28 | CLK = 1 << 9, /* CLK, PD09 */ |
29 | SCLK = 1 << 10, /* DAT0, PD10 */ |
30 | SLP_TR = 1 << 11, /* DAT1, PD11 */ |
31 | IRQ = 1 << 12, /* DAT2, PD12 */ |
32 | nSEL = 1 << 13, /* DAT3/CD, PD13 */ |
33 | }; |
34 | |
35 | |
36 | #define SOC_BASE 0x10000000 |
37 | |
38 | #define REG(n) (*(volatile uint32_t *) (dsc->mem+(n))) |
39 | |
40 | #define CGU(n) REG(0x00000+(n)) |
41 | #define GPIO(n) REG(0x10000+(n)) |
42 | #define MSC(n) REG(0x21000+(n)) |
43 | |
44 | #define PDPIN GPIO(0x300) /* port D pin level */ |
45 | #define PDDATS GPIO(0x314) /* port D data set */ |
46 | #define PDDATC GPIO(0x318) /* port D data clear */ |
47 | #define PDFUNS GPIO(0x344) /* port D function set */ |
48 | #define PDFUNC GPIO(0x348) /* port D function clear */ |
49 | #define PDDIRS GPIO(0x364) /* port D direction set */ |
50 | #define PDDIRC GPIO(0x368) /* port D direction clear */ |
51 | |
52 | #define MSC_STRPCL MSC(0x00) /* Start/stop MMC/SD clock */ |
53 | #define MSC_CLKRT MSC(0x08) /* MSC Clock Rate */ |
54 | |
55 | #define CLKGR CGU(0x0020) /* Clock Gate */ |
56 | #define MSCCDR CGU(0x0068) /* MSC device clock divider */ |
57 | |
58 | |
59 | #define PAGE_SIZE 4096 |
60 | |
61 | |
62 | struct atusd_dsc { |
63 | int fd; |
64 | void *mem; |
65 | }; |
66 | |
67 | |
68 | /* ----- Reset functions --------------------------------------------------- */ |
69 | |
70 | |
71 | static void atusd_cycle(struct atusd_dsc *dsc) |
72 | { |
73 | /* stop the MMC bus clock */ |
74 | MSC_STRPCL = 1; |
75 | |
76 | /* drive all outputs low (including the MMC bus clock) */ |
77 | PDDATC = MxSx | CLK | SCLK | SLP_TR | nSEL; |
78 | |
79 | /* make the MMC bus clock a regular output */ |
80 | PDFUNC = CLK; |
81 | |
82 | /* cut the power */ |
83 | PDDATS = VDD_OFF; |
84 | |
85 | /* Power drains within about 20 ms. Wait 100 ms to be sure. */ |
86 | usleep(100*1000); |
87 | |
88 | /* drive nSS high */ |
89 | PDDATS = nSEL; |
90 | |
91 | /* supply power */ |
92 | PDDATC = VDD_OFF; |
93 | |
94 | /* return the bus clock output to the MMC controller */ |
95 | PDFUNS = CLK; |
96 | |
97 | /* start MMC clock output */ |
98 | MSC_STRPCL = 2; |
99 | |
100 | /* |
101 | * Give power time to stabilize and the chip time to reset. |
102 | * Experiments show that even usleep(0) is long enough. |
103 | */ |
104 | usleep(10*1000); |
105 | } |
106 | |
107 | |
108 | #if 0 /* we probably won't need this anymore */ |
109 | static void atusd_reset(struct atusd_dsc *dsc) |
110 | { |
111 | /* activate reset */ |
112 | PDDATS = SLP_TR; |
113 | PDDATC = nSEL; |
114 | |
115 | /* |
116 | * Data sheet says 625 ns, programmer's guide says 6 us. Whom do we |
117 | * trust ? |
118 | */ |
119 | usleep(6); |
120 | |
121 | /* release reset */ |
122 | PDDATS = nSEL; |
123 | PDDATC = SLP_TR; |
124 | } |
125 | #endif |
126 | |
127 | |
128 | /* ----- Low-level SPI operations ------------------------------------------ */ |
129 | |
130 | |
131 | static void spi_begin(struct atusd_dsc *dsc) |
132 | { |
133 | PDDATC = nSEL; |
134 | } |
135 | |
136 | |
137 | static void spi_end(struct atusd_dsc *dsc) |
138 | { |
139 | PDDATS = nSEL; |
140 | } |
141 | |
142 | |
143 | static void spi_data_in(struct atusd_dsc *dsc) |
144 | { |
145 | PDDIRC = MxSx; |
146 | } |
147 | |
148 | |
149 | static void spi_data_out(struct atusd_dsc *dsc) |
150 | { |
151 | PDDIRS = MxSx; |
152 | } |
153 | |
154 | |
155 | |
156 | /* |
157 | * Send a sequence of bytes but leave the clock high on the last bit, so that |
158 | * we can turn around the data line for reads. |
159 | */ |
160 | |
161 | static void spi_send_partial(struct atusd_dsc *dsc, uint8_t v) |
162 | { |
163 | uint8_t mask; |
164 | |
165 | for (mask = 0x80; mask; mask >>= 1) { |
166 | PDDATC = SCLK; |
167 | if (v & mask) |
168 | PDDATS = MxSx; |
169 | else |
170 | PDDATC = MxSx; |
171 | PDDATS = SCLK; |
172 | } |
173 | } |
174 | |
175 | |
176 | static uint8_t spi_recv(struct atusd_dsc *dsc) |
177 | { |
178 | uint8_t res = 0; |
179 | uint8_t mask; |
180 | |
181 | for (mask = 0x80; mask; mask >>= 1) { |
182 | PDDATC = SCLK; |
183 | if (PDPIN & MxSx) |
184 | res |= mask; |
185 | PDDATS = SCLK; |
186 | } |
187 | PDDATC = SCLK; |
188 | return res; |
189 | } |
190 | |
191 | |
192 | static void spi_finish(struct atusd_dsc *dsc) |
193 | { |
194 | PDDATC = SCLK; |
195 | } |
196 | |
197 | |
198 | static void spi_send(struct atusd_dsc *dsc, uint8_t v) |
199 | { |
200 | spi_send_partial(dsc, v); |
201 | spi_finish(dsc); |
202 | } |
203 | |
204 | |
205 | /* ----- Driver operations ------------------------------------------------- */ |
206 | |
207 | |
208 | static void *atusd_open(void) |
209 | { |
210 | struct atusd_dsc *dsc; |
211 | |
212 | dsc = malloc(sizeof(*dsc)); |
213 | if (!dsc) { |
214 | perror("malloc"); |
215 | exit(1); |
216 | } |
217 | |
218 | dsc->fd = open("/dev/mem", O_RDWR); |
219 | if (dsc->fd < 0) { |
220 | perror("/dev/mem"); |
221 | exit(1); |
222 | } |
223 | dsc->mem = mmap(NULL, PAGE_SIZE*3*16, PROT_READ | PROT_WRITE, |
224 | MAP_SHARED, dsc->fd, SOC_BASE); |
225 | if (dsc->mem == MAP_FAILED) { |
226 | perror("mmap"); |
227 | exit(1); |
228 | } |
229 | |
230 | /* set the output levels */ |
231 | PDDATS = nSEL | VDD_OFF; |
232 | PDDATC = SCLK | SLP_TR; |
233 | |
234 | /* take the GPIOs away from the MMC controller */ |
235 | PDFUNC = MxSx | SCLK | SLP_TR | IRQ | nSEL; |
236 | PDFUNS = CLK; |
237 | |
238 | /* set the pin directions */ |
239 | PDDIRC = IRQ; |
240 | PDDIRS = MxSx | CLK | SCLK | SLP_TR | nSEL; |
241 | |
242 | /* enable power */ |
243 | PDDATC = VDD_OFF; |
244 | |
245 | /* set the MSC clock to 316 MHz / 21 = 16 MHz */ |
246 | MSCCDR = 20; |
247 | /* |
248 | * Enable the MSC clock. We need to do this before accessing any |
249 | * registers of the MSC block ! |
250 | */ |
251 | CLKGR &= ~(1 << 7); |
252 | /* bus clock = MSC clock / 1 */ |
253 | MSC_CLKRT = 0; |
254 | /* start MMC clock output */ |
255 | MSC_STRPCL = 2; |
256 | |
257 | atusd_cycle(dsc); |
258 | |
259 | return dsc; |
260 | } |
261 | |
262 | |
263 | static void atusd_close(void *arg) |
264 | { |
265 | struct atusd_dsc *dsc = arg; |
266 | |
267 | /* stop the MMC bus clock */ |
268 | MSC_STRPCL = 1; |
269 | |
270 | /* cut the power */ |
271 | PDDATS = VDD_OFF; |
272 | |
273 | /* make all MMC pins inputs */ |
274 | PDDIRC = MxSx | CLK | SCLK | SLP_TR | IRQ | nSEL; |
275 | } |
276 | |
277 | |
278 | static void atusd_reset_rf(void *handle) |
279 | { |
280 | struct atusd_dsc *dsc = handle; |
281 | |
282 | atusd_cycle(dsc); |
283 | } |
284 | |
285 | |
286 | static void atusd_reg_write(void *handle, uint8_t reg, uint8_t v) |
287 | { |
288 | struct atusd_dsc *dsc = handle; |
289 | |
290 | spi_begin(dsc); |
291 | spi_send(dsc, AT86RF230_REG_WRITE | reg); |
292 | spi_send(dsc, v); |
293 | spi_end(dsc); |
294 | } |
295 | |
296 | |
297 | static uint8_t atusd_reg_read(void *handle, uint8_t reg) |
298 | { |
299 | struct atusd_dsc *dsc = handle; |
300 | uint8_t res; |
301 | |
302 | spi_begin(dsc); |
303 | spi_send_partial(dsc, AT86RF230_REG_READ| reg); |
304 | spi_data_in(dsc); |
305 | res = spi_recv(dsc); |
306 | spi_finish(dsc); |
307 | spi_data_out(dsc); |
308 | spi_end(dsc); |
309 | return res; |
310 | } |
311 | |
312 | |
313 | /* ----- Driver interface -------------------------------------------------- */ |
314 | |
315 | |
316 | struct atspi_driver atusd_driver = { |
317 | .name = "uSD", |
318 | .open = atusd_open, |
319 | .close = atusd_close, |
320 | .reset = NULL, |
321 | .reset_rf = atusd_reset_rf, |
322 | .reg_write = atusd_reg_write, |
323 | .reg_read = atusd_reg_read, |
324 | #if 0 |
325 | .buf_write = atusd_buf_write, |
326 | .buf_read = atusd_buf_read, |
327 | #endif |
328 | }; |
329 |