Root/tools/lib/atusd.c

Source at commit b6a80ba3ddc63b4f1072a8e20db07e08fcac2738 created 9 years 11 months ago.
By Werner Almesberger, Added interrupt polling support for atusb.
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 << 2, /* VDD disable, PD02 */
27    MOSI = 1 << 8, /* CMD, PD08 */
28    CLK = 1 << 9, /* CLK, PD09 */
29    MISO = 1 << 10, /* DAT0, PD10 */
30    SCLK = 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 wait_for_power(void)
72{
73    /*
74     * Give power time to stabilize and the chip time to reset.
75     * Power takes about 2 ms to ramp up. We wait 10 ms to be sure.
76     */
77    usleep(10*1000);
78}
79
80
81static void atusd_cycle(struct atusd_dsc *dsc)
82{
83    /* stop the MMC bus clock */
84    MSC_STRPCL = 1;
85
86    /* drive all outputs low (including the MMC bus clock) */
87    PDDATC = MOSI | CLK | SCLK | nSEL;
88
89    /* make the MMC bus clock a regular output */
90    PDFUNC = CLK;
91
92    /* cut the power */
93    PDDATS = VDD_OFF;
94
95    /* Power drains within about 20 ms. Wait 100 ms to be sure. */
96    usleep(100*1000);
97
98    /* drive MOSI and nSS high */
99    PDDATS = MOSI | nSEL;
100
101    /* precharge the capacitors to avoid current surge */
102    wait_for_power();
103
104    /* return the bus clock output to the MMC controller */
105    PDFUNS = CLK;
106
107    /* start MMC clock output */
108    MSC_STRPCL = 2;
109
110    /* supply power */
111    PDDATC = VDD_OFF;
112
113    wait_for_power();
114}
115
116
117/* ----- Low-level SPI operations ------------------------------------------ */
118
119
120static void spi_begin(struct atusd_dsc *dsc)
121{
122    PDDATC = nSEL;
123}
124
125
126static void spi_end(struct atusd_dsc *dsc)
127{
128    PDDATS = nSEL;
129}
130
131
132static void spi_send(struct atusd_dsc *dsc, uint8_t v)
133{
134    uint8_t mask;
135
136    for (mask = 0x80; mask; mask >>= 1) {
137        if (v & mask)
138            PDDATS = MOSI;
139        else
140            PDDATC = MOSI;
141        PDDATS = SCLK;
142        PDDATC = SCLK;
143    }
144}
145
146
147static uint8_t spi_recv(struct atusd_dsc *dsc)
148{
149    uint8_t res = 0;
150        uint8_t mask;
151
152    for (mask = 0x80; mask; mask >>= 1) {
153        if (PDPIN & MISO)
154            res |= mask;
155        PDDATS = SCLK;
156        PDDATC = SCLK;
157    }
158        return res;
159}
160
161
162/* ----- Driver operations ------------------------------------------------- */
163
164
165static void atusd_reset_rf(void *handle)
166{
167    struct atusd_dsc *dsc = handle;
168
169    atusd_cycle(dsc);
170    wait_for_power();
171}
172
173
174static void *atusd_open(void)
175{
176    struct atusd_dsc *dsc;
177
178    dsc = malloc(sizeof(*dsc));
179    if (!dsc) {
180        perror("malloc");
181        exit(1);
182    }
183
184    dsc->fd = open("/dev/mem", O_RDWR);
185    if (dsc->fd < 0) {
186        perror("/dev/mem");
187        exit(1);
188    }
189    dsc->mem = mmap(NULL, PAGE_SIZE*3*16, PROT_READ | PROT_WRITE,
190        MAP_SHARED, dsc->fd, SOC_BASE);
191        if (dsc->mem == MAP_FAILED) {
192                perror("mmap");
193                exit(1);
194        }
195
196    /* set the output levels */
197    PDDATS = nSEL | VDD_OFF;
198    PDDATC = SCLK;
199
200    /* take the GPIOs away from the MMC controller */
201    PDFUNC = MOSI | MISO | SCLK | IRQ | nSEL;
202    PDFUNS = CLK;
203
204    /* set the pin directions */
205    PDDIRC = MISO | IRQ;
206    PDDIRS = MOSI | CLK | SCLK | nSEL;
207
208    /* let capacitors precharge */
209    wait_for_power();
210
211    /* enable power */
212    PDDATC = VDD_OFF;
213
214    /* set the MSC clock to 316 MHz / 21 = 16 MHz */
215    MSCCDR = 20;
216    /*
217     * Enable the MSC clock. We need to do this before accessing any
218     * registers of the MSC block !
219     */
220    CLKGR &= ~(1 << 7);
221    /* bus clock = MSC clock / 1 */
222    MSC_CLKRT = 0;
223    /* start MMC clock output */
224    MSC_STRPCL = 2;
225
226    wait_for_power();
227    atusd_reset_rf(dsc);
228
229    return dsc;
230}
231
232
233static void atusd_close(void *arg)
234{
235    struct atusd_dsc *dsc = arg;
236
237    /* stop the MMC bus clock */
238    MSC_STRPCL = 1;
239
240    /* cut the power */
241    PDDATS = VDD_OFF;
242
243    /* make all MMC pins inputs */
244    PDDIRC = MOSI | MISO | CLK | SCLK | IRQ | nSEL;
245}
246
247
248static void atusd_reg_write(void *handle, uint8_t reg, uint8_t v)
249{
250    struct atusd_dsc *dsc = handle;
251
252    spi_begin(dsc);
253    spi_send(dsc, AT86RF230_REG_WRITE | reg);
254    spi_send(dsc, v);
255    spi_end(dsc);
256}
257
258
259static uint8_t atusd_reg_read(void *handle, uint8_t reg)
260{
261    struct atusd_dsc *dsc = handle;
262    uint8_t res;
263
264    spi_begin(dsc);
265    spi_send(dsc, AT86RF230_REG_READ | reg);
266    res = spi_recv(dsc);
267    spi_end(dsc);
268    return res;
269}
270
271
272static void atusd_buf_write(void *handle, const void *buf, int size)
273{
274    struct atusd_dsc *dsc = handle;
275
276    spi_begin(dsc);
277    spi_send(dsc, AT86RF230_BUF_WRITE);
278    spi_send(dsc, size);
279    while (size--)
280        spi_send(dsc, *(uint8_t *) buf++);
281    spi_end(dsc);
282}
283
284
285static int atusd_buf_read(void *handle, void *buf, int size)
286{
287    struct atusd_dsc *dsc = handle;
288    uint8_t len, i;
289
290    spi_begin(dsc);
291    spi_send(dsc, AT86RF230_BUF_READ);
292    len = spi_recv(dsc);
293    len++; /* LQI */
294    if (len > size)
295        len = size;
296    for (i = 0; i != len; i++)
297        *(uint8_t *) buf++ = spi_recv(dsc);
298    spi_end(dsc);
299    return len;
300}
301
302
303/* ----- RF interrupt ------------------------------------------------------ */
304
305
306static int atusd_interrupt(void *handle)
307{
308        struct atusd_dsc *dsc = handle;
309
310    return !!(PDPIN & IRQ);
311}
312
313
314/* ----- Driver interface -------------------------------------------------- */
315
316
317struct atspi_driver atusd_driver = {
318    .name = "uSD",
319    .open = atusd_open,
320    .close = atusd_close,
321    .reset = NULL,
322    .reset_rf = atusd_reset_rf,
323    .reg_write = atusd_reg_write,
324    .reg_read = atusd_reg_read,
325    .buf_write = atusd_buf_write,
326    .buf_read = atusd_buf_read,
327    .interrupt = atusd_interrupt,
328};
329

Archive Download this file



interactive