bc8f9c690cbcde29a3220c3d864ec9b4b03130c3
[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 tx_busy(lpc_ssp) (lpc_ssp->sr & ((1 << LPC_SSP_SR_BSY) | (1 << LPC_SSP_SR_TNF))) != (1 << LPC_SSP_SR_TNF)
25 #define rx_busy(lpc_ssp) (lpc_ssp->sr & ((1 << LPC_SSP_SR_BSY) | (1 << LPC_SSP_SR_RNE))) != (1 << LPC_SSP_SR_RNE)
26
27 #define spi_loop(len, put, get) do {                                    \
28                 while (len--) {                                         \
29                         /* Wait for space in the fifo */                \
30                         while (tx_busy(lpc_ssp))                        \
31                                 ;                                       \
32                                                                         \
33                         /* send a byte */                               \
34                         lpc_ssp->dr = put;                              \
35                                                                         \
36                         /* Wait for byte to appear in the fifo */       \
37                         while (rx_busy(lpc_ssp))                        \
38                                 ;                                       \
39                                                                         \
40                         /* recv a byte */                               \
41                         get lpc_ssp->dr;                                \
42                 }                                                       \
43         } while (0)
44
45 void
46 ao_spi_send(const void *block, uint16_t len, uint8_t id)
47 {
48         const uint8_t   *b = block;
49         struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
50
51         spi_loop(len, *b++, (void));
52 }
53
54 void
55 ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t id)
56 {
57         struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
58
59         spi_loop(len, value, (void));
60 }
61
62 void
63 ao_spi_recv(void *block, uint16_t len, uint8_t id)
64 {
65         uint8_t *b = block;
66         struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
67
68         spi_loop(len, 0xff, *b++ =);
69 }
70
71 void
72 ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t id)
73 {
74         const uint8_t   *o = out;
75         uint8_t *i = in;
76         struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
77
78         spi_loop(len, *o++, *i++ =);
79 }
80
81 void
82 ao_spi_get(uint8_t id, uint32_t speed)
83 {
84         struct lpc_ssp  *lpc_ssp = ao_lpc_ssp[id];
85
86         ao_mutex_get(&ao_spi_mutex[id]);
87         
88         /* Set the clock prescale */
89         lpc_ssp->cpsr = speed;
90 }
91
92 void
93 ao_spi_put(uint8_t id)
94 {
95         ao_mutex_put(&ao_spi_mutex[id]);
96 }
97
98 static void
99 ao_spi_channel_init(uint8_t id)
100 {
101         struct lpc_ssp  *lpc_ssp = ao_lpc_ssp[id];
102         uint8_t d;
103
104         lpc_ssp->cr0 = ((LPC_SSP_CR0_DSS_8 << LPC_SSP_CR0_DSS) |
105                         (LPC_SSP_CR0_FRF_SPI << LPC_SSP_CR0_FRF) |
106                         (0 << LPC_SSP_CR0_CPOL) |
107                         (0 << LPC_SSP_CR0_CPHA) |
108                         (0 << LPC_SSP_CR0_SCR));
109
110         /* Enable the device */
111         lpc_ssp->cr1 = ((0 << LPC_SSP_CR1_LBM) |
112                         (1 << LPC_SSP_CR1_SSE) |
113                         (LPC_SSP_CR1_MS_MASTER << LPC_SSP_CR1_MS) |
114                         (0 << LPC_SSP_CR1_SOD));
115
116         /* Drain the receive fifo */
117         for (d = 0; d < LPC_SSP_FIFOSIZE; d++)
118                 (void) lpc_ssp->dr;
119 }
120
121 void
122 ao_spi_init(void)
123 {
124 #if HAS_SPI_0
125         /* Configure pins */
126 #if SPI_SCK0_P0_6
127         lpc_ioconf.pio0_6 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_6_SCK0);
128 #define HAS_SCK0
129 #endif
130 #if SPI_SCK0_P0_10
131         lpc_ioconf.pio0_10 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_10_SCK0);
132 #define HAS_SCK0
133 #endif
134 #if SPI_SCK0_P1_29
135         lpc_ioconf.pio1_29 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_29_SCK0);
136 #define HAS_SCK0
137 #endif
138 #ifndef HAS_SCK0
139 #error "No pin specified for SCK0"
140 #endif
141         lpc_ioconf.pio0_8 = ao_lpc_alternate(LPC_IOCONF_FUNC_MISO0);
142         lpc_ioconf.pio0_9 = ao_lpc_alternate(LPC_IOCONF_FUNC_MOSI0);
143
144         /* Enable the device */
145         lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_SSP0);
146
147         /* Turn on the clock */
148         lpc_scb.ssp0clkdiv = 1;
149
150         /* Reset the device */
151         lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
152         lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
153         ao_spi_channel_init(0);
154 #endif                     
155
156 #if HAS_SPI_1
157
158 #if SPI_SCK1_P1_15
159         lpc_ioconf.pio1_15 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_15_SCK1);
160 #define HAS_SCK1
161 #endif
162 #if SPI_SCK1_P1_20
163         lpc_ioconf.pio1_20 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_20_SCK1);
164 #define HAS_SCK1
165 #endif
166 #ifndef HAS_SCK1
167 #error "No pin specified for SCK1"
168 #endif
169
170 #if SPI_MISO1_P0_22
171         lpc_ioconf.pio0_22 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_22_MISO1);
172 #define HAS_MISO1
173 #endif
174 #if SPI_MISO1_P1_21
175         lpc_ioconf.pio1_21 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_21_MISO1);
176 #define HAS_MISO1
177 #endif
178 #ifndef HAS_MISO1
179 #error "No pin specified for MISO1"
180 #endif
181
182 #if SPI_MOSI1_P0_21
183         lpc_ioconf.pio0_21 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_21_MOSI1);
184 #define HAS_MOSI1
185 #endif
186 #if SPI_MOSI1_P1_22
187         lpc_ioconf.pio1_22 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_22_MOSI1);
188 #define HAS_MOSI1
189 #endif
190 #ifndef HAS_MOSI1
191 #error "No pin specified for MOSI1"
192 #endif
193                 
194         /* Enable the device */
195         lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_SSP1);
196
197         /* Turn on the clock */
198         lpc_scb.ssp1clkdiv = 1;
199
200         /* Reset the device */
201         lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP1_RST_N);
202         lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP1_RST_N);
203         ao_spi_channel_init(1);
204 #endif /* HAS_SPI_1 */
205 }