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.
17 static uint8_t ao_spi_mutex[SAMD21_NUM_SERCOM];
18 static uint8_t ao_spi_pin_config[SAMD21_NUM_SERCOM];
20 struct ao_spi_samd21_info {
21 struct samd21_sercom *sercom;
24 static const struct ao_spi_samd21_info ao_spi_samd21_info[SAMD21_NUM_SERCOM] = {
26 .sercom = &samd21_sercom0,
29 .sercom = &samd21_sercom1,
32 .sercom = &samd21_sercom2,
35 .sercom = &samd21_sercom3,
38 .sercom = &samd21_sercom4,
41 .sercom = &samd21_sercom5,
45 //static uint8_t spi_dev_null;
48 ao_spi_send(const void *block, uint16_t len, uint8_t spi_index)
50 uint8_t id = AO_SPI_INDEX(spi_index);
51 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
53 const uint8_t *b = block;
57 while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
64 ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
66 uint8_t id = AO_SPI_INDEX(spi_index);
67 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
73 while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
75 *b++ = (uint8_t) sercom->data;
81 ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index)
83 uint8_t id = AO_SPI_INDEX(spi_index);
84 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
86 const uint8_t *o = out;
91 while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXC)) == 0)
93 *i++ = (uint8_t) sercom->data;
98 ao_spi_disable_pin_config(uint8_t spi_pin_config)
100 switch (spi_pin_config) {
102 case AO_SPI_0_PA08_PA09_PA10:
103 samd21_port_pmux_clr(&samd21_port_a, 8); /* MOSI */
104 samd21_port_pmux_clr(&samd21_port_a, 9); /* SCLK */
105 samd21_port_pmux_clr(&samd21_port_a, 10); /* MISO */
107 case AO_SPI_0_PA04_PA05_PA06:
108 samd21_port_pmux_clr(&samd21_port_a, 4); /* MOSI */
109 samd21_port_pmux_clr(&samd21_port_a, 5); /* SCLK */
110 samd21_port_pmux_clr(&samd21_port_a, 6); /* MISO */
117 ao_spi_enable_pin_config(uint8_t spi_pin_config)
119 switch (spi_pin_config) {
121 case AO_SPI_0_PA08_PA09_PA10:
122 ao_enable_output(&samd21_port_a, 8, 1);
123 ao_enable_output(&samd21_port_a, 9, 1);
124 ao_enable_input(&samd21_port_a, 10, AO_MODE_PULL_NONE);
125 samd21_port_pmux_set(&samd21_port_a, 8, SAMD21_PORT_PMUX_FUNC_C); /* MOSI */
126 samd21_port_pmux_set(&samd21_port_a, 9, SAMD21_PORT_PMUX_FUNC_C); /* SCLK */
127 samd21_port_pmux_set(&samd21_port_a, 10, SAMD21_PORT_PMUX_FUNC_C); /* MISO */
129 case AO_SPI_0_PA04_PA05_PA06:
130 ao_enable_output(&samd21_port_a, 4, 1);
131 ao_enable_output(&samd21_port_a, 5, 1);
132 ao_enable_input(&samd21_port_a, 6, AO_MODE_PULL_NONE);
133 samd21_port_pmux_set(&samd21_port_a, 4, SAMD21_PORT_PMUX_FUNC_C); /* MOSI */
134 samd21_port_pmux_set(&samd21_port_a, 5, SAMD21_PORT_PMUX_FUNC_C); /* SCLK */
135 samd21_port_pmux_set(&samd21_port_a, 6, SAMD21_PORT_PMUX_FUNC_C); /* MISO */
142 ao_spi_config(uint8_t spi_index, uint32_t baud)
144 uint8_t spi_pin_config = AO_SPI_PIN_CONFIG(spi_index);
145 uint8_t id = AO_SPI_INDEX(spi_index);
146 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
148 if (spi_pin_config != ao_spi_pin_config[id]) {
149 ao_spi_disable_pin_config(ao_spi_pin_config[id]);
150 ao_spi_enable_pin_config(spi_pin_config);
151 ao_spi_pin_config[id] = spi_pin_config;
154 sercom->baud = (uint16_t) baud;
157 uint32_t ctrla = sercom->ctrla;
158 ctrla &= ~((1UL << SAMD21_SERCOM_CTRLA_CPOL) |
159 (1UL << SAMD21_SERCOM_CTRLA_CPHA));
160 ctrla |= ((AO_SPI_CPOL(spi_index) << SAMD21_SERCOM_CTRLA_CPOL) |
161 (AO_SPI_CPHA(spi_index) << SAMD21_SERCOM_CTRLA_CPHA));
163 /* finish setup and enable the hardware */
164 ctrla |= (1 << SAMD21_SERCOM_CTRLA_ENABLE);
166 sercom->ctrla = ctrla;
168 while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
173 ao_spi_get(uint8_t spi_index, uint32_t speed)
175 uint8_t id = AO_SPI_INDEX(spi_index);
177 ao_mutex_get(&ao_spi_mutex[id]);
178 ao_spi_config(spi_index, speed);
182 ao_spi_put(uint8_t spi_index)
184 uint8_t id = AO_SPI_INDEX(spi_index);
185 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
187 sercom->ctrla &= ~(1UL << SAMD21_SERCOM_CTRLA_ENABLE);
188 while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_ENABLE))
190 ao_mutex_put(&ao_spi_mutex[id]);
194 ao_spi_init_sercom(uint8_t id)
196 struct samd21_sercom *sercom = ao_spi_samd21_info[id].sercom;
198 /* Send a clock along */
199 samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE + id);
201 samd21_nvic_set_enable(SAMD21_NVIC_ISR_SERCOM0_POS + id);
202 samd21_nvic_set_priority(SAMD21_NVIC_ISR_SERCOM0_POS + id, 4);
205 samd21_pm.apbcmask |= (1 << (SAMD21_PM_APBCMASK_SERCOM0 + id));
208 sercom->ctrla = (1 << SAMD21_SERCOM_CTRLA_SWRST);
210 while ((sercom->ctrla & (1 << SAMD21_SERCOM_CTRLA_SWRST)) ||
211 (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SWRST)))
215 sercom->ctrla = ((SAMD21_SERCOM_CTRLA_DORD_MSB << SAMD21_SERCOM_CTRLA_DORD) |
216 (0 << SAMD21_SERCOM_CTRLA_CPOL) |
217 (0 << SAMD21_SERCOM_CTRLA_CPHA) |
218 (0 << SAMD21_SERCOM_CTRLA_FORM) |
219 (2 << SAMD21_SERCOM_CTRLA_DIPO) |
220 (0 << SAMD21_SERCOM_CTRLA_DOPO) |
221 (0 << SAMD21_SERCOM_CTRLA_IBON) |
222 (0 << SAMD21_SERCOM_CTRLA_RUNSTDBY) |
223 (SAMD21_SERCOM_CTRLA_MODE_SPI_HOST << SAMD21_SERCOM_CTRLA_MODE) |
224 (0 << SAMD21_SERCOM_CTRLA_ENABLE) |
225 (0 << SAMD21_SERCOM_CTRLA_SWRST));
227 sercom->ctrlb = ((1 << SAMD21_SERCOM_CTRLB_RXEN) |
228 (0 << SAMD21_SERCOM_CTRLB_AMODE) |
229 (0 << SAMD21_SERCOM_CTRLB_MSSEN) |
230 (0 << SAMD21_SERCOM_CTRLB_SSDE) |
231 (0 << SAMD21_SERCOM_CTRLB_PLOADEN) |
232 (SAMD21_SERCOM_CTRLB_CHSIZE_8 << SAMD21_SERCOM_CTRLB_CHSIZE));
235 ao_spi_enable_pin_config(id);
242 ao_spi_init_sercom(0);
245 ao_spi_init_sercom(1);
248 ao_spi_init_sercom(2);
251 ao_spi_init_sercom(3);
254 ao_spi_init_sercom(4);
257 ao_spi_init_sercom(5);