f091c89c570963542f602c0e7f6d8957116895d4
[fw/altos] / src / lpc / ao_spi_lpc.c
1 /*
2  * Copyright © 2013 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19
20 static uint8_t          ao_spi_mutex[LPC_NUM_SPI];
21
22 static struct lpc_ssp * const ao_lpc_ssp[LPC_NUM_SPI] = { &lpc_ssp0, &lpc_ssp1 };
23
24 #define spi_loop(len, put, get) do {                                    \
25                 while (len--) {                                         \
26                         /* send a byte */                               \
27                         lpc_ssp->dr = put;                              \
28                         /* wait for the received byte to appear */      \
29                         while ((lpc_ssp->sr & (1 << LPC_SSP_SR_RNE)) == 0) \
30                                 ;                                       \
31                         /* receive a byte */                            \
32                         get lpc_ssp->dr;                                \
33                 }                                                       \
34                 /* Wait for the SSP to go idle (it already should be) */ \
35                 while (lpc_ssp->sr & (1 << LPC_SSP_SR_BSY));            \
36         } while (0)
37
38 void
39 ao_spi_send(const void *block, uint16_t len, uint8_t id)
40 {
41         struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
42         const uint8_t   *o = block;
43
44         spi_loop(len, *o++, (void));
45 }
46
47 void
48 ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t id)
49 {
50         struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
51
52         spi_loop(len, value, (void));
53 }
54
55 void
56 ao_spi_recv(void *block, uint16_t len, uint8_t id)
57 {
58         struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
59         uint8_t *i = block;
60
61         spi_loop(len, 0xff, *i++ =);
62 }
63
64 void
65 ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t id)
66 {
67         struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
68         const uint8_t *o = out;
69         uint8_t *i = in;
70
71         spi_loop(len, *o++, *i++ =);
72 }
73
74 void
75 ao_spi_get(uint8_t id, uint32_t speed)
76 {
77         struct lpc_ssp  *lpc_ssp = ao_lpc_ssp[id];
78
79         ao_mutex_get(&ao_spi_mutex[id]);
80
81         /* Set the clock prescale */
82         lpc_ssp->cpsr = speed;
83 }
84
85 void
86 ao_spi_put(uint8_t id)
87 {
88         ao_mutex_put(&ao_spi_mutex[id]);
89 }
90
91 static void
92 ao_spi_channel_init(uint8_t id)
93 {
94         struct lpc_ssp  *lpc_ssp = ao_lpc_ssp[id];
95         uint8_t d;
96
97         /* Clear interrupt registers */
98         lpc_ssp->imsc = 0;
99         lpc_ssp->ris = 0;
100         lpc_ssp->mis = 0;
101
102         lpc_ssp->cr0 = ((LPC_SSP_CR0_DSS_8 << LPC_SSP_CR0_DSS) |
103                         (LPC_SSP_CR0_FRF_SPI << LPC_SSP_CR0_FRF) |
104                         (0 << LPC_SSP_CR0_CPOL) |
105                         (0 << LPC_SSP_CR0_CPHA) |
106                         (0 << LPC_SSP_CR0_SCR));
107
108         /* Enable the device */
109         lpc_ssp->cr1 = ((0 << LPC_SSP_CR1_LBM) |
110                         (1 << LPC_SSP_CR1_SSE) |
111                         (LPC_SSP_CR1_MS_MASTER << LPC_SSP_CR1_MS) |
112                         (0 << LPC_SSP_CR1_SOD));
113
114         /* Drain the receive fifo */
115         for (d = 0; d < LPC_SSP_FIFOSIZE; d++)
116                 (void) lpc_ssp->dr;
117 }
118
119 void
120 ao_spi_init(void)
121 {
122 #if HAS_SPI_0
123         /* Configure pins */
124 #if SPI_SCK0_P0_6
125         lpc_ioconf.pio0_6 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_6_SCK0);
126 #define HAS_SCK0
127 #endif
128 #if SPI_SCK0_P0_10
129         lpc_ioconf.pio0_10 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_10_SCK0);
130 #define HAS_SCK0
131 #endif
132 #if SPI_SCK0_P1_29
133         lpc_ioconf.pio1_29 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_29_SCK0);
134 #define HAS_SCK0
135 #endif
136 #ifndef HAS_SCK0
137 #error "No pin specified for SCK0"
138 #endif
139         lpc_ioconf.pio0_8 = ao_lpc_alternate(LPC_IOCONF_FUNC_MISO0);
140         lpc_ioconf.pio0_9 = ao_lpc_alternate(LPC_IOCONF_FUNC_MOSI0);
141
142         /* Enable the device */
143         lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_SSP0);
144
145         /* Turn on the clock */
146         lpc_scb.ssp0clkdiv = 1;
147
148         /* Reset the device */
149         lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
150         lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
151         ao_spi_channel_init(0);
152 #endif
153
154 #if HAS_SPI_1
155
156 #if SPI_SCK1_P1_15
157         lpc_ioconf.pio1_15 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_15_SCK1);
158 #define HAS_SCK1
159 #endif
160 #if SPI_SCK1_P1_20
161         lpc_ioconf.pio1_20 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_20_SCK1);
162 #define HAS_SCK1
163 #endif
164 #ifndef HAS_SCK1
165 #error "No pin specified for SCK1"
166 #endif
167
168 #if SPI_MISO1_P0_22
169         lpc_ioconf.pio0_22 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_22_MISO1);
170 #define HAS_MISO1
171 #endif
172 #if SPI_MISO1_P1_21
173         lpc_ioconf.pio1_21 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_21_MISO1);
174 #define HAS_MISO1
175 #endif
176 #ifndef HAS_MISO1
177 #error "No pin specified for MISO1"
178 #endif
179
180 #if SPI_MOSI1_P0_21
181         lpc_ioconf.pio0_21 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_21_MOSI1);
182 #define HAS_MOSI1
183 #endif
184 #if SPI_MOSI1_P1_22
185         lpc_ioconf.pio1_22 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_22_MOSI1);
186 #define HAS_MOSI1
187 #endif
188 #ifndef HAS_MOSI1
189 #error "No pin specified for MOSI1"
190 #endif
191
192         /* Enable the device */
193         lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_SSP1);
194
195         /* Turn on the clock */
196         lpc_scb.ssp1clkdiv = 1;
197
198         /* Reset the device */
199         lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP1_RST_N);
200         lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP1_RST_N);
201         ao_spi_channel_init(1);
202 #endif /* HAS_SPI_1 */
203 }