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