1 /*
2 * Copyright (c) 2019 Christian Taedcke <hacking@taedcke.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT silabs_gecko_spi_usart
8
9 #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
10 #include <logging/log.h>
11 LOG_MODULE_REGISTER(spi_gecko);
12 #include "spi_context.h"
13
14 #include <sys/sys_io.h>
15 #include <device.h>
16 #include <drivers/spi.h>
17 #include <soc.h>
18
19 #include "em_cmu.h"
20 #include "em_usart.h"
21
22 #include <stdbool.h>
23
24 #ifndef CONFIG_SOC_GECKO_HAS_INDIVIDUAL_PIN_LOCATION
25 #error "Individual pin location support is required"
26 #endif
27
28 #define CLOCK_USART(id) _CONCAT(cmuClock_USART, id)
29
30 #define SPI_WORD_SIZE 8
31
32 #define DEV_DATA(dev) ((struct spi_gecko_data *) ((dev)->data))
33
34 /* Structure Declarations */
35
36 struct spi_gecko_data {
37 struct spi_context ctx;
38 };
39
40 struct spi_gecko_config {
41 USART_TypeDef *base;
42 CMU_Clock_TypeDef clock;
43 struct soc_gpio_pin pin_rx;
44 struct soc_gpio_pin pin_tx;
45 struct soc_gpio_pin pin_clk;
46 uint8_t loc_rx;
47 uint8_t loc_tx;
48 uint8_t loc_clk;
49 };
50
51
52 /* Helper Functions */
spi_config(const struct device * dev,const struct spi_config * config,uint16_t * control)53 static int spi_config(const struct device *dev,
54 const struct spi_config *config,
55 uint16_t *control)
56 {
57 const struct spi_gecko_config *gecko_config = dev->config;
58 struct spi_gecko_data *data = DEV_DATA(dev);
59
60 if (SPI_WORD_SIZE_GET(config->operation) != SPI_WORD_SIZE) {
61 LOG_ERR("Word size must be %d", SPI_WORD_SIZE);
62 return -ENOTSUP;
63 }
64
65 if (config->operation & SPI_CS_ACTIVE_HIGH) {
66 LOG_ERR("CS active high not supported");
67 return -ENOTSUP;
68 }
69
70 if (config->operation & SPI_LOCK_ON) {
71 LOG_ERR("Lock On not supported");
72 return -ENOTSUP;
73 }
74
75 if ((config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) {
76 LOG_ERR("Only supports single mode");
77 return -ENOTSUP;
78 }
79
80 if (config->operation & SPI_TRANSFER_LSB) {
81 LOG_ERR("LSB first not supported");
82 return -ENOTSUP;
83 }
84
85 if (config->operation & (SPI_MODE_CPOL | SPI_MODE_CPHA)) {
86 LOG_ERR("Only supports CPOL=CPHA=0");
87 return -ENOTSUP;
88 }
89
90 if (config->operation & SPI_OP_MODE_SLAVE) {
91 LOG_ERR("Slave mode not supported");
92 return -ENOTSUP;
93 }
94
95 /* Set Loopback */
96 if (config->operation & SPI_MODE_LOOP) {
97 gecko_config->base->CTRL |= USART_CTRL_LOOPBK;
98 } else {
99 gecko_config->base->CTRL &= ~USART_CTRL_LOOPBK;
100 }
101
102 /* Set word size */
103 gecko_config->base->FRAME = usartDatabits8
104 | USART_FRAME_STOPBITS_DEFAULT
105 | USART_FRAME_PARITY_DEFAULT;
106
107 /* At this point, it's mandatory to set this on the context! */
108 data->ctx.config = config;
109
110 spi_context_cs_configure(&data->ctx);
111
112 return 0;
113 }
114
spi_gecko_send(USART_TypeDef * usart,uint8_t frame)115 static void spi_gecko_send(USART_TypeDef *usart, uint8_t frame)
116 {
117 /* Write frame to register */
118 USART_Tx(usart, frame);
119
120 /* Wait until the transfer ends */
121 while (!(usart->STATUS & USART_STATUS_TXC)) {
122 }
123 }
124
spi_gecko_recv(USART_TypeDef * usart)125 static uint8_t spi_gecko_recv(USART_TypeDef *usart)
126 {
127 /* Return data inside rx register */
128 return (uint8_t)usart->RXDATA;
129 }
130
spi_gecko_transfer_ongoing(struct spi_gecko_data * data)131 static bool spi_gecko_transfer_ongoing(struct spi_gecko_data *data)
132 {
133 return spi_context_tx_on(&data->ctx) || spi_context_rx_on(&data->ctx);
134 }
135
spi_gecko_next_tx(struct spi_gecko_data * data)136 static inline uint8_t spi_gecko_next_tx(struct spi_gecko_data *data)
137 {
138 uint8_t tx_frame = 0;
139
140 if (spi_context_tx_buf_on(&data->ctx)) {
141 tx_frame = UNALIGNED_GET((uint8_t *)(data->ctx.tx_buf));
142 }
143
144 return tx_frame;
145 }
146
spi_gecko_shift_frames(USART_TypeDef * usart,struct spi_gecko_data * data)147 static int spi_gecko_shift_frames(USART_TypeDef *usart,
148 struct spi_gecko_data *data)
149 {
150 uint8_t tx_frame;
151 uint8_t rx_frame;
152
153 tx_frame = spi_gecko_next_tx(data);
154 spi_gecko_send(usart, tx_frame);
155 spi_context_update_tx(&data->ctx, 1, 1);
156
157 rx_frame = spi_gecko_recv(usart);
158
159 if (spi_context_rx_buf_on(&data->ctx)) {
160 UNALIGNED_PUT(rx_frame, (uint8_t *)data->ctx.rx_buf);
161 }
162 spi_context_update_rx(&data->ctx, 1, 1);
163 return 0;
164 }
165
166
spi_gecko_xfer(const struct device * dev,const struct spi_config * config)167 static void spi_gecko_xfer(const struct device *dev,
168 const struct spi_config *config)
169 {
170 int ret;
171 struct spi_context *ctx = &DEV_DATA(dev)->ctx;
172 const struct spi_gecko_config *gecko_config = dev->config;
173 struct spi_gecko_data *data = DEV_DATA(dev);
174
175 spi_context_cs_control(ctx, true);
176
177 do {
178 ret = spi_gecko_shift_frames(gecko_config->base, data);
179 } while (!ret && spi_gecko_transfer_ongoing(data));
180
181 spi_context_cs_control(ctx, false);
182 spi_context_complete(ctx, 0);
183 }
184
spi_gecko_init_pins(const struct device * dev)185 static void spi_gecko_init_pins(const struct device *dev)
186 {
187 const struct spi_gecko_config *config = dev->config;
188
189 soc_gpio_configure(&config->pin_rx);
190 soc_gpio_configure(&config->pin_tx);
191 soc_gpio_configure(&config->pin_clk);
192
193 /* disable all pins while configuring */
194 config->base->ROUTEPEN = 0;
195
196 config->base->ROUTELOC0 =
197 (config->loc_tx << _USART_ROUTELOC0_TXLOC_SHIFT) |
198 (config->loc_rx << _USART_ROUTELOC0_RXLOC_SHIFT) |
199 (config->loc_clk << _USART_ROUTELOC0_CLKLOC_SHIFT);
200
201 config->base->ROUTELOC1 = _USART_ROUTELOC1_RESETVALUE;
202
203 config->base->ROUTEPEN = USART_ROUTEPEN_RXPEN | USART_ROUTEPEN_TXPEN |
204 USART_ROUTEPEN_CLKPEN;
205 }
206
207
208 /* API Functions */
209
spi_gecko_init(const struct device * dev)210 static int spi_gecko_init(const struct device *dev)
211 {
212 const struct spi_gecko_config *config = dev->config;
213 USART_InitSync_TypeDef usartInit = USART_INITSYNC_DEFAULT;
214
215 /* The peripheral and gpio clock are already enabled from soc and gpio
216 * driver
217 */
218
219 usartInit.enable = usartDisable;
220 usartInit.baudrate = 1000000;
221 usartInit.databits = usartDatabits8;
222 usartInit.master = 1;
223 usartInit.msbf = 1;
224 usartInit.clockMode = usartClockMode0;
225 #if defined(USART_INPUT_RXPRS) && defined(USART_TRIGCTRL_AUTOTXTEN)
226 usartInit.prsRxEnable = 0;
227 usartInit.prsRxCh = 0;
228 usartInit.autoTx = 0;
229 #endif
230
231 /* Enable USART clock */
232 CMU_ClockEnable(config->clock, true);
233
234 /* Init USART */
235 USART_InitSync(config->base, &usartInit);
236
237 /* Initialize USART pins */
238 spi_gecko_init_pins(dev);
239
240 /* Enable the peripheral */
241 config->base->CMD = (uint32_t) usartEnable;
242
243 return 0;
244 }
245
spi_gecko_transceive(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs)246 static int spi_gecko_transceive(const struct device *dev,
247 const struct spi_config *config,
248 const struct spi_buf_set *tx_bufs,
249 const struct spi_buf_set *rx_bufs)
250 {
251 uint16_t control = 0;
252
253 spi_config(dev, config, &control);
254 spi_context_buffers_setup(&DEV_DATA(dev)->ctx, tx_bufs, rx_bufs, 1);
255 spi_gecko_xfer(dev, config);
256 return 0;
257 }
258
259 #ifdef CONFIG_SPI_ASYNC
spi_gecko_transceive_async(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs,struct k_poll_signal * async)260 static int spi_gecko_transceive_async(const struct device *dev,
261 const struct spi_config *config,
262 const struct spi_buf_set *tx_bufs,
263 const struct spi_buf_set *rx_bufs,
264 struct k_poll_signal *async)
265 {
266 return -ENOTSUP;
267 }
268 #endif /* CONFIG_SPI_ASYNC */
269
spi_gecko_release(const struct device * dev,const struct spi_config * config)270 static int spi_gecko_release(const struct device *dev,
271 const struct spi_config *config)
272 {
273 const struct spi_gecko_config *gecko_config = dev->config;
274
275 if (!(gecko_config->base->STATUS & USART_STATUS_TXIDLE)) {
276 return -EBUSY;
277 }
278 return 0;
279 }
280
281 /* Device Instantiation */
282 static struct spi_driver_api spi_gecko_api = {
283 .transceive = spi_gecko_transceive,
284 #ifdef CONFIG_SPI_ASYNC
285 .transceive_async = spi_gecko_transceive_async,
286 #endif /* CONFIG_SPI_ASYNC */
287 .release = spi_gecko_release,
288 };
289
290 #define SPI_INIT2(n, usart) \
291 static struct spi_gecko_data spi_gecko_data_##n = { \
292 SPI_CONTEXT_INIT_LOCK(spi_gecko_data_##n, ctx), \
293 SPI_CONTEXT_INIT_SYNC(spi_gecko_data_##n, ctx), \
294 }; \
295 static struct spi_gecko_config spi_gecko_cfg_##n = { \
296 .base = (USART_TypeDef *) \
297 DT_INST_REG_ADDR(n), \
298 .clock = CLOCK_USART(usart), \
299 .pin_rx = { DT_INST_PROP_BY_IDX(n, location_rx, 1), \
300 DT_INST_PROP_BY_IDX(n, location_rx, 2), \
301 gpioModeInput, 1}, \
302 .pin_tx = { DT_INST_PROP_BY_IDX(n, location_tx, 1), \
303 DT_INST_PROP_BY_IDX(n, location_tx, 2), \
304 gpioModePushPull, 1}, \
305 .pin_clk = { DT_INST_PROP_BY_IDX(n, location_clk, 1), \
306 DT_INST_PROP_BY_IDX(n, location_clk, 2), \
307 gpioModePushPull, 1}, \
308 .loc_rx = DT_INST_PROP_BY_IDX(n, location_rx, 0), \
309 .loc_tx = DT_INST_PROP_BY_IDX(n, location_tx, 0), \
310 .loc_clk = DT_INST_PROP_BY_IDX(n, location_clk, 0), \
311 }; \
312 DEVICE_DT_INST_DEFINE(n, \
313 spi_gecko_init, \
314 NULL, \
315 &spi_gecko_data_##n, \
316 &spi_gecko_cfg_##n, \
317 POST_KERNEL, \
318 CONFIG_SPI_INIT_PRIORITY, \
319 &spi_gecko_api);
320
321 #define SPI_ID(n) DT_INST_PROP(n, peripheral_id)
322
323 #define SPI_INIT(n) SPI_INIT2(n, SPI_ID(n))
324
325 DT_INST_FOREACH_STATUS_OKAY(SPI_INIT)
326