1 /*
2 * Copyright (c) 2024 Daikin Comfort Technologies North America, Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT silabs_gecko_spi_eusart
8
9 #include <stdbool.h>
10
11 #include <zephyr/sys/sys_io.h>
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/spi.h>
14 #include <zephyr/drivers/clock_control.h>
15 #include <zephyr/drivers/clock_control/clock_control_silabs.h>
16 #include <zephyr/drivers/pinctrl.h>
17 #include <zephyr/logging/log.h>
18
19 #include <em_cmu.h>
20 #include <em_eusart.h>
21
22 LOG_MODULE_REGISTER(spi_gecko_eusart, CONFIG_SPI_LOG_LEVEL);
23
24 #include "spi_context.h"
25
26 #define SPI_WORD_SIZE 8
27
28 /* Structure Declarations */
29
30 struct spi_gecko_eusart_data {
31 struct spi_context ctx;
32 };
33
34 struct spi_gecko_eusart_config {
35 EUSART_TypeDef *base;
36 const struct device *clock_dev;
37 const struct silabs_clock_control_cmu_config clock_cfg;
38 uint32_t clock_frequency;
39 const struct pinctrl_dev_config *pcfg;
40 };
41
42 /* Helper Functions */
spi_eusart_config(const struct device * dev,const struct spi_config * config,uint16_t * control)43 static int spi_eusart_config(const struct device *dev, const struct spi_config *config,
44 uint16_t *control)
45 {
46 struct spi_gecko_eusart_data *data = dev->data;
47 const struct spi_gecko_eusart_config *gecko_config = dev->config;
48 uint32_t spi_frequency;
49
50 EUSART_SpiAdvancedInit_TypeDef eusartAdvancedSpiInit = EUSART_SPI_ADVANCED_INIT_DEFAULT;
51 EUSART_SpiInit_TypeDef eusartInit = EUSART_SPI_MASTER_INIT_DEFAULT_HF;
52
53 int err;
54
55 err = clock_control_get_rate(gecko_config->clock_dev,
56 (clock_control_subsys_t)&gecko_config->clock_cfg,
57 &spi_frequency);
58 if (err) {
59 return err;
60 }
61 /* Max supported SPI frequency is half the source clock */
62 spi_frequency /= 2;
63
64 if (spi_context_configured(&data->ctx, config)) {
65 /* Already configured. No need to do it again. */
66 return 0;
67 }
68
69 if (config->operation & SPI_HALF_DUPLEX) {
70 LOG_ERR("Half-duplex not supported");
71 return -ENOTSUP;
72 }
73
74 if (SPI_WORD_SIZE_GET(config->operation) != SPI_WORD_SIZE) {
75 LOG_ERR("Word size must be %d", SPI_WORD_SIZE);
76 return -ENOTSUP;
77 }
78
79 if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) &&
80 (config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) {
81 LOG_ERR("Only supports single mode");
82 return -ENOTSUP;
83 }
84
85 if (config->operation & SPI_OP_MODE_SLAVE) {
86 LOG_ERR("Slave mode not supported");
87 return -ENOTSUP;
88 }
89
90 /* Set frequency to the minimum of what the device supports, what the
91 * user has configured the controller to, and the max frequency for the
92 * transaction.
93 */
94 if (gecko_config->clock_frequency > spi_frequency) {
95 LOG_ERR("SPI clock-frequency too high");
96 return -EINVAL;
97 }
98 spi_frequency = MIN(gecko_config->clock_frequency, spi_frequency);
99 if (config->frequency) {
100 spi_frequency = MIN(config->frequency, spi_frequency);
101 }
102 eusartInit.bitRate = spi_frequency;
103
104 if (config->operation & SPI_MODE_LOOP) {
105 eusartInit.loopbackEnable = eusartLoopbackEnable;
106 } else {
107 eusartInit.loopbackEnable = eusartLoopbackDisable;
108 }
109
110 /* Set Clock Mode */
111 if (config->operation & SPI_MODE_CPOL) {
112 if (config->operation & SPI_MODE_CPHA) {
113 eusartInit.clockMode = eusartClockMode3;
114 } else {
115 eusartInit.clockMode = eusartClockMode2;
116 }
117 } else {
118 if (config->operation & SPI_MODE_CPHA) {
119 eusartInit.clockMode = eusartClockMode1;
120 } else {
121 eusartInit.clockMode = eusartClockMode0;
122 }
123 }
124
125 if (config->operation & SPI_CS_ACTIVE_HIGH) {
126 eusartAdvancedSpiInit.csPolarity = eusartCsActiveHigh;
127 } else {
128 eusartAdvancedSpiInit.csPolarity = eusartCsActiveLow;
129 }
130
131 eusartAdvancedSpiInit.msbFirst = !(config->operation & SPI_TRANSFER_LSB);
132 eusartAdvancedSpiInit.autoCsEnable = !spi_cs_is_gpio(config);
133 eusartInit.databits = eusartDataBits8;
134 eusartInit.advancedSettings = &eusartAdvancedSpiInit;
135
136 /* Enable EUSART clock */
137 err = clock_control_on(gecko_config->clock_dev,
138 (clock_control_subsys_t)&gecko_config->clock_cfg);
139 if (err < 0) {
140 return err;
141 }
142
143 /* Initialize the EUSART */
144 EUSART_SpiInit(gecko_config->base, &eusartInit);
145
146 data->ctx.config = config;
147
148 /* Enable the peripheral */
149 gecko_config->base->CMD = (uint32_t)eusartEnable;
150
151 return 0;
152 }
153
spi_gecko_eusart_send(EUSART_TypeDef * eusart,uint8_t frame)154 static void spi_gecko_eusart_send(EUSART_TypeDef *eusart, uint8_t frame)
155 {
156 /* Write frame to register */
157 EUSART_Tx(eusart, frame);
158
159 /* Wait until the transfer ends */
160 while (!(eusart->STATUS & EUSART_STATUS_TXC)) {
161 }
162 }
163
spi_gecko_eusart_recv(EUSART_TypeDef * eusart)164 static uint8_t spi_gecko_eusart_recv(EUSART_TypeDef *eusart)
165 {
166 /* Return data inside rx register */
167 return EUSART_Rx(eusart);
168 }
169
spi_eusart_transfer_ongoing(struct spi_gecko_eusart_data * data)170 static bool spi_eusart_transfer_ongoing(struct spi_gecko_eusart_data *data)
171 {
172 return spi_context_tx_on(&data->ctx) || spi_context_rx_on(&data->ctx);
173 }
174
spi_eusart_next_tx(struct spi_gecko_eusart_data * data)175 static inline uint8_t spi_eusart_next_tx(struct spi_gecko_eusart_data *data)
176 {
177 uint8_t tx_frame = 0;
178
179 if (spi_context_tx_buf_on(&data->ctx)) {
180 tx_frame = UNALIGNED_GET((uint8_t *)(data->ctx.tx_buf));
181 }
182
183 return tx_frame;
184 }
185
spi_eusart_shift_frames(EUSART_TypeDef * eusart,struct spi_gecko_eusart_data * data)186 static int spi_eusart_shift_frames(EUSART_TypeDef *eusart, struct spi_gecko_eusart_data *data)
187 {
188 uint8_t tx_frame;
189 uint8_t rx_frame;
190
191 tx_frame = spi_eusart_next_tx(data);
192 spi_gecko_eusart_send(eusart, tx_frame);
193 spi_context_update_tx(&data->ctx, 1, 1);
194
195 rx_frame = spi_gecko_eusart_recv(eusart);
196
197 if (spi_context_rx_buf_on(&data->ctx)) {
198 UNALIGNED_PUT(rx_frame, (uint8_t *)data->ctx.rx_buf);
199 }
200 spi_context_update_rx(&data->ctx, 1, 1);
201 return 0;
202 }
203
spi_gecko_eusart_xfer(const struct device * dev,const struct spi_config * config)204 static void spi_gecko_eusart_xfer(const struct device *dev, const struct spi_config *config)
205 {
206 int ret;
207 struct spi_gecko_eusart_data *data = dev->data;
208 struct spi_context *ctx = &data->ctx;
209 const struct spi_gecko_eusart_config *gecko_config = dev->config;
210
211 spi_context_cs_control(ctx, true);
212
213 do {
214 ret = spi_eusart_shift_frames(gecko_config->base, data);
215 } while (!ret && spi_eusart_transfer_ongoing(data));
216
217 spi_context_cs_control(ctx, false);
218 spi_context_complete(ctx, dev, 0);
219 }
220
221 /* API Functions */
spi_gecko_eusart_init(const struct device * dev)222 static int spi_gecko_eusart_init(const struct device *dev)
223 {
224 int err;
225 const struct spi_gecko_eusart_config *config = dev->config;
226 struct spi_gecko_eusart_data *data = dev->data;
227
228 err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
229 if (err < 0) {
230 return err;
231 }
232
233 err = spi_context_cs_configure_all(&data->ctx);
234 if (err < 0) {
235 return err;
236 }
237 spi_context_unlock_unconditionally(&data->ctx);
238
239 return 0;
240 }
241
spi_gecko_eusart_transceive(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs)242 static int spi_gecko_eusart_transceive(const struct device *dev, const struct spi_config *config,
243 const struct spi_buf_set *tx_bufs,
244 const struct spi_buf_set *rx_bufs)
245 {
246 struct spi_gecko_eusart_data *data = dev->data;
247 uint16_t control = 0;
248 int ret;
249
250 spi_context_lock(&data->ctx, false, NULL, NULL, config);
251
252 ret = spi_eusart_config(dev, config, &control);
253 if (ret < 0) {
254 spi_context_release(&data->ctx, ret);
255 return ret;
256 }
257
258 spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1);
259 spi_gecko_eusart_xfer(dev, config);
260
261 spi_context_release(&data->ctx, ret);
262
263 return 0;
264 }
265
266 #ifdef CONFIG_SPI_ASYNC
spi_gecko_eusart_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)267 static int spi_gecko_eusart_transceive_async(const struct device *dev,
268 const struct spi_config *config,
269 const struct spi_buf_set *tx_bufs,
270 const struct spi_buf_set *rx_bufs,
271 struct k_poll_signal *async)
272 {
273 return -ENOTSUP;
274 }
275 #endif /* CONFIG_SPI_ASYNC */
276
spi_gecko_eusart_release(const struct device * dev,const struct spi_config * config)277 static int spi_gecko_eusart_release(const struct device *dev, const struct spi_config *config)
278 {
279 const struct spi_gecko_eusart_config *gecko_config = dev->config;
280 struct spi_gecko_eusart_data *data = dev->data;
281
282 spi_context_unlock_unconditionally(&data->ctx);
283
284 if (!(gecko_config->base->STATUS & EUSART_STATUS_TXIDLE)) {
285 return -EBUSY;
286 }
287 return 0;
288 }
289
290 /* Device Instantiation */
291 static DEVICE_API(spi, spi_gecko_eusart_api) = {
292 .transceive = spi_gecko_eusart_transceive,
293 #ifdef CONFIG_SPI_ASYNC
294 .transceive_async = spi_gecko_eusart_transceive_async,
295 #endif /* CONFIG_SPI_ASYNC */
296 .release = spi_gecko_eusart_release,
297 };
298
299 #define SPI_INIT(n) \
300 PINCTRL_DT_INST_DEFINE(n); \
301 static struct spi_gecko_eusart_data spi_gecko_eusart_data_##n = { \
302 SPI_CONTEXT_INIT_LOCK(spi_gecko_eusart_data_##n, ctx), \
303 SPI_CONTEXT_INIT_SYNC(spi_gecko_eusart_data_##n, ctx), \
304 SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx)}; \
305 static struct spi_gecko_eusart_config spi_gecko_eusart_cfg_##n = { \
306 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
307 .base = (EUSART_TypeDef *)DT_INST_REG_ADDR(n), \
308 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
309 .clock_cfg = SILABS_DT_INST_CLOCK_CFG(n), \
310 .clock_frequency = DT_INST_PROP_OR(n, clock_frequency, 1000000) \
311 }; \
312 SPI_DEVICE_DT_INST_DEFINE(n, spi_gecko_eusart_init, NULL, &spi_gecko_eusart_data_##n, \
313 &spi_gecko_eusart_cfg_##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \
314 &spi_gecko_eusart_api);
315
316 DT_INST_FOREACH_STATUS_OKAY(SPI_INIT)
317