From 9cdc5d38b2a2a50aebeffdeca0998ea4c367c993 Mon Sep 17 00:00:00 2001 From: Ben Lauret Date: Sun, 13 Nov 2022 18:00:28 +0100 Subject: [PATCH] drivers: spi: Add driver for smartbond This adds the SPI driver for the Renesas SmartBond(tm) DA1469x MCU family. The driver only supports controller mode. All four SPI modes are supported. Note that the lowest supported speed is 2285714Hz. Requesting speeds higher than 16MHz, will result in a 16MHz SCLK. Co-authored-by: Stan Geitel Signed-off-by: Ben Lauret --- .../da1469x_dk_pro-pinctrl.dtsi | 24 ++ boards/arm/da1469x_dk_pro/da1469x_dk_pro.dts | 12 + boards/arm/da1469x_dk_pro/da1469x_dk_pro.yaml | 1 + boards/arm/da1469x_dk_pro/doc/index.rst | 2 + drivers/pinctrl/pinctrl_smartbond.c | 2 + drivers/spi/CMakeLists.txt | 1 + drivers/spi/Kconfig | 2 + drivers/spi/Kconfig.smartbond | 9 + drivers/spi/spi_smartbond.c | 291 ++++++++++++++++++ dts/arm/renesas/smartbond/da1469x.dtsi | 19 ++ dts/bindings/spi/renesas,smartbond-spi.yaml | 20 ++ .../renesas_smartbond/da1469x/pinctrl_soc.h | 4 + 12 files changed, 387 insertions(+) create mode 100644 drivers/spi/Kconfig.smartbond create mode 100644 drivers/spi/spi_smartbond.c create mode 100644 dts/bindings/spi/renesas,smartbond-spi.yaml diff --git a/boards/arm/da1469x_dk_pro/da1469x_dk_pro-pinctrl.dtsi b/boards/arm/da1469x_dk_pro/da1469x_dk_pro-pinctrl.dtsi index f3cb57d7395..7a92cc56f9b 100644 --- a/boards/arm/da1469x_dk_pro/da1469x_dk_pro-pinctrl.dtsi +++ b/boards/arm/da1469x_dk_pro/da1469x_dk_pro-pinctrl.dtsi @@ -31,4 +31,28 @@ bias-pull-up; }; }; + + spi_controller: spi_controller { + group1 { + pinmux = < SMARTBOND_PINMUX(SPI_CLK, 0, 21) >, + ; + output-enable; + }; + group2 { + pinmux = ; + input-enable; + }; + }; + + spi2_controller: spi2_controller { + group1 { + pinmux = < SMARTBOND_PINMUX(SPI2_CLK, 1, 3) >, + ; + output-enable; + }; + group2 { + pinmux = ; + input-enable; + }; + }; }; diff --git a/boards/arm/da1469x_dk_pro/da1469x_dk_pro.dts b/boards/arm/da1469x_dk_pro/da1469x_dk_pro.dts index 18a561624c7..fc84a64195f 100644 --- a/boards/arm/da1469x_dk_pro/da1469x_dk_pro.dts +++ b/boards/arm/da1469x_dk_pro/da1469x_dk_pro.dts @@ -149,3 +149,15 @@ pinctrl-0 = <&i2c2_default>; pinctrl-names = "default"; }; + +&spi { + status = "okay"; + pinctrl-0 = <&spi_controller>; + pinctrl-names = "default"; +}; + +&spi2 { + status = "okay"; + pinctrl-0 = <&spi2_controller>; + pinctrl-names = "default"; +}; diff --git a/boards/arm/da1469x_dk_pro/da1469x_dk_pro.yaml b/boards/arm/da1469x_dk_pro/da1469x_dk_pro.yaml index 2c02084deb7..8cb5c85eab0 100644 --- a/boards/arm/da1469x_dk_pro/da1469x_dk_pro.yaml +++ b/boards/arm/da1469x_dk_pro/da1469x_dk_pro.yaml @@ -12,3 +12,4 @@ supported: - gpio - watchdog - i2c + - spi diff --git a/boards/arm/da1469x_dk_pro/doc/index.rst b/boards/arm/da1469x_dk_pro/doc/index.rst index a29bab16df9..f45c1f333fe 100644 --- a/boards/arm/da1469x_dk_pro/doc/index.rst +++ b/boards/arm/da1469x_dk_pro/doc/index.rst @@ -45,6 +45,8 @@ hardware features: +-----------+------------+----------------------+ | UART | on-chip | serial | +-----------+------------+----------------------+ +| SPI | on-chip | spi | ++-----------+------------+----------------------+ Other hardware features, including the Configurable MAC (CMAC) controller, are currently not supported by the port. diff --git a/drivers/pinctrl/pinctrl_smartbond.c b/drivers/pinctrl/pinctrl_smartbond.c index 180b46fd69f..dc314af13c2 100644 --- a/drivers/pinctrl/pinctrl_smartbond.c +++ b/drivers/pinctrl/pinctrl_smartbond.c @@ -38,6 +38,8 @@ static void pinctrl_configure_pin(const pinctrl_soc_pin_t *pin) reg_val |= 0x01 << GPIO_P0_00_MODE_REG_PUPD_Pos; } else if (pin->bias_pull_down) { reg_val |= 0x02 << GPIO_P0_00_MODE_REG_PUPD_Pos; + } else if (pin->output_enable) { + reg_val |= 0x03 << GPIO_P0_00_MODE_REG_PUPD_Pos; } *reg = reg_val; diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index b889c0213d5..ff67d15547e 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -35,6 +35,7 @@ zephyr_library_sources_ifdef(CONFIG_SPI_ANDES_ATCSPI200 spi_andes_atcspi200.c) zephyr_library_sources_ifdef(CONFIG_NXP_S32_SPI spi_nxp_s32.c) zephyr_library_sources_ifdef(CONFIG_SPI_XMC4XXX spi_xmc4xxx.c) zephyr_library_sources_ifdef(CONFIG_SPI_PW spi_pw.c) +zephyr_library_sources_ifdef(CONFIG_SPI_SMARTBOND spi_smartbond.c) zephyr_library_sources_ifdef(CONFIG_SPI_RTIO spi_rtio.c) zephyr_library_sources_ifdef(CONFIG_SPI_ASYNC spi_signal.c) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index aea11e27c55..749b286a991 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -119,4 +119,6 @@ source "drivers/spi/Kconfig.xmc4xxx" source "drivers/spi/Kconfig.pw" +source "drivers/spi/Kconfig.smartbond" + endif # SPI diff --git a/drivers/spi/Kconfig.smartbond b/drivers/spi/Kconfig.smartbond new file mode 100644 index 00000000000..9c003eb915d --- /dev/null +++ b/drivers/spi/Kconfig.smartbond @@ -0,0 +1,9 @@ +# Copyright (c) 2022 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config SPI_SMARTBOND + bool "Renesas SmartBond(tm) SPI driver" + default y + depends on DT_HAS_RENESAS_SMARTBOND_SPI_ENABLED + help + Enables SPI driver for Renesas SmartBond(tm) DA1469x series MCU. diff --git a/drivers/spi/spi_smartbond.c b/drivers/spi/spi_smartbond.c new file mode 100644 index 00000000000..7f44c0f29aa --- /dev/null +++ b/drivers/spi/spi_smartbond.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2022 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_smartbond_spi + +#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL +#include +LOG_MODULE_REGISTER(spi_smartbond); + +#include "spi_context.h" + +#include +#include +#include + +#include + +#define DIVN_CLK 32000000 /* divN_clk 32MHz */ +#define SCLK_FREQ_2MHZ (DIVN_CLK / 14) /* 2.285714MHz*/ +#define SCLK_FREQ_4MHZ (DIVN_CLK / 8) /* 4MHz */ +#define SCLK_FREQ_8MHZ (DIVN_CLK / 4) /* 8MHz */ +#define SCLK_FREQ_16MHZ (DIVN_CLK / 2) /* 16MHz */ + +struct spi_smartbond_cfg { + SPI_Type *regs; + int periph_clock_config; + const struct pinctrl_dev_config *pcfg; +}; + +struct spi_smartbond_data { + struct spi_context ctx; + uint8_t dfs; +}; + +static inline void spi_smartbond_enable(const struct spi_smartbond_cfg *cfg, bool enable) +{ + if (enable) { + cfg->regs->SPI_CTRL_REG |= SPI_SPI_CTRL_REG_SPI_ON_Msk; + cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_RST_Msk; + } else { + cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_ON_Msk; + cfg->regs->SPI_CTRL_REG |= SPI_SPI_CTRL_REG_SPI_RST_Msk; + } +} + +static inline bool spi_smartbond_isenabled(const struct spi_smartbond_cfg *cfg) +{ + return (!!(cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_ON_Msk)) && + (!(cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_RST_Msk)); +} + +static inline int spi_smartbond_set_speed(const struct spi_smartbond_cfg *cfg, + const uint32_t frequency) +{ + if (frequency < SCLK_FREQ_2MHZ) { + LOG_ERR("Frequency is lower than minimal SCLK %d", SCLK_FREQ_2MHZ); + return -ENOTSUP; + } else if (frequency < SCLK_FREQ_4MHZ) { + cfg->regs->SPI_CTRL_REG = + (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk) | + 3UL << SPI_SPI_CTRL_REG_SPI_CLK_Pos; + } else if (frequency < SCLK_FREQ_8MHZ) { + cfg->regs->SPI_CTRL_REG = (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk); + } else if (frequency < SCLK_FREQ_16MHZ) { + cfg->regs->SPI_CTRL_REG = + (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk) | + 1UL << SPI_SPI_CTRL_REG_SPI_CLK_Pos; + } else { + cfg->regs->SPI_CTRL_REG = + (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_CLK_Msk) | + 2UL << SPI_SPI_CTRL_REG_SPI_CLK_Pos; + } + return 0; +} + +static inline int spi_smartbond_set_word_size(const struct spi_smartbond_cfg *cfg, + struct spi_smartbond_data *data, + const uint32_t operation) +{ + switch (SPI_WORD_SIZE_GET(operation)) { + case 8: + data->dfs = 1; + cfg->regs->SPI_CTRL_REG = + (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_WORD_Msk); + break; + case 16: + data->dfs = 2; + cfg->regs->SPI_CTRL_REG = + (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_WORD_Msk) | + (1UL << SPI_SPI_CTRL_REG_SPI_WORD_Pos); + break; + case 32: + data->dfs = 4; + cfg->regs->SPI_CTRL_REG = + (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_WORD_Msk) | + (2UL << SPI_SPI_CTRL_REG_SPI_WORD_Pos); + break; + default: + LOG_ERR("Word size not supported"); + return -ENOTSUP; + } + + return 0; +} + +static int spi_smartbond_configure(const struct spi_smartbond_cfg *cfg, + struct spi_smartbond_data *data, + const struct spi_config *spi_cfg) +{ + int rc; + + if (spi_context_configured(&data->ctx, spi_cfg)) { + return 0; + } + + if (spi_cfg->operation & SPI_OP_MODE_SLAVE) { + LOG_ERR("Slave mode not yet supported"); + return -ENOTSUP; + } + + if (spi_cfg->operation & SPI_HALF_DUPLEX) { + LOG_ERR("Half-duplex not supported"); + return -ENOTSUP; + } + + if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) && + (spi_cfg->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) { + LOG_ERR("Only single line mode is supported"); + return -ENOTSUP; + } + + if (spi_cfg->operation & SPI_MODE_LOOP) { + LOG_ERR("Loopback mode is not supported"); + return -ENOTSUP; + } + + if (spi_smartbond_isenabled(cfg)) { + spi_smartbond_enable(cfg, false); + } + + rc = spi_smartbond_set_speed(cfg, spi_cfg->frequency); + if (rc) { + return rc; + } + + cfg->regs->SPI_CTRL_REG = + (spi_cfg->operation & SPI_MODE_CPOL) + ? (cfg->regs->SPI_CTRL_REG | SPI_SPI_CTRL_REG_SPI_POL_Msk) + : (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_POL_Msk); + + cfg->regs->SPI_CTRL_REG = + (spi_cfg->operation & SPI_MODE_CPHA) + ? (cfg->regs->SPI_CTRL_REG | SPI_SPI_CTRL_REG_SPI_PHA_Msk) + : (cfg->regs->SPI_CTRL_REG & ~SPI_SPI_CTRL_REG_SPI_PHA_Msk); + + rc = spi_smartbond_set_word_size(cfg, data, spi_cfg->operation); + if (rc) { + return rc; + } + + cfg->regs->SPI_CTRL_REG &= ~(SPI_SPI_CTRL_REG_SPI_FIFO_MODE_Msk); + + spi_smartbond_enable(cfg, true); + + cfg->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_MINT_Msk; + + data->ctx.config = spi_cfg; + + return 0; +} + +static int spi_smartbond_transceive(const struct device *dev, const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs) +{ + const struct spi_smartbond_cfg *cfg = dev->config; + struct spi_smartbond_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + uint32_t bitmask; + int rc; + + spi_context_lock(&data->ctx, false, NULL, NULL, spi_cfg); + rc = spi_smartbond_configure(cfg, data, spi_cfg); + if (rc == 0) { + spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, data->dfs); + spi_context_cs_control(ctx, true); + + bitmask = ~((~0UL) << SPI_WORD_SIZE_GET(data->ctx.config->operation)); + while (spi_context_tx_buf_on(ctx) || spi_context_rx_buf_on(ctx)) { + if (spi_context_tx_buf_on(ctx)) { + cfg->regs->SPI_RX_TX_REG = (*(uint32_t *)ctx->tx_buf) & bitmask; + spi_context_update_tx(ctx, data->dfs, 1); + } else { + cfg->regs->SPI_RX_TX_REG = 0UL; + } + + while (!(cfg->regs->SPI_CTRL_REG & SPI_SPI_CTRL_REG_SPI_INT_BIT_Msk)) { + }; + if (spi_context_rx_buf_on(ctx)) { + (*(uint32_t *)ctx->rx_buf) = cfg->regs->SPI_RX_TX_REG & bitmask; + spi_context_update_rx(ctx, data->dfs, 1); + } else { + (void)cfg->regs->SPI_RX_TX_REG; + } + cfg->regs->SPI_CLEAR_INT_REG = 1UL; + } + } + spi_context_cs_control(ctx, false); + spi_context_release(&data->ctx, rc); + + return rc; +} +#ifdef CONFIG_SPI_ASYNC +static int spi_smartbond_transceive_async(const struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs, spi_callback_t cb, + void *userdata) +{ + return -ENOTSUP; +} +#endif + +static int spi_smartbond_release(const struct device *dev, const struct spi_config *spi_cfg) +{ + struct spi_smartbond_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + + if (!spi_context_configured(ctx, spi_cfg)) { + LOG_ERR("SPI configuration was not the last one to be used"); + return -EINVAL; + } + + spi_context_unlock_unconditionally(ctx); + + return 0; +} + +static const struct spi_driver_api spi_smartbond_driver_api = { + .transceive = spi_smartbond_transceive, +#ifdef CONFIG_SPI_ASYNC + .transceive_async = spi_smartbond_transceive_async, +#endif + .release = spi_smartbond_release, +}; + +static int spi_smartbond_init(const struct device *dev) +{ + const struct spi_smartbond_cfg *cfg = dev->config; + struct spi_smartbond_data *data = dev->data; + int rc; + + CRG_COM->RESET_CLK_COM_REG = cfg->periph_clock_config << 1; + CRG_COM->SET_CLK_COM_REG = cfg->periph_clock_config; + + rc = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (rc < 0) { + LOG_ERR("Failed to configure SPI pins"); + return rc; + } + + rc = spi_context_cs_configure_all(&data->ctx); + if (rc < 0) { + LOG_ERR("Failed to configure CS pins: %d", rc); + return rc; + } + + spi_context_unlock_unconditionally(&data->ctx); + + return 0; +} + +#define SPI_SMARTBOND_DEVICE(id) \ + PINCTRL_DT_INST_DEFINE(id); \ + static const struct spi_smartbond_cfg spi_smartbond_##id##_cfg = { \ + .regs = (SPI_Type *)DT_INST_REG_ADDR(id), \ + .periph_clock_config = DT_INST_PROP(id, periph_clock_config), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \ + }; \ + static struct spi_smartbond_data spi_smartbond_##id##_data = { \ + SPI_CONTEXT_INIT_LOCK(spi_smartbond_##id##_data, ctx), \ + SPI_CONTEXT_INIT_SYNC(spi_smartbond_##id##_data, ctx), \ + SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(id), ctx)}; \ + DEVICE_DT_INST_DEFINE(id, spi_smartbond_init, NULL, &spi_smartbond_##id##_data, \ + &spi_smartbond_##id##_cfg, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ + &spi_smartbond_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(SPI_SMARTBOND_DEVICE) diff --git a/dts/arm/renesas/smartbond/da1469x.dtsi b/dts/arm/renesas/smartbond/da1469x.dtsi index ed44a2f4f92..72b12efba60 100644 --- a/dts/arm/renesas/smartbond/da1469x.dtsi +++ b/dts/arm/renesas/smartbond/da1469x.dtsi @@ -178,6 +178,25 @@ reg = <0x50020700 0x100>; periph-clock-config = <0x0800>; interrupts = <9 0>; + }; + + spi: spi@50020300 { + compatible = "renesas,smartbond-spi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x50020300 0x100>; + periph-clock-config = <0x20>; + interrupts = <10 0>; + status = "disabled"; + }; + + spi2: spi@50020400 { + compatible = "renesas,smartbond-spi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x50020400 0x100>; + periph-clock-config = <0x80>; + interrupts = <11 0>; status = "disabled"; }; }; diff --git a/dts/bindings/spi/renesas,smartbond-spi.yaml b/dts/bindings/spi/renesas,smartbond-spi.yaml new file mode 100644 index 00000000000..bd2b29f1862 --- /dev/null +++ b/dts/bindings/spi/renesas,smartbond-spi.yaml @@ -0,0 +1,20 @@ +# Copyright (c) 2022 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas SmartBond(tm) SPI + +compatible: "renesas,smartbond-spi" + +include: [spi-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + periph-clock-config: + type: int + description: Peripheral clock register configuration (COM domain) + required: true diff --git a/soc/arm/renesas_smartbond/da1469x/pinctrl_soc.h b/soc/arm/renesas_smartbond/da1469x/pinctrl_soc.h index 8e91f07a6b6..9a726d70db4 100644 --- a/soc/arm/renesas_smartbond/da1469x/pinctrl_soc.h +++ b/soc/arm/renesas_smartbond/da1469x/pinctrl_soc.h @@ -15,6 +15,8 @@ struct smartbond_pinctrl_soc_pin { uint32_t pin : 5; uint32_t bias_pull_up : 1; uint32_t bias_pull_down : 1; + uint32_t output_enable : 1; + uint32_t input_enable : 1; }; typedef struct smartbond_pinctrl_soc_pin pinctrl_soc_pin_t; @@ -26,6 +28,8 @@ typedef struct smartbond_pinctrl_soc_pin pinctrl_soc_pin_t; SMARTBOND_GET_PIN(DT_PROP_BY_IDX(node_id, prop, idx)), \ DT_PROP(node_id, bias_pull_up), \ DT_PROP(node_id, bias_pull_down), \ + DT_PROP(node_id, output_enable), \ + DT_PROP(node_id, input_enable), \ }, #define Z_PINCTRL_STATE_PINS_INIT(node_id, prop) \