From b67aff915d68a9210d701a28bfa4ab35d892628c Mon Sep 17 00:00:00 2001 From: Sven Ginka Date: Tue, 10 Dec 2024 15:29:07 +0100 Subject: [PATCH 01/18] [nrf fromtree] soc: sensry: add pinctrl Add pin control support for the sy1xx soc. Signed-off-by: Adam Kondraciuk (cherry picked from commit 804e3f649762f56cd565729623c38aa3652dcb7e) Signed-off-by: Sven Ginka --- drivers/pinctrl/CMakeLists.txt | 1 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Kconfig.sy1xx | 9 +++ drivers/pinctrl/pinctrl_sy1xx.c | 59 +++++++++++++++ .../pinctrl/sensry,sy1xx-pinctrl.yaml | 74 +++++++++++++++++++ .../dt-bindings/pinctrl/sy1xx-pinctrl.h | 70 ++++++++++++++++++ soc/sensry/ganymed/sy1xx/common/pinctrl_soc.h | 72 ++++++++++++++++++ 7 files changed, 286 insertions(+) create mode 100644 drivers/pinctrl/Kconfig.sy1xx create mode 100644 drivers/pinctrl/pinctrl_sy1xx.c create mode 100644 dts/bindings/pinctrl/sensry,sy1xx-pinctrl.yaml create mode 100644 include/zephyr/dt-bindings/pinctrl/sy1xx-pinctrl.h create mode 100644 soc/sensry/ganymed/sy1xx/common/pinctrl_soc.h diff --git a/drivers/pinctrl/CMakeLists.txt b/drivers/pinctrl/CMakeLists.txt index d2834504eb2..0a27a2d4786 100644 --- a/drivers/pinctrl/CMakeLists.txt +++ b/drivers/pinctrl/CMakeLists.txt @@ -42,5 +42,6 @@ zephyr_library_sources_ifdef(CONFIG_PINCTRL_MAX32 pinctrl_max32.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_IMX_SCMI pinctrl_imx_scmi.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_MCHP_MEC5 pinctrl_mchp_mec5.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_WCH_AFIO pinctrl_wch_afio.c) +zephyr_library_sources_ifdef(CONFIG_PINCTRL_SY1XX pinctrl_sy1xx.c) add_subdirectory(renesas) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index d8621e7ad9e..d5872387090 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -69,6 +69,7 @@ source "drivers/pinctrl/Kconfig.zynqmp" source "drivers/pinctrl/Kconfig.max32" source "drivers/pinctrl/Kconfig.mec5" source "drivers/pinctrl/Kconfig.wch_afio" +source "drivers/pinctrl/Kconfig.sy1xx" rsource "renesas/Kconfig" diff --git a/drivers/pinctrl/Kconfig.sy1xx b/drivers/pinctrl/Kconfig.sy1xx new file mode 100644 index 00000000000..857920bf9d8 --- /dev/null +++ b/drivers/pinctrl/Kconfig.sy1xx @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2024 sensry.io + +config PINCTRL_SY1XX + bool "Sensry sy1xx pin controller driver" + default y + depends on DT_HAS_SENSRY_SY1XX_PINCTRL_ENABLED + help + Sensry pin controller driver is used on sy1xx SoC series diff --git a/drivers/pinctrl/pinctrl_sy1xx.c b/drivers/pinctrl/pinctrl_sy1xx.c new file mode 100644 index 00000000000..1343ba2c987 --- /dev/null +++ b/drivers/pinctrl/pinctrl_sy1xx.c @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 sensry.io + */ + +#define DT_DRV_COMPAT sensry_sy1xx_pinctrl + +#include +#include +#include + +#include + +static uint32_t pinctrl0_base_addr = DT_REG_ADDR(DT_NODELABEL(pinctrl)); +static uint32_t pinctrl0_base_mask = DT_REG_SIZE(DT_NODELABEL(pinctrl)) - 1; + +/** + * @brief Configure a pin. + * + * @param pin The pin to configure. + */ +static int pinctrl_configure_pin(const pinctrl_soc_pin_t *pin) +{ + uint32_t addr = (pin->addr & pinctrl0_base_mask) | pinctrl0_base_addr; + + switch (pin->iro) { + case 0: + case 8: + case 16: + case 24: + /* fall through */ + break; + default: + /* invalid inter address offset */ + return -EINVAL; + } + + uint32_t reg = ~(0xFFUL << pin->iro) & sys_read32(addr); + + reg |= FIELD_PREP((0xFFUL << pin->iro), pin->cfg); + sys_write32(reg, addr); + + return 0; +} + +int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintptr_t reg) +{ + ARG_UNUSED(reg); + + for (uint8_t i = 0U; i < pin_cnt; i++) { + int ret = pinctrl_configure_pin(pins++); + + if (ret < 0) { + return ret; + } + } + + return 0; +} diff --git a/dts/bindings/pinctrl/sensry,sy1xx-pinctrl.yaml b/dts/bindings/pinctrl/sensry,sy1xx-pinctrl.yaml new file mode 100644 index 00000000000..776f561f4c4 --- /dev/null +++ b/dts/bindings/pinctrl/sensry,sy1xx-pinctrl.yaml @@ -0,0 +1,74 @@ +# Copyright (c) 2024 sensry.io +# SPDX-License-Identifier: Apache-2.0 + +description: | + The sensry SY1xx pin controller is a single node responsible for controlling + pin configuration, such as pull-up, pull-down, tri-state, ... + + The node has the 'pinctrl0' node label set in your SoC's devicetree, + so you can modify it like this: + + &pinctrl0 { + /* your modifications go here */ + }; + + For example: + &pinctrl0 { + + /* UART0 */ + uart0_tx: uart0_tx { + pinmux = ; + }; + + uart0_rx: uart0_rx { + pinmux = ; + input-enable; + }; + } + + Then define the uart: + &uart0 { + pinctrl-0 = <&uart0_tx &uart0_rx>; + pinctrl-names = "default"; + }; + + Every pin configuration will be configured in a 32bit register. The configuration + itself is 8bit wide. So we have a number of 4 pin configurations per 32bit register. + + The pinmux describes the registers address and the offset [0, 8, 16, 24] for + the individual configuration. + + Allowed modifiers for each pin are: + - bias-high-impedance + - bias-pull-down + - bias-pull-up + - input-enable + - input-schmitt-enable + +compatible: "sensry,sy1xx-pinctrl" + +include: base.yaml + +properties: + reg: + required: true + +child-binding: + description: Each child node defines the configuration of a particular state. + + include: + - name: pincfg-node.yaml + property-allowlist: + - bias-high-impedance + - bias-pull-down + - bias-pull-up + - input-enable + - input-schmitt-enable + + properties: + pinmux: + required: true + type: array + description: | + Pin mux selection. See the SOC level pinctrl header + for a defined list of these options. diff --git a/include/zephyr/dt-bindings/pinctrl/sy1xx-pinctrl.h b/include/zephyr/dt-bindings/pinctrl/sy1xx-pinctrl.h new file mode 100644 index 00000000000..deee135bd60 --- /dev/null +++ b/include/zephyr/dt-bindings/pinctrl/sy1xx-pinctrl.h @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 sensry.io + */ + +#ifndef _ZEPHYR_DT_BINDINGS_PINCTRL_SY1XX_PINCTRL_ +#define _ZEPHYR_DT_BINDINGS_PINCTRL_SY1XX_PINCTRL_ + +#define SY1XX_PAD(pad) (pad * 8) + +#define SY1XX_UART0_PAD_CFG0 0x0020 +#define SY1XX_UART1_PAD_CFG0 0x0024 +#define SY1XX_UART2_PAD_CFG0 0x0028 + +#define SY1XX_SPI0_PAD_CFG0 0x002c +#define SY1XX_SPI0_PAD_CFG1 0x0030 + +#define SY1XX_SPI1_PAD_CFG0 0x0034 +#define SY1XX_SPI1_PAD_CFG1 0x0038 + +#define SY1XX_SPI2_PAD_CFG0 0x003c +#define SY1XX_SPI2_PAD_CFG1 0x0040 + +#define SY1XX_SPI3_PAD_CFG0 0x0044 +#define SY1XX_SPI3_PAD_CFG1 0x0048 + +#define SY1XX_SPI4_PAD_CFG0 0x004c +#define SY1XX_SPI4_PAD_CFG1 0x0050 + +#define SY1XX_SPI5_PAD_CFG0 0x0054 +#define SY1XX_SPI5_PAD_CFG1 0x0058 + +#define SY1XX_SPI6_PAD_CFG0 0x005c +#define SY1XX_SPI6_PAD_CFG1 0x0060 + +#define SY1XX_I2C0_PAD_CFG0 0x0100 +#define SY1XX_I2C1_PAD_CFG0 0x0104 +#define SY1XX_I2C2_PAD_CFG0 0x0108 +#define SY1XX_I2C3_PAD_CFG0 0x010c + +#define SY1XX_GPIO0_PAD_CFG0 0x0110 +#define SY1XX_GPIO0_PAD_CFG1 0x0114 +#define SY1XX_GPIO0_PAD_CFG2 0x0118 +#define SY1XX_GPIO0_PAD_CFG3 0x011c +#define SY1XX_GPIO0_PAD_CFG4 0x0120 +#define SY1XX_GPIO0_PAD_CFG5 0x0124 +#define SY1XX_GPIO0_PAD_CFG6 0x0128 +#define SY1XX_GPIO0_PAD_CFG7 0x012c + +#define SY1XX_RGMII0_PAD_CFG0 0x0130 +#define SY1XX_RGMII0_PAD_CFG1 0x0134 +#define SY1XX_RGMII0_PAD_CFG2 0x0138 +#define SY1XX_RGMII0_PAD_CFG3 0x013c + +#define SY1XX_CAN0_PAD_CFG0 0x0140 + +#define SY1XX_I2S0_PAD_CFG0 0x0144 +#define SY1XX_I2S1_PAD_CFG0 0x0148 +#define SY1XX_I2S2_PAD_CFG0 0x014c +#define SY1XX_I2S3_PAD_CFG0 0x0150 + +#define SY1XX_HBUS0_PAD_CFG0 0x0154 +#define SY1XX_HBUS0_PAD_CFG1 0x0158 +#define SY1XX_HBUS0_PAD_CFG2 0x015c +#define SY1XX_HBUS0_PAD_CFG3 0x0160 + +#define SY1XX_QSPI0_PAD_CFG0 0x0164 +#define SY1XX_QSPI0_PAD_CFG1 0x0168 + +#endif /* _ZEPHYR_DT_BINDINGS_PINCTRL_SY1XX_PINCTRL_ */ diff --git a/soc/sensry/ganymed/sy1xx/common/pinctrl_soc.h b/soc/sensry/ganymed/sy1xx/common/pinctrl_soc.h new file mode 100644 index 00000000000..5a124fa0783 --- /dev/null +++ b/soc/sensry/ganymed/sy1xx/common/pinctrl_soc.h @@ -0,0 +1,72 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 sensry.io + */ + +#ifndef GANYMED_SY1XX_PINCTRL_SOC_H +#define GANYMED_SY1XX_PINCTRL_SOC_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SY1XX_SCHMITT_ENABLE 1U +#define SY1XX_PULL_UP_ENABLE 1U +#define SY1XX_PULL_DOWN_ENABLE 1U +#define SY1XX_TRISTATE_ENABLE 1U +#define SY1XX_OUTPUT_ENABLE 1U + +#define SY1XX_PAD_SCHMITT_OFFS 7 +#define SY1XX_PAD_PULL_UP_OFFS 5 +#define SY1XX_PAD_PULL_DOWN_OFFS 4 +#define SY1XX_PAD_DRIVE_OFFS 2 +#define SY1XX_PAD_TRISTATE_OFFS 1 +#define SY1XX_PAD_DIR_OFFS 0 + +/** Type for SY1XX pin. */ +typedef struct { + /** address of pin config register */ + uint32_t addr; + /** intra register offset (8bit cfg per pin) */ + uint32_t iro; + /** config for pin (8bit), describes pull-up/down, ... */ + uint32_t cfg; +} pinctrl_soc_pin_t; + +#define Z_PINCTRL_CFG(node) \ + ( \ + \ + (SY1XX_SCHMITT_ENABLE * DT_PROP(node, input_schmitt_enable)) \ + << SY1XX_PAD_SCHMITT_OFFS | \ + (SY1XX_PULL_UP_ENABLE * DT_PROP(node, bias_pull_up)) << SY1XX_PAD_PULL_UP_OFFS | \ + (SY1XX_PULL_DOWN_ENABLE * DT_PROP(node, bias_pull_down)) \ + << SY1XX_PAD_PULL_DOWN_OFFS | \ + (SY1XX_TRISTATE_ENABLE * DT_PROP(node, bias_high_impedance)) \ + << SY1XX_PAD_TRISTATE_OFFS | \ + (SY1XX_OUTPUT_ENABLE & (1 - DT_PROP(node, input_enable))) << SY1XX_PAD_DIR_OFFS \ + \ + ) + +#define Z_PINCTRL_STATE_PIN_INIT(node, pr, idx) \ + { \ + \ + .addr = DT_PROP_BY_IDX(DT_PHANDLE_BY_IDX(node, pr, idx), pinmux, 0), \ + .iro = DT_PROP_BY_IDX(DT_PHANDLE_BY_IDX(node, pr, idx), pinmux, 1), \ + .cfg = Z_PINCTRL_CFG(DT_PHANDLE_BY_IDX(node, pr, idx)) \ + \ + }, + +#define Z_PINCTRL_STATE_PINS_INIT(node_id, prop) \ + { \ + DT_FOREACH_PROP_ELEM(node_id, prop, Z_PINCTRL_STATE_PIN_INIT) \ + } + +#ifdef __cplusplus +} +#endif + +#endif /* GANYMED_SY1XX_PINCTRL_SOC_H */ From 037e9a86450dcc6420ffaad5a367b9e21cf7909f Mon Sep 17 00:00:00 2001 From: Sven Ginka Date: Tue, 10 Dec 2024 16:15:21 +0100 Subject: [PATCH 02/18] [nrf fromtree] dts: sensry: add pinctrl Add pin ctrl to the sy1xx device tree. Signed-off-by: Adam Kondraciuk (cherry picked from commit fb53ea024a2fd7a7c49c799aca67b79eeb670c8e) Signed-off-by: Sven Ginka --- dts/riscv/sensry/ganymed-sy1xx.dtsi | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/dts/riscv/sensry/ganymed-sy1xx.dtsi b/dts/riscv/sensry/ganymed-sy1xx.dtsi index ad2df617b28..5e652c56a15 100644 --- a/dts/riscv/sensry/ganymed-sy1xx.dtsi +++ b/dts/riscv/sensry/ganymed-sy1xx.dtsi @@ -38,18 +38,18 @@ soc { #address-cells = <1>; - #size-cells = <0>; + #size-cells = <1>; event0: interrupt-controller@1000 { compatible = "sensry,sy1xx-event-unit"; - reg = <0x1000>; + reg = <0x1000 0x40>; interrupt-controller; #interrupt-cells = <1>; }; systick: timer@1a10b040 { compatible = "sensry,sy1xx-sys-timer"; - reg = <0x1a10b040>; + reg = <0x1a10b040 0x04>; interrupt-parent = <&event0>; interrupts = <10 0>; ticks_us = <1000>; @@ -57,7 +57,7 @@ timer1: timer@1a10b044 { compatible = "sensry,sy1xx-sys-timer"; - reg = <0x1a10b044>; + reg = <0x1a10b044 0x04>; interrupt-parent = <&event0>; interrupts = <11 0>; ticks_us = <1000>; @@ -65,7 +65,7 @@ uart0: uart@1a102000 { compatible = "sensry,sy1xx-uart"; - reg = <0x1a102000>; + reg = <0x1a102000 0x80>; instance = <0>; current-speed = <1000000>; status = "okay"; @@ -73,7 +73,7 @@ uart1: uart@1a102080 { compatible = "sensry,sy1xx-uart"; - reg = <0x1a102080>; + reg = <0x1a102080 0x80>; instance = <1>; current-speed = <1000000>; status = "okay"; @@ -81,11 +81,17 @@ uart2: uart@1a102100 { compatible = "sensry,sy1xx-uart"; - reg = <0x1a102100>; + reg = <0x1a102100 0x80>; instance = <2>; current-speed = <1000000>; status = "okay"; }; + pinctrl: pinctrl@1a104000 { + compatible = "sensry,sy1xx-pinctrl"; + reg = <0x1a104000 0x1000>; + status = "okay"; + }; + }; }; From 29ccc34e1e216d1aebc006f328b2ca32d6ec2ad0 Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Fri, 22 Nov 2024 16:54:13 +0800 Subject: [PATCH 03/18] [nrf fromtree] soc: realrek: ec: Add Realtek RTS5912 SoC Add support for Realtek RTS5912 embedded controller (EC). Signed-off-by: Adam Kondraciuk (cherry picked from commit b83501e6cc8932950c4bd4803ad6f40dda72faf1) Signed-off-by: Lin Yu-Cheng --- soc/realtek/ec/CMakeLists.txt | 7 + soc/realtek/ec/Kconfig | 34 ++++ soc/realtek/ec/Kconfig.defconfig | 10 ++ soc/realtek/ec/Kconfig.soc | 13 ++ soc/realtek/ec/common/CMakeLists.txt | 18 ++ .../ec/common/rts5912_imgtool/img_gen.py | 161 ++++++++++++++++++ soc/realtek/ec/rts5912/CMakeLists.txt | 15 ++ soc/realtek/ec/rts5912/Kconfig | 25 +++ .../ec/rts5912/Kconfig.defconfig.rts5912 | 24 +++ .../ec/rts5912/Kconfig.defconfig.series | 13 ++ soc/realtek/ec/rts5912/Kconfig.soc | 20 +++ soc/realtek/ec/rts5912/device_power.c | 25 +++ soc/realtek/ec/rts5912/device_power.h | 14 ++ soc/realtek/ec/rts5912/power.c | 88 ++++++++++ soc/realtek/ec/rts5912/soc.c | 28 +++ soc/realtek/ec/rts5912/soc.h | 13 ++ soc/realtek/ec/soc.yml | 2 + 17 files changed, 510 insertions(+) create mode 100644 soc/realtek/ec/CMakeLists.txt create mode 100644 soc/realtek/ec/Kconfig create mode 100644 soc/realtek/ec/Kconfig.defconfig create mode 100644 soc/realtek/ec/Kconfig.soc create mode 100644 soc/realtek/ec/common/CMakeLists.txt create mode 100644 soc/realtek/ec/common/rts5912_imgtool/img_gen.py create mode 100644 soc/realtek/ec/rts5912/CMakeLists.txt create mode 100644 soc/realtek/ec/rts5912/Kconfig create mode 100644 soc/realtek/ec/rts5912/Kconfig.defconfig.rts5912 create mode 100644 soc/realtek/ec/rts5912/Kconfig.defconfig.series create mode 100644 soc/realtek/ec/rts5912/Kconfig.soc create mode 100644 soc/realtek/ec/rts5912/device_power.c create mode 100644 soc/realtek/ec/rts5912/device_power.h create mode 100644 soc/realtek/ec/rts5912/power.c create mode 100644 soc/realtek/ec/rts5912/soc.c create mode 100644 soc/realtek/ec/rts5912/soc.h create mode 100644 soc/realtek/ec/soc.yml diff --git a/soc/realtek/ec/CMakeLists.txt b/soc/realtek/ec/CMakeLists.txt new file mode 100644 index 00000000000..9953afd14f4 --- /dev/null +++ b/soc/realtek/ec/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +add_subdirectory(${SOC_SERIES}) +add_subdirectory(common) diff --git a/soc/realtek/ec/Kconfig b/soc/realtek/ec/Kconfig new file mode 100644 index 00000000000..a759cb44c37 --- /dev/null +++ b/soc/realtek/ec/Kconfig @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +if SOC_FAMILY_REALTEK_EC + +menuconfig REALTEK_RTS5912_BOOTROM_HEADER + bool "Create BOOTROM header for RTS5912" + help + The RTS5912 BOOTROM needs the information about boot up progress. + Invoke the 'rts5912_imgtool' to generates the RTS5912 bootrom header. + +if REALTEK_RTS5912_BOOTROM_HEADER + +config REALTEK_HEADER_CHIP + string + default "RTS5912" if SOC_RTS5912 + +DT_CHOSEN_Z_FLASH := zephyr,flash +FLASH_BASE := $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_FLASH)) + +config REALTEK_RTS5912_BOOTROM_HEADER_LOAD_ADDRESS + string "Set the address for BOOTROM" + default "$(FLASH_BASE) - 0x20" + help + This address will be used by the RTS5912 BOOTROM to decide where firmware image start. + +endif # REALTEK_RTS5912_BOOTROM_HEADER + +# Select SoC Part No. and configuration options +rsource "*/Kconfig" + +endif # SOC_FAMILY_REALTEK_EC diff --git a/soc/realtek/ec/Kconfig.defconfig b/soc/realtek/ec/Kconfig.defconfig new file mode 100644 index 00000000000..effe686675a --- /dev/null +++ b/soc/realtek/ec/Kconfig.defconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +if SOC_FAMILY_REALTEK_EC + +rsource "*/Kconfig.defconfig.series" + +endif # SOC_FAMILY_REALTEK_EC diff --git a/soc/realtek/ec/Kconfig.soc b/soc/realtek/ec/Kconfig.soc new file mode 100644 index 00000000000..fd19689cb07 --- /dev/null +++ b/soc/realtek/ec/Kconfig.soc @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +config SOC_FAMILY_REALTEK_EC + bool + +config SOC_FAMILY + string + default "realtek_ec" if SOC_FAMILY_REALTEK_EC + +rsource "*/Kconfig.soc" diff --git a/soc/realtek/ec/common/CMakeLists.txt b/soc/realtek/ec/common/CMakeLists.txt new file mode 100644 index 00000000000..de4a266ee39 --- /dev/null +++ b/soc/realtek/ec/common/CMakeLists.txt @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +zephyr_include_directories(.) + +if (DEFINED CONFIG_REALTEK_RTS5912_BOOTROM_HEADER) + math(EXPR adjustment "${CONFIG_REALTEK_RTS5912_BOOTROM_HEADER_LOAD_ADDRESS}" OUTPUT_FORMAT DECIMAL) + set(RTS5912_BIN_NAME ${CONFIG_KERNEL_BIN_NAME}.rts5912.bin) + set_property(GLOBAL APPEND PROPERTY extra_post_build_commands + COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/soc/realtek/ec/common/rts5912_imgtool/img_gen.py + -L ${adjustment} + -I ${KERNEL_BIN_NAME} + -O ${RTS5912_BIN_NAME} + -V + ) +endif() diff --git a/soc/realtek/ec/common/rts5912_imgtool/img_gen.py b/soc/realtek/ec/common/rts5912_imgtool/img_gen.py new file mode 100644 index 00000000000..53fcefb916e --- /dev/null +++ b/soc/realtek/ec/common/rts5912_imgtool/img_gen.py @@ -0,0 +1,161 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# Author: Dylan Hsieh + +""" +The RTS5912 specific image header shows the bootROM how to +load the image from flash to internal SRAM, this script obtains +the header to original BIN and output a new BIN file. +""" + +import argparse +import os +import struct + +IMAGE_MAGIC = 0x524C544B # ASCII 'RLTK' +IMAGE_HDR_SIZE = 32 +RAM_LOAD = 0x0000020 +FREQ_50M = 0 +FREQ_25M = 1 +FREQ_12P5M = 2 +FREQ_6P25M = 3 +NORMAL_read = "0x03" +DUAL_read = "0x3B" +QUAD_read = "0x6B" + + +def parse_args(): + """ + Parsing the arguments + """ + parser = argparse.ArgumentParser(allow_abbrev=False) + + parser.add_argument( + "-L", + "--load-addr", + type=int, + dest="load_addr", + help="Load address for image when it should run from RAM.", + ) + + parser.add_argument( + "-I", + "--input", + type=str, + dest="original_bin", + default="zephyr.bin", + help="Input bin file path", + ) + + parser.add_argument( + "-O", + "--output", + type=str, + dest="signed_bin", + default="zephyr.rts5912.bin", + help="Output bin file path", + ) + + parser.add_argument( + "-F", + "--spi-freq", + type=int, + dest="spi_freq", + choices=[FREQ_50M, FREQ_25M, FREQ_12P5M, FREQ_6P25M], + default=FREQ_6P25M, + help="Specify the frequency of SPI I/F.", + ) + + parser.add_argument( + "-R", + "--spi-rdcmd", + type=str, + dest="spi_rdcmd", + choices=[NORMAL_read, DUAL_read, QUAD_read], + default=NORMAL_read, + help="Specify the command for flash read.", + ) + + parser.add_argument("-V", "--verbose", action="count", default=0, help="Verbose Output") + + ret_args = parser.parse_args() + return ret_args + + +def img_gen(load_addr, spi_freq, spi_rdcmd, original_bin, signed_bin): + """ + To obtain the RTS5912 image header and output a new BIN file + """ + img_size = os.path.getsize(original_bin) + payload = bytearray(0) + img_size = os.path.getsize(original_bin) + + fmt = ( + "<" + + + # type ImageHdr struct { + "I" # Magic uint32 + + "I" # LoadAddr uint32 + + "H" # HdrSz uint16 + + "H" # reserved uint16 + + "I" # ImgSz uint32 + + "I" # Flags uint32 + + "I" # reserved uint32 + + "I" # reserved uint32 + + "B" # SpiFmt uint8 + + "B" # SpiRdCmd uint8 + + "H" # reserved uint16 + ) # } + + header = struct.pack( + fmt, + IMAGE_MAGIC, + load_addr, + IMAGE_HDR_SIZE, + 0, + img_size, + RAM_LOAD, + 0, + 0, + 0 + (int(spi_freq) << 2), + int(spi_rdcmd, 0), + 0, + ) + + payload[: len(header)] = header + + with open(signed_bin, "wb") as signed: + signed.write(payload) + signed.flush() + signed.close() + + with open(signed_bin, "ab") as signed, open(original_bin, "rb") as original: + signed.write(original.read()) + signed.flush() + signed.close() + original.close() + + +def main(): + """ + Image generateor tool entry point + """ + args = parse_args() + if args.verbose: + print(f" Input = {args.original_bin}") + print(f" Output = {args.signed_bin}") + print(f" Load Address = {hex(args.load_addr)}") + print(f" SPI Frequency = {args.spi_freq}") + print(f" SPI Read Command = {args.spi_rdcmd}") + img_gen( + args.load_addr, + args.spi_freq, + args.spi_rdcmd, + args.original_bin, + args.signed_bin, + ) + + +if __name__ == "__main__": + main() diff --git a/soc/realtek/ec/rts5912/CMakeLists.txt b/soc/realtek/ec/rts5912/CMakeLists.txt new file mode 100644 index 00000000000..335d40578ba --- /dev/null +++ b/soc/realtek/ec/rts5912/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +zephyr_include_directories(${ZEPHYR_BASE}/drivers) +zephyr_sources(soc.c) +zephyr_include_directories(.) + +zephyr_sources_ifdef(CONFIG_PM + device_power.c + power.c + ) + +set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm/cortex_m/scripts/linker.ld CACHE INTERNAL "") diff --git a/soc/realtek/ec/rts5912/Kconfig b/soc/realtek/ec/rts5912/Kconfig new file mode 100644 index 00000000000..aebac7b3d00 --- /dev/null +++ b/soc/realtek/ec/rts5912/Kconfig @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +config SOC_SERIES_RTS5912 + select ARM + select CPU_CORTEX_M33 + select SYS_CLOCK_EXISTS + select DYNAMIC_INTERRUPTS + select SOC_EARLY_INIT_HOOK + +if SOC_SERIES_RTS5912 + +config RTS5912_ON_ENTER_CPU_IDLE_HOOK + bool "CPU idle hook enable" + default y + imply ARM_ON_ENTER_CPU_IDLE_HOOK + help + Enables a hook (z_arm_on_enter_cpu_idle()) that is called when + the CPU is made idle (by k_cpu_idle() or k_cpu_atomic_idle()). + If needed, this hook can be used to prevent the CPU from actually + entering sleep by skipping the WFE/WFI instruction. + +endif # SOC_SERIES_RTS5912 diff --git a/soc/realtek/ec/rts5912/Kconfig.defconfig.rts5912 b/soc/realtek/ec/rts5912/Kconfig.defconfig.rts5912 new file mode 100644 index 00000000000..5ff22637497 --- /dev/null +++ b/soc/realtek/ec/rts5912/Kconfig.defconfig.rts5912 @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +if SOC_RTS5912 + +if REALTEK_RTS5912_RTMR + +config PM + default y + +config SYS_CLOCK_HW_CYCLES_PER_SEC + default 32768 + +config SYS_CLOCK_TICKS_PER_SEC + default 32768 + +config ARCH_HAS_CUSTOM_BUSY_WAIT + default y + +endif # REALTEK_RTS5912_RTMR + +endif # SOC_RTS5912 diff --git a/soc/realtek/ec/rts5912/Kconfig.defconfig.series b/soc/realtek/ec/rts5912/Kconfig.defconfig.series new file mode 100644 index 00000000000..429dbd9c788 --- /dev/null +++ b/soc/realtek/ec/rts5912/Kconfig.defconfig.series @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +if SOC_SERIES_RTS5912 + +config NUM_IRQS + default 240 + +rsource "Kconfig.defconfig.rts5912" + +endif # SOC_SERIES_RTS5912 diff --git a/soc/realtek/ec/rts5912/Kconfig.soc b/soc/realtek/ec/rts5912/Kconfig.soc new file mode 100644 index 00000000000..cf86fe3bcb7 --- /dev/null +++ b/soc/realtek/ec/rts5912/Kconfig.soc @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +config SOC_SERIES_RTS5912 + bool + select SOC_FAMILY_REALTEK_EC + help + Enable support for REALTEK EC MCU series + +config SOC_SERIES + default "rts5912" if SOC_SERIES_RTS5912 + +config SOC_RTS5912 + bool + select SOC_SERIES_RTS5912 + +config SOC + default "rts5912" if SOC_RTS5912 diff --git a/soc/realtek/ec/rts5912/device_power.c b/soc/realtek/ec/rts5912/device_power.c new file mode 100644 index 00000000000..b346cafd156 --- /dev/null +++ b/soc/realtek/ec/rts5912/device_power.c @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#include +#include + +#include +#include "device_power.h" + +void before_rts5912_sleep(void) +{ + __disable_irq(); + __set_BASEPRI(0); + __ISB(); +} + +void after_rts5912_sleep(void) +{ + __enable_irq(); + __ISB(); +} diff --git a/soc/realtek/ec/rts5912/device_power.h b/soc/realtek/ec/rts5912/device_power.h new file mode 100644 index 00000000000..6a2817fa79f --- /dev/null +++ b/soc/realtek/ec/rts5912/device_power.h @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#ifndef ZEPHYR_SOC_REALTEK_RTS5912_DEVICE_POWER_H +#define ZEPHYR_SOC_REALTEK_RTS5912_DEVICE_POWER_H + +void before_rts5912_sleep(void); +void after_rts5912_sleep(void); + +#endif /* ZEPHYR_SOC_REALTEK_RTS5912_DEVICE_POWER_H */ diff --git a/soc/realtek/ec/rts5912/power.c b/soc/realtek/ec/rts5912/power.c new file mode 100644 index 00000000000..d0702bc4ada --- /dev/null +++ b/soc/realtek/ec/rts5912/power.c @@ -0,0 +1,88 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#include +#include + +#include +#include "device_power.h" + +#define RTS5912_SCCON_REG_BASE ((SYSTEM_Type *)(DT_REG_ADDR(DT_NODELABEL(sccon)))) + +static void realtek_WFI(void) +{ + __DSB(); + __WFI(); +} + +static void rts5912_light_sleep(void) +{ + SYSTEM_Type *sys_reg = RTS5912_SCCON_REG_BASE; + + before_rts5912_sleep(); + + sys_reg->SLPCTRL &= ~SYSTEM_SLPCTRL_SLPMDSEL_Msk; + + realtek_WFI(); + + after_rts5912_sleep(); +} + +static void rts5912_heavy_sleep(void) +{ + SYSTEM_Type *sys_reg = RTS5912_SCCON_REG_BASE; + int main_clk_src_record = sys_reg->SYSCLK; + int PLL_en_record = sys_reg->PLLCTRL; + + before_rts5912_sleep(); + + if ((main_clk_src_record & SYSTEM_SYSCLK_SRC_Msk) == 0x0) { + if ((PLL_en_record & SYSTEM_PLLCTRL_EN_Msk) == 0x0) { + sys_reg->PLLCTRL |= SYSTEM_PLLCTRL_EN_Msk; /* Force to enable PLL */ + while ((sys_reg->PLLCTRL & SYSTEM_PLLCTRL_RDY_Msk) == 0x00) { + ; /* Wait until PLL is ready */ + } + } + sys_reg->SYSCLK |= SYSTEM_SYSCLK_SRC_Msk; /* Switch system clock to PLL */ + } + + sys_reg->SLPCTRL |= SYSTEM_SLPCTRL_SLPMDSEL_Msk; /* Heavy Sleep mode */ + + realtek_WFI(); + + if ((main_clk_src_record & SYSTEM_SYSCLK_SRC_Msk) == 0) { + sys_reg->SYSCLK &= ~SYSTEM_SYSCLK_SRC_Msk; /* Return system clock to 25M */ + if ((PLL_en_record & SYSTEM_PLLCTRL_EN_Msk) == 0x0) { + sys_reg->PLLCTRL &= ~SYSTEM_PLLCTRL_EN_Msk; /* Disable PLL */ + } + } + + after_rts5912_sleep(); +} + +void pm_state_set(enum pm_state state, uint8_t substate_id) +{ + + switch (state) { + case PM_STATE_SUSPEND_TO_IDLE: + rts5912_light_sleep(); + break; + case PM_STATE_SUSPEND_TO_RAM: + rts5912_heavy_sleep(); + break; + default: + break; + } +} + +void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id) +{ + ARG_UNUSED(state); + ARG_UNUSED(substate_id); + + irq_unlock(0); +} diff --git a/soc/realtek/ec/rts5912/soc.c b/soc/realtek/ec/rts5912/soc.c new file mode 100644 index 00000000000..adb28d75864 --- /dev/null +++ b/soc/realtek/ec/rts5912/soc.c @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng / Titan Chen + */ + +#include +#include +#include + +#if defined(CONFIG_RTS5912_ON_ENTER_CPU_IDLE_HOOK) +bool z_arm_on_enter_cpu_idle(void) +{ + /* Returning false prevent device goes to sleep mode */ + return false; +} +#endif + +/** + * @brief Perform basic hardware initialization at boot. + * + * This needs to be run from the very beginning. + */ +void soc_early_init_hook(void) +{ + /* Apply device related preinit configuration */ +} diff --git a/soc/realtek/ec/rts5912/soc.h b/soc/realtek/ec/rts5912/soc.h new file mode 100644 index 00000000000..3824ae900fb --- /dev/null +++ b/soc/realtek/ec/rts5912/soc.h @@ -0,0 +1,13 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#ifndef SOC_REALTEK_RTS5912_H_ +#define SOC_REALTEK_RTS5912_H_ + +#include + +#endif /* SOC_REALTEK_RTS5912_H_ */ diff --git a/soc/realtek/ec/soc.yml b/soc/realtek/ec/soc.yml new file mode 100644 index 00000000000..928ceedb635 --- /dev/null +++ b/soc/realtek/ec/soc.yml @@ -0,0 +1,2 @@ +socs: +- name: rts5912 From a73a87802896addee050f2ee2576345b299f477d Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Fri, 22 Nov 2024 16:57:25 +0800 Subject: [PATCH 04/18] [nrf fromtree] dts: realtek: Add RTS5912 device tree files Add Realtek RTS5912 chip and driver device tree files. Signed-off-by: Adam Kondraciuk (cherry picked from commit 041bf2e4c6324b87989586d214f6168036204939) Signed-off-by: Lin Yu-Cheng --- dts/arm/realtek/ec/rts5912.dtsi | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 dts/arm/realtek/ec/rts5912.dtsi diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi new file mode 100644 index 00000000000..9167e0e8514 --- /dev/null +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * + */ + +#include +#include +#include + +/ { + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "arm,cortex-m33f"; + reg = <0>; + cpu-power-states = <&idle &suspend_to_ram>; + }; + + power-states { + idle: idle { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-idle"; + min-residency-us = <100000>; + }; + + suspend_to_ram: suspend_to_ram { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-ram"; + min-residency-us = <250000>; + }; + }; + }; + + flash0: flash@20000400 { + reg = <0x20000400 0x4FC00>; + }; + + sram0: memory@20050000 { + compatible = "mmio-sram"; + reg = <0x20050000 0x8000>; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&nvic>; + ranges; + }; +}; + +&nvic { + arm,num-irq-priority-bits = <3>; +}; + +&systick { + status = "disabled"; +}; From d78f53676020dc4a7dba8748b923c0234cef25bc Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Fri, 22 Nov 2024 17:01:05 +0800 Subject: [PATCH 05/18] [nrf fromtree] driver: clock_control: Add clock controller initial version of RTS5912. Add clock controller driver for Realtek RTS5912. Signed-off-by: Adam Kondraciuk (cherry picked from commit 6ea7560ce26514727d89563540559dc289050e61) Signed-off-by: Lin Yu-Cheng --- drivers/clock_control/CMakeLists.txt | 1 + drivers/clock_control/Kconfig | 2 + drivers/clock_control/Kconfig.rts5912 | 10 + .../clock_control_rts5912_sccon.c | 247 +++++++++++++ dts/arm/realtek/ec/rts5912.dtsi | 22 ++ dts/bindings/clock/realtek,rts5912-sccon.yaml | 21 ++ .../clock_control/clock_control_rts5912.h | 20 + .../zephyr/dt-bindings/clock/rts5912_clock.h | 343 ++++++++++++++++++ soc/realtek/ec/rts5912/reg/reg_system.h | 335 +++++++++++++++++ 9 files changed, 1001 insertions(+) create mode 100644 drivers/clock_control/Kconfig.rts5912 create mode 100644 drivers/clock_control/clock_control_rts5912_sccon.c create mode 100644 dts/bindings/clock/realtek,rts5912-sccon.yaml create mode 100644 include/zephyr/drivers/clock_control/clock_control_rts5912.h create mode 100644 include/zephyr/dt-bindings/clock/rts5912_clock.h create mode 100644 soc/realtek/ec/rts5912/reg/reg_system.h diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 057f4463247..8c451149a58 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -38,6 +38,7 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_AMBIQ clock_cont zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_PWM clock_control_pwm.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_RPI_PICO clock_control_rpi_pico.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF2_GLOBAL_HSFLL clock_control_nrf2_global_hsfll.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_RTS5912_SCCON clock_control_rts5912_sccon.c) if(CONFIG_CLOCK_CONTROL_NRF2) zephyr_library_sources(clock_control_nrf2_common.c) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index 040dab163c2..eec9b11c188 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -56,6 +56,8 @@ source "drivers/clock_control/Kconfig.npcx" source "drivers/clock_control/Kconfig.rv32m1" +source "drivers/clock_control/Kconfig.rts5912" + source "drivers/clock_control/Kconfig.esp32" source "drivers/clock_control/Kconfig.litex" diff --git a/drivers/clock_control/Kconfig.rts5912 b/drivers/clock_control/Kconfig.rts5912 new file mode 100644 index 00000000000..ec5547cb91d --- /dev/null +++ b/drivers/clock_control/Kconfig.rts5912 @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +config CLOCK_CONTROL_RTS5912_SCCON + bool "Realtek RTS5912 system clock controller driver" + default y if DT_HAS_REALTEK_RTS5912_SCCON_ENABLED + help + Enable support for RTS5912 system clock controller driver diff --git a/drivers/clock_control/clock_control_rts5912_sccon.c b/drivers/clock_control/clock_control_rts5912_sccon.c new file mode 100644 index 00000000000..686c47a4904 --- /dev/null +++ b/drivers/clock_control/clock_control_rts5912_sccon.c @@ -0,0 +1,247 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#define DT_DRV_COMPAT realtek_rts5912_sccon + +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(sccon, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +#define RC25M_FREQ DT_PROP(DT_INST_CLOCKS_CTLR_BY_NAME(0, rc25m), clock_frequency) +#define PLL_FREQ DT_PROP(DT_INST_CLOCKS_CTLR_BY_NAME(0, pll), clock_frequency) + +struct rts5912_sccon_config { + uint32_t reg_base; + uint8_t sysclk_src; + uint8_t sysclk_div; +}; + +static int rts5912_periph_clock_control(const struct device *dev, clock_control_subsys_t sub_system, + bool on_off) +{ + const struct rts5912_sccon_config *const config = dev->config; + struct rts5912_sccon_subsys *subsys = (struct rts5912_sccon_subsys *)sub_system; + + SYSTEM_Type *sys_reg = (SYSTEM_Type *)config->reg_base; + + uint32_t clk_grp = subsys->clk_grp; + uint32_t clk_idx = subsys->clk_idx; + + uint32_t module_idx; + + switch (clk_grp) { + case RTS5912_SCCON_I2C: + module_idx = (clk_idx - I2C0_CLKPWR) >> 2; + + if (on_off) { + sys_reg->I2CCLK |= BIT(clk_idx); + } else { + sys_reg->I2CCLK &= ~BIT(clk_idx); + } + + LOG_DBG("Turn I2C%d clock <%s>", module_idx, on_off ? "ON" : "OFF"); + break; + case RTS5912_SCCON_UART: + if (on_off) { + sys_reg->UARTCLK = BIT(clk_idx); + } else { + sys_reg->UARTCLK &= ~BIT(clk_idx); + } + + LOG_DBG("Turn UART0 clock <%s>", on_off ? "ON" : "OFF"); + break; + case RTS5912_SCCON_ADC: + if (on_off) { + sys_reg->ADCCLK = BIT(clk_idx); + } else { + sys_reg->ADCCLK &= ~BIT(clk_idx); + } + + LOG_DBG("Turn ADC clock <%s>", on_off ? "ON" : "OFF"); + break; + case RTS5912_SCCON_PERIPH_GRP0: + if (on_off) { + sys_reg->PERICLKPWR0 |= BIT(clk_idx); + } else { + sys_reg->PERICLKPWR0 &= ~BIT(clk_idx); + } + + LOG_DBG("Turn GRP0-%d clock <%s>", clk_idx, on_off ? "ON" : "OFF"); + break; + case RTS5912_SCCON_PERIPH_GRP1: + if (on_off) { + sys_reg->PERICLKPWR1 |= BIT(clk_idx); + } else { + sys_reg->PERICLKPWR1 &= ~BIT(clk_idx); + } + + LOG_DBG("Turn GRP1-%d clock <%s>", clk_idx, on_off ? "ON" : "OFF"); + break; + case RTS5912_SCCON_PERIPH_GRP2: + if (on_off) { + sys_reg->PERICLKPWR2 |= BIT(clk_idx); + } else { + sys_reg->PERICLKPWR2 &= ~BIT(clk_idx); + } + + LOG_DBG("Turn GRP2-%d clock <%s>", clk_idx, on_off ? "ON" : "OFF"); + break; + case RTS5912_SCCON_SYS: + LOG_ERR("Not support peripheral group #%d-%d", clk_grp, clk_idx); + return -ENOTSUP; + default: + LOG_ERR("Unknown peripheral group #%d", clk_grp); + return -EINVAL; + } + + return 0; +} + +static int rts5912_clock_control_on(const struct device *dev, clock_control_subsys_t sub_system) +{ + return rts5912_periph_clock_control(dev, sub_system, true); +} + +static int rts5912_clock_control_off(const struct device *dev, clock_control_subsys_t sub_system) +{ + return rts5912_periph_clock_control(dev, sub_system, false); +} + +static int rts5912_clock_control_get_rate(const struct device *dev, + clock_control_subsys_t sub_system, uint32_t *rate) +{ + const struct rts5912_sccon_config *const config = dev->config; + struct rts5912_sccon_subsys *subsys = (struct rts5912_sccon_subsys *)sub_system; + + SYSTEM_Type *sys_reg = (SYSTEM_Type *)config->reg_base; + + uint32_t clk_grp = subsys->clk_grp; + uint32_t clk_idx = subsys->clk_idx; + uint32_t module_idx; + + uint32_t offset; + uint32_t src, divide, freq = 0; + + switch (clk_grp) { + case RTS5912_SCCON_I2C: + module_idx = (clk_idx - I2C0_CLKPWR) >> 2; + + offset = (SYSTEM_I2CCLK_I2C1CLKSRC_Pos - SYSTEM_I2CCLK_I2C0CLKSRC_Pos) * module_idx; + src = ((sys_reg->I2CCLK >> offset) & SYSTEM_I2CCLK_I2C0CLKSRC_Msk) ? PLL_FREQ + : RC25M_FREQ; + + offset = SYSTEM_I2CCLK_I2C0CLKDIV_Pos; + offset += (SYSTEM_I2CCLK_I2C1CLKDIV_Pos - SYSTEM_I2CCLK_I2C0CLKDIV_Pos) * + module_idx; + divide = (sys_reg->I2CCLK >> offset) & SYSTEM_I2CCLK_I2C0CLKDIV_Msk; + + freq = src >> divide; + + LOG_DBG("I2C%d: src<%s> divide<%ld> freq<%d>", module_idx, + (src == PLL_FREQ) ? "PLL" : "RC25M", BIT(divide), freq); + break; + case RTS5912_SCCON_UART: + src = (sys_reg->UARTCLK & SYSTEM_UARTCLK_SRC_Msk) ? PLL_FREQ : RC25M_FREQ; + divide = (sys_reg->UARTCLK & SYSTEM_UARTCLK_DIV_Msk) >> SYSTEM_UARTCLK_DIV_Pos; + freq = src >> divide; + + LOG_DBG("UART0: src<%s> divide<%ld> freq<%d>", (src == PLL_FREQ) ? "PLL" : "RC25M", + BIT(divide), freq); + break; + case RTS5912_SCCON_ADC: + src = (sys_reg->ADCCLK & SYSTEM_ADCCLK_SRC_Msk) ? RC25M_FREQ : PLL_FREQ; + divide = (sys_reg->ADCCLK & SYSTEM_ADCCLK_DIV_Msk) >> SYSTEM_ADCCLK_DIV_Pos; + + switch (divide) { + case 0: + __fallthrough; + case 1: + __fallthrough; + case 2: + __fallthrough; + case 3: + divide += 1; + freq = src / divide; + break; + case 4: + freq = src / 6; + divide = 6; + break; + case 5: + freq = src / 8; + divide = 8; + break; + case 6: + freq = src / 12; + divide = 12; + break; + case 7: + freq = src / 16; + divide = 16; + break; + default: + return -EINVAL; + } + + LOG_DBG("ADC0: src<%s> divide<%d> freq<%d>", (src == PLL_FREQ) ? "PLL" : "RC25M", + divide, freq); + break; + case RTS5912_SCCON_SYS: + src = (sys_reg->SYSCLK & SYSTEM_SYSCLK_SRC_Msk) ? PLL_FREQ : RC25M_FREQ; + divide = (sys_reg->SYSCLK & SYSTEM_SYSCLK_DIV_Msk) ? 0x01ul : 0x00ul; + freq = src >> divide; + + LOG_DBG("System Clock: src<%s> divide<%d> freq<%d>", + (src == PLL_FREQ) ? "PLL" : "RC25M", divide, freq); + break; + case RTS5912_SCCON_PERIPH_GRP0: + __fallthrough; + case RTS5912_SCCON_PERIPH_GRP1: + __fallthrough; + case RTS5912_SCCON_PERIPH_GRP2: + LOG_ERR("Not support peripheral group #%d-%d", clk_grp, clk_idx); + return -ENOTSUP; + default: + LOG_ERR("Unknown peripheral group #%d", clk_grp); + return -EINVAL; + } + + *rate = freq; + return 0; +} + +static DEVICE_API(clock_control, rts5912_clock_control_api) = { + .on = rts5912_clock_control_on, + .off = rts5912_clock_control_off, + .get_rate = rts5912_clock_control_get_rate, +}; + +static int rts5912_clock_control_init(const struct device *dev) +{ + const struct rts5912_sccon_config *const config = dev->config; + SYSTEM_Type *sys_reg = (SYSTEM_Type *)config->reg_base; + + /* Setup system clock */ + sys_reg->SYSCLK = (config->sysclk_src << SYSTEM_SYSCLK_SRC_Pos) | + (config->sysclk_div << SYSTEM_SYSCLK_DIV_Pos); + + return 0; +} + +const struct rts5912_sccon_config rts5912_sccon_config = { + .reg_base = DT_INST_REG_ADDR_BY_IDX(0, 0), + .sysclk_src = 1, + .sysclk_div = 0, +}; + +DEVICE_DT_INST_DEFINE(0, &rts5912_clock_control_init, NULL, NULL, &rts5912_sccon_config, + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &rts5912_clock_control_api); diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi index 9167e0e8514..78115d654fb 100644 --- a/dts/arm/realtek/ec/rts5912.dtsi +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -44,12 +44,34 @@ reg = <0x20050000 0x8000>; }; + clocks { + rc25m: rc25m { + compatible = "fixed-clock"; + clock-frequency = <25000000>; + #clock-cells = <0>; + }; + + pll: pll { + compatible = "fixed-clock"; + clock-frequency = <100000000>; + #clock-cells = <0>; + }; + }; + soc { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; interrupt-parent = <&nvic>; ranges; + + sccon: clock-controller@40020000 { + compatible = "realtek,rts5912-sccon"; + reg = <0x40020000 0xf0>; + #clock-cells = <2>; + clocks = <&rc25m>, <&pll>; + clock-names = "rc25m", "pll"; + }; }; }; diff --git a/dts/bindings/clock/realtek,rts5912-sccon.yaml b/dts/bindings/clock/realtek,rts5912-sccon.yaml new file mode 100644 index 00000000000..594c0095235 --- /dev/null +++ b/dts/bindings/clock/realtek,rts5912-sccon.yaml @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +description: Realtek RTS5912 System Clock Controller (SCCON) + +compatible: "realtek,rts5912-sccon" + +include: [clock-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#clock-cells": + const: 2 + +clock-cells: + - clk-grp + - clk-idx diff --git a/include/zephyr/drivers/clock_control/clock_control_rts5912.h b/include/zephyr/drivers/clock_control/clock_control_rts5912.h new file mode 100644 index 00000000000..381502bf7c1 --- /dev/null +++ b/include/zephyr/drivers/clock_control/clock_control_rts5912.h @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#include + +#ifndef ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_RTS5912_H_ +#define ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_RTS5912_H_ + +struct rts5912_sccon_subsys { + uint32_t clk_grp; + uint32_t clk_idx; + uint32_t clk_src; + uint32_t clk_div; +}; + +#endif /* ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_RTS591X_H_ */ diff --git a/include/zephyr/dt-bindings/clock/rts5912_clock.h b/include/zephyr/dt-bindings/clock/rts5912_clock.h new file mode 100644 index 00000000000..ef754602dd6 --- /dev/null +++ b/include/zephyr/dt-bindings/clock/rts5912_clock.h @@ -0,0 +1,343 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_RTS5912_CLOCK_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_RTS5912_CLOCK_H_ + +/* ====================================================================================== */ +/* ===================================== I2CCLK ======================================= */ +#define I2CCLK_I2C0CLKPWR_Pos (0) /*!< I2C0CLKPWR (Bit 0) */ +#define I2CCLK_I2C0CLKPWR_Msk (0x1) /*!< I2C0CLKPWR (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C0CLKSRC_Pos (1) /*!< I2C0CLKSRC (Bit 1) */ +#define I2CCLK_I2C0CLKSRC_Msk (0x2) /*!< I2C0CLKSRC (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C0CLKDIV_Pos (2) /*!< I2C0CLKDIV (Bit 2) */ +#define I2CCLK_I2C0CLKDIV_Msk (0xc) /*!< I2C0CLKDIV (Bitfield-Mask: 0x03) */ +#define I2CCLK_I2C1CLKPWR_Pos (4) /*!< I2C1CLKPWR (Bit 4) */ +#define I2CCLK_I2C1CLKPWR_Msk (0x10) /*!< I2C1CLKPWR (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C1CLKSRC_Pos (5) /*!< I2C1CLKSRC (Bit 5) */ +#define I2CCLK_I2C1CLKSRC_Msk (0x20) /*!< I2C1CLKSRC (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C1CLKDIV_Pos (6) /*!< I2C1CLKDIV (Bit 6) */ +#define I2CCLK_I2C1CLKDIV_Msk (0xc0) /*!< I2C1CLKDIV (Bitfield-Mask: 0x03) */ +#define I2CCLK_I2C2CLKPWR_Pos (8) /*!< I2C2CLKPWR (Bit 8) */ +#define I2CCLK_I2C2CLKPWR_Msk (0x100) /*!< I2C2CLKPWR (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C2CLKSRC_Pos (9) /*!< I2C2CLKSRC (Bit 9) */ +#define I2CCLK_I2C2CLKSRC_Msk (0x200) /*!< I2C2CLKSRC (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C2CLKDIV_Pos (10) /*!< I2C2CLKDIV (Bit 10) */ +#define I2CCLK_I2C2CLKDIV_Msk (0xc00) /*!< I2C2CLKDIV (Bitfield-Mask: 0x03) */ +#define I2CCLK_I2C3CLKPWR_Pos (12) /*!< I2C3CLKPWR (Bit 12) */ +#define I2CCLK_I2C3CLKPWR_Msk (0x1000) /*!< I2C3CLKPWR (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C3CLKSRC_Pos (13) /*!< I2C3CLKSRC (Bit 13) */ +#define I2CCLK_I2C3CLKSRC_Msk (0x2000) /*!< I2C3CLKSRC (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C3CLKDIV_Pos (14) /*!< I2C3CLKDIV (Bit 14) */ +#define I2CCLK_I2C3CLKDIV_Msk (0xc000) /*!< I2C3CLKDIV (Bitfield-Mask: 0x03) */ +#define I2CCLK_I2C4CLKPWR_Pos (16) /*!< I2C4CLKPWR (Bit 16) */ +#define I2CCLK_I2C4CLKPWR_Msk (0x10000) /*!< I2C4CLKPWR (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C4CLKSRC_Pos (17) /*!< I2C4CLKSRC (Bit 17) */ +#define I2CCLK_I2C4CLKSRC_Msk (0x20000) /*!< I2C4CLKSRC (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C4CLKDIV_Pos (18) /*!< I2C4CLKDIV (Bit 18) */ +#define I2CCLK_I2C4CLKDIV_Msk (0xc0000) /*!< I2C4CLKDIV (Bitfield-Mask: 0x03) */ +#define I2CCLK_I2C5CLKPWR_Pos (20) /*!< I2C5CLKPWR (Bit 20) */ +#define I2CCLK_I2C5CLKPWR_Msk (0x100000) /*!< I2C5CLKPWR (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C5CLKSRC_Pos (21) /*!< I2C5CLKSRC (Bit 21) */ +#define I2CCLK_I2C5CLKSRC_Msk (0x200000) /*!< I2C5CLKSRC (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C5CLKDIV_Pos (22) /*!< I2C5CLKDIV (Bit 22) */ +#define I2CCLK_I2C5CLKDIV_Msk (0xc00000) /*!< I2C5CLKDIV (Bitfield-Mask: 0x03) */ +#define I2CCLK_I2C6CLKPWR_Pos (24) /*!< I2C6CLKPWR (Bit 24) */ +#define I2CCLK_I2C6CLKPWR_Msk (0x1000000) /*!< I2C6CLKPWR (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C6CLKSRC_Pos (25) /*!< I2C6CLKSRC (Bit 25) */ +#define I2CCLK_I2C6CLKSRC_Msk (0x2000000) /*!< I2C6CLKSRC (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C6CLKDIV_Pos (26) /*!< I2C6CLKDIV (Bit 26) */ +#define I2CCLK_I2C6CLKDIV_Msk (0xc000000) /*!< I2C6CLKDIV (Bitfield-Mask: 0x03) */ +#define I2CCLK_I2C7CLKPWR_Pos (28) /*!< I2C7CLKPWR (Bit 28) */ +#define I2CCLK_I2C7CLKPWR_Msk (0x10000000) /*!< I2C7CLKPWR (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C7CLKSRC_Pos (29) /*!< I2C7CLKSRC (Bit 29) */ +#define I2CCLK_I2C7CLKSRC_Msk (0x20000000) /*!< I2C7CLKSRC (Bitfield-Mask: 0x01) */ +#define I2CCLK_I2C7CLKDIV_Pos (30) /*!< I2C7CLKDIV (Bit 30) */ +#define I2CCLK_I2C7CLKDIV_Msk (0xc0000000) /*!< I2C7CLKDIV (Bitfield-Mask: 0x03) */ +/* =================================== PERICLKPWR0 ==================================== */ +#define PERICLKPWR0_GPIOCLKPWR_Pos (0) /*!< GPIOCLKPWR (Bit 0) */ +#define PERICLKPWR0_GPIOCLKPWR_Msk (0x1) /*!< GPIOCLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_TACHO0CLKPWR_Pos (1) /*!< TACHO0CLKPWR (Bit 1) */ +#define PERICLKPWR0_TACHO0CLKPWR_Msk (0x2) /*!< TACHO0CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_TACHO1CLKPWR_Pos (2) /*!< TACHO1CLKPWR (Bit 2) */ +#define PERICLKPWR0_TACHO1CLKPWR_Msk (0x4) /*!< TACHO1CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_TACHO2CLKPWR_Pos (3) /*!< TACHO2CLKPWR (Bit 3) */ +#define PERICLKPWR0_TACHO2CLKPWR_Msk (0x8) /*!< TACHO2CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_TACHO3CLKPWR_Pos (4) /*!< TACHO3CLKPWR (Bit 4) */ +#define PERICLKPWR0_TACHO3CLKPWR_Msk (0x10) /*!< TACHO3CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PS2CLKPWR_Pos (5) /*!< PS2CLKPWR (Bit 5) */ +#define PERICLKPWR0_PS2CLKPWR_Msk (0x20) /*!< PS2CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_KBMCLKPWR_Pos (6) /*!< KBMCLKPWR (Bit 6) */ +#define PERICLKPWR0_KBMCLKPWR_Msk (0x40) /*!< KBMCLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PECICLKPWR_Pos (7) /*!< PECICLKPWR (Bit 7) */ +#define PERICLKPWR0_PECICLKPWR_Msk (0x80) /*!< PECICLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PL0CLKPWR_Pos (8) /*!< PL0CLKPWR (Bit 8) */ +#define PERICLKPWR0_PL0CLKPWR_Msk (0x100) /*!< PL0CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PL1CLKPWR_Pos (9) /*!< PL1CLKPWR (Bit 9) */ +#define PERICLKPWR0_PL1CLKPWR_Msk (0x200) /*!< PL1CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM0CLKPWR_Pos (10) /*!< PWM0CLKPWR (Bit 10) */ +#define PERICLKPWR0_PWM0CLKPWR_Msk (0x400) /*!< PWM0CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM1CLKPWR_Pos (11) /*!< PWM1CLKPWR (Bit 11) */ +#define PERICLKPWR0_PWM1CLKPWR_Msk (0x800) /*!< PWM1CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM2CLKPWR_Pos (12) /*!< PWM2CLKPWR (Bit 12) */ +#define PERICLKPWR0_PWM2CLKPWR_Msk (0x1000) /*!< PWM2CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM3CLKPWR_Pos (13) /*!< PWM3CLKPWR (Bit 13) */ +#define PERICLKPWR0_PWM3CLKPWR_Msk (0x2000) /*!< PWM3CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM4CLKPWR_Pos (14) /*!< PWM4CLKPWR (Bit 14) */ +#define PERICLKPWR0_PWM4CLKPWR_Msk (0x4000) /*!< PWM4CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM5CLKPWR_Pos (15) /*!< PWM5CLKPWR (Bit 15) */ +#define PERICLKPWR0_PWM5CLKPWR_Msk (0x8000) /*!< PWM5CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM6CLKPWR_Pos (16) /*!< PWM6CLKPWR (Bit 16) */ +#define PERICLKPWR0_PWM6CLKPWR_Msk (0x10000) /*!< PWM6CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM7CLKPWR_Pos (17) /*!< PWM7CLKPWR (Bit 17) */ +#define PERICLKPWR0_PWM7CLKPWR_Msk (0x20000) /*!< PWM7CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM8CLKPWR_Pos (18) /*!< PWM8CLKPWR (Bit 18) */ +#define PERICLKPWR0_PWM8CLKPWR_Msk (0x40000) /*!< PWM8CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM9CLKPWR_Pos (19) /*!< PWM9CLKPWR (Bit 19) */ +#define PERICLKPWR0_PWM9CLKPWR_Msk (0x80000) /*!< PWM9CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM10CLKPWR_Pos (20) /*!< PWM10CLKPWR (Bit 20) */ +#define PERICLKPWR0_PWM10CLKPWR_Msk (0x100000) /*!< PWM10CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PWM11CLKPWR_Pos (21) /*!< PWM11CLKPWR (Bit 21) */ +#define PERICLKPWR0_PWM11CLKPWR_Msk (0x200000) /*!< PWM11CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_ESPICLKPWR_Pos (22) /*!< ESPICLKPWR (Bit 22) */ +#define PERICLKPWR0_ESPICLKPWR_Msk (0x400000) /*!< ESPICLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_KBCCLKPWR_Pos (23) /*!< KBCCLKPWR (Bit 23) */ +#define PERICLKPWR0_KBCCLKPWR_Msk (0x800000) /*!< KBCCLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_ACPICLKPWR_Pos (24) /*!< ACPICLKPWR (Bit 24) */ +#define PERICLKPWR0_ACPICLKPWR_Msk (0x1000000) /*!< ACPICLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_PMPORT0CLKPWR_Pos (25) /*!< PMPORT0CLKPWR (Bit 25) */ +#define PERICLKPWR0_PMPORT0CLKPWR_Msk (0x2000000) /*!< PMPORT0CLKPWR (Bitfield-Mask: 0x01)*/ +#define PERICLKPWR0_PMPORT1CLKPWR_Pos (26) /*!< PMPORT1CLKPWR (Bit 26) */ +#define PERICLKPWR0_PMPORT1CLKPWR_Msk (0x4000000) /*!< PMPORT1CLKPWR (Bitfield-Mask: 0x01)*/ +#define PERICLKPWR0_PMPORT2CLKPWR_Pos (27) /*!< PMPORT2CLKPWR (Bit 27) */ +#define PERICLKPWR0_PMPORT2CLKPWR_Msk (0x8000000) /*!< PMPORT2CLKPWR (Bitfield-Mask: 0x01)*/ +#define PERICLKPWR0_PMPORT3CLKPWR_Pos (28) /*!< PMPORT3CLKPWR (Bit 28) */ +#define PERICLKPWR0_PMPORT3CLKPWR_Msk (0x10000000) /*!< PMPORT3CLKPWR (Bitfield-Mask: 0x01)*/ +#define PERICLKPWR0_P80CLKPWR_Pos (29) /*!< P80CLKPWR (Bit 29) */ +#define PERICLKPWR0_P80CLKPWR_Msk (0x20000000) /*!< P80CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_EMI0CLKPWR_Pos (30) /*!< EMI0CLKPWR (Bit 30) */ +#define PERICLKPWR0_EMI0CLKPWR_Msk (0x40000000) /*!< EMI0CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR0_EMI1CLKPWR_Pos (31) /*!< EMI1CLKPWR (Bit 31) */ +#define PERICLKPWR0_EMI1CLKPWR_Msk (0x80000000) /*!< EMI1CLKPWR (Bitfield-Mask: 0x01) */ +/* ===================================== UARTCLK ====================================== */ +#define UARTCLK_PWR_Pos (0) /*!< PWR (Bit 0) */ +#define UARTCLK_PWR_Msk (0x1) /*!< PWR (Bitfield-Mask: 0x01) */ +#define UARTCLK_SRC_Pos (1) /*!< SRC (Bit 1) */ +#define UARTCLK_SRC_Msk (0x2) /*!< SRC (Bitfield-Mask: 0x01) */ +#define UARTCLK_DIV_Pos (2) /*!< DIV (Bit 2) */ +#define UARTCLK_DIV_Msk (0xc) /*!< DIV (Bitfield-Mask: 0x03) */ +/* ===================================== ADCCLK ======================================= */ +#define ADCCLK_PWR_Pos (0) /*!< PWR (Bit 0) */ +#define ADCCLK_PWR_Msk (0x1) /*!< PWR (Bitfield-Mask: 0x01) */ +#define ADCCLK_SRC_Pos (1) /*!< SRC (Bit 1) */ +#define ADCCLK_SRC_Msk (0x2) /*!< SRC (Bitfield-Mask: 0x01) */ +#define ADCCLK_DIV_Pos (2) /*!< DIV (Bit 2) */ +#define ADCCLK_DIV_Msk (0x1c) /*!< DIV (Bitfield-Mask: 0x07) */ +/* =================================== PERICLKPWR1 ==================================== */ +#define PERICLKPWR1_EMI2CLKPWR_Pos (0) /*!< EMI2CLKPWR (Bit 0) */ +#define PERICLKPWR1_EMI2CLKPWR_Msk (0x1) /*!< EMI2CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_EMI3CLKPWR_Pos (1) /*!< EMI3CLKPWR (Bit 1) */ +#define PERICLKPWR1_EMI3CLKPWR_Msk (0x2) /*!< EMI3CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_EMI4CLKPWR_Pos (2) /*!< EMI4CLKPWR (Bit 2) */ +#define PERICLKPWR1_EMI4CLKPWR_Msk (0x4) /*!< EMI4CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_EMI5CLKPWR_Pos (3) /*!< EMI5CLKPWR (Bit 3) */ +#define PERICLKPWR1_EMI5CLKPWR_Msk (0x8) /*!< EMI5CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_EMI6CLKPWR_Pos (4) /*!< EMI6CLKPWR (Bit 4) */ +#define PERICLKPWR1_EMI6CLKPWR_Msk (0x10) /*!< EMI6CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_EMI7CLKPWR_Pos (5) /*!< EMI7CLKPWR (Bit 5) */ +#define PERICLKPWR1_EMI7CLKPWR_Msk (0x20) /*!< EMI7CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_I3C0CLKPWR_Pos (9) /*!< I3C0CLKPWR (Bit 9) */ +#define PERICLKPWR1_I3C0CLKPWR_Msk (0x200) /*!< I3C0CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_I3C1CLKPWR_Pos (10) /*!< I3C1CLKPWR (Bit 10) */ +#define PERICLKPWR1_I3C1CLKPWR_Msk (0x400) /*!< I3C1CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_I2CAUTOCLKPWR_Pos (11) /*!< I2CAUTOCLKPWR (Bit 11) */ +#define PERICLKPWR1_I2CAUTOCLKPWR_Msk (0x800) /*!< I2CAUTOCLKPWR (Bitfield-Mask: 0x01)*/ +#define PERICLKPWR1_MCCLKPWR_Pos (12) /*!< MCCLKPWR (Bit 12) */ +#define PERICLKPWR1_MCCLKPWR_Msk (0x1000) /*!< MCCLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_TMR0CLKPWR_Pos (13) /*!< TMR0CLKPWR (Bit 13) */ +#define PERICLKPWR1_TMR0CLKPWR_Msk (0x2000) /*!< TMR0CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_TMR1CLKPWR_Pos (14) /*!< TMR1CLKPWR (Bit 14) */ +#define PERICLKPWR1_TMR1CLKPWR_Msk (0x4000) /*!< TMR1CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_TMR2CLKPWR_Pos (15) /*!< TMR2CLKPWR (Bit 15) */ +#define PERICLKPWR1_TMR2CLKPWR_Msk (0x8000) /*!< TMR2CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_TMR3CLKPWR_Pos (16) /*!< TMR3CLKPWR (Bit 16) */ +#define PERICLKPWR1_TMR3CLKPWR_Msk (0x10000) /*!< TMR3CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_TMR4CLKPWR_Pos (17) /*!< TMR4CLKPWR (Bit 17) */ +#define PERICLKPWR1_TMR4CLKPWR_Msk (0x20000) /*!< TMR4CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_TMR5CLKPWR_Pos (18) /*!< TMR5CLKPWR (Bit 18) */ +#define PERICLKPWR1_TMR5CLKPWR_Msk (0x40000) /*!< TMR5CLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_RTMRCLKPWR_Pos (19) /*!< RTMRCLKPWR (Bit 19) */ +#define PERICLKPWR1_RTMRCLKPWR_Msk (0x80000) /*!< RTMRCLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR1_SLWTMR0CLKPWR_Pos (20) /*!< SLWTMR0CLKPWR (Bit 20) */ +#define PERICLKPWR1_SLWTMR0CLKPWR_Msk (0x100000) /*!< SLWTMR0CLKPWR (Bitfield-Mask: 0x01)*/ +#define PERICLKPWR1_SLWTMR1CLKPWR_Pos (21) /*!< SLWTMR1CLKPWR (Bit 21) */ +#define PERICLKPWR1_SLWTMR1CLKPWR_Msk (0x200000) /*!< SLWTMR1CLKPWR (Bitfield-Mask: 0x01)*/ +/* =================================== PERICLKPWR2 ==================================== */ +#define PERICLKPWR2_RTCCLKPWR_Pos (0) /*!< RTCCLKPWR (Bit 0) */ +#define PERICLKPWR2_RTCCLKPWR_Msk (0x1) /*!< RTCCLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR2_WDTCLKPWR_Pos (1) /*!< WDTCLKPWR (Bit 1) */ +#define PERICLKPWR2_WDTCLKPWR_Msk (0x2) /*!< WDTCLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR2_PWRBTNCLKPWR_Pos (2) /*!< PWRBTNCLKPWR (Bit 2) */ +#define PERICLKPWR2_PWRBTNCLKPWR_Msk (0x4) /*!< PWRBTNCLKPWR (Bitfield-Mask: 0x01) */ +#define PERICLKPWR2_RC32KSRC_Pos (30) /*!< RC32KSRC (Bit 30) */ +#define PERICLKPWR2_RC32KSRC_Msk (0xc0000000) /*!< RC32KSRC (Bitfield-Mask: 0x03) */ +/* ====================================================================================== */ + +#define RTS5912_SCCON_SYS (0) +#define RTS5912_SCCON_I2C (2) +#define RTS5912_SCCON_UART (3) +#define RTS5912_SCCON_ADC (4) +#define RTS5912_SCCON_PERIPH_GRP0 (5) +#define RTS5912_SCCON_PERIPH_GRP1 (6) +#define RTS5912_SCCON_PERIPH_GRP2 (7) + +#define I2C0_CLKPWR (I2CCLK_I2C0CLKPWR_Pos) +#define I2C1_CLKPWR (I2CCLK_I2C1CLKPWR_Pos) +#define I2C2_CLKPWR (I2CCLK_I2C2CLKPWR_Pos) +#define I2C3_CLKPWR (I2CCLK_I2C3CLKPWR_Pos) +#define I2C4_CLKPWR (I2CCLK_I2C4CLKPWR_Pos) +#define I2C5_CLKPWR (I2CCLK_I2C5CLKPWR_Pos) +#define I2C6_CLKPWR (I2CCLK_I2C6CLKPWR_Pos) +#define I2C7_CLKPWR (I2CCLK_I2C7CLKPWR_Pos) + +#define I2C0_PLL (0x0 << I2CCLK_I2C0CLKSRC_Pos) +#define I2C0_RC25M (0x1 << I2CCLK_I2C0CLKSRC_Pos) +#define I2C1_PLL (0x0 << I2CCLK_I2C1CLKSRC_Pos) +#define I2C1_RC25M (0x1 << I2CCLK_I2C1CLKSRC_Pos) +#define I2C2_PLL (0x0 << I2CCLK_I2C2CLKSRC_Pos) +#define I2C2_RC25M (0x1 << I2CCLK_I2C2CLKSRC_Pos) +#define I2C3_PLL (0x0 << I2CCLK_I2C3CLKSRC_Pos) +#define I2C3_RC25M (0x1 << I2CCLK_I2C3CLKSRC_Pos) +#define I2C4_PLL (0x0 << I2CCLK_I2C4CLKSRC_Pos) +#define I2C4_RC25M (0x1 << I2CCLK_I2C4CLKSRC_Pos) +#define I2C5_PLL (0x0 << I2CCLK_I2C5CLKSRC_Pos) +#define I2C5_RC25M (0x1 << I2CCLK_I2C5CLKSRC_Pos) +#define I2C6_PLL (0x0 << I2CCLK_I2C6CLKSRC_Pos) +#define I2C6_RC25M (0x1 << I2CCLK_I2C6CLKSRC_Pos) +#define I2C7_PLL (0x0 << I2CCLK_I2C7CLKSRC_Pos) +#define I2C7_RC25M (0x1 << I2CCLK_I2C7CLKSRC_Pos) + +#define I2C0_CLKDIV1 (0 << I2CCLK_I2C0CLKDIV_Pos) +#define I2C0_CLKDIV2 (1 << I2CCLK_I2C0CLKDIV_Pos) +#define I2C0_CLKDIV4 (2 << I2CCLK_I2C0CLKDIV_Pos) +#define I2C0_CLKDIV8 (3 << I2CCLK_I2C0CLKDIV_Pos) + +#define I2C1_CLKDIV1 (0 << I2CCLK_I2C1CLKDIV_Pos) +#define I2C1_CLKDIV2 (1 << I2CCLK_I2C1CLKDIV_Pos) +#define I2C1_CLKDIV4 (2 << I2CCLK_I2C1CLKDIV_Pos) +#define I2C1_CLKDIV8 (3 << I2CCLK_I2C1CLKDIV_Pos) + +#define I2C2_CLKDIV1 (0 << I2CCLK_I2C2CLKDIV_Pos) +#define I2C2_CLKDIV2 (1 << I2CCLK_I2C2CLKDIV_Pos) +#define I2C2_CLKDIV4 (2 << I2CCLK_I2C2CLKDIV_Pos) +#define I2C2_CLKDIV8 (3 << I2CCLK_I2C2CLKDIV_Pos) + +#define I2C3_CLKDIV1 (0 << I2CCLK_I2C3CLKDIV_Pos) +#define I2C3_CLKDIV2 (1 << I2CCLK_I2C3CLKDIV_Pos) +#define I2C3_CLKDIV4 (2 << I2CCLK_I2C3CLKDIV_Pos) +#define I2C3_CLKDIV8 (3 << I2CCLK_I2C3CLKDIV_Pos) + +#define I2C4_CLKDIV1 (0 << I2CCLK_I2C4CLKDIV_Pos) +#define I2C4_CLKDIV2 (1 << I2CCLK_I2C4CLKDIV_Pos) +#define I2C4_CLKDIV4 (2 << I2CCLK_I2C4CLKDIV_Pos) +#define I2C4_CLKDIV8 (3 << I2CCLK_I2C4CLKDIV_Pos) + +#define I2C5_CLKDIV1 (0 << I2CCLK_I2C5CLKDIV_Pos) +#define I2C5_CLKDIV2 (1 << I2CCLK_I2C5CLKDIV_Pos) +#define I2C5_CLKDIV4 (2 << I2CCLK_I2C5CLKDIV_Pos) +#define I2C5_CLKDIV8 (3 << I2CCLK_I2C5CLKDIV_Pos) + +#define I2C6_CLKDIV1 (0 << I2CCLK_I2C6CLKDIV_Pos) +#define I2C6_CLKDIV2 (1 << I2CCLK_I2C6CLKDIV_Pos) +#define I2C6_CLKDIV4 (2 << I2CCLK_I2C6CLKDIV_Pos) +#define I2C6_CLKDIV8 (3 << I2CCLK_I2C6CLKDIV_Pos) + +#define I2C7_CLKDIV1 (0 << I2CCLK_I2C7CLKDIV_Pos) +#define I2C7_CLKDIV2 (1 << I2CCLK_I2C7CLKDIV_Pos) +#define I2C7_CLKDIV4 (2 << I2CCLK_I2C7CLKDIV_Pos) +#define I2C7_CLKDIV8 (3 << I2CCLK_I2C7CLKDIV_Pos) + +#define UART0_CLKPWR (UARTCLK_PWR_Pos) + +#define UART0_RC25M (0x0 << UARTCLK_SRC_Pos) +#define UART0_PLL (0x1 << UARTCLK_SRC_Pos) + +#define UART0_CLKDIV1 (0 << UARTCLK_DIV_Pos) +#define UART0_CLKDIV2 (1 << UARTCLK_DIV_Pos) +#define UART0_CLKDIV4 (2 << UARTCLK_DIV_Pos) +#define UART0_CLKDIV8 (3 << UARTCLK_DIV_Pos) + +#define ADC0_CLKPWR (ADCCLK_PWR_Pos) + +#define ADC0_RC25M (0x0 << ADCCLK_SRC_Pos) +#define ADC0_PLL (0x1 << ADCCLK_SRC_Pos) + +#define ADC0_CLKDIV1 (0 << ADCCLK_DIV_Pos) +#define ADC0_CLKDIV2 (1 << ADCCLK_DIV_Pos) +#define ADC0_CLKDIV3 (2 << ADCCLK_DIV_Pos) +#define ADC0_CLKDIV4 (3 << ADCCLK_DIV_Pos) +#define ADC0_CLKDIV6 (4 << ADCCLK_DIV_Pos) +#define ADC0_CLKDIV8 (5 << ADCCLK_DIV_Pos) + +#define PERIPH_GRP0_GPIO_CLKPWR (PERICLKPWR0_GPIOCLKPWR_Pos) +#define PERIPH_GRP0_TACH0_CLKPWR (PERICLKPWR0_TACHO0CLKPWR_Pos) +#define PERIPH_GRP0_TACH1_CLKPWR (PERICLKPWR0_TACHO1CLKPWR_Pos) +#define PERIPH_GRP0_TACH2_CLKPWR (PERICLKPWR0_TACHO2CLKPWR_Pos) +#define PERIPH_GRP0_TACH3_CLKPWR (PERICLKPWR0_TACHO3CLKPWR_Pos) +#define PERIPH_GRP0_PS2_CLKPWR (PERICLKPWR0_PS2CLKPWR_Pos) +#define PERIPH_GRP0_KBM_CLKPWR (PERICLKPWR0_KBMCLKPWR_Pos) +#define PERIPH_GRP0_PECI_CLKPWR (PERICLKPWR0_PECICLKPWR_Pos) +#define PERIPH_GRP0_LEDPWM0_CLKPWR (PERICLKPWR0_PL0CLKPWR_Pos) +#define PERIPH_GRP0_LEDPWM1_CLKPWR (PERICLKPWR0_PL1CLKPWR_Pos) +#define PERIPH_GRP0_PWM0_CLKPWR (PERICLKPWR0_PWM0CLKPWR_Pos) +#define PERIPH_GRP0_PWM1_CLKPWR (PERICLKPWR0_PWM1CLKPWR_Pos) +#define PERIPH_GRP0_PWM2_CLKPWR (PERICLKPWR0_PWM2CLKPWR_Pos) +#define PERIPH_GRP0_PWM3_CLKPWR (PERICLKPWR0_PWM3CLKPWR_Pos) +#define PERIPH_GRP0_PWM4_CLKPWR (PERICLKPWR0_PWM4CLKPWR_Pos) +#define PERIPH_GRP0_PWM5_CLKPWR (PERICLKPWR0_PWM5CLKPWR_Pos) +#define PERIPH_GRP0_PWM6_CLKPWR (PERICLKPWR0_PWM6CLKPWR_Pos) +#define PERIPH_GRP0_PWM7_CLKPWR (PERICLKPWR0_PWM7CLKPWR_Pos) +#define PERIPH_GRP0_PWM8_CLKPWR (PERICLKPWR0_PWM8CLKPWR_Pos) +#define PERIPH_GRP0_PWM9_CLKPWR (PERICLKPWR0_PWM9CLKPWR_Pos) +#define PERIPH_GRP0_PWM10_CLKPWR (PERICLKPWR0_PWM10CLKPWR_Pos) +#define PERIPH_GRP0_PWM11_CLKPWR (PERICLKPWR0_PWM11CLKPWR_Pos) +#define PERIPH_GRP0_ESPI_CLKPWR (PERICLKPWR0_ESPICLKPWR_Pos) +#define PERIPH_GRP0_KBC_CLKPWR (PERICLKPWR0_KBCCLKPWR_Pos) +#define PERIPH_GRP0_ACPI_CLKPWR (PERICLKPWR0_ACPICLKPWR_Pos) +#define PERIPH_GRP0_PMPORT0_CLKPWR (PERICLKPWR0_PMPORT0CLKPWR_Pos) +#define PERIPH_GRP0_PMPORT1_CLKPWR (PERICLKPWR0_PMPORT1CLKPWR_Pos) +#define PERIPH_GRP0_PMPORT2_CLKPWR (PERICLKPWR0_PMPORT2CLKPWR_Pos) +#define PERIPH_GRP0_PMPORT3_CLKPWR (PERICLKPWR0_PMPORT3CLKPWR_Pos) +#define PERIPH_GRP0_P80_CLKPWR (PERICLKPWR0_P80CLKPWR_Pos) +#define PERIPH_GRP0_EMI0_CLKPWR (PERICLKPWR0_EMI0CLKPWR_Pos) +#define PERIPH_GRP0_EMI1_CLKPWR (PERICLKPWR0_EMI1CLKPWR_Pos) + +#define PERIPH_GRP1_EMI2_CLKPWR (PERICLKPWR1_EMI2CLKPWR_Pos) +#define PERIPH_GRP1_EMI3_CLKPWR (PERICLKPWR1_EMI3CLKPWR_Pos) +#define PERIPH_GRP1_EMI4_CLKPWR (PERICLKPWR1_EMI4CLKPWR_Pos) +#define PERIPH_GRP1_EMI5_CLKPWR (PERICLKPWR1_EMI5CLKPWR_Pos) +#define PERIPH_GRP1_EMI6_CLKPWR (PERICLKPWR1_EMI6CLKPWR_Pos) +#define PERIPH_GRP1_EMI7_CLKPWR (PERICLKPWR1_EMI7CLKPWR_Pos) +#define PERIPH_GRP1_I3C0_CLKPWR (PERICLKPWR1_I3C0CLKPWR_Pos) +#define PERIPH_GRP1_I3C1_CLKPWR (PERICLKPWR1_I3C1CLKPWR_Pos) +#define PERIPH_GRP1_I2CAUTO_CLKPWR (PERICLKPWR1_I2CAUTOCLKPWR_Pos) +#define PERIPH_GRP1_MC_CLKPWR (PERICLKPWR1_MCCLKPWR_Pos) +#define PERIPH_GRP1_TMR0_CLKPWR (PERICLKPWR1_TMR0CLKPWR_Pos) +#define PERIPH_GRP1_TMR1_CLKPWR (PERICLKPWR1_TMR1CLKPWR_Pos) +#define PERIPH_GRP1_TMR2_CLKPWR (PERICLKPWR1_TMR2CLKPWR_Pos) +#define PERIPH_GRP1_TMR3_CLKPWR (PERICLKPWR1_TMR3CLKPWR_Pos) +#define PERIPH_GRP1_TMR4_CLKPWR (PERICLKPWR1_TMR4CLKPWR_Pos) +#define PERIPH_GRP1_TMR5_CLKPWR (PERICLKPWR1_TMR5CLKPWR_Pos) +#define PERIPH_GRP1_RTMR_CLKPWR (PERICLKPWR1_RTMRCLKPWR_Pos) +#define PERIPH_GRP1_SLWTMR0_CLKPWR (PERICLKPWR1_SLWTMR0CLKPWR_Pos) +#define PERIPH_GRP1_SLWTMR1_CLKPWR (PERICLKPWR1_SLWTMR1CLKPWR_Pos) + +#define PERIPH_GRP2_RTC_CLKPWR (PERICLKPWR2_RTCCLKPWR_Pos) +#define PERIPH_GRP2_WDT_CLKPWR (PERICLKPWR2_WDTCLKPWR_Pos) +#define PERIPH_GRP2_WDTPWRBTN_CLKPWR (PERICLKPWR2_PWRBTNCLKPWR_Pos) + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_RTS5912_CLOCK_H_ */ diff --git a/soc/realtek/ec/rts5912/reg/reg_system.h b/soc/realtek/ec/rts5912/reg/reg_system.h new file mode 100644 index 00000000000..673baf4ac35 --- /dev/null +++ b/soc/realtek/ec/rts5912/reg/reg_system.h @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2023 Realtek, SIBG-SD7 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SOC_REALTEK_RTS5912_REG_SYSTEM_H +#define ZEPHYR_SOC_REALTEK_RTS5912_REG_SYSTEM_H + +/* + * @brief System Controller (SYSTEM) + */ +typedef struct { + union { + volatile uint32_t I3CCLK; + volatile uint32_t TMRRST; + }; + volatile uint32_t I2CCLK; + volatile uint32_t TMRCLK; + volatile uint32_t PERICLKPWR0; + volatile uint32_t UARTCLK; + volatile uint32_t SYSCLK; + volatile uint32_t ADCCLK; + volatile uint32_t PERICLKPWR1; + volatile const uint32_t RESERVED[24]; + volatile uint32_t SLPCTRL; + volatile const uint32_t RESERVED1[7]; + volatile uint32_t VIVOCTRL; + volatile uint32_t LDOCTRL; + volatile uint32_t RC25MCTRL; + volatile uint32_t PLLCTRL; + volatile const uint32_t RESERVED2[12]; + volatile uint32_t RC32KCTRL; + volatile const uint32_t RESERVED3; + volatile uint32_t PERICLKPWR2; +} SYSTEM_Type; + +/* I3CCLK */ +#define SYSTEM_I3CCLK_I3C0DIV_Pos (0UL) +#define SYSTEM_I3CCLK_I3C0DIV_Msk GENMASK(1, 0) +#define SYSTEM_I3CCLK_I3C1DIV_Pos (2UL) +#define SYSTEM_I3CCLK_I3C1DIV_Msk GENMASK(3, 2) +/* TMRRST */ +#define SYSTEM_TMRRST_TMR0RST_Pos (4UL) +#define SYSTEM_TMRRST_TMR0RST_Msk BIT(SYSTEM_TMRRST_TMR0RST_Pos) +#define SYSTEM_TMRRST_TMR1RST_Pos (5UL) +#define SYSTEM_TMRRST_TMR1RST_Msk BIT(SYSTEM_TMRRST_TMR1RST_Pos) +#define SYSTEM_TMRRST_TMR2RST_Pos (6UL) +#define SYSTEM_TMRRST_TMR2RST_Msk BIT(SYSTEM_TMRRST_TMR2RST_Pos) +#define SYSTEM_TMRRST_TMR3RST_Pos (7UL) +#define SYSTEM_TMRRST_TMR3RST_Msk BIT(SYSTEM_TMRRST_TMR3RST_Pos) +#define SYSTEM_TMRRST_TMR4RST_Pos (8UL) +#define SYSTEM_TMRRST_TMR4RST_Msk BIT(SYSTEM_TMRRST_TMR4RST_Pos) +#define SYSTEM_TMRRST_TMR5RST_Pos (9UL) +#define SYSTEM_TMRRST_TMR5RST_Msk BIT(SYSTEM_TMRRST_TMR5RST_Pos) +/* I2CCLK */ +#define SYSTEM_I2CCLK_I2C0CLKPWR_Pos (0UL) +#define SYSTEM_I2CCLK_I2C0CLKPWR_Msk BIT(SYSTEM_I2CCLK_I2C0CLKPWR_Pos) +#define SYSTEM_I2CCLK_I2C0CLKSRC_Pos (1UL) +#define SYSTEM_I2CCLK_I2C0CLKSRC_Msk BIT(SYSTEM_I2CCLK_I2C0CLKSRC_Pos) +#define SYSTEM_I2CCLK_I2C0CLKDIV_Pos (2UL) +#define SYSTEM_I2CCLK_I2C0CLKDIV_Msk GENMASK(3, 2) +#define SYSTEM_I2CCLK_I2C1CLKPWR_Pos (4UL) +#define SYSTEM_I2CCLK_I2C1CLKPWR_Msk BIT(SYSTEM_I2CCLK_I2C1CLKPWR_Pos) +#define SYSTEM_I2CCLK_I2C1CLKSRC_Pos (5UL) +#define SYSTEM_I2CCLK_I2C1CLKSRC_Msk BIT(SYSTEM_I2CCLK_I2C1CLKSRC_Pos) +#define SYSTEM_I2CCLK_I2C1CLKDIV_Pos (6UL) +#define SYSTEM_I2CCLK_I2C1CLKDIV_Msk GENMASK(7, 6) +#define SYSTEM_I2CCLK_I2C2CLKPWR_Pos (8UL) +#define SYSTEM_I2CCLK_I2C2CLKPWR_Msk BIT(SYSTEM_I2CCLK_I2C2CLKPWR_Pos) +#define SYSTEM_I2CCLK_I2C2CLKSRC_Pos (9UL) +#define SYSTEM_I2CCLK_I2C2CLKSRC_Msk BIT(SYSTEM_I2CCLK_I2C2CLKSRC_Pos) +#define SYSTEM_I2CCLK_I2C2CLKDIV_Pos (10UL) +#define SYSTEM_I2CCLK_I2C2CLKDIV_Msk GENMASK(11, 10) +#define SYSTEM_I2CCLK_I2C3CLKPWR_Pos (12UL) +#define SYSTEM_I2CCLK_I2C3CLKPWR_Msk BIT(SYSTEM_I2CCLK_I2C3CLKPWR_Pos) +#define SYSTEM_I2CCLK_I2C3CLKSRC_Pos (13UL) +#define SYSTEM_I2CCLK_I2C3CLKSRC_Msk BIT(SYSTEM_I2CCLK_I2C3CLKSRC_Pos) +#define SYSTEM_I2CCLK_I2C3CLKDIV_Pos (14UL) +#define SYSTEM_I2CCLK_I2C3CLKDIV_Msk GENMASK(15, 14) +#define SYSTEM_I2CCLK_I2C4CLKPWR_Pos (16UL) +#define SYSTEM_I2CCLK_I2C4CLKPWR_Msk BIT(SYSTEM_I2CCLK_I2C4CLKPWR_Pos) +#define SYSTEM_I2CCLK_I2C4CLKSRC_Pos (17UL) +#define SYSTEM_I2CCLK_I2C4CLKSRC_Msk BIT(SYSTEM_I2CCLK_I2C4CLKSRC_Pos) +#define SYSTEM_I2CCLK_I2C4CLKDIV_Pos (18UL) +#define SYSTEM_I2CCLK_I2C4CLKDIV_Msk GENMASK(19, 18) +#define SYSTEM_I2CCLK_I2C5CLKPWR_Pos (20UL) +#define SYSTEM_I2CCLK_I2C5CLKPWR_Msk BIT(SYSTEM_I2CCLK_I2C5CLKPWR_Pos) +#define SYSTEM_I2CCLK_I2C5CLKSRC_Pos (21UL) +#define SYSTEM_I2CCLK_I2C5CLKSRC_Msk BIT(SYSTEM_I2CCLK_I2C5CLKSRC_Pos) +#define SYSTEM_I2CCLK_I2C5CLKDIV_Pos (22UL) +#define SYSTEM_I2CCLK_I2C5CLKDIV_Msk GENMASK(23, 22) +#define SYSTEM_I2CCLK_I2C6CLKPWR_Pos (24UL) +#define SYSTEM_I2CCLK_I2C6CLKPWR_Msk BIT(SYSTEM_I2CCLK_I2C6CLKPWR_Pos) +#define SYSTEM_I2CCLK_I2C6CLKSRC_Pos (25UL) +#define SYSTEM_I2CCLK_I2C6CLKSRC_Msk BIT(SYSTEM_I2CCLK_I2C6CLKSRC_Pos) +#define SYSTEM_I2CCLK_I2C6CLKDIV_Pos (26UL) +#define SYSTEM_I2CCLK_I2C6CLKDIV_Msk GENMASK(27, 26) +#define SYSTEM_I2CCLK_I2C7CLKPWR_Pos (28UL) +#define SYSTEM_I2CCLK_I2C7CLKPWR_Msk BIT(SYSTEM_I2CCLK_I2C7CLKPWR_Pos) +#define SYSTEM_I2CCLK_I2C7CLKSRC_Pos (29UL) +#define SYSTEM_I2CCLK_I2C7CLKSRC_Msk BIT(SYSTEM_I2CCLK_I2C7CLKSRC_Pos) +#define SYSTEM_I2CCLK_I2C7CLKDIV_Pos (30UL) +#define SYSTEM_I2CCLK_I2C7CLKDIV_Msk GENMASK(31, 30) +/* TMRCLK */ +#define SYSTEM_TMRCLK_TMR0DIV_Pos (0UL) +#define SYSTEM_TMRCLK_TMR0DIV_Msk GENMASK(3, 0) +#define SYSTEM_TMRCLK_TMR1DIV_Pos (4UL) +#define SYSTEM_TMRCLK_TMR1DIV_Msk GENMASK(7, 4) +#define SYSTEM_TMRCLK_TMR2DIV_Pos (8UL) +#define SYSTEM_TMRCLK_TMR2DIV_Msk GENMASK(11, 8) +#define SYSTEM_TMRCLK_TMR3DIV_Pos (12UL) +#define SYSTEM_TMRCLK_TMR3DIV_Msk GENMASK(15, 12) +#define SYSTEM_TMRCLK_TMR4DIV_Pos (16UL) +#define SYSTEM_TMRCLK_TMR4DIV_Msk GENMASK(19, 16) +#define SYSTEM_TMRCLK_TMR5DIV_Pos (20UL) +#define SYSTEM_TMRCLK_TMR5DIV_Msk GENMASK(23, 20) +#define SYSTEM_TMRCLK_TMR0PAUSE_Pos (24UL) +#define SYSTEM_TMRCLK_TMR0PAUSE_Msk BIT(SYSTEM_TMRCLK_TMR0PAUSE_Pos) +#define SYSTEM_TMRCLK_TMR1PAUSE_Pos (25UL) +#define SYSTEM_TMRCLK_TMR1PAUSE_Msk BIT(SYSTEM_TMRCLK_TMR1PAUSE_Pos) +#define SYSTEM_TMRCLK_TMR2PAUSE_Pos (26UL) +#define SYSTEM_TMRCLK_TMR2PAUSE_Msk BIT(SYSTEM_TMRCLK_TMR2PAUSE_Pos) +#define SYSTEM_TMRCLK_TMR3PAUSE_Pos (27UL) +#define SYSTEM_TMRCLK_TMR3PAUSE_Msk BIT(SYSTEM_TMRCLK_TMR3PAUSE_Pos) +#define SYSTEM_TMRCLK_TMR4PAUSE_Pos (28UL) +#define SYSTEM_TMRCLK_TMR4PAUSE_Msk BIT(SYSTEM_TMRCLK_TMR4PAUSE_Pos) +#define SYSTEM_TMRCLK_TMR5PAUSE_Pos (29UL) +#define SYSTEM_TMRCLK_TMR5PAUSE_Msk BIT(SYSTEM_TMRCLK_TMR5PAUSE_Pos) +/* PERICLKPWR0 */ +#define SYSTEM_PERICLKPWR0_GPIOCLKPWR_Pos (0UL) +#define SYSTEM_PERICLKPWR0_GPIOCLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_GPIOCLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_TACHO0CLKPWR_Pos (1UL) +#define SYSTEM_PERICLKPWR0_TACHO0CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_TACHO0CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_TACHO1CLKPWR_Pos (2UL) +#define SYSTEM_PERICLKPWR0_TACHO1CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_TACHO1CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_TACHO2CLKPWR_Pos (3UL) +#define SYSTEM_PERICLKPWR0_TACHO2CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_TACHO2CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_TACHO3CLKPWR_Pos (4UL) +#define SYSTEM_PERICLKPWR0_TACHO3CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_TACHO3CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PS2CLKPWR_Pos (5UL) +#define SYSTEM_PERICLKPWR0_PS2CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PS2CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_KBMCLKPWR_Pos (6UL) +#define SYSTEM_PERICLKPWR0_KBMCLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_KBMCLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PECICLKPWR_Pos (7UL) +#define SYSTEM_PERICLKPWR0_PECICLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PECICLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PL0CLKPWR_Pos (8UL) +#define SYSTEM_PERICLKPWR0_PL0CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PL0CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PL1CLKPWR_Pos (9UL) +#define SYSTEM_PERICLKPWR0_PL1CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PL1CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM0CLKPWR_Pos (10UL) +#define SYSTEM_PERICLKPWR0_PWM0CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM0CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM1CLKPWR_Pos (11UL) +#define SYSTEM_PERICLKPWR0_PWM1CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM1CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM2CLKPWR_Pos (12UL) +#define SYSTEM_PERICLKPWR0_PWM2CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM2CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM3CLKPWR_Pos (13UL) +#define SYSTEM_PERICLKPWR0_PWM3CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM3CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM4CLKPWR_Pos (14UL) +#define SYSTEM_PERICLKPWR0_PWM4CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM4CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM5CLKPWR_Pos (15UL) +#define SYSTEM_PERICLKPWR0_PWM5CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM5CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM6CLKPWR_Pos (16UL) +#define SYSTEM_PERICLKPWR0_PWM6CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM6CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM7CLKPWR_Pos (17UL) +#define SYSTEM_PERICLKPWR0_PWM7CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM7CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM8CLKPWR_Pos (18UL) +#define SYSTEM_PERICLKPWR0_PWM8CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM8CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM9CLKPWR_Pos (19UL) +#define SYSTEM_PERICLKPWR0_PWM9CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM9CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM10CLKPWR_Pos (20UL) +#define SYSTEM_PERICLKPWR0_PWM10CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM10CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PWM11CLKPWR_Pos (21UL) +#define SYSTEM_PERICLKPWR0_PWM11CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PWM11CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_ESPICLKPWR_Pos (22UL) +#define SYSTEM_PERICLKPWR0_ESPICLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_ESPICLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_KBCCLKPWR_Pos (23UL) +#define SYSTEM_PERICLKPWR0_KBCCLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_KBCCLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_ACPICLKPWR_Pos (24UL) +#define SYSTEM_PERICLKPWR0_ACPICLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_ACPICLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PMPORT0CLKPWR_Pos (25UL) +#define SYSTEM_PERICLKPWR0_PMPORT0CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PMPORT0CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PMPORT1CLKPWR_Pos (26UL) +#define SYSTEM_PERICLKPWR0_PMPORT1CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PMPORT1CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PMPORT2CLKPWR_Pos (27UL) +#define SYSTEM_PERICLKPWR0_PMPORT2CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PMPORT2CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_PMPORT3CLKPWR_Pos (28UL) +#define SYSTEM_PERICLKPWR0_PMPORT3CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_PMPORT3CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_P80CLKPWR_Pos (29UL) +#define SYSTEM_PERICLKPWR0_P80CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_P80CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_EMI0CLKPWR_Pos (30UL) +#define SYSTEM_PERICLKPWR0_EMI0CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_EMI0CLKPWR_Pos) +#define SYSTEM_PERICLKPWR0_EMI1CLKPWR_Pos (31UL) +#define SYSTEM_PERICLKPWR0_EMI1CLKPWR_Msk BIT(SYSTEM_PERICLKPWR0_EMI1CLKPWR_Pos) +/* UARTCLK */ +#define SYSTEM_UARTCLK_PWR_Pos (0UL) +#define SYSTEM_UARTCLK_PWR_Msk BIT(SYSTEM_UARTCLK_PWR_Pos) +#define SYSTEM_UARTCLK_SRC_Pos (1UL) +#define SYSTEM_UARTCLK_SRC_Msk BIT(SYSTEM_UARTCLK_SRC_Pos) +#define SYSTEM_UARTCLK_DIV_Pos (2UL) +#define SYSTEM_UARTCLK_DIV_Msk GENMASK(3, 2) +/* SYSCLK */ +#define SYSTEM_SYSCLK_SRC_Pos (1UL) +#define SYSTEM_SYSCLK_SRC_Msk BIT(SYSTEM_SYSCLK_SRC_Pos) +#define SYSTEM_SYSCLK_DIV_Pos (2UL) +#define SYSTEM_SYSCLK_DIV_Msk BIT(SYSTEM_SYSCLK_DIV_Pos) +/* ADCCLK */ +#define SYSTEM_ADCCLK_PWR_Pos (0UL) +#define SYSTEM_ADCCLK_PWR_Msk BIT(SYSTEM_ADCCLK_PWR_Pos) +#define SYSTEM_ADCCLK_SRC_Pos (1UL) +#define SYSTEM_ADCCLK_SRC_Msk BIT(SYSTEM_ADCCLK_SRC_Pos) +#define SYSTEM_ADCCLK_DIV_Pos (2UL) +#define SYSTEM_ADCCLK_DIV_Msk GENMASK(4, 2) +/* PERICLKPWR1 */ +#define SYSTEM_PERICLKPWR1_EMI2CLKPWR_Pos (0UL) +#define SYSTEM_PERICLKPWR1_EMI2CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_EMI2CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_EMI3CLKPWR_Pos (1UL) +#define SYSTEM_PERICLKPWR1_EMI3CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_EMI3CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_EMI4CLKPWR_Pos (2UL) +#define SYSTEM_PERICLKPWR1_EMI4CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_EMI4CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_EMI5CLKPWR_Pos (3UL) +#define SYSTEM_PERICLKPWR1_EMI5CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_EMI5CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_EMI6CLKPWR_Pos (4UL) +#define SYSTEM_PERICLKPWR1_EMI6CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_EMI6CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_EMI7CLKPWR_Pos (5UL) +#define SYSTEM_PERICLKPWR1_EMI7CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_EMI7CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_I3C0CLKPWR_Pos (9UL) +#define SYSTEM_PERICLKPWR1_I3C0CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_I3C0CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_I3C1CLKPWR_Pos (10UL) +#define SYSTEM_PERICLKPWR1_I3C1CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_I3C1CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_I2CAUTOCLKPWR_Pos (11UL) +#define SYSTEM_PERICLKPWR1_I2CAUTOCLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_I2CAUTOCLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_MCCLKPWR_Pos (12UL) +#define SYSTEM_PERICLKPWR1_MCCLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_MCCLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_TMR0CLKPWR_Pos (13UL) +#define SYSTEM_PERICLKPWR1_TMR0CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_TMR0CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_TMR1CLKPWR_Pos (14UL) +#define SYSTEM_PERICLKPWR1_TMR1CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_TMR1CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_TMR2CLKPWR_Pos (15UL) +#define SYSTEM_PERICLKPWR1_TMR2CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_TMR2CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_TMR3CLKPWR_Pos (16UL) +#define SYSTEM_PERICLKPWR1_TMR3CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_TMR3CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_TMR4CLKPWR_Pos (17UL) +#define SYSTEM_PERICLKPWR1_TMR4CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_TMR4CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_TMR5CLKPWR_Pos (18UL) +#define SYSTEM_PERICLKPWR1_TMR5CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_TMR5CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_RTMRCLKPWR_Pos (19UL) +#define SYSTEM_PERICLKPWR1_RTMRCLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_RTMRCLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_SLWTMR0CLKPWR_Pos (20UL) +#define SYSTEM_PERICLKPWR1_SLWTMR0CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_SLWTMR0CLKPWR_Pos) +#define SYSTEM_PERICLKPWR1_SLWTMR1CLKPWR_Pos (21UL) +#define SYSTEM_PERICLKPWR1_SLWTMR1CLKPWR_Msk BIT(SYSTEM_PERICLKPWR1_SLWTMR1CLKPWR_Pos) +/* SLPCTRL */ +#define SYSTEM_SLPCTRL_SLPMDSEL_Pos (1UL) +#define SYSTEM_SLPCTRL_SLPMDSEL_Msk BIT(SYSTEM_SLPCTRL_SLPMDSEL_Pos) +#define SYSTEM_SLPCTRL_ESPIWKEN_Pos (2UL) +#define SYSTEM_SLPCTRL_ESPIWKEN_Msk BIT(SYSTEM_SLPCTRL_ESPIWKEN_Pos) +#define SYSTEM_SLPCTRL_PS2WKEN_Pos (3UL) +#define SYSTEM_SLPCTRL_PS2WKEN_Msk BIT(SYSTEM_SLPCTRL_PS2WKEN_Pos) +#define SYSTEM_SLPCTRL_I2CWKEN_Pos (4UL) +#define SYSTEM_SLPCTRL_I2CWKEN_Msk BIT(SYSTEM_SLPCTRL_I2CWKEN_Pos) +#define SYSTEM_SLPCTRL_GPIOWKEN_Pos (5UL) +#define SYSTEM_SLPCTRL_GPIOWKEN_Msk BIT(SYSTEM_SLPCTRL_GPIOWKEN_Pos) +/* VIVOCTRL */ +#define SYSTEM_VIVOCTRL_VIN0MD_Pos (0UL) +#define SYSTEM_VIVOCTRL_VIN0MD_Msk BIT(SYSTEM_VIVOCTRL_VIN0MD_Pos) +#define SYSTEM_VIVOCTRL_VIN1MD_Pos (1UL) +#define SYSTEM_VIVOCTRL_VIN1MD_Msk BIT(SYSTEM_VIVOCTRL_VIN1MD_Pos) +#define SYSTEM_VIVOCTRL_VIN2MD_Pos (2UL) +#define SYSTEM_VIVOCTRL_VIN2MD_Msk BIT(SYSTEM_VIVOCTRL_VIN2MD_Pos) +#define SYSTEM_VIVOCTRL_VIN3MD_Pos (3UL) +#define SYSTEM_VIVOCTRL_VIN3MD_Msk BIT(SYSTEM_VIVOCTRL_VIN3MD_Pos) +#define SYSTEM_VIVOCTRL_VIN4MD_Pos (4UL) +#define SYSTEM_VIVOCTRL_VIN4MD_Msk BIT(SYSTEM_VIVOCTRL_VIN4MD_Pos) +#define SYSTEM_VIVOCTRL_VIN5MD_Pos (5UL) +#define SYSTEM_VIVOCTRL_VIN5MD_Msk BIT(SYSTEM_VIVOCTRL_VIN5MD_Pos) +#define SYSTEM_VIVOCTRL_VIN0STS_Pos (6UL) +#define SYSTEM_VIVOCTRL_VIN0STS_Msk BIT(SYSTEM_VIVOCTRL_VIN0STS_Pos) +#define SYSTEM_VIVOCTRL_VIN1STS_Pos (7UL) +#define SYSTEM_VIVOCTRL_VIN1STS_Msk BIT(SYSTEM_VIVOCTRL_VIN1STS_Pos) +#define SYSTEM_VIVOCTRL_VIN2STS_Pos (8UL) +#define SYSTEM_VIVOCTRL_VIN2STS_Msk BIT(SYSTEM_VIVOCTRL_VIN2STS_Pos) +#define SYSTEM_VIVOCTRL_VIN3STS_Pos (9UL) +#define SYSTEM_VIVOCTRL_VIN3STS_Msk BIT(SYSTEM_VIVOCTRL_VIN3STS_Pos) +#define SYSTEM_VIVOCTRL_VIN4STS_Pos (10UL) +#define SYSTEM_VIVOCTRL_VIN4STS_Msk BIT(SYSTEM_VIVOCTRL_VIN4STS_Pos) +#define SYSTEM_VIVOCTRL_VIN5STS_Pos (11UL) +#define SYSTEM_VIVOCTRL_VIN5STS_Msk BIT(SYSTEM_VIVOCTRL_VIN5STS_Pos) +#define SYSTEM_VIVOCTRL_VIN0POL_Pos (12UL) +#define SYSTEM_VIVOCTRL_VIN0POL_Msk BIT(SYSTEM_VIVOCTRL_VIN0POL_Pos) +#define SYSTEM_VIVOCTRL_VIN1POL_Pos (13UL) +#define SYSTEM_VIVOCTRL_VIN1POL_Msk BIT(SYSTEM_VIVOCTRL_VIN1POL_Pos) +#define SYSTEM_VIVOCTRL_VIN2POL_Pos (14UL) +#define SYSTEM_VIVOCTRL_VIN2POL_Msk BIT(SYSTEM_VIVOCTRL_VIN2POL_Pos) +#define SYSTEM_VIVOCTRL_VIN3POL_Pos (15UL) +#define SYSTEM_VIVOCTRL_VIN3POL_Msk BIT(SYSTEM_VIVOCTRL_VIN3POL_Pos) +#define SYSTEM_VIVOCTRL_VIN4POL_Pos (16UL) +#define SYSTEM_VIVOCTRL_VIN4POL_Msk BIT(SYSTEM_VIVOCTRL_VIN4POL_Pos) +#define SYSTEM_VIVOCTRL_VIN5POL_Pos (17UL) +#define SYSTEM_VIVOCTRL_VIN5POL_Msk BIT(SYSTEM_VIVOCTRL_VIN5POL_Pos) +#define SYSTEM_VIVOCTRL_REGWREN_Pos (30UL) +#define SYSTEM_VIVOCTRL_REGWREN_Msk BIT(SYSTEM_VIVOCTRL_REGWREN_Pos) +#define SYSTEM_VIVOCTRL_VOUTMD_Pos (31UL) +#define SYSTEM_VIVOCTRL_VOUTMD_Msk BIT(SYSTEM_VIVOCTRL_VOUTMD_Pos) +/* LDOCTRL */ +#define SYSTEM_LDOCTRL_LDO2EN_Pos (3UL) +#define SYSTEM_LDOCTRL_LDO2EN_Msk BIT(SYSTEM_LDOCTRL_LDO2EN_Pos) +#define SYSTEM_LDOCTRL_LDO3EN_Pos (7UL) +#define SYSTEM_LDOCTRL_LDO3EN_Msk BIT(SYSTEM_LDOCTRL_LDO3EN_Pos) +/* RC25MCTRL */ +#define SYSTEM_RC25MCTRL_EN_Pos (0UL) +#define SYSTEM_RC25MCTRL_EN_Msk BIT(SYSTEM_RC25MCTRL_EN_Pos) +#define SYSTEM_RC25MCTRL_CALCURR_Pos (1UL) +#define SYSTEM_RC25MCTRL_CALCURR_Msk GENMASK(7, 1) +/* PLLCTRL */ +#define SYSTEM_PLLCTRL_EN_Pos (0UL) +#define SYSTEM_PLLCTRL_EN_Msk BIT(SYSTEM_PLLCTRL_EN_Pos) +#define SYSTEM_PLLCTRL_RDY_Pos (19UL) +#define SYSTEM_PLLCTRL_RDY_Msk BIT(SYSTEM_PLLCTRL_RDY_Pos) +/* RC32KCTRL */ +#define SYSTEM_RC32KCTRL_EN_Pos (0UL) +#define SYSTEM_RC32KCTRL_EN_Msk BIT(SYSTEM_RC32KCTRL_EN_Pos) +#define SYSTEM_RC32KCTRL_CAL_Pos (1UL) +#define SYSTEM_RC32KCTRL_CAL_Msk GENMASK(7, 1) +/* PERICLKPWR2 */ +#define SYSTEM_PERICLKPWR2_RTCCLKPWR_Pos (0UL) +#define SYSTEM_PERICLKPWR2_RTCCLKPWR_Msk BIT(SYSTEM_PERICLKPWR2_RTCCLKPWR_Pos) +#define SYSTEM_PERICLKPWR2_WDTCLKPWR_Pos (1UL) +#define SYSTEM_PERICLKPWR2_WDTCLKPWR_Msk BIT(SYSTEM_PERICLKPWR2_WDTCLKPWR_Pos) +#define SYSTEM_PERICLKPWR2_PWRBTNCLKPWR_Pos (2UL) +#define SYSTEM_PERICLKPWR2_PWRBTNCLKPWR_Msk BIT(SYSTEM_PERICLKPWR2_PWRBTNCLKPWR_Pos) +#define SYSTEM_PERICLKPWR2_RC32KSRC_Pos (30UL) +#define SYSTEM_PERICLKPWR2_RC32KSRC_Msk GENMASK(31, 30) + +#endif /* ZEPHYR_SOC_REALTEK_RTS5912_REG_SYSTEM_H */ From f725206f8b978b81780676c342177946a59d8e7e Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Fri, 22 Nov 2024 17:07:43 +0800 Subject: [PATCH 06/18] [nrf fromtree] driver: pinctrl: Add pinctrl initial version of RTS5912. Add pinctrl driver for Realtek RTS5912. Signed-off-by: Adam Kondraciuk (cherry picked from commit 2c25182572fce020f5cf3bb2e432cba156009f4d) Signed-off-by: Lin Yu-Cheng --- drivers/pinctrl/CMakeLists.txt | 1 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Kconfig.realtek_rts5912 | 10 + drivers/pinctrl/pinctrl_realtek_rts5912.c | 38 ++ dts/arm/realtek/ec/rts5912-pinctrl.dtsi | 645 ++++++++++++++++++ dts/arm/realtek/ec/rts5912.dtsi | 7 + .../pinctrl/realtek,rts5912-pinctrl.yaml | 54 ++ .../pinctrl/realtek-rts5912-pinctrl.h | 48 ++ soc/realtek/ec/common/pinctrl_soc.h | 63 ++ 9 files changed, 867 insertions(+) create mode 100644 drivers/pinctrl/Kconfig.realtek_rts5912 create mode 100644 drivers/pinctrl/pinctrl_realtek_rts5912.c create mode 100644 dts/arm/realtek/ec/rts5912-pinctrl.dtsi create mode 100644 dts/bindings/pinctrl/realtek,rts5912-pinctrl.yaml create mode 100644 include/zephyr/dt-bindings/pinctrl/realtek-rts5912-pinctrl.h create mode 100644 soc/realtek/ec/common/pinctrl_soc.h diff --git a/drivers/pinctrl/CMakeLists.txt b/drivers/pinctrl/CMakeLists.txt index 0a27a2d4786..e56922d083e 100644 --- a/drivers/pinctrl/CMakeLists.txt +++ b/drivers/pinctrl/CMakeLists.txt @@ -43,5 +43,6 @@ zephyr_library_sources_ifdef(CONFIG_PINCTRL_IMX_SCMI pinctrl_imx_scmi.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_MCHP_MEC5 pinctrl_mchp_mec5.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_WCH_AFIO pinctrl_wch_afio.c) zephyr_library_sources_ifdef(CONFIG_PINCTRL_SY1XX pinctrl_sy1xx.c) +zephyr_library_sources_ifdef(CONFIG_PINCTRL_REALTEK_RTS5912 pinctrl_realtek_rts5912.c) add_subdirectory(renesas) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index d5872387090..299372eea7f 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -70,6 +70,7 @@ source "drivers/pinctrl/Kconfig.max32" source "drivers/pinctrl/Kconfig.mec5" source "drivers/pinctrl/Kconfig.wch_afio" source "drivers/pinctrl/Kconfig.sy1xx" +source "drivers/pinctrl/Kconfig.realtek_rts5912" rsource "renesas/Kconfig" diff --git a/drivers/pinctrl/Kconfig.realtek_rts5912 b/drivers/pinctrl/Kconfig.realtek_rts5912 new file mode 100644 index 00000000000..12b926b3f52 --- /dev/null +++ b/drivers/pinctrl/Kconfig.realtek_rts5912 @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +config PINCTRL_REALTEK_RTS5912 + bool "Pin controller driver for REALTEK RTS MCUs" + default y if DT_HAS_REALTEK_RTS5912_PINCTRL_ENABLED + help + Enable pin controller driver for REALTEK RTS MCUs diff --git a/drivers/pinctrl/pinctrl_realtek_rts5912.c b/drivers/pinctrl/pinctrl_realtek_rts5912.c new file mode 100644 index 00000000000..5764a73f086 --- /dev/null +++ b/drivers/pinctrl/pinctrl_realtek_rts5912.c @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#define DT_DRV_COMPAT realtek_rts5912_pinctrl + +#include +#include + +#include + +#define REALTEK_RTS5912_PINMUX_GET_GPIO_PIN(n) \ + (((((n) >> REALTEK_RTS5912_GPIO_LOW_POS) & REALTEK_RTS5912_GPIO_LOW_MSK)) | \ + (((((n) >> REALTEK_RTS5912_GPIO_HIGH_POS) & REALTEK_RTS5912_GPIO_HIGH_MSK)) << 5)) + +#define PURE_PINMUX_MASK (GENMASK(31, 24) | GENMASK(17, 8) | GENMASK(2, 0)) +#define REALTEK_RTS5912_GET_PURE_PINMUX(n) (n & PURE_PINMUX_MASK) + +static volatile GPIO_Type *pinctrl_base = + (volatile GPIO_Type *)(DT_REG_ADDR(DT_NODELABEL(pinctrl))); + +int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, uintptr_t reg) +{ + ARG_UNUSED(reg); + uint32_t pin, pinmux, func; + + for (uint8_t i = 0U; i < pin_cnt; i++) { + pinmux = (uint32_t)pins[i]; + pin = REALTEK_RTS5912_PINMUX_GET_GPIO_PIN(pinmux); + func = REALTEK_RTS5912_GET_PURE_PINMUX(pinmux); + pinctrl_base->GCR[pin] = func; + } + + return 0; +} diff --git a/dts/arm/realtek/ec/rts5912-pinctrl.dtsi b/dts/arm/realtek/ec/rts5912-pinctrl.dtsi new file mode 100644 index 00000000000..3ac81a34862 --- /dev/null +++ b/dts/arm/realtek/ec/rts5912-pinctrl.dtsi @@ -0,0 +1,645 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * + */ + +#include + +&pinctrl +{ + /* ADC PINCTRL SETTING START */ + /omit-if-no-ref/ adc0_gpio074: adc0_gpio074 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ adc1_gpio075: adc1_gpio075 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ adc2_gpio076: adc2_gpio076 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ adc3_gpio077: adc3_gpio077 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ adc4_gpio078: adc4_gpio078 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ adc5_gpio079: adc5_gpio079 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ adc6_gpio080: adc6_gpio080 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ adc7_gpio081: adc7_gpio081 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ adc8_gpio082: adc8_gpio082 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ adc9_gpio054: adc9_gpio054 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ adc10_gpio098: adc10_gpio098 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ adc11_gpio024: adc11_gpio024 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* ADC PINCTRL SETTING END */ + /* ESPI PINCTRL SETTING START */ + /omit-if-no-ref/ espi_alert_gpio003: espi_alert_gpio003 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ espi_cs_gpio004: espi_cs_gpio004 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ espi_io3_gpio005: espi_io3_gpio005 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ espi_io2_gpio006: espi_io2_gpio006 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ espi_io1_gpio007: espi_io1_gpio007 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ espi_io0_gpio008: espi_io0_gpio008 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ espi_clk_gpio009: espi_clk_gpio009 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ espi_reset_gpio020: espi_reset_gpio020 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* ESPI PINCTRL SETTING END */ + /* I2C PINCTRL SETTING START */ + /omit-if-no-ref/ i2c00_clk_gpio094: i2c00_clk_gpio094 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ i2c00_data_gpio095: i2c00_data_gpio095 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ i2c00_clk_gpio115: i2c00_clk_gpio115 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ i2c00_data_gpio131: i2c00_data_gpio131 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ i2c01_clk_gpio118: i2c01_clk_gpio118 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ i2c01_data_gpio119: i2c01_data_gpio119 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ i2c02_clk_gpio014: i2c02_clk_gpio014 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ i2c02_data_gpio121: i2c02_data_gpio121 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ i2c03_clk_gpio100: i2c03_clk_gpio100 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ i2c03_data_gpio101: i2c03_data_gpio101 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ i2c04_clk_gpio017: i2c04_clk_gpio017 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ i2c04_data_gpio018: i2c04_data_gpio018 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ i2c05_clk_gpio027: i2c05_clk_gpio027 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ i2c05_data_gpio028: i2c05_data_gpio028 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ i2c05_clk_gpio128: i2c05_clk_gpio128 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ i2c05_data_gpio130: i2c05_data_gpio130 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ i2c06_clk_gpio036: i2c06_clk_gpio036 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ i2c06_data_gpio037: i2c06_data_gpio037 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ i2c07_clk_gpio038: i2c07_clk_gpio038 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ i2c07_data_gpio039: i2c07_data_gpio039 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* I2C PINCTRL SETTING END */ + /* JTAG PINCTRL SETTING START */ + /omit-if-no-ref/ jtag_tdi_gpio87: jtag_tdi_gpio87 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ jtag_tdo_gpio88: jtag_tdo_gpio88 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ jtag_rst_gpio89: jtag_rst_gpio89 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ jtag_clk_gpio90: jtag_clk_gpio90 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ jtag_tms_gpio91: jtag_tms_gpio91 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* JTAG PINCTRL SETTING END */ + /* KSM PINCTRL SETTING START */ + /* KSO PINCTRL SETTING START */ + /omit-if-no-ref/ kso0_gpio041: kso0_gpio041 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso1_gpio042: kso1_gpio042 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso2_gpio043: kso2_gpio043 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso3_gpio044: kso3_gpio044 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ kso4_gpio045: kso4_gpio045 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso5_gpio046: kso5_gpio046 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso6_gpio047: kso6_gpio047 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso7_gpio048: kso7_gpio048 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ kso8_gpio049: kso8_gpio049 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso9_gpio050: kso9_gpio050 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso10_gpio051: kso10_gpio051 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso11_gpio055: kso11_gpio055 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ kso12_gpio056: kso12_gpio056 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso13_gpio057: kso13_gpio057 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso14_gpio058: kso14_gpio058 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso15_gpio059: kso15_gpio059 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ kso16_gpio060: kso16_gpio060 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso17_gpio061: kso17_gpio061 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso18_gpio092: kso18_gpio092 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ kso19_gpio093: kso19_gpio093 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* KSO PINCTRL SETTING END */ + /* KSI PINCTRL SETTING START */ + /omit-if-no-ref/ ksi0_gpio064: ksi0_gpio064 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ ksi1_gpio065: ksi1_gpio065 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ ksi2_gpio066: ksi2_gpio066 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ ksi3_gpio067: ksi3_gpio067 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ ksi4_gpio068: ksi4_gpio068 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ ksi5_gpio069: ksi5_gpio069 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ ksi6_gpio070: ksi6_gpio070 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ ksi7_gpio071: ksi7_gpio071 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ ksi8_gpio054: ksi8_gpio054 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ ksi9_gpio098: ksi9_gpio098 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* KSO PINCTRL SETTING END */ + /* KSM PINCTRL SETTING END */ + /* PS2 PINCTRL SETTING START */ + /omit-if-no-ref/ ps2clk0_gpio092: ps2clk0_gpio092 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ ps2data0_gpio093: ps2data0_gpio093 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ ps2clk0_gpio096: ps2clk0_gpio096 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ ps2data0_gpio097: ps2data0_gpio097 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* PS2 PINCTRL SETTING END */ + /* PWM PINCTRL SETTING START */ + /omit-if-no-ref/ pwm0_gpio022: pwm0_gpio022 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ pwm1_gpio023: pwm1_gpio023 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ pwm2_gpio025: pwm2_gpio025 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ pwm3_gpio026: pwm3_gpio026 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ pwm4_gpio027: pwm4_gpio027 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ pwm5_gpio028: pwm5_gpio028 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ pwm6_gpio029: pwm6_gpio029 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ pwm7_gpio031: pwm7_gpio031 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ pwm8_gpio032: pwm8_gpio032 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ pwm9_gpio033: pwm9_gpio033 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ pwm10_gpio034: pwm10_gpio034 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ pwm11_gpio035: pwm11_gpio035 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* PWM PINCTRL SETTING END */ + /* SPIC PINCTRL SETTING START */ + /omit-if-no-ref/ spic_cs_gpio107: spic_cs_gpio107 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ spic_si_gpio108: spic_si_gpio108 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ spic_so_gpio109: spic_so_gpio109 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ spic_clk_gpio111: spic_clk_gpio111 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ spic_io2_gpio124: spic_io2_gpio124 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ spic_io3_gpio122: spic_io3_gpio122 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* SPIC PINCTRL SETTING END */ + /* TACHO PINCTRL SETTING START */ + /omit-if-no-ref/ tacho0_gpio052: tacho0_gpio052 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ tacho1_gpio053: tacho1_gpio053 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ tacho1_gpio086: tacho1_gpio086 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ tacho2_gpio085: tacho2_gpio085 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ tacho3_gpio083: tacho3_gpio083 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ tacho3_gpio084: tacho3_gpio084 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* TACHO PINCTRL SETTING END */ + /* UART PINCTRL SETTING START */ + /omit-if-no-ref/ uart_rx_gpio113: uart0_rx_gpio113 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ uart_tx_gpio114: uart0_tx_gpio114 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ uart_rx_gpio014: uart1_rx_gpio014 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ uart_tx_gpio015: uart1_tx_gpio015 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ uart_rx_gpio100: uart_rx_gpio100 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ uart_tx_gpio101: uart_tx_gpio101 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + + /omit-if-no-ref/ uart_dtr_gpio039: uart_dtr_gpio039 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ uart_rts_gpio040: uart_rts_gpio040 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ uart_dcd_gpio079: uart_dcd_gpio079 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ uart_dsr_gpio080: uart_dsr_gpio080 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ uart_cts_gpio081: uart_cts_gpio081 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ uart_ri_gpio088: uart_ri_gpio088 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /omit-if-no-ref/ uart_dtr_gpio124: uart_dtr_gpio124 { + pinmux = ; + input-enable; + input-schmitt-enable; + }; + /* UART PINCTRL SETTING END */ +}; diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi index 78115d654fb..3bd1b120eac 100644 --- a/dts/arm/realtek/ec/rts5912.dtsi +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -72,6 +72,13 @@ clocks = <&rc25m>, <&pll>; clock-names = "rc25m", "pll"; }; + + pinctrl: pin-controller@40090000 { + compatible = "realtek,rts5912-pinctrl"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x40090000 0x300>; + }; }; }; diff --git a/dts/bindings/pinctrl/realtek,rts5912-pinctrl.yaml b/dts/bindings/pinctrl/realtek,rts5912-pinctrl.yaml new file mode 100644 index 00000000000..c5b00ae822a --- /dev/null +++ b/dts/bindings/pinctrl/realtek,rts5912-pinctrl.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +description: | + This binding gives a base representation of the pins configuration + +compatible: "realtek,rts5912-pinctrl" + +include: [base.yaml, pinctrl-device.yaml, pincfg-node.yaml] + +properties: + reg: + required: true + +child-binding: + description: | + This binding gives a base representation of the pins configuration + + include: + - name: pincfg-node.yaml + property-allowlist: + - bias-pull-down + - bias-pull-up + - drive-push-pull + - drive-open-drain + - input-enable + - output-enable + - output-high + - output-low + - input-schmitt-enable + + properties: + pinmux: + type: int + required: true + description: Pinmux selection + drive-strength: + type: string + enum: + - "low" + - "high" + description: | + "low" — 4mA/8mA drive strength + "high" — 8mA/12mA drive strength + slew-rate: + type: string + enum: + - "fast" + - "low" + description: | + "fast" — Fast Frequency Slew Rate + "slow" — Slow Frequency Slew Rate diff --git a/include/zephyr/dt-bindings/pinctrl/realtek-rts5912-pinctrl.h b/include/zephyr/dt-bindings/pinctrl/realtek-rts5912-pinctrl.h new file mode 100644 index 00000000000..9954f4105d7 --- /dev/null +++ b/include/zephyr/dt-bindings/pinctrl/realtek-rts5912-pinctrl.h @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_PINCTRL_REALTEK_RTS5912_PINCTRL_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_PINCTRL_REALTEK_RTS5912_PINCTRL_H_ + +#include + +#define REALTEK_RTS5912_GPIO_INOUT BIT(0) /* IN/OUT : 0 input 1 output */ +#define REALTEK_RTS5912_GPIO_PINON BIT(1) /* Input_detect : 1 enable 0 disable */ +#define REALTEK_RTS5912_GPIO_VOLT BIT(2) /* Pin Volt : 1 1.8V 0 3.3V */ +#define REALTEK_RTS5912_FUNC0 0 /* GPIO mode */ +#define REALTEK_RTS5912_FUNC1 BIT(8) /* Function mode use BIT0~2 */ +#define REALTEK_RTS5912_FUNC2 BIT(9) +#define REALTEK_RTS5912_FUNC3 ((BIT(8)) | (BIT(9))) +#define REALTEK_RTS5912_FUNC4 BIT(10) + +#define REALTEK_RTS5912_INPUT_OUTPUT_POS 0 +#define REALTEK_RTS5912_INPUT_DETECTION_POS 1 +#define REALTEK_RTS5912_VOLTAGE_POS 2 +#define REALTEK_RTS5912_DRV_STR_POS 11 +#define REALTEK_RTS5912_SLEW_RATE_POS 12 +#define REALTEK_RTS5912_PD_POS 13 +#define REALTEK_RTS5912_PU_POS 14 +#define REALTEK_RTS5912_SCHMITTER_POS 15 +#define REALTEK_RTS5912_TYPE_POS 16 +#define REALTEK_RTS5912_HIGH_LOW_POS 17 + +#define REALTEK_RTS5912_GPIO_HIGH_POS 18 +#define REALTEK_RTS5912_GPIO_HIGH_MSK 0x3f +#define REALTEK_RTS5912_GPIO_LOW_POS 3 +#define REALTEK_RTS5912_GPIO_LOW_MSK 0x1f + +#define FUNC0 REALTEK_RTS5912_FUNC0 +#define FUNC1 REALTEK_RTS5912_FUNC1 +#define FUNC2 REALTEK_RTS5912_FUNC2 +#define FUNC3 REALTEK_RTS5912_FUNC3 +#define FUNC4 REALTEK_RTS5912_FUNC4 + +#define REALTEK_RTS5912_PINMUX(n, f) \ + (((((n) >> 5) & REALTEK_RTS5912_GPIO_HIGH_MSK) << REALTEK_RTS5912_GPIO_HIGH_POS) | \ + (((n) & REALTEK_RTS5912_GPIO_LOW_MSK) << REALTEK_RTS5912_GPIO_LOW_POS) | (f)) + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_PINCTRL_REALTEK_RTS5912_PINCTRL_H_ */ diff --git a/soc/realtek/ec/common/pinctrl_soc.h b/soc/realtek/ec/common/pinctrl_soc.h new file mode 100644 index 00000000000..5ca47e1e8a5 --- /dev/null +++ b/soc/realtek/ec/common/pinctrl_soc.h @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +/** + * @file + * realtek SoC specific helpers for pinctrl driver + */ + +#ifndef ZEPHYR_SOC_ARM_REALTEK_COMMON_PINCTRL_SOC_H_ +#define ZEPHYR_SOC_ARM_REALTEK_COMMON_PINCTRL_SOC_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ + +typedef uint32_t pinctrl_soc_pin_t; + +/** + * @brief Utility macro to initialize each pin. + * + * @param node_id Node identifier. + * @param prop Property name. + * @param idx Property entry index. + */ +#define Z_PINCTRL_REALTEK_RTS5912_PINMUX_INIT(node_id) (uint32_t)(DT_PROP(node_id, pinmux)) + +#define Z_PINCTRL_STATE_PINCFG_INIT(node_id) \ + ((DT_PROP(node_id, bias_pull_down) << REALTEK_RTS5912_PD_POS) | \ + (DT_PROP(node_id, bias_pull_up) << REALTEK_RTS5912_PU_POS) | \ + (DT_PROP(node_id, drive_open_drain) << REALTEK_RTS5912_TYPE_POS) | \ + (DT_PROP(node_id, output_high) << REALTEK_RTS5912_HIGH_LOW_POS) | \ + (DT_PROP(node_id, output_enable) << REALTEK_RTS5912_INPUT_OUTPUT_POS) | \ + (DT_PROP(node_id, input_schmitt_enable) << REALTEK_RTS5912_SCHMITTER_POS) | \ + (DT_PROP(node_id, input_enable) << REALTEK_RTS5912_INPUT_DETECTION_POS) | \ + (DT_ENUM_IDX_OR(node_id, slew_rate, 0x0) << REALTEK_RTS5912_SLEW_RATE_POS) | \ + (DT_ENUM_IDX_OR(node_id, drive_strength, 0x0) << REALTEK_RTS5912_DRV_STR_POS)) + +#define Z_PINCTRL_STATE_PIN_INIT(node_id, state_prop, idx) \ + (Z_PINCTRL_REALTEK_RTS5912_PINMUX_INIT(DT_PROP_BY_IDX(node_id, state_prop, idx)) | \ + Z_PINCTRL_STATE_PINCFG_INIT(DT_PROP_BY_IDX(node_id, state_prop, idx))), + +#define Z_PINCTRL_STATE_PINS_INIT(node_id, prop) \ + { \ + DT_FOREACH_PROP_ELEM(node_id, prop, Z_PINCTRL_STATE_PIN_INIT) \ + } + +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_SOC_ARM_REALTEK_COMMON_PINCTRL_SOC_H_ */ From d4414230a9f1fed0529437f27b07b41994d88984 Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Fri, 22 Nov 2024 18:23:37 +0800 Subject: [PATCH 07/18] [nrf fromtree] soc: realrek: ec: Add debug_swj initial version of RTS5912. Add swj driver for Realtek RTS5912. Signed-off-by: Adam Kondraciuk (cherry picked from commit 471cc3512d0ea043876bed42220882bc11c9cb56) Signed-off-by: Lin Yu-Cheng --- dts/arm/realtek/ec/rts5912.dtsi | 7 +++++++ soc/realtek/ec/rts5912/CMakeLists.txt | 2 ++ soc/realtek/ec/rts5912/debug_swj.c | 27 +++++++++++++++++++++++++++ soc/realtek/ec/rts5912/debug_swj.h | 8 ++++++++ soc/realtek/ec/rts5912/soc.c | 10 ++++++++++ 5 files changed, 54 insertions(+) create mode 100644 soc/realtek/ec/rts5912/debug_swj.c create mode 100644 soc/realtek/ec/rts5912/debug_swj.h diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi index 3bd1b120eac..b5f6daad4c7 100644 --- a/dts/arm/realtek/ec/rts5912.dtsi +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -80,6 +80,13 @@ reg = <0x40090000 0x300>; }; }; + + swj_port: swj-port { + compatible = "swj-connector"; + pinctrl-0 = <&jtag_tdi_gpio87 &jtag_tdo_gpio88 &jtag_rst_gpio89 + &jtag_clk_gpio90 &jtag_tms_gpio91>; + pinctrl-names = "default"; + }; }; &nvic { diff --git a/soc/realtek/ec/rts5912/CMakeLists.txt b/soc/realtek/ec/rts5912/CMakeLists.txt index 335d40578ba..219c68e5a8d 100644 --- a/soc/realtek/ec/rts5912/CMakeLists.txt +++ b/soc/realtek/ec/rts5912/CMakeLists.txt @@ -12,4 +12,6 @@ zephyr_sources_ifdef(CONFIG_PM power.c ) +zephyr_sources_ifdef(CONFIG_DT_HAS_SWJ_CONNECTOR_ENABLED debug_swj.c) + set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm/cortex_m/scripts/linker.ld CACHE INTERNAL "") diff --git a/soc/realtek/ec/rts5912/debug_swj.c b/soc/realtek/ec/rts5912/debug_swj.c new file mode 100644 index 00000000000..c04c9d8c106 --- /dev/null +++ b/soc/realtek/ec/rts5912/debug_swj.c @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Titan Chen + */ + +#include +#include + +#define SWJ_NODE DT_NODELABEL(swj_port) + +PINCTRL_DT_DEFINE(SWJ_NODE); + +const struct pinctrl_dev_config *swj_pcfg = PINCTRL_DT_DEV_CONFIG_GET(SWJ_NODE); + +int swj_connector_init(void) +{ + int err; + + err = pinctrl_apply_state(swj_pcfg, PINCTRL_STATE_DEFAULT); + if (err < 0) { + return err; + } + + return 0; +} diff --git a/soc/realtek/ec/rts5912/debug_swj.h b/soc/realtek/ec/rts5912/debug_swj.h new file mode 100644 index 00000000000..0f610b7285f --- /dev/null +++ b/soc/realtek/ec/rts5912/debug_swj.h @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +int swj_connector_init(void); diff --git a/soc/realtek/ec/rts5912/soc.c b/soc/realtek/ec/rts5912/soc.c index adb28d75864..ce1e254e051 100644 --- a/soc/realtek/ec/rts5912/soc.c +++ b/soc/realtek/ec/rts5912/soc.c @@ -8,6 +8,10 @@ #include #include #include +#include +#include "debug_swj.h" + +LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL); #if defined(CONFIG_RTS5912_ON_ENTER_CPU_IDLE_HOOK) bool z_arm_on_enter_cpu_idle(void) @@ -24,5 +28,11 @@ bool z_arm_on_enter_cpu_idle(void) */ void soc_early_init_hook(void) { + int ret; + /* Apply device related preinit configuration */ + ret = swj_connector_init(); + if (ret < 0) { + LOG_ERR("SWJ init failed"); + } } From ae27a558933c1d0aeab96cf90f4937282394b7d7 Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Fri, 22 Nov 2024 18:27:29 +0800 Subject: [PATCH 08/18] [nrf fromtree] driver: timer: Add timer driver initial version of RTS5912. Add timer driver for Realtek RTS5912. Signed-off-by: Adam Kondraciuk (cherry picked from commit cfb2074a5e939bc6737fa9d63d211ff0f5a2dd94) Signed-off-by: Lin Yu-Cheng --- drivers/timer/CMakeLists.txt | 1 + drivers/timer/Kconfig | 1 + drivers/timer/Kconfig.realtek_rts5912_rtmr | 13 + drivers/timer/realtek_rts5912_rtmr.c | 251 ++++++++++++++++++ dts/arm/realtek/ec/rts5912.dtsi | 19 ++ dts/bindings/timer/realtek,rts5912-rtmr.yaml | 17 ++ .../timer/realtek,rts5912-slwtimer.yaml | 30 +++ soc/realtek/ec/rts5912/reg/reg_rtmr.h | 31 +++ 8 files changed, 363 insertions(+) create mode 100644 drivers/timer/Kconfig.realtek_rts5912_rtmr create mode 100644 drivers/timer/realtek_rts5912_rtmr.c create mode 100644 dts/bindings/timer/realtek,rts5912-rtmr.yaml create mode 100644 dts/bindings/timer/realtek,rts5912-slwtimer.yaml create mode 100644 soc/realtek/ec/rts5912/reg/reg_rtmr.h diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index 2b837fba480..51b0e552f4a 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -33,6 +33,7 @@ zephyr_library_sources_ifdef(CONFIG_NRF_RTC_TIMER nrf_rtc_timer.c) zephyr_library_sources_ifdef(CONFIG_RCAR_CMT_TIMER rcar_cmt_timer.c) zephyr_library_sources_ifdef(CONFIG_RISCV_MACHINE_TIMER riscv_machine_timer.c) zephyr_library_sources_ifdef(CONFIG_RV32M1_LPTMR_TIMER rv32m1_lptmr_timer.c) +zephyr_library_sources_ifdef(CONFIG_REALTEK_RTS5912_RTMR realtek_rts5912_rtmr.c) zephyr_library_sources_ifdef(CONFIG_SAM0_RTC_TIMER sam0_rtc_timer.c) zephyr_library_sources_ifdef(CONFIG_SILABS_SLEEPTIMER_TIMER silabs_sleeptimer_timer.c) zephyr_library_sources_ifdef(CONFIG_STM32_LPTIM_TIMER stm32_lptim_timer.c) diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index a43e666349c..f017589a60e 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -91,6 +91,7 @@ source "drivers/timer/Kconfig.nrf_xrtc" source "drivers/timer/Kconfig.rcar_cmt" source "drivers/timer/Kconfig.riscv_machine" source "drivers/timer/Kconfig.rv32m1_lptmr" +source "drivers/timer/Kconfig.realtek_rts5912_rtmr" source "drivers/timer/Kconfig.sam0_rtc" source "drivers/timer/Kconfig.silabs" source "drivers/timer/Kconfig.smartbond" diff --git a/drivers/timer/Kconfig.realtek_rts5912_rtmr b/drivers/timer/Kconfig.realtek_rts5912_rtmr new file mode 100644 index 00000000000..560ea7a7059 --- /dev/null +++ b/drivers/timer/Kconfig.realtek_rts5912_rtmr @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +config REALTEK_RTS5912_RTMR + bool "Realtek RTS5912 RTOS Timer" + default y if DT_HAS_REALTEK_RTS5912_RTMR_ENABLED + select TICKLESS_CAPABLE + select SYSTEM_TIMER_HAS_DISABLE_SUPPORT + help + This module implements a kernel device driver for the Realtek + RTS5912 series RTOS timer. diff --git a/drivers/timer/realtek_rts5912_rtmr.c b/drivers/timer/realtek_rts5912_rtmr.c new file mode 100644 index 00000000000..3476e730999 --- /dev/null +++ b/drivers/timer/realtek_rts5912_rtmr.c @@ -0,0 +1,251 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#define DT_DRV_COMPAT realtek_rts5912_rtmr + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define RTS5912_SCCON_REG_BASE ((SYSTEM_Type *)(DT_REG_ADDR(DT_NODELABEL(sccon)))) + +#define CYCLES_PER_TICK (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC) + +BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, + "Realtek RTOS timer is not supported multiple instances"); + +#define RTMR_REG ((RTOSTMR_Type *)DT_INST_REG_ADDR(0)) + +#define SLWTMR_REG \ + ((RTOSTMR_Type *)(DT_REG_ADDR(DT_COMPAT_GET_ANY_STATUS_OKAY(realtek_rts5912_slwtimer)))) + +#define SSCON_REG ((SYSTEM_Type *)(DT_REG_ADDR(DT_NODELABEL(sccon)))) + +#define RTMR_COUNTER_MAX 0x0ffffffful +#define RTMR_COUNTER_MSK 0x0ffffffful +#define RTMR_TIMER_STOPPED 0xf0000000ul + +#define MAX_TICKS ((k_ticks_t)(RTMR_COUNTER_MAX / CYCLES_PER_TICK) - 1) + +#define RTMR_ADJUST_LIMIT 2 +#define RTMR_ADJUST_CYCLES 1 + +static struct k_spinlock lock; +static uint32_t accumulated_cycles; +static uint32_t previous_cnt; /* Record the counter set into RTMR */ +static uint32_t last_announcement; /* Record the last tick announced to system */ + +static void rtmr_restart(uint32_t counter) +{ + RTMR_REG->CTRL = 0ul; + RTMR_REG->LDCNT = counter; + RTMR_REG->CTRL = RTOSTMR_CTRL_INTEN_Msk | RTOSTMR_CTRL_EN_Msk; +} + +static uint32_t rtmr_get_counter(void) +{ + uint32_t counter = RTMR_REG->CNT; + + if ((counter == 0) && (RTMR_REG->CTRL & RTOSTMR_CTRL_EN_Msk)) { + counter = previous_cnt; + } + + return counter; +} + +static void rtmr_isr(const void *arg) +{ + ARG_UNUSED(arg); + + uint32_t cycles; + int32_t ticks; + + k_spinlock_key_t key = k_spin_lock(&lock); + + rtmr_restart(RTMR_COUNTER_MAX * CYCLES_PER_TICK); + + cycles = previous_cnt; + previous_cnt = RTMR_COUNTER_MAX * CYCLES_PER_TICK; + + accumulated_cycles += cycles; + + if (accumulated_cycles > RTMR_COUNTER_MSK) { + accumulated_cycles &= RTMR_COUNTER_MSK; + } + + ticks = accumulated_cycles - last_announcement; + ticks &= RTMR_COUNTER_MSK; + ticks /= CYCLES_PER_TICK; + + last_announcement = accumulated_cycles; + + k_spin_unlock(&lock, key); + + sys_clock_announce(ticks); +} + +void sys_clock_set_timeout(int32_t ticks, bool idle) +{ + ARG_UNUSED(idle); + + uint32_t cur_cnt, temp; + int full_ticks; + uint32_t full_cycles; + uint32_t partial_cycles; + + if (idle && (ticks == K_TICKS_FOREVER)) { + RTMR_REG->CTRL = 0U; + previous_cnt = RTMR_TIMER_STOPPED; + return; + } + + if (ticks < 1) { + full_ticks = 0; + } else if ((ticks == K_TICKS_FOREVER) || (ticks > MAX_TICKS)) { + full_ticks = MAX_TICKS - 1; + } else { + full_ticks = ticks - 1; + } + + full_cycles = full_ticks * CYCLES_PER_TICK; + + k_spinlock_key_t key = k_spin_lock(&lock); + + cur_cnt = rtmr_get_counter(); + + RTMR_REG->CTRL = 0U; + + temp = accumulated_cycles; + temp += previous_cnt - cur_cnt; + temp &= RTMR_COUNTER_MSK; + accumulated_cycles = temp; + + partial_cycles = CYCLES_PER_TICK - (accumulated_cycles % CYCLES_PER_TICK); + previous_cnt = full_cycles + partial_cycles; + + temp = previous_cnt; + if (temp > RTMR_ADJUST_LIMIT) { + temp -= RTMR_ADJUST_CYCLES; + } + rtmr_restart(temp); + + k_spin_unlock(&lock, key); +} + +uint32_t sys_clock_elapsed(void) +{ + uint32_t cur_cnt; + uint32_t ticks; + int32_t elapsed; + + k_spinlock_key_t key = k_spin_lock(&lock); + + cur_cnt = rtmr_get_counter(); + + elapsed = (int32_t)accumulated_cycles - (int32_t)last_announcement; + if (elapsed < 0) { + elapsed = -1 * elapsed; + } + ticks = (uint32_t)elapsed; + ticks += previous_cnt - cur_cnt; + ticks /= CYCLES_PER_TICK; + ticks &= RTMR_COUNTER_MSK; + + k_spin_unlock(&lock, key); + + return ticks; +} + +void sys_clock_idle_exit(void) +{ + if (previous_cnt == RTMR_TIMER_STOPPED) { + previous_cnt = CYCLES_PER_TICK; + rtmr_restart(previous_cnt); + } +} + +void sys_clock_disable(void) +{ + /* Disable RTMR. */ + RTMR_REG->CTRL = 0ul; +} + +uint32_t sys_clock_cycle_get_32(void) +{ + uint32_t ret; + uint32_t cur_cnt; + + k_spinlock_key_t key = k_spin_lock(&lock); + + cur_cnt = rtmr_get_counter(); + ret = (accumulated_cycles + (previous_cnt - cur_cnt)) & RTMR_COUNTER_MSK; + + k_spin_unlock(&lock, key); + + return ret; +} + +#ifdef CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT + +void arch_busy_wait(uint32_t n_usec) +{ + if (n_usec == 0) { + return; + } + + uint32_t start = SLWTMR_REG->CNT; + + for (;;) { + uint32_t curr = SLWTMR_REG->CNT; + + if ((start - curr) >= n_usec) { + break; + } + } +} +#endif + +static int sys_clock_driver_init(void) +{ + /* Enable RTMR clock power */ + + SYSTEM_Type *sys_reg = RTS5912_SCCON_REG_BASE; + + sys_reg->PERICLKPWR1 |= SYSTEM_PERICLKPWR1_RTMRCLKPWR_Msk; + + /* Enable RTMR interrupt. */ + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), rtmr_isr, 0, 0); + irq_enable(DT_INST_IRQN(0)); + + /* Trigger RTMR and wait it start to counting */ + previous_cnt = RTMR_COUNTER_MAX; + + rtmr_restart(previous_cnt); + while (RTMR_REG->CNT == 0) { + }; + +#ifdef CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT + /* Enable SLWTMR0 clock power */ + SSCON_REG->PERICLKPWR1 |= BIT(SYSTEM_PERICLKPWR1_SLWTMR0CLKPWR_Pos); + + /* Enable SLWTMR0 */ + SLWTMR_REG->LDCNT = UINT32_MAX; + SLWTMR_REG->CTRL = RTOSTMR_CTRL_MDSEL_Msk | RTOSTMR_CTRL_EN_Msk; +#endif + + return 0; +} + +SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi index b5f6daad4c7..bff47d6803a 100644 --- a/dts/arm/realtek/ec/rts5912.dtsi +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -73,6 +73,25 @@ clock-names = "rc25m", "pll"; }; + slwtmr0: slwtmr0@4000c200 { + compatible = "realtek,rts5912-slwtimer"; + reg = <0x4000c200 0x10>; + interrupts = <202 0>; + clocks = <&sccon RTS5912_SCCON_PERIPH_GRP1 PERIPH_GRP1_SLWTMR0_CLKPWR>; + clock-names = "slwtmr"; + max-value = <0xFFFFFFFF>; + clock-frequency = <1000000>; + prescaler = <0>; + status = "okay"; + }; + + rtmr: rtmr@4000c500 { + compatible = "realtek,rts5912-rtmr"; + reg = <0x4000c500 0x10>; + interrupts = <204 0>; + status = "okay"; + }; + pinctrl: pin-controller@40090000 { compatible = "realtek,rts5912-pinctrl"; #address-cells = <1>; diff --git a/dts/bindings/timer/realtek,rts5912-rtmr.yaml b/dts/bindings/timer/realtek,rts5912-rtmr.yaml new file mode 100644 index 00000000000..6e6c0d3a0a6 --- /dev/null +++ b/dts/bindings/timer/realtek,rts5912-rtmr.yaml @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +description: RTOS Timer on Realtek RTS5912 EC + +compatible: "realtek,rts5912-rtmr" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true diff --git a/dts/bindings/timer/realtek,rts5912-slwtimer.yaml b/dts/bindings/timer/realtek,rts5912-slwtimer.yaml new file mode 100644 index 00000000000..aa8a3f40510 --- /dev/null +++ b/dts/bindings/timer/realtek,rts5912-slwtimer.yaml @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +description: Realtek RTS5912 32-bit slow timer + +compatible: "realtek,rts5912-slwtimer" + +include: rtc.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + max-value: + type: int + required: true + description: Maximum counter value the instance can handle + + clock-frequency: + required: true + + prescaler: + type: int + required: true + description: Timer frequency equals clock-frequency divided by the prescaler value diff --git a/soc/realtek/ec/rts5912/reg/reg_rtmr.h b/soc/realtek/ec/rts5912/reg/reg_rtmr.h new file mode 100644 index 00000000000..63957494728 --- /dev/null +++ b/soc/realtek/ec/rts5912/reg/reg_rtmr.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Realtek, SIBG-SD7 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SOC_REALTEK_RTS5912_REG_RTMR_H +#define ZEPHYR_SOC_REALTEK_RTS5912_REG_RTMR_H + +/* + * @brief RTOS Timer Controller (RTMR) + */ + +typedef struct { + volatile uint32_t LDCNT; + volatile uint32_t CNT; + volatile uint32_t CTRL; + volatile uint32_t INTSTS; +} RTOSTMR_Type; +/* CTRL */ +#define RTOSTMR_CTRL_EN_Pos (0UL) +#define RTOSTMR_CTRL_EN_Msk BIT(RTOSTMR_CTRL_EN_Pos) +#define RTOSTMR_CTRL_MDSEL_Pos (1UL) +#define RTOSTMR_CTRL_MDSEL_Msk BIT(RTOSTMR_CTRL_MDSEL_Pos) +#define RTOSTMR_CTRL_INTEN_Pos (2UL) +#define RTOSTMR_CTRL_INTEN_Msk BIT(RTOSTMR_CTRL_INTEN_Pos) +/* INTSTS */ +#define RTOSTMR_INTSTS_STS_Pos (0UL) +#define RTOSTMR_INTSTS_STS_Msk BIT(RTOSTMR_INTSTS_STS_Pos) + +#endif /* ZEPHYR_SOC_REALTEK_RTS5912_REG_RTMR_H */ From 0d8633c2baf3272f4cdca1db253ca7ffe6b6940b Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Fri, 22 Nov 2024 19:46:53 +0800 Subject: [PATCH 09/18] [nrf fromtree] driver: gpio: Add gpio driver initial version of RTS5912. Add gpio driver for Realtek RTS5912. Signed-off-by: Adam Kondraciuk (cherry picked from commit 2656029c3aac3d7dc390c1aea33616817377ab0c) Signed-off-by: Lin Yu-Cheng --- drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig | 1 + drivers/gpio/Kconfig.rts5912 | 10 + drivers/gpio/gpio_rts5912.c | 457 ++++++++++++++++++ dts/arm/realtek/ec/rts5912.dtsi | 117 +++++ dts/bindings/gpio/realtek,rts5912-gpio.yaml | 18 + .../zephyr/dt-bindings/gpio/realtek-gpio.h | 23 + soc/realtek/ec/rts5912/reg/reg_gpio.h | 50 ++ 8 files changed, 677 insertions(+) create mode 100644 drivers/gpio/Kconfig.rts5912 create mode 100644 drivers/gpio/gpio_rts5912.c create mode 100644 dts/bindings/gpio/realtek,rts5912-gpio.yaml create mode 100644 include/zephyr/dt-bindings/gpio/realtek-gpio.h create mode 100644 soc/realtek/ec/rts5912/reg/reg_gpio.h diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index dcc4b96a54f..d88cb2b87a9 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -82,6 +82,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_RP1 gpio_rp1.c) zephyr_library_sources_ifdef(CONFIG_GPIO_RPI_PICO gpio_rpi_pico.c) 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_RTS5912 gpio_rts5912.c) zephyr_library_sources_ifdef(CONFIG_GPIO_RV32M1 gpio_rv32m1.c) zephyr_library_sources_ifdef(CONFIG_GPIO_RZT2M gpio_rzt2m.c) zephyr_library_sources_ifdef(CONFIG_GPIO_SAM gpio_sam.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ba1ed8a3f64..6ca6216a046 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -167,6 +167,7 @@ source "drivers/gpio/Kconfig.renesas_rz" source "drivers/gpio/Kconfig.rp1" source "drivers/gpio/Kconfig.rpi_pico" source "drivers/gpio/Kconfig.rt1718s" +source "drivers/gpio/Kconfig.rts5912" source "drivers/gpio/Kconfig.rv32m1" source "drivers/gpio/Kconfig.rzt2m" source "drivers/gpio/Kconfig.sam" diff --git a/drivers/gpio/Kconfig.rts5912 b/drivers/gpio/Kconfig.rts5912 new file mode 100644 index 00000000000..a0d845a8b3e --- /dev/null +++ b/drivers/gpio/Kconfig.rts5912 @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +config GPIO_RTS5912 + bool "Realtek embedded controller (EC) gpio driver" + default y if DT_HAS_REALTEK_RTS5912_GPIO_ENABLED + help + Enable support for Realtek GPIO controller. diff --git a/drivers/gpio/gpio_rts5912.c b/drivers/gpio/gpio_rts5912.c new file mode 100644 index 00000000000..bda68ae39a5 --- /dev/null +++ b/drivers/gpio/gpio_rts5912.c @@ -0,0 +1,457 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#define DT_DRV_COMPAT realtek_rts5912_gpio + +#include +#include +#include +#include +#include +#include "zephyr/drivers/gpio/gpio_utils.h" +#include +#include + +#include + +LOG_MODULE_REGISTER(gpio_rts5912, CONFIG_GPIO_LOG_LEVEL); + +#define RTS5912_GPIOA_REG_BASE ((GPIO_Type *)(DT_REG_ADDR(DT_NODELABEL(gpioa)))) + +struct gpio_rts5912_config { + struct gpio_driver_config common; + volatile uint32_t *reg_base; + uint8_t num_pins; +}; + +struct gpio_rts5912_data { + struct gpio_driver_data common; + sys_slist_t callbacks; +}; + +static int pin_is_valid(const struct gpio_rts5912_config *config, gpio_pin_t pin) +{ + if (pin >= config->num_pins) { + return -EINVAL; + } + + return 0; +} + +static int pin_output_high(const struct device *port, gpio_pin_t pin) +{ + const struct gpio_rts5912_config *config = port->config; + volatile uint32_t *gcr = &config->reg_base[pin]; + + int err = pin_is_valid(config, pin); + + if (err) { + return err; + } + + if (*gcr & GPIO_GCR_OUTMD_Msk) { + /* Switch I/O mode to input mode when configuration is open-drain with output high + */ + *gcr = (*gcr & ~GPIO_GCR_DIR_Msk) | GPIO_GCR_OUTCTRL_Msk; + } else { + *gcr |= GPIO_GCR_OUTCTRL_Msk | GPIO_GCR_DIR_Msk; + } + + return 0; +} + +static int pin_output_low(const struct device *port, gpio_pin_t pin) +{ + const struct gpio_rts5912_config *config = port->config; + volatile uint32_t *gcr = &config->reg_base[pin]; + + int err = pin_is_valid(config, pin); + + if (err) { + return err; + } + + *gcr = (*gcr & ~GPIO_GCR_OUTCTRL_Msk) | GPIO_GCR_DIR_Msk; + + return 0; +} + +static int gpio_rts5912_configuration(const struct device *port, gpio_pin_t pin, gpio_flags_t flags) +{ + const struct gpio_rts5912_config *config = port->config; + volatile uint32_t *gcr = &config->reg_base[pin]; + uint32_t cfg_val = *gcr; + + int err = pin_is_valid(config, pin); + + if (err) { + return err; + } + + if (flags & GPIO_INPUT) { + cfg_val &= ~GPIO_GCR_DIR_Msk; + cfg_val &= ~GPIO_GCR_OUTCTRL_Msk; + cfg_val |= GPIO_GCR_INDETEN_Msk; + } + + if (flags & GPIO_OPEN_DRAIN) { + cfg_val |= GPIO_GCR_OUTMD_Msk; + } else { + cfg_val &= ~GPIO_GCR_OUTMD_Msk; + } + + switch (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) { + case GPIO_PULL_UP: + cfg_val &= ~GPIO_GCR_PULLDWEN_Msk; + cfg_val |= GPIO_GCR_PULLUPEN_Msk; + break; + case GPIO_PULL_DOWN: + cfg_val &= ~GPIO_GCR_PULLUPEN_Msk; + cfg_val |= GPIO_GCR_PULLDWEN_Msk; + break; + default: + break; + } + + switch (flags & RTS5912_GPIO_VOLTAGE_MASK) { + case RTS5912_GPIO_VOLTAGE_1V8: + cfg_val |= GPIO_GCR_INVOLMD_Msk; + break; + case RTS5912_GPIO_VOLTAGE_DEFAULT: + case RTS5912_GPIO_VOLTAGE_3V3: + cfg_val &= ~GPIO_GCR_INVOLMD_Msk; + break; + case RTS5912_GPIO_VOLTAGE_5V0: + return -ENOTSUP; + default: + break; + } + + *gcr = cfg_val; + + if (flags & GPIO_OUTPUT) { + if (flags & GPIO_OUTPUT_INIT_HIGH) { + pin_output_high(port, pin); + } else { + pin_output_low(port, pin); + } + } + + return 0; +} + +#ifdef CONFIG_GPIO_GET_CONFIG +static int gpio_rts5912_get_configuration(const struct device *port, gpio_pin_t pin, + gpio_flags_t *flags) +{ + const struct gpio_rts5912_config *config = port->config; + volatile uint32_t *gcr = &config->reg_base[pin]; + gpio_flags_t cfg_flag = 0x0UL; + + int err = pin_is_valid(config, pin); + + if (err) { + return err; + } + + if (*gcr & GPIO_GCR_OUTCTRL_Msk) { + cfg_flag |= GPIO_OUTPUT | GPIO_OUTPUT_INIT_HIGH; + } else { + if (*gcr & GPIO_GCR_DIR_Msk) { + cfg_flag |= GPIO_OUTPUT | GPIO_OUTPUT_INIT_LOW; + } else { + cfg_flag |= GPIO_INPUT; + if (*gcr & GPIO_GCR_INVOLMD_Msk) { + cfg_flag |= RTS5912_GPIO_VOLTAGE_1V8; + } else { + cfg_flag |= RTS5912_GPIO_VOLTAGE_3V3; + } + } + } + + if (*gcr & GPIO_GCR_OUTMD_Msk) { + cfg_flag |= GPIO_OPEN_DRAIN; + } + + if (*gcr & GPIO_GCR_PULLUPEN_Msk) { + cfg_flag |= GPIO_PULL_UP; + } else if (*gcr & GPIO_GCR_PULLDWEN_Msk) { + cfg_flag |= GPIO_PULL_DOWN; + } + + *flags = cfg_flag; + + return 0; +} +#endif + +static int gpio_rts5912_port_get_raw(const struct device *port, gpio_port_value_t *value) +{ + const struct gpio_rts5912_config *config = port->config; + gpio_port_value_t ret_val = 0; + uint16_t mask = 0x1U; + + for (gpio_pin_t i = 0; i < config->num_pins; i++) { + if (config->reg_base[i] & GPIO_GCR_PINSTS_Msk) { + ret_val |= (gpio_port_value_t)mask; + } + mask <<= 1; + } + + *value = ret_val; + + return 0; +} + +static int gpio_rts5912_port_set_masked_raw(const struct device *port, gpio_port_pins_t mask, + gpio_port_value_t value) +{ + const struct gpio_rts5912_config *config = port->config; + uint32_t pin; + + mask &= 0x0000FFFF; + for (; mask; mask &= ~BIT(pin)) { + pin = find_lsb_set(mask) - 1; + if (pin >= config->num_pins) { + break; + } + + if (value & BIT(pin)) { + pin_output_high(port, pin); + } else { + pin_output_low(port, pin); + } + } + + return 0; +} + +static int gpio_rts5912_port_set_bits_raw(const struct device *port, gpio_port_pins_t pins) +{ + const struct gpio_rts5912_config *config = port->config; + volatile uint32_t *gcr = config->reg_base; + uint32_t pin = 0; + + pins &= 0x0000FFFF; + gpio_port_pins_t sel_pin = 1; + + for (; pins;) { + if (pins & sel_pin) { + pin_output_high(port, pin); + } + pins &= ~sel_pin; + sel_pin <<= 1; + gcr++; + pin++; + } + + return 0; +} + +static int gpio_rts5912_port_clear_bits_raw(const struct device *port, gpio_port_pins_t pins) +{ + const struct gpio_rts5912_config *config = port->config; + volatile uint32_t *gcr = config->reg_base; + uint32_t pin = 0; + + pins &= 0x0000FFFF; + gpio_port_pins_t sel_pin = 1; + + for (; pins;) { + if (pins & sel_pin) { + pin_output_low(port, pin); + } + pins &= ~sel_pin; + sel_pin <<= 1; + gcr++; + pin++; + } + + return 0; +} + +static int gpio_rts5912_port_toggle_bits(const struct device *port, gpio_port_pins_t pins) +{ + const struct gpio_rts5912_config *config = port->config; + volatile uint32_t *gcr = config->reg_base; + uint32_t pin = 0; + + pins &= 0x0000FFFF; + gpio_port_pins_t sel_pin = 0x1UL; + + for (; pins;) { + if (pins & sel_pin) { + if (*gcr & GPIO_GCR_OUTCTRL_Msk) { + pin_output_low(port, pin); + } else { + pin_output_high(port, pin); + } + } + + pins &= ~sel_pin; + sel_pin <<= 1; + gcr++; + pin++; + } + + return 0; +} + +static gpio_pin_t gpio_rts5912_get_intr_pin(volatile uint32_t *reg_base) +{ + gpio_pin_t pin = 0U; + + for (; pin < 16; pin++) { + if (reg_base[pin] & GPIO_GCR_INTSTS_Msk) { + break; + } + } + + return pin; +} + +static void gpio_rts5912_isr(const void *arg) +{ + const struct device *port = arg; + const struct gpio_rts5912_config *config = port->config; + struct gpio_rts5912_data *data = port->data; + volatile uint32_t *gcr = config->reg_base; + unsigned int key = irq_lock(); + gpio_pin_t pin = gpio_rts5912_get_intr_pin(gcr); + + if (gcr[pin] & GPIO_GCR_INTSTS_Msk) { + gcr[pin] |= GPIO_GCR_INTSTS_Msk; + + gpio_fire_callbacks(&data->callbacks, port, BIT(pin)); + } + irq_unlock(key); +} + +static int gpio_rts5912_intr_config(const struct device *port, gpio_pin_t pin, + enum gpio_int_mode mode, enum gpio_int_trig trig) +{ + const struct gpio_rts5912_config *config = port->config; + volatile uint32_t *gcr = &config->reg_base[pin]; + uint32_t cfg_val = *gcr; + uint32_t pin_index = + DT_IRQ_BY_IDX(DT_NODELABEL(gpioa), 0, irq) + + ((uint32_t)(&config->reg_base[pin]) - (uint32_t)(RTS5912_GPIOA_REG_BASE)) / 4; + + int err = pin_is_valid(config, pin); + + if (err) { + return err; + } + + switch (mode) { + case GPIO_INT_MODE_DISABLED: + cfg_val &= ~GPIO_GCR_INTEN_Msk; + irq_disable(pin_index); + *gcr = cfg_val; + return 0; + case GPIO_INT_MODE_LEVEL: + switch (trig) { + case GPIO_INT_TRIG_LOW: + cfg_val &= ~GPIO_GCR_INTCTRL_Msk; + cfg_val |= 0x03UL << GPIO_GCR_INTCTRL_Pos; + break; + case GPIO_INT_TRIG_HIGH: + cfg_val &= ~GPIO_GCR_INTCTRL_Msk; + cfg_val |= 0x04UL << GPIO_GCR_INTCTRL_Pos; + break; + default: + return -EINVAL; + } + break; + case GPIO_INT_MODE_EDGE: + switch (trig) { + case GPIO_INT_TRIG_LOW: + cfg_val &= ~GPIO_GCR_INTCTRL_Msk; + cfg_val |= 0x01UL << GPIO_GCR_INTCTRL_Pos; + break; + case GPIO_INT_TRIG_HIGH: + cfg_val &= ~GPIO_GCR_INTCTRL_Msk; + cfg_val |= 0x00UL << GPIO_GCR_INTCTRL_Pos; + break; + case GPIO_INT_TRIG_BOTH: + cfg_val &= ~GPIO_GCR_INTCTRL_Msk; + cfg_val |= 0x2UL << GPIO_GCR_INTCTRL_Pos; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + cfg_val |= GPIO_GCR_INTEN_Msk; + *gcr = cfg_val; + + irq_enable(pin_index); + + return 0; +} + +static int gpio_rts5912_manage_cb(const struct device *port, struct gpio_callback *cb, bool set) +{ + struct gpio_rts5912_data *data = port->data; + + return gpio_manage_callback(&data->callbacks, cb, set); +} + +static DEVICE_API(gpio, gpio_rts5912_driver_api) = { + .pin_configure = gpio_rts5912_configuration, +#ifdef CONFIG_GPIO_GET_CONFIG + .pin_get_config = gpio_rts5912_get_configuration, +#endif + .port_get_raw = gpio_rts5912_port_get_raw, + .port_set_masked_raw = gpio_rts5912_port_set_masked_raw, + .port_set_bits_raw = gpio_rts5912_port_set_bits_raw, + .port_clear_bits_raw = gpio_rts5912_port_clear_bits_raw, + .port_toggle_bits = gpio_rts5912_port_toggle_bits, + .pin_interrupt_configure = gpio_rts5912_intr_config, + .manage_callback = gpio_rts5912_manage_cb, +}; + +#ifdef CONFIG_GEN_ISR_TABLES +#define RTS5912_GPIO_DTNAMIC_IRQ(id) \ + for (int i = 0; i < 16 && (DT_INST_IRQ_BY_IDX(id, 0, irq) + i) < 132; i++) { \ + irq_connect_dynamic((DT_INST_IRQ_BY_IDX(id, 0, irq) + i), \ + DT_INST_IRQ(id, priority), gpio_rts5912_isr, \ + DEVICE_DT_INST_GET(id), 0U); \ + } +#else +#define RTS5912_GPIO_DTNAMIC_IRQ(id) \ + IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), gpio_rts5912_isr, \ + DEVICE_DT_INST_GET(id), 0U); +#endif + +#define GPIO_RTS5912_INIT(id) \ + static int gpio_rts5912_init_##id(const struct device *dev) \ + { \ + if (!(DT_INST_IRQ_HAS_CELL(id, irq))) { \ + return 0; \ + } \ + \ + RTS5912_GPIO_DTNAMIC_IRQ(id) \ + \ + return 0; \ + } \ + \ + static struct gpio_rts5912_data gpio_rts5912_data_##id; \ + \ + static const struct gpio_rts5912_config gpio_rts5912_config_##id = { \ + .common = {.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(id)}, \ + .reg_base = (volatile uint32_t *)DT_INST_REG_ADDR(id), \ + .num_pins = DT_INST_PROP(id, ngpios), \ + }; \ + DEVICE_DT_INST_DEFINE(id, gpio_rts5912_init_##id, NULL, &gpio_rts5912_data_##id, \ + &gpio_rts5912_config_##id, POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY, \ + &gpio_rts5912_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_RTS5912_INIT) diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi index bff47d6803a..fe1b87dcb6f 100644 --- a/dts/arm/realtek/ec/rts5912.dtsi +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -97,6 +97,123 @@ #address-cells = <1>; #size-cells = <1>; reg = <0x40090000 0x300>; + + /* GPIO0-GPIO15 */ + gpioa: gpio@40090000 { + compatible = "realtek,rts5912-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x40090000 0x40>; + ngpios = <16>; + interrupts = <0 0 1 0 2 0 3 0 + 4 0 5 0 6 0 7 0 + 8 0 9 0 10 0 11 0 + 12 0 13 0 14 0 15 0>; + }; + + /* GPIO16-GPIO31 */ + gpiob: gpio@40090040 { + compatible = "realtek,rts5912-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x40090040 0x40>; + ngpios = <16>; + interrupts = <16 0 17 0 18 0 19 0 + 20 0 21 0 22 0 23 0 + 24 0 25 0 26 0 27 0 + 28 0 29 0 30 0 31 0>; + }; + + /* GPIO32-GPIO47 */ + gpioc: gpio@40090080 { + compatible = "realtek,rts5912-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x40090080 0x40>; + ngpios = <16>; + interrupts = <32 0 33 0 34 0 35 0 + 36 0 37 0 38 0 39 0 + 40 0 41 0 42 0 43 0 + 44 0 45 0 46 0 47 0>; + }; + + /* GPIO48-GPIO63 */ + gpiod: gpio@400900c0 { + compatible = "realtek,rts5912-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x400900c0 0x40>; + ngpios = <16>; + interrupts = <48 0 49 0 50 0 51 0 + 52 0 53 0 54 0 55 0 + 56 0 57 0 58 0 59 0 + 60 0 61 0 62 0 63 0>; + }; + + /* GPIO64-GPIO79 */ + gpioe: gpio@40090100 { + compatible = "realtek,rts5912-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x40090100 0x40>; + ngpios = <16>; + interrupts = <64 0 65 0 66 0 67 0 + 68 0 69 0 70 0 71 0 + 72 0 73 0 74 0 75 0 + 76 0 77 0 78 0 79 0>; + }; + + /* GPIO80-GPIO95 */ + gpiof: gpio@40090140 { + compatible = "realtek,rts5912-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x40090140 0x40>; + ngpios = <16>; + interrupts = <80 0 81 0 82 0 83 0 + 84 0 85 0 86 0 87 0 + 88 0 89 0 90 0 91 0 + 92 0 93 0 94 0 95 0>; + }; + + /* GPIO96-GPIO111 */ + gpiog: gpio@40090180 { + compatible = "realtek,rts5912-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x40090180 0x40>; + ngpios = <16>; + interrupts = <96 0 97 0 98 0 99 0 + 100 0 101 0 102 0 103 0 + 104 0 105 0 106 0 107 0 + 108 0 109 0 110 0 111 0>; + }; + + /* GPIO112-GPIO127 */ + gpioh: gpio@400901c0 { + compatible = "realtek,rts5912-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x400901c0 0x40>; + ngpios = <16>; + interrupts = <112 0 113 0 114 0 115 0 + 116 0 117 0 118 0 119 0 + 120 0 121 0 122 0 123 0 + 124 0 125 0 126 0 127 0>; + }; + + /* GPIO128-GPIO131 */ + gpioi: gpio@40090200 { + compatible = "realtek,rts5912-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x40090200 0x10>; + ngpios = <4>; + interrupts = <128 0 129 0 130 0 131 0 + 132 0 133 0 134 0 135 0 + 136 0 137 0 138 0 139 0 + 140 0 141 0 142 0 143 0>; + }; }; }; diff --git a/dts/bindings/gpio/realtek,rts5912-gpio.yaml b/dts/bindings/gpio/realtek,rts5912-gpio.yaml new file mode 100644 index 00000000000..4bf4d44e42f --- /dev/null +++ b/dts/bindings/gpio/realtek,rts5912-gpio.yaml @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +description: Realtek RTS5912 GPIO + +compatible: "realtek,rts5912-gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + +gpio-cells: + - pin + - flags diff --git a/include/zephyr/dt-bindings/gpio/realtek-gpio.h b/include/zephyr/dt-bindings/gpio/realtek-gpio.h new file mode 100644 index 00000000000..9b346f4301b --- /dev/null +++ b/include/zephyr/dt-bindings/gpio/realtek-gpio.h @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_REALTEK_GPIO_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_REALTEK_GPIO_H_ + +#define RTS5912_GPIO_VOLTAGE_POS 11 +#define RTS5912_GPIO_VOLTAGE_MASK (3U << RTS5912_GPIO_VOLTAGE_POS) + +/** Set pin at the default voltage level */ +#define RTS5912_GPIO_VOLTAGE_DEFAULT (0U << RTS5912_GPIO_VOLTAGE_POS) +/** Set pin voltage level at 1.8 V */ +#define RTS5912_GPIO_VOLTAGE_1V8 (1U << RTS5912_GPIO_VOLTAGE_POS) +/** Set pin voltage level at 3.3 V */ +#define RTS5912_GPIO_VOLTAGE_3V3 (2U << RTS5912_GPIO_VOLTAGE_POS) +/** Set pin voltage level at 5.0 V */ +#define RTS5912_GPIO_VOLTAGE_5V0 (3U << RTS5912_GPIO_VOLTAGE_POS) + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_REALTEK_GPIO_H_ */ diff --git a/soc/realtek/ec/rts5912/reg/reg_gpio.h b/soc/realtek/ec/rts5912/reg/reg_gpio.h new file mode 100644 index 00000000000..818408c6914 --- /dev/null +++ b/soc/realtek/ec/rts5912/reg/reg_gpio.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Realtek, SIBG-SD7 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SOC_REALTEK_RTS5912_REG_GPIO_H +#define ZEPHYR_SOC_REALTEK_RTS5912_REG_GPIO_H + +/* + * @brief GPIO Controller (GPIO) + */ + +typedef struct { + volatile uint32_t GCR[132]; +} GPIO_Type; + +/* GCR */ +#define GPIO_GCR_DIR_Pos (0UL) +#define GPIO_GCR_DIR_Msk BIT(GPIO_GCR_DIR_Pos) +#define GPIO_GCR_INDETEN_Pos (1UL) +#define GPIO_GCR_INDETEN_Msk BIT(GPIO_GCR_INDETEN_Pos) +#define GPIO_GCR_INVOLMD_Pos (2UL) +#define GPIO_GCR_INVOLMD_Msk BIT(GPIO_GCR_INVOLMD_Pos) +#define GPIO_GCR_PINSTS_Pos (3UL) +#define GPIO_GCR_PINSTS_Msk BIT(GPIO_GCR_PINSTS_Pos) +#define GPIO_GCR_MFCTRL_Pos (8UL) +#define GPIO_GCR_MFCTRL_Msk GENMASK(10, 8) +#define GPIO_GCR_OUTDRV_Pos (11UL) +#define GPIO_GCR_OUTDRV_Msk BIT(GPIO_GCR_OUTDRV_Pos) +#define GPIO_GCR_SLEWRATE_Pos (12UL) +#define GPIO_GCR_SLEWRATE_Msk BIT(GPIO_GCR_SLEWRATE_Pos) +#define GPIO_GCR_PULLDWEN_Pos (13UL) +#define GPIO_GCR_PULLDWEN_Msk BIT(GPIO_GCR_PULLDWEN_Pos) +#define GPIO_GCR_PULLUPEN_Pos (14UL) +#define GPIO_GCR_PULLUPEN_Msk BIT(GPIO_GCR_PULLUPEN_Pos) +#define GPIO_GCR_SCHEN_Pos (15UL) +#define GPIO_GCR_SCHEN_Msk BIT(GPIO_GCR_SCHEN_Pos) +#define GPIO_GCR_OUTMD_Pos (16UL) +#define GPIO_GCR_OUTMD_Msk BIT(GPIO_GCR_OUTMD_Pos) +#define GPIO_GCR_OUTCTRL_Pos (17UL) +#define GPIO_GCR_OUTCTRL_Msk BIT(GPIO_GCR_OUTCTRL_Pos) +#define GPIO_GCR_INTCTRL_Pos (24UL) +#define GPIO_GCR_INTCTRL_Msk GENMASK(26, 24) +#define GPIO_GCR_INTEN_Pos (28UL) +#define GPIO_GCR_INTEN_Msk BIT(GPIO_GCR_INTEN_Pos) +#define GPIO_GCR_INTSTS_Pos (31UL) +#define GPIO_GCR_INTSTS_Msk BIT(GPIO_GCR_INTSTS_Pos) + +#endif /* ZEPHYR_SOC_REALTEK_RTS5912_REG_GPIO_H */ From e029b03cdec82f95eaacf506386740fbd2ee87bb Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Sat, 23 Nov 2024 08:20:58 +0800 Subject: [PATCH 10/18] [nrf fromtree] driver: serial: Add UART driver initial version of RTS5912. Add UART driver for Realtek RTS5912. Signed-off-by: Adam Kondraciuk (cherry picked from commit a3c0b03915d7a554a9cb4c092910ff015547ff20) Signed-off-by: Lin Yu-Cheng --- drivers/serial/CMakeLists.txt | 1 + drivers/serial/Kconfig | 2 + drivers/serial/Kconfig.realtek_rts5912 | 19 +++++ drivers/serial/uart_realtek_rts5912.c | 75 +++++++++++++++++++ dts/arm/realtek/ec/rts5912.dtsi | 18 +++++ dts/bindings/serial/realtek,rts5912-uart.yaml | 24 ++++++ 6 files changed, 139 insertions(+) create mode 100644 drivers/serial/Kconfig.realtek_rts5912 create mode 100644 drivers/serial/uart_realtek_rts5912.c create mode 100644 dts/bindings/serial/realtek,rts5912-uart.yaml diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index dcae664944d..c29700ac858 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -61,6 +61,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_RCAR uart_rcar.c) zephyr_library_sources_ifdef(CONFIG_UART_RENESAS_RA uart_renesas_ra.c) zephyr_library_sources_ifdef(CONFIG_UART_RENESAS_RZ_SCIF uart_renesas_rz_scif.c) zephyr_library_sources_ifdef(CONFIG_UART_RPI_PICO_PIO uart_rpi_pico_pio.c) +zephyr_library_sources_ifdef(CONFIG_UART_RTS5912 uart_realtek_rts5912.c) zephyr_library_sources_ifdef(CONFIG_UART_RTT_DRIVER uart_rtt.c) zephyr_library_sources_ifdef(CONFIG_UART_RV32M1_LPUART uart_rv32m1_lpuart.c) zephyr_library_sources_ifdef(CONFIG_UART_RZT2M uart_rzt2m.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 5c84620990f..baf3f7e4121 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -230,4 +230,6 @@ source "drivers/serial/Kconfig.si32" source "drivers/serial/Kconfig.wch_usart" +source "drivers/serial/Kconfig.realtek_rts5912" + endif # SERIAL diff --git a/drivers/serial/Kconfig.realtek_rts5912 b/drivers/serial/Kconfig.realtek_rts5912 new file mode 100644 index 00000000000..bafb518aa43 --- /dev/null +++ b/drivers/serial/Kconfig.realtek_rts5912 @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +config UART_RTS5912 + bool "UART driver for Realtek RTS5912 EC" + default y if DT_HAS_REALTEK_RTS5912_UART_ENABLED + select PINCTRL + select CLOCK_CONTROL + help + This option enables the RTS5912 UART wrapper driver. + +config UART_RTS5912_INIT_PRIORITY + int "RTS5912 UART wrapper init priority" + default 49 + depends on UART_RTS5912 + help + Initialization priority for Realtek RTS5912 UART wrapper driver. diff --git a/drivers/serial/uart_realtek_rts5912.c b/drivers/serial/uart_realtek_rts5912.c new file mode 100644 index 00000000000..c57c3774514 --- /dev/null +++ b/drivers/serial/uart_realtek_rts5912.c @@ -0,0 +1,75 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#define DT_DRV_COMPAT realtek_rts5912_uart + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(uart_rts5912, CONFIG_UART_LOG_LEVEL); + +BUILD_ASSERT(CONFIG_UART_RTS5912_INIT_PRIORITY < CONFIG_SERIAL_INIT_PRIORITY, + "The uart_realtek_rts5912 driver must be initialized before the uart_ns16550 driver"); + +/* device config */ +struct uart_rts5912_device_config { + const struct pinctrl_dev_config *pcfg; + const struct device *clk_dev; + struct rts5912_sccon_subsys sccon_cfg; +}; + +/** Device data structure */ +struct uart_rts5912_dev_data { +}; + +static int rts5912_uart_init(const struct device *dev) +{ + const struct uart_rts5912_device_config *const dev_cfg = dev->config; + int rc; + + if (!device_is_ready(dev_cfg->clk_dev)) { + return -ENODEV; + } + + rc = clock_control_on(dev_cfg->clk_dev, (clock_control_subsys_t)&dev_cfg->sccon_cfg); + if (rc != 0) { + return rc; + } + + rc = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (rc != 0) { + return rc; + } + + return 0; +} + +#define UART_RTS5912_DEVICE_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static const struct uart_rts5912_device_config uart_rts5912_dev_cfg_##n = { \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .sccon_cfg = \ + { \ + .clk_grp = DT_INST_CLOCKS_CELL_BY_NAME(n, uart##n, clk_grp), \ + .clk_idx = DT_INST_CLOCKS_CELL_BY_NAME(n, uart##n, clk_idx), \ + }, \ + }; \ + \ + static struct uart_rts5912_dev_data uart_rts5912_dev_data_##n; \ + \ + DEVICE_DT_INST_DEFINE(n, &rts5912_uart_init, NULL, &uart_rts5912_dev_data_##n, \ + &uart_rts5912_dev_cfg_##n, PRE_KERNEL_1, \ + CONFIG_UART_RTS5912_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(UART_RTS5912_DEVICE_INIT) diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi index fe1b87dcb6f..aa994898109 100644 --- a/dts/arm/realtek/ec/rts5912.dtsi +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -92,6 +92,24 @@ status = "okay"; }; + uart0: uart@40010100 { + compatible = "ns16550"; + reg = <0x40010100 0x100>; + reg-shift = <2>; + clock-frequency = <25000000>; + interrupts = <191 0>; + status = "disabled"; + }; + + uart0_wrapper: uart_wrapper@40010200 { + compatible = "realtek,rts5912-uart"; + reg = <0x40010200 0x0020>; + port = <0>; + clocks = <&sccon RTS5912_SCCON_UART UART0_CLKPWR>; + clock-names = "uart0"; + status = "disabled"; + }; + pinctrl: pin-controller@40090000 { compatible = "realtek,rts5912-pinctrl"; #address-cells = <1>; diff --git a/dts/bindings/serial/realtek,rts5912-uart.yaml b/dts/bindings/serial/realtek,rts5912-uart.yaml new file mode 100644 index 00000000000..b1464900490 --- /dev/null +++ b/dts/bindings/serial/realtek,rts5912-uart.yaml @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +description: Realtek RTS5912 UART + +compatible: "realtek,rts5912-uart" + +include: [uart-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + port: + type: int + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true From f82e065b67e47dbcd5fa705319a37cff9444701f Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Sat, 23 Nov 2024 08:23:08 +0800 Subject: [PATCH 11/18] [nrf fromtree] boards: realtek: Add rts5912_evb board Add support for Realtek rts5912_evb board Signed-off-by: Adam Kondraciuk (cherry picked from commit 7762d3ea2b3204d01b86371a2cae8c5459f86d7e) Signed-off-by: Lin Yu-Cheng --- boards/realtek/index.rst | 10 ++ boards/realtek/rts5912_evb/Kconfig.defconfig | 4 + .../realtek/rts5912_evb/Kconfig.rts5912_evb | 7 ++ boards/realtek/rts5912_evb/board.cmake | 6 ++ boards/realtek/rts5912_evb/board.yml | 5 + .../realtek/rts5912_evb/doc/rts5912_evb.rst | 93 ++++++++++++++++++ .../realtek/rts5912_evb/doc/rts5912evb.webp | Bin 0 -> 82900 bytes boards/realtek/rts5912_evb/rts5912_evb.dts | 36 +++++++ boards/realtek/rts5912_evb/rts5912_evb.yaml | 19 ++++ .../realtek/rts5912_evb/rts5912_evb_defconfig | 19 ++++ 10 files changed, 199 insertions(+) create mode 100644 boards/realtek/index.rst create mode 100644 boards/realtek/rts5912_evb/Kconfig.defconfig create mode 100644 boards/realtek/rts5912_evb/Kconfig.rts5912_evb create mode 100644 boards/realtek/rts5912_evb/board.cmake create mode 100644 boards/realtek/rts5912_evb/board.yml create mode 100644 boards/realtek/rts5912_evb/doc/rts5912_evb.rst create mode 100644 boards/realtek/rts5912_evb/doc/rts5912evb.webp create mode 100644 boards/realtek/rts5912_evb/rts5912_evb.dts create mode 100644 boards/realtek/rts5912_evb/rts5912_evb.yaml create mode 100644 boards/realtek/rts5912_evb/rts5912_evb_defconfig diff --git a/boards/realtek/index.rst b/boards/realtek/index.rst new file mode 100644 index 00000000000..b15a0a3d34c --- /dev/null +++ b/boards/realtek/index.rst @@ -0,0 +1,10 @@ +.. _boards-realtek: + +Realtek +####### + +.. toctree:: + :maxdepth: 1 + :glob: + + **/* diff --git a/boards/realtek/rts5912_evb/Kconfig.defconfig b/boards/realtek/rts5912_evb/Kconfig.defconfig new file mode 100644 index 00000000000..21c1e3c6514 --- /dev/null +++ b/boards/realtek/rts5912_evb/Kconfig.defconfig @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# diff --git a/boards/realtek/rts5912_evb/Kconfig.rts5912_evb b/boards/realtek/rts5912_evb/Kconfig.rts5912_evb new file mode 100644 index 00000000000..d9e479f42b9 --- /dev/null +++ b/boards/realtek/rts5912_evb/Kconfig.rts5912_evb @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +config BOARD_RTS5912_EVB + select SOC_RTS5912 diff --git a/boards/realtek/rts5912_evb/board.cmake b/boards/realtek/rts5912_evb/board.cmake new file mode 100644 index 00000000000..46c6d4bd1ff --- /dev/null +++ b/boards/realtek/rts5912_evb/board.cmake @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake) diff --git a/boards/realtek/rts5912_evb/board.yml b/boards/realtek/rts5912_evb/board.yml new file mode 100644 index 00000000000..2be9b8d1746 --- /dev/null +++ b/boards/realtek/rts5912_evb/board.yml @@ -0,0 +1,5 @@ +board: + name: rts5912_evb + vendor: realtek + socs: + - name: rts5912 diff --git a/boards/realtek/rts5912_evb/doc/rts5912_evb.rst b/boards/realtek/rts5912_evb/doc/rts5912_evb.rst new file mode 100644 index 00000000000..cb77ad613cc --- /dev/null +++ b/boards/realtek/rts5912_evb/doc/rts5912_evb.rst @@ -0,0 +1,93 @@ +.. _rts5912_evb: + +Realtek RTS5912 Evaluation Board +################################ + +Overview +******** + +The RTS5912 EVB is a development platform to evaluate the Realtek RTS5912 embedded controller. + +.. figure:: rts5912evb.webp + :width: 800px + :align: center + :alt: RTS5912-GR EVB + +Hardware +******** + +- Realtek-M300 Processor (compatible to Cortex-M33) +- Memory: + + - 384 KB SRAM + - 64 KB ROM + - 512 KB Flash(MCM) + - 256 B Battery SRAM +- PECI interface 3.1 +- FAN, PWM and TACHO pins +- 6x I2C instances +- eSPI header +- 1x PS/2 ports +- Keyboard interface headers + +For more information about the evb board please see `RTS5912_EVB_Schematics`_ and `RTS5912_DATASHEET`_ + +The board is powered through the +5V USB Type-C connector or adaptor. + +Supported Features +================== + +The ``rts5912_evb`` supports the following hardware features: + ++-----------+------------+-------------------------------------+ +| Interface | Controller | Driver/Component | ++===========+============+=====================================+ +| NVIC | on-chip | nested vector interrupt controller | ++-----------+------------+-------------------------------------+ +| UART | on-chip | serial | ++-----------+------------+-------------------------------------+ +| GPIO | on-chip | gpio | ++-----------+------------+-------------------------------------+ +| PINCTRL | on-chip | pinctrl | ++-----------+------------+-------------------------------------+ +| SCCON | on-chip | clock_control | ++-----------+------------+-------------------------------------+ +| RTMR | on-chip | timer | ++-----------+------------+-------------------------------------+ + +Other hardware features are not currently supported by Zephyr. + +Programming and Debugging +************************* + +Building +======== + +#. Build :zephyr:code-sample:`hello_world` application as you would normally do. + +#. The file ``zephyr.rts5912.bin`` will be created if the build system can build successfully. + This binary image can be found under file "build/zephyr/". + +Flashing +======== + +#. Connect Dediprog into header ``J81`` and ``J82``. +#. Use Dediprog SF600 programmer to write the binary into the external flash ``U10`` at the address 0x0. +#. Power off the board. +#. Set the strap pin ``GPIO108`` to high and power on the board. + +Debugging +========= + +Using SWD or JTAG with ULINPRO. + +References +********** + +.. target-notes:: + +.. _RTS5912_EVB_Schematics: + https://github.com/JasonLin-RealTek/Realtek_EC/blob/main/RTS5912_EVB_Schematic_Ver%201.1_20240701_1407.pdf + +.. _RTS5912_DATASHEET: + https://github.com/JasonLin-RealTek/Realtek_EC/blob/main/RTS5912_datasheet_brief.pdf diff --git a/boards/realtek/rts5912_evb/doc/rts5912evb.webp b/boards/realtek/rts5912_evb/doc/rts5912evb.webp new file mode 100644 index 0000000000000000000000000000000000000000..e8a7d96420af3ad3e67ac591d0b8ec6ebca08f0c GIT binary patch literal 82900 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 K5O9hKM@89n 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 +#include +#include +/ { + model = "Realtek RTS5912 Evaluation Board"; + compatible = "realtek,rts5912-evb"; + + chosen { + zephyr,sram = &sram0; + zephyr,console = &uart0; + zephyr,flash = &flash0; + }; +}; + +&uart0 { + status = "okay"; + current-speed = <115200>; +}; + +&uart0_wrapper { + status = "okay"; + pinctrl-0 = <&uart_rx_gpio113 &uart_tx_gpio114>; + pinctrl-names = "default"; +}; + +&swj_port { + status = "okay"; +}; diff --git a/boards/realtek/rts5912_evb/rts5912_evb.yaml b/boards/realtek/rts5912_evb/rts5912_evb.yaml new file mode 100644 index 00000000000..199a6e1027a --- /dev/null +++ b/boards/realtek/rts5912_evb/rts5912_evb.yaml @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +identifier: rts5912_evb +name: RTS5912-EVB +type: mcu +arch: arm +toolchain: + - zephyr + - gnuarmemb + - xtools +ram: 64 +flash: 320 +supported: + - gpio + - pinmux +vendor: realtek diff --git a/boards/realtek/rts5912_evb/rts5912_evb_defconfig b/boards/realtek/rts5912_evb/rts5912_evb_defconfig new file mode 100644 index 00000000000..64454c374d5 --- /dev/null +++ b/boards/realtek/rts5912_evb/rts5912_evb_defconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +# Enable RTS5912 image tool +CONFIG_REALTEK_RTS5912_BOOTROM_HEADER=y + +# Serial Driver +CONFIG_SERIAL=y +CONFIG_UART_NS16550=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# Console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +# Enable GPIO +CONFIG_GPIO=y From ce479975cd0d450f2a772cba4dbf8547ff82c75b Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Thu, 28 Nov 2024 11:23:08 +0800 Subject: [PATCH 12/18] [nrf fromtree] MAINTAINERS: add maintainer for Realtek EC api and driver This commit adds api and driver of Realtek EC to maintainers.yml Signed-off-by: Adam Kondraciuk (cherry picked from commit a4d0591ddf2daa68cf75bcd0148eab20836adc6b) Signed-off-by: Lin Yu-Cheng --- MAINTAINERS.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index f5e76e43935..5eed35c70da 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -3931,6 +3931,19 @@ OpenTitan Platforms: description: >- OpenTitan boards, SOCs, dts files and related drivers. +Realtek EC Platforms: + status: maintained + maintainers: + - JasonLin-RealTek + files: + - boards/realtek/ + - drivers/*/*rts5912* + - dts/bindings/*/*rts5912* + - dts/arm/realtek/ec/ + - soc/realtek/ec/ + labels: + - "platform: Realtek EC" + Renesas SmartBond Platforms: status: maintained maintainers: From 5fef9a68e2a8092211c1cfc1553c91261408c83e Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Fri, 21 Mar 2025 13:30:34 +0100 Subject: [PATCH 13/18] [nrf fromlist] dts: bindings: clock: add nordic,nrfs-audiopll Introduce Nordic NRFS AudioPLL bindings. Upstream PR #: 87471 Signed-off-by: Bjarki Arge Andreasen --- dts/bindings/clock/nordic,nrfs-audiopll.yaml | 18 ++++++++++++++++++ .../zephyr/dt-bindings/clock/nrfs-audiopll.h | 15 +++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 dts/bindings/clock/nordic,nrfs-audiopll.yaml create mode 100644 include/zephyr/dt-bindings/clock/nrfs-audiopll.h diff --git a/dts/bindings/clock/nordic,nrfs-audiopll.yaml b/dts/bindings/clock/nordic,nrfs-audiopll.yaml new file mode 100644 index 00000000000..a305b1c8088 --- /dev/null +++ b/dts/bindings/clock/nordic,nrfs-audiopll.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Nordic NRFS AudioPLL clock + +compatible: "nordic,nrfs-audiopll" + +include: base.yaml + +properties: + frequency: + type: int + required: true + description: | + Initial frequency in Hertz. + + See zephyr/include/zephyr/dt-bindings/nrfs_audiopll.h for + predefined frequencies. diff --git a/include/zephyr/dt-bindings/clock/nrfs-audiopll.h b/include/zephyr/dt-bindings/clock/nrfs-audiopll.h new file mode 100644 index 00000000000..b4c3975b5e1 --- /dev/null +++ b/include/zephyr/dt-bindings/clock/nrfs-audiopll.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_NRFS_AUDIOPLL_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_NRFS_AUDIOPLL_H_ + +#define NRFS_AUDIOPLL_FREQ_MIN 10666707 +#define NRFS_AUDIOPLL_FREQ_AUDIO_44K1 11289591 +#define NRFS_AUDIOPLL_FREQ_AUDIO_48K 12287963 +#define NRFS_AUDIOPLL_FREQ_MAX 13333292 + +#endif /* #define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_NRFS_AUDIOPLL_H_ */ From c31a03b96bc4a9b9b0c23b72d6c0014630147e8e Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Fri, 21 Mar 2025 13:31:28 +0100 Subject: [PATCH 14/18] [nrf fromlist] dts: common: nordic: nrf54h20: add audiopll node Add audiopll node to nrf54h20. Upstream PR #: 87471 Signed-off-by: Bjarki Arge Andreasen --- dts/common/nordic/nrf54h20.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dts/common/nordic/nrf54h20.dtsi b/dts/common/nordic/nrf54h20.dtsi index 1fe345e33f2..2211a4b6643 100644 --- a/dts/common/nordic/nrf54h20.dtsi +++ b/dts/common/nordic/nrf54h20.dtsi @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -201,6 +202,12 @@ clocks = <&hfxo>, <&lfxo>; clock-names = "hfxo", "lfxo"; }; + + audiopll: audiopll { + compatible = "nordic,nrfs-audiopll"; + frequency = ; + status = "disabled"; + }; }; gpd: global-power-domain { From 2415ddef78d47c658121d03697fa28b001b0f2f2 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Fri, 21 Mar 2025 13:32:29 +0100 Subject: [PATCH 15/18] [nrf fromlist] drivers: clock_control: add nrfs_audiopll clock driver Add NRFS AudioPLL clock control device driver. Upstream PR #: 87471 Signed-off-by: Bjarki Arge Andreasen --- drivers/clock_control/CMakeLists.txt | 1 + drivers/clock_control/Kconfig.nrf | 6 + .../clock_control_nrf2_audiopll.c | 307 ++++++++++++++++++ 3 files changed, 314 insertions(+) create mode 100644 drivers/clock_control/clock_control_nrf2_audiopll.c diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 8c451149a58..33ad557742a 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -39,6 +39,7 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_PWM clock_cont zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_RPI_PICO clock_control_rpi_pico.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF2_GLOBAL_HSFLL clock_control_nrf2_global_hsfll.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_RTS5912_SCCON clock_control_rts5912_sccon.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF2_AUDIOPLL clock_control_nrf2_audiopll.c) if(CONFIG_CLOCK_CONTROL_NRF2) zephyr_library_sources(clock_control_nrf2_common.c) diff --git a/drivers/clock_control/Kconfig.nrf b/drivers/clock_control/Kconfig.nrf index b5b066ac48e..da330f7b08e 100644 --- a/drivers/clock_control/Kconfig.nrf +++ b/drivers/clock_control/Kconfig.nrf @@ -226,4 +226,10 @@ config CLOCK_CONTROL_NRF2_GLOBAL_HSFLL_INIT_PRIORITY endif # CLOCK_CONTROL_NRF2_GLOBAL_HSFLL +config CLOCK_CONTROL_NRF2_AUDIOPLL + bool "NRFS AudioPLL driver support" + depends on DT_HAS_NORDIC_NRFS_AUDIOPLL_ENABLED + depends on NRFS_AUDIOPLL_SERVICE_ENABLED + default y + endif # CLOCK_CONTROL_NRF2 diff --git a/drivers/clock_control/clock_control_nrf2_audiopll.c b/drivers/clock_control/clock_control_nrf2_audiopll.c new file mode 100644 index 00000000000..c4df962144a --- /dev/null +++ b/drivers/clock_control/clock_control_nrf2_audiopll.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nordic_nrfs_audiopll + +#include "clock_control_nrf2_common.h" +#include +#include +#include +#include +#include + +#include +LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +#define SHIM_DEFAULT_PRESCALER AUDIOPLL_DIV_12 + +BUILD_ASSERT( + DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, + "multiple instances not supported" +); + +BUILD_ASSERT(DT_INST_PROP(0, frequency) >= NRFS_AUDIOPLL_FREQ_MIN); +BUILD_ASSERT(DT_INST_PROP(0, frequency) <= NRFS_AUDIOPLL_FREQ_MAX); + +struct shim_data { + struct onoff_manager mgr; + onoff_notify_fn mgr_notify; + const struct device *dev; + struct k_sem evt_sem; + nrfs_audiopll_evt_type_t evt; +}; + +static int shim_nrfs_request_enable(const struct device *dev) +{ + struct shim_data *dev_data = dev->data; + nrfs_err_t err; + + LOG_DBG("send enable request"); + + dev_data->evt = NRFS_AUDIOPLL_EVT_ENABLED; + err = nrfs_audiopll_enable_request(dev_data); + if (err != NRFS_SUCCESS) { + return -EIO; + } + + return 0; +} + +static int shim_nrfs_request_disable(const struct device *dev) +{ + struct shim_data *dev_data = dev->data; + nrfs_err_t err; + + LOG_DBG("send disable request"); + + dev_data->evt = NRFS_AUDIOPLL_EVT_DISABLED; + err = nrfs_audiopll_disable_request(dev_data); + if (err != NRFS_SUCCESS) { + return -EIO; + } + + return 0; +} + +static void onoff_start_option(struct onoff_manager *mgr, onoff_notify_fn notify) +{ + struct shim_data *dev_data = CONTAINER_OF(mgr, struct shim_data, mgr); + const struct device *dev = dev_data->dev; + int ret; + + dev_data->mgr_notify = notify; + + ret = shim_nrfs_request_enable(dev); + if (ret) { + dev_data->mgr_notify = NULL; + notify(mgr, -EIO); + } +} + +static void onoff_stop_option(struct onoff_manager *mgr, onoff_notify_fn notify) +{ + struct shim_data *dev_data = CONTAINER_OF(mgr, struct shim_data, mgr); + const struct device *dev = dev_data->dev; + int ret; + + dev_data->mgr_notify = notify; + + ret = shim_nrfs_request_disable(dev); + if (ret) { + dev_data->mgr_notify = NULL; + notify(mgr, -EIO); + } +} + +static const struct onoff_transitions shim_mgr_transitions = { + .start = onoff_start_option, + .stop = onoff_stop_option +}; + +/* + * Formula: + * + * frequency = ((4 + (freq_fraction * 2^-16)) * 32000000) / 12 + * + * Simplified linear approximation: + * + * frequency = 10666666 + (((13333292 - 10666666) / 65535) * freq_fraction) + * frequency = 10666666 + ((2666626 / 65535) * freq_fraction) + * frequency = ((10666666 * 65535) + (2666626 * freq_fraction)) / 65535 + * frequency = (699039956310 + (2666626 * freq_fraction)) / 65535 + * + * Isolate freq_fraction: + * + * frequency = (699039956310 + (2666626 * freq_fraction)) / 65535 + * frequency * 65535 = 699039956310 + (2666626 * freq_fraction) + * (frequency * 65535) - 699039956310 = 2666626 * freq_fraction + * freq_fraction = ((frequency * 65535) - 699039956310) / 2666626 + */ +static uint16_t shim_frequency_to_freq_fraction(uint32_t frequency) +{ + uint64_t freq_fraction; + + freq_fraction = frequency; + freq_fraction *= 65535; + freq_fraction -= 699039956310; + freq_fraction = DIV_ROUND_CLOSEST(freq_fraction, 2666626); + + return (uint16_t)freq_fraction; +} + +static int shim_nrfs_request_freq_sync(const struct device *dev, uint16_t freq_fraction) +{ + struct shim_data *dev_data = dev->data; + nrfs_err_t err; + + LOG_DBG("send freq request"); + + err = nrfs_audiopll_request_freq(freq_fraction, dev_data); + if (err != NRFS_SUCCESS) { + return -EIO; + } + + k_sem_take(&dev_data->evt_sem, K_FOREVER); + return dev_data->evt == NRFS_AUDIOPLL_EVT_FREQ_CONFIRMED ? 0 : -EIO; +} + +static int shim_nrfs_request_prescaler_sync(const struct device *dev, + enum audiopll_prescaler_div div) +{ + struct shim_data *dev_data = dev->data; + nrfs_err_t err; + + LOG_DBG("send prescaler request"); + + err = nrfs_audiopll_request_prescaler(div, dev_data); + if (err != NRFS_SUCCESS) { + return -EIO; + } + + k_sem_take(&dev_data->evt_sem, K_FOREVER); + return dev_data->evt == NRFS_AUDIOPLL_EVT_PRESCALER_CONFIRMED ? 0 : -EIO; +} + +static void shim_nrfs_audiopll_init_evt_handler(nrfs_audiopll_evt_t const *evt, void *context) +{ + struct shim_data *dev_data = context; + + LOG_DBG("init resp evt %u", (uint32_t)evt->type); + + dev_data->evt = evt->type; + k_sem_give(&dev_data->evt_sem); +} + +static void shim_nrfs_audiopll_evt_handler(nrfs_audiopll_evt_t const *evt, void *context) +{ + struct shim_data *dev_data = context; + int ret; + + LOG_DBG("resp evt %u", (uint32_t)evt->type); + + if (dev_data->mgr_notify == NULL) { + return; + } + + ret = evt->type == dev_data->evt ? 0 : -EIO; + dev_data->mgr_notify(&dev_data->mgr, ret); +} + +static int api_request_audiopll(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + struct shim_data *dev_data = dev->data; + struct onoff_manager *mgr = &dev_data->mgr; + + ARG_UNUSED(spec); + + return onoff_request(mgr, cli); +} + +static int api_release_audiopll(const struct device *dev, + const struct nrf_clock_spec *spec) +{ + struct shim_data *dev_data = dev->data; + struct onoff_manager *mgr = &dev_data->mgr; + + ARG_UNUSED(spec); + + return onoff_release(mgr); +} + +static int api_cancel_or_release_audiopll(const struct device *dev, + const struct nrf_clock_spec *spec, + struct onoff_client *cli) +{ + struct shim_data *dev_data = dev->data; + struct onoff_manager *mgr = &dev_data->mgr; + + ARG_UNUSED(spec); + + return onoff_cancel_or_release(mgr, cli); +} + +static DEVICE_API(nrf_clock_control, shim_driver_api) = { + .std_api = { + .on = api_nosys_on_off, + .off = api_nosys_on_off, + }, + .request = api_request_audiopll, + .release = api_release_audiopll, + .cancel_or_release = api_cancel_or_release_audiopll, +}; + +static int shim_init(const struct device *dev) +{ + struct shim_data *dev_data = dev->data; + nrfs_err_t err; + int ret; + uint16_t freq_fraction; + + LOG_DBG("waiting for nrfs backend connected"); + err = nrfs_backend_wait_for_connection(K_FOREVER); + if (err != NRFS_SUCCESS) { + LOG_ERR("nrfs backend not connected"); + return -ENODEV; + } + + k_sem_init(&dev_data->evt_sem, 0, 1); + + err = nrfs_audiopll_init(shim_nrfs_audiopll_init_evt_handler); + if (err != NRFS_SUCCESS) { + LOG_ERR("failed to init audiopll service"); + return -ENODEV; + } + + ret = shim_nrfs_request_prescaler_sync(dev, SHIM_DEFAULT_PRESCALER); + if (ret) { + LOG_ERR("failed to set prescaler divider"); + return ret; + } + + freq_fraction = shim_frequency_to_freq_fraction(DT_INST_PROP(0, frequency)); + + LOG_DBG("requesting freq_fraction %u for frequency %uHz", + freq_fraction, + DT_INST_PROP(0, frequency)); + + ret = shim_nrfs_request_freq_sync(dev, freq_fraction); + if (ret) { + LOG_ERR("failed to set freq_fraction"); + return ret; + } + + nrfs_audiopll_uninit(); + + ret = onoff_manager_init(&dev_data->mgr, &shim_mgr_transitions); + if (ret < 0) { + LOG_ERR("failed to init onoff manager"); + return ret; + } + + err = nrfs_audiopll_init(shim_nrfs_audiopll_evt_handler); + if (err != NRFS_SUCCESS) { + LOG_ERR("failed to init audiopll service"); + return -ENODEV; + } + + return 0; +} + +static struct shim_data shim_data = { + .dev = DEVICE_DT_INST_GET(0), +}; + +DEVICE_DT_INST_DEFINE( + 0, + shim_init, + NULL, + &shim_data, + NULL, + POST_KERNEL, + UTIL_INC(CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO), + &shim_driver_api, +); From 37d40916e32091a5a173425c0f1eca45231e9ed7 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Fri, 28 Mar 2025 10:45:51 +0100 Subject: [PATCH 16/18] [nrf fromlist] samples: boards: nordic: add audiopll Add audiopll to sample. Upstream PR #: 87471 Signed-off-by: Bjarki Arge Andreasen --- .../nordic/clock_control/configs/audiopll.conf | 4 ++++ .../nordic/clock_control/configs/audiopll.overlay | 15 +++++++++++++++ samples/boards/nordic/clock_control/sample.yaml | 5 +++++ 3 files changed, 24 insertions(+) create mode 100644 samples/boards/nordic/clock_control/configs/audiopll.conf create mode 100644 samples/boards/nordic/clock_control/configs/audiopll.overlay diff --git a/samples/boards/nordic/clock_control/configs/audiopll.conf b/samples/boards/nordic/clock_control/configs/audiopll.conf new file mode 100644 index 00000000000..c06f206ab86 --- /dev/null +++ b/samples/boards/nordic/clock_control/configs/audiopll.conf @@ -0,0 +1,4 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_CLOCK_CONTROL=y diff --git a/samples/boards/nordic/clock_control/configs/audiopll.overlay b/samples/boards/nordic/clock_control/configs/audiopll.overlay new file mode 100644 index 00000000000..3e99bbdbeb8 --- /dev/null +++ b/samples/boards/nordic/clock_control/configs/audiopll.overlay @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/{ + aliases { + sample-clock = &audiopll; + }; +}; + +&audiopll { + status = "okay"; +}; diff --git a/samples/boards/nordic/clock_control/sample.yaml b/samples/boards/nordic/clock_control/sample.yaml index 820ad0de75f..ee63e7c518e 100644 --- a/samples/boards/nordic/clock_control/sample.yaml +++ b/samples/boards/nordic/clock_control/sample.yaml @@ -41,3 +41,8 @@ tests: extra_args: - CONF_FILE="configs/global_hsfll.conf" - DTC_OVERLAY_FILE="configs/global_hsfll.overlay" + sample.boards.nrf.clock_control.audiopll: + filter: dt_nodelabel_enabled("audiopll") + extra_args: + - CONF_FILE="configs/audiopll.conf" + - DTC_OVERLAY_FILE="configs/audiopll.overlay" From bb4940bdfb375804f43801bb3835f2f7c5d10e3a Mon Sep 17 00:00:00 2001 From: Adam Kondraciuk Date: Tue, 1 Apr 2025 12:31:59 +0200 Subject: [PATCH 17/18] Revert "[nrf fromlist] drivers: i2s: Add support for nRF TDM peripherals" This reverts commit a4e63c80ca707673097c21ce87af02e6c4e137f6. Signed-off-by: Adam Kondraciuk --- drivers/i2s/CMakeLists.txt | 1 - drivers/i2s/Kconfig.nrfx | 21 - drivers/i2s/i2s_nrfx_tdm.c | 1109 ------------------------------------ 3 files changed, 1131 deletions(-) delete mode 100644 drivers/i2s/i2s_nrfx_tdm.c diff --git a/drivers/i2s/CMakeLists.txt b/drivers/i2s/CMakeLists.txt index 29a9da412ce..7857b6e863b 100644 --- a/drivers/i2s/CMakeLists.txt +++ b/drivers/i2s/CMakeLists.txt @@ -11,7 +11,6 @@ zephyr_library_sources_ifdef(CONFIG_I2S_STM32 i2s_ll_stm32.c) zephyr_library_sources_ifdef(CONFIG_I2S_LITEX i2s_litex.c) zephyr_library_sources_ifdef(CONFIG_I2S_MCUX_FLEXCOMM i2s_mcux_flexcomm.c) zephyr_library_sources_ifdef(CONFIG_I2S_NRFX i2s_nrfx.c) -zephyr_library_sources_ifdef(CONFIG_TDM_NRFX i2s_nrfx_tdm.c) zephyr_library_sources_ifdef(CONFIG_I2S_MCUX_SAI i2s_mcux_sai.c) zephyr_library_sources_ifdef(CONFIG_I2S_ESP32 i2s_esp32.c) zephyr_library_sources_ifdef(CONFIG_I2S_TEST i2s_test.c) diff --git a/drivers/i2s/Kconfig.nrfx b/drivers/i2s/Kconfig.nrfx index 4b7a9f6177a..b36f3eb9c64 100644 --- a/drivers/i2s/Kconfig.nrfx +++ b/drivers/i2s/Kconfig.nrfx @@ -22,24 +22,3 @@ config I2S_NRFX_TX_BLOCK_COUNT default 4 endif # I2S_NRFX - -menuconfig TDM_NRFX - bool "nRF TDM nrfx driver" - default y - depends on DT_HAS_NORDIC_NRF_TDM_ENABLED - select NRFX_TDM130 if HAS_HW_NRF_TDM130 - select PINCTRL - help - Enable support for nrfx TDM driver for nRF MCU series. - -if TDM_NRFX - -config TDM_NRFX_RX_BLOCK_COUNT - int "RX queue length" - default 4 - -config TDM_NRFX_TX_BLOCK_COUNT - int "TX queue length" - default 4 - -endif # TDM_NRFX diff --git a/drivers/i2s/i2s_nrfx_tdm.c b/drivers/i2s/i2s_nrfx_tdm.c deleted file mode 100644 index 0482fa732f6..00000000000 --- a/drivers/i2s/i2s_nrfx_tdm.c +++ /dev/null @@ -1,1109 +0,0 @@ -/* - * Copyright (c) 2024 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -LOG_MODULE_REGISTER(tdm_nrfx, CONFIG_I2S_LOG_LEVEL); - -/* The application must provide buffers that are to be used in the next - * part of the transfer. - */ -#define NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED BIT(0) - -/* The TDM peripheral has been stopped and all buffers that were passed - * to the driver have been released. - */ -#define NRFX_TDM_STATUS_TRANSFER_STOPPED BIT(1) - -typedef struct { - uint32_t *p_rx_buffer; - uint32_t const *p_tx_buffer; - void *p_tx_mem_slab; - void *p_rx_mem_slab; - uint16_t buffer_size; -} tdm_buffers_t; - -typedef void (*tdm_data_handler_t)(tdm_buffers_t const *p_released, uint32_t status); - -typedef struct { - tdm_data_handler_t handler; - bool use_rx: 1; - bool use_tx: 1; - bool rx_ready: 1; - bool tx_ready: 1; - bool buffers_needed: 1; - bool buffers_reused: 1; - tdm_buffers_t next_buffers; - tdm_buffers_t current_buffers; -} tdm_ctrl_t; - -struct stream_cfg { - struct i2s_config cfg; - nrf_tdm_config_t nrfx_cfg; -}; - -struct tdm_buf { - void *mem_block; - size_t size; - void *dmm_buf; -}; - -struct tdm_drv_cfg { - tdm_data_handler_t data_handler; - const struct pinctrl_dev_config *pcfg; - NRF_TDM_Type *p_reg; - void *mem_reg; - tdm_ctrl_t *control_data; - uint32_t mck_frequency; - uint32_t pclk_frequency; - enum clock_source { - PCLK, - ACLK - } clk_src; -}; - -struct tdm_drv_data { - struct onoff_manager *clk_mgr; - struct onoff_client clk_cli; - struct stream_cfg tx; - struct k_msgq tx_queue; - struct stream_cfg rx; - struct k_msgq rx_queue; - const struct tdm_drv_cfg *drv_cfg; - const uint32_t *last_tx_buffer; - void *last_tx_mem_slab; - enum i2s_state state; - enum i2s_dir active_dir; - bool stop; /* stop after the current (TX or RX) block */ - bool discard_rx; /* discard further RX blocks */ - volatile bool next_tx_buffer_needed; - bool tx_configured: 1; - bool rx_configured: 1; - bool request_clock: 1; -}; - -void tdm_irq_handler(const struct device *dev) -{ - const struct tdm_drv_cfg *drv_cfg = dev->config; - NRF_TDM_Type *p_reg = drv_cfg->p_reg; - tdm_ctrl_t *ctrl_data = drv_cfg->control_data; - uint32_t event_mask = 0; - - if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_MAXCNT)) { - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_MAXCNT); - } - if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_TXPTRUPD)) { - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); - event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_TXPTRUPD); - ctrl_data->tx_ready = true; - if (ctrl_data->use_tx && ctrl_data->buffers_needed) { - ctrl_data->buffers_reused = true; - } - } - if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_RXPTRUPD)) { - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); - event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_RXPTRUPD); - ctrl_data->rx_ready = true; - if (ctrl_data->use_rx && ctrl_data->buffers_needed) { - ctrl_data->buffers_reused = true; - } - } - if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_STOPPED)) { - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_STOPPED); - event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_STOPPED); - nrf_tdm_int_disable(p_reg, NRF_TDM_INT_STOPPED_MASK_MASK); - nrf_tdm_disable(p_reg); - /* When stopped, release all buffers, including these scheduled for - * the next part of the transfer, and signal that the transfer has - * finished. - */ - ctrl_data->handler(&ctrl_data->current_buffers, 0); - ctrl_data->handler(&ctrl_data->next_buffers, NRFX_TDM_STATUS_TRANSFER_STOPPED); - } else { - /* Check if the requested transfer has been completed: - * - full-duplex mode - */ - if ((ctrl_data->use_tx && ctrl_data->use_rx && ctrl_data->tx_ready && - ctrl_data->rx_ready) || - /* - TX only mode */ - (!ctrl_data->use_rx && ctrl_data->tx_ready) || - /* - RX only mode */ - (!ctrl_data->use_tx && ctrl_data->rx_ready)) { - ctrl_data->tx_ready = false; - ctrl_data->rx_ready = false; - - /* If the application did not supply the buffers for the next - * part of the transfer until this moment, the current buffers - * cannot be released, since the I2S peripheral already started - * using them. Signal this situation to the application by - * passing NULL instead of the structure with released buffers. - */ - if (ctrl_data->buffers_reused) { - ctrl_data->buffers_reused = false; - /* This will most likely be set at this point. However, there is - * a small time window between TXPTRUPD and RXPTRUPD events, - * and it is theoretically possible that next buffers will be - * set in this window, so to be sure this flag is set to true, - * set it explicitly. - */ - ctrl_data->buffers_needed = true; - ctrl_data->handler(NULL, NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED); - } else { - /* Buffers that have been used by the I2S peripheral (current) - * are now released and will be returned to the application, - * and the ones scheduled to be used as next become the current - * ones. - */ - tdm_buffers_t released_buffers = ctrl_data->current_buffers; - - ctrl_data->current_buffers = ctrl_data->next_buffers; - ctrl_data->next_buffers.p_rx_buffer = NULL; - ctrl_data->next_buffers.p_tx_buffer = NULL; - ctrl_data->buffers_needed = true; - ctrl_data->handler(&released_buffers, - NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED); - } - } - } -} - -static uint32_t div_calculate(uint32_t src_freq, uint32_t requested_clk_freq) -{ - enum { - MCKCONST = 1048576 - }; - /* As specified in the PS: - * - * DIV = 4096 * floor(f_MCK * 1048576 / - * (f_source + f_MCK / 2)) - * f_actual = f_source / - * floor(1048576 * 4096 / DIV) - */ - - uint32_t ck_div = (uint32_t)(((uint64_t)requested_clk_freq * MCKCONST) / - (src_freq + requested_clk_freq / 2)); - return (ck_div * 4096); -} - -static bool get_next_tx_buffer(struct tdm_drv_data *drv_data, tdm_buffers_t *buffers) -{ - struct tdm_buf buf; - int ret = k_msgq_get(&drv_data->tx_queue, &buf, K_NO_WAIT); - - if (ret != 0) { - return false; - } - buffers->p_tx_buffer = buf.dmm_buf; - buffers->p_tx_mem_slab = buf.mem_block; - buffers->buffer_size = buf.size / sizeof(uint32_t); - return true; -} - -static bool get_next_rx_buffer(struct tdm_drv_data *drv_data, tdm_buffers_t *buffers) -{ - const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; - int ret = k_mem_slab_alloc(drv_data->rx.cfg.mem_slab, &buffers->p_rx_mem_slab, K_NO_WAIT); - - if (ret < 0) { - LOG_ERR("Failed to allocate next RX buffer: %d", ret); - return false; - } - ret = dmm_buffer_in_prepare(drv_cfg->mem_reg, buffers->p_rx_mem_slab, - buffers->buffer_size * sizeof(uint32_t), - (void **)&buffers->p_rx_buffer); - if (ret < 0) { - LOG_ERR("Failed to prepare buffer: %d", ret); - return false; - } - - return true; -} - -static void free_tx_buffer(struct tdm_drv_data *drv_data, struct tdm_buf *buf) -{ - const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; - - (void)dmm_buffer_out_release(drv_cfg->mem_reg, buf->dmm_buf); - k_mem_slab_free(drv_data->tx.cfg.mem_slab, buf->mem_block); - LOG_DBG("Freed TX %p", buf->mem_block); -} - -static void free_rx_buffer(struct tdm_drv_data *drv_data, struct tdm_buf *buf) -{ - const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; - - (void)dmm_buffer_in_release(drv_cfg->mem_reg, buf->mem_block, buf->size, buf->dmm_buf); - k_mem_slab_free(drv_data->rx.cfg.mem_slab, buf->mem_block); - LOG_DBG("Freed RX %p", buf->mem_block); -} - -static void tdm_start(struct tdm_drv_data *drv_data, tdm_buffers_t const *p_initial_buffers) -{ - NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; - tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; - - __ASSERT_NO_MSG(p_initial_buffers->p_rx_buffer != NULL || - p_initial_buffers->p_tx_buffer != NULL); - ctrl_data->use_rx = (p_initial_buffers->p_rx_buffer != NULL); - ctrl_data->use_tx = (p_initial_buffers->p_tx_buffer != NULL); - ctrl_data->rx_ready = false; - ctrl_data->tx_ready = false; - ctrl_data->buffers_needed = false; - - ctrl_data->next_buffers = *p_initial_buffers; - ctrl_data->current_buffers.p_rx_buffer = NULL; - ctrl_data->current_buffers.p_tx_buffer = NULL; - nrf_tdm_enable(p_reg); - - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); - - nrf_tdm_int_enable( - p_reg, - (p_initial_buffers->p_rx_buffer ? NRF_TDM_INT_RXPTRUPD_MASK_MASK : 0UL) | - (p_initial_buffers->p_tx_buffer ? NRF_TDM_INT_TXPTRUPD_MASK_MASK : 0UL) | - NRF_TDM_INT_STOPPED_MASK_MASK); - - nrf_tdm_tx_count_set(p_reg, p_initial_buffers->buffer_size); - nrf_tdm_rx_count_set(p_reg, p_initial_buffers->buffer_size); - nrf_tdm_rx_buffer_set(p_reg, p_initial_buffers->p_rx_buffer); - nrf_tdm_tx_buffer_set(p_reg, p_initial_buffers->p_tx_buffer); - nrf_tdm_task_trigger(p_reg, NRF_TDM_TASK_START); -} - -static void tdm_stop(NRF_TDM_Type *p_reg) -{ - nrf_tdm_int_disable(p_reg, NRF_TDM_INT_RXPTRUPD_MASK_MASK | NRF_TDM_INT_TXPTRUPD_MASK_MASK); - - nrf_tdm_task_trigger(p_reg, NRF_TDM_TASK_STOP); -} - -static bool next_buffers_set(struct tdm_drv_data *drv_data, tdm_buffers_t const *p_buffers) -{ - NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; - tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; - nrf_tdm_rxtxen_t dir = NRF_TDM_RXTXEN_DUPLEX; - - __ASSERT_NO_MSG(p_buffers->p_rx_buffer != NULL || p_buffers->p_tx_buffer != NULL); - - if (!ctrl_data->buffers_needed) { - return false; - } - - nrf_tdm_tx_count_set(p_reg, p_buffers->buffer_size); - nrf_tdm_rx_count_set(p_reg, p_buffers->buffer_size); - nrf_tdm_rx_buffer_set(p_reg, p_buffers->p_rx_buffer); - nrf_tdm_tx_buffer_set(p_reg, p_buffers->p_tx_buffer); - - if (p_buffers->p_rx_buffer == NULL) { - dir = NRF_TDM_RXTXEN_TX; - } else if (p_buffers->p_tx_buffer == NULL) { - dir = NRF_TDM_RXTXEN_RX; - } - nrf_tdm_transfer_direction_set(p_reg, dir); - - ctrl_data->next_buffers = *p_buffers; - ctrl_data->buffers_needed = false; - - return true; -} - -static bool supply_next_buffers(struct tdm_drv_data *drv_data, tdm_buffers_t *next) -{ - const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; - - if (drv_data->active_dir != I2S_DIR_TX) { /* -> RX active */ - if (!get_next_rx_buffer(drv_data, next)) { - drv_data->state = I2S_STATE_ERROR; - tdm_stop(drv_cfg->p_reg); - return false; - } - /* Set buffer size if there is no TX buffer (which effectively - * controls how many bytes will be received). - */ - if (drv_data->active_dir == I2S_DIR_RX) { - next->buffer_size = drv_data->rx.cfg.block_size / sizeof(uint32_t); - } - } - - drv_data->last_tx_buffer = next->p_tx_buffer; - drv_data->last_tx_mem_slab = next->p_tx_mem_slab; - - LOG_DBG("Next buffers: %p/%p", next->p_tx_buffer, next->p_rx_buffer); - return next_buffers_set(drv_data, next); -} - -static void purge_queue(const struct device *dev, enum i2s_dir dir) -{ - struct tdm_drv_data *drv_data = dev->data; - struct tdm_buf buf; - - if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { - while (k_msgq_get(&drv_data->tx_queue, &buf, K_NO_WAIT) == 0) { - free_tx_buffer(drv_data, &buf); - } - } - - if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { - while (k_msgq_get(&drv_data->rx_queue, &buf, K_NO_WAIT) == 0) { - free_rx_buffer(drv_data, &buf); - } - } -} - -static void tdm_uninit(struct tdm_drv_data *drv_data) -{ - NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; - - tdm_stop(p_reg); - NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_reg)); -} - -static int tdm_nrfx_configure(const struct device *dev, enum i2s_dir dir, - const struct i2s_config *tdm_cfg) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - nrf_tdm_config_t nrfx_cfg; - uint32_t chan_mask = 0; - - if (drv_data->state != I2S_STATE_READY) { - LOG_ERR("Cannot configure in state: %d", drv_data->state); - return -EINVAL; - } - - if (tdm_cfg->frame_clk_freq == 0) { /* -> reset state */ - purge_queue(dev, dir); - if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { - drv_data->tx_configured = false; - memset(&drv_data->tx, 0, sizeof(drv_data->tx)); - } - if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { - drv_data->rx_configured = false; - memset(&drv_data->rx, 0, sizeof(drv_data->rx)); - } - return 0; - } - - __ASSERT_NO_MSG(tdm_cfg->mem_slab != NULL && tdm_cfg->block_size != 0); - - if ((tdm_cfg->block_size % sizeof(uint32_t)) != 0) { - LOG_ERR("This device can transfer only full 32-bit words"); - return -EINVAL; - } - - switch (tdm_cfg->word_size) { - case 8: - nrfx_cfg.sample_width = NRF_TDM_SWIDTH_8BIT; - break; - case 16: - nrfx_cfg.sample_width = NRF_TDM_SWIDTH_16BIT; - break; - case 24: - nrfx_cfg.sample_width = NRF_TDM_SWIDTH_24BIT; - break; - case 32: - nrfx_cfg.sample_width = NRF_TDM_SWIDTH_32BIT; - break; - default: - LOG_ERR("Unsupported word size: %u", tdm_cfg->word_size); - return -EINVAL; - } - - switch (tdm_cfg->format & I2S_FMT_DATA_FORMAT_MASK) { - case I2S_FMT_DATA_FORMAT_I2S: - nrfx_cfg.alignment = NRF_TDM_ALIGN_LEFT; - nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_NEGEDGE; - nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; - nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; - nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_1CK; - break; - case I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED: - nrfx_cfg.alignment = NRF_TDM_ALIGN_LEFT; - nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_POSEDGE; - nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; - nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; - nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_NONE; - break; - case I2S_FMT_DATA_FORMAT_RIGHT_JUSTIFIED: - nrfx_cfg.alignment = NRF_TDM_ALIGN_RIGHT; - nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_POSEDGE; - nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; - nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; - nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_NONE; - break; - default: - LOG_ERR("Unsupported data format: 0x%02x", tdm_cfg->format); - return -EINVAL; - } - - if ((tdm_cfg->format & I2S_FMT_DATA_ORDER_LSB) || (tdm_cfg->format & I2S_FMT_BIT_CLK_INV) || - (tdm_cfg->format & I2S_FMT_FRAME_CLK_INV)) { - LOG_ERR("Unsupported stream format: 0x%02x", tdm_cfg->format); - return -EINVAL; - } - - if (tdm_cfg->channels == 2) { - nrfx_cfg.num_of_channels = NRF_TDM_CHANNELS_COUNT_2; - } else if (tdm_cfg->channels == 1) { - nrfx_cfg.num_of_channels = NRF_TDM_CHANNELS_COUNT_1; - } else { - LOG_ERR("Unsupported number of channels: %u", tdm_cfg->channels); - return -EINVAL; - } - chan_mask = BIT_MASK(tdm_cfg->channels); - - if ((tdm_cfg->options & I2S_OPT_BIT_CLK_SLAVE) && - (tdm_cfg->options & I2S_OPT_FRAME_CLK_SLAVE)) { - nrfx_cfg.mode = NRF_TDM_MODE_SLAVE; - } else if (!(tdm_cfg->options & I2S_OPT_BIT_CLK_SLAVE) && - !(tdm_cfg->options & I2S_OPT_FRAME_CLK_SLAVE)) { - nrfx_cfg.mode = NRF_TDM_MODE_MASTER; - } else { - LOG_ERR("Unsupported operation mode: 0x%02x", tdm_cfg->options); - return -EINVAL; - } - - nrfx_cfg.mck_setup = 0; - if (nrfx_cfg.mode == NRF_TDM_MODE_MASTER) { - uint32_t sck = tdm_cfg->word_size * tdm_cfg->frame_clk_freq * tdm_cfg->channels; - const uint32_t src_freq = - (drv_cfg->clk_src == ACLK) - ? DT_PROP_OR(DT_NODELABEL(clock), hfclkaudio_frequency, 0) - : drv_cfg->pclk_frequency; - - /* Unless the PCLK source is used, - * it is required to request the proper clock to be running - * before starting the transfer itself. - */ - drv_data->request_clock = (drv_cfg->clk_src != PCLK); - nrfx_cfg.sck_setup = div_calculate(src_freq, sck); - - if (((nrf_tdm_mck_pin_get(drv_cfg->p_reg) & TDM_PSEL_MCK_CONNECT_Msk) == - TDM_PSEL_MCK_CONNECT_Connected << TDM_PSEL_MCK_CONNECT_Pos) && - drv_cfg->mck_frequency != 0) { - nrfx_cfg.mck_setup = div_calculate(src_freq, drv_cfg->mck_frequency); - } - } else { - drv_data->request_clock = false; - } - - if ((tdm_cfg->options & I2S_OPT_LOOPBACK) || (tdm_cfg->options & I2S_OPT_PINGPONG)) { - LOG_ERR("Unsupported options: 0x%02x", tdm_cfg->options); - return -EINVAL; - } - - if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { - nrfx_cfg.channels = (chan_mask << TDM_CONFIG_CHANNEL_MASK_Tx0Enable_Pos); - drv_data->tx.cfg = *tdm_cfg; - drv_data->tx.nrfx_cfg = nrfx_cfg; - drv_data->tx_configured = true; - } - - if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { - nrfx_cfg.channels = (chan_mask << TDM_CONFIG_CHANNEL_MASK_Rx0Enable_Pos); - drv_data->rx.cfg = *tdm_cfg; - drv_data->rx.nrfx_cfg = nrfx_cfg; - drv_data->rx_configured = true; - } - return 0; -} - -static const struct i2s_config *tdm_nrfx_config_get(const struct device *dev, enum i2s_dir dir) -{ - struct tdm_drv_data *drv_data = dev->data; - - if (dir == I2S_DIR_TX && drv_data->tx_configured) { - return &drv_data->tx.cfg; - } - if (dir == I2S_DIR_RX && drv_data->rx_configured) { - return &drv_data->rx.cfg; - } - - return NULL; -} - -static int tdm_nrfx_read(const struct device *dev, void **mem_block, size_t *size) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; - struct tdm_buf buf; - int ret; - - if (!drv_data->rx_configured) { - LOG_ERR("Device is not configured"); - return -EIO; - } - ret = k_msgq_get(&drv_data->rx_queue, &buf, - (drv_data->state == I2S_STATE_ERROR) - ? K_NO_WAIT - : SYS_TIMEOUT_MS(drv_data->rx.cfg.timeout)); - if (ret == -ENOMSG) { - return -EIO; - } - - LOG_DBG("Released RX %p", buf.mem_block); - - if (ret == 0) { - (void)dmm_buffer_in_release(drv_cfg->mem_reg, buf.mem_block, buf.size, buf.dmm_buf); - *mem_block = buf.mem_block; - *size = buf.size; - } - return ret; -} - -static int tdm_nrfx_write(const struct device *dev, void *mem_block, size_t size) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - struct tdm_buf buf = {.mem_block = mem_block, .size = size}; - int ret; - - if (!drv_data->tx_configured) { - LOG_ERR("Device is not configured"); - return -EIO; - } - - if (drv_data->state != I2S_STATE_RUNNING && drv_data->state != I2S_STATE_READY) { - LOG_ERR("Cannot write in state: %d", drv_data->state); - return -EIO; - } - - if (size > drv_data->tx.cfg.block_size || size < sizeof(uint32_t)) { - LOG_ERR("This device can only write blocks up to %u bytes", - drv_data->tx.cfg.block_size); - return -EIO; - } - ret = dmm_buffer_out_prepare(drv_cfg->mem_reg, buf.mem_block, buf.size, - (void **)&buf.dmm_buf); - ret = k_msgq_put(&drv_data->tx_queue, &buf, SYS_TIMEOUT_MS(drv_data->tx.cfg.timeout)); - if (ret < 0) { - return ret; - } - - /* Check if interrupt wanted to get next TX buffer before current buffer - * was queued. Do not move this check before queuing because doing so - * opens the possibility for a race condition between this function and - * data_handler() that is called in interrupt context. - */ - if (drv_data->state == I2S_STATE_RUNNING && drv_data->next_tx_buffer_needed) { - tdm_buffers_t next = {0}; - - if (!get_next_tx_buffer(drv_data, &next)) { - /* Log error because this is definitely unexpected. - * Do not return error because the caller is no longer - * responsible for releasing the buffer. - */ - LOG_ERR("Cannot reacquire queued buffer"); - return 0; - } - - drv_data->next_tx_buffer_needed = false; - - LOG_DBG("Next TX %p", next.p_tx_buffer); - - if (!supply_next_buffers(drv_data, &next)) { - LOG_ERR("Cannot supply buffer"); - return -EIO; - } - } - return 0; -} - -static int start_transfer(struct tdm_drv_data *drv_data) -{ - tdm_buffers_t initial_buffers = {0}; - int ret = 0; - - if (drv_data->active_dir != I2S_DIR_RX && /* -> TX to be started */ - !get_next_tx_buffer(drv_data, &initial_buffers)) { - LOG_ERR("No TX buffer available"); - ret = -ENOMEM; - } else if (drv_data->active_dir != I2S_DIR_TX && /* -> RX to be started */ - !get_next_rx_buffer(drv_data, &initial_buffers)) { - /* Failed to allocate next RX buffer */ - ret = -ENOMEM; - } else { - /* It is necessary to set buffer size here only for I2S_DIR_RX, - * because only then the get_next_tx_buffer() call in the if - * condition above gets short-circuited. - */ - if (drv_data->active_dir == I2S_DIR_RX) { - initial_buffers.buffer_size = - drv_data->rx.cfg.block_size / sizeof(uint32_t); - } - - drv_data->last_tx_buffer = initial_buffers.p_tx_buffer; - drv_data->last_tx_mem_slab = initial_buffers.p_tx_mem_slab; - - tdm_start(drv_data, &initial_buffers); - } - if (ret < 0) { - tdm_uninit(drv_data); - if (drv_data->request_clock) { - (void)onoff_release(drv_data->clk_mgr); - } - - if (initial_buffers.p_tx_buffer) { - struct tdm_buf buf = {.mem_block = (void *)initial_buffers.p_tx_mem_slab, - .dmm_buf = (void *)initial_buffers.p_tx_buffer, - .size = initial_buffers.buffer_size * - sizeof(uint32_t)}; - free_tx_buffer(drv_data, &buf); - } - if (initial_buffers.p_rx_buffer) { - struct tdm_buf buf = {.mem_block = initial_buffers.p_rx_mem_slab, - .dmm_buf = (void *)initial_buffers.p_rx_buffer, - .size = initial_buffers.buffer_size * - sizeof(uint32_t)}; - free_rx_buffer(drv_data, &buf); - } - - drv_data->state = I2S_STATE_ERROR; - } - return ret; -} - -static void tdm_init(struct tdm_drv_data *drv_data, nrf_tdm_config_t const *p_config, - tdm_data_handler_t handler) -{ - tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; - NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; - - nrf_tdm_configure(p_reg, p_config); - nrf_tdm_mck_set(p_reg, p_config->mck_setup != 0); - - ctrl_data->handler = handler; - - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_STOPPED); - NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_reg)); -} - -static void clock_started_callback(struct onoff_manager *mgr, struct onoff_client *cli, - uint32_t state, int res) -{ - struct tdm_drv_data *drv_data = CONTAINER_OF(cli, struct tdm_drv_data, clk_cli); - - /* The driver state can be set back to READY at this point if the DROP - * command was triggered before the clock has started. Do not start - * the actual transfer in such case. - */ - if (drv_data->state == I2S_STATE_READY) { - tdm_uninit(drv_data); - (void)onoff_release(drv_data->clk_mgr); - } else { - (void)start_transfer(drv_data); - } -} - -static int trigger_start(const struct device *dev) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - int ret; - const nrf_tdm_config_t *nrfx_cfg = (drv_data->active_dir == I2S_DIR_TX) - ? &drv_data->tx.nrfx_cfg - : &drv_data->rx.nrfx_cfg; - - tdm_init(drv_data, nrfx_cfg, drv_cfg->data_handler); - - drv_data->state = I2S_STATE_RUNNING; - - nrf_tdm_sck_configure(drv_cfg->p_reg, - drv_cfg->clk_src == ACLK ? NRF_TDM_SRC_ACLK : NRF_TDM_SRC_PCLK32M, - false); - - nrf_tdm_mck_configure(drv_cfg->p_reg, - drv_cfg->clk_src == ACLK ? NRF_TDM_SRC_ACLK : NRF_TDM_SRC_PCLK32M, - false); - /* If it is required to use certain HF clock, request it to be running - * first. If not, start the transfer directly. - */ - if (drv_data->request_clock) { - sys_notify_init_callback(&drv_data->clk_cli.notify, clock_started_callback); - ret = onoff_request(drv_data->clk_mgr, &drv_data->clk_cli); - if (ret < 0) { - tdm_uninit(drv_data); - drv_data->state = I2S_STATE_READY; - - LOG_ERR("Failed to request clock: %d", ret); - return -EIO; - } - } else { - ret = start_transfer(drv_data); - if (ret < 0) { - return ret; - } - } - - return 0; -} - -static int tdm_nrfx_trigger(const struct device *dev, enum i2s_dir dir, enum i2s_trigger_cmd cmd) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - bool configured = false; - bool cmd_allowed; - - /* This driver does not use the I2S_STATE_NOT_READY value. - * Instead, if a given stream is not configured, the respective - * flag (tx_configured or rx_configured) is cleared. - */ - drv_data->tx.nrfx_cfg.channels |= drv_data->rx.nrfx_cfg.channels; - drv_data->rx.nrfx_cfg.channels |= drv_data->tx.nrfx_cfg.channels; - if (dir == I2S_DIR_BOTH) { - - configured = drv_data->tx_configured && drv_data->rx_configured; - } else if (dir == I2S_DIR_TX) { - configured = drv_data->tx_configured; - } else if (dir == I2S_DIR_RX) { - configured = drv_data->rx_configured; - } - - if (!configured) { - LOG_ERR("Device is not configured"); - return -EIO; - } - - if (dir == I2S_DIR_BOTH && (memcmp(&drv_data->tx.nrfx_cfg, &drv_data->rx.nrfx_cfg, - sizeof(drv_data->rx.nrfx_cfg)) != 0 || - (drv_data->tx.cfg.block_size != drv_data->rx.cfg.block_size))) { - LOG_ERR("TX and RX configurations are different"); - return -EIO; - } - - switch (cmd) { - case I2S_TRIGGER_START: - cmd_allowed = (drv_data->state == I2S_STATE_READY); - break; - case I2S_TRIGGER_STOP: - case I2S_TRIGGER_DRAIN: - cmd_allowed = (drv_data->state == I2S_STATE_RUNNING); - break; - case I2S_TRIGGER_DROP: - cmd_allowed = configured; - break; - case I2S_TRIGGER_PREPARE: - cmd_allowed = (drv_data->state == I2S_STATE_ERROR); - break; - default: - LOG_ERR("Invalid trigger: %d", cmd); - return -EINVAL; - } - - if (!cmd_allowed) { - LOG_ERR("Not allowed"); - return -EIO; - } - - /* For triggers applicable to the RUNNING state (i.e. STOP, DRAIN, - * and DROP), ensure that the command is applied to the streams - * that are currently active (this device cannot e.g. stop only TX - * without stopping RX). - */ - if (drv_data->state == I2S_STATE_RUNNING && drv_data->active_dir != dir) { - LOG_ERR("Inappropriate trigger (%d/%d), active stream(s): %d", cmd, dir, - drv_data->active_dir); - return -EINVAL; - } - - switch (cmd) { - case I2S_TRIGGER_START: - drv_data->stop = false; - drv_data->discard_rx = false; - drv_data->active_dir = dir; - drv_data->next_tx_buffer_needed = false; - return trigger_start(dev); - - case I2S_TRIGGER_STOP: - drv_data->state = I2S_STATE_STOPPING; - drv_data->stop = true; - return 0; - - case I2S_TRIGGER_DRAIN: - drv_data->state = I2S_STATE_STOPPING; - /* If only RX is active, DRAIN is equivalent to STOP. */ - drv_data->stop = (drv_data->active_dir == I2S_DIR_RX); - return 0; - - case I2S_TRIGGER_DROP: - if (drv_data->state != I2S_STATE_READY) { - drv_data->discard_rx = true; - tdm_stop(drv_cfg->p_reg); - } - purge_queue(dev, dir); - drv_data->state = I2S_STATE_READY; - return 0; - - case I2S_TRIGGER_PREPARE: - purge_queue(dev, dir); - drv_data->state = I2S_STATE_READY; - return 0; - - default: - LOG_ERR("Invalid trigger: %d", cmd); - return -EINVAL; - } -} - -#if CONFIG_CLOCK_CONTROL_NRF -static void init_clock_manager(const struct device *dev) -{ - struct tdm_drv_data *drv_data = dev->data; - clock_control_subsys_t subsys; - -#if NRF_CLOCK_HAS_HFCLKAUDIO - const struct tdm_drv_cfg *drv_cfg = dev->config; - - if (drv_cfg->clk_src == ACLK) { - subsys = CLOCK_CONTROL_NRF_SUBSYS_HFAUDIO; - } else { - subsys = CLOCK_CONTROL_NRF_SUBSYS_HF; - } -#else - subsys = CLOCK_CONTROL_NRF_SUBSYS_HF; -#endif - - drv_data->clk_mgr = z_nrf_clock_control_get_onoff(subsys); - __ASSERT_NO_MSG(drv_data->clk_mgr != NULL); -} -#endif /* CONFIG_CLOCK_CONTROL_NRF */ - -static void data_handler(const struct device *dev, const tdm_buffers_t *released, uint32_t status) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - bool stop_transfer = false; - struct tdm_buf buf = {.mem_block = NULL, .dmm_buf = NULL, .size = 0}; - - if (released != NULL) { - buf.size = released->buffer_size * sizeof(uint32_t); - } - - if (status & NRFX_TDM_STATUS_TRANSFER_STOPPED) { - if (drv_data->state == I2S_STATE_STOPPING) { - drv_data->state = I2S_STATE_READY; - } - if (drv_data->last_tx_buffer) { - /* Usually, these pointers are equal, i.e. the last TX - * buffer that were to be transferred is released by the - * driver after it stops. The last TX buffer pointer is - * then set to NULL here so that the buffer can be freed - * below, just as any other TX buffer released by the - * driver. However, it may happen that the buffer is not - * released this way, for example, when the transfer - * ends with an error because an RX buffer allocation - * fails. In such case, the last TX buffer needs to be - * freed here. - */ - - if ((released != NULL) != 0 && - (drv_data->last_tx_buffer != released->p_tx_buffer)) { - buf.dmm_buf = (void *)drv_data->last_tx_buffer; - buf.mem_block = (void *)drv_data->last_tx_mem_slab; - free_tx_buffer(drv_data, &buf); - } - drv_data->last_tx_buffer = NULL; - } - tdm_uninit(drv_data); - if (drv_data->request_clock) { - (void)onoff_release(drv_data->clk_mgr); - } - } - - if (released == NULL) { - /* This means that buffers for the next part of the transfer - * were not supplied and the previous ones cannot be released - * yet, as pointers to them were latched in the I2S registers. - * It is not an error when the transfer is to be stopped (those - * buffers will be released after the transfer actually stops). - */ - if (drv_data->state != I2S_STATE_STOPPING) { - drv_data->state = I2S_STATE_ERROR; - } - tdm_stop(drv_cfg->p_reg); - return; - } - if (released->p_rx_buffer) { - buf.mem_block = (void *)released->p_rx_mem_slab; - buf.dmm_buf = (void *)released->p_rx_buffer; - if (drv_data->discard_rx) { - free_rx_buffer(drv_data, &buf); - } else { - int ret = k_msgq_put(&drv_data->rx_queue, &buf, K_NO_WAIT); - - if (ret < 0) { - LOG_ERR("No room in RX queue"); - drv_data->state = I2S_STATE_ERROR; - stop_transfer = true; - - free_rx_buffer(drv_data, &buf); - } else { - - /* If the TX direction is not active and - * the transfer should be stopped after - * the current block, stop the reception. - */ - if (drv_data->active_dir == I2S_DIR_RX && drv_data->stop) { - drv_data->discard_rx = true; - stop_transfer = true; - } - } - } - } - - if (released->p_tx_buffer) { - buf.mem_block = (void *)released->p_tx_mem_slab; - buf.dmm_buf = (void *)released->p_tx_buffer; - /* If the last buffer that was to be transferred has just been - * released, it is time to stop the transfer. - */ - if (released->p_tx_buffer == drv_data->last_tx_buffer) { - drv_data->discard_rx = true; - stop_transfer = true; - } else { - free_tx_buffer(drv_data, &buf); - } - } - - if (stop_transfer) { - tdm_stop(drv_cfg->p_reg); - } else if (status & NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED) { - tdm_buffers_t next = {0}; - - if (drv_data->active_dir != I2S_DIR_RX) { /* -> TX active */ - if (drv_data->stop) { - /* If the stream is to be stopped, don't get - * the next TX buffer from the queue, instead - * supply the one used last time (it won't be - * transferred, the stream will stop right - * before this buffer would be started again). - */ - next.p_tx_buffer = drv_data->last_tx_buffer; - next.p_tx_mem_slab = drv_data->last_tx_mem_slab; - next.buffer_size = 1; - } else if (get_next_tx_buffer(drv_data, &next)) { - /* Next TX buffer successfully retrieved from - * the queue, nothing more to do here. - */ - } else if (drv_data->state == I2S_STATE_STOPPING) { - /* If there are no more TX blocks queued and - * the current state is STOPPING (so the DRAIN - * command was triggered) it is time to finish - * the transfer. - */ - drv_data->stop = true; - /* Supply the same buffer as last time; it will - * not be transferred anyway, as the transfer - * will be stopped earlier. - */ - next.p_tx_buffer = drv_data->last_tx_buffer; - next.p_tx_mem_slab = drv_data->last_tx_mem_slab; - next.buffer_size = 1; - } else { - /* Next TX buffer cannot be supplied now. - * Defer it to when the user writes more data. - */ - drv_data->next_tx_buffer_needed = true; - return; - } - } - (void)supply_next_buffers(drv_data, &next); - } -} - -static int data_init(const struct device *dev) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - - drv_data->state = I2S_STATE_READY; - int err = pinctrl_apply_state(drv_cfg->pcfg, PINCTRL_STATE_DEFAULT); - - if (err < 0) { - return err; - } - drv_data->drv_cfg = drv_cfg; - IF_ENABLED(CONFIG_CLOCK_CONTROL_NRF, (init_clock_manager(dev);)) - return err; -} - -static const struct i2s_driver_api tdm_nrf_drv_api = { - .configure = tdm_nrfx_configure, - .config_get = tdm_nrfx_config_get, - .read = tdm_nrfx_read, - .write = tdm_nrfx_write, - .trigger = tdm_nrfx_trigger, -}; - -#define TDM(idx) DT_NODELABEL(tdm##idx) -#define TDM_CLK_SRC(idx) DT_STRING_TOKEN(TDM(idx), clock_source) -#define PCLK_NODE(idx) DT_CLOCKS_CTLR(TDM(idx)) - -#define TDM_NRFX_DEVICE(idx) \ - static tdm_ctrl_t tdm##idx##data; \ - static struct tdm_buf tx_msgs##idx[CONFIG_TDM_NRFX_TX_BLOCK_COUNT]; \ - static struct tdm_buf rx_msgs##idx[CONFIG_TDM_NRFX_RX_BLOCK_COUNT]; \ - static void tdm_##idx##_irq_handler(const struct device *dev) \ - { \ - tdm_irq_handler(dev); \ - } \ - static void tdm_##idx##data_handler(tdm_buffers_t const *p_released, uint32_t status) \ - { \ - data_handler(DEVICE_DT_GET(TDM(idx)), p_released, status); \ - } \ - PINCTRL_DT_DEFINE(TDM(idx)); \ - static const struct tdm_drv_cfg tdm_nrfx_cfg##idx = { \ - .data_handler = tdm_##idx##data_handler, \ - .pcfg = PINCTRL_DT_DEV_CONFIG_GET(TDM(idx)), \ - .clk_src = TDM_CLK_SRC(idx), \ - .mck_frequency = DT_PROP_OR(TDM(idx), mck_frequency, 0), \ - .pclk_frequency = DT_PROP(PCLK_NODE(idx), clock_frequency), \ - .p_reg = NRF_TDM##idx, \ - .control_data = &tdm##idx##data, \ - .mem_reg = DMM_DEV_TO_REG(TDM(idx)), \ - }; \ - static struct tdm_drv_data tdm_nrfx_data##idx; \ - static int tdm_nrfx_init##idx(const struct device *dev) \ - { \ - IRQ_CONNECT(DT_IRQN(TDM(idx)), DT_IRQ(TDM(idx), priority), \ - tdm_##idx##_irq_handler, DEVICE_DT_GET(TDM(idx)), 0); \ - \ - int err = data_init(dev); \ - if (err < 0) { \ - return err; \ - } \ - k_msgq_init(&tdm_nrfx_data##idx.tx_queue, (char *)tx_msgs##idx, \ - sizeof(struct tdm_buf), ARRAY_SIZE(tx_msgs##idx)); \ - k_msgq_init(&tdm_nrfx_data##idx.rx_queue, (char *)rx_msgs##idx, \ - sizeof(struct tdm_buf), ARRAY_SIZE(rx_msgs##idx)); \ - return 0; \ - } \ - BUILD_ASSERT(TDM_CLK_SRC(idx) != ACLK, "Clock source ACLK is currently not supported. "); \ - DEVICE_DT_DEFINE(TDM(idx), tdm_nrfx_init##idx, NULL, &tdm_nrfx_data##idx, \ - &tdm_nrfx_cfg##idx, POST_KERNEL, CONFIG_I2S_INIT_PRIORITY, \ - &tdm_nrf_drv_api); - -/* Execute macro f(x) for all instances. */ -#define TDM_FOR_EACH_INSTANCE(f, sep, off_code, ...) \ - NRFX_FOREACH_PRESENT(TDM, f, sep, off_code, __VA_ARGS__) - -#define COND_TDM_NRFX_DEVICE(unused, prefix, i, _) \ - IF_ENABLED(CONFIG_HAS_HW_NRF_TDM##prefix##i, (TDM_NRFX_DEVICE(prefix##i);)) - -TDM_FOR_EACH_INSTANCE(COND_TDM_NRFX_DEVICE, (), ()) From cbcc02bcf7855d9a18371fbefcf4fcf4f2e92d37 Mon Sep 17 00:00:00 2001 From: Adam Kondraciuk Date: Wed, 27 Nov 2024 16:19:17 +0100 Subject: [PATCH 18/18] [nrf fromlist] drivers: i2s: Add support for nRF TDM peripherals Add a shim that allows using the nRF TDM (Time division multiplexed audio interface) HAL by I2S Zephyr API. Upstream PR #: 82144 Signed-off-by: Adam Kondraciuk --- drivers/i2s/CMakeLists.txt | 1 + drivers/i2s/Kconfig.nrfx | 22 + drivers/i2s/i2s_nrfx_tdm.c | 1116 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1139 insertions(+) create mode 100644 drivers/i2s/i2s_nrfx_tdm.c diff --git a/drivers/i2s/CMakeLists.txt b/drivers/i2s/CMakeLists.txt index 7857b6e863b..29a9da412ce 100644 --- a/drivers/i2s/CMakeLists.txt +++ b/drivers/i2s/CMakeLists.txt @@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_I2S_STM32 i2s_ll_stm32.c) zephyr_library_sources_ifdef(CONFIG_I2S_LITEX i2s_litex.c) zephyr_library_sources_ifdef(CONFIG_I2S_MCUX_FLEXCOMM i2s_mcux_flexcomm.c) zephyr_library_sources_ifdef(CONFIG_I2S_NRFX i2s_nrfx.c) +zephyr_library_sources_ifdef(CONFIG_TDM_NRFX i2s_nrfx_tdm.c) zephyr_library_sources_ifdef(CONFIG_I2S_MCUX_SAI i2s_mcux_sai.c) zephyr_library_sources_ifdef(CONFIG_I2S_ESP32 i2s_esp32.c) zephyr_library_sources_ifdef(CONFIG_I2S_TEST i2s_test.c) diff --git a/drivers/i2s/Kconfig.nrfx b/drivers/i2s/Kconfig.nrfx index b36f3eb9c64..6c92cda3993 100644 --- a/drivers/i2s/Kconfig.nrfx +++ b/drivers/i2s/Kconfig.nrfx @@ -22,3 +22,25 @@ config I2S_NRFX_TX_BLOCK_COUNT default 4 endif # I2S_NRFX + +menuconfig TDM_NRFX + bool "nRF TDM nrfx driver" + default y + depends on DT_HAS_NORDIC_NRF_TDM_ENABLED + select NRFX_TDM130 if HAS_HW_NRF_TDM130 + select CLOCK_CONTROL + select PINCTRL + help + Enable support for nrfx TDM driver for nRF MCU series. + +if TDM_NRFX + +config TDM_NRFX_RX_BLOCK_COUNT + int "RX queue length" + default 4 + +config TDM_NRFX_TX_BLOCK_COUNT + int "TX queue length" + default 4 + +endif # TDM_NRFX diff --git a/drivers/i2s/i2s_nrfx_tdm.c b/drivers/i2s/i2s_nrfx_tdm.c new file mode 100644 index 00000000000..4ca14ccd48f --- /dev/null +++ b/drivers/i2s/i2s_nrfx_tdm.c @@ -0,0 +1,1116 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(tdm_nrfx, CONFIG_I2S_LOG_LEVEL); + +/* The application must provide buffers that are to be used in the next + * part of the transfer. + */ +#define NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED BIT(0) + +/* The TDM peripheral has been stopped and all buffers that were passed + * to the driver have been released. + */ +#define NRFX_TDM_STATUS_TRANSFER_STOPPED BIT(1) + +#define NODE_AUDIOPLL DT_NODELABEL(audiopll) + +#if DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL) +static const struct device *audiopll = DEVICE_DT_GET(NODE_AUDIOPLL); +const struct nrf_clock_spec aclk_spec = { + .frequency = DT_PROP_OR(NODE_AUDIOPLL, frequency, 0), + .accuracy = 0, + .precision = NRF_CLOCK_CONTROL_PRECISION_DEFAULT, +}; +#endif + +typedef struct { + uint32_t *p_rx_buffer; + uint32_t const *p_tx_buffer; + void *p_tx_mem_slab; + void *p_rx_mem_slab; + uint16_t buffer_size; +} tdm_buffers_t; + +typedef void (*tdm_data_handler_t)(tdm_buffers_t const *p_released, uint32_t status); + +typedef struct { + tdm_data_handler_t handler; + bool use_rx: 1; + bool use_tx: 1; + bool rx_ready: 1; + bool tx_ready: 1; + bool buffers_needed: 1; + bool buffers_reused: 1; + tdm_buffers_t next_buffers; + tdm_buffers_t current_buffers; +} tdm_ctrl_t; + +struct stream_cfg { + struct i2s_config cfg; + nrf_tdm_config_t nrfx_cfg; +}; + +struct tdm_buf { + void *mem_block; + size_t size; + void *dmm_buf; +}; + +struct tdm_drv_cfg { + tdm_data_handler_t data_handler; + const struct pinctrl_dev_config *pcfg; + NRF_TDM_Type *p_reg; + void *mem_reg; + tdm_ctrl_t *control_data; + uint32_t mck_frequency; + uint32_t pclk_frequency; + enum clock_source { + PCLK, + ACLK + } clk_src; +}; + +struct tdm_drv_data { + struct onoff_client clk_cli; + struct stream_cfg tx; + struct k_msgq tx_queue; + struct stream_cfg rx; + struct k_msgq rx_queue; + const struct tdm_drv_cfg *drv_cfg; + const uint32_t *last_tx_buffer; + void *last_tx_mem_slab; + enum i2s_state state; + enum i2s_dir active_dir; + bool stop; /* stop after the current (TX or RX) block */ + bool discard_rx; /* discard further RX blocks */ + volatile bool next_tx_buffer_needed; + bool tx_configured: 1; + bool rx_configured: 1; + bool request_clock: 1; +}; + +static int audio_clock_request(struct tdm_drv_data *drv_data) +{ +#if DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL) + return nrf_clock_control_request(audiopll, &aclk_spec, &drv_data->clk_cli); +#else + (void)drv_data; + + return -ENOTSUP; +#endif +} + +static int audio_clock_release(void) +{ +#if DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL) + return nrf_clock_control_release(audiopll, &aclk_spec); +#else + return -ENOTSUP; +#endif +} + +void tdm_irq_handler(const struct device *dev) +{ + const struct tdm_drv_cfg *drv_cfg = dev->config; + NRF_TDM_Type *p_reg = drv_cfg->p_reg; + tdm_ctrl_t *ctrl_data = drv_cfg->control_data; + uint32_t event_mask = 0; + + if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_MAXCNT)) { + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_MAXCNT); + } + if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_TXPTRUPD)) { + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); + event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_TXPTRUPD); + ctrl_data->tx_ready = true; + if (ctrl_data->use_tx && ctrl_data->buffers_needed) { + ctrl_data->buffers_reused = true; + } + } + if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_RXPTRUPD)) { + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); + event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_RXPTRUPD); + ctrl_data->rx_ready = true; + if (ctrl_data->use_rx && ctrl_data->buffers_needed) { + ctrl_data->buffers_reused = true; + } + } + if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_STOPPED)) { + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_STOPPED); + event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_STOPPED); + nrf_tdm_int_disable(p_reg, NRF_TDM_INT_STOPPED_MASK_MASK); + nrf_tdm_disable(p_reg); + /* When stopped, release all buffers, including these scheduled for + * the next part of the transfer, and signal that the transfer has + * finished. + */ + ctrl_data->handler(&ctrl_data->current_buffers, 0); + ctrl_data->handler(&ctrl_data->next_buffers, NRFX_TDM_STATUS_TRANSFER_STOPPED); + } else { + /* Check if the requested transfer has been completed: + * - full-duplex mode + */ + if ((ctrl_data->use_tx && ctrl_data->use_rx && ctrl_data->tx_ready && + ctrl_data->rx_ready) || + /* - TX only mode */ + (!ctrl_data->use_rx && ctrl_data->tx_ready) || + /* - RX only mode */ + (!ctrl_data->use_tx && ctrl_data->rx_ready)) { + ctrl_data->tx_ready = false; + ctrl_data->rx_ready = false; + + /* If the application did not supply the buffers for the next + * part of the transfer until this moment, the current buffers + * cannot be released, since the I2S peripheral already started + * using them. Signal this situation to the application by + * passing NULL instead of the structure with released buffers. + */ + if (ctrl_data->buffers_reused) { + ctrl_data->buffers_reused = false; + /* This will most likely be set at this point. However, there is + * a small time window between TXPTRUPD and RXPTRUPD events, + * and it is theoretically possible that next buffers will be + * set in this window, so to be sure this flag is set to true, + * set it explicitly. + */ + ctrl_data->buffers_needed = true; + ctrl_data->handler(NULL, NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED); + } else { + /* Buffers that have been used by the I2S peripheral (current) + * are now released and will be returned to the application, + * and the ones scheduled to be used as next become the current + * ones. + */ + tdm_buffers_t released_buffers = ctrl_data->current_buffers; + + ctrl_data->current_buffers = ctrl_data->next_buffers; + ctrl_data->next_buffers.p_rx_buffer = NULL; + ctrl_data->next_buffers.p_tx_buffer = NULL; + ctrl_data->buffers_needed = true; + ctrl_data->handler(&released_buffers, + NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED); + } + } + } +} + +static uint32_t div_calculate(uint32_t src_freq, uint32_t requested_clk_freq) +{ + enum { + MCKCONST = 1048576 + }; + /* As specified in the PS: + * + * DIV = 4096 * floor(f_MCK * 1048576 / + * (f_source + f_MCK / 2)) + * f_actual = f_source / + * floor(1048576 * 4096 / DIV) + */ + + uint32_t ck_div = (uint32_t)(((uint64_t)requested_clk_freq * MCKCONST) / + (src_freq + requested_clk_freq / 2)); + return (ck_div * 4096); +} + +static bool get_next_tx_buffer(struct tdm_drv_data *drv_data, tdm_buffers_t *buffers) +{ + struct tdm_buf buf; + int ret = k_msgq_get(&drv_data->tx_queue, &buf, K_NO_WAIT); + + if (ret != 0) { + return false; + } + buffers->p_tx_buffer = buf.dmm_buf; + buffers->p_tx_mem_slab = buf.mem_block; + buffers->buffer_size = buf.size / sizeof(uint32_t); + return true; +} + +static bool get_next_rx_buffer(struct tdm_drv_data *drv_data, tdm_buffers_t *buffers) +{ + const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; + int ret = k_mem_slab_alloc(drv_data->rx.cfg.mem_slab, &buffers->p_rx_mem_slab, K_NO_WAIT); + + if (ret < 0) { + LOG_ERR("Failed to allocate next RX buffer: %d", ret); + return false; + } + ret = dmm_buffer_in_prepare(drv_cfg->mem_reg, buffers->p_rx_mem_slab, + buffers->buffer_size * sizeof(uint32_t), + (void **)&buffers->p_rx_buffer); + if (ret < 0) { + LOG_ERR("Failed to prepare buffer: %d", ret); + return false; + } + + return true; +} + +static void free_tx_buffer(struct tdm_drv_data *drv_data, struct tdm_buf *buf) +{ + const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; + + (void)dmm_buffer_out_release(drv_cfg->mem_reg, buf->dmm_buf); + k_mem_slab_free(drv_data->tx.cfg.mem_slab, buf->mem_block); + LOG_DBG("Freed TX %p", buf->mem_block); +} + +static void free_rx_buffer(struct tdm_drv_data *drv_data, struct tdm_buf *buf) +{ + const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; + + (void)dmm_buffer_in_release(drv_cfg->mem_reg, buf->mem_block, buf->size, buf->dmm_buf); + k_mem_slab_free(drv_data->rx.cfg.mem_slab, buf->mem_block); + LOG_DBG("Freed RX %p", buf->mem_block); +} + +static void tdm_start(struct tdm_drv_data *drv_data, tdm_buffers_t const *p_initial_buffers) +{ + NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; + tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; + + __ASSERT_NO_MSG(p_initial_buffers->p_rx_buffer != NULL || + p_initial_buffers->p_tx_buffer != NULL); + ctrl_data->use_rx = (p_initial_buffers->p_rx_buffer != NULL); + ctrl_data->use_tx = (p_initial_buffers->p_tx_buffer != NULL); + ctrl_data->rx_ready = false; + ctrl_data->tx_ready = false; + ctrl_data->buffers_needed = false; + + ctrl_data->next_buffers = *p_initial_buffers; + ctrl_data->current_buffers.p_rx_buffer = NULL; + ctrl_data->current_buffers.p_tx_buffer = NULL; + nrf_tdm_enable(p_reg); + + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); + + nrf_tdm_int_enable( + p_reg, + (p_initial_buffers->p_rx_buffer ? NRF_TDM_INT_RXPTRUPD_MASK_MASK : 0UL) | + (p_initial_buffers->p_tx_buffer ? NRF_TDM_INT_TXPTRUPD_MASK_MASK : 0UL) | + NRF_TDM_INT_STOPPED_MASK_MASK); + + nrf_tdm_tx_count_set(p_reg, p_initial_buffers->buffer_size); + nrf_tdm_rx_count_set(p_reg, p_initial_buffers->buffer_size); + nrf_tdm_rx_buffer_set(p_reg, p_initial_buffers->p_rx_buffer); + nrf_tdm_tx_buffer_set(p_reg, p_initial_buffers->p_tx_buffer); + nrf_tdm_task_trigger(p_reg, NRF_TDM_TASK_START); +} + +static void tdm_stop(NRF_TDM_Type *p_reg) +{ + nrf_tdm_int_disable(p_reg, NRF_TDM_INT_RXPTRUPD_MASK_MASK | NRF_TDM_INT_TXPTRUPD_MASK_MASK); + + nrf_tdm_task_trigger(p_reg, NRF_TDM_TASK_STOP); +} + +static bool next_buffers_set(struct tdm_drv_data *drv_data, tdm_buffers_t const *p_buffers) +{ + NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; + tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; + nrf_tdm_rxtxen_t dir = NRF_TDM_RXTXEN_DUPLEX; + + __ASSERT_NO_MSG(p_buffers->p_rx_buffer != NULL || p_buffers->p_tx_buffer != NULL); + + if (!ctrl_data->buffers_needed) { + return false; + } + + nrf_tdm_tx_count_set(p_reg, p_buffers->buffer_size); + nrf_tdm_rx_count_set(p_reg, p_buffers->buffer_size); + nrf_tdm_rx_buffer_set(p_reg, p_buffers->p_rx_buffer); + nrf_tdm_tx_buffer_set(p_reg, p_buffers->p_tx_buffer); + + if (p_buffers->p_rx_buffer == NULL) { + dir = NRF_TDM_RXTXEN_TX; + } else if (p_buffers->p_tx_buffer == NULL) { + dir = NRF_TDM_RXTXEN_RX; + } + nrf_tdm_transfer_direction_set(p_reg, dir); + + ctrl_data->next_buffers = *p_buffers; + ctrl_data->buffers_needed = false; + + return true; +} + +static bool supply_next_buffers(struct tdm_drv_data *drv_data, tdm_buffers_t *next) +{ + const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; + + if (drv_data->active_dir != I2S_DIR_TX) { /* -> RX active */ + if (!get_next_rx_buffer(drv_data, next)) { + drv_data->state = I2S_STATE_ERROR; + tdm_stop(drv_cfg->p_reg); + return false; + } + /* Set buffer size if there is no TX buffer (which effectively + * controls how many bytes will be received). + */ + if (drv_data->active_dir == I2S_DIR_RX) { + next->buffer_size = drv_data->rx.cfg.block_size / sizeof(uint32_t); + } + } + + drv_data->last_tx_buffer = next->p_tx_buffer; + drv_data->last_tx_mem_slab = next->p_tx_mem_slab; + + LOG_DBG("Next buffers: %p/%p", next->p_tx_buffer, next->p_rx_buffer); + return next_buffers_set(drv_data, next); +} + +static void purge_queue(const struct device *dev, enum i2s_dir dir) +{ + struct tdm_drv_data *drv_data = dev->data; + struct tdm_buf buf; + + if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { + while (k_msgq_get(&drv_data->tx_queue, &buf, K_NO_WAIT) == 0) { + free_tx_buffer(drv_data, &buf); + } + } + + if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { + while (k_msgq_get(&drv_data->rx_queue, &buf, K_NO_WAIT) == 0) { + free_rx_buffer(drv_data, &buf); + } + } +} + +static void tdm_uninit(struct tdm_drv_data *drv_data) +{ + NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; + + tdm_stop(p_reg); + NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_reg)); +} + +static int tdm_nrfx_configure(const struct device *dev, enum i2s_dir dir, + const struct i2s_config *tdm_cfg) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + nrf_tdm_config_t nrfx_cfg; + uint32_t chan_mask = 0; + + if (drv_data->state != I2S_STATE_READY) { + LOG_ERR("Cannot configure in state: %d", drv_data->state); + return -EINVAL; + } + + if (tdm_cfg->frame_clk_freq == 0) { /* -> reset state */ + purge_queue(dev, dir); + if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { + drv_data->tx_configured = false; + memset(&drv_data->tx, 0, sizeof(drv_data->tx)); + } + if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { + drv_data->rx_configured = false; + memset(&drv_data->rx, 0, sizeof(drv_data->rx)); + } + return 0; + } + + __ASSERT_NO_MSG(tdm_cfg->mem_slab != NULL && tdm_cfg->block_size != 0); + + if ((tdm_cfg->block_size % sizeof(uint32_t)) != 0) { + LOG_ERR("This device can transfer only full 32-bit words"); + return -EINVAL; + } + + switch (tdm_cfg->word_size) { + case 8: + nrfx_cfg.sample_width = NRF_TDM_SWIDTH_8BIT; + break; + case 16: + nrfx_cfg.sample_width = NRF_TDM_SWIDTH_16BIT; + break; + case 24: + nrfx_cfg.sample_width = NRF_TDM_SWIDTH_24BIT; + break; + case 32: + nrfx_cfg.sample_width = NRF_TDM_SWIDTH_32BIT; + break; + default: + LOG_ERR("Unsupported word size: %u", tdm_cfg->word_size); + return -EINVAL; + } + + switch (tdm_cfg->format & I2S_FMT_DATA_FORMAT_MASK) { + case I2S_FMT_DATA_FORMAT_I2S: + nrfx_cfg.alignment = NRF_TDM_ALIGN_LEFT; + nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_NEGEDGE; + nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; + nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; + nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_1CK; + break; + case I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED: + nrfx_cfg.alignment = NRF_TDM_ALIGN_LEFT; + nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_POSEDGE; + nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; + nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; + nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_NONE; + break; + case I2S_FMT_DATA_FORMAT_RIGHT_JUSTIFIED: + nrfx_cfg.alignment = NRF_TDM_ALIGN_RIGHT; + nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_POSEDGE; + nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; + nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; + nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_NONE; + break; + default: + LOG_ERR("Unsupported data format: 0x%02x", tdm_cfg->format); + return -EINVAL; + } + + if ((tdm_cfg->format & I2S_FMT_DATA_ORDER_LSB) || (tdm_cfg->format & I2S_FMT_BIT_CLK_INV) || + (tdm_cfg->format & I2S_FMT_FRAME_CLK_INV)) { + LOG_ERR("Unsupported stream format: 0x%02x", tdm_cfg->format); + return -EINVAL; + } + + if (tdm_cfg->channels == 2) { + nrfx_cfg.num_of_channels = NRF_TDM_CHANNELS_COUNT_2; + } else if (tdm_cfg->channels == 1) { + nrfx_cfg.num_of_channels = NRF_TDM_CHANNELS_COUNT_1; + } else { + LOG_ERR("Unsupported number of channels: %u", tdm_cfg->channels); + return -EINVAL; + } + chan_mask = BIT_MASK(tdm_cfg->channels); + + if ((tdm_cfg->options & I2S_OPT_BIT_CLK_SLAVE) && + (tdm_cfg->options & I2S_OPT_FRAME_CLK_SLAVE)) { + nrfx_cfg.mode = NRF_TDM_MODE_SLAVE; + } else if (!(tdm_cfg->options & I2S_OPT_BIT_CLK_SLAVE) && + !(tdm_cfg->options & I2S_OPT_FRAME_CLK_SLAVE)) { + nrfx_cfg.mode = NRF_TDM_MODE_MASTER; + } else { + LOG_ERR("Unsupported operation mode: 0x%02x", tdm_cfg->options); + return -EINVAL; + } + + nrfx_cfg.mck_setup = 0; + if (nrfx_cfg.mode == NRF_TDM_MODE_MASTER) { + uint32_t sck = tdm_cfg->word_size * tdm_cfg->frame_clk_freq * tdm_cfg->channels; + const uint32_t src_freq = (drv_cfg->clk_src == ACLK) + ? DT_PROP_OR(NODE_AUDIOPLL, frequency, 0) + : drv_cfg->pclk_frequency; + + /* Unless the PCLK source is used, + * it is required to request the proper clock to be running + * before starting the transfer itself. + */ + drv_data->request_clock = (drv_cfg->clk_src != PCLK); + nrfx_cfg.sck_setup = div_calculate(src_freq, sck); + + if (((nrf_tdm_mck_pin_get(drv_cfg->p_reg) & TDM_PSEL_MCK_CONNECT_Msk) == + TDM_PSEL_MCK_CONNECT_Connected << TDM_PSEL_MCK_CONNECT_Pos) && + drv_cfg->mck_frequency != 0) { + nrfx_cfg.mck_setup = div_calculate(src_freq, drv_cfg->mck_frequency); + } + } else { + drv_data->request_clock = false; + } + + if ((tdm_cfg->options & I2S_OPT_LOOPBACK) || (tdm_cfg->options & I2S_OPT_PINGPONG)) { + LOG_ERR("Unsupported options: 0x%02x", tdm_cfg->options); + return -EINVAL; + } + + if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { + nrfx_cfg.channels = (chan_mask << TDM_CONFIG_CHANNEL_MASK_Tx0Enable_Pos); + drv_data->tx.cfg = *tdm_cfg; + drv_data->tx.nrfx_cfg = nrfx_cfg; + drv_data->tx_configured = true; + } + + if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { + nrfx_cfg.channels = (chan_mask << TDM_CONFIG_CHANNEL_MASK_Rx0Enable_Pos); + drv_data->rx.cfg = *tdm_cfg; + drv_data->rx.nrfx_cfg = nrfx_cfg; + drv_data->rx_configured = true; + } + return 0; +} + +static const struct i2s_config *tdm_nrfx_config_get(const struct device *dev, enum i2s_dir dir) +{ + struct tdm_drv_data *drv_data = dev->data; + + if (dir == I2S_DIR_TX && drv_data->tx_configured) { + return &drv_data->tx.cfg; + } + if (dir == I2S_DIR_RX && drv_data->rx_configured) { + return &drv_data->rx.cfg; + } + + return NULL; +} + +static int tdm_nrfx_read(const struct device *dev, void **mem_block, size_t *size) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; + struct tdm_buf buf; + int ret; + + if (!drv_data->rx_configured) { + LOG_ERR("Device is not configured"); + return -EIO; + } + ret = k_msgq_get(&drv_data->rx_queue, &buf, + (drv_data->state == I2S_STATE_ERROR) + ? K_NO_WAIT + : SYS_TIMEOUT_MS(drv_data->rx.cfg.timeout)); + if (ret == -ENOMSG) { + return -EIO; + } + + LOG_DBG("Released RX %p", buf.mem_block); + + if (ret == 0) { + (void)dmm_buffer_in_release(drv_cfg->mem_reg, buf.mem_block, buf.size, buf.dmm_buf); + *mem_block = buf.mem_block; + *size = buf.size; + } + return ret; +} + +static int tdm_nrfx_write(const struct device *dev, void *mem_block, size_t size) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + struct tdm_buf buf = {.mem_block = mem_block, .size = size}; + int ret; + + if (!drv_data->tx_configured) { + LOG_ERR("Device is not configured"); + return -EIO; + } + + if (drv_data->state != I2S_STATE_RUNNING && drv_data->state != I2S_STATE_READY) { + LOG_ERR("Cannot write in state: %d", drv_data->state); + return -EIO; + } + + if (size > drv_data->tx.cfg.block_size || size < sizeof(uint32_t)) { + LOG_ERR("This device can only write blocks up to %u bytes", + drv_data->tx.cfg.block_size); + return -EIO; + } + ret = dmm_buffer_out_prepare(drv_cfg->mem_reg, buf.mem_block, buf.size, + (void **)&buf.dmm_buf); + ret = k_msgq_put(&drv_data->tx_queue, &buf, SYS_TIMEOUT_MS(drv_data->tx.cfg.timeout)); + if (ret < 0) { + return ret; + } + + /* Check if interrupt wanted to get next TX buffer before current buffer + * was queued. Do not move this check before queuing because doing so + * opens the possibility for a race condition between this function and + * data_handler() that is called in interrupt context. + */ + if (drv_data->state == I2S_STATE_RUNNING && drv_data->next_tx_buffer_needed) { + tdm_buffers_t next = {0}; + + if (!get_next_tx_buffer(drv_data, &next)) { + /* Log error because this is definitely unexpected. + * Do not return error because the caller is no longer + * responsible for releasing the buffer. + */ + LOG_ERR("Cannot reacquire queued buffer"); + return 0; + } + + drv_data->next_tx_buffer_needed = false; + + LOG_DBG("Next TX %p", next.p_tx_buffer); + + if (!supply_next_buffers(drv_data, &next)) { + LOG_ERR("Cannot supply buffer"); + return -EIO; + } + } + return 0; +} + +static int start_transfer(struct tdm_drv_data *drv_data) +{ + tdm_buffers_t initial_buffers = {0}; + int ret = 0; + + if (drv_data->active_dir != I2S_DIR_RX && /* -> TX to be started */ + !get_next_tx_buffer(drv_data, &initial_buffers)) { + LOG_ERR("No TX buffer available"); + ret = -ENOMEM; + } else if (drv_data->active_dir != I2S_DIR_TX && /* -> RX to be started */ + !get_next_rx_buffer(drv_data, &initial_buffers)) { + /* Failed to allocate next RX buffer */ + ret = -ENOMEM; + } else { + /* It is necessary to set buffer size here only for I2S_DIR_RX, + * because only then the get_next_tx_buffer() call in the if + * condition above gets short-circuited. + */ + if (drv_data->active_dir == I2S_DIR_RX) { + initial_buffers.buffer_size = + drv_data->rx.cfg.block_size / sizeof(uint32_t); + } + + drv_data->last_tx_buffer = initial_buffers.p_tx_buffer; + drv_data->last_tx_mem_slab = initial_buffers.p_tx_mem_slab; + + tdm_start(drv_data, &initial_buffers); + } + if (ret < 0) { + tdm_uninit(drv_data); + if (drv_data->request_clock) { + (void)audio_clock_release(); + } + + if (initial_buffers.p_tx_buffer) { + struct tdm_buf buf = {.mem_block = (void *)initial_buffers.p_tx_mem_slab, + .dmm_buf = (void *)initial_buffers.p_tx_buffer, + .size = initial_buffers.buffer_size * + sizeof(uint32_t)}; + free_tx_buffer(drv_data, &buf); + } + if (initial_buffers.p_rx_buffer) { + struct tdm_buf buf = {.mem_block = initial_buffers.p_rx_mem_slab, + .dmm_buf = (void *)initial_buffers.p_rx_buffer, + .size = initial_buffers.buffer_size * + sizeof(uint32_t)}; + free_rx_buffer(drv_data, &buf); + } + + drv_data->state = I2S_STATE_ERROR; + } + return ret; +} + +static void tdm_init(struct tdm_drv_data *drv_data, nrf_tdm_config_t const *p_config, + tdm_data_handler_t handler) +{ + tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; + NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; + + nrf_tdm_configure(p_reg, p_config); + nrf_tdm_mck_set(p_reg, p_config->mck_setup != 0); + + ctrl_data->handler = handler; + + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_STOPPED); + NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_reg)); +} + +static void clock_started_callback(struct onoff_manager *mgr, struct onoff_client *cli, + uint32_t state, int res) +{ + struct tdm_drv_data *drv_data = CONTAINER_OF(cli, struct tdm_drv_data, clk_cli); + + /* The driver state can be set back to READY at this point if the DROP + * command was triggered before the clock has started. Do not start + * the actual transfer in such case. + */ + if (drv_data->state == I2S_STATE_READY) { + tdm_uninit(drv_data); + (void)audio_clock_release(); + } else { + (void)start_transfer(drv_data); + } +} + +static int trigger_start(const struct device *dev) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + int ret; + const nrf_tdm_config_t *nrfx_cfg = (drv_data->active_dir == I2S_DIR_TX) + ? &drv_data->tx.nrfx_cfg + : &drv_data->rx.nrfx_cfg; + + tdm_init(drv_data, nrfx_cfg, drv_cfg->data_handler); + + drv_data->state = I2S_STATE_RUNNING; + + nrf_tdm_sck_configure(drv_cfg->p_reg, + drv_cfg->clk_src == ACLK ? NRF_TDM_SRC_ACLK : NRF_TDM_SRC_PCLK32M, + false); + + nrf_tdm_mck_configure(drv_cfg->p_reg, + drv_cfg->clk_src == ACLK ? NRF_TDM_SRC_ACLK : NRF_TDM_SRC_PCLK32M, + false); + /* If it is required to use certain HF clock, request it to be running + * first. If not, start the transfer directly. + */ + if (drv_data->request_clock) { + sys_notify_init_callback(&drv_data->clk_cli.notify, clock_started_callback); + ret = audio_clock_request(drv_data); + if (ret < 0) { + tdm_uninit(drv_data); + drv_data->state = I2S_STATE_READY; + + LOG_ERR("Failed to request clock: %d", ret); + return -EIO; + } + } else { + ret = start_transfer(drv_data); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +static int tdm_nrfx_trigger(const struct device *dev, enum i2s_dir dir, enum i2s_trigger_cmd cmd) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + bool configured = false; + bool cmd_allowed; + + /* This driver does not use the I2S_STATE_NOT_READY value. + * Instead, if a given stream is not configured, the respective + * flag (tx_configured or rx_configured) is cleared. + */ + drv_data->tx.nrfx_cfg.channels |= drv_data->rx.nrfx_cfg.channels; + drv_data->rx.nrfx_cfg.channels |= drv_data->tx.nrfx_cfg.channels; + if (dir == I2S_DIR_BOTH) { + + configured = drv_data->tx_configured && drv_data->rx_configured; + } else if (dir == I2S_DIR_TX) { + configured = drv_data->tx_configured; + } else if (dir == I2S_DIR_RX) { + configured = drv_data->rx_configured; + } + + if (!configured) { + LOG_ERR("Device is not configured"); + return -EIO; + } + + if (dir == I2S_DIR_BOTH && (memcmp(&drv_data->tx.nrfx_cfg, &drv_data->rx.nrfx_cfg, + sizeof(drv_data->rx.nrfx_cfg)) != 0 || + (drv_data->tx.cfg.block_size != drv_data->rx.cfg.block_size))) { + LOG_ERR("TX and RX configurations are different"); + return -EIO; + } + + switch (cmd) { + case I2S_TRIGGER_START: + cmd_allowed = (drv_data->state == I2S_STATE_READY); + break; + case I2S_TRIGGER_STOP: + case I2S_TRIGGER_DRAIN: + cmd_allowed = (drv_data->state == I2S_STATE_RUNNING); + break; + case I2S_TRIGGER_DROP: + cmd_allowed = configured; + break; + case I2S_TRIGGER_PREPARE: + cmd_allowed = (drv_data->state == I2S_STATE_ERROR); + break; + default: + LOG_ERR("Invalid trigger: %d", cmd); + return -EINVAL; + } + + if (!cmd_allowed) { + LOG_ERR("Not allowed"); + return -EIO; + } + + /* For triggers applicable to the RUNNING state (i.e. STOP, DRAIN, + * and DROP), ensure that the command is applied to the streams + * that are currently active (this device cannot e.g. stop only TX + * without stopping RX). + */ + if (drv_data->state == I2S_STATE_RUNNING && drv_data->active_dir != dir) { + LOG_ERR("Inappropriate trigger (%d/%d), active stream(s): %d", cmd, dir, + drv_data->active_dir); + return -EINVAL; + } + + switch (cmd) { + case I2S_TRIGGER_START: + drv_data->stop = false; + drv_data->discard_rx = false; + drv_data->active_dir = dir; + drv_data->next_tx_buffer_needed = false; + return trigger_start(dev); + + case I2S_TRIGGER_STOP: + drv_data->state = I2S_STATE_STOPPING; + drv_data->stop = true; + return 0; + + case I2S_TRIGGER_DRAIN: + drv_data->state = I2S_STATE_STOPPING; + /* If only RX is active, DRAIN is equivalent to STOP. */ + drv_data->stop = (drv_data->active_dir == I2S_DIR_RX); + return 0; + + case I2S_TRIGGER_DROP: + if (drv_data->state != I2S_STATE_READY) { + drv_data->discard_rx = true; + tdm_stop(drv_cfg->p_reg); + } + purge_queue(dev, dir); + drv_data->state = I2S_STATE_READY; + return 0; + + case I2S_TRIGGER_PREPARE: + purge_queue(dev, dir); + drv_data->state = I2S_STATE_READY; + return 0; + + default: + LOG_ERR("Invalid trigger: %d", cmd); + return -EINVAL; + } +} + +static void data_handler(const struct device *dev, const tdm_buffers_t *released, uint32_t status) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + bool stop_transfer = false; + struct tdm_buf buf = {.mem_block = NULL, .dmm_buf = NULL, .size = 0}; + + if (released != NULL) { + buf.size = released->buffer_size * sizeof(uint32_t); + } + + if (status & NRFX_TDM_STATUS_TRANSFER_STOPPED) { + if (drv_data->state == I2S_STATE_STOPPING) { + drv_data->state = I2S_STATE_READY; + } + if (drv_data->last_tx_buffer) { + /* Usually, these pointers are equal, i.e. the last TX + * buffer that were to be transferred is released by the + * driver after it stops. The last TX buffer pointer is + * then set to NULL here so that the buffer can be freed + * below, just as any other TX buffer released by the + * driver. However, it may happen that the buffer is not + * released this way, for example, when the transfer + * ends with an error because an RX buffer allocation + * fails. In such case, the last TX buffer needs to be + * freed here. + */ + + if ((released != NULL) != 0 && + (drv_data->last_tx_buffer != released->p_tx_buffer)) { + buf.dmm_buf = (void *)drv_data->last_tx_buffer; + buf.mem_block = (void *)drv_data->last_tx_mem_slab; + free_tx_buffer(drv_data, &buf); + } + drv_data->last_tx_buffer = NULL; + } + tdm_uninit(drv_data); + if (drv_data->request_clock) { + (void)audio_clock_release(); + } + } + + if (released == NULL) { + /* This means that buffers for the next part of the transfer + * were not supplied and the previous ones cannot be released + * yet, as pointers to them were latched in the I2S registers. + * It is not an error when the transfer is to be stopped (those + * buffers will be released after the transfer actually stops). + */ + if (drv_data->state != I2S_STATE_STOPPING) { + drv_data->state = I2S_STATE_ERROR; + } + tdm_stop(drv_cfg->p_reg); + return; + } + if (released->p_rx_buffer) { + buf.mem_block = (void *)released->p_rx_mem_slab; + buf.dmm_buf = (void *)released->p_rx_buffer; + if (drv_data->discard_rx) { + free_rx_buffer(drv_data, &buf); + } else { + int ret = k_msgq_put(&drv_data->rx_queue, &buf, K_NO_WAIT); + + if (ret < 0) { + LOG_ERR("No room in RX queue"); + drv_data->state = I2S_STATE_ERROR; + stop_transfer = true; + + free_rx_buffer(drv_data, &buf); + } else { + + /* If the TX direction is not active and + * the transfer should be stopped after + * the current block, stop the reception. + */ + if (drv_data->active_dir == I2S_DIR_RX && drv_data->stop) { + drv_data->discard_rx = true; + stop_transfer = true; + } + } + } + } + + if (released->p_tx_buffer) { + buf.mem_block = (void *)released->p_tx_mem_slab; + buf.dmm_buf = (void *)released->p_tx_buffer; + /* If the last buffer that was to be transferred has just been + * released, it is time to stop the transfer. + */ + if (released->p_tx_buffer == drv_data->last_tx_buffer) { + drv_data->discard_rx = true; + stop_transfer = true; + } else { + free_tx_buffer(drv_data, &buf); + } + } + + if (stop_transfer) { + tdm_stop(drv_cfg->p_reg); + } else if (status & NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED) { + tdm_buffers_t next = {0}; + + if (drv_data->active_dir != I2S_DIR_RX) { /* -> TX active */ + if (drv_data->stop) { + /* If the stream is to be stopped, don't get + * the next TX buffer from the queue, instead + * supply the one used last time (it won't be + * transferred, the stream will stop right + * before this buffer would be started again). + */ + next.p_tx_buffer = drv_data->last_tx_buffer; + next.p_tx_mem_slab = drv_data->last_tx_mem_slab; + next.buffer_size = 1; + } else if (get_next_tx_buffer(drv_data, &next)) { + /* Next TX buffer successfully retrieved from + * the queue, nothing more to do here. + */ + } else if (drv_data->state == I2S_STATE_STOPPING) { + /* If there are no more TX blocks queued and + * the current state is STOPPING (so the DRAIN + * command was triggered) it is time to finish + * the transfer. + */ + drv_data->stop = true; + /* Supply the same buffer as last time; it will + * not be transferred anyway, as the transfer + * will be stopped earlier. + */ + next.p_tx_buffer = drv_data->last_tx_buffer; + next.p_tx_mem_slab = drv_data->last_tx_mem_slab; + next.buffer_size = 1; + } else { + /* Next TX buffer cannot be supplied now. + * Defer it to when the user writes more data. + */ + drv_data->next_tx_buffer_needed = true; + return; + } + } + (void)supply_next_buffers(drv_data, &next); + } +} + +static int data_init(const struct device *dev) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + + drv_data->state = I2S_STATE_READY; + int err = pinctrl_apply_state(drv_cfg->pcfg, PINCTRL_STATE_DEFAULT); + + if (err < 0) { + return err; + } + drv_data->drv_cfg = drv_cfg; + return err; +} + +static const struct i2s_driver_api tdm_nrf_drv_api = { + .configure = tdm_nrfx_configure, + .config_get = tdm_nrfx_config_get, + .read = tdm_nrfx_read, + .write = tdm_nrfx_write, + .trigger = tdm_nrfx_trigger, +}; + +#define TDM(idx) DT_NODELABEL(tdm##idx) +#define TDM_CLK_SRC(idx) DT_STRING_TOKEN(TDM(idx), clock_source) +#define PCLK_NODE(idx) DT_CLOCKS_CTLR(TDM(idx)) + +#define TDM_NRFX_DEVICE(idx) \ + static tdm_ctrl_t tdm##idx##data; \ + static struct tdm_buf tx_msgs##idx[CONFIG_TDM_NRFX_TX_BLOCK_COUNT]; \ + static struct tdm_buf rx_msgs##idx[CONFIG_TDM_NRFX_RX_BLOCK_COUNT]; \ + static void tdm_##idx##_irq_handler(const struct device *dev) \ + { \ + tdm_irq_handler(dev); \ + } \ + static void tdm_##idx##data_handler(tdm_buffers_t const *p_released, uint32_t status) \ + { \ + data_handler(DEVICE_DT_GET(TDM(idx)), p_released, status); \ + } \ + PINCTRL_DT_DEFINE(TDM(idx)); \ + static const struct tdm_drv_cfg tdm_nrfx_cfg##idx = { \ + .data_handler = tdm_##idx##data_handler, \ + .pcfg = PINCTRL_DT_DEV_CONFIG_GET(TDM(idx)), \ + .clk_src = TDM_CLK_SRC(idx), \ + .mck_frequency = DT_PROP_OR(TDM(idx), mck_frequency, 0), \ + .pclk_frequency = DT_PROP(PCLK_NODE(idx), clock_frequency), \ + .p_reg = NRF_TDM##idx, \ + .control_data = &tdm##idx##data, \ + .mem_reg = DMM_DEV_TO_REG(TDM(idx)), \ + }; \ + static struct tdm_drv_data tdm_nrfx_data##idx; \ + static int tdm_nrfx_init##idx(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_IRQN(TDM(idx)), DT_IRQ(TDM(idx), priority), \ + tdm_##idx##_irq_handler, DEVICE_DT_GET(TDM(idx)), 0); \ + \ + int err = data_init(dev); \ + if (err < 0) { \ + return err; \ + } \ + k_msgq_init(&tdm_nrfx_data##idx.tx_queue, (char *)tx_msgs##idx, \ + sizeof(struct tdm_buf), ARRAY_SIZE(tx_msgs##idx)); \ + k_msgq_init(&tdm_nrfx_data##idx.rx_queue, (char *)rx_msgs##idx, \ + sizeof(struct tdm_buf), ARRAY_SIZE(rx_msgs##idx)); \ + return 0; \ + } \ + BUILD_ASSERT(TDM_CLK_SRC(idx) != ACLK || DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL), \ + "Clock source ACLK requires the audiopll node."); \ + DEVICE_DT_DEFINE(TDM(idx), tdm_nrfx_init##idx, NULL, &tdm_nrfx_data##idx, \ + &tdm_nrfx_cfg##idx, POST_KERNEL, CONFIG_I2S_INIT_PRIORITY, \ + &tdm_nrf_drv_api); + +/* Execute macro f(x) for all instances. */ +#define TDM_FOR_EACH_INSTANCE(f, sep, off_code, ...) \ + NRFX_FOREACH_PRESENT(TDM, f, sep, off_code, __VA_ARGS__) + +#define COND_TDM_NRFX_DEVICE(unused, prefix, i, _) \ + IF_ENABLED(CONFIG_HAS_HW_NRF_TDM##prefix##i, (TDM_NRFX_DEVICE(prefix##i);)) + +TDM_FOR_EACH_INSTANCE(COND_TDM_NRFX_DEVICE, (), ())