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