Root/tools/lib/atusd.c

Source at commit 406517baa24266d2c86f9e6621bca1b56952d831 created 8 years 10 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
25enum {
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
62struct atusd_dsc {
63    int fd;
64    void *mem;
65};
66
67
68/* ----- Reset functions --------------------------------------------------- */
69
70
71static 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 */
109static 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
131static void spi_begin(struct atusd_dsc *dsc)
132{
133    PDDATC = nSEL;
134}
135
136
137static void spi_end(struct atusd_dsc *dsc)
138{
139    PDDATS = nSEL;
140}
141
142
143static void spi_data_in(struct atusd_dsc *dsc)
144{
145    PDDIRC = MxSx;
146}
147
148
149static 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
161static 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
176static 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
192static void spi_finish(struct atusd_dsc *dsc)
193{
194    PDDATC = SCLK;
195}
196
197
198static 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
208static 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
263static 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
278static void atusd_reset_rf(void *handle)
279{
280    struct atusd_dsc *dsc = handle;
281
282    atusd_cycle(dsc);
283}
284
285
286static 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
297static 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
316struct 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

Archive Download this file



interactive