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