diff --git a/drivers/pinctrl/pinctrl_gecko.c b/drivers/pinctrl/pinctrl_gecko.c index f2d55046169b2f9..ec3a6a35f4c5934 100644 --- a/drivers/pinctrl/pinctrl_gecko.c +++ b/drivers/pinctrl/pinctrl_gecko.c @@ -24,6 +24,11 @@ int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintp (void)base; #endif /*USART_COUNT > 1*/ +#ifdef CONFIG_SPI_GECKO_EUSART + EUSART_TypeDef *ebase = (EUSART_TypeDef *)reg; + int eusart_num = EUSART_NUM(ebase); +#endif + #endif #ifdef CONFIG_I2C_GECKO @@ -171,7 +176,53 @@ int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintp #endif /* CONFIG_SOC_FAMILY_SILABS_S1 */ #endif /* CONFIG_UART_GECKO */ -#ifdef CONFIG_SPI_GECKO +#ifdef CONFIG_SPI_GECKO_EUSART + case GECKO_FUN_SPI_SCK: + pin_config.mode = gpioModePushPull; + pin_config.out = 1; + GPIO->EUSARTROUTE[eusart_num].SCLKROUTE = + (pin_config.port << _GPIO_EUSART_SCLKROUTE_PORT_SHIFT) | + (pin_config.pin << _GPIO_EUSART_SCLKROUTE_PIN_SHIFT); + GPIO->EUSARTROUTE[eusart_num].ROUTEEN |= GPIO_EUSART_ROUTEEN_SCLKPEN; + GPIO_PinModeSet(pin_config.port, pin_config.pin, pin_config.mode, + pin_config.out); + break; + + case GECKO_FUN_SPI_CSN: + pin_config.mode = gpioModePushPull; + pin_config.out = 1; + GPIO->EUSARTROUTE[eusart_num].CSROUTE = + (pin_config.port << _GPIO_EUSART_CSROUTE_PORT_SHIFT) | + (pin_config.pin << _GPIO_EUSART_CSROUTE_PIN_SHIFT); + GPIO->EUSARTROUTE[eusart_num].ROUTEEN |= GPIO_EUSART_ROUTEEN_CSPEN; + GPIO_PinModeSet(pin_config.port, pin_config.pin, pin_config.mode, + pin_config.out); + break; + + case GECKO_FUN_SPI_MOSI: + pin_config.mode = gpioModePushPull; + pin_config.out = 1; + GPIO->EUSARTROUTE[eusart_num].TXROUTE = + (pin_config.port << _GPIO_EUSART_TXROUTE_PORT_SHIFT) | + (pin_config.pin << _GPIO_EUSART_TXROUTE_PIN_SHIFT); + GPIO->EUSARTROUTE[eusart_num].ROUTEEN |= GPIO_EUSART_ROUTEEN_TXPEN; + GPIO_PinModeSet(pin_config.port, pin_config.pin, pin_config.mode, + pin_config.out); + break; + + case GECKO_FUN_SPI_MISO: + pin_config.mode = gpioModeInput; + pin_config.out = 1; + GPIO->EUSARTROUTE[eusart_num].RXROUTE = + (pin_config.port << _GPIO_EUSART_RXROUTE_PORT_SHIFT) | + (pin_config.pin << _GPIO_EUSART_RXROUTE_PIN_SHIFT); + GPIO->EUSARTROUTE[EUSART_NUM(EUSART1)].ROUTEEN |= GPIO_EUSART_ROUTEEN_RXPEN; + GPIO_PinModeSet(pin_config.port, pin_config.pin, pin_config.mode, + pin_config.out); + break; +#endif /*CONFIG_SPI_GECKO_EUSART*/ + +#ifdef CONFIG_SPI_GECKO_USART #ifdef CONFIG_SOC_FAMILY_SILABS_S1 case GECKO_FUN_SPIM_SCK: pin_config.mode = gpioModePushPull; @@ -287,7 +338,7 @@ int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintp pin_config.out); break; #endif /* CONFIG_SOC_FAMILY_SILABS_S1 */ -#endif /* CONFIG_SPI_GECKO */ +#endif /* CONFIG_SPI_GECKO_USART */ #ifdef CONFIG_I2C_GECKO case GECKO_FUN_I2C_SDA: diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index 722a8eba58745a6..d88ff745a23022c 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -28,7 +28,8 @@ zephyr_library_sources_ifdef(CONFIG_SPI_LITEX spi_litex.c) zephyr_library_sources_ifdef(CONFIG_SPI_LITEX_LITESPI spi_litex_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_SPI_GECKO_USART spi_gecko_usart.c) +zephyr_library_sources_ifdef(CONFIG_SPI_GECKO_EUSART spi_gecko_eusart.c) zephyr_library_sources_ifdef(CONFIG_SPI_XLNX_AXI_QUADSPI spi_xlnx_axi_quadspi.c) zephyr_library_sources_ifdef(CONFIG_ESP32_SPIM spi_esp32_spim.c) zephyr_library_sources_ifdef(CONFIG_SPI_TEST spi_test.c) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 97fb1e0c95f0199..1c4aaac2013a207 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -111,7 +111,9 @@ source "drivers/spi/Kconfig.oc_simple" source "drivers/spi/Kconfig.xec_qmspi" -source "drivers/spi/Kconfig.gecko" +source "drivers/spi/Kconfig.gecko_usart" + +source "drivers/spi/Kconfig.gecko_eusart" source "drivers/spi/Kconfig.xlnx" diff --git a/drivers/spi/Kconfig.gecko_eusart b/drivers/spi/Kconfig.gecko_eusart new file mode 100644 index 000000000000000..ca2e64adda8f949 --- /dev/null +++ b/drivers/spi/Kconfig.gecko_eusart @@ -0,0 +1,13 @@ +# Gecko SPI configuration option + +# Copyright (c) 2024 Daikin Comfort Technologies North America, Inc. +# SPDX-License-Identifier: Apache-2.0 + +config SPI_GECKO_EUSART + bool "Gecko EUSART SPI controller driver" + default y + depends on DT_HAS_SILABS_GECKO_SPI_EUSART_ENABLED + depends on GPIO + select SOC_GECKO_EUSART + help + Enable the EUSART SPI peripherals on Gecko diff --git a/drivers/spi/Kconfig.gecko b/drivers/spi/Kconfig.gecko_usart similarity index 94% rename from drivers/spi/Kconfig.gecko rename to drivers/spi/Kconfig.gecko_usart index b2f47f2e366ef02..87d4c9723c5694e 100644 --- a/drivers/spi/Kconfig.gecko +++ b/drivers/spi/Kconfig.gecko_usart @@ -3,7 +3,7 @@ # Copyright (c) 2019 Christian Taedcke # SPDX-License-Identifier: Apache-2.0 -config SPI_GECKO +config SPI_GECKO_USART bool "Gecko SPI controller driver" default y depends on DT_HAS_SILABS_GECKO_SPI_USART_ENABLED diff --git a/drivers/spi/spi_gecko_eusart.c b/drivers/spi/spi_gecko_eusart.c new file mode 100644 index 000000000000000..1b94ace1e27f751 --- /dev/null +++ b/drivers/spi/spi_gecko_eusart.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2024 Daikin Comfort Technologies North America, Inc. + * Copyright (c) 2019 Christian Taedcke + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_gecko_spi_eusart + +#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL +#include +LOG_MODULE_REGISTER(spi_gecko_eusart); +#include "spi_context.h" + +#include +#include +#include +#include + +#include "em_cmu.h" +#include "em_eusart.h" + +#include +#include + +#include +#include +#define GET_GECKO_EUSART_CLOCK(idx) \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \ + .clock_cfg = SILABS_DT_INST_CLOCK_CFG(idx), + +#define SPI_WORD_SIZE 8 + +/* Structure Declarations */ + +struct spi_gecko_eusart_data { + struct spi_context ctx; +}; + +struct spi_gecko_eusart_config { + EUSART_TypeDef *base; + const struct device *clock_dev; + const struct silabs_clock_control_cmu_config clock_cfg; + uint32_t clock_frequency; + const struct pinctrl_dev_config *pcfg; +}; + +/* Helper Functions */ +static int spi_eusart_config(const struct device *dev, const struct spi_config *config, + uint16_t *control) +{ + struct spi_gecko_eusart_data *data = dev->data; + const struct spi_gecko_eusart_config *gecko_config = dev->config; + uint32_t spi_frequency; + + EUSART_SpiAdvancedInit_TypeDef eusartAdvancedSpiInit = EUSART_SPI_ADVANCED_INIT_DEFAULT; + EUSART_SpiInit_TypeDef eusartInit = EUSART_SPI_MASTER_INIT_DEFAULT_HF; + + int err; + + err = clock_control_get_rate(gecko_config->clock_dev, + (clock_control_subsys_t)&gecko_config->clock_cfg, + &spi_frequency); + if (err) { + return err; + } + /* Max supported SPI frequency is half the source clock */ + spi_frequency /= 2; + + if (spi_context_configured(&data->ctx, config)) { + /* Already configured. No need to do it again. */ + return 0; + } + + if (config->operation & SPI_HALF_DUPLEX) { + LOG_ERR("Half-duplex not supported"); + return -ENOTSUP; + } + + if (SPI_WORD_SIZE_GET(config->operation) != SPI_WORD_SIZE) { + LOG_ERR("Word size must be %d", SPI_WORD_SIZE); + return -ENOTSUP; + } + + if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) && + (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_OP_MODE_SLAVE) { + LOG_ERR("Slave mode not supported"); + return -ENOTSUP; + } + + /* Set frequency to the minimum of what the device supports, what the + * user has configured the controller to, and the max frequency for the + * transaction. + */ + if (gecko_config->clock_frequency > spi_frequency) { + LOG_ERR("SPI clock-frequency too high"); + return -EINVAL; + } + spi_frequency = MIN(gecko_config->clock_frequency, spi_frequency); + if (config->frequency) { + spi_frequency = MIN(config->frequency, spi_frequency); + } + eusartInit.bitRate = spi_frequency; + + if (config->operation & SPI_MODE_LOOP) { + eusartInit.loopbackEnable = eusartLoopbackEnable; + } else { + eusartInit.loopbackEnable = eusartLoopbackDisable; + } + + /* Set Clock Mode */ + if (config->operation & SPI_MODE_CPOL) { + if (config->operation & SPI_MODE_CPHA) { + eusartInit.clockMode = eusartClockMode3; + } else { + eusartInit.clockMode = eusartClockMode2; + } + } else { + if (config->operation & SPI_MODE_CPHA) { + eusartInit.clockMode = eusartClockMode1; + } else { + eusartInit.clockMode = eusartClockMode0; + } + } + + if (config->operation & SPI_CS_ACTIVE_HIGH) { + eusartAdvancedSpiInit.csPolarity = eusartCsActiveHigh; + } else { + eusartAdvancedSpiInit.csPolarity = eusartCsActiveLow; + } + + eusartInit.databits = eusartDataBits8; + eusartInit.advancedSettings = &eusartAdvancedSpiInit; + + /* Enable EUSART clock */ + err = clock_control_on(gecko_config->clock_dev, + (clock_control_subsys_t)&gecko_config->clock_cfg); + if (err < 0) { + return err; + } + + /* Initialize the EUSART */ + EUSART_SpiInit(gecko_config->base, &eusartInit); + + data->ctx.config = config; + + /* Enable the peripheral */ + gecko_config->base->CMD = (uint32_t)eusartEnable; + + return 0; +} + +static void spi_gecko_eusart_send(EUSART_TypeDef *eusart, uint8_t frame) +{ + /* Write frame to register */ + EUSART_Tx(eusart, frame); + + /* Wait until the transfer ends */ + while (!(eusart->STATUS & EUSART_STATUS_TXC)) { + } +} + +static uint8_t spi_gecko_eusart_recv(EUSART_TypeDef *eusart) +{ + /* Return data inside rx register */ + return EUSART_Rx(eusart); +} + +static bool spi_eusart_transfer_ongoing(struct spi_gecko_eusart_data *data) +{ + return spi_context_tx_on(&data->ctx) || spi_context_rx_on(&data->ctx); +} + +static inline uint8_t spi_eusart_next_tx(struct spi_gecko_eusart_data *data) +{ + uint8_t tx_frame = 0; + + if (spi_context_tx_buf_on(&data->ctx)) { + tx_frame = UNALIGNED_GET((uint8_t *)(data->ctx.tx_buf)); + } + + return tx_frame; +} + +static int spi_eusart_shift_frames(EUSART_TypeDef *eusart, struct spi_gecko_eusart_data *data) +{ + uint8_t tx_frame; + uint8_t rx_frame; + + tx_frame = spi_eusart_next_tx(data); + spi_gecko_eusart_send(eusart, tx_frame); + spi_context_update_tx(&data->ctx, 1, 1); + + rx_frame = spi_gecko_eusart_recv(eusart); + + if (spi_context_rx_buf_on(&data->ctx)) { + UNALIGNED_PUT(rx_frame, (uint8_t *)data->ctx.rx_buf); + } + spi_context_update_rx(&data->ctx, 1, 1); + return 0; +} + +static void spi_gecko_eusart_xfer(const struct device *dev, const struct spi_config *config) +{ + int ret; + struct spi_gecko_eusart_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + const struct spi_gecko_eusart_config *gecko_config = dev->config; + + spi_context_cs_control(ctx, true); + + do { + ret = spi_eusart_shift_frames(gecko_config->base, data); + } while (!ret && spi_eusart_transfer_ongoing(data)); + + spi_context_cs_control(ctx, false); + spi_context_complete(ctx, dev, 0); +} + +/* API Functions */ +static int spi_gecko_eusart_init(const struct device *dev) +{ + int err; + const struct spi_gecko_eusart_config *config = dev->config; + struct spi_gecko_eusart_data *data = dev->data; + + err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (err < 0) { + return err; + } + + err = spi_context_cs_configure_all(&data->ctx); + if (err < 0) { + return err; + } + spi_context_unlock_unconditionally(&data->ctx); + + return 0; +} + +static int 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) +{ + struct spi_gecko_eusart_data *data = dev->data; + uint16_t control = 0; + int ret; + + spi_context_lock(&data->ctx, false, NULL, NULL, config); + + ret = spi_eusart_config(dev, config, &control); + if (ret < 0) { + spi_context_release(&data->ctx, ret); + return ret; + } + + spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1); + spi_gecko_eusart_xfer(dev, config); + + spi_context_release(&data->ctx, ret); + + return 0; +} + +#ifdef CONFIG_SPI_ASYNC +static int 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) +{ + return -ENOTSUP; +} +#endif /* CONFIG_SPI_ASYNC */ + +static int spi_gecko_eusart_release(const struct device *dev, const struct spi_config *config) +{ + const struct spi_gecko_eusart_config *gecko_config = dev->config; + struct spi_gecko_eusart_data *data = dev->data; + + spi_context_unlock_unconditionally(&data->ctx); + + if (!(gecko_config->base->STATUS & EUSART_STATUS_TXIDLE)) { + return -EBUSY; + } + return 0; +} + +/* Device Instantiation */ +static const struct spi_driver_api spi_gecko_eusart_api = { + .transceive = spi_gecko_eusart_transceive, +#ifdef CONFIG_SPI_ASYNC + .transceive_async = spi_gecko_eusart_transceive_async, +#endif /* CONFIG_SPI_ASYNC */ + .release = spi_gecko_eusart_release, +}; + +#define SPI_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + static struct spi_gecko_eusart_data spi_gecko_eusart_data_##n = { \ + SPI_CONTEXT_INIT_LOCK(spi_gecko_eusart_data_##n, ctx), \ + SPI_CONTEXT_INIT_SYNC(spi_gecko_eusart_data_##n, ctx), \ + SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx)}; \ + static struct spi_gecko_eusart_config spi_gecko_eusart_cfg_##n = { \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .base = (EUSART_TypeDef *)DT_INST_REG_ADDR(n), \ + GET_GECKO_EUSART_CLOCK(n) \ + .clock_frequency = DT_INST_PROP_OR(n, clock_frequency, 1000000) \ + }; \ + DEVICE_DT_INST_DEFINE(n, spi_gecko_eusart_init, NULL, &spi_gecko_eusart_data_##n, \ + &spi_gecko_eusart_cfg_##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ + &spi_gecko_eusart_api); + +DT_INST_FOREACH_STATUS_OKAY(SPI_INIT) diff --git a/drivers/spi/spi_gecko.c b/drivers/spi/spi_gecko_usart.c similarity index 100% rename from drivers/spi/spi_gecko.c rename to drivers/spi/spi_gecko_usart.c diff --git a/dts/arm/silabs/efr32mg24.dtsi b/dts/arm/silabs/efr32mg24.dtsi index 29fa2cb647bd6c2..c12421bc602129e 100644 --- a/dts/arm/silabs/efr32mg24.dtsi +++ b/dts/arm/silabs/efr32mg24.dtsi @@ -127,6 +127,7 @@ clocks = <&cmu CLOCK_EUSART0 CLOCK_BRANCH_EUSART0CLK>; status = "disabled"; }; + eusart1: eusart@500a0000 { compatible = "silabs,gecko-spi-eusart"; reg = <0x500A0000 0x4000>; diff --git a/dts/bindings/spi/silabs,gecko-spi-eusart.yaml b/dts/bindings/spi/silabs,gecko-spi-eusart.yaml new file mode 100644 index 000000000000000..1663c8ec8dc7983 --- /dev/null +++ b/dts/bindings/spi/silabs,gecko-spi-eusart.yaml @@ -0,0 +1,21 @@ +description: GECKO EUSART SPI + +compatible: "silabs,gecko-spi-eusart" + +include: [spi-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true + + clocks: + required: true diff --git a/soc/silabs/Kconfig b/soc/silabs/Kconfig index c5a87b19c961230..82cae9d7e3a27cc 100644 --- a/soc/silabs/Kconfig +++ b/soc/silabs/Kconfig @@ -105,6 +105,12 @@ config SOC_GECKO_USART Set if the Universal Synchronous Asynchronous Receiver/Transmitter (USART) HAL module is used. +config SOC_GECKO_EUSART + bool + help + Set if the Extended Universal Synchronous Asynchronous Receiver/Transmitter (EUSART) + HAL module is used. + config SOC_GECKO_WDOG bool help