2 * Copyright © 2022 Keith Packard <keithp@keithp.com>
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.
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.
16 #include <ao_dma_samd21.h>
18 static uint8_t ao_spi_mutex[SAMD21_NUM_SERCOM];
19 static uint8_t ao_spi_pin_config[SAMD21_NUM_SERCOM];
21 struct ao_spi_samd21_info {
22 struct samd21_sercom *sercom;
25 static const struct ao_spi_samd21_info ao_spi_samd21_info[SAMD21_NUM_SERCOM] = {
27 .sercom = &samd21_sercom0,
30 .sercom = &samd21_sercom1,
33 .sercom = &samd21_sercom2,
36 .sercom = &samd21_sercom3,
39 .sercom = &samd21_sercom4,
42 .sercom = &samd21_sercom5,
46 static uint8_t spi_dev_null;
52 #define AO_SAMD21_SPI_MISO_DMA_ID 0
53 #define AO_SAMD21_SPI_MOSI_DMA_ID 1
55 static uint8_t ao_spi_done[SAMD21_NUM_SERCOM];
58 _ao_spi_recv_dma_done(uint8_t dma_id, void *closure)
60 uint8_t id = (uint8_t) (uintptr_t) closure;
64 ao_wakeup(&ao_spi_done[id]);
69 ao_spi_send(const void *block, uint16_t len, uint8_t spi_index)
71 uint8_t id = AO_SPI_INDEX(spi_index);
72 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
75 ao_arch_block_interrupts();
77 _ao_dma_start_transfer(AO_SAMD21_SPI_MISO_DMA_ID,
78 (void *) &sercom->data,
82 (SAMD21_DMAC_CHCTRLB_CMD_NOACT << SAMD21_DMAC_CHCTRLB_CMD) |
83 (SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT << SAMD21_DMAC_CHCTRLB_TRIGACT) |
84 (SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_RX(id) << SAMD21_DMAC_CHCTRLB_TRIGSRC) |
85 (SAMD21_DMAC_CHCTRLB_LVL_LVL3 << SAMD21_DMAC_CHCTRLB_LVL) |
86 (0UL << SAMD21_DMAC_CHCTRLB_EVOE) |
87 (0UL << SAMD21_DMAC_CHCTRLB_EVIE) |
88 (SAMD21_DMAC_CHCTRLB_EVACT_NOACT << SAMD21_DMAC_CHCTRLB_EVACT),
90 (SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X1 << SAMD21_DMAC_DESC_BTCTRL_STEPSIZE) |
91 (SAMD21_DMAC_DESC_BTCTRL_STEPSEL_DST << SAMD21_DMAC_DESC_BTCTRL_STEPSEL) |
92 (0UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC) |
93 (0UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC) |
94 (SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE << SAMD21_DMAC_DESC_BTCTRL_BEATSIZE) |
95 (SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_INT << SAMD21_DMAC_DESC_BTCTRL_BLOCKACT) |
96 (SAMD21_DMAC_DESC_BTCTRL_EVOSEL_DISABLE << SAMD21_DMAC_DESC_BTCTRL_EVOSEL) |
97 (1UL << SAMD21_DMAC_DESC_BTCTRL_VALID),
99 _ao_spi_recv_dma_done,
100 (void *) (uintptr_t) id
103 _ao_dma_start_transfer(AO_SAMD21_SPI_MOSI_DMA_ID,
104 (uint8_t *) block + len,
105 (void *) &sercom->data,
108 (SAMD21_DMAC_CHCTRLB_CMD_NOACT << SAMD21_DMAC_CHCTRLB_CMD) |
109 (SAMD21_DMAC_CHCTRLB_TRIGACT_BEAT << SAMD21_DMAC_CHCTRLB_TRIGACT) |
110 (SAMD21_DMAC_CHCTRLB_TRIGSRC_SERCOM_TX(id) << SAMD21_DMAC_CHCTRLB_TRIGSRC) |
111 (SAMD21_DMAC_CHCTRLB_LVL_LVL2 << SAMD21_DMAC_CHCTRLB_LVL) |
112 (0UL << SAMD21_DMAC_CHCTRLB_EVOE) |
113 (0UL << SAMD21_DMAC_CHCTRLB_EVIE) |
114 (SAMD21_DMAC_CHCTRLB_EVACT_NOACT << SAMD21_DMAC_CHCTRLB_EVACT),
116 (SAMD21_DMAC_DESC_BTCTRL_STEPSIZE_X1 << SAMD21_DMAC_DESC_BTCTRL_STEPSIZE) |
117 (SAMD21_DMAC_DESC_BTCTRL_STEPSEL_SRC << SAMD21_DMAC_DESC_BTCTRL_STEPSEL) |
118 (0UL << SAMD21_DMAC_DESC_BTCTRL_DSTINC) |
119 (1UL << SAMD21_DMAC_DESC_BTCTRL_SRCINC) |
120 (SAMD21_DMAC_DESC_BTCTRL_BEATSIZE_BYTE << SAMD21_DMAC_DESC_BTCTRL_BEATSIZE) |
121 (SAMD21_DMAC_DESC_BTCTRL_BLOCKACT_NOACT << SAMD21_DMAC_DESC_BTCTRL_BLOCKACT) |
122 (SAMD21_DMAC_DESC_BTCTRL_EVOSEL_DISABLE << SAMD21_DMAC_DESC_BTCTRL_EVOSEL) |
123 (1 << SAMD21_DMAC_DESC_BTCTRL_VALID),
129 while (ao_spi_done[id] == 0)
130 ao_sleep(&ao_spi_done[id]);
132 _ao_dma_done_transfer(AO_SAMD21_SPI_MOSI_DMA_ID);
133 _ao_dma_done_transfer(AO_SAMD21_SPI_MISO_DMA_ID);
134 ao_arch_release_interrupts();
136 const uint8_t *b = block;
140 while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
149 ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
151 uint8_t id = AO_SPI_INDEX(spi_index);
152 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
158 while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
160 *b++ = (uint8_t) sercom->data;
166 ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index)
168 uint8_t id = AO_SPI_INDEX(spi_index);
169 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
171 const uint8_t *o = out;
176 while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
178 *i++ = (uint8_t) sercom->data;
183 ao_spi_disable_pin_config(uint8_t spi_pin_config)
185 switch (spi_pin_config) {
187 case AO_SPI_0_PA08_PA09_PA10:
188 samd21_port_pmux_clr(&samd21_port_a, 8); /* MOSI */
189 samd21_port_pmux_clr(&samd21_port_a, 9); /* SCLK */
190 samd21_port_pmux_clr(&samd21_port_a, 10); /* MISO */
192 case AO_SPI_0_PA04_PA05_PA06:
193 samd21_port_pmux_clr(&samd21_port_a, 4); /* MOSI */
194 samd21_port_pmux_clr(&samd21_port_a, 5); /* SCLK */
195 samd21_port_pmux_clr(&samd21_port_a, 6); /* MISO */
202 ao_spi_enable_pin_config(uint8_t spi_pin_config)
204 switch (spi_pin_config) {
206 case AO_SPI_0_PA08_PA09_PA10:
207 ao_enable_output(&samd21_port_a, 8, 1);
208 ao_enable_output(&samd21_port_a, 9, 1);
209 ao_enable_input(&samd21_port_a, 10, AO_MODE_PULL_NONE);
210 samd21_port_pmux_set(&samd21_port_a, 8, SAMD21_PORT_PMUX_FUNC_C); /* MOSI */
211 samd21_port_pmux_set(&samd21_port_a, 9, SAMD21_PORT_PMUX_FUNC_C); /* SCLK */
212 samd21_port_pmux_set(&samd21_port_a, 10, SAMD21_PORT_PMUX_FUNC_C); /* MISO */
214 case AO_SPI_0_PA04_PA05_PA06:
215 ao_enable_output(&samd21_port_a, 4, 1);
216 ao_enable_output(&samd21_port_a, 5, 1);
217 ao_enable_input(&samd21_port_a, 6, AO_MODE_PULL_NONE);
218 samd21_port_pmux_set(&samd21_port_a, 4, SAMD21_PORT_PMUX_FUNC_C); /* MOSI */
219 samd21_port_pmux_set(&samd21_port_a, 5, SAMD21_PORT_PMUX_FUNC_C); /* SCLK */
220 samd21_port_pmux_set(&samd21_port_a, 6, SAMD21_PORT_PMUX_FUNC_C); /* MISO */
227 ao_spi_config(uint8_t spi_index, uint32_t baud)
229 uint8_t spi_pin_config = AO_SPI_PIN_CONFIG(spi_index);
230 uint8_t id = AO_SPI_INDEX(spi_index);
231 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
233 if (spi_pin_config != ao_spi_pin_config[id]) {
234 ao_spi_disable_pin_config(ao_spi_pin_config[id]);
235 ao_spi_enable_pin_config(spi_pin_config);
236 ao_spi_pin_config[id] = spi_pin_config;
239 sercom->baud = (uint16_t) baud;
242 uint32_t ctrla = sercom->ctrla;
243 ctrla &= ~((1UL << SAMD21_SERCOM_CTRLA_CPOL) |
244 (1UL << SAMD21_SERCOM_CTRLA_CPHA));
245 ctrla |= ((AO_SPI_CPOL(spi_index) << SAMD21_SERCOM_CTRLA_CPOL) |
246 (AO_SPI_CPHA(spi_index) << SAMD21_SERCOM_CTRLA_CPHA));
248 /* finish setup and enable the hardware */
249 ctrla |= (1 << SAMD21_SERCOM_CTRLA_ENABLE);
251 sercom->ctrla = ctrla;
253 while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
258 ao_spi_get(uint8_t spi_index, uint32_t speed)
260 uint8_t id = AO_SPI_INDEX(spi_index);
262 ao_mutex_get(&ao_spi_mutex[id]);
263 ao_spi_config(spi_index, speed);
267 ao_spi_put(uint8_t spi_index)
269 uint8_t id = AO_SPI_INDEX(spi_index);
270 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
272 sercom->ctrla &= ~(1UL << SAMD21_SERCOM_CTRLA_ENABLE);
273 while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
275 ao_mutex_put(&ao_spi_mutex[id]);
279 ao_spi_init_sercom(uint8_t id)
281 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
283 /* Send a clock along */
284 samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE + id);
286 samd21_nvic_set_enable(SAMD21_NVIC_ISR_SERCOM0_POS + id);
287 samd21_nvic_set_priority(SAMD21_NVIC_ISR_SERCOM0_POS + id, 4);
290 samd21_pm.apbcmask |= (1 << (SAMD21_PM_APBCMASK_SERCOM0 + id));
293 sercom->ctrla = (1 << SAMD21_SERCOM_CTRLA_SWRST);
295 while ((sercom->ctrla & (1 << SAMD21_SERCOM_CTRLA_SWRST)) ||
296 (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SWRST)))
300 sercom->ctrla = ((SAMD21_SERCOM_CTRLA_DORD_MSB << SAMD21_SERCOM_CTRLA_DORD) |
301 (0 << SAMD21_SERCOM_CTRLA_CPOL) |
302 (0 << SAMD21_SERCOM_CTRLA_CPHA) |
303 (0 << SAMD21_SERCOM_CTRLA_FORM) |
304 (2 << SAMD21_SERCOM_CTRLA_DIPO) |
305 (0 << SAMD21_SERCOM_CTRLA_DOPO) |
306 (0 << SAMD21_SERCOM_CTRLA_IBON) |
307 (0 << SAMD21_SERCOM_CTRLA_RUNSTDBY) |
308 (SAMD21_SERCOM_CTRLA_MODE_SPI_HOST << SAMD21_SERCOM_CTRLA_MODE) |
309 (0 << SAMD21_SERCOM_CTRLA_ENABLE) |
310 (0 << SAMD21_SERCOM_CTRLA_SWRST));
312 sercom->ctrlb = ((1 << SAMD21_SERCOM_CTRLB_RXEN) |
313 (0 << SAMD21_SERCOM_CTRLB_AMODE) |
314 (0 << SAMD21_SERCOM_CTRLB_MSSEN) |
315 (0 << SAMD21_SERCOM_CTRLB_SSDE) |
316 (0 << SAMD21_SERCOM_CTRLB_PLOADEN) |
317 (SAMD21_SERCOM_CTRLB_CHSIZE_8 << SAMD21_SERCOM_CTRLB_CHSIZE));
320 ao_spi_enable_pin_config(id);
327 ao_spi_init_sercom(0);
330 ao_spi_init_sercom(1);
333 ao_spi_init_sercom(2);
336 ao_spi_init_sercom(3);
339 ao_spi_init_sercom(4);
342 ao_spi_init_sercom(5);