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 <hacking@taedcke.com>
diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt
index e1f637e..7a463b4 100644
--- a/drivers/spi/CMakeLists.txt
+++ b/drivers/spi/CMakeLists.txt
@@ -18,5 +18,6 @@
 zephyr_library_sources_ifdef(CONFIG_SPI_LITESPI		spi_litespi.c)
 zephyr_library_sources_ifdef(CONFIG_SPI_OC_SIMPLE	spi_oc_simple.c)
 zephyr_library_sources_ifdef(CONFIG_SPI_XEC_QMSPI	spi_xec_qmspi.c)
+zephyr_library_sources_ifdef(CONFIG_SPI_GECKO		spi_gecko.c)
 
 zephyr_library_sources_ifdef(CONFIG_USERSPACE		spi_handlers.c)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 820f8bf..dc84b14 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -244,4 +244,6 @@
 
 source "drivers/spi/Kconfig.xec_qmspi"
 
+source "drivers/spi/Kconfig.gecko"
+
 endif # SPI
diff --git a/drivers/spi/Kconfig.gecko b/drivers/spi/Kconfig.gecko
new file mode 100644
index 0000000..c2228d9
--- /dev/null
+++ b/drivers/spi/Kconfig.gecko
@@ -0,0 +1,14 @@
+# Kconfig.gecko - Gecko SPI configuration option
+#
+# Copyright (c) 2019 Christian Taedcke <hacking@taedcke.com>
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+menuconfig SPI_GECKO
+	bool "Gecko SPI controller driver"
+	depends on HAS_SILABS_GECKO
+	depends on GPIO_GECKO
+	select SOC_GECKO_USART
+	help
+	  Enable the SPI peripherals on Gecko
diff --git a/drivers/spi/spi_gecko.c b/drivers/spi/spi_gecko.c
new file mode 100644
index 0000000..4d0c243
--- /dev/null
+++ b/drivers/spi/spi_gecko.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2019 Christian Taedcke <hacking@taedcke.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
+#include <logging/log.h>
+LOG_MODULE_REGISTER(spi_gecko);
+#include "spi_context.h"
+
+#include <sys/sys_io.h>
+#include <device.h>
+#include <drivers/spi.h>
+#include <soc.h>
+
+#include "em_cmu.h"
+#include "em_usart.h"
+
+#include <stdbool.h>
+
+#ifndef CONFIG_SOC_GECKO_HAS_INDIVIDUAL_PIN_LOCATION
+#error "Individual pin location support is required"
+#endif
+
+#define CLOCK_USART(id) _CONCAT(cmuClock_USART, id)
+
+#define SPI_WORD_SIZE 8
+
+#define DEV_DATA(dev) ((struct spi_gecko_data *) ((dev)->driver_data))
+
+/* Structure Declarations */
+
+struct spi_gecko_data {
+	struct spi_context ctx;
+};
+
+struct spi_gecko_config {
+	USART_TypeDef *base;
+	CMU_Clock_TypeDef clock;
+	struct soc_gpio_pin pin_rx;
+	struct soc_gpio_pin pin_tx;
+	struct soc_gpio_pin pin_clk;
+	u8_t loc_rx;
+	u8_t loc_tx;
+	u8_t loc_clk;
+};
+
+
+/* Helper Functions */
+static int spi_config(struct device *dev, const struct spi_config *config,
+		      u16_t *control)
+{
+	const struct spi_gecko_config *gecko_config = dev->config->config_info;
+	struct spi_gecko_data *data = DEV_DATA(dev);
+
+	if (SPI_WORD_SIZE_GET(config->operation) != SPI_WORD_SIZE) {
+		LOG_ERR("Word size must be %d", SPI_WORD_SIZE);
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_LOCK_ON) {
+		LOG_ERR("Lock On not supported");
+		return -ENOTSUP;
+	}
+
+	if ((config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) {
+		LOG_ERR("Only supports single mode");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_TRANSFER_LSB) {
+		LOG_ERR("LSB first not supported");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & (SPI_MODE_CPOL | SPI_MODE_CPHA)) {
+		LOG_ERR("Only supports CPOL=CPHA=0");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_OP_MODE_SLAVE) {
+		LOG_ERR("Slave mode not supported");
+		return -ENOTSUP;
+	}
+
+	/* Set Loopback */
+	if (config->operation & SPI_MODE_LOOP) {
+		gecko_config->base->CTRL |= USART_CTRL_LOOPBK;
+	} else {
+		gecko_config->base->CTRL &= ~USART_CTRL_LOOPBK;
+	}
+
+	/* Set word size */
+	gecko_config->base->FRAME = usartDatabits8
+	    | USART_FRAME_STOPBITS_DEFAULT
+	    | USART_FRAME_PARITY_DEFAULT;
+
+	/* At this point, it's mandatory to set this on the context! */
+	data->ctx.config = config;
+
+	spi_context_cs_configure(&data->ctx);
+
+	return 0;
+}
+
+static void spi_gecko_send(USART_TypeDef *usart, u8_t frame)
+{
+	/* Write frame to register */
+	USART_Tx(usart, frame);
+
+	/* Wait until the transfer ends */
+	while (!(usart->STATUS & USART_STATUS_TXC)) {
+	}
+}
+
+static u8_t spi_gecko_recv(USART_TypeDef *usart)
+{
+	/* Return data inside rx register */
+	return (u8_t)usart->RXDATA;
+}
+
+static bool spi_gecko_transfer_ongoing(struct spi_gecko_data *data)
+{
+	return spi_context_tx_on(&data->ctx) || spi_context_rx_on(&data->ctx);
+}
+
+static inline u8_t spi_gecko_next_tx(struct spi_gecko_data *data)
+{
+	u8_t tx_frame = 0;
+
+	if (spi_context_tx_buf_on(&data->ctx)) {
+		tx_frame = UNALIGNED_GET((u8_t *)(data->ctx.tx_buf));
+	}
+
+	return tx_frame;
+}
+
+static int spi_gecko_shift_frames(USART_TypeDef *usart,
+				  struct spi_gecko_data *data)
+{
+	u8_t tx_frame;
+	u8_t rx_frame;
+
+	tx_frame = spi_gecko_next_tx(data);
+	spi_gecko_send(usart, tx_frame);
+	spi_context_update_tx(&data->ctx, 1, 1);
+
+	rx_frame = spi_gecko_recv(usart);
+
+	if (spi_context_rx_buf_on(&data->ctx)) {
+		UNALIGNED_PUT(rx_frame, (u8_t *)data->ctx.rx_buf);
+	}
+	spi_context_update_rx(&data->ctx, 1, 1);
+	return 0;
+}
+
+
+static void spi_gecko_xfer(struct device *dev,
+		const struct spi_config *config)
+{
+	int ret;
+	struct spi_context *ctx = &DEV_DATA(dev)->ctx;
+	const struct spi_gecko_config *gecko_config = dev->config->config_info;
+	struct spi_gecko_data *data = DEV_DATA(dev);
+
+	spi_context_cs_control(ctx, true);
+
+	do {
+		ret = spi_gecko_shift_frames(gecko_config->base, data);
+	} while (!ret && spi_gecko_transfer_ongoing(data));
+
+	spi_context_cs_control(ctx, false);
+	spi_context_complete(ctx, 0);
+}
+
+static void spi_gecko_init_pins(struct device *dev)
+{
+	const struct spi_gecko_config *config = dev->config->config_info;
+
+	soc_gpio_configure(&config->pin_rx);
+	soc_gpio_configure(&config->pin_tx);
+	soc_gpio_configure(&config->pin_clk);
+
+	/* disable all pins while configuring */
+	config->base->ROUTEPEN = 0;
+
+	config->base->ROUTELOC0 =
+		(config->loc_tx << _USART_ROUTELOC0_TXLOC_SHIFT) |
+		(config->loc_rx << _USART_ROUTELOC0_RXLOC_SHIFT) |
+		(config->loc_clk << _USART_ROUTELOC0_CLKLOC_SHIFT);
+
+	config->base->ROUTELOC1 = _USART_ROUTELOC1_RESETVALUE;
+
+	config->base->ROUTEPEN = USART_ROUTEPEN_RXPEN | USART_ROUTEPEN_TXPEN |
+		USART_ROUTEPEN_CLKPEN;
+}
+
+
+/* API Functions */
+
+static int spi_gecko_init(struct device *dev)
+{
+	const struct spi_gecko_config *config = dev->config->config_info;
+	USART_InitSync_TypeDef usartInit = USART_INITSYNC_DEFAULT;
+
+	/* The peripheral and gpio clock are already enabled from soc and gpio
+	 * driver
+	 */
+
+	usartInit.enable = usartDisable;
+	usartInit.baudrate = 1000000;
+	usartInit.databits = usartDatabits8;
+	usartInit.master = 1;
+	usartInit.msbf = 1;
+	usartInit.clockMode = usartClockMode0;
+#if defined(USART_INPUT_RXPRS) && defined(USART_TRIGCTRL_AUTOTXTEN)
+	usartInit.prsRxEnable = 0;
+	usartInit.prsRxCh = 0;
+	usartInit.autoTx = 0;
+#endif
+
+	/* Enable USART clock */
+	CMU_ClockEnable(config->clock, true);
+
+	/* Init USART */
+	USART_InitSync(config->base, &usartInit);
+
+	/* Initialize USART pins */
+	spi_gecko_init_pins(dev);
+
+	/* Enable the peripheral */
+	config->base->CMD = (uint32_t) usartEnable;
+
+	return 0;
+}
+
+static int spi_gecko_transceive(struct device *dev,
+			  const struct spi_config *config,
+			  const struct spi_buf_set *tx_bufs,
+			  const struct spi_buf_set *rx_bufs)
+{
+	u16_t control = 0;
+
+	spi_config(dev, config, &control);
+	spi_context_buffers_setup(&DEV_DATA(dev)->ctx, tx_bufs, rx_bufs, 1);
+	spi_gecko_xfer(dev, config);
+	return 0;
+}
+
+#ifdef CONFIG_SPI_ASYNC
+static int spi_gecko_transceive_async(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)
+{
+	return -ENOTSUP;
+}
+#endif /* CONFIG_SPI_ASYNC */
+
+static int spi_gecko_release(struct device *dev,
+			     const struct spi_config *config)
+{
+	const struct spi_gecko_config *gecko_config = dev->config->config_info;
+
+	if (!(gecko_config->base->STATUS & USART_STATUS_TXIDLE)) {
+		return -EBUSY;
+	}
+	return 0;
+}
+
+/* Device Instantiation */
+static struct spi_driver_api spi_gecko_api = {
+	.transceive = spi_gecko_transceive,
+#ifdef CONFIG_SPI_ASYNC
+	.transceive_async = spi_gecko_transceive_async,
+#endif /* CONFIG_SPI_ASYNC */
+	.release = spi_gecko_release,
+};
+
+#define SPI_INIT2(n, usart)				    \
+	static struct spi_gecko_data spi_gecko_data_##n = { \
+		SPI_CONTEXT_INIT_LOCK(spi_gecko_data_##n, ctx), \
+		SPI_CONTEXT_INIT_SYNC(spi_gecko_data_##n, ctx), \
+	}; \
+	static struct spi_gecko_config spi_gecko_cfg_##n = { \
+	    .base = (USART_TypeDef *) \
+		 DT_INST_##n##_SILABS_GECKO_SPI_USART_BASE_ADDRESS, \
+	    .clock = CLOCK_USART(usart), \
+	    .pin_rx = { DT_INST_##n##_SILABS_GECKO_SPI_USART_LOCATION_RX_1, \
+			DT_INST_##n##_SILABS_GECKO_SPI_USART_LOCATION_RX_2, \
+			gpioModeInput, 1},				\
+	    .pin_tx = { DT_INST_##n##_SILABS_GECKO_SPI_USART_LOCATION_TX_1, \
+			DT_INST_##n##_SILABS_GECKO_SPI_USART_LOCATION_TX_2, \
+			gpioModePushPull, 1},				\
+	    .pin_clk = { DT_INST_##n##_SILABS_GECKO_SPI_USART_LOCATION_CLK_1, \
+			DT_INST_##n##_SILABS_GECKO_SPI_USART_LOCATION_CLK_2, \
+			gpioModePushPull, 1},				\
+	    .loc_rx = DT_INST_##n##_SILABS_GECKO_SPI_USART_LOCATION_RX_0, \
+	    .loc_tx = DT_INST_##n##_SILABS_GECKO_SPI_USART_LOCATION_TX_0, \
+	    .loc_clk = DT_INST_##n##_SILABS_GECKO_SPI_USART_LOCATION_CLK_0, \
+	}; \
+	DEVICE_AND_API_INIT(spi_##n, \
+			DT_INST_##n##_SILABS_GECKO_SPI_USART_LABEL, \
+			spi_gecko_init, \
+			&spi_gecko_data_##n, \
+			&spi_gecko_cfg_##n, \
+			POST_KERNEL, \
+			CONFIG_SPI_INIT_PRIORITY, \
+			&spi_gecko_api)
+
+#define SPI_ID(n) DT_INST_##n##_SILABS_GECKO_SPI_USART_PERIPHERAL_ID
+
+#define SPI_INIT(n) SPI_INIT2(n, SPI_ID(n))
+
+#ifdef DT_INST_0_SILABS_GECKO_SPI_USART_LABEL
+
+SPI_INIT(0);
+
+#endif /* DT_INST_0_SILABS_GECKO_SPI_USART_LABEL */
+
+#ifdef DT_INST_1_SILABS_GECKO_SPI_USART_LABEL
+
+SPI_INIT(1);
+
+#endif /* DT_INST_1_SILABS_GECKO_SPI_USART_LABEL */
+
+#ifdef DT_INST_2_SILABS_GECKO_SPI_USART_LABEL
+
+SPI_INIT(2);
+
+#endif /* DT_INST_2_SILABS_GECKO_SPI_USART_LABEL */
+
+#ifdef DT_INST_3_SILABS_GECKO_SPI_USART_LABEL
+
+SPI_INIT(3);
+
+#endif /* DT_INST_3_SILABS_GECKO_SPI_USART_LABEL */
diff --git a/dts/bindings/spi/silabs,gecko-spi-usart.yaml b/dts/bindings/spi/silabs,gecko-spi-usart.yaml
new file mode 100644
index 0000000..8c68fe3
--- /dev/null
+++ b/dts/bindings/spi/silabs,gecko-spi-usart.yaml
@@ -0,0 +1,38 @@
+title: GECKO USART SPI
+
+description: >
+    This binding gives a base representation of the Gecko USART SPI
+
+compatible: "silabs,gecko-spi-usart"
+
+include: spi-controller.yaml
+
+properties:
+    reg:
+      required: true
+
+    interrupts:
+      required: true
+
+    peripheral-id:
+      type: int
+      required: true
+      description: peripheral ID
+
+    # Note: Not all SoC series support setting individual pin location. If this
+    # is a case all location-* properties need to have identical value.
+
+    location-rx:
+      type: array
+      required: true
+      description: RX pin configuration defined as <location port pin>
+
+    location-tx:
+      type: array
+      required: true
+      description: TX pin configuration defined as <location port pin>
+
+    location-clk:
+      type: array
+      required: true
+      description: CLK pin configuration defined as <location port pin>