Skip to content

Commit 6da94af

Browse files
chrtaMaureenHelm
authored andcommitted
drivers: spi: Add Gecko SPI driver
This commit adds SPI driver and its bindings using the USART peripheral for Silicon Labs EFM32 and EFR32 MCUs. Signed-off-by: Christian Taedcke <[email protected]>
1 parent 44daa9f commit 6da94af

File tree

5 files changed

+399
-0
lines changed

5 files changed

+399
-0
lines changed

drivers/spi/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ zephyr_library_sources_ifdef(CONFIG_NRFX_SPIS spi_nrfx_spis.c)
1818
zephyr_library_sources_ifdef(CONFIG_SPI_LITESPI spi_litespi.c)
1919
zephyr_library_sources_ifdef(CONFIG_SPI_OC_SIMPLE spi_oc_simple.c)
2020
zephyr_library_sources_ifdef(CONFIG_SPI_XEC_QMSPI spi_xec_qmspi.c)
21+
zephyr_library_sources_ifdef(CONFIG_SPI_GECKO spi_gecko.c)
2122

2223
zephyr_library_sources_ifdef(CONFIG_USERSPACE spi_handlers.c)

drivers/spi/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,6 @@ source "drivers/spi/Kconfig.oc_simple"
244244

245245
source "drivers/spi/Kconfig.xec_qmspi"
246246

247+
source "drivers/spi/Kconfig.gecko"
248+
247249
endif # SPI

drivers/spi/Kconfig.gecko

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Kconfig.gecko - Gecko SPI configuration option
2+
#
3+
# Copyright (c) 2019 Christian Taedcke <[email protected]>
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
#
7+
8+
menuconfig SPI_GECKO
9+
bool "Gecko SPI controller driver"
10+
depends on HAS_SILABS_GECKO
11+
depends on GPIO_GECKO
12+
select SOC_GECKO_USART
13+
help
14+
Enable the SPI peripherals on Gecko

drivers/spi/spi_gecko.c

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

0 commit comments

Comments
 (0)