diff --git a/boards/arm/gardena_rm_sim3u/CMakeLists.txt b/boards/arm/gardena_rm_sim3u/CMakeLists.txt new file mode 100644 index 00000000000000..568900d0cc5b07 --- /dev/null +++ b/boards/arm/gardena_rm_sim3u/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(board.c) diff --git a/boards/arm/gardena_rm_sim3u/Kconfig.board b/boards/arm/gardena_rm_sim3u/Kconfig.board new file mode 100644 index 00000000000000..49e2767e55fe1b --- /dev/null +++ b/boards/arm/gardena_rm_sim3u/Kconfig.board @@ -0,0 +1,8 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_GARDENA_RM_SIM3U + bool "GARDENA SIM3U radio module" + depends on SOC_SERIES_SIM3U + select SOC_PART_NUMBER_SIM3U167_B_GM diff --git a/boards/arm/gardena_rm_sim3u/Kconfig.defconfig b/boards/arm/gardena_rm_sim3u/Kconfig.defconfig new file mode 100644 index 00000000000000..1cd1dcdabe6ee8 --- /dev/null +++ b/boards/arm/gardena_rm_sim3u/Kconfig.defconfig @@ -0,0 +1,10 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_GARDENA_RM_SIM3U + +config BOARD + default "gardena_rm_sim3u" + +endif # BOARD_GARDENA_RM_SIM3U diff --git a/boards/arm/gardena_rm_sim3u/board.c b/boards/arm/gardena_rm_sim3u/board.c new file mode 100644 index 00000000000000..f7979571eede77 --- /dev/null +++ b/boards/arm/gardena_rm_sim3u/board.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +static const struct gpio_dt_spec diversity_mode_gpio = + GPIO_DT_SPEC_GET(DT_NODELABEL(gardena_antenna), diversity_mode_gpios); +static const struct gpio_dt_spec diversity_state_gpio = + GPIO_DT_SPEC_GET(DT_NODELABEL(gardena_antenna), diversity_state_gpios); +static const struct gpio_dt_spec antenna_control_gpio = + GPIO_DT_SPEC_GET(DT_NODELABEL(gardena_antenna), antenna_control_gpios); +static const struct gpio_dt_spec antenna_input_gpio = + GPIO_DT_SPEC_GET(DT_NODELABEL(gardena_antenna), antenna_input_gpios); + +struct led_mirror { + struct gpio_dt_spec source; + struct gpio_dt_spec destination; + struct gpio_callback callback; +}; + +static struct led_mirror led_mirrors[] = { + { + .source = GPIO_DT_SPEC_GET(DT_NODELABEL(gardena_antenna), led_red_input_gpios), + .destination = GPIO_DT_SPEC_GET(DT_NODELABEL(led_red), gpios), + }, + { + .source = GPIO_DT_SPEC_GET(DT_NODELABEL(gardena_antenna), led_green_input_gpios), + .destination = GPIO_DT_SPEC_GET(DT_NODELABEL(led_green), gpios), + }, + { + .source = GPIO_DT_SPEC_GET(DT_NODELABEL(gardena_antenna), led_blue_input_gpios), + .destination = GPIO_DT_SPEC_GET(DT_NODELABEL(led_blue), gpios), + }, +}; + +static void led_mirror_callback_handler(const struct device *port, struct gpio_callback *cb, + gpio_port_pins_t pins) +{ + ARG_UNUSED(port); + ARG_UNUSED(pins); + + struct led_mirror *led_mirror = CONTAINER_OF(cb, struct led_mirror, callback); + + gpio_pin_set_dt(&led_mirror->destination, gpio_pin_get_dt(&led_mirror->source)); +} + +static int board_init(void) +{ + if (!device_is_ready(diversity_mode_gpio.port)) { + return -ENODEV; + } + if (!device_is_ready(diversity_state_gpio.port)) { + return -ENODEV; + } + if (!device_is_ready(antenna_control_gpio.port)) { + return -ENODEV; + } + if (!device_is_ready(antenna_input_gpio.port)) { + return -ENODEV; + } + + /* inactive: manual, active: auto */ + gpio_pin_configure_dt(&diversity_mode_gpio, GPIO_OUTPUT_INACTIVE); + + /* In manual mode: diversity pin state */ + gpio_pin_configure_dt(&diversity_state_gpio, GPIO_OUTPUT_INACTIVE); + + /* inactive: external, active: internal */ + gpio_pin_configure_dt(&antenna_control_gpio, GPIO_OUTPUT_ACTIVE); + + /* Main MCU requests specific antenna. Ignored for now + * inactive: external, active: internal + */ + gpio_pin_configure_dt(&antenna_input_gpio, GPIO_INPUT); + + for (size_t index = 0; index < ARRAY_SIZE(led_mirrors); index += 1) { + struct led_mirror *led_mirror = &led_mirrors[index]; + + if (!device_is_ready(led_mirror->source.port)) { + continue; + } + if (!device_is_ready(led_mirror->destination.port)) { + continue; + } + + gpio_pin_configure_dt(&led_mirror->source, GPIO_INPUT); + gpio_pin_configure_dt(&led_mirror->destination, GPIO_OUTPUT); + + gpio_init_callback(&led_mirror->callback, led_mirror_callback_handler, + BIT(led_mirror->source.pin)); + gpio_add_callback(led_mirror->source.port, &led_mirror->callback); + + gpio_pin_interrupt_configure_dt(&led_mirror->source, GPIO_INT_EDGE_BOTH); + + /* Initially apply mirror just in case */ + led_mirror_callback_handler(NULL, &led_mirror->callback, 0); + } + + return 0; +} + +SYS_INIT(board_init, PRE_KERNEL_2, 0); diff --git a/boards/arm/gardena_rm_sim3u/board.cmake b/boards/arm/gardena_rm_sim3u/board.cmake new file mode 100644 index 00000000000000..6ca708e7b99e29 --- /dev/null +++ b/boards/arm/gardena_rm_sim3u/board.cmake @@ -0,0 +1,5 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake) diff --git a/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u-pinctrl.dtsi b/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u-pinctrl.dtsi new file mode 100644 index 00000000000000..90c39b82a4c290 --- /dev/null +++ b/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u-pinctrl.dtsi @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +&pinctrl { + usart0_default: usart0_default { + group1 { + pinmux = , + ; + output-enable; + }; + group2 { + pinmux = , + ; + input-enable; + }; + }; + + spi2_default: spi2_default { + group1 { + pinmux = , + , + ; + output-enable; + }; + + group2 { + pinmux = ; + input-enable; + }; + }; +}; + diff --git a/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u.dts b/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u.dts new file mode 100644 index 00000000000000..e0d1935644f094 --- /dev/null +++ b/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u.dts @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include +#include "gardena_rm_sim3u-pinctrl.dtsi" + +/ { + model = "GARDENA sim3u radio module"; + compatible = "gardena,rm_sim3u", "silabs,sim3u"; + + chosen { + zephyr,console = &usart0; + zephyr,shell-uart = &usart0; + zephyr,sram = &sram0; + zephyr,flash = &flash0; + }; + + leds { + compatible = "gpio-leds"; + status = "okay"; + + led_red: led_red { + gpios = <&gpio4 0 (GPIO_ACTIVE_LOW | SI32_GPIO_DS_ENABLE_CURRENT_LIMIT)>; + label = "Red LED 0"; + }; + led_green: led_green { + gpios = <&gpio4 1 (GPIO_ACTIVE_LOW | SI32_GPIO_DS_ENABLE_CURRENT_LIMIT)>; + label = "Green LED 0"; + }; + led_blue: led_blue { + gpios = <&gpio4 2 (GPIO_ACTIVE_LOW | SI32_GPIO_DS_ENABLE_CURRENT_LIMIT)>; + label = "Blue LED 0"; + }; + }; + + gardena_antenna: gardena_antenna { + compatible = "gardena,antenna"; + diversity-mode-gpios = <&gpio2 14 GPIO_ACTIVE_HIGH>; + diversity-state-gpios = <&gpio2 10 GPIO_ACTIVE_HIGH>; + antenna-control-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; + antenna-input-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>; + + led-red-input-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>; + led-green-input-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>; + led-blue-input-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>; + }; +}; + +&cpu0 { + clock-frequency = <76953600>; +}; + +&pll0 { + status = "okay"; +}; + +&clk_ahb { + clocks = <&pll0>; + status = "okay"; +}; + +&clk_apb { + divider = <2>; + status = "okay"; +}; + +&usart0 { + current-speed = <115200>; + pinctrl-0 = <&usart0_default>; + pinctrl-names = "default"; + hw-flow-control; + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + disable-pullups; + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpio4 { + nchannel-current-limit = <7>; + pchannel-current-limit = <15>; + enable-bias; + low-power-port-mode; + enable-drivers; + status = "okay"; +}; + +&spi2 { + pinctrl-0 = <&spi2_default>; + pinctrl-names = "default"; + status = "ok"; + + si4467@0 { + compatible = "silabs,si4467"; + spi-max-frequency = <2000000>; + xtal-frequency = <26000000>; + reg = <0>; + gpio-0-gpios = <&gpio2 4 GPIO_ACTIVE_HIGH>; + gpio-1-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>; + gpio-2-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>; + gpio-3-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>; + + sdn-gpios = <&gpio3 4 GPIO_ACTIVE_HIGH>; + nirq-gpios = <&gpio3 5 GPIO_ACTIVE_LOW>; + + rx-enable-gpios = <&gpio4 4 (GPIO_ACTIVE_HIGH | SI32_GPIO_DS_ENABLE_CURRENT_LIMIT)>; + tx-enable-gpios = <&gpio4 5 (GPIO_ACTIVE_HIGH | SI32_GPIO_DS_ENABLE_CURRENT_LIMIT)>; + }; + +}; + +&flash0 { + status = "okay"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + slot0_partition: partition@0 { + label = "image-0"; + reg = <0x00000000 DT_SIZE_K(1664)>; + }; + + storage_partition: partition@250000 { + label = "storage"; + reg = <0x00030000 DT_SIZE_K(64)>; + }; + }; +}; diff --git a/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u.yaml b/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u.yaml new file mode 100644 index 00000000000000..3ab6173bbe0f89 --- /dev/null +++ b/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +identifier: gardena_rm_sim3u +name: GARDENA sim3u radio module +type: mcu +arch: arm +ram: 32 +flash: 256 +toolchain: + - zephyr + - gnuarmemb + - xtools +supported: + - gpio diff --git a/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u_defconfig b/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u_defconfig new file mode 100644 index 00000000000000..36094989ef1cf8 --- /dev/null +++ b/boards/arm/gardena_rm_sim3u/gardena_rm_sim3u_defconfig @@ -0,0 +1,13 @@ +CONFIG_SOC_SERIES_SIM3U=y + +# enable uart driver +CONFIG_SERIAL=y + +# enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +# enable GPIO +CONFIG_GPIO=y + +CONFIG_SPI=y diff --git a/boards/arm/gardena_rm_sim3u/support/openocd.cfg b/boards/arm/gardena_rm_sim3u/support/openocd.cfg new file mode 100644 index 00000000000000..148c80ce9ab29b --- /dev/null +++ b/boards/arm/gardena_rm_sim3u/support/openocd.cfg @@ -0,0 +1,21 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg] +source [find interface/ftdi/olimex-arm-jtag-swd.cfg] + +source [find target/sim3x.cfg] + +reset_config none separate + +$_TARGETNAME configure -event gdb-attach { + echo "Debugger attaching: halting execution" + reset halt + gdb_breakpoint_override hard +} + +$_TARGETNAME configure -event gdb-detach { + echo "Debugger detaching: resuming execution" + resume +} diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index d75498f5363e82..25dd609a5dad2f 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -22,6 +22,9 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION nrf_clo zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_RV32M1_PCC clock_control_rv32m1_pcc.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_INFINEON_CAT1 clock_control_ifx_cat1.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SAM clock_control_sam_pmc.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SI32_PLL clock_control_si32_pll.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SI32_AHB clock_control_si32_ahb.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SI32_APB clock_control_si32_apb.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SMARTBOND clock_control_smartbond.c) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index c21af00cdb4ac9..ec05ba65c2a5eb 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -70,6 +70,8 @@ source "drivers/clock_control/Kconfig.gd32" source "drivers/clock_control/Kconfig.sam" +source "drivers/clock_control/Kconfig.si32" + source "drivers/clock_control/Kconfig.smartbond" endif # CLOCK_CONTROL diff --git a/drivers/clock_control/Kconfig.si32 b/drivers/clock_control/Kconfig.si32 new file mode 100644 index 00000000000000..c7ebc8250e6a40 --- /dev/null +++ b/drivers/clock_control/Kconfig.si32 @@ -0,0 +1,18 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_CONTROL_SI32_PLL + bool "SI32 PLL clock control" + default y + depends on DT_HAS_SILABS_SI32_PLL_ENABLED + +config CLOCK_CONTROL_SI32_AHB + bool "SI32 AHB clock control" + default y + depends on DT_HAS_SILABS_SI32_AHB_ENABLED + +config CLOCK_CONTROL_SI32_APB + bool "SI32 APB clock control" + default y + depends on DT_HAS_SILABS_SI32_APB_ENABLED diff --git a/drivers/clock_control/clock_control_si32_ahb.c b/drivers/clock_control/clock_control_si32_ahb.c new file mode 100644 index 00000000000000..81387055e4a5bd --- /dev/null +++ b/drivers/clock_control/clock_control_si32_ahb.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_si32_ahb + +#include + +#include +#include +#include + +#include +#include +#include + +#define LOG_LEVEL LOG_LEVEL_DBG +#include +LOG_MODULE_REGISTER(ahb); + +struct clock_control_si32_ahb_config { + const struct device *clock_dev; + uint32_t freq; +}; + +static int clock_control_si32_ahb_on(const struct device *dev, clock_control_subsys_t sys) +{ + return -ENOTSUP; +} + +static int clock_control_si32_ahb_off(const struct device *dev, clock_control_subsys_t sys) +{ + + return -ENOTSUP; +} + +static int clock_control_si32_ahb_get_rate(const struct device *dev, clock_control_subsys_t sys, + uint32_t *rate) +{ + const struct clock_control_si32_ahb_config *config = dev->config; + *rate = config->freq; + return 0; +} + +static struct clock_control_driver_api clock_control_si32_ahb_api = { + .on = clock_control_si32_ahb_on, + .off = clock_control_si32_ahb_off, + .get_rate = clock_control_si32_ahb_get_rate, +}; + +static int clock_control_si32_ahb_init(const struct device *dev) +{ + const struct clock_control_si32_ahb_config *config = dev->config; + int ret; + + if (!device_is_ready(config->clock_dev)) { + return -ENODEV; + } + + if (config->freq != 20000000) { + uint32_t freq = config->freq; + ret = clock_control_set_rate(config->clock_dev, NULL, &freq); + if (ret) { + LOG_ERR("failed to set parent clock rate: %d", ret); + return ret; + } + + ret = clock_control_on(config->clock_dev, NULL); + if (ret) { + LOG_ERR("failed to enable parent clock: %d", ret); + return ret; + } + + uint32_t spmd; + if (config->freq > 80000000) { + spmd = 3; + } else if (config->freq > 53000000) { + spmd = 2; + } else if (config->freq > 26000000) { + spmd = 1; + } else { + spmd = 0; + } + SI32_FLASHCTRL_A_select_flash_speed_mode(SI32_FLASHCTRL_0, spmd); + + /* TODO: support other clock sources */ + SI32_CLKCTRL_A_select_ahb_source_pll(SI32_CLKCTRL_0); + } + + return 0; +} + +static const struct clock_control_si32_ahb_config config = { + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)), + .freq = DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency), +}; + +DEVICE_DT_INST_DEFINE(0, clock_control_si32_ahb_init, NULL, NULL, &config, PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_si32_ahb_api); diff --git a/drivers/clock_control/clock_control_si32_apb.c b/drivers/clock_control/clock_control_si32_apb.c new file mode 100644 index 00000000000000..5ae8b568325fef --- /dev/null +++ b/drivers/clock_control/clock_control_si32_apb.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_si32_apb + +#include + +#include +#include +#include + +#include +#include + +struct clock_control_si32_apb_config { + const struct device *clock_dev; + uint32_t divider; +}; + +static int clock_control_si32_apb_on(const struct device *dev, clock_control_subsys_t sys) +{ + return -ENOTSUP; +} + +static int clock_control_si32_apb_off(const struct device *dev, clock_control_subsys_t sys) +{ + + return -ENOTSUP; +} + +static int clock_control_si32_apb_get_rate(const struct device *dev, clock_control_subsys_t sys, + uint32_t *rate) +{ + const struct clock_control_si32_apb_config *config = dev->config; + + int ret = clock_control_get_rate(config->clock_dev, NULL, rate); + if (ret) { + return ret; + } + + *rate /= config->divider; + + return 0; +} + +static struct clock_control_driver_api clock_control_si32_apb_api = { + .on = clock_control_si32_apb_on, + .off = clock_control_si32_apb_off, + .get_rate = clock_control_si32_apb_get_rate, +}; + +static int clock_control_si32_apb_init(const struct device *dev) +{ + const struct clock_control_si32_apb_config *config = dev->config; + + if (!device_is_ready(config->clock_dev)) { + return -ENODEV; + } + + if (config->divider == 1) { + SI32_CLKCTRL_A_select_apb_divider_1(SI32_CLKCTRL_0); + } else if (config->divider == 2) { + SI32_CLKCTRL_A_select_apb_divider_2(SI32_CLKCTRL_0); + } else { + return -ENOTSUP; + } + + return 0; +} + +static const struct clock_control_si32_apb_config config = { + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)), + .divider = DT_PROP(DT_NODELABEL(clk_apb), divider), +}; + +DEVICE_DT_INST_DEFINE(0, clock_control_si32_apb_init, NULL, NULL, &config, PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_si32_apb_api); diff --git a/drivers/clock_control/clock_control_si32_pll.c b/drivers/clock_control/clock_control_si32_pll.c new file mode 100644 index 00000000000000..9d6d1f031ad24e --- /dev/null +++ b/drivers/clock_control/clock_control_si32_pll.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_si32_pll + +#include + +#include +#include +#include + +#include +#include +#include + +#define LOG_LEVEL LOG_LEVEL_DBG +#include +LOG_MODULE_REGISTER(pll); + +struct clock_control_si32_pll_config { + SI32_PLL_A_Type *pll; +}; + +struct clock_control_si32_pll_data { + uint32_t freq; +}; + +static int clock_control_si32_pll_on(const struct device *dev, clock_control_subsys_t sys) +{ + const struct clock_control_si32_pll_config *config = dev->config; + struct clock_control_si32_pll_data *data = dev->data; + + if (data->freq == 0) { + return -ENOTSUP; + } + + uint32_t dco_range; + if (data->freq > 80000000) { + dco_range = 4; + } else if (data->freq > 76500000) { + dco_range = 4; + } else if (data->freq > 62000000) { + dco_range = 3; + } else if (data->freq > 49500000) { + dco_range = 2; + } else if (data->freq > 35000000) { + dco_range = 1; + } else if (data->freq > 23000000) { + dco_range = 0; + } else { + return -ENOTSUP; + } + + /* lposc0div */ + uint32_t source_clock_freq = 2500000; + + uint32_t div_m = 100; + uint32_t div_n = (data->freq / source_clock_freq) * (div_m + 1) - 1; + if (div_n < 32 || div_n > 4095) { + return -ENOTSUP; + } + + /* Setup PLL to lock to requested frequency */ + SI32_PLL_A_initialize(config->pll, 0x00, 0x00, 0x00, 0x000FFF0); + SI32_PLL_A_set_numerator(config->pll, div_n); + SI32_PLL_A_set_denominator(config->pll, div_m); + /* TODO: support other clock sources */ + SI32_PLL_A_select_reference_clock_source_lp0oscdiv(config->pll); + + /* Wait for lock */ + SI32_PLL_A_select_disable_dco_output(config->pll); + SI32_PLL_A_set_frequency_adjuster_value(config->pll, 0xFFF); + SI32_PLL_A_set_output_frequency_range(config->pll, dco_range); + + /* Lock and block for result */ + SI32_PLL_A_select_dco_frequency_lock_mode(config->pll); + while (!(SI32_PLL_A_is_locked(config->pll) || + SI32_PLL_A_is_saturation_low_interrupt_pending(config->pll) || + SI32_PLL_A_is_saturation_high_interrupt_pending(config->pll))) + ; + + return 0; +} + +static int clock_control_si32_pll_off(const struct device *dev, clock_control_subsys_t sys) +{ + + return -ENOTSUP; +} + +static int clock_control_si32_pll_get_rate(const struct device *dev, clock_control_subsys_t sys, + uint32_t *rate) +{ + struct clock_control_si32_pll_data *data = dev->data; + *rate = data->freq; + return 0; +} + +static int clock_control_si32_pll_set_rate(const struct device *dev, clock_control_subsys_t sys, + clock_control_subsys_rate_t rate_) +{ + struct clock_control_si32_pll_data *data = dev->data; + const uint32_t *rate = rate_; + + data->freq = *rate; + + return 0; +} + +static struct clock_control_driver_api clock_control_si32_pll_api = { + .on = clock_control_si32_pll_on, + .off = clock_control_si32_pll_off, + .get_rate = clock_control_si32_pll_get_rate, + .set_rate = clock_control_si32_pll_set_rate, +}; + +static int clock_control_si32_pll_init(const struct device *dev) +{ + SI32_CLKCTRL_A_enable_apb_to_modules_0(SI32_CLKCTRL_0, SI32_CLKCTRL_A_APBCLKG0_PLL0); + + return 0; +} + +static const struct clock_control_si32_pll_config config = { + .pll = (SI32_PLL_A_Type *)DT_REG_ADDR(DT_NODELABEL(pll0)), +}; + +static struct clock_control_si32_pll_data data = { + .freq = 0, +}; + +DEVICE_DT_INST_DEFINE(0, clock_control_si32_pll_init, NULL, &data, &config, PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_si32_pll_api); diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index a68281b071f683..b1a62f08711e86 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -29,6 +29,7 @@ zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_SMARTBOND flash_smartbond.c) zephyr_library_sources_ifdef(CONFIG_FLASH_CAD_QSPI_NOR flash_cadence_qspi_nor.c flash_cadence_qspi_nor_ll.c) zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_XMC4XXX soc_flash_xmc4xxx.c) zephyr_library_sources_ifdef(CONFIG_FLASH_RPI_PICO flash_rpi_pico.c) +zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_SI32 flash_si32.c) if(CONFIG_FLASH_MCUX_FLEXSPI_XIP) dt_chosen(chosen_flash PROPERTY "zephyr,flash") diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index 84ee3d9769fbbd..a3f5b96358c3fe 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -150,4 +150,6 @@ source "drivers/flash/Kconfig.xmc4xxx" source "drivers/flash/Kconfig.ifx_cat1" +source "drivers/flash/Kconfig.si32" + endif # FLASH diff --git a/drivers/flash/Kconfig.si32 b/drivers/flash/Kconfig.si32 new file mode 100644 index 00000000000000..4edc3482541972 --- /dev/null +++ b/drivers/flash/Kconfig.si32 @@ -0,0 +1,12 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config SOC_FLASH_SI32 + bool "SI32 flash driver" + default y + depends on DT_HAS_SILABS_SI32_FLASH_CONTROLLER_ENABLED + select FLASH_HAS_DRIVER_ENABLED + select FLASH_HAS_PAGE_LAYOUT + help + Enable SI32 flash driver. diff --git a/drivers/flash/flash_si32.c b/drivers/flash/flash_si32.c new file mode 100644 index 00000000000000..f9c4e4a14998ca --- /dev/null +++ b/drivers/flash/flash_si32.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_si32_flash_controller + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL +#include +LOG_MODULE_REGISTER(flash_si32); + +#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash) +#define SOC_NV_FLASH_SIZE DT_REG_SIZE(SOC_NV_FLASH_NODE) +#define SOC_NV_FLASH_ADDR DT_REG_ADDR(SOC_NV_FLASH_NODE) +#define SOC_NV_FLASH_WRITE_BLOCK_SIZE DT_PROP(SOC_NV_FLASH_NODE, write_block_size) +#define SOC_NV_FLASH_ERASE_BLOCK_SIZE DT_PROP(SOC_NV_FLASH_NODE, erase_block_size) +BUILD_ASSERT(SOC_NV_FLASH_WRITE_BLOCK_SIZE == 2, "other values weren't tested yet"); + +struct flash_si32_data { + struct k_sem mutex; +}; + +struct flash_si32_config { + SI32_FLASHCTRL_A_Type *controller; +}; + +static const struct flash_parameters flash_si32_parameters = { + .write_block_size = SOC_NV_FLASH_WRITE_BLOCK_SIZE, + .erase_value = 0xff, +}; + +static bool flash_si32_valid_range(off_t offset, uint32_t size, bool write) +{ + if ((offset > SOC_NV_FLASH_SIZE) || ((offset + size) > SOC_NV_FLASH_SIZE)) { + return false; + } + + if (write) { + if ((offset % SOC_NV_FLASH_WRITE_BLOCK_SIZE) != 0) { + return -EINVAL; + } + + if ((size % SOC_NV_FLASH_WRITE_BLOCK_SIZE) != 0) { + return -EINVAL; + } + } + + return true; +} + +static int flash_si32_read(const struct device *dev, off_t offset, void *data, size_t size) +{ + if (!flash_si32_valid_range(offset, size, false)) { + return -EINVAL; + } + + if (!size) { + return 0; + } + + memcpy(data, (uint8_t *)CONFIG_FLASH_BASE_ADDRESS + offset, size); + + return 0; +} + +static int flash_si32_write(const struct device *dev, off_t offset, const void *data_, size_t size) +{ + const uint8_t *data = data_; + struct flash_si32_data *const dev_data = dev->data; + const struct flash_si32_config *const config = dev->config; + + if (!flash_si32_valid_range(offset, size, true)) { + return -EINVAL; + } + + if (!size) { + return 0; + } + + k_sem_take(&dev_data->mutex, K_FOREVER); + + SI32_FLASHCTRL_A_write_wraddr(config->controller, offset); + SI32_FLASHCTRL_A_enter_multi_byte_write_mode(config->controller); + SI32_FLASHCTRL_A_write_flash_key(config->controller, 0xA5); + SI32_FLASHCTRL_A_write_flash_key(config->controller, 0xF2); + + for (size_t i = 0; i < size; i += SOC_NV_FLASH_WRITE_BLOCK_SIZE) { + uint16_t halfword = (uint16_t)(data[i]) | ((uint16_t)data[i + 1] << 8); + SI32_FLASHCTRL_A_write_wrdata(config->controller, (uint32_t)halfword); + + while (SI32_FLASHCTRL_A_is_flash_busy(config->controller)) { + } + } + + while (SI32_FLASHCTRL_A_is_buffer_full(config->controller)) { + } + + SI32_FLASHCTRL_A_write_flash_key(config->controller, 0x5A); + + k_sem_give(&dev_data->mutex); + + return 0; +} + +static int flash_si32_erase(const struct device *dev, off_t offset, size_t size) +{ + struct flash_si32_data *const dev_data = dev->data; + const struct flash_si32_config *const config = dev->config; + + if (!flash_si32_valid_range(offset, size, false)) { + return -EINVAL; + } + + if ((offset % SOC_NV_FLASH_ERASE_BLOCK_SIZE) != 0) { + LOG_ERR("offset 0x%lx: not on a page boundary", (long)offset); + return -EINVAL; + } + + if ((size % SOC_NV_FLASH_ERASE_BLOCK_SIZE) != 0) { + LOG_ERR("size %zu: not multiple of a page size", size); + return -EINVAL; + } + + if (!size) { + return 0; + } + + k_sem_take(&dev_data->mutex, K_FOREVER); + + SI32_FLASHCTRL_A_enter_flash_erase_mode(config->controller); + SI32_FLASHCTRL_A_write_flash_key(config->controller, 0xA5); + SI32_FLASHCTRL_A_write_flash_key(config->controller, 0xF2); + + for (size_t i = 0; i < size; i += SOC_NV_FLASH_ERASE_BLOCK_SIZE) { + SI32_FLASHCTRL_A_write_wraddr(config->controller, offset + i); + SI32_FLASHCTRL_A_write_wrdata(config->controller, 0); + + while (SI32_FLASHCTRL_A_is_buffer_full(config->controller)) { + } + } + + SI32_FLASHCTRL_A_write_flash_key(config->controller, 0x5A); + SI32_FLASHCTRL_A_exit_flash_erase_mode(config->controller); + + k_sem_give(&dev_data->mutex); + + return 0; +} + +#if CONFIG_FLASH_PAGE_LAYOUT +static const struct flash_pages_layout flash_si32_0_pages_layout = { + .pages_count = SOC_NV_FLASH_SIZE / SOC_NV_FLASH_ERASE_BLOCK_SIZE, + .pages_size = SOC_NV_FLASH_ERASE_BLOCK_SIZE, +}; + +void flash_si32_page_layout(const struct device *dev, const struct flash_pages_layout **layout, + size_t *layout_size) +{ + *layout = &flash_si32_0_pages_layout; + *layout_size = 1; +} +#endif /* CONFIG_FLASH_PAGE_LAYOUT */ + +static const struct flash_parameters *flash_si32_get_parameters(const struct device *dev) +{ + ARG_UNUSED(dev); + + return &flash_si32_parameters; +} + +static int flash_si32_init(const struct device *dev) +{ + struct flash_si32_data *const dev_data = dev->data; + const struct flash_si32_config *const config = dev->config; + + k_sem_init(&dev_data->mutex, 1, 1); + + SI32_FLASHCTRL_A_exit_read_store_mode(config->controller); + + return 0; +} + +static const struct flash_driver_api flash_si32_driver_api = { + .read = flash_si32_read, + .write = flash_si32_write, + .erase = flash_si32_erase, + .get_parameters = flash_si32_get_parameters, +#ifdef CONFIG_FLASH_PAGE_LAYOUT + .page_layout = flash_si32_page_layout, +#endif +}; + +static struct flash_si32_data flash_si32_0_data; + +static const struct flash_si32_config flash_si32_config = { + .controller = (SI32_FLASHCTRL_A_Type *)DT_INST_REG_ADDR(0), +}; + +DEVICE_DT_INST_DEFINE(0, flash_si32_init, NULL, &flash_si32_0_data, &flash_si32_config, POST_KERNEL, + CONFIG_FLASH_INIT_PRIORITY, &flash_si32_driver_api); diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 1237fe7bd74103..0966be2f5b5ea0 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -77,6 +77,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_RT1718S gpio_rt1718s.c) zephyr_library_sources_ifdef(CONFIG_GPIO_RT1718S gpio_rt1718s_port.c) zephyr_library_sources_ifdef(CONFIG_GPIO_NUMICRO gpio_numicro.c) zephyr_library_sources_ifdef(CONFIG_GPIO_HOGS gpio_hogs.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_SI32 gpio_si32.c gpio_si32_hd.c) if(CONFIG_GPIO_SC18IM704) zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b4a2f0bef6758d..05b07e4c5863a3 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -197,4 +197,6 @@ source "drivers/gpio/Kconfig.bd8lb600fs" source "drivers/gpio/Kconfig.sc18im704" +source "drivers/gpio/Kconfig.si32" + endif # GPIO diff --git a/drivers/gpio/Kconfig.si32 b/drivers/gpio/Kconfig.si32 new file mode 100644 index 00000000000000..7215be8d9b4f4d --- /dev/null +++ b/drivers/gpio/Kconfig.si32 @@ -0,0 +1,10 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_SI32 + bool "GPIO Driver for SI32 family of MCUs" + default y + depends on DT_HAS_SILABS_SI32_GPIO_ENABLED + help + Enable GPIO driver for SI32 line of MCUs diff --git a/drivers/gpio/gpio_si32.c b/drivers/gpio/gpio_si32.c new file mode 100644 index 00000000000000..8d88b91e2ed29b --- /dev/null +++ b/drivers/gpio/gpio_si32.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include +#include + +struct gpio_si32_config { + struct gpio_driver_config common; + SI32_PBSTD_A_Type *base; + bool disable_pullups; +}; + +struct gpio_si32_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data common; + sys_slist_t cb; + gpio_port_pins_t trig_low; + gpio_port_pins_t trig_high; + uint32_t pin_values; +}; + +static int gpio_si32_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) +{ + const struct gpio_si32_config *config = dev->config; + uint32_t key = irq_lock(); + + if (flags & GPIO_OUTPUT) { + if (flags & GPIO_OUTPUT_INIT_HIGH) { + SI32_PBSTD_A_write_pins_high(config->base, BIT(pin)); + } else if (flags & GPIO_OUTPUT_INIT_LOW) { + SI32_PBSTD_A_write_pins_low(config->base, BIT(pin)); + } + + SI32_PBSTD_A_set_pins_push_pull_output(config->base, BIT(pin)); + } else if (flags & GPIO_INPUT) { + SI32_PBSTD_A_set_pins_digital_input(config->base, BIT(pin)); + } else { + SI32_PBSTD_A_set_pins_analog(config->base, BIT(pin)); + } + + /* Initially, configure interrupt to trgger on the active value. + * Otherwise we'd get an interrupt immediately after enabling them. + */ + if (flags & GPIO_ACTIVE_HIGH) { + config->base->PM_SET = BIT(pin); + } else { + config->base->PM_CLR = BIT(pin); + } + + irq_unlock(key); + return 0; +} + +static int gpio_si32_port_get_raw(const struct device *dev, uint32_t *value) +{ + const struct gpio_si32_config *config = dev->config; + *value = SI32_PBSTD_A_read_pins(config->base); + return 0; +} + +static int gpio_si32_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, + gpio_port_value_t value) +{ + const struct gpio_si32_config *config = dev->config; + SI32_PBSTD_A_write_pins_masked(config->base, value, mask); + return 0; +} + +static int gpio_si32_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) +{ + const struct gpio_si32_config *config = dev->config; + SI32_PBSTD_A_write_pins_high(config->base, pins); + return 0; +} + +static int gpio_si32_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) +{ + const struct gpio_si32_config *config = dev->config; + SI32_PBSTD_A_write_pins_low(config->base, pins); + return 0; +} + +static int gpio_si32_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) +{ + const struct gpio_si32_config *config = dev->config; + SI32_PBSTD_A_toggle_pins(config->base, pins); + return 0; +} + +static int gpio_si32_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, + enum gpio_int_mode mode, enum gpio_int_trig trig) +{ + const struct gpio_si32_config *config = dev->config; + struct gpio_si32_data *data = dev->data; + int ret; + + uint32_t key = irq_lock(); + + if (mode == GPIO_INT_MODE_DISABLED) { + config->base->PMEN_CLR = BIT(pin); + WRITE_BIT(data->trig_low, pin, 0); + WRITE_BIT(data->trig_high, pin, 0); + + ret = 0; + goto unlock; + } else if (mode == GPIO_INT_MODE_EDGE) { + } else { + /* Not yet implemented */ + ret = -ENOTSUP; + goto unlock; + } + + WRITE_BIT(data->trig_low, pin, trig & GPIO_INT_TRIG_LOW); + WRITE_BIT(data->trig_high, pin, trig & GPIO_INT_TRIG_HIGH); + + config->base->PMEN_SET = BIT(pin); + + ret = 0; + +unlock: + irq_unlock(key); + return ret; +} + +static int gpio_si32_manage_callback(const struct device *dev, struct gpio_callback *callback, + bool set) +{ + struct gpio_si32_data *data = dev->data; + + return gpio_manage_callback(&data->cb, callback, set); +} + +static const struct gpio_driver_api gpio_si32_driver = { + .pin_configure = gpio_si32_configure, + .port_get_raw = gpio_si32_port_get_raw, + .port_set_masked_raw = gpio_si32_port_set_masked_raw, + .port_set_bits_raw = gpio_si32_port_set_bits_raw, + .port_clear_bits_raw = gpio_si32_port_clear_bits_raw, + .port_toggle_bits = gpio_si32_port_toggle_bits, + .pin_interrupt_configure = gpio_si32_pin_interrupt_configure, + .manage_callback = gpio_si32_manage_callback, +}; + +static int gpio_si32_init(const struct device *dev) +{ + const struct gpio_si32_config *config = dev->config; + + if (config->disable_pullups) { + SI32_PBSTD_A_disable_pullup_resistors(config->base); + } + + return 0; +} + +#define GPIO_DEVICE_INIT(__node, __suffix, __base_addr) \ + static const struct gpio_si32_config gpio_si32_cfg_##__suffix = { \ + .common = \ + { \ + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_NGPIOS(16U), \ + }, \ + .base = (SI32_PBSTD_A_Type *)__base_addr, \ + .disable_pullups = DT_PROP(__node, disable_pullups), \ + }; \ + static struct gpio_si32_data gpio_si32_data_##__suffix; \ + DEVICE_DT_DEFINE(__node, gpio_si32_init, NULL, &gpio_si32_data_##__suffix, \ + &gpio_si32_cfg_##__suffix, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, \ + &gpio_si32_driver) + +#define GPIO_DEVICE_INIT_SI32(__suffix) \ + GPIO_DEVICE_INIT(DT_NODELABEL(gpio##__suffix), __suffix, \ + DT_REG_ADDR(DT_NODELABEL(gpio##__suffix))) + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio0), okay) +GPIO_DEVICE_INIT_SI32(0); +#endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay) +GPIO_DEVICE_INIT_SI32(1); +#endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio2), okay) +GPIO_DEVICE_INIT_SI32(2); +#endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio3), okay) +GPIO_DEVICE_INIT_SI32(3); +#endif + +#define GPIO_DEVICE_LIST_ENTRY(node_id) &DEVICE_NAME_GET(Z_DEVICE_DT_DEV_ID(node_id)), +static const struct device *gpio_devices[] = { + DT_FOREACH_STATUS_OKAY(silabs_si32_gpio, GPIO_DEVICE_LIST_ENTRY)}; + +/* The hardware only supports level interrupts, so we have to emulate edge + * interrupts in this handler. + */ +static void gpio_si32_irq_handler(const struct device *arg) +{ + ARG_UNUSED(arg); + + irq_disable(PMATCH_IRQn); + NVIC_ClearPendingIRQ(PMATCH_IRQn); + + for (size_t i = 0; i < ARRAY_SIZE(gpio_devices); i++) { + const struct device *dev = gpio_devices[i]; + const struct gpio_si32_config *config = dev->config; + struct gpio_si32_data *data = dev->data; + + uint32_t pmen = SI32_PBSTD_A_read_pmen(config->base); + uint32_t pm = SI32_PBSTD_A_read_pm(config->base); + uint32_t values = SI32_PBSTD_A_read_pins(config->base); + + /* Invert triggers for all pins which are at their trigger + * values. This disables interrupts until they change again + * since the hardware only supports level interrupts. + */ + uint32_t pins_not_at_trigger_value = (pm ^ values) & pmen; + uint32_t pins_at_trigger_value = (~pins_not_at_trigger_value) & pmen; + SI32_PBSTD_A_write_pm(config->base, pm ^ pins_at_trigger_value); + + /* To check which pins actually changed we have to store and + * compare the previous value. + */ + uint32_t changed_pins = (values ^ data->pin_values) & pmen; + data->pin_values = values; + + if (changed_pins) { + /* The user might be interested in both levels or just one, + * so filter those events here. + */ + uint32_t changed_pins_high = (values & changed_pins) & data->trig_high; + uint32_t changed_pins_low = ((~values) & changed_pins) & data->trig_low; + + gpio_fire_callbacks(&data->cb, dev, changed_pins_high | changed_pins_low); + } + } + + irq_enable(PMATCH_IRQn); +} + +static int gpio_si32_common_init(void) +{ + /* This is the only mode we support right now */ + SI32_PBCFG_A_select_port_match_mode_pin_match(SI32_PBCFG_0); + + IRQ_CONNECT(PMATCH_IRQn, 0, gpio_si32_irq_handler, NULL, 0); + irq_enable(PMATCH_IRQn); + + return 0; +} +SYS_INIT(gpio_si32_common_init, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY); diff --git a/drivers/gpio/gpio_si32_hd.c b/drivers/gpio/gpio_si32_hd.c new file mode 100644 index 00000000000000..f079d81d8176e0 --- /dev/null +++ b/drivers/gpio/gpio_si32_hd.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +struct gpio_si32_hd_config { + struct gpio_driver_config common; + SI32_PBHD_A_Type *base; + + uint32_t nchannel_current_limit; + bool has_nchannel_current_limit; + + uint32_t pchannel_current_limit; + bool has_pchannel_current_limit; + + bool enable_bias; + bool low_power_port_mode; + bool enable_drivers; +}; + +struct gpio_si32_hd_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data common; +}; + +static int gpio_si32_hd_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) +{ + const struct gpio_si32_hd_config *config = dev->config; + + /* Set pin function to GPIO. + * This is the only supported mode for now. + */ + switch (pin) { + case 0: + SI32_PBHD_A_select_pin0_function(SI32_PBHD_4, 0); + break; + case 1: + SI32_PBHD_A_select_pin1_function(SI32_PBHD_4, 0); + break; + case 2: + SI32_PBHD_A_select_pin2_function(SI32_PBHD_4, 0); + break; + case 3: + SI32_PBHD_A_select_pin3_function(SI32_PBHD_4, 0); + break; + case 4: + SI32_PBHD_A_select_pin4_function(SI32_PBHD_4, 0); + break; + case 5: + SI32_PBHD_A_select_pin5_function(SI32_PBHD_4, 0); + break; + } + + if (flags & SI32_GPIO_DS_ENABLE_CURRENT_LIMIT) { + SI32_PBHD_A_enable_pin_current_limit(config->base, BIT(pin)); + } + + if (flags & GPIO_OUTPUT) { + if (flags & GPIO_OUTPUT_INIT_HIGH) { + SI32_PBHD_A_write_pins_high(config->base, BIT(pin)); + } else if (flags & GPIO_OUTPUT_INIT_LOW) { + SI32_PBHD_A_write_pins_low(config->base, BIT(pin)); + } + + SI32_PBHD_A_set_pins_push_pull_output(config->base, BIT(pin)); + } else if (flags & GPIO_INPUT) { + SI32_PBHD_A_set_pins_digital_input(config->base, BIT(pin)); + } else { + SI32_PBHD_A_set_pins_analog(config->base, BIT(pin)); + } + + return 0; +} + +static int gpio_si32_hd_port_get_raw(const struct device *dev, uint32_t *value) +{ + const struct gpio_si32_hd_config *config = dev->config; + *value = SI32_PBHD_A_read_pins(config->base); + return 0; +} + +static int gpio_si32_hd_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, + gpio_port_value_t value) +{ + const struct gpio_si32_hd_config *config = dev->config; + SI32_PBHD_A_write_pins_masked(config->base, value, mask); + return 0; +} + +static int gpio_si32_hd_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) +{ + const struct gpio_si32_hd_config *config = dev->config; + SI32_PBHD_A_write_pins_high(config->base, pins); + return 0; +} + +static int gpio_si32_hd_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) +{ + const struct gpio_si32_hd_config *config = dev->config; + SI32_PBHD_A_write_pins_low(config->base, pins); + return 0; +} + +static int gpio_si32_hd_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) +{ + const struct gpio_si32_hd_config *config = dev->config; + SI32_PBHD_A_toggle_pins(config->base, pins); + return 0; +} + +static int gpio_si32_hd_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, + enum gpio_int_mode mode, enum gpio_int_trig trig) +{ + return -ENOTSUP; +} + +static const struct gpio_driver_api gpio_si32_hd_driver = { + .pin_configure = gpio_si32_hd_configure, + .port_get_raw = gpio_si32_hd_port_get_raw, + .port_set_masked_raw = gpio_si32_hd_port_set_masked_raw, + .port_set_bits_raw = gpio_si32_hd_port_set_bits_raw, + .port_clear_bits_raw = gpio_si32_hd_port_clear_bits_raw, + .port_toggle_bits = gpio_si32_hd_port_toggle_bits, + .pin_interrupt_configure = gpio_si32_hd_pin_interrupt_configure, +}; + +static int gpio_si32_hd_init(const struct device *dev) +{ + const struct gpio_si32_hd_config *config = dev->config; + + SI32_PBCFG_A_unlock_ports(SI32_PBCFG_0); + SI32_PBHD_A_write_pblock(config->base, 0x0000); + + if (config->has_nchannel_current_limit) { + SI32_PBHD_A_select_nchannel_current_limit(config->base, + config->nchannel_current_limit); + } + if (config->has_pchannel_current_limit) { + SI32_PBHD_A_select_pchannel_current_limit(config->base, + config->pchannel_current_limit); + } + if (config->enable_bias) { + SI32_PBHD_A_enable_bias(config->base); + } + if (config->low_power_port_mode) { + SI32_PBHD_A_select_low_power_port_mode(config->base); + } + if (config->enable_drivers) { + SI32_PBHD_A_enable_drivers(config->base); + } + + return 0; +} + +#define OPT_INT_PROP(__node, name) \ + COND_CODE_1(DT_NODE_HAS_PROP(__node, name), \ + (.name = DT_PROP(__node, name), .has_##name = true, ), ()) + +#define GPIO_DEVICE_INIT(__node, __suffix, __base_addr) \ + static const struct gpio_si32_hd_config gpio_si32_hd_cfg_##__suffix = { \ + .common = \ + { \ + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_NGPIOS(6U), \ + }, \ + .base = (SI32_PBHD_A_Type *)__base_addr, \ + OPT_INT_PROP(__node, nchannel_current_limit) \ + OPT_INT_PROP(__node, nchannel_current_limit) \ + .enable_bias = DT_PROP(__node, enable_bias), \ + .low_power_port_mode = DT_PROP(__node, low_power_port_mode), \ + .enable_drivers = DT_PROP(__node, enable_drivers), \ + }; \ + static struct gpio_si32_hd_data gpio_si32_hd_data_##__suffix; \ + DEVICE_DT_DEFINE(__node, gpio_si32_hd_init, NULL, &gpio_si32_hd_data_##__suffix, \ + &gpio_si32_hd_cfg_##__suffix, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, \ + &gpio_si32_hd_driver) + +#define GPIO_DEVICE_INIT_SI32_HD(__suffix) \ + GPIO_DEVICE_INIT(DT_NODELABEL(gpio##__suffix), __suffix, \ + DT_REG_ADDR(DT_NODELABEL(gpio##__suffix))) + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio4), okay) +GPIO_DEVICE_INIT_SI32_HD(4); +#endif diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index fcbfd6b079a189..071d5825484ef5 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -59,6 +59,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_CDNS uart_cdns.c) zephyr_library_sources_ifdef(CONFIG_UART_OPENTITAN uart_opentitan.c) zephyr_library_sources_ifdef(CONFIG_UART_HOSTLINK uart_hostlink.c) zephyr_library_sources_ifdef(CONFIG_UART_EMUL uart_emul.c) +zephyr_library_sources_ifdef(CONFIG_UART_SI32_USART uart_si32_usart.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE uart_handlers.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 38ab4a9e415576..ff20375f3d222e 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -218,4 +218,6 @@ source "drivers/serial/Kconfig.hostlink" source "drivers/serial/Kconfig.emul" +source "drivers/serial/Kconfig.si32" + endif # SERIAL diff --git a/drivers/serial/Kconfig.si32 b/drivers/serial/Kconfig.si32 new file mode 100644 index 00000000000000..d5cea4556cadef --- /dev/null +++ b/drivers/serial/Kconfig.si32 @@ -0,0 +1,10 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config UART_SI32_USART + bool "SI32 USART MCU serial driver" + default y + depends on DT_HAS_SILABS_SI32_USART_ENABLED + select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT diff --git a/drivers/serial/uart_si32_usart.c b/drivers/serial/uart_si32_usart.c new file mode 100644 index 00000000000000..882ed6a74b6dc3 --- /dev/null +++ b/drivers/serial/uart_si32_usart.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_si32_usart + +#include +#include +#include +#include +#include + +#include +#include +#include + +struct usart_si32_config { + SI32_USART_A_Type *usart; + bool hw_flow_control; + uint8_t parity; +#if defined(CONFIG_UART_INTERRUPT_DRIVEN) + uart_irq_config_func_t irq_config_func; +#endif + const struct device *clock_dev; +}; + +struct usart_si32_data { + uint32_t baud_rate; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t callback; + void *cb_data; +#endif +}; + +static int usart_si32_poll_in(const struct device *dev, unsigned char *c) +{ + const struct usart_si32_config *config = dev->config; + int ret = -1; + + if (SI32_USART_A_read_rx_fifo_count(config->usart) != 0) { + *c = SI32_USART_A_read_data_u8(config->usart); + ret = 0; + } + + return ret; +} + +static void usart_si32_poll_out(const struct device *dev, unsigned char c) +{ + const struct usart_si32_config *config = dev->config; + + while (SI32_USART_A_read_tx_fifo_count(config->usart) || + SI32_USART_A_is_tx_busy(config->usart)) + ; + + SI32_USART_A_write_data_u8(config->usart, c); +} + +static int usart_si32_err_check(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + int ret = 0; + + if (SI32_USART_A_is_tx_fifo_error_interrupt_pending(config->usart)) { + SI32_USART_A_clear_tx_fifo_error_interrupt(config->usart); + } + + if (SI32_USART_A_is_rx_overrun_interrupt_pending(config->usart)) { + SI32_USART_A_clear_rx_overrun_error_interrupt(config->usart); + ret |= UART_ERROR_OVERRUN; + } + + if (SI32_USART_A_is_rx_parity_error_interrupt_pending(config->usart)) { + SI32_USART_A_clear_rx_parity_error_interrupt(config->usart); + ret |= UART_ERROR_PARITY; + } + + if (SI32_USART_A_is_rx_frame_error_interrupt_pending(config->usart)) { + SI32_USART_A_clear_rx_frame_error_interrupt(config->usart); + ret |= UART_ERROR_FRAMING; + } + + return ret; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static int usart_si32_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) +{ + const struct usart_si32_config *config = dev->config; + int i; + + /* NOTE: Checking `SI32_USART_A_is_tx_busy` is a workaround. + * For some reason data gets corrupted when writing to the FIFO + * while a write is happening. + */ + for (i = 0; i < size && SI32_USART_A_read_tx_fifo_count(config->usart) == 0 && + !SI32_USART_A_is_tx_busy(config->usart); + i++) { + SI32_USART_A_write_data_u8(config->usart, tx_data[i]); + } + + return i; +} + +static int usart_si32_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) +{ + const struct usart_si32_config *config = dev->config; + int i; + + for (i = 0; i < size; i++) { + if (!SI32_USART_A_read_rx_fifo_count(config->usart)) { + break; + } + + rx_data[i] = SI32_USART_A_read_data_u8(config->usart); + } + + return i; +} + +static void usart_si32_irq_tx_enable(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + SI32_USART_A_enable_tx_data_request_interrupt(config->usart); +} + +static void usart_si32_irq_tx_disable(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + SI32_USART_A_disable_tx_data_request_interrupt(config->usart); +} + +static int usart_si32_irq_tx_ready(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + return SI32_USART_A_is_tx_data_request_interrupt_pending(config->usart); +} + +static int usart_si32_irq_tx_complete(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + return SI32_USART_A_is_tx_complete(config->usart); +} + +static void usart_si32_irq_rx_enable(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + SI32_USART_A_enable_rx_data_request_interrupt(config->usart); +} + +static void usart_si32_irq_rx_disable(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + SI32_USART_A_disable_rx_data_request_interrupt(config->usart); +} + +static int usart_si32_irq_rx_ready(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + return SI32_USART_A_is_rx_data_request_interrupt_pending(config->usart); +} + +static void usart_si32_irq_err_enable(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + + SI32_USART_A_enable_rx_error_interrupts(config->usart); + SI32_USART_A_enable_tx_error_interrupts(config->usart); +} + +static void usart_si32_irq_err_disable(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + + SI32_USART_A_disable_rx_error_interrupts(config->usart); + SI32_USART_A_disable_tx_error_interrupts(config->usart); +} + +static int usart_si32_irq_is_pending(const struct device *dev) +{ + return usart_si32_irq_rx_ready(dev) || usart_si32_irq_tx_ready(dev); +} + +static int usart_si32_irq_update(const struct device *dev) +{ + return 1; +} + +static void usart_si32_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, + void *cb_data) +{ + struct usart_si32_data *data = dev->data; + + data->callback = cb; + data->cb_data = cb_data; +} + +static void usart_si32_irq_handler(const struct device *dev) +{ + struct usart_si32_data *data = dev->data; + + if (data->callback) { + data->callback(dev, data->cb_data); + } + + usart_si32_err_check(dev); +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static const struct uart_driver_api usart_si32_driver_api = { + .poll_in = usart_si32_poll_in, + .poll_out = usart_si32_poll_out, + .err_check = usart_si32_err_check, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = usart_si32_fifo_fill, + .fifo_read = usart_si32_fifo_read, + .irq_tx_enable = usart_si32_irq_tx_enable, + .irq_tx_disable = usart_si32_irq_tx_disable, + .irq_tx_ready = usart_si32_irq_tx_ready, + .irq_tx_complete = usart_si32_irq_tx_complete, + .irq_rx_enable = usart_si32_irq_rx_enable, + .irq_rx_disable = usart_si32_irq_rx_disable, + .irq_rx_ready = usart_si32_irq_rx_ready, + .irq_err_enable = usart_si32_irq_err_enable, + .irq_err_disable = usart_si32_irq_err_disable, + .irq_is_pending = usart_si32_irq_is_pending, + .irq_update = usart_si32_irq_update, + .irq_callback_set = usart_si32_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +static int usart_si32_init(const struct device *dev) +{ + const struct usart_si32_config *config = dev->config; + struct usart_si32_data *data = dev->data; + + if (!device_is_ready(config->clock_dev)) { + return -ENODEV; + } + + uint32_t apb_freq; + int ret = clock_control_get_rate(config->clock_dev, NULL, &apb_freq); + if (ret) { + return ret; + } + + enum SI32_USART_A_PARITY_Enum parity = SI32_USART_A_PARITY_ODD; + bool parity_enabled; + switch (config->parity) { + case UART_CFG_PARITY_NONE: + parity_enabled = false; + break; + case UART_CFG_PARITY_ODD: + parity = SI32_USART_A_PARITY_ODD; + parity_enabled = true; + break; + case UART_CFG_PARITY_EVEN: + parity = SI32_USART_A_PARITY_EVEN; + parity_enabled = true; + break; + case UART_CFG_PARITY_MARK: + parity = SI32_USART_A_PARITY_SET; + parity_enabled = true; + break; + case UART_CFG_PARITY_SPACE: + parity = SI32_USART_A_PARITY_CLEAR; + parity_enabled = true; + break; + default: + return -ENOTSUP; + } + + if (config->usart == SI32_USART_0) { + SI32_CLKCTRL_A_enable_apb_to_modules_0(SI32_CLKCTRL_0, + SI32_CLKCTRL_A_APBCLKG0_USART0); + } else if (config->usart == SI32_USART_1) { + SI32_CLKCTRL_A_enable_apb_to_modules_0(SI32_CLKCTRL_0, + SI32_CLKCTRL_A_APBCLKG0_USART1); + } else { + return -ENOTSUP; + } + + uint32_t baud_register_value = (apb_freq / (2 * data->baud_rate)) - 1; + + SI32_USART_A_exit_loopback_mode(config->usart); + + if (config->hw_flow_control) { + SI32_USART_A_enable_rts(config->usart); + SI32_USART_A_select_rts_deassert_on_byte_free(config->usart); + SI32_USART_A_disable_rts_inversion(config->usart); + + SI32_USART_A_enable_cts(config->usart); + SI32_USART_A_disable_cts_inversion(config->usart); + } + + /* Transmitter */ + if (parity_enabled) { + SI32_USART_A_select_tx_parity(config->usart, parity); + SI32_USART_A_enable_tx_parity_bit(config->usart); + } else { + SI32_USART_A_disable_tx_parity_bit(config->usart); + } + SI32_USART_A_select_tx_data_length(config->usart, SI32_USART_A_DATA_LENGTH_8_BITS); + SI32_USART_A_enable_tx_start_bit(config->usart); + SI32_USART_A_enable_tx_stop_bit(config->usart); + SI32_USART_A_select_tx_stop_bits(config->usart, SI32_USART_A_STOP_BITS_1_BIT); + SI32_USART_A_set_tx_baudrate(config->usart, (uint16_t)baud_register_value); + SI32_USART_A_select_tx_asynchronous_mode(config->usart); + SI32_USART_A_disable_tx_signal_inversion(config->usart); + SI32_USART_A_select_tx_fifo_threshold_for_request_to_1(config->usart); + SI32_USART_A_enable_tx(config->usart); + + /* Receiver */ + if (parity_enabled) { + SI32_USART_A_select_rx_parity(config->usart, parity); + SI32_USART_A_enable_rx_parity_bit(config->usart); + } else { + SI32_USART_A_disable_rx_parity_bit(config->usart); + } + SI32_USART_A_select_rx_data_length(config->usart, SI32_USART_A_DATA_LENGTH_8_BITS); + SI32_USART_A_enable_rx_start_bit(config->usart); + SI32_USART_A_enable_rx_stop_bit(config->usart); + SI32_USART_A_select_rx_stop_bits(config->usart, SI32_USART_A_STOP_BITS_1_BIT); + SI32_USART_A_set_rx_baudrate(config->usart, (uint16_t)baud_register_value); + SI32_USART_A_select_rx_asynchronous_mode(config->usart); + SI32_USART_A_disable_rx_signal_inversion(config->usart); + SI32_USART_A_select_rx_fifo_threshold_1(config->usart); + SI32_USART_A_enable_rx(config->usart); + + SI32_USART_A_flush_tx_fifo(config->usart); + SI32_USART_A_flush_rx_fifo(config->usart); + +#if defined(CONFIG_UART_INTERRUPT_DRIVEN) + config->irq_config_func(dev); +#endif + + return 0; +} + +#if defined(CONFIG_UART_INTERRUPT_DRIVEN) +#define SI32_USART_IRQ_HANDLER_DECL(index) \ + static void usart_si32_irq_config_func_##index(const struct device *dev); +#define SI32_USART_IRQ_HANDLER(index) \ + static void usart_si32_irq_config_func_##index(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(index), DT_INST_IRQ(index, priority), \ + usart_si32_irq_handler, DEVICE_DT_INST_GET(index), 0); \ + irq_enable(DT_INST_IRQN(index)); \ + } +#else +#define SI32_USART_IRQ_HANDLER_DECL(index) /* Not used */ +#define SI32_USART_IRQ_HANDLER(index) /* Not used */ +#endif + +#if defined(CONFIG_UART_INTERRUPT_DRIVEN) +#define SI32_USART_IRQ_HANDLER_FUNC(index) .irq_config_func = usart_si32_irq_config_func_##index, +#else +#define SI32_USART_IRQ_HANDLER_FUNC(index) /* Not used */ +#endif + +#define SI32_USART_INIT(index) \ + SI32_USART_IRQ_HANDLER_DECL(index) \ + \ + static const struct usart_si32_config usart_si32_cfg_##index = { \ + .usart = (SI32_USART_A_Type *)DT_INST_REG_ADDR(index), \ + .hw_flow_control = DT_INST_PROP(index, hw_flow_control), \ + .parity = DT_INST_ENUM_IDX_OR(index, parity, UART_CFG_PARITY_NONE), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(index)), \ + SI32_USART_IRQ_HANDLER_FUNC(index)}; \ + \ + static struct usart_si32_data usart_si32_data_##index = { \ + .baud_rate = DT_INST_PROP(index, current_speed), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(index, &usart_si32_init, NULL, &usart_si32_data_##index, \ + &usart_si32_cfg_##index, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \ + &usart_si32_driver_api); \ + \ + SI32_USART_IRQ_HANDLER(index) + +DT_INST_FOREACH_STATUS_OKAY(SI32_USART_INIT) diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index 0f7d5bcb16b615..21e304979181e4 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -37,6 +37,7 @@ 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_OPENTITAN spi_opentitan.c) +zephyr_library_sources_ifdef(CONFIG_SPI_SI32 spi_si32.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 c4e457b07901b5..1f2fc8f576506a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -123,4 +123,6 @@ source "drivers/spi/Kconfig.smartbond" source "drivers/spi/Kconfig.opentitan" +source "drivers/spi/Kconfig.si32" + endif # SPI diff --git a/drivers/spi/Kconfig.si32 b/drivers/spi/Kconfig.si32 new file mode 100644 index 00000000000000..f130f1b3dabd12 --- /dev/null +++ b/drivers/spi/Kconfig.si32 @@ -0,0 +1,8 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config SPI_SI32 + bool "SI32 SPI controller driver" + default y + depends on DT_HAS_SILABS_SI32_SPI_ENABLED diff --git a/drivers/spi/spi_si32.c b/drivers/spi/spi_si32.c new file mode 100644 index 00000000000000..65128716eb1c58 --- /dev/null +++ b/drivers/spi/spi_si32.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_si32_spi + +#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL +#include +LOG_MODULE_REGISTER(spi_silabs_si32); + +#include "spi_context.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include + +struct spi_si32_data { + struct spi_context ctx; +}; + +struct spi_si32_config { + SI32_SPI_A_Type *spi; + void (*irq_connect)(void); + unsigned int irq; + const struct device *clock_dev; +}; + +static int spi_si32_configure(const struct device *dev, const struct spi_config *spi_config) +{ + const struct spi_si32_config *config = dev->config; + struct spi_si32_data *data = dev->data; + + if (spi_context_configured(&data->ctx, spi_config)) { + return 0; + } + + if (!device_is_ready(config->clock_dev)) { + LOG_ERR("source clock is not ready"); + return -ENODEV; + } + + if (SPI_OP_MODE_GET(spi_config->operation) != SPI_OP_MODE_MASTER) { + LOG_ERR("only master mode is supported right now"); + return -ENOTSUP; + } + if (SPI_OP_MODE_GET(spi_config->operation) & ~(SPI_MODE_CPOL | SPI_MODE_CPHA)) { + LOG_ERR("unsupported mode flags: 0x%x", SPI_OP_MODE_GET(spi_config->operation)); + return -ENOTSUP; + } + + if ((spi_config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) { + LOG_ERR("unsupported lines config: 0x%x", spi_config->operation & SPI_LINES_MASK); + return -ENOTSUP; + } + + uint32_t word_size = SPI_WORD_SIZE_GET(spi_config->operation); + if (word_size == 0 || word_size > 16) { + LOG_ERR("sunsupported word size: %u", word_size); + return -ENOTSUP; + } + + uint32_t apb_freq; + int ret = clock_control_get_rate(config->clock_dev, NULL, &apb_freq); + if (ret) { + LOG_ERR("failed to get source clock rate: %d", ret); + return ret; + } + + SI32_SPI_A_set_clock_divisor(config->spi, apb_freq / spi_config->frequency); + + SI32_SPI_A_disable_module(config->spi); + + if (spi_config->cs.gpio.port != NULL) { + SI32_SPI_A_select_3wire_master_mode(config->spi); + } else { + if (spi_config->operation & SPI_CS_ACTIVE_HIGH) { + SI32_SPI_A_select_4wire_master_mode_nss_low(config->spi); + } else { + SI32_SPI_A_select_4wire_master_mode_nss_high(config->spi); + } + } + + SI32_SPI_A_set_data_length(config->spi, word_size); + + if (spi_config->operation & SPI_TRANSFER_LSB) { + SI32_SPI_A_select_direction_lsb_first(config->spi); + } else { + SI32_SPI_A_select_direction_msb_first(config->spi); + } + + if (SPI_MODE_GET(spi_config->operation) & SPI_MODE_CPOL) { + SI32_SPI_A_select_clock_idle_high(config->spi); + } else { + SI32_SPI_A_select_clock_idle_low(config->spi); + } + + if (SPI_MODE_GET(spi_config->operation) & SPI_MODE_CPHA) { + SI32_SPI_A_select_data_change_first_edge(config->spi); + } else { + SI32_SPI_A_select_data_change_second_edge(config->spi); + } + + SI32_SPI_A_select_master_mode(config->spi); + + SI32_SPI_A_select_tx_fifo_threshold(config->spi, SI32_SPI_FIFO_THRESHOLD_ONE); + SI32_SPI_A_select_rx_fifo_threshold(config->spi, SI32_SPI_FIFO_THRESHOLD_ONE); + + SI32_SPI_A_disable_rx_fifo_read_request_interrupt(config->spi); + SI32_SPI_A_disable_tx_fifo_write_request_interrupt(config->spi); + SI32_SPI_A_disable_rx_fifo_read_request_interrupt(config->spi); + SI32_SPI_A_disable_shift_register_empty_interrupt(config->spi); + SI32_SPI_A_disable_underrun_interrupt(config->spi); + SI32_SPI_A_enable_rx_fifo_overrun_interrupt(config->spi); + SI32_SPI_A_enable_tx_fifo_overrun_interrupt(config->spi); + SI32_SPI_A_enable_mode_fault_interrupt(config->spi); + SI32_SPI_A_clear_all_interrupts(config->spi); + + SI32_SPI_A_enable_module(config->spi); + + SI32_SPI_A_enable_stall_in_debug_mode(config->spi); + + data->ctx.config = spi_config; + + return 0; +} + +static void spi_si32_cs_control_hw(const struct device *dev, bool on, bool force_off) +{ + const struct spi_si32_config *config = dev->config; + struct spi_si32_data *data = dev->data; + const struct spi_config *spi_config = data->ctx.config; + + if (on) { + SI32_SPI_A_clear_nss(config->spi); + } else { + if (!force_off && spi_config->operation & SPI_HOLD_ON_CS) { + return; + } + + SI32_SPI_A_set_nss(config->spi); + } +} + +static void spi_si32_cs_control(const struct device *dev, bool on) +{ + struct spi_si32_data *data = dev->data; + const struct spi_config *spi_config = data->ctx.config; + + if (!data->ctx.config) { + LOG_ERR("can't control CS without config"); + return; + } + + if (spi_config->cs.gpio.port != NULL) { + spi_context_cs_control(&data->ctx, on); + return; + } + + spi_si32_cs_control_hw(dev, on, false); +} + +static int spi_si32_transceive(const struct device *dev, const struct spi_config *spi_config, + const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs) +{ + const struct spi_si32_config *config = dev->config; + struct spi_si32_data *data = dev->data; + int ret; + + spi_context_lock(&data->ctx, false, NULL, NULL, spi_config); + + ret = spi_si32_configure(dev, spi_config); + if (ret) { + goto release; + } + + spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1); + + spi_si32_cs_control(dev, true); + + /* Flush SPI FIFOs */ + SI32_SPI_A_flush_rx_fifo(config->spi); + while (config->spi->CONFIG.RFIFOFL) + ; + SI32_SPI_A_flush_tx_fifo(config->spi); + while (config->spi->CONFIG.TFIFOFL) + ; + + /* Clear all interrupts */ + SI32_SPI_A_clear_all_interrupts(config->spi); + NVIC_ClearPendingIRQ(config->irq); + + /* Enable relevant interrupts */ + irq_enable(config->irq); + SI32_SPI_A_enable_rx_fifo_read_request_interrupt(config->spi); + SI32_SPI_A_enable_shift_register_empty_interrupt(config->spi); + + ret = spi_context_wait_for_completion(&data->ctx); + + spi_si32_cs_control(dev, false); + +release: + spi_context_release(&data->ctx, ret); + return ret; +} + +static int spi_si32_release(const struct device *dev, const struct spi_config *spi_config) +{ + struct spi_si32_data *data = dev->data; + + /* spi_context_unlock_unconditionally handles the software path already */ + spi_si32_cs_control_hw(dev, false, true); + + spi_context_unlock_unconditionally(&data->ctx); + return 0; +} + +static bool spi_si32_transfer_ongoing(struct spi_si32_data *data) +{ + return spi_context_tx_on(&data->ctx) || spi_context_rx_on(&data->ctx); +} + +static void spi_si32_irq_handler(const struct device *dev) +{ + const struct spi_si32_config *config = dev->config; + struct spi_si32_data *data = dev->data; + uint8_t byte = 0; + int err = 0; + + if (SI32_SPI_A_is_rx_fifo_read_request_interrupt_pending(config->spi)) { + byte = SI32_SPI_A_read_rx_fifo_u8(config->spi); + + if (spi_context_rx_buf_on(&data->ctx)) { + UNALIGNED_PUT(byte, (uint8_t *)data->ctx.rx_buf); + } + + spi_context_update_rx(&data->ctx, 1, 1); + } + + else if (SI32_SPI_A_is_shift_register_empty_interrupt_pending(config->spi)) { + if (spi_context_tx_buf_on(&data->ctx)) { + byte = UNALIGNED_GET((uint8_t *)(data->ctx.tx_buf)); + } + + SI32_SPI_A_write_tx_fifo_u8(config->spi, byte); + + spi_context_update_tx(&data->ctx, 1, 1); + } + + if (SI32_SPI_A_is_rx_fifo_overrun_interrupt_pending(config->spi)) { + LOG_ERR("RX FIFO overrun"); + err = -EIO; + } + if (SI32_SPI_A_is_tx_fifo_overrun_interrupt_pending(config->spi)) { + LOG_ERR("TX FIFO overrun"); + err = -EIO; + } + if (SI32_SPI_A_is_mode_fault_interrupt_pending(config->spi)) { + LOG_ERR("mode fault"); + err = -EIO; + } + if (SI32_SPI_A_is_illegal_rx_fifo_access_interrupt_pending(config->spi)) { + LOG_ERR("illegal RX FIFO access"); + err = -EIO; + } + if (SI32_SPI_A_is_illegal_tx_fifo_access_interrupt_pending(config->spi)) { + LOG_ERR("illegal TX FIFO access"); + err = -EIO; + } + + SI32_SPI_A_clear_all_interrupts(config->spi); + + if (err || !spi_si32_transfer_ongoing(data)) { + SI32_SPI_A_disable_rx_fifo_read_request_interrupt(config->spi); + SI32_SPI_A_disable_shift_register_empty_interrupt(config->spi); + irq_disable(config->irq); + SI32_SPI_A_clear_all_interrupts(config->spi); + NVIC_ClearPendingIRQ(config->irq); + + spi_context_complete(&data->ctx, dev, err); + } +} + +static struct spi_driver_api spi_si32_api = { + .transceive = spi_si32_transceive, + .release = spi_si32_release, +}; + +static int spi_si32_init(const struct device *dev) +{ + int err; + const struct spi_si32_config *config = dev->config; + struct spi_si32_data *data = dev->data; + + if (config->spi == SI32_SPI_0) { + SI32_CLKCTRL_A_enable_apb_to_modules_0(SI32_CLKCTRL_0, + SI32_CLKCTRL_A_APBCLKG0_SPI0); + } else if (config->spi == SI32_SPI_2) { + SI32_CLKCTRL_A_enable_apb_to_modules_0(SI32_CLKCTRL_0, + SI32_CLKCTRL_A_APBCLKG0_SPI2); + } else { + LOG_ERR("unsupported spi device"); + return -ENOTSUP; + } + + irq_disable(config->irq); + config->irq_connect(); + + err = spi_context_cs_configure_all(&data->ctx); + if (err < 0) { + return err; + } + + /* Make sure the context is unlocked */ + spi_context_unlock_unconditionally(&data->ctx); + return 0; +} + +#define SI32_SPI_INIT(n) \ + static void irq_connect_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), spi_si32_irq_handler, \ + DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + static struct spi_si32_data spi_si32_data_##n = { \ + SPI_CONTEXT_INIT_LOCK(spi_si32_data_##n, ctx), \ + SPI_CONTEXT_INIT_SYNC(spi_si32_data_##n, ctx), \ + }; \ + static struct spi_si32_config spi_si32_cfg_##n = { \ + .spi = (SI32_SPI_A_Type *)DT_INST_REG_ADDR(n), \ + .irq_connect = irq_connect_##n, \ + .irq = DT_INST_IRQN(n), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + }; \ + DEVICE_DT_INST_DEFINE(n, spi_si32_init, NULL, &spi_si32_data_##n, &spi_si32_cfg_##n, \ + POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, &spi_si32_api); + +DT_INST_FOREACH_STATUS_OKAY(SI32_SPI_INIT) diff --git a/dts/arm/silabs/sim3u.dtsi b/dts/arm/silabs/sim3u.dtsi new file mode 100644 index 00000000000000..77095d8931acc3 --- /dev/null +++ b/dts/arm/silabs/sim3u.dtsi @@ -0,0 +1,140 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +/ { + chosen { + zephyr,flash-controller = &flash; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "arm,cortex-m3"; + device_type = "cpu"; + clock-frequency = <20000000>; + reg = <0>; + }; + }; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + }; + + pinctrl: pinctrl { + compatible = "silabs,si32-pinctrl"; + status = "okay"; + }; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + pll0: pll0@4003b000 { + compatible = "silabs,si32-pll"; + #clock-cells = <0>; + reg = <0x4003b000>; + status = "disabled"; + }; + + clk_ahb: clk-ahb { + compatible = "silabs,si32-ahb"; + #clock-cells = <0>; + status = "disabled"; + }; + + clk_apb: clk-apb { + compatible = "silabs,si32-apb"; + #clock-cells = <0>; + divider = <1>; + clocks = <&clk_ahb>; + status = "disabled"; + }; + }; + + soc { + flash: flash-controller@4002e000 { + compatible = "silabs,si32-flash-controller"; + reg = <0x4002e000 0x1000>; + + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "soc-nv-flash"; + write-block-size = <2>; + }; + }; + + usart0: usart@40000000 { + compatible = "silabs,si32-usart"; + reg = <0x40000000 0x1000>; + interrupts = <27 0>; + clocks = <&clk_apb>; + status = "disabled"; + }; + + usart1: usart@40001000 { + compatible = "silabs,si32-usart"; + reg = <0x40001000 0x1000>; + interrupts = <28 0>; + clocks = <&clk_apb>; + status = "disabled"; + }; + + spi2: spi@40006000 { + compatible = "silabs,si32-spi"; + reg = <0x40006000 0x1000>; + interrupts = <31 0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk_apb>; + status = "disabled"; + }; + + gpio0: gpio@4002a0a0 { + compatible = "silabs,si32-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x4002a0a0 0xa0>; + }; + + gpio1: gpio@4002a140 { + compatible = "silabs,si32-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x4002a140 0xa0>; + }; + + gpio2: gpio@4002a1e0 { + compatible = "silabs,si32-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x4002a1e0 0x140>; + }; + + gpio3: gpio@4002a320 { + compatible = "silabs,si32-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x4002a320 0xa0>; + }; + + gpio4: gpio@4002a3c0 { + compatible = "silabs,si32-hd-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x4002a3c0 0xa0>; + }; + }; +}; + +&nvic { + arm,num-irq-priority-bits = <4>; +}; diff --git a/dts/arm/silabs/sim3u167bgm.dtsi b/dts/arm/silabs/sim3u167bgm.dtsi new file mode 100644 index 00000000000000..f97a27a6b40c7f --- /dev/null +++ b/dts/arm/silabs/sim3u167bgm.dtsi @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/ { + sram0: memory@20000000 { + reg = <0x20000000 DT_SIZE_K(32)>; + }; + + soc { + compatible = "silabs,sim3u167bgm", "silabs,sim3u", "silabs,sim3", "simple-bus"; + + flash-controller@4002e000 { + flash0: flash@0 { + reg = <0 DT_SIZE_K(256)>; + erase-block-size = ; + }; + }; + }; +}; diff --git a/dts/bindings/clock/silabs,si32-ahb.yaml b/dts/bindings/clock/silabs,si32-ahb.yaml new file mode 100644 index 00000000000000..f7e16fb2ea39f8 --- /dev/null +++ b/dts/bindings/clock/silabs,si32-ahb.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: Silabs SI32 AHB clock controller node + +compatible: "silabs,si32-ahb" + +include: base.yaml + +properties: + clocks: + required: true diff --git a/dts/bindings/clock/silabs,si32-apb.yaml b/dts/bindings/clock/silabs,si32-apb.yaml new file mode 100644 index 00000000000000..ad8d9676b3b18c --- /dev/null +++ b/dts/bindings/clock/silabs,si32-apb.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: Silabs SI32 APB clock controller node + +compatible: "silabs,si32-apb" + +include: base.yaml + +properties: + clocks: + required: true + + divider: + type: int + required: true diff --git a/dts/bindings/clock/silabs,si32-pll.yaml b/dts/bindings/clock/silabs,si32-pll.yaml new file mode 100644 index 00000000000000..6fd9e0af1a3a92 --- /dev/null +++ b/dts/bindings/clock/silabs,si32-pll.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: Silabs SI32 PLL clock controller node + +compatible: "silabs,si32-pll" + +include: base.yaml + +properties: + reg: + required: true diff --git a/dts/bindings/flash_controller/silabs,si32-flash-controller.yaml b/dts/bindings/flash_controller/silabs,si32-flash-controller.yaml new file mode 100644 index 00000000000000..e3d6d3262270a4 --- /dev/null +++ b/dts/bindings/flash_controller/silabs,si32-flash-controller.yaml @@ -0,0 +1,9 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +compatible: "silabs,si32-flash-controller" + +description: Silabs SI32 flash controller + +include: flash-controller.yaml diff --git a/dts/bindings/gpio/silabs,si32-gpio.yaml b/dts/bindings/gpio/silabs,si32-gpio.yaml new file mode 100644 index 00000000000000..e6fe121c4e3f72 --- /dev/null +++ b/dts/bindings/gpio/silabs,si32-gpio.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: SI32 GPIO node + +compatible: "silabs,si32-gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#gpio-cells": + const: 2 + + disable-pullups: + required: false + type: boolean + +gpio-cells: + - pin + - flags + diff --git a/dts/bindings/gpio/silabs,si32-hd-gpio.yaml b/dts/bindings/gpio/silabs,si32-hd-gpio.yaml new file mode 100644 index 00000000000000..e7727ee45cebf8 --- /dev/null +++ b/dts/bindings/gpio/silabs,si32-hd-gpio.yaml @@ -0,0 +1,40 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: SI32 HD GPIO node + +compatible: "silabs,si32-hd-gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#gpio-cells": + const: 2 + + nchannel-current-limit: + required: false + type: int + + pchannel-current-limit: + required: false + type: int + + enable-bias: + required: false + type: boolean + + low-power-port-mode: + required: false + type: boolean + + enable-drivers: + required: false + type: boolean + +gpio-cells: + - pin + - flags diff --git a/dts/bindings/misc/gardena,antenna.yaml b/dts/bindings/misc/gardena,antenna.yaml new file mode 100644 index 00000000000000..fea3fd07f517a9 --- /dev/null +++ b/dts/bindings/misc/gardena,antenna.yaml @@ -0,0 +1,32 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: default GPIO config for GARDENA sim3u radio module + +compatible: "gardena,antenna" + +include: base.yaml + +properties: + diversity-mode-gpios: + type: phandle-array + required: true + diversity-state-gpios: + type: phandle-array + required: true + antenna-control-gpios: + type: phandle-array + required: true + antenna-input-gpios: + type: phandle-array + required: true + led-red-input-gpios: + type: phandle-array + required: true + led-green-input-gpios: + type: phandle-array + required: true + led-blue-input-gpios: + type: phandle-array + required: true diff --git a/dts/bindings/pinctrl/silabs,si32-pinctrl.yaml b/dts/bindings/pinctrl/silabs,si32-pinctrl.yaml new file mode 100644 index 00000000000000..2213a28c238b96 --- /dev/null +++ b/dts/bindings/pinctrl/silabs,si32-pinctrl.yaml @@ -0,0 +1,32 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: | + Ailabs si32 pinctrl node. + +compatible: "silabs,si32-pinctrl" + +include: + - name: base.yaml + +child-binding: + description: si32 pin controller pin group + child-binding: + description: | + si32 pin controller pin configuration node + + include: + - name: pincfg-node.yaml + property-allowlist: + - input-enable + - output-enable + - output-low + - output-high + + properties: + pinmux: + required: true + type: array + description: | + Pin mux selections for this group. diff --git a/dts/bindings/serial/silabs,si32-usart.yaml b/dts/bindings/serial/silabs,si32-usart.yaml new file mode 100644 index 00000000000000..14c83f52bf7e37 --- /dev/null +++ b/dts/bindings/serial/silabs,si32-usart.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: SI32 USART + +compatible: "silabs,si32-usart" + +include: [uart-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + clocks: + required: true diff --git a/dts/bindings/spi/silabs,si32-spi.yaml b/dts/bindings/spi/silabs,si32-spi.yaml new file mode 100644 index 00000000000000..0a4e23dbfedccc --- /dev/null +++ b/dts/bindings/spi/silabs,si32-spi.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: SI32 SPI + +compatible: "silabs,si32-spi" + +include: [spi-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + clocks: + required: true diff --git a/include/zephyr/dt-bindings/gpio/silabs-si32-hd-gpio.h b/include/zephyr/dt-bindings/gpio/silabs-si32-hd-gpio.h new file mode 100644 index 00000000000000..b8daad9f6daf21 --- /dev/null +++ b/include/zephyr/dt-bindings/gpio/silabs-si32-hd-gpio.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_SILABS_SI32_HD_GPIO_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_SILABS_SI32_HD_GPIO_H_ + +/** + * @name GPIO drive strength flags + * + * The drive strength flags are a Zephyr specific extension of the standard GPIO + * flags specified by the Linux GPIO binding. Only applicable for Espressif + * ESP32 SoCs. + * + * The interface supports two different drive strengths: + * `DFLT` - The lowest drive strength supported by the HW + * `ALT` - The highest drive strength supported by the HW + * + * @{ + */ +/** @cond INTERNAL_HIDDEN */ +#define SI32_HD_GPIO_DS_POS 9 +/** @endcond */ + +/** Enable port bank current limit on this pin. */ +#define SI32_GPIO_DS_ENABLE_CURRENT_LIMIT (0x0U << SI32_HD_GPIO_DS_POS) + +/** @} */ + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_SILABS_SI32_HD_GPIO_H_ */ diff --git a/include/zephyr/dt-bindings/pinctrl/si32-pinctrl.h b/include/zephyr/dt-bindings/pinctrl/si32-pinctrl.h new file mode 100644 index 00000000000000..81f45d755e2853 --- /dev/null +++ b/include/zephyr/dt-bindings/pinctrl/si32-pinctrl.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _ZEPHYR_DT_BINDINGS_PINCTRL_SI32_PINCTRL_ +#define _ZEPHYR_DT_BINDINGS_PINCTRL_SI32_PINCTRL_ + +#define SI32_SIGNAL_USART0_TX 0 +#define SI32_SIGNAL_USART0_RX 1 +#define SI32_SIGNAL_USART0_RTS 2 +#define SI32_SIGNAL_USART0_CTS 3 +#define SI32_SIGNAL_USART0_UCLK 4 + +#define SI32_SIGNAL_SPI0_SCK 5 +#define SI32_SIGNAL_SPI0_MISO 6 +#define SI32_SIGNAL_SPI0_MOSI 7 +#define SI32_SIGNAL_SPI0_NSS 8 + +#define SI32_SIGNAL_USART1_TX 9 +#define SI32_SIGNAL_USART1_RX 10 +#define SI32_SIGNAL_USART1_RTS 11 +#define SI32_SIGNAL_USART1_CTS 12 +#define SI32_SIGNAL_USART1_UCLK 13 + +#define SI32_SIGNAL_EPCA0_CEX0 14 +#define SI32_SIGNAL_EPCA0_CEX1 15 +#define SI32_SIGNAL_EPCA0_CEX2 16 +#define SI32_SIGNAL_EPCA0_CEX3 17 +#define SI32_SIGNAL_EPCA0_CEX4 18 +#define SI32_SIGNAL_EPCA0_CEX4 19 + +#define SI32_SIGNAL_PCA0_CEX0 20 +#define SI32_SIGNAL_PCA0_CEX1 21 + +#define SI32_SIGNAL_PCA1_CEX0 22 +#define SI32_SIGNAL_PCA1_CEX1 23 + +#define SI32_SIGNAL_EPCA0_ECI 24 + +#define SI32_SIGNAL_PCA0_ECI 25 + +#define SI32_SIGNAL_PCA1_ECI 26 + +#define SI32_SIGNAL_I2S0_TX_WS 27 +#define SI32_SIGNAL_I2S0_TX_SCK 28 +#define SI32_SIGNAL_I2S0_TX_SD 29 + +#define SI32_SIGNAL_I2C0_SDA 30 +#define SI32_SIGNAL_I2C0_SCL 31 + +#define SI32_SIGNAL_CMP0S 32 +#define SI32_SIGNAL_CMP0A 33 + +#define SI32_SIGNAL_CMP1S 34 +#define SI32_SIGNAL_CMP1A 35 + +#define SI32_SIGNAL_TIMER0_CT 36 +#define SI32_SIGNAL_TIMER0_EX 37 + +#define SI32_SIGNAL_TIMER1_CT 38 +#define SI32_SIGNAL_TIMER1_EX 39 + +#define SI32_SIGNAL_UART0_TX 40 +#define SI32_SIGNAL_UART0_RX 41 +#define SI32_SIGNAL_UART0_RTS 42 +#define SI32_SIGNAL_UART0_CTS 43 + +#define SI32_SIGNAL_UART1_TX 44 +#define SI32_SIGNAL_UART1_RX 45 + +#define SI32_SIGNAL_SPI1_SCK 46 +#define SI32_SIGNAL_SPI1_MISO 47 +#define SI32_SIGNAL_SPI1_MOSI 48 +#define SI32_SIGNAL_SPI1_NSS 49 + +#define SI32_SIGNAL_SPI2_SCK 50 +#define SI32_SIGNAL_SPI2_MISO 51 +#define SI32_SIGNAL_SPI2_MOSI 52 +#define SI32_SIGNAL_SPI2_NSS 53 + +#define SI32_SIGNAL_AHB_OUT 54 + +#define SI32_SIGNAL_SSG0_EX0 55 +#define SI32_SIGNAL_SSG0_EX1 56 +#define SI32_SIGNAL_SSG0_EX2 57 +#define SI32_SIGNAL_SSG0_EX3 58 + +#define SI32_SIGNAL_RTC0_OUT 59 + +#define SI32_SIGNAL_I2S0_RX_WS 60 +#define SI32_SIGNAL_I2S0_RX_SCK 61 +#define SI32_SIGNAL_I2S0_RX_SD 62 + +#define SI32_SIGNAL_LPTIMER0_OUT 63 + +#define SI32_SIGNAL_I2C1_SDA 64 +#define SI32_SIGNAL_I2C1_SCL 65 + +#define SI32_SIGNAL_PB_HDKILL 66 + +/** + * @brief Specify MUX field + * + * @param fun Function name + * @param port Port number (0 to 4) + * @param pin Port pin number (0 to 15) + */ +#define SI32_MUX(fun, port, pin) \ + ((((port) & 0x7)) | \ + (((pin) & 0xF) << 3) | \ + ((SI32_SIGNAL_ ## fun & 0x7F) << 22)) + +#endif /* _ZEPHYR_DT_BINDINGS_PINCTRL_SI32_PINCTRL_ */ diff --git a/modules/Kconfig.silabs b/modules/Kconfig.silabs index 8abab8b1dd73ce..f234f724363c26 100644 --- a/modules/Kconfig.silabs +++ b/modules/Kconfig.silabs @@ -7,3 +7,8 @@ config HAS_SILABS_GECKO bool select HAS_CMSIS_CORE depends on SOC_FAMILY_EXX32 + +config HAS_SILABS_SI32 + bool + select HAS_CMSIS_CORE + depends on SOC_FAMILY_SIM3 diff --git a/soc/arm/silabs_sim3/CMakeLists.txt b/soc/arm/silabs_sim3/CMakeLists.txt new file mode 100644 index 00000000000000..dd7862666ec2e4 --- /dev/null +++ b/soc/arm/silabs_sim3/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory(sim3u) diff --git a/soc/arm/silabs_sim3/Kconfig b/soc/arm/silabs_sim3/Kconfig new file mode 100644 index 00000000000000..e33f8184361de8 --- /dev/null +++ b/soc/arm/silabs_sim3/Kconfig @@ -0,0 +1,18 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config SOC_FAMILY_SIM3 + bool + select HAS_SEGGER_RTT + select BUILD_OUTPUT_HEX + +if SOC_FAMILY_SIM3 + +config SOC_FAMILY + string + default "silabs_sim3" + +source "soc/arm/silabs_sim3/*/Kconfig.soc" + +endif # SOC_FAMILY_SIM3 diff --git a/soc/arm/silabs_sim3/Kconfig.defconfig b/soc/arm/silabs_sim3/Kconfig.defconfig new file mode 100644 index 00000000000000..c560477fd2c006 --- /dev/null +++ b/soc/arm/silabs_sim3/Kconfig.defconfig @@ -0,0 +1,5 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +source "soc/arm/silabs_sim3/*/Kconfig.defconfig.series" diff --git a/soc/arm/silabs_sim3/Kconfig.soc b/soc/arm/silabs_sim3/Kconfig.soc new file mode 100644 index 00000000000000..3f80bb343c97a6 --- /dev/null +++ b/soc/arm/silabs_sim3/Kconfig.soc @@ -0,0 +1,5 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +source "soc/arm/silabs_sim3/*/Kconfig.series" diff --git a/soc/arm/silabs_sim3/sim3u/CMakeLists.txt b/soc/arm/silabs_sim3/sim3u/CMakeLists.txt new file mode 100644 index 00000000000000..57f0bbcdb1920b --- /dev/null +++ b/soc/arm/silabs_sim3/sim3u/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +zephyr_sources(soc.c) + +set(CROSSBAR_CONFIG_H ${CMAKE_BINARY_DIR}/zephyr/include/generated/silabs_crossbar_config.h) +add_custom_command( + OUTPUT ${CROSSBAR_CONFIG_H} + DEPENDS ${EDT_PICKLE} + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_crossbar_config.py + ${EDT_PICKLE} ${CROSSBAR_CONFIG_H}) +add_custom_target(silabs_crossbar_config_h DEPENDS ${CROSSBAR_CONFIG_H}) +add_dependencies(zephyr_interface silabs_crossbar_config_h) diff --git a/soc/arm/silabs_sim3/sim3u/Kconfig.defconfig.series b/soc/arm/silabs_sim3/sim3u/Kconfig.defconfig.series new file mode 100644 index 00000000000000..7216b2ce7dfee2 --- /dev/null +++ b/soc/arm/silabs_sim3/sim3u/Kconfig.defconfig.series @@ -0,0 +1,23 @@ +# SIM3U series configuration options + +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +if SOC_SERIES_SIM3U + +config SOC_SERIES + default "sim3u" + +config SOC_PART_NUMBER + default "SIM3U167-A-GM" if SOC_PART_NUMBER_SIM3U167_A_GM + +config SYS_CLOCK_HW_CYCLES_PER_SEC + default $(dt_node_int_prop_int,/cpus/cpu@0,clock-frequency) + +config NUM_IRQS + int + # must be >= the highest interrupt number used + default 53 + +endif # SOC_SERIES_SIM3U diff --git a/soc/arm/silabs_sim3/sim3u/Kconfig.series b/soc/arm/silabs_sim3/sim3u/Kconfig.series new file mode 100644 index 00000000000000..5079c33bc63014 --- /dev/null +++ b/soc/arm/silabs_sim3/sim3u/Kconfig.series @@ -0,0 +1,15 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config SOC_SERIES_SIM3U + bool "SIM3U Series MCU" + select ARM + select CPU_CORTEX_M3 + select SOC_FAMILY_SIM3 + select CLOCK_CONTROL + select CPU_CORTEX_M_HAS_SYSTICK + select HAS_SILABS_SI32 + + help + Enable support for SiM3U MCU series diff --git a/soc/arm/silabs_sim3/sim3u/Kconfig.soc b/soc/arm/silabs_sim3/sim3u/Kconfig.soc new file mode 100644 index 00000000000000..19ca78e65cff21 --- /dev/null +++ b/soc/arm/silabs_sim3/sim3u/Kconfig.soc @@ -0,0 +1,3 @@ +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/soc/arm/silabs_sim3/sim3u/gen_crossbar_config.py b/soc/arm/silabs_sim3/sim3u/gen_crossbar_config.py new file mode 100755 index 00000000000000..848603db0abc01 --- /dev/null +++ b/soc/arm/silabs_sim3/sim3u/gen_crossbar_config.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +"""Generate crossbar register values from devicetree pinctrl nodes. + +The sim3u soc does support pinmuxing, but with many limimitations. +It's also non-optional so we can't just not support it and rely on default +values. + +The hardware doesn't allow us to properly implement the pinmux API, but +we still want to use the standard pinctrl nodes inside the devicetree to define +which peripheral should be muxed to which pins. To accomplish that, this script +parses these nodes and generates code that applies all `default` nodes of all +enabled devices within soc.c. + +There's two crossbars: +- crossbar0: which controls portbanks 0 and 1 +- crossbar1: which controls portbanks 2 and 3 + +Each crossbar has two configuration-values which reside in different registers: +- config: A bitmask which tells the crossbar which peripherals should be muxed. + Some peripherals have multiple bits to prevent having to mux unused + pins (like UART flow control). +- enable: A bit which enables or disables the whole crossbar. When disabled, + all pins of the crossbar are disconnected. +And each portbank has this related config: +- pbskipen: A bitmask where value `1` means, that the pin will not be muxed. + The index of the bit refers to the pin number. + +A crossbar has a list of signals that it tries to mux to pins that it controls. +That list has a fixed order and signals that belong to the same peripheral are +next to each other. The crossbar hardware simply iterates the signal list and +assigns each signal to the lowest available pin. That has a few implications: +- Pins of a peripheral are always consecutive within all non-skipped pins. +- Peripherals that appear first in the signal list, will always use lower + pin-numbers than later ones +- There's no way to change the muxing without side-effects. Writing to pbskipen + while the crossbar is enabled might temporarily cause unwanted muxing. + Disabling the crossbar will disconnect all peripherals for a short time. + +The last point is the reason why we don't implement zephyrs runtime pinmuxing +API. Applying the pinmuxing one by one as drivers get initialized would require +disconnecting pins quite often. +""" + +import sys +import pickle +import argparse +import os +import enum + +sys.path.insert( + 0, os.path.join(os.environ["ZEPHYR_BASE"], "scripts/dts/python-devicetree/src") +) + +from devicetree import edtlib + + +class Signal(enum.Enum): + USART0_TX = 0 + USART0_RX = 1 + USART0_RTS = 2 + USART0_CTS = 3 + USART0_UCLK = 4 + + USART1_TX = 9 + USART1_RX = 10 + USART1_RTS = 11 + USART1_CTS = 12 + USART1_UCLK = 13 + + SPI2_SCK = 50 + SPI2_MISO = 51 + SPI2_MOSI = 52 + SPI2_NSS = 53 + + +class PinMode(enum.Enum): + ANALOG = 0 + DIGITAL_INPUT = 1 + PUSH_PULL_OUTPUT = 2 + + +class Pinmux: + def __init__(self, value, props): + self.pin = (value & 0x7, (value >> 3) & 0xFF) + self.signal = Signal((value >> 22) & 0x7F) + + output_low = props["output-low"].val + output_high = props["output-high"].val + output_enable = props["output-enable"].val or output_low or output_high + + input_enable = props["input-enable"].val + + if output_enable and input_enable: + raise Exception("can't enable both output and input") + if output_low and output_high: + raise Exception("can't define output as both low and high") + + if input_enable: + self.pinmode = PinMode.DIGITAL_INPUT + elif output_enable: + self.pinmode = PinMode.PUSH_PULL_OUTPUT + + if output_low: + self.output_value = True + elif output_high: + self.output_value = False + else: + self.output_value = None + else: + self.pinmode = None + + def __repr__(self): + return self.__dict__.__repr__() + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument( + "edt_pickle", help="path to read the pickled edtlib.EDT object from" + ) + parser.add_argument("out", help="path to write the header file") + + return parser.parse_args() + + +class CrossbarBit: + def __init__(self, bit, signals, pin_first, pin_last): + assert pin_first <= pin_last + + self.bit = bit + self.signals = signals + self.pin_first = pin_first + self.pin_last = pin_last + + def __repr__(self): + return self.__dict__.__repr__() + + +class Crossbar: + def __init__(self, id, bits, portbanks): + self.id = id + self.value = 0 + self.bits = bits + self.first_configurable = (0, 0) + self.portbanks = portbanks + + def enable_bit(self, bit, pins): + if len(pins) != len(bit.signals): + raise Exception( + f"pins({pins}) and signals({bit.signals}) must be the same length" + ) + + for pin in pins: + if pin < self.first_configurable: + raise Exception( + "can't enable crossbar pin anymore", pin, self.first_configurable + ) + + self.portbanks[pin[0]].unskip(pin[1]) + + self.first_configurable = (pin[0], pin[1] + 1) + + self.value |= 1 << bit.bit + + def mux(self, muxs): + for bit in self.bits: + # collect the signals that are enabled by this bit + signal_muxs = {} + for mux in muxs: + if self.id == 0 and mux.pin[0] != 0 and mux.pin[0] != 1: + continue + if self.id == 1 and mux.pin[0] != 2 and mux.pin[0] != 3: + continue + + if mux.signal not in bit.signals: + continue + if mux.signal in signal_muxs: + raise Exception("duplicate signal", mux) + if mux.pin < bit.pin_first or mux.pin > bit.pin_last: + raise Exception("can't mux signal to pin", mux, bit) + + signal_muxs[mux.signal] = mux + + # this bit is disabled + if len(signal_muxs.keys()) == 0: + continue + # we have to enable all of the signals + if len(signal_muxs.keys()) != len(bit.signals): + raise Exception("missing signals for bit", bit, signal_muxs) + + # build pin list for this bit in the required order + pins = [] + for index, signal in enumerate(bit.signals): + mux = signal_muxs[signal] + pins.append(mux.pin) + + self.enable_bit(bit, pins) + + for mux in signal_muxs.values(): + self.portbanks[mux.pin[0]].apply_mux(mux.pin[1], mux) + + def __repr__(self): + return self.__dict__.__repr__() + + +class Portbank: + def __init__(self): + self.pins_high = 0 + self.pins_low = 0 + self.pins_push_pull_output = 0 + self.pins_digital_input = 0 + self.pins_analog = 0 + + # skip all pins by default + self.skip_enable = 0xFFFF + + def unskip(self, pin): + self.skip_enable &= ~(1 << pin) + + def apply_mux(self, pin, mux): + if mux.pinmode == PinMode.ANALOG: + self.pins_analog |= 1 << pin + elif mux.pinmode == PinMode.DIGITAL_INPUT: + self.pins_digital_input |= 1 << pin + elif mux.pinmode == PinMode.PUSH_PULL_OUTPUT: + self.pins_push_pull_output |= 1 << pin + + if mux.output_value == True: + self.pins_high |= 1 << pin + elif mux.output_value == False: + self.pins_low |= 1 << pin + elif mux.output_value == None: + pass + else: + raise Exception("unsupported output value", mux.output_value) + elif mux.pinmode == None: + pass + else: + raise Exception("unsupported pinmode", mux.pinmode) + + def __repr__(self): + return self.__dict__.__repr__() + + +def main(): + args = parse_args() + + with open(args.edt_pickle, "rb") as f: + edt = pickle.load(f) + + pinmux_table = [] + for node in edt.nodes: + if node.status != "okay": + continue + if not node.pinctrls: + continue + + pinctrl = None + for p in node.pinctrls: + if p.name != "default": + continue + + if pinctrl is not None: + raise Exception("multiple default nodes", node) + pinctrl = p + + if pinctrl is None: + raise Exception("no default node", node) + + for conf_node in pinctrl.conf_nodes: + for child in conf_node.children.values(): + for pin in child.props["pinmux"].val: + pinmux_table.append(Pinmux(pin, child.props)) + + crossbar0_bits = [ + CrossbarBit(0, [Signal.USART0_TX, Signal.USART0_RX], (0, 0), (1, 15)), + CrossbarBit(1, [Signal.USART0_RTS, Signal.USART0_CTS], (0, 0), (1, 15)), + CrossbarBit(2, [Signal.USART0_UCLK], (0, 0), (1, 15)), + CrossbarBit(5, [Signal.USART1_TX, Signal.USART1_RX], (0, 0), (1, 15)), + CrossbarBit(6, [Signal.USART1_RTS, Signal.USART1_CTS], (0, 0), (1, 15)), + CrossbarBit(7, [Signal.USART1_UCLK], (0, 0), (1, 15)), + CrossbarBit( + 32 + 3, + [Signal.SPI2_SCK, Signal.SPI2_MISO, Signal.SPI2_MOSI], + (0, 0), + (1, 15), + ), + CrossbarBit(32 + 4, [Signal.SPI2_NSS], (0, 0), (1, 15)), + ] + crossbar1_bits = [ + CrossbarBit( + 7, [Signal.SPI2_SCK, Signal.SPI2_MISO, Signal.SPI2_MOSI], (2, 6), (3, 11) + ), + CrossbarBit(8, [Signal.SPI2_NSS], (2, 6), (3, 11)), + ] + + portbanks = [Portbank(), Portbank(), Portbank(), Portbank()] + crossbars = [ + Crossbar(0, crossbar0_bits, portbanks), + Crossbar(1, crossbar1_bits, portbanks), + ] + + for crossbar in crossbars: + crossbar.mux(pinmux_table) + + with open(args.out, "w", encoding="utf-8") as f: + for index, crossbar in enumerate(crossbars): + print(f"#define CROSSBAR_{index}_CONFIG 0x{crossbar.value:08X}ULL", file=f) + + for index, portbank in enumerate(portbanks): + print( + f"#define PORTBANK_{index}_SKIPEN_VALUE 0x{portbank.skip_enable:04X}", + file=f, + ) + + print( + f"#define PORTBANK_{index}_PINS_HIGH 0x{portbank.pins_high:04X}", file=f + ) + print( + f"#define PORTBANK_{index}_PINS_LOW 0x{portbank.pins_low:04X}", file=f + ) + + print( + f"#define PORTBANK_{index}_PINS_DIGITAL_INPUT 0x{portbank.pins_digital_input:04X}", + file=f, + ) + print( + f"#define PORTBANK_{index}_PINS_PUSH_PULL_OUTPUT 0x{portbank.pins_push_pull_output:04X}", + file=f, + ) + print( + f"#define PORTBANK_{index}_PINS_ANALOG 0x{portbank.pins_analog:04X}", + file=f, + ) + + +if __name__ == "__main__": + main() diff --git a/soc/arm/silabs_sim3/sim3u/linker.ld b/soc/arm/silabs_sim3/sim3u/linker.ld new file mode 100644 index 00000000000000..219b03d3641531 --- /dev/null +++ b/soc/arm/silabs_sim3/sim3u/linker.ld @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include diff --git a/soc/arm/silabs_sim3/sim3u/soc.c b/soc/arm/silabs_sim3/sim3u/soc.c new file mode 100644 index 00000000000000..bdd9d3d4dc3a2f --- /dev/null +++ b/soc/arm/silabs_sim3/sim3u/soc.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static void gpio_init(void) +{ + /* Enable clocks that we need in any case */ + SI32_CLKCTRL_A_enable_apb_to_modules_0( + SI32_CLKCTRL_0, SI32_CLKCTRL_A_APBCLKG0_PB0 | SI32_CLKCTRL_A_APBCLKG0_FLASHCTRL0); + + /* Enable misc clocks. + * We may or may nor need all of that but it includes some basics like + * LOCK0, WDTIMER0 and DMA. Doing it here prevents the actual drivers + * needing to know whoelse needs one of these clocks. + */ + SI32_CLKCTRL_A_enable_apb_to_modules_1( + SI32_CLKCTRL_0, SI32_CLKCTRL_A_APBCLKG1_MISC0 | SI32_CLKCTRL_A_APBCLKG1_MISC1); + SI32_CLKCTRL_A_enable_ahb_to_dma_controller(SI32_CLKCTRL_0); + + /* Apply pinctrl gpio settings */ + SI32_PBSTD_A_write_pins_high(SI32_PBSTD_0, PORTBANK_0_PINS_HIGH); + SI32_PBSTD_A_write_pins_low(SI32_PBSTD_0, PORTBANK_0_PINS_LOW); + SI32_PBSTD_A_set_pins_digital_input(SI32_PBSTD_0, PORTBANK_0_PINS_DIGITAL_INPUT); + SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_0, PORTBANK_0_PINS_PUSH_PULL_OUTPUT); + SI32_PBSTD_A_set_pins_analog(SI32_PBSTD_0, PORTBANK_0_PINS_ANALOG); + + SI32_PBSTD_A_write_pins_high(SI32_PBSTD_1, PORTBANK_1_PINS_HIGH); + SI32_PBSTD_A_write_pins_low(SI32_PBSTD_1, PORTBANK_1_PINS_LOW); + SI32_PBSTD_A_set_pins_digital_input(SI32_PBSTD_1, PORTBANK_1_PINS_DIGITAL_INPUT); + SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_1, PORTBANK_1_PINS_PUSH_PULL_OUTPUT); + SI32_PBSTD_A_set_pins_analog(SI32_PBSTD_1, PORTBANK_1_PINS_ANALOG); + + SI32_PBSTD_A_write_pins_high(SI32_PBSTD_2, PORTBANK_2_PINS_HIGH); + SI32_PBSTD_A_write_pins_low(SI32_PBSTD_2, PORTBANK_2_PINS_LOW); + SI32_PBSTD_A_set_pins_digital_input(SI32_PBSTD_2, PORTBANK_2_PINS_DIGITAL_INPUT); + SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_2, PORTBANK_2_PINS_PUSH_PULL_OUTPUT); + SI32_PBSTD_A_set_pins_analog(SI32_PBSTD_2, PORTBANK_2_PINS_ANALOG); + + SI32_PBSTD_A_write_pins_high(SI32_PBSTD_3, PORTBANK_3_PINS_HIGH); + SI32_PBSTD_A_write_pins_low(SI32_PBSTD_3, PORTBANK_3_PINS_LOW); + SI32_PBSTD_A_set_pins_digital_input(SI32_PBSTD_3, PORTBANK_3_PINS_DIGITAL_INPUT); + SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_3, PORTBANK_3_PINS_PUSH_PULL_OUTPUT); + SI32_PBSTD_A_set_pins_analog(SI32_PBSTD_3, PORTBANK_3_PINS_ANALOG); + + /* Configure skips */ + SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_0, PORTBANK_0_SKIPEN_VALUE); + SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_1, PORTBANK_1_SKIPEN_VALUE); + SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_2, PORTBANK_2_SKIPEN_VALUE); + SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_3, PORTBANK_3_SKIPEN_VALUE); + + /* Configure crossbars */ + SI32_PBCFG_A_enable_xbar0l_peripherals(SI32_PBCFG_0, CROSSBAR_0_CONFIG & 0xFFFFFFFF); + SI32_PBCFG_A_enable_xbar0h_peripherals(SI32_PBCFG_0, + (CROSSBAR_0_CONFIG >> 32) & 0xFFFFFFFF); + SI32_PBCFG_A_enable_xbar1_peripherals(SI32_PBCFG_0, CROSSBAR_1_CONFIG & 0xFFFFFFFF); + + /* Enable crossbars */ + SI32_PBCFG_A_enable_crossbar_0(SI32_PBCFG_0); + SI32_PBCFG_A_enable_crossbar_1(SI32_PBCFG_0); +} + +static void vddlow_irq_handler(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* nothing to do here, we just don't want any spurious interrupts */ +} + +static void vmon_init(void) +{ + /* VMON must be enabled for flash write/erase support */ + + NVIC_ClearPendingIRQ(VDDLOW_IRQn); + + IRQ_CONNECT(VDDLOW_IRQn, 0, vddlow_irq_handler, NULL, 0); + irq_enable(VDDLOW_IRQn); + + SI32_VMON_A_enable_vdd_supply_monitor(SI32_VMON_0); + SI32_VMON_A_enable_vdd_low_interrupt(SI32_VMON_0); + SI32_VMON_A_select_vdd_standard_threshold(SI32_VMON_0); +} + +__no_optimization static void busy_delay(uint32_t cycles) +{ + while (cycles) { + cycles--; + } +} + +static int silabs_sim3u_init(void) +{ + uint32_t key; + + key = irq_lock(); + + /* The watchdog may be enabled already so we have to disable it */ + SI32_WDTIMER_A_reset_counter(SI32_WDTIMER_0); + SI32_WDTIMER_A_stop_counter(SI32_WDTIMER_0); + SI32_RSTSRC_A_disable_watchdog_timer_reset_source(SI32_RSTSRC_0); + + /* Since a hardware reset affects the debug hardware as well, this + * makes it easier to recover from a broken firmware */ + busy_delay(3000000); + + gpio_init(); + vmon_init(); + + /* Install default handler that simply resets the CPU + * if configured in the kernel, NOP otherwise + */ + NMI_INIT(); + + irq_unlock(key); + + return 0; +} + +SYS_INIT(silabs_sim3u_init, PRE_KERNEL_1, 0); diff --git a/soc/arm/silabs_sim3/sim3u/soc.h b/soc/arm/silabs_sim3/sim3u/soc.h new file mode 100644 index 00000000000000..0f61cb6dbb80fa --- /dev/null +++ b/soc/arm/silabs_sim3/sim3u/soc.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2023 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _SOC__H_ +#define _SOC__H_ + +#endif /* _SOC__H_ */ diff --git a/west.yml b/west.yml index ee7b8ff70fe2a0..ab30881c3cf738 100644 --- a/west.yml +++ b/west.yml @@ -126,7 +126,7 @@ manifest: groups: - hal - name: hal_silabs - revision: 249c08f16f5ee9663c8b225b68faf8ec54a21e8e + revision: 785f4ed3aadb57d1b694be48c6f06fd5617df609 path: modules/hal/silabs groups: - hal