From 4306081f9af4704029cc0bb67343522ee1b9d746 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 18 Dec 2025 11:47:42 -0600 Subject: [PATCH 001/143] UPSTREAM: soc: ti: k3: Add support for AM62L commit a159d0ee0351a6a43a188b15d06f88ac00527735 upstream. The TI AM62L is a low-power ARM Cortex-A53 based SoC with display for IOT, HMI and general purpose applications. More information here: https://www.ti.com/product/AM62L Add initial SoC and DTS support here. Signed-off-by: Andrew Davis --- dts/arm64/ti/am62l3_a53.dtsi | 173 +++++++++++++++++++ dts/vendor/ti/am62l-main.dtsi | 283 ++++++++++++++++++++++++++++++++ dts/vendor/ti/am62l-wakeup.dtsi | 56 +++++++ soc/ti/k3/am6x/CMakeLists.txt | 2 +- soc/ti/k3/am6x/Kconfig | 1 + soc/ti/k3/am6x/Kconfig.soc | 5 + soc/ti/k3/soc.yml | 3 + 7 files changed, 522 insertions(+), 1 deletion(-) create mode 100644 dts/arm64/ti/am62l3_a53.dtsi create mode 100644 dts/vendor/ti/am62l-main.dtsi create mode 100644 dts/vendor/ti/am62l-wakeup.dtsi diff --git a/dts/arm64/ti/am62l3_a53.dtsi b/dts/arm64/ti/am62l3_a53.dtsi new file mode 100644 index 000000000000..fa450878fc87 --- /dev/null +++ b/dts/arm64/ti/am62l3_a53.dtsi @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/ { + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <1>; + }; + }; + + firmware { + psci: psci { + compatible = "arm,psci-1.1"; + method = "smc"; + }; + + scmi { + compatible = "arm,scmi-smc"; + arm,smc-id = <0x82004000>; + shmem = <&scmi_shmem>; + + #address-cells = <1>; + #size-cells = <0>; + + scmi_pds: protocol@11 { + compatible = "arm,scmi-power"; + reg = <0x11>; + #power-domain-cells = <1>; + }; + + scmi_clk: protocol@14 { + compatible = "arm,scmi-clock"; + reg = <0x14>; + #clock-cells = <1>; + }; + }; + }; + + oc_sram: sram@70800000 { + compatible = "mmio-sram"; + reg = <0x70800000 DT_SIZE_K(64)>; + ranges = <0x0 0x70800000 0x10000>; + #address-cells = <1>; + #size-cells = <1>; + + scmi_shmem: sram@0 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x100>; + }; + }; + + arch_timer: timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + interrupt-parent = <&gic>; + }; + + gic: interrupt-controller@1800000 { + compatible = "arm,gic-v3", "arm,gic"; + reg = <0x01800000 0x10000>, /* GICD */ + <0x01840000 0xc0000>; /* GICR */ + interrupt-controller; + #interrupt-cells = <4>; + #address-cells = <1>; + #size-cells = <1>; + status = "okay"; + + its: msi-controller@1820000 { + compatible = "arm,gic-v3-its"; + reg = <0x01820000 0x10000>; + status = "okay"; + }; + }; +}; + +&wkup_timer0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&wkup_timer1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&wkup_i2c0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_uart0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_uart1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_uart2 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_uart3 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_uart4 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_uart5 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_uart6 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_i2c0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_i2c1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_i2c2 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_i2c3 { + interrupts = ; + interrupt-parent = <&gic>; +}; diff --git a/dts/vendor/ti/am62l-main.dtsi b/dts/vendor/ti/am62l-main.dtsi new file mode 100644 index 000000000000..c1e42304a49f --- /dev/null +++ b/dts/vendor/ti/am62l-main.dtsi @@ -0,0 +1,283 @@ +/* + * Device Tree Source for AM62L SoC Family Main Domain peripherals + * + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + main_uart0: serial@2800000 { + compatible = "ns16550"; + reg = <0x02800000 0x100>; + clock-frequency = <48000000>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart1: serial@2810000 { + compatible = "ns16550"; + reg = <0x02810000 0x100>; + clock-frequency = <48000000>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart2: serial@2820000 { + compatible = "ns16550"; + reg = <0x02820000 0x100>; + clock-frequency = <48000000>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart3: serial@2830000 { + compatible = "ns16550"; + reg = <0x02830000 0x100>; + clock-frequency = <48000000>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart4: serial@2840000 { + compatible = "ns16550"; + reg = <0x02840000 0x100>; + clock-frequency = <48000000>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart5: serial@2850000 { + compatible = "ns16550"; + reg = <0x02850000 0x100>; + clock-frequency = <48000000>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart6: serial@2860000 { + compatible = "ns16550"; + reg = <0x02860000 0x100>; + clock-frequency = <48000000>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_i2c0: i2c@20000000 { + compatible = "ti,omap-i2c"; + reg = <0x20000000 0x100>; + clock-frequency = <100000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + main_i2c1: i2c@20010000 { + compatible = "ti,omap-i2c"; + reg = <0x20010000 0x100>; + clock-frequency = <100000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + main_i2c2: i2c@20020000 { + compatible = "ti,omap-i2c"; + reg = <0x20020000 0x100>; + clock-frequency = <100000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + main_i2c3: i2c@20030000 { + compatible = "ti,omap-i2c"; + reg = <0x20030000 0x100>; + clock-frequency = <100000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + main_gpio0: main-gpio0 { + #gpio-cells = <2>; + gpio-map = <0 0 &main_gpio0_0 0 0>, <1 0 &main_gpio0_0 1 0>, + <2 0 &main_gpio0_0 2 0>, <3 0 &main_gpio0_0 3 0>, + <4 0 &main_gpio0_0 4 0>, <5 0 &main_gpio0_0 5 0>, + <6 0 &main_gpio0_0 6 0>, <7 0 &main_gpio0_0 7 0>, + <8 0 &main_gpio0_0 8 0>, <9 0 &main_gpio0_0 9 0>, + <10 0 &main_gpio0_0 10 0>, <11 0 &main_gpio0_0 11 0>, + <12 0 &main_gpio0_0 12 0>, <13 0 &main_gpio0_0 13 0>, + <14 0 &main_gpio0_0 14 0>, <15 0 &main_gpio0_0 15 0>, + <16 0 &main_gpio0_0 16 0>, <17 0 &main_gpio0_0 17 0>, + <18 0 &main_gpio0_0 18 0>, <19 0 &main_gpio0_0 19 0>, + <20 0 &main_gpio0_0 20 0>, <21 0 &main_gpio0_0 21 0>, + <22 0 &main_gpio0_0 22 0>, <23 0 &main_gpio0_0 23 0>, + <24 0 &main_gpio0_0 24 0>, <25 0 &main_gpio0_0 25 0>, + <26 0 &main_gpio0_0 26 0>, <27 0 &main_gpio0_0 27 0>, + <28 0 &main_gpio0_0 28 0>, <29 0 &main_gpio0_0 29 0>, + <30 0 &main_gpio0_0 30 0>, <31 0 &main_gpio0_0 31 0>, + <32 0 &main_gpio0_1 0 0>, <33 0 &main_gpio0_1 1 0>, + <34 0 &main_gpio0_1 2 0>, <35 0 &main_gpio0_1 3 0>, + <36 0 &main_gpio0_1 4 0>, <37 0 &main_gpio0_1 5 0>, + <38 0 &main_gpio0_1 6 0>, <39 0 &main_gpio0_1 7 0>, + <40 0 &main_gpio0_1 8 0>, <41 0 &main_gpio0_1 9 0>, + <42 0 &main_gpio0_1 10 0>, <43 0 &main_gpio0_1 11 0>, + <44 0 &main_gpio0_1 12 0>, <45 0 &main_gpio0_1 13 0>, + <46 0 &main_gpio0_1 14 0>, <47 0 &main_gpio0_1 15 0>, + <48 0 &main_gpio0_1 16 0>, <49 0 &main_gpio0_1 17 0>, + <50 0 &main_gpio0_1 18 0>, <51 0 &main_gpio0_1 19 0>, + <52 0 &main_gpio0_1 20 0>, <53 0 &main_gpio0_1 21 0>, + <54 0 &main_gpio0_1 22 0>, <55 0 &main_gpio0_1 23 0>, + <56 0 &main_gpio0_1 24 0>, <57 0 &main_gpio0_1 25 0>, + <58 0 &main_gpio0_1 26 0>, <59 0 &main_gpio0_1 27 0>, + <60 0 &main_gpio0_1 28 0>, <61 0 &main_gpio0_1 29 0>, + <62 0 &main_gpio0_1 30 0>, <63 0 &main_gpio0_1 31 0>, + <64 0 &main_gpio0_2 0 0>, <65 0 &main_gpio0_2 1 0>, + <66 0 &main_gpio0_2 2 0>, <67 0 &main_gpio0_2 3 0>, + <68 0 &main_gpio0_2 4 0>, <69 0 &main_gpio0_2 5 0>, + <70 0 &main_gpio0_2 6 0>, <71 0 &main_gpio0_2 7 0>, + <72 0 &main_gpio0_2 8 0>, <73 0 &main_gpio0_2 9 0>, + <74 0 &main_gpio0_2 10 0>, <75 0 &main_gpio0_2 11 0>, + <76 0 &main_gpio0_2 12 0>, <77 0 &main_gpio0_2 13 0>, + <78 0 &main_gpio0_2 14 0>, <79 0 &main_gpio0_2 15 0>, + <80 0 &main_gpio0_2 16 0>, <81 0 &main_gpio0_2 17 0>, + <82 0 &main_gpio0_2 18 0>, <83 0 &main_gpio0_2 17 0>, + <84 0 &main_gpio0_2 20 0>, <85 0 &main_gpio0_2 21 0>, + <86 0 &main_gpio0_2 22 0>, <87 0 &main_gpio0_2 23 0>, + <88 0 &main_gpio0_2 24 0>, <89 0 &main_gpio0_2 25 0>, + <90 0 &main_gpio0_2 26 0>, <91 0 &main_gpio0_2 27 0>, + <92 0 &main_gpio0_2 28 0>, <93 0 &main_gpio0_2 29 0>, + <94 0 &main_gpio0_2 30 0>, <95 0 &main_gpio0_2 31 0>, + <96 0 &main_gpio0_3 0 0>, <97 0 &main_gpio0_3 1 0>, + <98 0 &main_gpio0_3 2 0>, <99 0 &main_gpio0_3 3 0>, + <100 0 &main_gpio0_3 4 0>, <101 0 &main_gpio0_3 5 0>, + <102 0 &main_gpio0_3 6 0>, <103 0 &main_gpio0_3 7 0>, + <104 0 &main_gpio0_3 8 0>, <105 0 &main_gpio0_3 9 0>, + <106 0 &main_gpio0_3 10 0>, <107 0 &main_gpio0_3 11 0>, + <108 0 &main_gpio0_3 12 0>, <109 0 &main_gpio0_3 13 0>, + <110 0 &main_gpio0_3 14 0>, <111 0 &main_gpio0_3 15 0>, + <112 0 &main_gpio0_3 16 0>, <113 0 &main_gpio0_3 17 0>, + <114 0 &main_gpio0_3 18 0>, <115 0 &main_gpio0_3 17 0>, + <116 0 &main_gpio0_3 20 0>, <117 0 &main_gpio0_3 21 0>, + <118 0 &main_gpio0_3 22 0>, <119 0 &main_gpio0_3 23 0>, + <120 0 &main_gpio0_3 24 0>, <121 0 &main_gpio0_3 25 0>, + <122 0 &main_gpio0_3 26 0>, <123 0 &main_gpio0_3 27 0>, + <124 0 &main_gpio0_3 28 0>, <125 0 &main_gpio0_3 29 0>; + gpio-map-mask = <0xffff 0x0>; + gpio-map-pass-thru = <0x0 0x1>; + }; + + main_gpio0_0: gpio@600010 { + compatible = "ti,davinci-gpio"; + reg = <0x00600010 0x28>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + status = "disabled"; + }; + + main_gpio0_1: gpio@600038 { + compatible = "ti,davinci-gpio"; + reg = <0x00600038 0x28>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + status = "disabled"; + }; + + main_gpio0_2: gpio@600060 { + compatible = "ti,davinci-gpio"; + reg = <0x00600060 0x28>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + status = "disabled"; + }; + + main_gpio0_3: gpio@600088 { + compatible = "ti,davinci-gpio"; + reg = <0x00600088 0x28>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <30>; + status = "disabled"; + }; + + main_rti0: watchdog@e000000 { + compatible = "ti,j7-rti-wdt"; + reg = <0x0e000000 0x100>; + clock-frequency = ; + status = "disabled"; + }; + + main_rti1: watchdog@e010000 { + compatible = "ti,j7-rti-wdt"; + reg = <0x0e010000 0x100>; + clock-frequency = ; + status = "disabled"; + }; + + main_spi0: spi@20100000 { + compatible = "ti,omap-mcspi"; + reg = <0x20100000 0x400>; + interrupts = ; + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&scmi_pds 72>; + clock-frequency = ; + clocks = <&scmi_clk 299>; + ti,spi-num-cs = <4>; + status = "disabled"; + }; + + main_spi1: spi@20110000 { + compatible = "ti,omap-mcspi"; + reg = <0x20110000 0x400>; + interrupts = ; + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&scmi_pds 73>; + clock-frequency = ; + clocks = <&scmi_clk 302>; + ti,spi-num-cs = <4>; + status = "disabled"; + }; + + main_spi2: spi@20120000 { + compatible = "ti,omap-mcspi"; + reg = <0x20120000 0x400>; + interrupts = ; + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&scmi_pds 74>; + clock-frequency = ; + clocks = <&scmi_clk 305>; + ti,spi-num-cs = <4>; + status = "disabled"; + }; + + main_spi3: spi@20130000 { + compatible = "ti,omap-mcspi"; + reg = <0x20130000 0x400>; + interrupts = ; + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&scmi_pds 75>; + clock-frequency = ; + clocks = <&scmi_clk 308>; + ti,spi-num-cs = <4>; + status = "disabled"; + }; +}; diff --git a/dts/vendor/ti/am62l-wakeup.dtsi b/dts/vendor/ti/am62l-wakeup.dtsi new file mode 100644 index 000000000000..b583b6522056 --- /dev/null +++ b/dts/vendor/ti/am62l-wakeup.dtsi @@ -0,0 +1,56 @@ +/* + * Device Tree Source for AM62L SoC Family Wakeup Domain peripherals + * + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + wkup_pinctrl: pinctrl@4084000 { + compatible = "ti,k3-pinctrl"; + reg = <0x04084000 0x24c>; + status = "disabled"; + }; + + wkup_gpio0: gpio@4201010 { + compatible = "ti,davinci-gpio"; + reg = <0x04201010 0x28>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <7>; + power-domains = <&scmi_pds 36>; + clocks = <&scmi_clk 146>; + clock-names = "gpio"; + status = "disabled"; + }; + + wkup_timer0: timer@2b100000 { + compatible = "ti,am654-timer"; + reg = <0x2b100000 DT_SIZE_K(1)>; + clocks = <&scmi_clk 93>; + clock-names = "fck"; + power-domains = <&scmi_pds 19>; + status = "disabled"; + }; + + wkup_timer1: timer@2b110000 { + compatible = "ti,am654-timer"; + reg = <0x2b110000 DT_SIZE_K(1)>; + clocks = <&scmi_clk 98>; + clock-names = "fck"; + power-domains = <&scmi_pds 20>; + status = "disabled"; + }; + + wkup_i2c0: i2c@2b200000 { + compatible = "ti,omap-i2c"; + reg = <0x2b200000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&scmi_pds 57>; + clocks = <&scmi_clk 262>; + clock-names = "fck"; + status = "disabled"; + }; +}; diff --git a/soc/ti/k3/am6x/CMakeLists.txt b/soc/ti/k3/am6x/CMakeLists.txt index c20bed681437..206e041f725c 100644 --- a/soc/ti/k3/am6x/CMakeLists.txt +++ b/soc/ti/k3/am6x/CMakeLists.txt @@ -4,7 +4,7 @@ zephyr_include_directories(.) zephyr_sources(common/ctrl_partitions.c) -if(CONFIG_SOC_AM6234_A53 OR CONFIG_SOC_AM6232_A53 OR CONFIG_SOC_AM6254_A53) +if(CONFIG_SOC_AM6234_A53 OR CONFIG_SOC_AM6232_A53 OR CONFIG_SOC_AM6254_A53 OR CONFIG_SOC_AM62L3_A53) zephyr_sources_ifdef(CONFIG_ARM_MMU a53/mmu_regions.c) set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm64/scripts/linker.ld CACHE INTERNAL "") diff --git a/soc/ti/k3/am6x/Kconfig b/soc/ti/k3/am6x/Kconfig index 8a5513d7458e..a619a02a1a56 100644 --- a/soc/ti/k3/am6x/Kconfig +++ b/soc/ti/k3/am6x/Kconfig @@ -39,6 +39,7 @@ config SOC_PART_NUMBER default "AM6234" if SOC_AM6234_A53 default "AM6234" if SOC_AM6234_M4 default "AM6254" if SOC_AM6254_M4 || SOC_AM6254_A53 + default "AM62L3" if SOC_AM62L3_A53 default "AM6442" if SOC_AM6442_M4 default "AM6442" if SOC_AM6442_R5F0_0 default "AM6442" if SOC_AM6442_R5F0_1 diff --git a/soc/ti/k3/am6x/Kconfig.soc b/soc/ti/k3/am6x/Kconfig.soc index dae5216d5531..054215d6e513 100644 --- a/soc/ti/k3/am6x/Kconfig.soc +++ b/soc/ti/k3/am6x/Kconfig.soc @@ -47,6 +47,10 @@ config SOC_AM6232_M4 bool select SOC_SERIES_AM6X_M4 +config SOC_AM62L3_A53 + bool + select SOC_SERIES_AM6X_A53 + config SOC_AM6442_M4 bool select SOC_SERIES_AM6X_M4 @@ -95,6 +99,7 @@ config SOC default "am6232" if SOC_AM6232_M4 || SOC_AM6232_A53 default "am6234" if SOC_AM6234_M4 || SOC_AM6234_A53 default "am6254" if SOC_AM6254_M4 || SOC_AM6254_A53 + default "am62l3" if SOC_AM62L3_A53 default "am6442" if SOC_AM6442_M4 default "am6442" if SOC_AM6442_R5F0_0 default "am6442" if SOC_AM6442_R5F0_1 diff --git a/soc/ti/k3/soc.yml b/soc/ti/k3/soc.yml index 7234dacefa01..3f2cb22e156d 100644 --- a/soc/ti/k3/soc.yml +++ b/soc/ti/k3/soc.yml @@ -15,6 +15,9 @@ family: cpuclusters: - name: m4 - name: a53 + - name: am62l3 + cpuclusters: + - name: a53 - name: am6442 cpuclusters: - name: m4 From 70a9a9fcc3efcd689eee7b4d63f6038cc3dca138 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 23 Dec 2025 15:16:21 -0600 Subject: [PATCH 002/143] UPSTREAM: boards: ti: Add support for the AM62L EVM commit 9c3343d00fa208f495a3723f8bf84ec76990dd8c upstream. The AM62L evaluation module (TMDS62LEVM) is designed for low-cost and performance optimized AM62L family of application processors. More information here: https://www.ti.com/tool/TMDS62LEVM Add base board support here. Signed-off-by: Andrew Davis --- boards/ti/am62l_evm/Kconfig.am62l_evm | 8 ++ .../am62l_evm_am62l3_a53-pinctrl.dtsi | 17 +++ boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts | 43 +++++++ boards/ti/am62l_evm/am62l_evm_am62l3_a53.yaml | 15 +++ .../am62l_evm/am62l_evm_am62l3_a53_defconfig | 32 +++++ boards/ti/am62l_evm/board.yml | 6 + boards/ti/am62l_evm/doc/img/am62l_evm.webp | Bin 0 -> 20988 bytes boards/ti/am62l_evm/doc/index.rst | 109 ++++++++++++++++++ 8 files changed, 230 insertions(+) create mode 100644 boards/ti/am62l_evm/Kconfig.am62l_evm create mode 100644 boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi create mode 100644 boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts create mode 100644 boards/ti/am62l_evm/am62l_evm_am62l3_a53.yaml create mode 100644 boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig create mode 100644 boards/ti/am62l_evm/board.yml create mode 100644 boards/ti/am62l_evm/doc/img/am62l_evm.webp create mode 100644 boards/ti/am62l_evm/doc/index.rst diff --git a/boards/ti/am62l_evm/Kconfig.am62l_evm b/boards/ti/am62l_evm/Kconfig.am62l_evm new file mode 100644 index 000000000000..05a9d9425ab4 --- /dev/null +++ b/boards/ti/am62l_evm/Kconfig.am62l_evm @@ -0,0 +1,8 @@ +# Texas Instruments Sitara AM62L EVM +# +# Copyright (c) 2025 Texas Instruments Incorporated +# +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_AM62L_EVM + select SOC_AM62L3_A53 diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi new file mode 100644 index 000000000000..16a3351009fb --- /dev/null +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&wkup_pinctrl { + status = "okay"; + + uart0_rx_default: uart0_rx_default { + pinmux = ; /* (D13) UART0_RXD */ + }; + + uart0_tx_default: uart0_tx_default { + pinmux = ; /* (C13) UART0_TXD */ + }; +}; diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts new file mode 100644 index 000000000000..3c9170c6c30a --- /dev/null +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; + +#include +#include "am62l_evm_am62l3_a53-pinctrl.dtsi" + +/ { + model = "TI AM62L EVALUATION MODULE (EVM)"; + compatible = "ti,am62l_evm"; + + chosen { + zephyr,console = &main_uart0; + zephyr,shell-uart = &main_uart0; + zephyr,sram = &ddr0; + }; + + cpus { + cpu@0 { + status = "okay"; + }; + + cpu@1 { + status = "okay"; + }; + }; + + ddr0: memory@82000000 { + reg = <0x82000000 (DT_SIZE_G(2) - DT_SIZE_M(32))>; + }; +}; + +&main_uart0 { + current-speed = <115200>; + pinctrl-0 = <&uart0_rx_default>, + <&uart0_tx_default>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.yaml b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.yaml new file mode 100644 index 000000000000..6d469560df03 --- /dev/null +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.yaml @@ -0,0 +1,15 @@ +identifier: am62l_evm/am62l3/a53 +name: TI AM62L Evaluation Module (EVM) +type: mcu +arch: arm64 +toolchain: + - zephyr + - cross-compile +ram: 2048 +supported: + - uart +testing: + ignore_tags: + - net + - bluetooth +vendor: ti diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig b/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig new file mode 100644 index 000000000000..fb49deec39fc --- /dev/null +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig @@ -0,0 +1,32 @@ +# Texas Instruments Sitara AM62L EVM +# +# Copyright (c) 2025 Texas Instruments Incorporated +# +# SPDX-License-Identifier: Apache-2.0 + +# Zephyr Kernel Configuration +CONFIG_XIP=n + +# Serial Driver +CONFIG_SERIAL=y + +# Enable Console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# ARM Options +CONFIG_AARCH64_IMAGE_HEADER=y +CONFIG_ARMV8_A_NS=y +CONFIG_ARM64_VA_BITS_36=y +CONFIG_ARM64_PA_BITS_36=y + +# Cache Options +CONFIG_CACHE_MANAGEMENT=y +CONFIG_DCACHE_LINE_SIZE_DETECT=y +CONFIG_ICACHE_LINE_SIZE_DETECT=y + +# Multicore Support +CONFIG_SMP=y +CONFIG_PM_CPU_OPS=y +CONFIG_MP_MAX_NUM_CPUS=2 diff --git a/boards/ti/am62l_evm/board.yml b/boards/ti/am62l_evm/board.yml new file mode 100644 index 000000000000..c42a8cc6ed73 --- /dev/null +++ b/boards/ti/am62l_evm/board.yml @@ -0,0 +1,6 @@ +board: + name: am62l_evm + full_name: AM62L TMDS62LEVM evaluation module (EVM) + vendor: ti + socs: + - name: am62l3 diff --git a/boards/ti/am62l_evm/doc/img/am62l_evm.webp b/boards/ti/am62l_evm/doc/img/am62l_evm.webp new file mode 100644 index 0000000000000000000000000000000000000000..d9245a843127911b5dae674fc2b05c7a97cdacc8 GIT binary patch literal 20988 zcmZ6wV~{RPu&w#FZQHhO+qP}nwr$(CZSS_ZyLa2&^PPK7Oiav=`csinnNhjwSrJ*v zQsUxIN&tX{n6RR{BBv%a002PzZ)pGnlz;$2vWk*Kr~m-azX-cu;)?HIzI+y*o}ArI zBdByjD0s1$9#9Yc&Yl(Y4h&Qe508y@!9sa#-D5Ou*a5 z@9)R2!S8R3#V}W?T%vnvGR|H(~ zJ*=(3xpr^bcOO?z%{@F;?s|p#9a_hJ4&~>zN_qwfJjUo$!&2hfK4~V^C&lIV(rrAN z==p~0YCOc0`Sg#~eFeSt1XOX&j{WrfT}oZ8V)nxNDIRHVwR@fyC+*rWwB0&)z!Bo% z&hh3kDvMcI=6J#qk0>ZEZDDPq48rz3JYwlsLhU~tdUe;(i-%sw#vL>33E-m*$={7m zdxjWbnF}#oa8Nvm>;`|!`wdB?B!4?mT+p|}9^fc*r?13;aWo|Je%(DKLpN6leM|z5 zn}(#~;bm|h*ipC7oK9jB%`qP6Qdo>orn`wBHf7V;p0#+Oo#D{Jn-eGC{r6qs9|fmw=4Wu+DO%!p0~dJUloSeR-}6_QU(h@BMeC>VMNk$xVy=`S zAxGtDS-GmJ%ZTqO3F`)Iy>mPm8Mc<<9KI}0@IYNJ4vo^)ifi90 z5V)bHF_B#RpC@voCo?pqq*8dCr!gv?Ytfre#B-CHlT-Z-^bVD~-U8S?M-VR6)sL_@ zx~ViokJZX{V~-VB#MF^jHh-wiqE|ix*d3?BI^I*$R?}&Kpi%G8H@%1JKcU2@r5!(v z!c&Gvebz2{elJE4)YX@DgmvzrliPRBN!E3JQp`-;3Fr27q=<;;(|Bfi-&Az-CvQZt zS;o|Wj&^Mj^!dvY-jQ3 zv{kf=h(4;nIrd{_*kxwr%ZlXEFz4%DMpGD;^#$I7{4?!4|=69{lN9EtBL%;BIU|(I~xqZ)@DOZ+LuwI#KW2zWvc*0*_)6*Ko34 zxY9Ex@K3cbo861Q7%Rdh0(6I6O9#1292xelgqE(Bv~?!rcHjMQtqE^cG_>6gyzS)Z zCT$%%bfd(sfNyvJb(An%AUjHo-}O` z*$r!+G5(dn|IF0=Vw4Xt%`OByOWf}7Jccae951-8E-aWjsp*t8y}h##XBjoK01_WE z%tVeXK}mQa6#eI*2NHA}B;X#Ob<;Bf3Li#U)8DloN!9}I$ zz?7fYmzWj37h0h9a0A_OSPUw;snYl^**W5ta7oMx*vTs}B4x$R*OpT#VMtJi^=j8Nk4=(7h(jx;WBob2K|ea+00wM-v4iZLQ& zg3|*RjWt*T=Mgs2k9`R;O?7%zY2yL8nig9ysZ!lhjLggjgt@sS)b@ zzwOk$I&6x~V$OYO;Ouy4qRQM*`dgX0ayhj_#CkI_px>UE;)E`cdOEtxhdh?=N_G;o z?I`@rQ5#EHJYZ=(LfzR?X4)x;aDkE900(?V=>m>2D<4*G=u1=()?oQ4b~3&0TBD2g zI&!hnsZ85Z>Jp|%ZD*r-_lJU^yt~aJB1~_0oC!zW7l-jXr6j zl%`MI_J;6<>~RYnPTPK$gtBxHu2C|=q7o>&JG>W?bKdlfx3)j`8k>DfMo&D>MBx)R zI*hpBulPRcLm&BnjO6gnv#Oq~)3hBd_c9M~X1^XHT&GXEo}JZ#If5Y|bU#r@o)yVx z-ve8&wQYu>_m%Fp9G<5mq}E${jbH*BO;Tu8~U%Q`Q;LzUdF4nT)1Cx-^zpa)nsx4(PEc4`T1Qv2(6zqw|* zKT89dqL{NBV6KOYper_jJ8!$t1XV$h`51Iwa!4^DZ0(yoSE`n7V7i>g;dQ|MR@mVa;C1K&LuAmu6l{DPIDxD5x z*%a%f^;M&6@+tYQ)$JSBx~DCog2?=GM6pU>QOY8fgBqE9MQ@ce4!qTo6s=r?EFM}vv5;@^ z^jtco($?H6sYH^0d*mGdpy6z;Z~961yX$N>IMcNTZn(_7ojLNDrLnZj{k5_u;sPPg zXHYU`&KmRQS6D8_tmF0-AD`|Am~`CnN93rSoyg$XsC`k`Hdd@CX0%V4xue3`p`sEu zS8$_@EdK}_1Y_-;&raE~$yomI7YH%YejMn5m=TLCpVz5n7Hw-7;*{jmY4zhYa>Ze_ z*ug42W@HdW7HJnqXlQH+mv#1B5{sx~GwDWP4hOhsZWc+3!!%>|OTXqgS{79Y>`l&^ zPbaQ>HqvUVZWquq&CY2S3PBs`;+j>;?7N*i1?OZ=CTesW;E;Ikr+>0EMWEsS6NAV= zhiRC=Bz-EPa%r7BVC9^qRSq9iHWm0$3B3S=U4)f2lC=FS15*Iqu>QuF~5@ZA8LEZ1DSJR<UKL=^CMdA9PB}!@Dzps?&bI`|(SE9Msge`jO zL?>*ADVCCvk>+#2%n4@R{Z2A9ow*h)G3#7WNBAhnM;*^RPH7ga zENUDsvKBcD^4mflzD#A{J;PUTfGbAjO`>@l^f~XF+KgLC`dN4g165M|X>(HGp7bAD zE7DL*2~ zu1=J^$`tO!I4w+h`b5P51=PRzMawSdoDNaL0uso;Iz^AqKdJy=YI3(Ks>VtnZGoZn zDEtS>_oj>yprjYIkKfP=cw}rEUdsyG>~r(ftapwSCLndJ|;k)W;rS9$NrnBG8G!A6WxQhb2RhG zd1#ps6mY&3b%E+aZx1R2sC3(nm1X@jTjoSvl$R>(9Id4D@_*E}eun^(fSnxG{HS`& zp(YNj-bF#mc`%^RZwLY$^fF`Qkfyx5g8%_Zj$|M+GmYop<=#;I3id2VDljSFyPcg7 zfPRH4h>vXB^8zOZynSR(grk%Z!lhcF?*Qt*)eYf(ObwPQA@8 zbarp@m=x`QP4-wg3m2_2%W6RL_y&KM7+nUhG{xh%Z!JPiRm((ix5}rI6rClnLEan> zx?3hW#;oo+!|iBbJWPH0*U>_&;ubqZZ5H{De4Rc6^8?x;4 zpctJU5a8FJfPccf@!W)#N2>BCCdMWE{U6~S-&ur?VpMkWh`irYc`e{!-$D>ojKuoq z-}XJ$+_V({La!amDKcASBw^0>ngM{%?iVGro>~Aud#kDhS6QiJKa6i9y_tknM{@yb zX!0C^>N46|;302|b1(}eTwRW}C_W3j&G+IGzy~3zb;&M<#dJPap9D8^=`|?xyy)ti z)QtZZYppak9%PmD$AV0^$V=h%x1u}&1>vIV7CVA+_04%9@aonYUa@tRohDv!>h0xU zAplMSAQRTUK;YPJAyYRc0>6~`@^&z3hzDeQnzM(3B*IGiAXiz&&sk}jM3D8?Bszs3ibjTPm&QMWjEBov1j)wq( zQ}UZs>6L1T4u`Vk%{&F2GxW^0MD4nq_I-Ctda?iIvii}5&}J!lu)okX0O@YfGSA@J zsGqF;zSLA719ofp%b3TLf4+{Ye}LozAF3H#PNUf#$KKzG)N~;Y{ni{vxrojA_}UsZ z67vB^H4)|0@Y&xFH^GV4^}GlC_X74QXv#m2BQ4#aUX_5~HNN7Lkkrqs(cTv{JugL2 zsjN72FG4Khcm*r1DLJs{qDAi9KLM=2&qeohU%6h2sC{SWe|*X-S5$C5xbF*Do#O&Z zUve{jolWID*dU}!uUA!h;{L!gVUVvcAt_Qz&AG7i$TFMt^0QEHMg=Bc^3tdO(Mo1| zUMhlm-r-c_uEAsaqmoSYzBEPU_5_Nm_%~5wRX_Owh2S=OaWMuYtabTTw?~qB^t;i) z8HEErRfXt;W9b$N?(WteXJu3aY`XgH5A3HnwP%3E|EFx=j$oh*U^EHPCa+RhYl3as@q3{>hTE6qqZ6A9N z8yMQ*5{0~HINjIANHVb_Jn%v0gG{kUNES|SudBHxJCOONf7g^C@=}1-N=?0ixDad?YN?Bt`8JG)$EzsCQ97y9m*rU?fFbFsQxWDU2`=V<@0Yddp|Gb%2(}8a+Eg9 zzOMdGQ!QZHt?OEON<{9`!GZ4jqy|__2QyoFSkdJpCFRja9aZp>++Sjr43do^B2Ra- zEmu$w2!nf|m<*JyBAg&^t*AI2By7h*%st9b=H2P8RzA%QG}Wbl$xr)cYnk9SZ?U*H zKd|}s-Aw(S+?=1Av&qa04`?jU!BdB}e>CzQDZM_#tFY}8f)Lagn@-E4^R z!djm(O?c(~){9Wv%mt_7gWCvkG)^{wlc}g22ONZSKw3%qQE0YF!V}zL6(lDB8pL45 zSh(3-V2iIb8wE&WNI9oD24pGo$Mv^R+S-xyg!_jWosCK0^jwd``Vx{8vzuOlLAky6 zRp1s6r4&SB)VyfDSlF(ssi-G>!=30Lv$7yZHYPOl^X05O>6y>VQ5Uw`h~C$Deme&z zeAV@Qs7Q&AKOdcb3em;H?6-Rln3MOTfEkUL>ijtjE#%6}`2ce2Ikr4ZbULBGnUHi- z1}r`q-zGLJ=u9F%XTL9;0s%g?)M!*d9ENyd{u=Ru2+^^2wZ8R-W&N#1MJL?@We})E z`c1&lFTlzDahV&9)7)8wE+B>MXM87I9~&Q+gJ!29AS!zNE#}y!`yVJSx0>JbPp;{d zx1Ybf35Fwp&TaNZ!g%PeL0+NucsNL;a*^$f#F~YapO>{Q2nQ%)Nm&P`l8%DhbtALe z9u!w941K@9zst$ejsqT7PVdh%ocrk@QX1m_JJK`w1Nd)tNLb4U05l>1GWuD2+xU>VzZ+@Td=ohBxeVz3KJaBd7HFlD|AHCQfBjLq z!uT7&O^NP?@bC@x12+=@7vQ$OR49OP*YO?lVd#yD#yDjzD``4H|E3%aBx5)g1)FKM z53MLjWsAi`lmgq6Nb~bz8>_e~BaY=UoQepo1OC6!u9)l}HBoB$MV_#TyKS4GVJu*2 zuxBh%ycGum znTo{1tkClHg)7y^LuVY65->`awe9B#NS0SZ*x5-XZ-Z_(qoRy_ zs3cz37n*eRnZO?XvRcG%Iaw~DUfqj}Xm9*!zimFaDm5_3`B8%&_13qydjEd4Zt5)IOmxc_7{JLXF8g@pP*D&E`3wrGN#ls(~C#`1x4acy0@B2MZh+0RGw4MRZd405#7|Gj?Pg?<}F0ioXa8Zt+0FbB~kGh&vcuNkY)WRDsZ$oohgM6c%6}RcpbU^Xeg8v=b05Ms?Nm(&b!sqwE3}W zfp(LJlL$TUq>IY7t(T}hT_AsopvUv?p_^b^{=1)+@+ZdGK;6155}ow?!e3xIM}wmG z58p8s4MgM@j;)vMJFmC-5?uo7q=V!tY!wKJ!L9?Jkc@0$OamReWAI)n-&{SK*{S1F z!QAtSG8fo|{}Fp~A^eY!-)9WdJsu*b_rE$#R<)T1!XTI2(0!;XL1DCLe}Rdi-vi@& z_{?h9M4u^)eOGg3Q&8aap=75C<2IL= zE+Z+Kr&2-=2MH18TUr|?r(kchu}jy-s#vQn_AX0nh-lxQ{W7|u}(K7jU#PG$5W6aqE4O}7OeT9eJa28~Vp z1%hf5eF@YsN^bkpK(>A@H2eULCe~zu0EPf5CS=(3P3hA8Fqvm#QWgBqf6C_Lesa_o zI7I52>CF@TwQ!iaBP`qh!N&yZXf|@(O)C+7Ypn|sbxqX%mwQ|)|5-~KuSX< zK0(UD*JwH#PWRS;bJ*C`j(oJmOoxo^T0kc?t>tGsJ%0Tv{GbeuWd9M+Ph#{B@~6fA z<(*8{QAODKn#I1esUTf_Kpp|p=FB7MH#F)F`3bsF+sFk)HnRu)P3SUnyHxQ`Ry}D2 zw}f;!s2+yO7n!?6JQrvmm{2YjGBd3r6?z#L54|Tc$AM#YO~S zL&gb8R2YR}x5Z*Sh)2W&4zjlws*3`-5L+MFZ5jE2UsYw+nR|Lb;yuQr1ukE)%P?0@ zb}6vuodQ*SqN|J3#+}U@OdJ|H2E%ifNQ^$Nl8%hwLs-Ji$DAY|j;B^y`hZztsTH=C z)F967-}&sOslQ;&zk~_jKZ{w+pVj}1e&u*6gF~+PBKx&y5IcFlj_=q#0*^CB(xZX0 zIvHIgW(H)hiY>^*M0lLTUBzw(qJHq^wc!PD&AK`uD8TFctxz*d!j5&B4X$6uyZ`AO zcsRzgj-p+Ec;m+Jw8Oh~3wmq7PD0hRPAV}QQP$u8V>aotG8IckG`cMkaZ*(4WR4C% zp(keTeRu1P3;WM10&7J zN32mzZR){I1ke?4+!t=363Xwn<&VJ8fu6)nH|p38(*Ks2;Y%e1P?e#7mEbW@l4l&p zNDh>Gkt++1HkjUizk|3Klu|g7`T4Z`Q}uuK^?$RYww2^H91pyzrKPf$Jq3JSj)XOS zzGwdDuK%4@_CGrOKXGWuS}Dj_j5GbH|7&*S|3Bll1pt0NT?haB#e|Xo-^9axp}O>9 zA4V>S9Z(-?C*^b8ez!ZmUHfX(T6PU61OS*uvPMXh=wZ8=xaN`Hw*n1;tGc2M-pxP+ zTXHHfgH~u`C#*BX} zndk;%oZkgNqCR9g!PV6};K)PjhXl|_qMdBH4gF4H4g$bLv-LoT-0ZZ0i>J{G>&el2JUl+f5T*DGeH0s`(;D%EmyUQuTKPGF+wvb$Y`Z|-@@PploOOgrYHUZ zP?;wOO)8Y5vROup=b@`4N1HV}*yn-A7jtS*i5EBgyxlhM06kc*Ti1|6(OeYnAX=RRiXz)SZ7d?VbN32+O^dZ)a^Nx}-cs zJ9T!=(e+9>ef^C_p|B*=fSFS;LoK70z!iP?}4G?o%BOvzMh zA>S{1Kq8x2L5c!5Kk~aFnQa6aPf+P^aDO__Z#v#@3nX5Bj0-}@YAkP?aF0Ucmw=s1 z3mU2q1ZpwJZZI4jrHCjisRC6dm2#v~q^GyM5e7nPKXbg)wiVTY%bK_TMsRI8%|p$M z%W4E6I}_e>iPl?sB-z~uyP zJ*cxqBhSglW`$5_uYSzNVZM1(R9^BOlc~gVv;)UwD67+U=WB~o!!82rzJXX9tw5& zua59;JiT@MdI&?CK+l>>uLNhCnv9aj|LP&Zh%bD zU!2CU^Hn`-Zj{%^JXbYi83*uUydme85R-%|@&dQ39Vo5{l@GM!IHpy?(iOpc@*a#a zD#({+X4^6&uLh_Q_A6eboo;(EL%hJA>8UZAY_vvNRuIN~?##o8X4bg z)i4tghsoVO)xLdG0bGPzG@fxU3$wYGTpW*We%{!{KDlt}JWbIEGC}7H+QP8?G5N_Q zgkf(lY*VjgHh2G?d`l86CY;e)EoO9GrA=^{s0M^wZmJ$|FQ$pI5)^2e+Cipa;hWYSrzSSwevbF&Hs5$4QstAo5>ARK<(kR^Lya4?Qd{ z^b2M^4&S(xb2p0&(;6&x@tO_)@oJPVWi~k6wc0&97zh* zurFc|fdG+E&W>w}riH0xY#ACl+hOZAs@knHX4tFeaV1p7-&NM@iW~c}BMtfEtquZ( zq{t4VLz}I)h_%tC9YNWoKA&c`oU1y#J`TH3lRsoWQleCKxVK=}O*u`2XT0|vNWO`E@CLJ$`mZ(UTTkZP9>(f13_MNGjd^L2vC?=1sP~sd_W2THu;zK5= z1(RsNK%EQ~eq`INAvf!E{i z;rercWAyftyIYs!{8J!(*EiNwspnIbgGkVcy;!7Kbx~21CW_ZVVr7kUg$7#IH#&Fb zm+wwBPs}hzV!7f<&K^0)Vr5hLd}u=Pubsp}eV3mPMXuigX`WRic?#!D0IIw{-O@d_ zTnf52Rn(QQ&{+jf^0g0;*$HJzA@vi{n-~uytsGR=Wiu5FO5RkiSG^8`#1$kkJ41hL z!pa}F6%4Zh$y1&IrTNw3=8m+}XbEOH6=A+n3qT&tov&eP>@)4<`oiW!-&Q`E*l&Ay z9~_VrGB6EY8JyQSKJu!_ z^>jJK95o}7t-FWy91-m9RRrqgn*ACs&f~^z8w6{jaEApSrR-pV*l z=)521<=hwtkT11u}tFRB)7Ev!y z@7{(9dS{fVy%!7k{$?T8@YtFn0}z%9S=WP-V7DpQ5>@zg@_+B;d3yaVwG)sh&pU)N zy7mNkxM()-hAcDsC`meEE(YaOtl196&qI@Z!Z`S{25w_NHuPGw-?ChzmF*_*x;%GK z#Z~c{SlZ9K{JJCO%x}7ci^Vq-lxC$ zEjTH~(A5!zY0hpu-N{x`2NwzYnU~`?KBVeMonWs%ioSYd;?=v+_n7&C2p~>^H$8o!56yl;q_NJ}j*wR#(06IVPuQ$1 znvWwprNaPsrZEyMT5(lkcZ3!Tet8dHC7VI*r!n>#o%;r*{q(!A^hHPqj*HW`vdQq& zGF2s9J=B3#^E-aG%~Kr#4Byjo=5n&xLK0uG8ba-ON6AmRpcpO9*S`z@5W#duY=8hh ze0^dl7Kc9N&Qx?#8T|5{<>@qtGG|Sv``W$ue$S_C+6eH|n?p zV+n9#7$13gIO;{R#aZ+O18zPul2w*BS(3ew>1^JG2cBo=L5#%^0l%%Xa^^n1M$vC9 zr1m>=HxJSc9dB>jG`)K7;8Fpwwdy9B;Yt^$uM2_}3jbnsYVYlo(XZMhB=IiVp3N`1 z)$R>fBSvX$3<}9utF?<_dja=q1md0DJ@Ddq_5M(BtTt2*|%n%$kxuUNLC+Qy=2Sh z4?+G=#;HIBwN!|`4p--p(IEC{C)InM>z9lvCSCHV*InNti; zE9_6-4`xY*UzewOMO>74eqL@wXU*s$a?#q)i)ecrW^q6rl-)$NF81@sA4#d*_u>*B z0lEH4D_3wp;Fl;vY=GHERY=K^zsAIMoEczx#R)>e%{i>VRdf2`E$D;{ka#m6@uLBV@adQNmkruR0G3xt+Lg^-7dPrv%^oiJ<`4ND1&0w)0zU0WZFZNc za!GYT&4mlfP{UUi8^BbM`sMI1)lmhgN*TW00q#?)K=4_-lk|#Mu5UH~r$FxE4~g>T zBG%$`Ib@yDKXm?*#r7uGJOQG02Ukj`yZPV+?#4l+Tse5+&pgTVsG+qU7fC+Q^*hnq zHTS{0wtC0*xf@G0Tpnw-Sq07gtUVaSY4K@J5)24uv>gVv+V@j#v^pI)hY+(oUh>gc zeOAz#DveFm(G9sOyIi;_Tt-VTc4(|kD>o)zXY_?fZ2$(C|GUWnf)fI~{#9fF?p(^i z)j(w-tG*+ML+>I(T1?byQR?(FeEL8Z)R(J9p#P#4r$nCu&F=0V1+Q=CKg)9|xQ$4h ziH_P6^MfadYlEGm?KU66X^Q1_vz73u$rMHtN4R#^S%>`|qCl;^u*#5eX9KSC1sae_ z%{}MQ15>DF7Mpi-M;{mvIw}xK)|Ws@!)x=d1pkHPpwGn}_CC3+TD(AgmXQUkf+zabwi_00^P1ajRzK7nPZeJLok{yMQ!O>8;RjfuW$I#Z8QReDKX zWz52IlrjeoaG^i8CFGXM`${FHLpjt|5)T_>GO*OIB>wtk)^RCORcr+Uh-ldv!8NE9 zLUfzB+v!~bx9svk(Z%b-84+sV^hQB1;R)rxp-^e}2>nbm} z5=`;W?2il>KFZVAzeQISH7$d%V`U2np4CxRMb-@48v8Wow!K!s&FK?IZz_;PeO=b4 zSZh(19Y}s`qMs-^lKh|vY&=z%ff*r8I%@n)v_Pg~@V)>+4g+)6y;BR>gLGYU+Acp)K5NMG4yCWR?U|6fP%cFChHZQij>PB(!pR9ra6o|u@W^h zLIEe_ktlRne;)9jw27U8e=%Gxu9IQ94Qd6JD>GgPPSe6?F6G>X-CLcj;0Nzz2D%yR zxR#Q(QCP+4dPBMxO%W*q?c*%|c#QIIr%&MQ@D)OHvz&hYS@Rlu?Y)7;{#DV|k>lT$ z%HGi!{s7<|@h``ldBy>7q%FXi-u)ycqoz{4QrMa)Q+)h)17F#l7OGN%vQTY<4ue`> zym&kfPK+Pka=3_a_576ChRYHEA9ZO+VATT-t4T@Tvm{T_v~W4M3|=X)HUD%+AW~h- zO75wL%GA9uly?V>U=4-`@V$>o-6xgRN9NebfTQFORCE^AZb+89bt8=cI@qLChnhaH z(xmmgS7=2E6MCAc))X4Myd31%pdhweSuEIx9Hkuo+c1iYu6LfvPNRh8q<=kqcw+}h z1`s2{q;h=^Aln`k#g!-0K|iYLFpxG^j}}S$cxh;a`RAA&A#qT&$`QNb3r1+9K@qPJ zN0yVQU@Bapav%;2%-1iMM3_6wPM(!bWz2#*h8AYnS_$-S6|Ii^*E zyt#L@a=8fRvF0~f$J|8qaqmhr*JZsZD?EH~&l(lseB79qG}LJo!5j_TK$nWYBCiPM z*V(S-4%q-m?7+4=10x=58$#A4H=D7g!(alsQF@1?EnbC4TpSmWq;HfvZY1_VnMgPL za6aJC5ZaI8?^9H{=bv!oDTGx|^|Wtbh2nH9mxZCZt!U;2+;fXk6T2~J0Uy#fZ3e}5 zsCBcVA15m8FdUr(ct;1Sa%u3fm8~xa2gS}tOK>bss)df;Yw6YQPj$)OP&5S&q47e0 zMi5&dC+vRj8kS5$&sjKat~uS8<)e~?c%jSRlPUZo;A1L!7rBZ?Rx^Ek`!@EU0Nw4~Z?(3TIwq)Ji1G z{hIqL;UsUI$@i<{B#3r1qO*S=cI_cZdUqs(5{|v31SXMKYo6Jq|##Fld5AeqH;R{XGdkQ-p&E_k~fU4A?-Y|jl^=>p9J9*~xx@e{)Dt!xM- z8vU`LPYIMYtKJYU4-dk-ga5Kc)Wt#d=d*0fH0-rL5iDgr2FNn^F?_##7QsaauzRgHMRqWS$5_uqkYJ zV!}8dJ~sMqUfW*~w9$xlQtIqQd zRd#C4aQw5Ja>obuDIV~hy^AAx;wC_b0xI47vCkgFyE21r&$NkB<<}WV5j2I@>{OH) zXN2A}eK^*gmG;l5^9vxsEAGg0B^UV5O93J?X_^{<&S~!TGX*hBj(ruJSD2$o!QoJ( zfQO7?>#c9B@gs}z%L;lITZo_s#GlcGP^HIEA9ghzUmDa^{+U=KB$@u{^7+v46x?d}_b4_UdOm($ccN%_+^YaUu?THqe_iYR`Y$G~ z2rN-P7Zh;H z?=Mr@31bD?UPg{Cf&8BxSUg{}H?KT_@FM;mu1203nwx!obUJVzNwlYkioa_j>;Q!g z!9#G>H4c00msC#jYAwsU%MD#c+jT^wCm$B@v#RYc$Wj-$kz%zvr6rMAFa^M74|qDw zMU;A%X}8~X8>lU4it=d+YBY(Vhgc`cHbRh4JK3Nl{CXCkYEpu)+{7e4K8F^jsT&TU zC7S`K6lGLxO1QfGej&2o35gsM)^)Dw4>Jw@e6$uK;8%c-GBV(mol;VfZyE^C3eX%P z8QA82(C1n`EXdt`RwRyH`6UkjBUVA)Bo8ZwGv{6wa5*w^GarP!L?Hz6_{|{2s$nDt zt8fIaF|d5=B!Oii$2g?K%NzTikyiKLy(5Oq2^u*ZlD-{d?Ip$T!H8nlyVY3Aw4(V9t2?YqSUc+#dL(b)fo zSGBL2bFFaYoHN!rFQsXB4#`Vo=O%ipH%C;pPT;JIMc3!ocJ7cWeQB-XR@t0?^GX_k z*7_@?qbb+-rjPIj8I>tp>sEu(ju%b^5p5N&t*rIPQYG$|*?Hm8r#L9Pp*$}VY@5qX zBdoz%Bp!@dt>h7JAXhWv2oXqOR9Kn1zDmtgLdptxj6LALn7F6@Tx z)qm&+bXV;vC^s|9{7~>UfC;)MR6gN$(N#D8(@4~Ce>CU{DX=m1z_(S#q-#CU(PH}X zT$85o8R&lvU+iVq{aIFrVV7squH*(*aW{z&(xOdQMP{%v+z&Rx`(14khIwpoZ%&fm z#GLXT(sDt&QEU(dE$18g^e&`hobo*#V^I%C!0zQby@`M-?khG1QEYL&pjsPx zK3-bRg1&xijMWx08N9ExewBhe44y~K2m2W=l$9qGBtgm$DqV(uF4Gm;7#ugm^P@uQ zqE7>CdHZ3^g55|q(`BM+I?)d?oHYJ9zKxLFf}nVIn21O+YFbWc#1MKW>DZYNc7qE5 zp93)K?Ub<#7OO#i2`g>ib!eY(pni;x)o|qWdNww7IHMTbqq_%&FL9jUR7!4?3A%Be z^gq{6s%gqiHJ><}k0w}3;AvKwm?c=c+;I(p@tp@N){gzFdOp9bG{wb}iL_8;-Z5u2 z@c7}Ca0^TAR819w$r3s9)O<}j{w-FT{Y#pc%j*W8q%WGK-U|uQ6)s)gPIZjDzN;&S z5TyhkXs~a%Z;1s{xH?7o1Hs2W*ldPX3?z@j7+#9iNxbnO+;z)`yuzgEu1W_?sO+v8 zwf&*~Ez~y7E|7-e!pw53+B^N*5yOLru&ub5-CB^iSvAF+?TSLaeM420lroL-^H{VV ztsRj(@=e>fqWxry#&}H**Wr1#8&_@uqbdAV-pRbb!N2YVpDsB8Rq)W&W+Gof zDfSl>vgo1XND0<_K+d(GMk19S6Jr#^7%aGD9lm8PSH z`gHKi{1614wsoyWS_h1Xv8jb3L&OnOA1$pLt$y?M#K5; zS^}+iSZE=xsXUWuC3fG;MGmg^tspK}d=SJR$7n_ul!PRacQ^lZ#!8ldN12o^numb* zM79Xl==64fWwxLl`V*jmtA;xO?U1q&IK+ST-~^bIOsK55oO#Gy$bE9E4E@@ln!Z^5S`<%%MMsVX9F5$bN?cC}fj^Cfh09Fz zioH!JH;D#{y&dh}Xxa?b0<&u%wp=xx5A`vW{yu{vF~I%wL47e7 z;-lKchIg=7b(^Xez+2gzh$%Dv)bfnazqxzzWdKKlbVk4GFJdTotR1$+SsDAi8%8|S znI6-W*J!r~I#ZQThMaWgU&0PSh|sB}b~r#A6Tsf7*S%!MQLW3q5>vZlSaq@bdq&aM;u8IFBOY&sl!$kGFyI~Z%HXkW3#k|*Cv zcg5)VYKrAb&pp;8jB=ODwW!JIm@Oi;wz~F(GIY1*K-6jy1$3# z(>K@c9`j7(3a`3d*FD@H3beSEM4T+AENH|gZ>bO%?dALg48iKA@$g41{ zv#;DF#MWgbQn{|GoJ`R-2s%DwbSU0B2kW&&_((d5=DvAMY{md7N;c(|x?utDN>W>$ zb|d|f^1ehO%VtKt9vE!IEs@Pm z-?TYM&<4}~d$v?Nvq&W;Vn@lxp&}C?k$S?h*S;e#O=vi&Pvy_xw0XL)=Mcmw`*1&i}10N$~^*NsvaNs78V=!S8{Z95!o;Uk8LW1Xey;W z>QS18cQmR&wGhm1@cI2h)IF)AcU4ypTzfpnNx3y?>DSik0Ns*Nnn_=(L{BR|!D#J-g#0>m12$1#Ghw zeg8pzu;{5YppcKWkW-S~7m`rWVu*%J2(XEQ9WhpbU5&stqbk3yRv8O7nKN2I*yaQJ z887pbF@1sC^BY&#W@G}23vt%pzf>cha-OUg2f}LH5)PT-Mn_~pVbMO0>>IMJ+2w0= zOj989u20-G{H@@H{7-Uzor?Nd-!8qLc0vF7Ky})D#l2T;N9{IBUW9Hu3C2UwL+m*4 zue|E$90ojUdPr`jxD?iw+ymS~N1*B{yM48N0@P%IhNdH7+(PmhJ*~@%wGbr*9mu`( zhM+Nqtie=Pjv{%25~UXq=*lvY-qh^~&=Xk$+IytDMD?5u3PraAWy~K)P(OEG`;}!| z!zU4zQI$<#rllRX|0jPDfbK`_&j(}dsmC&?N7Pr;S;MR_D3M350EFn;9!pkdR~Jv> z5mj)bUHvK0HtoTI%xtW(?6Wt_HI9Vl`$FKR+A($xCFD1rcmJLlsW#ysE2F()$2L z$q4rI!4jEUDVfA@V6e4K1?`xF>H!h-e`#R&K#B_t4to<~d%;!gG1&p$vK~18lMzfn z(mbdvodE;7Bo3=p9*mG&Q=RiP>Nuok3h-Byq1wn-`MG|o#r^wufkEgi>mxqG4)o$F9(PxQbd01{rcJLrLYqLKmVbOuCJ=5VqABnJHiVm;EV;uxY8X{* zx!cBBgP}C3dcQ}(KlYoGKO-z=wX+U4*Q5dzAKvn&>g@|)_3Dk~O3_*7RIq=WuKkFq zW(T?&nJPVFUm?OTq6qL{^q6y?m>&}nYjh4rETVUES2Li=!(!8c1YW9xSWe|+6(-Um zK}BqN$2MtlXdF+cAJ}wZ-1>{u%2K)wQZ^_QaXSq?jN%Dp)y&zT*}8TR@TTB~tb@~s zdG>7m@n`>jWMh=A2)sLOEbx53YhbgjOV`h2ktdG$*yV~Hp}_jguG*+TM|juCujnQX zYx>JEqsVnhu1popR>?q`p!K8Iy>_0f6U=a)_iqHug(!FZ6p}An{g{@3Qf`IQZYzXSF}+*aZFtIbI*{xf~6Q zFkIgmc&}AoPf!W2jik+muzMfWdS*XPxKX9bG2M0l>!Gl`VYs*6^(G}LL6&6a&AaSC z4OEcL1@UBH<4zCMSh7|u2p;3u*@IB z+UggkO{hm=d8kjD5AtuCj4u|R&QW&=m`S%G3;YzNB<^9?`)F3?uvmU|Z^``i!2}9$ zq!~)EkI3M+3__)7KFEJ&eFj@wz^{BNn8!m->|eGF?se(Q4e<$Fd80G3(#UIN1Qp6G zv#)y#3A+M~1mr5L0L(VKpMLT(VIpRLTI(E7Ggtf0ePltBUV%GX7D#CcBba7UMUa=o zO*5B?wn5Fa%Zyw>hw`mtUE02UOyDfoyIq9{82krPOl+CnDfYOjnNsJp*^&xnfp4qZ z;ZK1A3qLpi!NPzQoVlG44oxx0A1JHFx`ACC$SlKZR0TNbVV=BhN+6Lj$Cfy?gl5!H z-sI?f8|f!f&Ff%GRwy)P(+Y0>8=h9yt5C32BPk99SP{r*r@W5brh@MohD`lyyO2n? zP+w%OtRl}^ukuL|qpK=8NyzK`6DSd^cRg8k5+iTmfyF?x*Y3UbwaxwSejGSAfulIF zT!A|j@M4W3%*$u+SmzFZD^CtEo!wv2NiXhy+H&Tibu*!dEsHBHnfZ%j1aI?JcH9OR z2hm}-LpoJx5BHO(=!9yt-O$5OwNR>7anKI+1YdddNyuKoF9B%zun^iIR6H@Ku;iAV)C^H_in+m?Q2Y5$ z+-}HZ=8p!+9vzb|br*Gz7_Z@~dz|Wz5!tMZ@T;X!LpLS4($zfwEMcV4K+$2xF=_QP zQn5H-H3IUyPJ7uMn2Ed$?=VIh$bg+HCQz`7OsT7>Th73xU>C;eko)9zawUw|YPvaK z!@tdN`67+i7D2=AB^A`$-2*d>)odJ}qmUTHs0f*NFtq{1(QnT(@;&u8)*%4a5=*+@ZN{t)=VA7O-@Y3oOlkg*z zM`GTh*6N_&P3fkgqxShp5n4H?E8gD@+I@VhZo7`^6o$sdeVDyZNKMwnT0#isMtC7u3k(MoWdImNU*_r@`Fano)Hbe`odI&by6E8o z|L#a^c06asjLSHi3cQZ$S;@W6tRT?xc37$Cbb^F@6#*~o1<2Fed|&Ut+UbN7 znO{~{*tv4;KjZJu*thkeNwM=F($BA{F+YsI8?7nYO9DbWd+`32w-$E|{XAm$WuEz8JiL2v0ZUwK5kTsdO*qWzr zQ?n$3u6mC^J(yed;Z1kRoysD42w+dcVC(&)GyC``KI9=U6cOb+14!z9sH<-EN2|Ntmd%;Cy|-nG}t3BP&A~%Kb@9?F*m)F9hM6F1HW;JDPR5mSGb5_cA1ME z+s#8zT<;9L|J|9({N;9PoOY?s^VN1aDKVq47S|!kpb@lR$=TP&ZT>3^AI4rDatn{i zZTrKT$Ze#OmyLpg=##Sv=xDsH5Ph(O&0NHfC|!6D;r8PU;0Aku&lRtC_24$HHSGDB z6M9ZnEXcHQxcyAj)%dkPqMDBg`4sBt)j=8VamMXRmQ{hFO}~x`D_=i}xLNaMOo;vi zLdCA*z0}eP_loVZuf)d6`GZnNvr25HeUG&`9MZQY&2xlFW|di@3`}?zBN+T3=E|E; z2sKLbDZBt-P-67BJFN1o<;!q47;7rF0CuZuLc}uOU;J>N#|cw*i#nKjs!lQ3fIAmB zs;Jip-ZZp@Ic@;-TP!YLcD1;V5Mtp$4y2}D&aabY35NJ`hKc`P<~^1CLR>C$c{ZkA zqzHv`Ku>qPNjcv&SI*on+@bCIV>*)vOc`E5+jfQHOhqDB2Nvxk&V}qu7spIsyE9f} zTT=}|hOZKlr)hZ^)(n6MKYmoP#|ROQ{XCcWOn{oc5qJ$fmZ?vx#EjPCCW!YG#Ke_-;`Gh^P?AKydZW7^>RTY|T z^#s*9#;J%u$c0Y0<|Q}ab`dwov1hA%i@_kWajhKVmrUVKpPUVSNS%~PewTX9TJ&;n z7zUXxD-uEVg4S6-tYe(R=!$u8dvLp?q9lj*GFGc*zM$5oRnl7vi~HrIPPFHmzaIdJV466 zM0_q^X#T9MN?-^{wgX9MI{+4X6)Nk0+1UcQDhn_JhnOMOZ|6c@t4Yzd#q}{~p`GVS z-8#Eibd?K`0wy|JV>^?B7M+_jG^I|a>!C}@j&@}f*k5 zy<{C;A7~0u_==D&iHr)y4b(oGXLk?aeX5x4CTlrg4xINn81@1yh6;g$=0-3}ORb;C zQd8Ee)tzm*BUY)i(jTgBQwz_ni%dp)4tRCpyPg{-4?COHzUo=BM+kT#N1mC)CM6d2 z{3T=QQ9!{A79RS`9^H5mA+QkkqT5{Nq}Qh3Qx&fM+g|ZBWx_;F)KU`3qG`KJbr2tJ z=%Iq$FIobcOE*unu}UkkxC(&$vZM~++LFr0V|;&h=+&{ysj_~6HggvAKlFs|!%wnv zX4Vle+*;hsE?ysy^f!K0y`XXkFhWTb2D8LcxlHtm3urzJHbSWY5DLrS%;=GZFGCOr zZ??M(7C>s-l9|}JM}$DK^RRH<6J9Z_quFT&@I}o^OUeJas^0|pFM|Wgm36bP)Im4@ z1644n$aIseoViD?ah!+f1ssjV+1>{x4a_Ump=+3z+B~Pj>zJ>27p|O3N+6PqkGfTZ zPp8)BuTL?*l_5kLPIv!CNz`+-9hGkxHrtT1%Po3K7bOANuQM^L8qsOox4cMFJO70@ z2Mm%8bFaD^2%ww-C=V(m)VziHq~Jkbk#U4C^rN*UK1kII$p8Q!WngiCxR()#0008eA)jG{4106B z)1v8!#zU{UQkDk@DkdPK+HEY%V-jQ0yd{Y;atS@@N=L6Ps>BP2H>eQk3UT(gQ7 z)#V&P4gKPDWSI;LdvB}J|Lw+OmU0ravm`aiW}ECbTLjrcM(<7)EjnD`c@}a{?iV5)Glk)`-eW z>OTp{LnaW{kzNJHYk&NWO|~zc2ibtT5mH3c8@Hr9uORv zoCk5F{`ZNiV5!+Q88Oa~MjXCoaGS0)I_V407e1`J6n3Ux?^re6Gj5VkwhhhSp@)DI z+)*s)pd8m36X%{eRX%U*+Fa+U2gNPKxMam=r=$TIvify00RGuZ7%`zJFDEpPudJQG zF;OjGX#NX-^7j^qC$hg<-DUXzN2?=U(y|}G_J>7@butzCg^PjfNL6M0;T6R^s^}-t zzhIhKuQ-TBc*=2T((#TK&p8H*^X?yD7c?75ohb~*HcKZ52g6$@wn$^3S6EC|cnH~L z&JZ!C3Eq93yKU(Kn5fxSOs)<`B`V+*@(y~apVb-yKi6%=#CDsaX}i;#{QE!uvZ4R% zl|TQn^LA%ftHL>B+%pSGU;qJhCnf2zCp6<-rNjxKWz50{Vh@JY{G+bj2E(7wvKYcV z^uSc;ol!b}JaVo?n|$I;!lAA#2G&5FKohZ>L;4lMjZ8VLzh;7BjAFkt90SBx@Req; c%aNzR000000000007oV2&;S4c literal 0 HcmV?d00001 diff --git a/boards/ti/am62l_evm/doc/index.rst b/boards/ti/am62l_evm/doc/index.rst new file mode 100644 index 000000000000..211843dc20c2 --- /dev/null +++ b/boards/ti/am62l_evm/doc/index.rst @@ -0,0 +1,109 @@ +.. zephyr:board:: am62l_evm + +Overview +******** + +The AM62L EVM board configuration is used by Zephyr applications that run on +the TI AM62L platform. The board configuration provides support for: + +- ARM Cortex-A53 core and the following features: + + - General Interrupt Controller (GIC) + - ARM Generic Timer (arch_timer) + - On-chip SRAM (oc_sram) + - UART interfaces (uart0 to uart6) + +The board configuration also enables support for the semihosting debugging console. + +See the `TI AM62L Product Page`_ for details. + +Hardware +******** +The AM62L EVM features the AM62L SoC, which is composed of a dual Cortex-A53 +cluster. The following listed hardware specifications are used: + +- High-performance ARM Cortex-A53 +- Memory + + - 160KB of SRAM + - 2GB of DDR4 + +- Debug + + - XDS110 based JTAG + +Supported Features +================== + +.. zephyr:board-supported-hw:: + +Devices +======== +System Clock +------------ + +This board configuration uses a system clock frequency of 1250 MHz. + +DDR RAM +------- + +The board has 2GB of DDR RAM available. + +Serial Port +----------- + +This board configuration uses a single serial communication channel with the +MAIN domain UART (main_uart0). + +SD Card +******* + +Download TI's official `WIC`_ and flash the WIC file with an etching software +onto an SD-card. + +Copy the compiled ``zephyr.bin`` to the first FAT partition of the SD card and +plug the SD card into the board. Power it up and stop the u-boot execution at +prompt. + +Use U-Boot to load and start zephyr.bin: + +.. code-block:: console + + fatload mmc 1:1 0x82000000 zephyr.bin; dcache flush; icache flush; dcache off; icache off; go 0x82000000 + +The Zephyr application should start running on the A53 core. + +Debugging +********* + +The board is equipped with an XDS110 JTAG debugger. To debug a binary, utilize the ``debug`` build target: + +.. zephyr-app-commands:: + :app: + :board: am62l_evm/am62l3/a53 + :maybe-skip-config: + :goals: debug + +.. hint:: + To utilize this feature, you'll need OpenOCD version 0.12 or higher. Due to the possibility of + older versions being available in package feeds, it's advisable to `build OpenOCD from source`_. + +References +********** + +https://www.ti.com/tool/TMDS62LEVM + +.. _AM62L EVM TRM: + https://www.ti.com/lit/pdf/SPRUJG8 + +.. _TI AM62L Product Page: + https://www.ti.com/product/AM62L + +.. _WIC: + https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-PvdSyIiioq/10.01.10.04/tisdk-default-image-am62xx-evm-10.01.10.04.rootfs.wic.xz + +.. _EVM User's Guide: + https://www.ti.com/lit/pdf/SPRUJG8 + +.. _build OpenOCD from source: + https://docs.u-boot.org/en/latest/board/ti/k3.html#building-openocd-from-source From 76775fb764bbfa4f9ff70e12f8490d96c8bca8f4 Mon Sep 17 00:00:00 2001 From: Soumya Tripathy Date: Wed, 4 Feb 2026 12:06:14 +0530 Subject: [PATCH 003/143] UPSTREAM: boards: ti: sk_am62: use correct AM6254 SoC variant commit 6557337773505483f5f41b8e4989ba812f69d157 upstream. The SK-AM62 and SK-AM62B-P1 board variants both use the AM6254 SoC, not AM6234. Update board configuration to reference the correct SoC variant. This changes the board identifiers from sk_am62/am6234/* to sk_am62/am6254/* for both M4 and A53 cores. Documentation updated to mention both board variants: - SK-AM62 (base variant) - SK-AM62B-P1 (variant with integrated PMIC) Signed-off-by: Soumya Tripathy --- boards/ti/sk_am62/Kconfig.sk_am62 | 4 ++-- boards/ti/sk_am62/board.yml | 2 +- boards/ti/sk_am62/doc/index.rst | 15 ++++++++++----- ...pinctrl.dts => sk-am62_am6254_a53-pinctrl.dts} | 0 ...am62_am6234_a53.dts => sk_am62_am6254_a53.dts} | 4 ++-- ...62_am6234_a53.yaml => sk_am62_am6254_a53.yaml} | 2 +- ...a53_defconfig => sk_am62_am6254_a53_defconfig} | 0 ...k_am62_am6234_m4.dts => sk_am62_am6254_m4.dts} | 0 ...am62_am6234_m4.yaml => sk_am62_am6254_m4.yaml} | 2 +- ...4_m4_defconfig => sk_am62_am6254_m4_defconfig} | 0 soc/ti/k3/am6x/common/ctrl_partitions.c | 4 ++-- 11 files changed, 19 insertions(+), 14 deletions(-) rename boards/ti/sk_am62/{sk-am62_am6234_a53-pinctrl.dts => sk-am62_am6254_a53-pinctrl.dts} (100%) rename boards/ti/sk_am62/{sk_am62_am6234_a53.dts => sk_am62_am6254_a53.dts} (90%) rename boards/ti/sk_am62/{sk_am62_am6234_a53.yaml => sk_am62_am6254_a53.yaml} (84%) rename boards/ti/sk_am62/{sk_am62_am6234_a53_defconfig => sk_am62_am6254_a53_defconfig} (100%) rename boards/ti/sk_am62/{sk_am62_am6234_m4.dts => sk_am62_am6254_m4.dts} (100%) rename boards/ti/sk_am62/{sk_am62_am6234_m4.yaml => sk_am62_am6254_m4.yaml} (76%) rename boards/ti/sk_am62/{sk_am62_am6234_m4_defconfig => sk_am62_am6254_m4_defconfig} (100%) diff --git a/boards/ti/sk_am62/Kconfig.sk_am62 b/boards/ti/sk_am62/Kconfig.sk_am62 index c5001d13618a..f46c1080c323 100644 --- a/boards/ti/sk_am62/Kconfig.sk_am62 +++ b/boards/ti/sk_am62/Kconfig.sk_am62 @@ -6,5 +6,5 @@ # SPDX-License-Identifier: Apache-2.0 config BOARD_SK_AM62 - select SOC_AM6234_A53 if BOARD_SK_AM62_AM6234_A53 - select SOC_AM6234_M4 if BOARD_SK_AM62_AM6234_M4 + select SOC_AM6254_A53 if BOARD_SK_AM62_AM6254_A53 + select SOC_AM6254_M4 if BOARD_SK_AM62_AM6254_M4 diff --git a/boards/ti/sk_am62/board.yml b/boards/ti/sk_am62/board.yml index 7201cf894d77..a99653cb3432 100644 --- a/boards/ti/sk_am62/board.yml +++ b/boards/ti/sk_am62/board.yml @@ -3,4 +3,4 @@ board: full_name: SK-AM62 Evaluation board vendor: ti socs: - - name: am6234 + - name: am6254 diff --git a/boards/ti/sk_am62/doc/index.rst b/boards/ti/sk_am62/doc/index.rst index 0e8b065b9202..b08ea3085797 100644 --- a/boards/ti/sk_am62/doc/index.rst +++ b/boards/ti/sk_am62/doc/index.rst @@ -4,7 +4,12 @@ Overview ******** The SK-AM62 board configuration is used by Zephyr applications that run on -the TI AM62x platform. The board configuration provides support for: +the TI AM62x platform. This configuration supports two board variants: + +- SK-AM62 (base variant) +- SK-AM62B-P1 (variant with integrated PMIC) + +Both variants use the AM6254 SoC. The board configuration provides support for: - ARM Cortex-M4F MCU core and the following features: @@ -25,7 +30,7 @@ See the `TI AM62X Product Page`_ for details. Hardware ******** -The SK-AM62 EVM features the AM62x SoC, which is composed of a quad Cortex-A53 +The SK-AM62 EVM features the AM6254 SoC, which is composed of a quad Cortex-A53 cluster and a single Cortex-M4 core in the MCU domain. Zephyr is ported to run on the M4F and A53 cores. The following listed hardware specifications are used: @@ -83,7 +88,7 @@ To test the M4F core, we build the :zephyr:code-sample:`hello_world` sample with .. code-block:: console # From the root of the Zephyr repository - west build -p -b sk_am62/am6234/m4 samples/hello_world + west build -p -b sk_am62/am6254/m4 samples/hello_world This builds the program and the binary is present in the :file:`build/zephyr` directory as :file:`zephyr.elf`. @@ -129,7 +134,7 @@ The board is equipped with an XDS110 JTAG debugger. To debug a binary, utilize t .. zephyr-app-commands:: :app: - :board: sk_am62/am6234/m4 + :board: sk_am62/am6254/m4 :maybe-skip-config: :goals: debug @@ -137,7 +142,7 @@ The board is equipped with an XDS110 JTAG debugger. To debug a binary, utilize t .. zephyr-app-commands:: :app: - :board: sk_am62/am6234/a53 + :board: sk_am62/am6254/a53 :maybe-skip-config: :goals: debug diff --git a/boards/ti/sk_am62/sk-am62_am6234_a53-pinctrl.dts b/boards/ti/sk_am62/sk-am62_am6254_a53-pinctrl.dts similarity index 100% rename from boards/ti/sk_am62/sk-am62_am6234_a53-pinctrl.dts rename to boards/ti/sk_am62/sk-am62_am6254_a53-pinctrl.dts diff --git a/boards/ti/sk_am62/sk_am62_am6234_a53.dts b/boards/ti/sk_am62/sk_am62_am6254_a53.dts similarity index 90% rename from boards/ti/sk_am62/sk_am62_am6234_a53.dts rename to boards/ti/sk_am62/sk_am62_am6254_a53.dts index c2e6746d8757..e774fe27a460 100644 --- a/boards/ti/sk_am62/sk_am62_am6234_a53.dts +++ b/boards/ti/sk_am62/sk_am62_am6254_a53.dts @@ -6,8 +6,8 @@ /dts-v1/; -#include -#include "sk-am62_am6234_a53-pinctrl.dts" +#include +#include "sk-am62_am6254_a53-pinctrl.dts" / { model = "TI AM62X STARTER KIT (SK) EVALUATION MODULE (EVM)"; diff --git a/boards/ti/sk_am62/sk_am62_am6234_a53.yaml b/boards/ti/sk_am62/sk_am62_am6254_a53.yaml similarity index 84% rename from boards/ti/sk_am62/sk_am62_am6234_a53.yaml rename to boards/ti/sk_am62/sk_am62_am6254_a53.yaml index 75390238c116..be81adca4f93 100644 --- a/boards/ti/sk_am62/sk_am62_am6234_a53.yaml +++ b/boards/ti/sk_am62/sk_am62_am6254_a53.yaml @@ -1,4 +1,4 @@ -identifier: sk_am62/am6234/a53 +identifier: sk_am62/am6254/a53 name: TI AM62X A53 Starter Kit (SK) type: mcu arch: arm64 diff --git a/boards/ti/sk_am62/sk_am62_am6234_a53_defconfig b/boards/ti/sk_am62/sk_am62_am6254_a53_defconfig similarity index 100% rename from boards/ti/sk_am62/sk_am62_am6234_a53_defconfig rename to boards/ti/sk_am62/sk_am62_am6254_a53_defconfig diff --git a/boards/ti/sk_am62/sk_am62_am6234_m4.dts b/boards/ti/sk_am62/sk_am62_am6254_m4.dts similarity index 100% rename from boards/ti/sk_am62/sk_am62_am6234_m4.dts rename to boards/ti/sk_am62/sk_am62_am6254_m4.dts diff --git a/boards/ti/sk_am62/sk_am62_am6234_m4.yaml b/boards/ti/sk_am62/sk_am62_am6254_m4.yaml similarity index 76% rename from boards/ti/sk_am62/sk_am62_am6234_m4.yaml rename to boards/ti/sk_am62/sk_am62_am6254_m4.yaml index c61f12035cdf..c823e100737b 100644 --- a/boards/ti/sk_am62/sk_am62_am6234_m4.yaml +++ b/boards/ti/sk_am62/sk_am62_am6254_m4.yaml @@ -1,4 +1,4 @@ -identifier: sk_am62/am6234/m4 +identifier: sk_am62/am6254/m4 name: TI AM62X M4 Starter Kit (SK) type: mcu arch: arm diff --git a/boards/ti/sk_am62/sk_am62_am6234_m4_defconfig b/boards/ti/sk_am62/sk_am62_am6254_m4_defconfig similarity index 100% rename from boards/ti/sk_am62/sk_am62_am6234_m4_defconfig rename to boards/ti/sk_am62/sk_am62_am6254_m4_defconfig diff --git a/soc/ti/k3/am6x/common/ctrl_partitions.c b/soc/ti/k3/am6x/common/ctrl_partitions.c index a5c8e54563de..e2dc6ed35c6b 100644 --- a/soc/ti/k3/am6x/common/ctrl_partitions.c +++ b/soc/ti/k3/am6x/common/ctrl_partitions.c @@ -19,7 +19,7 @@ #if defined CONFIG_SOC_AM6442_M4 || defined CONFIG_SOC_AM2434_M4 #define MCU_PADCFG_BASE (0x4080000) -#elif defined CONFIG_SOC_AM6234_M4 | defined CONFIG_SOC_AM6232_M4 +#elif defined CONFIG_SOC_AM6234_M4 | defined CONFIG_SOC_AM6232_M4 | defined CONFIG_SOC_AM6254_M4 #define WKUP_PADCFG_BASE (0x4080000) #elif defined CONFIG_SOC_AM2434_R5F0_0 #define MCU_PADCFG_BASE (0x4080000) @@ -30,7 +30,7 @@ static const uintptr_t ctrl_partitions[] = { #if defined CONFIG_SOC_AM6442_M4 || defined CONFIG_SOC_AM2434_M4 CTRL_PARTITION(MCU_PADCFG_BASE, 0), CTRL_PARTITION(MCU_PADCFG_BASE, 1), -#elif defined CONFIG_SOC_AM6234_M4 | defined CONFIG_SOC_AM6232_M4 +#elif defined CONFIG_SOC_AM6234_M4 | defined CONFIG_SOC_AM6232_M4 | defined CONFIG_SOC_AM6254_M4 CTRL_PARTITION(WKUP_PADCFG_BASE, 0), CTRL_PARTITION(WKUP_PADCFG_BASE, 1), #elif defined CONFIG_SOC_AM2434_R5F0_0 From c22bda6f7056f218d84573f489b46574185e689a Mon Sep 17 00:00:00 2001 From: Soumya Tripathy Date: Tue, 27 Jan 2026 11:46:17 +0530 Subject: [PATCH 004/143] UPSTREAM: boards: ti: sk_am62: enable all four A53 cores commit 1f0c37e92a23f7abbdfed0296c220aeee83fcc77 upstream. Enable all four Cortex-A53 cores in the AM6254 device tree and update documentation to reflect the expected multi-core boot sequence output. Signed-off-by: Soumya Tripathy --- boards/ti/sk_am62/doc/index.rst | 13 ++++++++++++- boards/ti/sk_am62/sk_am62_am6254_a53.dts | 6 +++--- boards/ti/sk_am62/sk_am62_am6254_a53_defconfig | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/boards/ti/sk_am62/doc/index.rst b/boards/ti/sk_am62/doc/index.rst index b08ea3085797..64a6264d1bdb 100644 --- a/boards/ti/sk_am62/doc/index.rst +++ b/boards/ti/sk_am62/doc/index.rst @@ -123,7 +123,18 @@ Use U-Boot to load and kick zephyr.bin: fatload mmc 1:1 0x82000000 zephyr.bin; go 0x82000000 -The Zephyr application should start running on the A53 core. +The Zephyr application should start running on the A53 core. When running the +hello_world sample, you should see output similar to: + +.. code-block:: console + + *** Booting Zephyr OS build v4.3.0-4646-g13fc152f3546 *** + Secondary CPU core 1 (MPID:0x1) is up + Secondary CPU core 2 (MPID:0x2) is up + Secondary CPU core 3 (MPID:0x3) is up + Hello World! sk_am62/am6254/a53 + +This indicates that all four A53 cores have successfully booted. Debugging ********* diff --git a/boards/ti/sk_am62/sk_am62_am6254_a53.dts b/boards/ti/sk_am62/sk_am62_am6254_a53.dts index e774fe27a460..c31554f27d96 100644 --- a/boards/ti/sk_am62/sk_am62_am6254_a53.dts +++ b/boards/ti/sk_am62/sk_am62_am6254_a53.dts @@ -25,15 +25,15 @@ }; cpu@1 { - status = "disabled"; + status = "okay"; }; cpu@2 { - status = "disabled"; + status = "okay"; }; cpu@3 { - status = "disabled"; + status = "okay"; }; }; diff --git a/boards/ti/sk_am62/sk_am62_am6254_a53_defconfig b/boards/ti/sk_am62/sk_am62_am6254_a53_defconfig index 5f0314b43918..ddb02b1e6f9f 100644 --- a/boards/ti/sk_am62/sk_am62_am6254_a53_defconfig +++ b/boards/ti/sk_am62/sk_am62_am6254_a53_defconfig @@ -15,3 +15,19 @@ CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y CONFIG_UART_INTERRUPT_DRIVEN=y + +# ARM Options +CONFIG_AARCH64_IMAGE_HEADER=y +CONFIG_ARMV8_A_NS=y +CONFIG_ARM64_VA_BITS_36=y +CONFIG_ARM64_PA_BITS_36=y + +# Cache Options +CONFIG_CACHE_MANAGEMENT=y +CONFIG_DCACHE_LINE_SIZE_DETECT=y +CONFIG_ICACHE_LINE_SIZE_DETECT=y + +# Multicore Support +CONFIG_SMP=y +CONFIG_PM_CPU_OPS=y +CONFIG_MP_MAX_NUM_CPUS=4 From 9af62f5980bd1a272c74203a7fab15a75bf8f3d7 Mon Sep 17 00:00:00 2001 From: Soumya Tripathy Date: Thu, 5 Feb 2026 20:29:31 +0530 Subject: [PATCH 005/143] UPSTREAM: boards: ti: am62l_evm: update documentation links commit 5ca8185a03e170c56d1295b7079d64bf38b1c626 upstream. Update AM62L EVM documentation references to point to correct technical resources: - Update TRM link (correct AM62L SoC TRM) - Update correct link to linux SDK WIC image for am62l_evm Ensures users access accurate AM62L-specific documentation and software resources. Signed-off-by: Soumya Tripathy --- boards/ti/am62l_evm/doc/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/boards/ti/am62l_evm/doc/index.rst b/boards/ti/am62l_evm/doc/index.rst index 211843dc20c2..05b8b0efeefd 100644 --- a/boards/ti/am62l_evm/doc/index.rst +++ b/boards/ti/am62l_evm/doc/index.rst @@ -94,13 +94,13 @@ References https://www.ti.com/tool/TMDS62LEVM .. _AM62L EVM TRM: - https://www.ti.com/lit/pdf/SPRUJG8 + https://www.ti.com/lit/pdf/sprujb4 .. _TI AM62L Product Page: https://www.ti.com/product/AM62L .. _WIC: - https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-PvdSyIiioq/10.01.10.04/tisdk-default-image-am62xx-evm-10.01.10.04.rootfs.wic.xz + https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-YjEeNKJJjt/11.02.08.02/tisdk-default-image-am62lxx-evm-11.02.08.02.rootfs.wic.xz .. _EVM User's Guide: https://www.ti.com/lit/pdf/SPRUJG8 From 8d42c74a7c6b18328ab6e2128b92d6d56aa7b002 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Wed, 18 Mar 2026 18:25:00 +0530 Subject: [PATCH 006/143] UPSTREAM: drivers: adc: ti_am335x: make internal reference optional commit 267e3f39fccefaf3a1cf6b48719e9b5d4c9462bb upstream. Allow using other references by removing the hard requirement on it being internal. To be able to use internal voltage, the DT property ti,vrefp can be used, otherwise it is ignored. Signed-off-by: Amneesh Singh --- drivers/adc/adc_ti_am335x.c | 8 +++++++- dts/bindings/adc/ti,am335x-adc.yaml | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/adc/adc_ti_am335x.c b/drivers/adc/adc_ti_am335x.c index 80cd63b60c61..f32edbb7b8b4 100644 --- a/drivers/adc/adc_ti_am335x.c +++ b/drivers/adc/adc_ti_am335x.c @@ -191,6 +191,12 @@ static int ti_adc_channel_setup(const struct device *dev, const struct adc_chann return -EINVAL; } + if (chan_cfg->reference == ADC_REF_INTERNAL && + DEVICE_API_GET(adc, dev)->ref_internal == 0) { + LOG_ERR("Voltage reference must be provided for ADC_REF_INTERNAL"); + return -EINVAL; + } + if (cfg->oversampling[chan] > TI_ADC_STEPCONFIG_AVERAGING_MAX) { LOG_ERR("Invalid oversampling value"); return -EINVAL; @@ -374,7 +380,7 @@ static void ti_adc_isr(const struct device *dev) static DEVICE_API(adc, ti_adc_driver_api_##n) = { \ .channel_setup = ti_adc_channel_setup, \ .read = ti_adc_read, \ - .ref_internal = DT_INST_PROP(n, ti_vrefp), \ + .ref_internal = DT_INST_PROP_OR(n, ti_vrefp, 0), \ IF_ENABLED(CONFIG_ADC_ASYNC, (.read_async = ti_adc_read_async,)) }; \ \ static void ti_adc_irq_setup_##n(const struct device *dev) \ diff --git a/dts/bindings/adc/ti,am335x-adc.yaml b/dts/bindings/adc/ti,am335x-adc.yaml index d9ef025c4c7a..651e94fdc014 100644 --- a/dts/bindings/adc/ti,am335x-adc.yaml +++ b/dts/bindings/adc/ti,am335x-adc.yaml @@ -14,7 +14,6 @@ properties: ti,vrefp: type: int - required: true description: Reference Voltage (in mV) ti,fifo: From 9bcb291ecea9b50b02a9aa122d72216a1e8baafb Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Wed, 18 Mar 2026 18:25:18 +0530 Subject: [PATCH 007/143] UPSTREAM: boards: am243x_evm: change ADC channel ref voltage commit 187e2a7af61f10e07319c4a9cd046dc21d885506 upstream. Remove custom DT property ti,vrefp and change reference voltage from INTERNAL to VDD_1 for all channels Signed-off-by: Amneesh Singh --- .../am243x_evm/am243x_evm_am2434_r5f0_0.dts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts index b498b214c257..4dac2b5912aa 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts @@ -97,7 +97,6 @@ }; &main_adc0 { - ti,vrefp = <1800>; ti,fifo = <0>; status = "okay"; power-domains = <&adc0_pd>; @@ -106,80 +105,88 @@ reg = <0>; ti,open-delay = <0>; zephyr,gain = "ADC_GAIN_1"; - zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,reference = "ADC_REF_VDD_1"; zephyr,acquisition-time = <0>; zephyr,resolution = <12>; zephyr,oversampling = <4>; + zephyr,vref-mv = <1800>; }; channel@1 { reg = <1>; ti,open-delay = <0>; zephyr,gain = "ADC_GAIN_1"; - zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,reference = "ADC_REF_VDD_1"; zephyr,acquisition-time = <0>; zephyr,resolution = <12>; zephyr,oversampling = <4>; + zephyr,vref-mv = <1800>; }; channel@2 { reg = <2>; ti,open-delay = <0>; zephyr,gain = "ADC_GAIN_1"; - zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,reference = "ADC_REF_VDD_1"; zephyr,acquisition-time = <0>; zephyr,resolution = <12>; zephyr,oversampling = <4>; + zephyr,vref-mv = <1800>; }; channel@3 { reg = <3>; ti,open-delay = <0>; zephyr,gain = "ADC_GAIN_1"; - zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,reference = "ADC_REF_VDD_1"; zephyr,acquisition-time = <0>; zephyr,resolution = <12>; zephyr,oversampling = <4>; + zephyr,vref-mv = <1800>; }; channel@4 { reg = <4>; ti,open-delay = <0>; zephyr,gain = "ADC_GAIN_1"; - zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,reference = "ADC_REF_VDD_1"; zephyr,acquisition-time = <0>; zephyr,resolution = <12>; zephyr,oversampling = <4>; + zephyr,vref-mv = <1800>; }; channel@5 { reg = <5>; ti,open-delay = <0>; zephyr,gain = "ADC_GAIN_1"; - zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,reference = "ADC_REF_VDD_1"; zephyr,acquisition-time = <0>; zephyr,resolution = <12>; zephyr,oversampling = <4>; + zephyr,vref-mv = <1800>; }; channel@6 { reg = <6>; ti,open-delay = <0>; zephyr,gain = "ADC_GAIN_1"; - zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,reference = "ADC_REF_VDD_1"; zephyr,acquisition-time = <0>; zephyr,resolution = <12>; zephyr,oversampling = <4>; + zephyr,vref-mv = <1800>; }; channel@7 { reg = <7>; ti,open-delay = <0>; zephyr,gain = "ADC_GAIN_1"; - zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,reference = "ADC_REF_VDD_1"; zephyr,acquisition-time = <0>; zephyr,resolution = <12>; zephyr,oversampling = <4>; + zephyr,vref-mv = <1800>; }; }; From e999ada0f4d7c9aae8ddf975ceb3d0222e54007e Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Wed, 18 Mar 2026 18:25:45 +0530 Subject: [PATCH 008/143] UPSTREAM: drivers: adc: adc_ti_am335x: add support for pinctrl commit 8134b3b4e8f03362f6c917ebbf182d60c39c9290 upstream. Support configuration of pins for ADC channels in the driver. Signed-off-by: Amneesh Singh --- drivers/adc/adc_ti_am335x.c | 11 +++++++++++ dts/bindings/adc/ti,am335x-adc.yaml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/adc/adc_ti_am335x.c b/drivers/adc/adc_ti_am335x.c index f32edbb7b8b4..07c037d984a7 100644 --- a/drivers/adc/adc_ti_am335x.c +++ b/drivers/adc/adc_ti_am335x.c @@ -8,6 +8,7 @@ #include #include #include +#include #define ADC_CONTEXT_USES_KERNEL_TIMER #include "adc_context.h" @@ -97,6 +98,7 @@ enum ti_adc_irq { struct ti_adc_cfg { DEVICE_MMIO_ROM; + const struct pinctrl_dev_config *pinctrl; void (*irq_func)(const struct device *dev); uint32_t open_delay[TI_ADC_TOTAL_CHANNELS]; uint8_t oversampling[TI_ADC_TOTAL_CHANNELS]; @@ -295,9 +297,16 @@ static int ti_adc_init(const struct device *dev) const struct ti_adc_cfg *cfg = DEV_CFG(dev); struct ti_adc_data *data = DEV_DATA(dev); struct ti_adc_regs *regs = DEV_REGS(dev); + int ret; DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); + ret = pinctrl_apply_state(cfg->pinctrl, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("failed to apply pinctrl"); + return ret; + } + cfg->irq_func(dev); if (cfg->fifo == 0) { @@ -390,8 +399,10 @@ static void ti_adc_isr(const struct device *dev) irq_enable(DT_INST_IRQN(n)); \ } \ \ + PINCTRL_DT_INST_DEFINE(n); \ static const struct ti_adc_cfg ti_adc_cfg_##n = { \ DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \ + .pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ .irq_func = &ti_adc_irq_setup_##n, \ .open_delay = CHAN_PROP_LIST(n, ti_open_delay), \ .oversampling = CHAN_PROP_LIST(n, zephyr_oversampling), \ diff --git a/dts/bindings/adc/ti,am335x-adc.yaml b/dts/bindings/adc/ti,am335x-adc.yaml index 651e94fdc014..d72b4b0b644c 100644 --- a/dts/bindings/adc/ti,am335x-adc.yaml +++ b/dts/bindings/adc/ti,am335x-adc.yaml @@ -6,7 +6,7 @@ description: TI AM335X ADC compatible: "ti,am335x-adc" -include: [adc-controller.yaml] +include: [adc-controller.yaml, pinctrl-device.yaml] properties: interrupts: From bfc368b76ccb4ca95c0ac55ac0e30a33e164f0b5 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Wed, 18 Mar 2026 18:26:03 +0530 Subject: [PATCH 009/143] UPSTREAM: drivers: adc: adc_ti_am335x: fix register MMIO mapping commit 138d2bdc313fdc3a0df6939e2c43fc8d85c0570d upstream. Bind registers to the local variable in the ti_adc_init() function after mapping the address space via DEVICE_MMIO_MAP. Signed-off-by: Amneesh Singh --- drivers/adc/adc_ti_am335x.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/adc/adc_ti_am335x.c b/drivers/adc/adc_ti_am335x.c index 07c037d984a7..e46740e63277 100644 --- a/drivers/adc/adc_ti_am335x.c +++ b/drivers/adc/adc_ti_am335x.c @@ -296,11 +296,13 @@ static int ti_adc_init(const struct device *dev) { const struct ti_adc_cfg *cfg = DEV_CFG(dev); struct ti_adc_data *data = DEV_DATA(dev); - struct ti_adc_regs *regs = DEV_REGS(dev); + struct ti_adc_regs *regs; int ret; DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); + regs = DEV_REGS(dev); + ret = pinctrl_apply_state(cfg->pinctrl, PINCTRL_STATE_DEFAULT); if (ret < 0) { LOG_ERR("failed to apply pinctrl"); From de16b97c099c38ddb80e84898b7fd6f09b94c718 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Wed, 18 Mar 2026 18:26:19 +0530 Subject: [PATCH 010/143] UPSTREAM: boards: am243x_evm: add pinctrl nodes for main_adc0 commit 8e1b2141ec55300c21fb80722b9c212cc75f651a upstream. Add the missing pins for ADC0 channels in the main domain. Signed-off-by: Amneesh Singh --- .../am243x_evm_am2434_r5f0_0-pinctrl.dtsi | 32 +++++++++++++++++++ .../am243x_evm/am243x_evm_am2434_r5f0_0.dts | 9 ++++++ 2 files changed, 41 insertions(+) diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi index ebb531a86293..6f12264fad22 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi @@ -25,6 +25,38 @@ pinmux = ; }; + main_adc0_ain0: adc0_ain0_default { + pinmux = ; + }; + + main_adc0_ain1: adc0_ain1_default { + pinmux = ; + }; + + main_adc0_ain2: adc0_ain2_default { + pinmux = ; + }; + + main_adc0_ain3: adc0_ain3_default { + pinmux = ; + }; + + main_adc0_ain4: adc0_ain4_default { + pinmux = ; + }; + + main_adc0_ain5: adc0_ain5_default { + pinmux = ; + }; + + main_adc0_ain6: adc0_ain6_default { + pinmux = ; + }; + + main_adc0_ain7: adc0_ain7_default { + pinmux = ; + }; + spi0_cs0: spi0_cs0_default { pinmux = ; }; diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts index 4dac2b5912aa..7085cbc3c9cb 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts @@ -98,6 +98,15 @@ &main_adc0 { ti,fifo = <0>; + pinctrl-0 = <&main_adc0_ain0 + &main_adc0_ain1 + &main_adc0_ain2 + &main_adc0_ain3 + &main_adc0_ain4 + &main_adc0_ain5 + &main_adc0_ain6 + &main_adc0_ain7>; + pinctrl-names = "default"; status = "okay"; power-domains = <&adc0_pd>; From b0931e8fb11c26214f94a5de9c28de18fafeb25f Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Wed, 4 Feb 2026 11:31:15 +0530 Subject: [PATCH 011/143] UPSTREAM: drivers: i2c: i2c_omap: fix FIFO byte count extraction commit 7eb1cf21334073ae78a2963082ca3752b6524231 upstream. The driver was not correctly extracting FIFO byte counts from the BUFSTAT register, causing multi-byte I2C transfers to fail. The BUFSTAT register layout is: - Bits [13:8]: RX FIFO byte count - Bits [5:0]: TX FIFO byte count Also, the driver didnot update the remaining bytes after each transfer which is fixed by decrementing the remaining message length after each transfer. This fix aligns the driver implementation with Linux. Signed-off-by: Sunil Hegde --- drivers/i2c/i2c_omap.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/i2c_omap.c b/drivers/i2c/i2c_omap.c index 7deea477ed2a..e453b917409a 100644 --- a/drivers/i2c/i2c_omap.c +++ b/drivers/i2c/i2c_omap.c @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef CONFIG_I2C_OMAP_BUS_RECOVERY #include "i2c_bitbang.h" @@ -26,6 +27,8 @@ LOG_MODULE_REGISTER(omap_i2c, CONFIG_I2C_LOG_LEVEL); #define RETRY -1 #define I2C_BITRATE_FAST 400000 #define I2C_BITRATE_STANDARD 100000 +#define I2C_BUFSTAT_RX_MASK GENMASK(13, 8) +#define I2C_BUFSTAT_TX_MASK GENMASK(5, 0) /* I2C Registers */ typedef struct { @@ -277,6 +280,7 @@ static void i2c_omap_transmit_receive_data(const struct device *dev, uint8_t num } else { i2c_base_addr->DATA = *(buf_ptr++); } + data->current_msg.len--; } } @@ -452,6 +456,7 @@ static int i2c_omap_transfer_message_ll(const struct device *dev) struct i2c_omap_data *data = DEV_DATA(dev); volatile i2c_omap_regs_t *i2c_base_addr = DEV_I2C_BASE(dev); uint16_t stat = i2c_base_addr->STAT, result = 0; + uint8_t num_bytes; if (data->receiver) { stat &= ~(I2C_OMAP_STAT_XDR | I2C_OMAP_STAT_XRDY); @@ -479,9 +484,10 @@ static int i2c_omap_transfer_message_ll(const struct device *dev) /* Handle receive logic */ if (stat & (I2C_OMAP_STAT_RRDY | I2C_OMAP_STAT_RDR)) { - int buffer = - (stat & I2C_OMAP_STAT_RRDY) ? i2c_base_addr->BUF : i2c_base_addr->BUFSTAT; - i2c_omap_transmit_receive_data(dev, buffer); + num_bytes = FIELD_GET(I2C_BUFSTAT_RX_MASK, i2c_base_addr->BUFSTAT); + if (num_bytes > 0) { + i2c_omap_transmit_receive_data(dev, num_bytes); + } i2c_base_addr->STAT |= (stat & I2C_OMAP_STAT_RRDY) ? I2C_OMAP_STAT_RRDY : I2C_OMAP_STAT_RDR; return RETRY; @@ -489,9 +495,10 @@ static int i2c_omap_transfer_message_ll(const struct device *dev) /* Handle transmit logic */ if (stat & (I2C_OMAP_STAT_XRDY | I2C_OMAP_STAT_XDR)) { - int buffer = - (stat & I2C_OMAP_STAT_XRDY) ? i2c_base_addr->BUF : i2c_base_addr->BUFSTAT; - i2c_omap_transmit_receive_data(dev, buffer); + num_bytes = FIELD_GET(I2C_BUFSTAT_TX_MASK, i2c_base_addr->BUFSTAT); + if (num_bytes > 0) { + i2c_omap_transmit_receive_data(dev, num_bytes); + } i2c_base_addr->STAT |= (stat & I2C_OMAP_STAT_XRDY) ? I2C_OMAP_STAT_XRDY : I2C_OMAP_STAT_XDR; return RETRY; From 907bd968aad8c6d1776a65f80e7e34f27f4e66a7 Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Thu, 5 Feb 2026 11:48:21 +0530 Subject: [PATCH 012/143] UPSTREAM: drivers: i2c: i2c_omap: improve timeout handling... commit 809ca435a7842eab84093d3e1a5c1771642adf50 upstream. The driver used a decrement loop to check for time out during message transfer, which was not sufficient. The newer implementation uses a fixed time of 1000ms for timeout to be consistent with the Linux kernel. Signed-off-by: Sunil Hegde --- drivers/i2c/i2c_omap.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/i2c_omap.c b/drivers/i2c/i2c_omap.c index e453b917409a..153332adbd61 100644 --- a/drivers/i2c/i2c_omap.c +++ b/drivers/i2c/i2c_omap.c @@ -22,6 +22,7 @@ LOG_MODULE_REGISTER(omap_i2c, CONFIG_I2C_LOG_LEVEL); #define I2C_OMAP_TIMEOUT 100U +#define I2C_OMAP_TRANSFER_TIMEOUT 1000U /* OCP_SYSSTATUS bit definitions */ #define SYSS_RESETDONE_MASK BIT(0) #define RETRY -1 @@ -539,7 +540,7 @@ static int i2c_omap_transfer_message(const struct device *dev, struct i2c_msg *m { struct i2c_omap_data *data = DEV_DATA(dev); volatile i2c_omap_regs_t *i2c_base_addr = DEV_I2C_BASE(dev); - unsigned long time_left = 1000; + k_timepoint_t end; uint16_t control_reg; int result = 0; /* Determine message direction (read or write) and update the receiver flag */ @@ -579,10 +580,10 @@ static int i2c_omap_transfer_message(const struct device *dev, struct i2c_msg *m i2c_base_addr->CON = control_reg; /* Poll for status until the transfer is complete */ /* Call a lower-level function to continue the transfer */ + end = sys_timepoint_calc(K_MSEC(I2C_OMAP_TRANSFER_TIMEOUT)); do { result = i2c_omap_transfer_message_ll(dev); - time_left--; - } while (result == RETRY && time_left); + } while (result == RETRY && !sys_timepoint_expired(end)); /* If no errors occurred, return success */ if (!result) { From 59b84c86ed3fd4bddd3fe91bec024fc5c8d9e23a Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Thu, 5 Feb 2026 15:42:59 +0530 Subject: [PATCH 013/143] UPSTREAM: drivers: i2c: i2c_omap: fix inconsistent register... commit 945894c8fa5cc7d46fe5b85b33090916d7f6ea3c upstream. This commit fixes the inconsistent type usage. Functions which initialised i2c registers as uint16_t, changed to uint32_t to match with register definition struct. Signed-off-by: Sunil Hegde --- drivers/i2c/i2c_omap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c_omap.c b/drivers/i2c/i2c_omap.c index 153332adbd61..d9d5d6e82a23 100644 --- a/drivers/i2c/i2c_omap.c +++ b/drivers/i2c/i2c_omap.c @@ -167,7 +167,7 @@ static int i2c_omap_reset(const struct device *dev) struct i2c_omap_data *data = DEV_DATA(dev); volatile i2c_omap_regs_t *i2c_base_addr = DEV_I2C_BASE(dev); uint64_t timeout; - uint16_t sysc; + uint32_t sysc; sysc = i2c_base_addr->SYSC; i2c_base_addr->CON &= ~I2C_OMAP_CON_EN; @@ -456,7 +456,7 @@ static int i2c_omap_transfer_message_ll(const struct device *dev) { struct i2c_omap_data *data = DEV_DATA(dev); volatile i2c_omap_regs_t *i2c_base_addr = DEV_I2C_BASE(dev); - uint16_t stat = i2c_base_addr->STAT, result = 0; + uint32_t stat = i2c_base_addr->STAT, result = 0; uint8_t num_bytes; if (data->receiver) { From f63ff3f093160811e4ae31a9cb50b3ffd1510da3 Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Wed, 3 Dec 2025 10:17:25 +0530 Subject: [PATCH 014/143] UPSTREAM: drivers: watchdog: wdt_ti_rti: add callback support commit 33a7ed01793ab0f4d8c92b15b6970b76a1bbcbce upstream. - Add callback support for the watchdog driver. Signed-off-by: Sunil Hegde --- drivers/watchdog/wdt_ti_rti.c | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/watchdog/wdt_ti_rti.c b/drivers/watchdog/wdt_ti_rti.c index 3644dd2ccbc4..bb644a4b806f 100644 --- a/drivers/watchdog/wdt_ti_rti.c +++ b/drivers/watchdog/wdt_ti_rti.c @@ -7,6 +7,7 @@ #define DT_DRV_COMPAT ti_j7_rti_wdt #include +#include #include #include @@ -20,6 +21,9 @@ #define WDT_PRELOAD_MAX 0xfff +/* Only the last 6 bits are used */ +#define CLEAR_WDSTATUS 0x3f + #define RTIWWDRX_NMI 0xa #define RTIWWDRX_RESET 0x5 @@ -59,12 +63,15 @@ struct wdt_ti_rti_regs { struct wdt_ti_rti_data { DEVICE_MMIO_RAM; + + wdt_callback_t callback; }; struct wdt_ti_rti_config { DEVICE_MMIO_ROM; uint64_t freq; + void (*irq_config_func)(void); }; static int wdt_ti_rti_setup(const struct device *dev, uint8_t options) @@ -79,6 +86,8 @@ static int wdt_ti_rti_setup(const struct device *dev, uint8_t options) regs->GCTRL = RTIGCTRL_RUN_BY_DBG; } + /* Clear the Watchdog status register before enabling */ + regs->WDSTATUS = CLEAR_WDSTATUS; regs->DWDCTRL = WDENABLE_KEY; return 0; @@ -119,10 +128,13 @@ static int wdt_ti_rti_window_size(const struct wdt_window window) static int wdt_ti_rti_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg) { const struct wdt_ti_rti_config *config = DEV_CFG(dev); + struct wdt_ti_rti_data *data = DEV_DATA(dev); struct wdt_ti_rti_regs *regs = DEV_REGS(dev); uint32_t timer_margin; int window_size; + data->callback = cfg->callback; + window_size = wdt_ti_rti_window_size(cfg->window); if (window_size < 0) { return window_size; @@ -134,6 +146,7 @@ static int wdt_ti_rti_timeout(const struct device *dev, const struct wdt_timeout return -EINVAL; } + /* Only NMI is supported */ if (cfg->flags == WDT_FLAG_RESET_SOC) { regs->WWDRXNCTRL = RTIWWDRX_NMI; } @@ -158,8 +171,23 @@ static int wdt_ti_rti_feed(const struct device *dev, int channel_id) return 0; } +static void wdt_ti_rti_isr(const struct device *dev) +{ + struct wdt_ti_rti_data *data = DEV_DATA(dev); + struct wdt_ti_rti_regs *regs = DEV_REGS(dev); + volatile uint32_t status = regs->WDSTATUS; + /* Clear status register before servicing the watchdog */ + regs->WDSTATUS = CLEAR_WDSTATUS; + if (status & BIT(5) && data->callback) { + data->callback(dev, 0); + } + regs->WDKEY = WDKEY_SEQ0; + regs->WDKEY = WDKEY_SEQ1; +} + static int wdt_ti_rti_init(const struct device *dev) { + const struct wdt_ti_rti_config *config = DEV_CFG(dev); struct wdt_ti_rti_regs *regs; DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); @@ -169,6 +197,7 @@ static int wdt_ti_rti_init(const struct device *dev) return -EINVAL; } + config->irq_config_func(); return 0; } @@ -180,11 +209,18 @@ static DEVICE_API(wdt, wdt_ti_rti_api) = { }; #define WDT_TI_RTI_INIT(i) \ + static void wdt_ti_rti_irq_config_##i(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(i), DT_INST_IRQ(i, priority), wdt_ti_rti_isr, \ + DEVICE_DT_INST_GET(i), 0); \ + irq_enable(DT_INST_IRQN(i)); \ + }; \ static struct wdt_ti_rti_data wdt_ti_rti_data_##i = {}; \ \ static struct wdt_ti_rti_config wdt_ti_rti_config_##i = { \ DEVICE_MMIO_ROM_INIT(DT_DRV_INST(i)), \ .freq = DT_INST_PROP(i, clock_frequency), \ + .irq_config_func = wdt_ti_rti_irq_config_##i, \ }; \ \ DEVICE_DT_INST_DEFINE(i, wdt_ti_rti_init, NULL, &wdt_ti_rti_data_##i, \ From 088863c099712d42d654eb392096db6094d9c94a Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Wed, 3 Dec 2025 10:17:24 +0530 Subject: [PATCH 015/143] UPSTREAM: board: ti: am243x: Add watchdog support commit da0d8572d52289811dfc78a4ed6da511cdcb1642 upstream. - Change the dtsi to include only r5f0_0 - Add main_rti0 and main_rti1 for a53 cores - Add main_rti8 through main_rti11 for respective r5 cores - Use main_rti8 for the r5f0_0 core - Add mcu_rti0 for the m4 core Signed-off-by: Sunil Hegde --- boards/ti/am243x_evm/am243x_evm_am2434_m4.dts | 5 ++ .../am243x_evm/am243x_evm_am2434_r5f0_0.dts | 7 ++- dts/arm/ti/am64x_m4.dtsi | 5 ++ dts/arm/ti/am64x_r5f0_0.dtsi | 5 ++ dts/arm/ti/am64x_r5f0_1.dtsi | 5 ++ dts/arm/ti/am64x_r5f1_0.dtsi | 5 ++ dts/arm/ti/am64x_r5f1_1.dtsi | 5 ++ dts/vendor/ti/am64x_main.dtsi | 48 +++++++++++++++++++ dts/vendor/ti/am64x_mcu.dtsi | 8 ++++ 9 files changed, 92 insertions(+), 1 deletion(-) diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_m4.dts b/boards/ti/am243x_evm/am243x_evm_am2434_m4.dts index 4d1357183747..9f18b3804b14 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_m4.dts +++ b/boards/ti/am243x_evm/am243x_evm_am2434_m4.dts @@ -24,6 +24,7 @@ aliases { led0 = &ld26; + watchdog0 = &mcu_rti0; }; cpus { @@ -77,3 +78,7 @@ usr-id = <3>; status = "okay"; }; + +&mcu_rti0 { + status = "okay"; +}; diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts index 7085cbc3c9cb..6993e8208a11 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts @@ -6,7 +6,7 @@ /dts-v1/; #include -#include +#include #include "am243x_evm_am2434_r5f0_0-pinctrl.dtsi" / { @@ -25,6 +25,7 @@ aliases { led0 = &ld26; adc0 = &main_adc0; + watchdog0 = &main_rti8; }; leds: leds { @@ -199,6 +200,10 @@ }; }; +&main_rti8 { + status = "okay"; +}; + &main_mbox6 { usr-id = <0>; status = "okay"; diff --git a/dts/arm/ti/am64x_m4.dtsi b/dts/arm/ti/am64x_m4.dtsi index 29ed4b98528b..b98e4d45193b 100644 --- a/dts/arm/ti/am64x_m4.dtsi +++ b/dts/arm/ti/am64x_m4.dtsi @@ -77,3 +77,8 @@ interrupts = <57 4>; interrupt-parent = <&nvic>; }; + +&mcu_rti0 { + interrupts = <19 4>; + interrupt-parent = <&nvic>; +}; diff --git a/dts/arm/ti/am64x_r5f0_0.dtsi b/dts/arm/ti/am64x_r5f0_0.dtsi index 0a2f943748d6..58c8c00715a2 100644 --- a/dts/arm/ti/am64x_r5f0_0.dtsi +++ b/dts/arm/ti/am64x_r5f0_0.dtsi @@ -10,3 +10,8 @@ usr-id = <0>; status = "okay"; }; + +&main_rti8 { + interrupts = <0 0 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; diff --git a/dts/arm/ti/am64x_r5f0_1.dtsi b/dts/arm/ti/am64x_r5f0_1.dtsi index 427adad04cf3..1af8efbfa1ef 100644 --- a/dts/arm/ti/am64x_r5f0_1.dtsi +++ b/dts/arm/ti/am64x_r5f0_1.dtsi @@ -10,3 +10,8 @@ usr-id = <1>; status = "okay"; }; + +&main_rti9 { + interrupts = <0 0 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; diff --git a/dts/arm/ti/am64x_r5f1_0.dtsi b/dts/arm/ti/am64x_r5f1_0.dtsi index cf9ab7896918..22a3d991881a 100644 --- a/dts/arm/ti/am64x_r5f1_0.dtsi +++ b/dts/arm/ti/am64x_r5f1_0.dtsi @@ -10,3 +10,8 @@ usr-id = <0>; status = "okay"; }; + +&main_rti10 { + interrupts = <0 0 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; diff --git a/dts/arm/ti/am64x_r5f1_1.dtsi b/dts/arm/ti/am64x_r5f1_1.dtsi index 92022c357247..a380eae0f8d4 100644 --- a/dts/arm/ti/am64x_r5f1_1.dtsi +++ b/dts/arm/ti/am64x_r5f1_1.dtsi @@ -10,3 +10,8 @@ usr-id = <1>; status = "okay"; }; + +&main_rti11 { + interrupts = <0 0 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; diff --git a/dts/vendor/ti/am64x_main.dtsi b/dts/vendor/ti/am64x_main.dtsi index b77e13a8bdd4..95adba3b4106 100644 --- a/dts/vendor/ti/am64x_main.dtsi +++ b/dts/vendor/ti/am64x_main.dtsi @@ -272,6 +272,54 @@ #size-cells = <0>; }; + main_rti0: watchdog@e000000 { + compatible = "ti,j7-rti-wdt"; + reg = <0xe000000 0x100>; + clock-frequency = ; + power-domains = <&rti0_pd>; + status = "disabled"; + }; + + main_rti1: watchdog@e010000 { + compatible = "ti,j7-rti-wdt"; + reg = <0xe010000 0x100>; + clock-frequency = ; + power-domains = <&rti1_pd>; + status = "disabled"; + }; + + main_rti8: watchdog@e080000 { + compatible = "ti,j7-rti-wdt"; + reg = <0xe080000 0x100>; + clock-frequency = ; + power-domains = <&rti8_pd>; + status = "disabled"; + }; + + main_rti9: watchdog@e090000 { + compatible = "ti,j7-rti-wdt"; + reg = <0xe090000 0x100>; + clock-frequency = ; + power-domains = <&rti9_pd>; + status = "disabled"; + }; + + main_rti10: watchdog@e0a0000 { + compatible = "ti,j7-rti-wdt"; + reg = <0xe0a0000 0x100>; + clock-frequency = ; + power-domains = <&rti10_pd>; + status = "disabled"; + }; + + main_rti11: watchdog@e0b0000 { + compatible = "ti,j7-rti-wdt"; + reg = <0xe0b0000 0x100>; + clock-frequency = ; + power-domains = <&rti11_pd>; + status = "disabled"; + }; + /* users: r5f0_0, r5f0_1, r5f1_0, r5f1_1 */ main_mbox0: mailbox@29000000 { compatible = "ti,omap-mailbox"; diff --git a/dts/vendor/ti/am64x_mcu.dtsi b/dts/vendor/ti/am64x_mcu.dtsi index 5e3b14a6cb25..5b770bd0f2cc 100644 --- a/dts/vendor/ti/am64x_mcu.dtsi +++ b/dts/vendor/ti/am64x_mcu.dtsi @@ -81,4 +81,12 @@ #size-cells = <0>; status = "disabled"; }; + + mcu_rti0: watchdog@4880000 { + compatible = "ti,j7-rti-wdt"; + reg = <0x4880000 0x100>; + clock-frequency = ; + power-domains = <&mcu_rti0_pd>; + status = "disabled"; + }; }; From 8120596b9df3e55523b4198b88506f40ee1ae35c Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Thu, 23 Oct 2025 17:18:23 +0800 Subject: [PATCH 016/143] UPSTREAM: drivers: firmware: scmi: add reset vector interface commit 28b98c9ae85fec4cb3eeea500ff150928266be72 upstream. Add scmi_cpu_reset_vector API for NXP SCMI CPU domain protocol. This API allows setting CPU reset vector addresses using the CPU_RESET_VECTOR_SET command. The vector address must point to the initial address of the vector table Supports 64-bit addresses and boot/start/resume vector flags. Signed-off-by: Yongxu Wang --- drivers/firmware/scmi/nxp/cpu.c | 32 +++++++++++++++++++ .../zephyr/drivers/firmware/scmi/nxp/cpu.h | 31 ++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/drivers/firmware/scmi/nxp/cpu.c b/drivers/firmware/scmi/nxp/cpu.c index babd201a4465..fd93af0afe6c 100644 --- a/drivers/firmware/scmi/nxp/cpu.c +++ b/drivers/firmware/scmi/nxp/cpu.c @@ -114,3 +114,35 @@ int scmi_cpu_set_irq_mask(struct scmi_cpu_irq_mask_config *cfg) return scmi_status_to_errno(status); } + +int scmi_cpu_reset_vector(struct scmi_cpu_vector_config *cfg) +{ + struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_CPU_DOMAIN); + struct scmi_message msg, reply; + int status, ret; + + /* sanity checks */ + if (!proto || !cfg) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_CPU_DOMAIN) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_CPU_DOMAIN_MSG_CPU_RESET_VECTOR_SET, SCMI_COMMAND, + proto->id, 0x0); + msg.len = sizeof(*cfg); + msg.content = cfg; + + reply.hdr = msg.hdr; + reply.len = sizeof(status); + reply.content = &status; + + ret = scmi_send_message(proto, &msg, &reply, true); + if (ret < 0) { + return ret; + } + + return scmi_status_to_errno(status); +} diff --git a/include/zephyr/drivers/firmware/scmi/nxp/cpu.h b/include/zephyr/drivers/firmware/scmi/nxp/cpu.h index 7d7f742a6aaf..d9cbcd56b521 100644 --- a/include/zephyr/drivers/firmware/scmi/nxp/cpu.h +++ b/include/zephyr/drivers/firmware/scmi/nxp/cpu.h @@ -25,6 +25,15 @@ #define SCMI_CPU_IRQ_WAKE_NUM 22U +/** CPU vector flag: Boot address (cold boot/reset) */ +#define SCMI_CPU_VEC_FLAGS_BOOT BIT(29) + +/** CPU vector flag: Start address (warm start) */ +#define SCMI_CPU_VEC_FLAGS_START BIT(30) + +/** CPU vector flag: Resume address (exit from suspend) */ +#define SCMI_CPU_VEC_FLAGS_RESUME BIT(31) + /** * @struct scmi_cpu_sleep_mode_config * @@ -66,6 +75,18 @@ struct scmi_cpu_irq_mask_config { uint32_t mask[SCMI_CPU_IRQ_WAKE_NUM]; }; +/** + * @struct scmi_cpu_vector_config + * + * @brief Describes the parameters for the CPU_RESET_VECTOR_SET command + */ +struct scmi_cpu_vector_config { + uint32_t cpu_id; + uint32_t flags; + uint32_t vector_low; + uint32_t vector_high; +}; + /** * @brief CPU domain protocol command message IDs */ @@ -117,4 +138,14 @@ int scmi_cpu_pd_lpm_set(struct scmi_cpu_pd_lpm_config *cfg); * @retval negative errno if failure */ int scmi_cpu_set_irq_mask(struct scmi_cpu_irq_mask_config *cfg); + +/** + * @brief Send the CPU_RESET_VECTOR_SET command and get its reply + * + * @param cfg pointer to structure containing configuration to be set + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_cpu_reset_vector(struct scmi_cpu_vector_config *cfg); #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_CPU_H_ */ From df25bb0e9935d2b94b326719d0722a4f5a160ce8 Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Thu, 23 Oct 2025 17:25:14 +0800 Subject: [PATCH 017/143] UPSTREAM: drivers: firmware: scmi: add cpu info get interface commit dfe2c262c810f8c7578668dd8e2ba1fd761b6670 upstream. Add scmi_cpu_info_get API to query CPU information via NXP SCMI CPU domain protocol. Retrieves run mode, sleep mode, and reset vector addresses for the specified CPU ID using CPU_INFO_GET command. Signed-off-by: Yongxu Wang --- drivers/firmware/scmi/nxp/cpu.c | 40 +++++++++++++++++++ .../zephyr/drivers/firmware/scmi/nxp/cpu.h | 23 +++++++++++ 2 files changed, 63 insertions(+) diff --git a/drivers/firmware/scmi/nxp/cpu.c b/drivers/firmware/scmi/nxp/cpu.c index fd93af0afe6c..1a46c541713e 100644 --- a/drivers/firmware/scmi/nxp/cpu.c +++ b/drivers/firmware/scmi/nxp/cpu.c @@ -10,6 +10,11 @@ DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, nxp_scmi_cpu), NULL); +struct scmi_cpu_info_get_reply { + int32_t status; + struct scmi_cpu_info data; +}; + int scmi_cpu_sleep_mode_set(struct scmi_cpu_sleep_mode_config *cfg) { struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_CPU_DOMAIN); @@ -146,3 +151,38 @@ int scmi_cpu_reset_vector(struct scmi_cpu_vector_config *cfg) return scmi_status_to_errno(status); } + +int scmi_cpu_info_get(uint32_t cpu_id, struct scmi_cpu_info *cfg) +{ + struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_CPU_DOMAIN); + struct scmi_message msg, reply; + struct scmi_cpu_info_get_reply reply_buffer; + int ret; + + /* sanity checks */ + if (!proto || !cfg) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_CPU_DOMAIN) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_CPU_DOMAIN_MSG_CPU_INFO_GET, SCMI_COMMAND, + proto->id, 0x0); + msg.len = sizeof(uint32_t); + msg.content = &cpu_id; + + reply.hdr = msg.hdr; + reply.len = sizeof(reply_buffer); + reply.content = &reply_buffer; + + ret = scmi_send_message(proto, &msg, &reply, true); + if (ret < 0) { + return ret; + } + + *cfg = reply_buffer.data; + + return scmi_status_to_errno(reply_buffer.status); +} diff --git a/include/zephyr/drivers/firmware/scmi/nxp/cpu.h b/include/zephyr/drivers/firmware/scmi/nxp/cpu.h index d9cbcd56b521..37bf63bc6a36 100644 --- a/include/zephyr/drivers/firmware/scmi/nxp/cpu.h +++ b/include/zephyr/drivers/firmware/scmi/nxp/cpu.h @@ -87,6 +87,18 @@ struct scmi_cpu_vector_config { uint32_t vector_high; }; +/** + * @struct scmi_cpu_info + * + * @brief Describes the parameters for the CPU_INFO_GET command + */ +struct scmi_cpu_info { + uint32_t run_mode; + uint32_t sleep_mode; + uint32_t vector_low; + uint32_t vector_high; +}; + /** * @brief CPU domain protocol command message IDs */ @@ -148,4 +160,15 @@ int scmi_cpu_set_irq_mask(struct scmi_cpu_irq_mask_config *cfg); * @retval negative errno if failure */ int scmi_cpu_reset_vector(struct scmi_cpu_vector_config *cfg); + +/** + * @brief Send the CPU_INFO_GET command and get its reply + * + * @param cpu_id CPU ID to query (input) + * @param cfg pointer to structure to receive CPU information (output) + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_cpu_info_get(uint32_t cpu_id, struct scmi_cpu_info *cfg); #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_CPU_H_ */ From b91260733116fc6bbf32247bea26a87fa95e97a1 Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Thu, 11 Sep 2025 16:29:26 +0800 Subject: [PATCH 018/143] UPSTREAM: drivers: firmware: scmi: define generic power... commit c77b4ba46f2e8289001b8a6819c2eb0e37bebf00 upstream. Add SCMI generic power state definitions for ON and OFF as specified by the SCMI Power Domain protocol. These constants will be used when setting or querying power states. Signed-off-by: Yongxu Wang --- include/zephyr/drivers/firmware/scmi/power.h | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/include/zephyr/drivers/firmware/scmi/power.h b/include/zephyr/drivers/firmware/scmi/power.h index 649209c74690..dcf73ef13f78 100644 --- a/include/zephyr/drivers/firmware/scmi/power.h +++ b/include/zephyr/drivers/firmware/scmi/power.h @@ -16,6 +16,42 @@ #define SCMI_POWER_STATE_SET_FLAGS_ASYNC BIT(0) +/** + * @name SCMI power domain state parameters + * @{ + */ + +/** Power state type field bit shift */ +#define SCMI_POWER_STATE_TYPE_SHIFT 30U + +/** Power state ID field mask */ +#define SCMI_POWER_STATE_ID_MASK (BIT(28) - 1) + +/** + * @brief Construct SCMI power state parameter + * + * @param type Power state type + * @param id Power state ID + */ +#define SCMI_POWER_STATE_PARAM(type, id) \ + ((((type) & BIT(0)) << SCMI_POWER_STATE_TYPE_SHIFT) | \ + ((id) & SCMI_POWER_STATE_ID_MASK)) + +/** @} */ + +/** + * @name SCMI power domain generic states + * @{ + */ + +/** Power domain is in ON state */ +#define SCMI_POWER_STATE_GENERIC_ON SCMI_POWER_STATE_PARAM(0, 0) + +/** Power domain is in OFF state */ +#define SCMI_POWER_STATE_GENERIC_OFF SCMI_POWER_STATE_PARAM(1, 0) + +/** @} */ + /** * @struct scmi_power_state_config * From 58410f691125e8d1a969f7d31f3457de0f2a4a3f Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Fri, 14 Nov 2025 15:05:47 +0800 Subject: [PATCH 019/143] UPSTREAM: drivers: firmware: scmi: add common protocol interface functions commit d0e9d950fb9bb27091d08816f9e189646be5ad90 upstream. Add standardized SCMI protocol common interface functions that can be reused across different SCMI protocol implementations: - scmi_protocol_get_version(): Query protocol version - scmi_protocol_attributes_get(): Get protocol-specific attributes - scmi_protocol_message_attributes_get(): Query message capabilities - scmi_protocol_version_negotiate(): Negotiate protocol version support Signed-off-by: Yongxu Wang --- drivers/firmware/scmi/CMakeLists.txt | 2 +- drivers/firmware/scmi/common.c | 162 ++++++++++++++++++ .../zephyr/drivers/firmware/scmi/protocol.h | 56 ++++++ 3 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/scmi/common.c diff --git a/drivers/firmware/scmi/CMakeLists.txt b/drivers/firmware/scmi/CMakeLists.txt index 60ea86a5cb69..9108f9b41745 100644 --- a/drivers/firmware/scmi/CMakeLists.txt +++ b/drivers/firmware/scmi/CMakeLists.txt @@ -3,7 +3,7 @@ zephyr_library() # SCMI core files -zephyr_library_sources_ifdef(CONFIG_ARM_SCMI core.c) +zephyr_library_sources_ifdef(CONFIG_ARM_SCMI core.c common.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_MAILBOX_TRANSPORT mailbox.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SHMEM shmem.c) diff --git a/drivers/firmware/scmi/common.c b/drivers/firmware/scmi/common.c new file mode 100644 index 000000000000..d2e768c69c7a --- /dev/null +++ b/drivers/firmware/scmi/common.c @@ -0,0 +1,162 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief SCMI Common Protocol Commands Implementation + * + * This file contains the implementation of the SCMI commands that are + * common to all protocols (generic or vendor-specific) as listed in + * ARM SCMI Specification such as v4.0 (DEN0056F), Section 3.2.2 "Base protocol Commands". + * + * The following common commands are implemented: + * - PROTOCOL_VERSION (0x0): Query protocol version + * - PROTOCOL_ATTRIBUTES (0x1): Get protocol-specific attributes + * - MESSAGE_ATTRIBUTES (0x2): Query message capabilities + * - NEGOTIATE_PROTOCOL_VERSION (0x10): Negotiate protocol version support + * + * These commands provide standardized interfaces that can be reused across + * different SCMI protocol implementations, ensuring consistency and reducing + * code duplication. + * + * Reference: ARM System Control and Management Interface Platform Design Document + * Version 4.0, Document number: DEN0056F + * Available at: https://developer.arm.com/documentation/den0056/latest + */ + +#include +#include +#include + +struct scmi_protocol_version_reply { + int32_t status; + uint32_t version; +}; + +struct scmi_protocol_attributes_reply { + int32_t status; + uint32_t attributes; +}; + +struct scmi_protocol_message_attributes_reply { + int32_t status; + uint32_t attributes; +}; + +int scmi_protocol_get_version(struct scmi_protocol *proto, uint32_t *version) +{ + struct scmi_protocol_version_reply reply_buffer; + struct scmi_message msg, reply; + int ret; + + if (!proto || !version) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_MSG_PROTOCOL_VERSION, SCMI_COMMAND, + proto->id, 0x0); + msg.len = 0; + msg.content = NULL; + + reply.hdr = msg.hdr; + reply.len = sizeof(reply_buffer); + reply.content = &reply_buffer; + + ret = scmi_send_message(proto, &msg, &reply, k_is_pre_kernel()); + if (ret < 0) { + return ret; + } + + *version = reply_buffer.version; + + return scmi_status_to_errno(reply_buffer.status); +} + +int scmi_protocol_attributes_get(struct scmi_protocol *proto, uint32_t *attributes) +{ + struct scmi_protocol_attributes_reply reply_buffer; + struct scmi_message msg, reply; + int ret; + + if (!proto || !attributes) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_MSG_PROTOCOL_ATTRIBUTES, SCMI_COMMAND, + proto->id, 0x0); + msg.len = 0; + msg.content = NULL; + + reply.hdr = msg.hdr; + reply.len = sizeof(reply_buffer); + reply.content = &reply_buffer; + + ret = scmi_send_message(proto, &msg, &reply, k_is_pre_kernel()); + if (ret < 0) { + return ret; + } + + *attributes = reply_buffer.attributes; + + return scmi_status_to_errno(reply_buffer.status); +} + +int scmi_protocol_message_attributes_get(struct scmi_protocol *proto, + uint32_t message_id, uint32_t *attributes) +{ + struct scmi_protocol_message_attributes_reply reply_buffer; + struct scmi_message msg, reply; + int ret; + + if (!proto || !attributes) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_MSG_MESSAGE_ATTRIBUTES, SCMI_COMMAND, + proto->id, 0x0); + msg.len = sizeof(message_id); + msg.content = &message_id; + + reply.hdr = msg.hdr; + reply.len = sizeof(reply_buffer); + reply.content = &reply_buffer; + + ret = scmi_send_message(proto, &msg, &reply, k_is_pre_kernel()); + if (ret < 0) { + return ret; + } + + *attributes = reply_buffer.attributes; + + return scmi_status_to_errno(reply_buffer.status); +} + +int scmi_protocol_version_negotiate(struct scmi_protocol *proto, uint32_t version) +{ + struct scmi_message msg, reply; + int32_t status; + int ret; + + if (!proto) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_MSG_NEGOTIATE_PROTOCOL_VERSION, SCMI_COMMAND, + proto->id, 0x0); + msg.len = sizeof(version); + msg.content = &version; + + reply.hdr = msg.hdr; + reply.len = sizeof(status); + reply.content = &status; + + ret = scmi_send_message(proto, &msg, &reply, k_is_pre_kernel()); + if (ret < 0) { + return ret; + } + + return scmi_status_to_errno(status); +} diff --git a/include/zephyr/drivers/firmware/scmi/protocol.h b/include/zephyr/drivers/firmware/scmi/protocol.h index 920aaf5c435d..fd530961a410 100644 --- a/include/zephyr/drivers/firmware/scmi/protocol.h +++ b/include/zephyr/drivers/firmware/scmi/protocol.h @@ -66,6 +66,16 @@ enum scmi_status_code { SCMI_IN_USE = -11, }; +/** + * @brief SCMI common command + */ +enum scmi_common_cmd { + SCMI_MSG_PROTOCOL_VERSION = 0x0, + SCMI_MSG_PROTOCOL_ATTRIBUTES = 0x1, + SCMI_MSG_MESSAGE_ATTRIBUTES = 0x2, + SCMI_MSG_NEGOTIATE_PROTOCOL_VERSION = 0x10, +}; + /** * @struct scmi_protocol * @@ -126,4 +136,50 @@ int scmi_send_message(struct scmi_protocol *proto, struct scmi_message *msg, struct scmi_message *reply, bool use_polling); +/** + * @brief Get protocol version + * + * @param proto Protocol instance + * @param version Pointer to store protocol version + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_protocol_get_version(struct scmi_protocol *proto, uint32_t *version); + +/** + * @brief Get protocol attributes + * + * @param proto Protocol instance + * @param attributes Pointer to store protocol attributes + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_protocol_attributes_get(struct scmi_protocol *proto, uint32_t *attributes); + +/** + * @brief Get protocol message attributes + * + * @param proto Protocol instance + * @param message_id Message ID to query + * @param attributes Pointer to store message attributes + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_protocol_message_attributes_get(struct scmi_protocol *proto, + uint32_t message_id, uint32_t *attributes); + +/** + * @brief Negotiate protocol version + * + * @param proto Protocol instance + * @param version Desired protocol version + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_protocol_version_negotiate(struct scmi_protocol *proto, uint32_t version); + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_ */ From 8249bf7f94acbdd2d7df9d3e9a183ffacb81b51c Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Thu, 20 Nov 2025 13:02:01 +0800 Subject: [PATCH 020/143] UPSTREAM: drivers: firmware: scmi: add protocol version support commit 9d163c73fb894b1f6624afe64266b0f17a232a34 upstream. Add version field to struct scmi_protocol and update protocol macros to accept version parameter. Protocol versions added: - Clock Control: v3.0.0 (SCMI v3.2 DEN0056E) - Pin Control: v1.0.0 (SCMI v3.2 DEN0056E) - Power Domain: v3.0.1 (SCMI v3.2 DEN0056E) - NXP CPU: v1.0.0 (vendor extension) Updated macros: - DT_SCMI_PROTOCOL_DATA_DEFINE - DT_SCMI_PROTOCOL_DEFINE - DT_SCMI_PROTOCOL_DEFINE_NODEV - DT_INST_SCMI_PROTOCOL_DEFINE Signed-off-by: Yongxu Wang --- drivers/clock_control/clock_control_arm_scmi.c | 2 +- drivers/firmware/scmi/nxp/cpu.c | 3 ++- drivers/firmware/scmi/pinctrl.c | 3 ++- drivers/firmware/scmi/power.c | 3 ++- include/zephyr/drivers/firmware/scmi/clk.h | 2 ++ include/zephyr/drivers/firmware/scmi/nxp/cpu.h | 2 ++ include/zephyr/drivers/firmware/scmi/pinctrl.h | 2 ++ include/zephyr/drivers/firmware/scmi/power.h | 2 ++ include/zephyr/drivers/firmware/scmi/protocol.h | 2 ++ include/zephyr/drivers/firmware/scmi/util.h | 17 ++++++++++------- 10 files changed, 27 insertions(+), 11 deletions(-) diff --git a/drivers/clock_control/clock_control_arm_scmi.c b/drivers/clock_control/clock_control_arm_scmi.c index f48d91dae620..c6780882c358 100644 --- a/drivers/clock_control/clock_control_arm_scmi.c +++ b/drivers/clock_control/clock_control_arm_scmi.c @@ -121,4 +121,4 @@ static struct scmi_clock_data data; DT_INST_SCMI_PROTOCOL_DEFINE(0, &scmi_clock_init, NULL, &data, NULL, PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, - &scmi_clock_api); + &scmi_clock_api, SCMI_CLK_PROTOCOL_SUPPORTED_VERSION); diff --git a/drivers/firmware/scmi/nxp/cpu.c b/drivers/firmware/scmi/nxp/cpu.c index 1a46c541713e..a1b0021814b6 100644 --- a/drivers/firmware/scmi/nxp/cpu.c +++ b/drivers/firmware/scmi/nxp/cpu.c @@ -8,7 +8,8 @@ #include #include -DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, nxp_scmi_cpu), NULL); +DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, nxp_scmi_cpu), NULL, + SCMI_NXP_CPU_PROTOCOL_SUPPORTED_VERSION); struct scmi_cpu_info_get_reply { int32_t status; diff --git a/drivers/firmware/scmi/pinctrl.c b/drivers/firmware/scmi/pinctrl.c index 73ed000daa0e..abfcae844a67 100644 --- a/drivers/firmware/scmi/pinctrl.c +++ b/drivers/firmware/scmi/pinctrl.c @@ -7,7 +7,8 @@ #include #include -DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, arm_scmi_pinctrl), NULL); +DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, arm_scmi_pinctrl), NULL, + SCMI_PIN_CONTROL_PROTOCOL_SUPPORTED_VERSION); int scmi_pinctrl_settings_configure(struct scmi_pinctrl_settings *settings) { diff --git a/drivers/firmware/scmi/power.c b/drivers/firmware/scmi/power.c index b560ce484571..6396029071be 100644 --- a/drivers/firmware/scmi/power.c +++ b/drivers/firmware/scmi/power.c @@ -8,7 +8,8 @@ #include #include -DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, arm_scmi_power), NULL); +DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, arm_scmi_power), NULL, + SCMI_POWER_DOMAIN_PROTOCOL_SUPPORTED_VERSION); struct scmi_power_state_get_reply { int32_t status; diff --git a/include/zephyr/drivers/firmware/scmi/clk.h b/include/zephyr/drivers/firmware/scmi/clk.h index d13cfd136794..c3b511c4f517 100644 --- a/include/zephyr/drivers/firmware/scmi/clk.h +++ b/include/zephyr/drivers/firmware/scmi/clk.h @@ -25,6 +25,8 @@ #define SCMI_CLK_RATE_SET_FLAGS_ROUNDS_UP_DOWN BIT(2) #define SCMI_CLK_RATE_SET_FLAGS_ROUNDS_AUTO BIT(3) +#define SCMI_CLK_PROTOCOL_SUPPORTED_VERSION 0x30000 + /** * @struct scmi_clock_config * diff --git a/include/zephyr/drivers/firmware/scmi/nxp/cpu.h b/include/zephyr/drivers/firmware/scmi/nxp/cpu.h index 37bf63bc6a36..34711e8a175b 100644 --- a/include/zephyr/drivers/firmware/scmi/nxp/cpu.h +++ b/include/zephyr/drivers/firmware/scmi/nxp/cpu.h @@ -34,6 +34,8 @@ /** CPU vector flag: Resume address (exit from suspend) */ #define SCMI_CPU_VEC_FLAGS_RESUME BIT(31) +#define SCMI_NXP_CPU_PROTOCOL_SUPPORTED_VERSION 0x10000 + /** * @struct scmi_cpu_sleep_mode_config * diff --git a/include/zephyr/drivers/firmware/scmi/pinctrl.h b/include/zephyr/drivers/firmware/scmi/pinctrl.h index 3906b91db681..cb6a39a765e2 100644 --- a/include/zephyr/drivers/firmware/scmi/pinctrl.h +++ b/include/zephyr/drivers/firmware/scmi/pinctrl.h @@ -29,6 +29,8 @@ #define SCMI_PINCTRL_ATTRIBUTES_CONFIG_NUM(attributes)\ (((attributes) & GENMASK(9, 2)) >> 2) +#define SCMI_PIN_CONTROL_PROTOCOL_SUPPORTED_VERSION 0x10000 + /** * @brief Pinctrl protocol command message IDs */ diff --git a/include/zephyr/drivers/firmware/scmi/power.h b/include/zephyr/drivers/firmware/scmi/power.h index dcf73ef13f78..7545da9ef968 100644 --- a/include/zephyr/drivers/firmware/scmi/power.h +++ b/include/zephyr/drivers/firmware/scmi/power.h @@ -16,6 +16,8 @@ #define SCMI_POWER_STATE_SET_FLAGS_ASYNC BIT(0) +#define SCMI_POWER_DOMAIN_PROTOCOL_SUPPORTED_VERSION 0x30001 + /** * @name SCMI power domain state parameters * @{ diff --git a/include/zephyr/drivers/firmware/scmi/protocol.h b/include/zephyr/drivers/firmware/scmi/protocol.h index fd530961a410..a03d8cf493f8 100644 --- a/include/zephyr/drivers/firmware/scmi/protocol.h +++ b/include/zephyr/drivers/firmware/scmi/protocol.h @@ -90,6 +90,8 @@ struct scmi_protocol { const struct device *transport; /** protocol private data */ void *data; + /** protocol supported version */ + uint32_t version; }; /** diff --git a/include/zephyr/drivers/firmware/scmi/util.h b/include/zephyr/drivers/firmware/scmi/util.h index cd05f984ac20..55bceeaaf904 100644 --- a/include/zephyr/drivers/firmware/scmi/util.h +++ b/include/zephyr/drivers/firmware/scmi/util.h @@ -146,12 +146,13 @@ * @param proto protocol ID in decimal format * @param pdata protocol private data */ -#define DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, proto, pdata) \ +#define DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, proto, pdata, version_val) \ STRUCT_SECTION_ITERABLE(scmi_protocol, SCMI_PROTOCOL_NAME(proto)) = \ { \ .id = proto, \ .tx = DT_SCMI_TRANSPORT_TX_CHAN(node_id), \ .data = pdata, \ + .version = version_val \ } #else /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */ @@ -209,9 +210,10 @@ * @param prio protocol's priority within its initialization level */ #define DT_SCMI_PROTOCOL_DEFINE(node_id, init_fn, pm, data, config, \ - level, prio, api) \ + level, prio, api, version_val) \ DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \ - DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR_RAW(node_id), data); \ + DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR_RAW(node_id), data, \ + version_val); \ DEVICE_DT_DEFINE(node_id, init_fn, pm, \ &SCMI_PROTOCOL_NAME(DT_REG_ADDR_RAW(node_id)), \ config, level, prio, api) @@ -230,9 +232,9 @@ * @param prio protocol's priority within its initialization level */ #define DT_INST_SCMI_PROTOCOL_DEFINE(inst, init_fn, pm, data, config, \ - level, prio, api) \ + level, prio, api, version) \ DT_SCMI_PROTOCOL_DEFINE(DT_INST(inst, DT_DRV_COMPAT), init_fn, pm, \ - data, config, level, prio, api) + data, config, level, prio, api, version) /** * @brief Define an SCMI protocol with no device @@ -245,9 +247,10 @@ * @param node_id protocol node identifier * @param data protocol private data */ -#define DT_SCMI_PROTOCOL_DEFINE_NODEV(node_id, data) \ +#define DT_SCMI_PROTOCOL_DEFINE_NODEV(node_id, data, version) \ DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \ - DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR_RAW(node_id), data) + DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR_RAW(node_id), data, \ + version) \ /** * @brief Create an SCMI message field From 5d07d88c84e3bfb0e11ce20409264adede2025fd Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Thu, 20 Nov 2025 13:03:14 +0800 Subject: [PATCH 021/143] UPSTREAM: drivers: firmware: scmi: add protocol version negotiate commit ada584012b5ff410add1c59f0dd40c123140e624 upstream. Add SCMI protocol version negotiation to handle compatibility between different agent and platform versions. The negotiation automatically downgrades to supported version when needed and maintains backward compatibility with legacy systems. Signed-off-by: Yongxu Wang --- drivers/firmware/scmi/core.c | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/firmware/scmi/core.c b/drivers/firmware/scmi/core.c index 378b654c5ec6..547ee92f43e5 100644 --- a/drivers/firmware/scmi/core.c +++ b/drivers/firmware/scmi/core.c @@ -215,6 +215,45 @@ int scmi_send_message(struct scmi_protocol *proto, struct scmi_message *msg, } } +static int scmi_core_protocol_negotiate(struct scmi_protocol *proto) + +{ + uint32_t agent_version, platform_version; + int ret; + + if (!proto) { + return -EINVAL; + } + + agent_version = proto->version; + + if (!agent_version) { + LOG_ERR("Protocol 0x%X: Agent version not specified", proto->id); + return -EINVAL; + } + + ret = scmi_protocol_get_version(proto, &platform_version); + if (ret < 0) { + LOG_ERR("Protocol 0x%X: Failed to get platform version: %d", + proto->id, ret); + return ret; + } + + if (platform_version > agent_version) { + ret = scmi_protocol_version_negotiate(proto, agent_version); + if (ret < 0) { + LOG_WRN("Protocol 0x%X: Negotiation failed (%d). " + "Platform v0x%08x does not support downgrade to agent v0x%08x", + proto->id, ret, platform_version, agent_version); + } + } + + LOG_INF("Using protocol 0x%X: agent version 0x%08x, platform version 0x%08x", + proto->id, agent_version, platform_version); + + return 0; +} + static int scmi_core_protocol_setup(const struct device *transport) { int ret; @@ -235,6 +274,12 @@ static int scmi_core_protocol_setup(const struct device *transport) if (ret < 0) { return ret; } + + ret = scmi_core_protocol_negotiate(it); + if (ret < 0) { + return ret; + } + } return 0; From 70966d6494cf3fac398b9827e4e50e2a00d96e9d Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Wed, 5 Nov 2025 15:09:51 +0800 Subject: [PATCH 022/143] UPSTREAM: drivers: firmware: scmi: Introduce basic system power protocol commit cc069c02010b76cdb0d514f1c63d9468b2333f2d upstream. Add SCMI System Power Domain Protocol support with: - System power state set operations - Standard SCMI power states (shutdown, cold/warm reset, suspend) - Graceful and forceful power transitions - Optional NXP vendor-specific power states - Device tree binding for "arm,scmi-system" This enables system-wide power management through standardized SCMI interfaces for reboot and power control operations. Signed-off-by: Yongxu Wang --- drivers/firmware/scmi/CMakeLists.txt | 1 + drivers/firmware/scmi/Kconfig | 7 + drivers/firmware/scmi/system.c | 76 +++++++++ dts/bindings/firmware/arm,scmi-system.yaml | 13 ++ .../zephyr/drivers/firmware/scmi/nxp/system.h | 39 +++++ include/zephyr/drivers/firmware/scmi/system.h | 153 ++++++++++++++++++ 6 files changed, 289 insertions(+) create mode 100644 drivers/firmware/scmi/system.c create mode 100644 dts/bindings/firmware/arm,scmi-system.yaml create mode 100644 include/zephyr/drivers/firmware/scmi/nxp/system.h create mode 100644 include/zephyr/drivers/firmware/scmi/system.h diff --git a/drivers/firmware/scmi/CMakeLists.txt b/drivers/firmware/scmi/CMakeLists.txt index 9108f9b41745..d0e6128a97fe 100644 --- a/drivers/firmware/scmi/CMakeLists.txt +++ b/drivers/firmware/scmi/CMakeLists.txt @@ -11,5 +11,6 @@ zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SHMEM shmem.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_CLK_HELPERS clk.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_PINCTRL_HELPERS pinctrl.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_POWER_DOMAIN_HELPERS power.c) +zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SYSTEM system.c) add_subdirectory_ifdef(CONFIG_ARM_SCMI nxp) diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index 1f59ab872e67..fb35de45d2a7 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -48,6 +48,13 @@ config ARM_SCMI_SHMEM_INIT_PRIORITY help SCMI SHMEM driver device initialization priority. +config ARM_SCMI_SYSTEM + bool "SCMI system power protocol" + default y + depends on DT_HAS_ARM_SCMI_SYSTEM_ENABLED + help + Enable support for SCMI system protocol functions. + config ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS bool "Transport layer has static channels" help diff --git a/drivers/firmware/scmi/system.c b/drivers/firmware/scmi/system.c new file mode 100644 index 000000000000..d96efd27e3bb --- /dev/null +++ b/drivers/firmware/scmi/system.c @@ -0,0 +1,76 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, arm_scmi_system), NULL, + SCMI_SYSTEM_POWER_PROTOCOL_SUPPORTED_VERSION); + +int scmi_system_protocol_version(uint32_t *version) +{ + struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_SYSTEM); + + return scmi_protocol_get_version(proto, version); +} + +int scmi_system_protocol_attributes(uint32_t *attributes) +{ + struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_SYSTEM); + + return scmi_protocol_attributes_get(proto, attributes); +} + +int scmi_system_protocol_message_attributes(uint32_t message_id, uint32_t *attributes) +{ + struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_SYSTEM); + + return scmi_protocol_message_attributes_get(proto, message_id, attributes); +} + +int scmi_system_protocol_version_negotiate(uint32_t version) +{ + struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_SYSTEM); + + return scmi_protocol_version_negotiate(proto, version); +} + +int scmi_system_power_state_set(struct scmi_system_power_state_config *cfg) +{ + struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_SYSTEM); + struct scmi_message msg, reply; + int32_t status; + int ret; + bool use_polling; + + /* sanity checks */ + if (!proto || !cfg) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_SYSTEM) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_SYSTEM_MSG_POWER_STATE_SET, SCMI_COMMAND, + proto->id, 0x0); + msg.len = sizeof(*cfg); + msg.content = cfg; + + reply.hdr = msg.hdr; + reply.len = sizeof(status); + reply.content = &status; + + use_polling = k_is_pre_kernel(); + + ret = scmi_send_message(proto, &msg, &reply, use_polling); + if (ret < 0) { + return ret; + } + + return scmi_status_to_errno(status); +} diff --git a/dts/bindings/firmware/arm,scmi-system.yaml b/dts/bindings/firmware/arm,scmi-system.yaml new file mode 100644 index 000000000000..77aba5abf176 --- /dev/null +++ b/dts/bindings/firmware/arm,scmi-system.yaml @@ -0,0 +1,13 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: System Control and Management Interface (SCMI) system power protocol + +compatible: "arm,scmi-system" + +include: [base.yaml] + +properties: + reg: + required: true + const: [0x12] diff --git a/include/zephyr/drivers/firmware/scmi/nxp/system.h b/include/zephyr/drivers/firmware/scmi/nxp/system.h new file mode 100644 index 000000000000..63f3502cf7e8 --- /dev/null +++ b/include/zephyr/drivers/firmware/scmi/nxp/system.h @@ -0,0 +1,39 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_FIRMWARE_SCMI_NXP_SYSTEM_H_ +#define ZEPHYR_INCLUDE_DRIVERS_FIRMWARE_SCMI_NXP_SYSTEM_H_ + +/** + * @file + * @brief NXP SCMI System Protocol Extensions + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name NXP Vendor System Power States + * @{ + */ +#define SCMI_NXP_SYSTEM_POWER_STATE_WAKE 0x80000000U +#define SCMI_NXP_SYSTEM_POWER_STATE_FULL_SHUTDOWN 0x80000001U +#define SCMI_NXP_SYSTEM_POWER_STATE_FULL_RESET 0x80000002U +#define SCMI_NXP_SYSTEM_POWER_STATE_FULL_SUSPEND 0x80000003U +#define SCMI_NXP_SYSTEM_POWER_STATE_FULL_WAKE 0x80000004U +#define SCMI_NXP_SYSTEM_POWER_STATE_GRP_SHUTDOWN 0x80000005U +#define SCMI_NXP_SYSTEM_POWER_STATE_GRP_RESET 0x80000006U +#define SCMI_NXP_SYSTEM_POWER_STATE_SUBSYS_RESET 0x80000007U +#define SCMI_NXP_SYSTEM_POWER_STATE_MODE 0xC0000000U +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_FIRMWARE_SCMI_NXP_SYSTEM_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/system.h b/include/zephyr/drivers/firmware/scmi/system.h new file mode 100644 index 000000000000..b947d89b3607 --- /dev/null +++ b/include/zephyr/drivers/firmware/scmi/system.h @@ -0,0 +1,153 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_FIRMWARE_SCMI_SYSTEM_H_ +#define ZEPHYR_INCLUDE_DRIVERS_FIRMWARE_SCMI_SYSTEM_H_ + +/** + * @file + * @brief SCMI System Power Management Protocol + */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief System protocol command message IDs + */ +enum scmi_system_message { + SCMI_SYSTEM_MSG_PROTOCOL_VERSION = 0x0, + SCMI_SYSTEM_MSG_PROTOCOL_ATTRIBUTES = 0x1, + SCMI_SYSTEM_MSG_MESSAGE_ATTRIBUTES = 0x2, + SCMI_SYSTEM_MSG_POWER_STATE_SET = 0x3, + SCMI_SYSTEM_MSG_POWER_STATE_NOTIFY = 0x5, + SCMI_SYSTEM_MSG_NEGOTIATE_PROTOCOL_VERSION = 0x10, +}; + +#define SCMI_SYSTEM_POWER_PROTOCOL_SUPPORTED_VERSION 0x20001 + +/** + * @name Standard SCMI System Power States + * @{ + */ + +/**< Shutdown off */ +#define SCMI_SYSTEM_POWER_STATE_SHUTDOWN 0x00000000U +/**< Cold reset */ +#define SCMI_SYSTEM_POWER_STATE_COLD_RESET 0x00000001U +/**< Warm reset */ +#define SCMI_SYSTEM_POWER_STATE_WARM_RESET 0x00000002U +/**< Power up */ +#define SCMI_SYSTEM_POWER_STATE_POWER_UP 0x00000003U +/**< Suspend */ +#define SCMI_SYSTEM_POWER_STATE_SUSPEND 0x00000004U + +/** @} */ + +/** + * @name System Power State flags + * @{ + */ + +/** @cond INTERNAL_HIDDEN */ +#define SCMI_SYSTEM_POWER_FLAG_SHIFT (0) +/** @endcond */ + +/**< Forceful request */ +#define SCMI_SYSTEM_POWER_FLAG_FORCEFUL (0 << SCMI_SYSTEM_POWER_FLAG_SHIFT) +/**< Graceful request */ +#define SCMI_SYSTEM_POWER_FLAG_GRACEFUL (1 << SCMI_SYSTEM_POWER_FLAG_SHIFT) + +/** @} */ + + +/*! + * @name SCMI system message attributes + * @{ + */ + +/** @cond INTERNAL_HIDDEN */ +#define SCMI_SYSTEM_MSG_ATTR_SUSPEND_SHIFT (30U) +#define SCMI_SYSTEM_MSG_ATTR_WARM_RESET_SHIFT (31U) +/** @endcond */ + +/*! System suspend support */ +#define SCMI_SYSTEM_MSG_ATTR_SUSPEND (1 << SCMI_SYSTEM_MSG_ATTR_SUSPEND_SHIFT) +/*! System warm reset support */ +#define SCMI_SYSTEM_MSG_ATTR_WARM_RESET (1 << SCMI_SYSTEM_MSG_ATTR_WARM_RESET_SHIFT) + +/** @} */ + +/** + * @struct scmi_system_power_state_config + * @brief System power state configuration + */ +struct scmi_system_power_state_config { + uint32_t flags; + uint32_t system_state; +}; + +/** + * @brief Get protocol version + * + * @param version Protocol version + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_system_protocol_version(uint32_t *version); + +/** + * @brief Get protocol attributes + * + * @param attributes Protocol attributes + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_system_protocol_attributes(uint32_t *attributes); + +/** + * @brief Get protocol message attributes + * + * @param message_id Message ID of the message + * @param attributes Message attributes + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_system_protocol_message_attributes(uint32_t message_id, uint32_t *attributes); + +/** + * @brief Negotiate protocol version + * + * @param version desired protocol version + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_system_protocol_version_negotiate(uint32_t version); + +/** + * @brief Set system power state + * + * @param cfg pointer to power state configuration + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_system_power_state_set(struct scmi_system_power_state_config *cfg); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_FIRMWARE_SCMI_SYSTEM_H_ */ From a535d02e6748d71103864cf1154e2e1ac2f51a1c Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Mon, 17 Nov 2025 14:43:59 +0800 Subject: [PATCH 023/143] UPSTREAM: drivers: firmware: scmi: add initial reboot interface commit 2d3c40d1b368d0e60df65c73bf05fe3b1f970b85 upstream. Implement sys_arch_reboot() using SCMI system power protocol for warm and cold reset operations on SCMI-enabled platforms. Signed-off-by: Yongxu Wang --- drivers/firmware/scmi/CMakeLists.txt | 1 + drivers/firmware/scmi/Kconfig | 8 ++++ drivers/firmware/scmi/reboot.c | 59 ++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 drivers/firmware/scmi/reboot.c diff --git a/drivers/firmware/scmi/CMakeLists.txt b/drivers/firmware/scmi/CMakeLists.txt index d0e6128a97fe..1118c34090a6 100644 --- a/drivers/firmware/scmi/CMakeLists.txt +++ b/drivers/firmware/scmi/CMakeLists.txt @@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SHMEM shmem.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_CLK_HELPERS clk.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_PINCTRL_HELPERS pinctrl.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_POWER_DOMAIN_HELPERS power.c) +zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_REBOOT reboot.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SYSTEM system.c) add_subdirectory_ifdef(CONFIG_ARM_SCMI nxp) diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index fb35de45d2a7..a1fb0ae55ba9 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -35,6 +35,14 @@ config ARM_SCMI_POWER_DOMAIN_HELPERS help Enable support for SCMI power domain protocol helper functions. +config ARM_SCMI_REBOOT + bool "SCMI system reboot support" + depends on ARM_SCMI_SYSTEM && REBOOT + default y + help + Enable system reboot functionality using SCMI system power protocol. + This provides sys_arch_reboot() implementation for platforms with SCMI. + config ARM_SCMI_SHMEM bool "SCMI shared memory (SHMEM) driver" default y diff --git a/drivers/firmware/scmi/reboot.c b/drivers/firmware/scmi/reboot.c new file mode 100644 index 000000000000..d00c3ff201ae --- /dev/null +++ b/drivers/firmware/scmi/reboot.c @@ -0,0 +1,59 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(scmi_reboot, CONFIG_PM_LOG_LEVEL); + +static int scmi_reboot_handler(int type) +{ + struct scmi_system_power_state_config cfg; + int ret; + uint32_t mesg_attr; + + cfg.flags = SCMI_SYSTEM_POWER_FLAG_FORCEFUL; + + switch (type) { + case SYS_REBOOT_WARM: + ret = scmi_system_protocol_message_attributes(SCMI_SYSTEM_MSG_POWER_STATE_SET, + &mesg_attr); + if (ret < 0) { + LOG_ERR("Failed to query SCMI system capabilities: %d", ret); + return ret; + } + + if (!(mesg_attr & SCMI_SYSTEM_MSG_ATTR_WARM_RESET)) { + LOG_WRN("Warm reset not supported by platform"); + return -ENOTSUP; + } + + cfg.system_state = SCMI_SYSTEM_POWER_STATE_WARM_RESET; + break; + + case SYS_REBOOT_COLD: + cfg.system_state = SCMI_SYSTEM_POWER_STATE_COLD_RESET; + break; + + default: + LOG_ERR("Unsupported reboot type: %d", type); + return -EINVAL; + } + + ret = scmi_system_power_state_set(&cfg); + if (ret < 0) { + LOG_ERR("System reboot failed with error: %d", ret); + } + + return ret; +} + +void sys_arch_reboot(int type) +{ + scmi_reboot_handler(type); +} From 9190af5c11e4f3bd2f33e775711682d3c1016fc5 Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Thu, 20 Nov 2025 16:44:30 +0800 Subject: [PATCH 024/143] UPSTREAM: drivers: firmware: scmi: force polling mode... commit 90f2a228823659fbdc246b68e31e7428a5a9eea5 upstream. During reboot operations, interrupts are disabled but k_is_pre_kernel() returns false, causing SCMI communication to fail when using interrupt mode. Force polling mode to ensure reliable operation in all contexts. Signed-off-by: Yongxu Wang --- drivers/firmware/scmi/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/scmi/common.c b/drivers/firmware/scmi/common.c index d2e768c69c7a..11ff71b90f70 100644 --- a/drivers/firmware/scmi/common.c +++ b/drivers/firmware/scmi/common.c @@ -124,7 +124,7 @@ int scmi_protocol_message_attributes_get(struct scmi_protocol *proto, reply.len = sizeof(reply_buffer); reply.content = &reply_buffer; - ret = scmi_send_message(proto, &msg, &reply, k_is_pre_kernel()); + ret = scmi_send_message(proto, &msg, &reply, true); if (ret < 0) { return ret; } From 24120b4e3b6d9af782bf436cbf8379ddf81bb75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Fri, 12 Dec 2025 16:43:21 +0100 Subject: [PATCH 025/143] UPSTREAM: doc: hardware: scmi: fix formatting of SCMI docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b96c49d737f6bf1678edab755ccafb550a5f5a01 upstream. Fix incorrect indentation that caused some elements to render as defintion lists. Added proper nesting of the various headings as everything was top-level Signed-off-by: Benjamin Cabé --- doc/hardware/arch/arm-scmi.rst | 153 +++++++++++++++++---------------- 1 file changed, 79 insertions(+), 74 deletions(-) diff --git a/doc/hardware/arch/arm-scmi.rst b/doc/hardware/arch/arm-scmi.rst index 1055f5d6b896..6f2b92de112c 100644 --- a/doc/hardware/arch/arm-scmi.rst +++ b/doc/hardware/arch/arm-scmi.rst @@ -7,7 +7,7 @@ Overview ******** What is SCMI? -************* +============= System Control and Management Interface (SCMI) is a specification developed by ARM, which describes a set of OS-agnostic software interfaces used to perform @@ -15,52 +15,53 @@ system management (e.g: clock control, pinctrl, etc...). Agent, platform, protocol and transport -*************************************** +======================================= The SCMI specification defines **four** key terms, which will also be used throughout this documentation: - #. Agent - Entity that performs SCMI requests (e.g: gating a clock or configuring - a pin). In this context, Zephyr itself is an agent. - #. Platform - This refers to a set of hardware components that handle the requests from - agents and provide the necessary functionality. In some cases, the requests - are handled by a firmware, running on a core dedicated to performing system - management tasks. - #. Protocol - A protocol is a set of messages grouped by functionality. Intuitively, a message - can be thought of as a remote procedure call. - - The SCMI specification defines ten standard protocols: - - #. **Base** (0x10) - #. **Power domain management** (0x11) - #. **System power management** (0x12) - #. **Performance domain management** (0x13) - #. **Clock management** (0x14) - #. **Sensor management** (0x15) - #. **Reset domain management** (0x16) - #. **Voltage domain management** (0x17) - #. **Power capping and monitoring** (0x18) - #. **Pin Control** (0x19) - - where each of these protocols is identified by an unique protocol ID - (listed between brackets). - - Apart from the standard protocols, the SCMI specification reserves the - **0x80-0xFF** protocol ID range for vendor-specific protocols. - - - #. Transport - This describes how messages are exchanged between agents and the platform. - The communication itself happens through channels. +#. Agent + Entity that performs SCMI requests (e.g: gating a clock or configuring + a pin). In this context, Zephyr itself is an agent. + +#. Platform + This refers to a set of hardware components that handle the requests from + agents and provide the necessary functionality. In some cases, the requests + are handled by a firmware, running on a core dedicated to performing system + management tasks. + +#. Protocol + A protocol is a set of messages grouped by functionality. Intuitively, a message + can be thought of as a remote procedure call. + + The SCMI specification defines ten standard protocols: + + #. **Base** (0x10) + #. **Power domain management** (0x11) + #. **System power management** (0x12) + #. **Performance domain management** (0x13) + #. **Clock management** (0x14) + #. **Sensor management** (0x15) + #. **Reset domain management** (0x16) + #. **Voltage domain management** (0x17) + #. **Power capping and monitoring** (0x18) + #. **Pin Control** (0x19) + + where each of these protocols is identified by an unique protocol ID + (listed between brackets). + + Apart from the standard protocols, the SCMI specification reserves the + **0x80-0xFF** protocol ID range for vendor-specific protocols. + +#. Transport + This describes how messages are exchanged between agents and the platform. + The communication itself happens through channels. .. note:: A system may have more than one agent. Channels -******** +======== A **channel** is the medium through which agents and the platform exchange messages. The structure of a channel and the way it works is solely dependent on the transport. @@ -71,31 +72,34 @@ by two different agents for example. Channels are **bidirectional** (exception: FastChannels), and, depending on which entity initiates the communication, can be one of **two** types: - #. A2P (agent to platform) - The agent is the initiator/requester. The messages passed through these - channels are known as **commands**. - #. P2A (platform to agent) - The platform is the initiator/requester. +#. A2P (agent to platform) + The agent is the initiator/requester. The messages passed through these + channels are known as **commands**. + +#. P2A (platform to agent) + The platform is the initiator/requester. Messages -******** +======== The SCMI specification defines **four** types of messages: - #. Synchronous - These are commands that block until the platform has completed the - requested work and are sent over A2P channels. - #. Asynchronous - For these commands, the platform schedules the requested work to - be performed at a later time. As such, they return almost immediately. - These commands are sent over A2P channels. - #. Delayed response - These messages indicate the completion of the work associated - with an asynchronous command. These are sent over P2A channels. - - #. Notification - These messages are used to notify agents of events that take place on - the platform. These are sent over P2A channels. +#. Synchronous + These are commands that block until the platform has completed the + requested work and are sent over A2P channels. + +#. Asynchronous + For these commands, the platform schedules the requested work to + be performed at a later time. As such, they return almost immediately. + These commands are sent over A2P channels. + +#. Delayed response + These messages indicate the completion of the work associated + with an asynchronous command. These are sent over P2A channels. + +#. Notification + These messages are used to notify agents of events that take place on + the platform. These are sent over P2A channels. The Zephyr support for SCMI is based on the documentation provided by ARM: `DEN0056E `_. For more details @@ -105,7 +109,7 @@ SCMI support in Zephyr ********************** Shared memory and doorbell-based transport -****************************************** +========================================== This form of transport uses shared memory for reading/writing messages and doorbells for signaling. The interaction with the shared @@ -120,10 +124,10 @@ transport driver (:file:`drivers/firmware/scmi/mailbox.c`). The steps below exemplify how the communication between the Zephyr agent and the platform may happen using this transport: - #. Write message to the shared memory area. - #. Zephyr rings request doorbell. If in ``PRE_KERNEL_1`` or ``PRE_KERNEL_2`` phase start polling for reply, otherwise wait for reply doorbell ring. - #. Platform reads message from shared memory area, processes it, writes the reply back to the same area and rings the reply doorbell. - #. Zephyr reads reply from the shared memory area. +#. Write message to the shared memory area. +#. Zephyr rings request doorbell. If in ``PRE_KERNEL_1`` or ``PRE_KERNEL_2`` phase start polling for reply, otherwise wait for reply doorbell ring. +#. Platform reads message from shared memory area, processes it, writes the reply back to the same area and rings the reply doorbell. +#. Zephyr reads reply from the shared memory area. In the context of this transport, a channel is comprised of a **single** shared memory area and one or more mailbox channels. This is because users may need/want @@ -131,19 +135,20 @@ to use different mailbox channels for the request/reply doorbells. Protocols -********* +========= Currently, Zephyr has support for the following standard protocols: - #. **Power domain management** - #. **Clock management** - #. **Pin Control** +#. **Power domain management** +#. **Clock management** +#. **Pin Control** NXP-specific protocols: - #. **CPU domain management** + +#. **CPU domain management** Power domain management -*********************** +----------------------- This protocol is intended for management of power states of power domains. This is done via a set of functions implementing various commands, for @@ -153,8 +158,8 @@ example, ``POWER_STATE_GET`` and ``POWER_STATE_SET``. This driver is vendor-agnostic. As such, it may be used on any system that uses SCMI for power domain management operations. -Clock management protocol -************************* +Clock management +---------------- This protocol is used to perform clock management operations. This is done via a driver (:file:`drivers/clock_control/clock_control_arm_scmi.c`), which @@ -166,8 +171,8 @@ management driver. This driver is vendor-agnostic. As such, it may be used on any system that uses SCMI for clock management operations. -Pin Control protocol -******************** +Pin Control +----------- This protocol is used to perform pin configuration operations. This is done via a set of functions implementing various commands. Currently, the only @@ -181,7 +186,7 @@ supported command is ``PINCTRL_SETTINGS_CONFIGURE``. ``PINCTRL_SETTINGS_CONFIGURE`` command. NXP - CPU domain management -*************************** +--------------------------- This protocol is intended for management of cpu states. This is done via a set of functions implementing various commands, for From 00f0eb838ad65b43e68846d0a6a63f93efd72dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 15 Dec 2025 16:13:01 +0100 Subject: [PATCH 026/143] UPSTREAM: doc: hardware: scmi: add System power mgmt... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 008116935940401aa00106e7db7eae186cf19790 upstream. There is an implementation of the System power management protocol in the SCMI driver, so mention it. Signed-off-by: Benjamin Cabé --- doc/hardware/arch/arm-scmi.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/hardware/arch/arm-scmi.rst b/doc/hardware/arch/arm-scmi.rst index 6f2b92de112c..8b217494fe80 100644 --- a/doc/hardware/arch/arm-scmi.rst +++ b/doc/hardware/arch/arm-scmi.rst @@ -140,6 +140,7 @@ Protocols Currently, Zephyr has support for the following standard protocols: #. **Power domain management** +#. **System power management** #. **Clock management** #. **Pin Control** @@ -158,6 +159,16 @@ example, ``POWER_STATE_GET`` and ``POWER_STATE_SET``. This driver is vendor-agnostic. As such, it may be used on any system that uses SCMI for power domain management operations. +System power management +----------------------- + +This protocol is intended for system power management. This is done via a set +of functions implementing various commands, for example, ``SYSTEM_POWER_STATE_SET``. + +.. note:: + This driver is vendor-agnostic. As such, it may be used on any + system that uses SCMI for system power management operations. + Clock management ---------------- From 36f74c616950bb7242dc227ea3801b4cc8520931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 15 Dec 2025 16:22:55 +0100 Subject: [PATCH 027/143] UPSTREAM: include: firmware: scmi: add proper doxygen groups for SCMI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 66d6edaf5cc80341852bce1597c8f0314ca37ed5 upstream. This adds proper doxygen groups for all public SCMI headers Signed-off-by: Benjamin Cabé --- doc/hardware/arch/arm-scmi.rst | 6 ++++++ include/zephyr/drivers/firmware/scmi/clk.h | 14 ++++++++++++- .../zephyr/drivers/firmware/scmi/pinctrl.h | 14 ++++++++++++- include/zephyr/drivers/firmware/scmi/power.h | 14 ++++++++++++- .../zephyr/drivers/firmware/scmi/protocol.h | 20 ++++++++++++++++++- include/zephyr/drivers/firmware/scmi/shmem.h | 14 ++++++++++++- include/zephyr/drivers/firmware/scmi/system.h | 14 ++++++++++++- .../zephyr/drivers/firmware/scmi/transport.h | 14 ++++++++++++- include/zephyr/drivers/firmware/scmi/util.h | 14 ++++++++++++- 9 files changed, 116 insertions(+), 8 deletions(-) diff --git a/doc/hardware/arch/arm-scmi.rst b/doc/hardware/arch/arm-scmi.rst index 8b217494fe80..41ceebd46528 100644 --- a/doc/hardware/arch/arm-scmi.rst +++ b/doc/hardware/arch/arm-scmi.rst @@ -159,6 +159,8 @@ example, ``POWER_STATE_GET`` and ``POWER_STATE_SET``. This driver is vendor-agnostic. As such, it may be used on any system that uses SCMI for power domain management operations. +.. doxygengroup:: scmi_power + System power management ----------------------- @@ -169,6 +171,8 @@ of functions implementing various commands, for example, ``SYSTEM_POWER_STATE_SE This driver is vendor-agnostic. As such, it may be used on any system that uses SCMI for system power management operations. +.. doxygengroup:: scmi_system + Clock management ---------------- @@ -196,6 +200,8 @@ supported command is ``PINCTRL_SETTINGS_CONFIGURE``. call into the SCMI pin control protocol function implementing the ``PINCTRL_SETTINGS_CONFIGURE`` command. +.. doxygengroup:: scmi_pinctrl + NXP - CPU domain management --------------------------- diff --git a/include/zephyr/drivers/firmware/scmi/clk.h b/include/zephyr/drivers/firmware/scmi/clk.h index c3b511c4f517..2554a9f2ac50 100644 --- a/include/zephyr/drivers/firmware/scmi/clk.h +++ b/include/zephyr/drivers/firmware/scmi/clk.h @@ -6,7 +6,8 @@ /** * @file - * @brief SCMI clock protocol helpers + * @ingroup scmi_clk + * @brief Header file for the SCMI Clock Protocol. */ #ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_CLK_H_ @@ -14,6 +15,13 @@ #include +/** + * @brief Clock management operations via SCMI + * @defgroup scmi_clk Clock Protocol + * @ingroup scmi_protocols + * @{ + */ + #define SCMI_CLK_CONFIG_DISABLE_ENABLE_MASK GENMASK(1, 0) #define SCMI_CLK_CONFIG_ENABLE_DISABLE(x)\ ((uint32_t)(x) & SCMI_CLK_CONFIG_DISABLE_ENABLE_MASK) @@ -149,4 +157,8 @@ int scmi_clock_parent_get(struct scmi_protocol *proto, uint32_t clk_id, uint32_t */ int scmi_clock_parent_set(struct scmi_protocol *proto, uint32_t clk_id, uint32_t parent_id); +/** + * @} + */ + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_CLK_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/pinctrl.h b/include/zephyr/drivers/firmware/scmi/pinctrl.h index cb6a39a765e2..7e5d6b22d95e 100644 --- a/include/zephyr/drivers/firmware/scmi/pinctrl.h +++ b/include/zephyr/drivers/firmware/scmi/pinctrl.h @@ -6,7 +6,8 @@ /** * @file - * @brief SCMI pinctrl protocol helpers + * @ingroup scmi_pinctrl + * @brief Header file for the SCMI Pin Control Protocol. */ #ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PINCTRL_H_ @@ -14,6 +15,13 @@ #include +/** + * @brief Pin configuration and control via SCMI + * @defgroup scmi_pinctrl Pin Control Protocol + * @ingroup scmi_protocols + * @{ + */ + #define ARM_SCMI_PINCTRL_MAX_CONFIG_SIZE (10 * 2) #define SCMI_PINCTRL_NO_FUNCTION 0xFFFFFFFF @@ -102,4 +110,8 @@ struct scmi_pinctrl_settings { */ int scmi_pinctrl_settings_configure(struct scmi_pinctrl_settings *settings); +/** + * @} + */ + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PINCTRL_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/power.h b/include/zephyr/drivers/firmware/scmi/power.h index 7545da9ef968..2b8c76655c59 100644 --- a/include/zephyr/drivers/firmware/scmi/power.h +++ b/include/zephyr/drivers/firmware/scmi/power.h @@ -6,7 +6,8 @@ /** * @file - * @brief SCMI power domain protocol helpers + * @ingroup scmi_power + * @brief Header file for the SCMI Power Domain Protocol. */ #ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_POWER_H_ @@ -14,6 +15,13 @@ #include +/** + * @brief Power domain state management via SCMI + * @defgroup scmi_power Power Domain Protocol + * @ingroup scmi_protocols + * @{ + */ + #define SCMI_POWER_STATE_SET_FLAGS_ASYNC BIT(0) #define SCMI_POWER_DOMAIN_PROTOCOL_SUPPORTED_VERSION 0x30001 @@ -103,4 +111,8 @@ int scmi_power_state_set(struct scmi_power_state_config *cfg); */ int scmi_power_state_get(uint32_t domain_id, uint32_t *power_state); +/** + * @} + */ + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_POWER_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/protocol.h b/include/zephyr/drivers/firmware/scmi/protocol.h index a03d8cf493f8..dff3febef608 100644 --- a/include/zephyr/drivers/firmware/scmi/protocol.h +++ b/include/zephyr/drivers/firmware/scmi/protocol.h @@ -6,12 +6,20 @@ /** * @file - * @brief SCMI protocol generic functions and structures + * @ingroup scmi_interface + * @brief Header file for the SCMI (System Control and Management Interface) driver API. */ #ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_ #define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_ +/** + * @brief Interfaces for ARM System Control and Management Interface (SCMI) + * @defgroup scmi_interface SCMI + * @ingroup io_interfaces + * @{ + */ + #include #include #include @@ -184,4 +192,14 @@ int scmi_protocol_message_attributes_get(struct scmi_protocol *proto, */ int scmi_protocol_version_negotiate(struct scmi_protocol *proto, uint32_t version); +/** + * @} + */ + +/** + * @brief Standard SCMI Protocol definitions + * @defgroup scmi_protocols Protocols + * @ingroup scmi_interface + */ + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/shmem.h b/include/zephyr/drivers/firmware/scmi/shmem.h index 662bf7933735..ec984a47bd77 100644 --- a/include/zephyr/drivers/firmware/scmi/shmem.h +++ b/include/zephyr/drivers/firmware/scmi/shmem.h @@ -6,7 +6,8 @@ /** * @file - * @brief SCMI SHMEM API + * @ingroup scmi_shmem + * @brief Header file for the SCMI Shared Memory. */ #ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_SHMEM_H_ @@ -16,6 +17,13 @@ #include #include +/** + * @brief Shared memory transport definitions for SCMI + * @defgroup scmi_shmem Shared Memory + * @ingroup scmi_transport + * @{ + */ + #define SCMI_SHMEM_CHAN_STATUS_BUSY_BIT BIT(0) #define SCMI_SHMEM_CHAN_FLAG_IRQ_BIT BIT(0) @@ -93,4 +101,8 @@ int scmi_shmem_vendor_write_message(struct scmi_shmem_layout *layout); */ int scmi_shmem_vendor_read_message(const struct scmi_shmem_layout *layout); +/** + * @} + */ + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_SHMEM_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/system.h b/include/zephyr/drivers/firmware/scmi/system.h index b947d89b3607..c8c44189f9fc 100644 --- a/include/zephyr/drivers/firmware/scmi/system.h +++ b/include/zephyr/drivers/firmware/scmi/system.h @@ -9,13 +9,21 @@ /** * @file - * @brief SCMI System Power Management Protocol + * @ingroup scmi_system + * @brief Header file for the SCMI System Power Management Protocol. */ #include #include #include +/** + * @brief System-wide power state management via SCMI + * @defgroup scmi_system System Power Protocol + * @ingroup scmi_protocols + * @{ + */ + #ifdef __cplusplus extern "C" { #endif @@ -150,4 +158,8 @@ int scmi_system_power_state_set(struct scmi_system_power_state_config *cfg); } #endif +/** + * @} + */ + #endif /* ZEPHYR_INCLUDE_DRIVERS_FIRMWARE_SCMI_SYSTEM_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/transport.h b/include/zephyr/drivers/firmware/scmi/transport.h index 2cb72a2b9042..773b1e7d478f 100644 --- a/include/zephyr/drivers/firmware/scmi/transport.h +++ b/include/zephyr/drivers/firmware/scmi/transport.h @@ -6,7 +6,8 @@ /** * @file - * @brief Public APIs for the SCMI transport layer drivers + * @ingroup scmi_transport + * @brief Header file for the SCMI Transport Layer. */ #ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_ @@ -15,6 +16,13 @@ #include #include +/** + * @brief SCMI Transport Layer abstraction and definitions + * @defgroup scmi_transport Transport + * @ingroup scmi_interface + * @{ + */ + struct scmi_message; struct scmi_channel; @@ -271,4 +279,8 @@ static inline bool scmi_transport_channel_is_free(const struct device *transport */ int scmi_core_transport_init(const struct device *transport); +/** + * @} + */ + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/util.h b/include/zephyr/drivers/firmware/scmi/util.h index 55bceeaaf904..56b6d3cbc69a 100644 --- a/include/zephyr/drivers/firmware/scmi/util.h +++ b/include/zephyr/drivers/firmware/scmi/util.h @@ -6,7 +6,8 @@ /** * @file - * @brief ARM SCMI utility header + * @ingroup scmi_util + * @brief Header file for SCMI Utility Macros. * * Contains various utility macros and macros used for protocol and * transport "registration". @@ -15,6 +16,13 @@ #ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_UTIL_H_ #define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_UTIL_H_ +/** + * @brief Helper macros and utilities for SCMI drivers + * @defgroup scmi_util Utilities + * @ingroup scmi_interface + * @{ + */ + /** * @brief Build protocol name from its ID * @@ -286,4 +294,8 @@ #define SCMI_PROTOCOL_PCAP_MONITOR 24 #define SCMI_PROTOCOL_PINCTRL 25 +/** + * @} + */ + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_UTIL_H_ */ From 12e53d5ed290371f052f63305b2dafbec6f5c72f Mon Sep 17 00:00:00 2001 From: Biwen Li Date: Wed, 17 Dec 2025 14:28:15 +0900 Subject: [PATCH 028/143] UPSTREAM: firmware: scmi: fix build issue commit c2d201d24e240ead347633b0a2811a9d88f4e693 upstream. arch reboot is not included in PM flow, so drop the option CONFIG_PM_LOG_LEVEL to fix build issue: - error: CONFIG_PM_LOG_LEVEL undeclared here (not in a function) Signed-off-by: Biwen Li --- drivers/firmware/scmi/reboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/scmi/reboot.c b/drivers/firmware/scmi/reboot.c index d00c3ff201ae..18b02a082439 100644 --- a/drivers/firmware/scmi/reboot.c +++ b/drivers/firmware/scmi/reboot.c @@ -9,7 +9,7 @@ #include #include -LOG_MODULE_REGISTER(scmi_reboot, CONFIG_PM_LOG_LEVEL); +LOG_MODULE_REGISTER(scmi_reboot); static int scmi_reboot_handler(int type) { From c102ee7a941ec9b91645b86c120483727e5968c9 Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Mon, 22 Dec 2025 14:17:37 +0800 Subject: [PATCH 029/143] UPSTREAM: firmware: scmi: add mutex protection to polling mode commit 52c712d4d13efbeadd53f6c6bd61dbe370fd088f upstream. Protect scmi_send_message_polling() with channel mutex to prevent concurrent access with interrupt-driven transfers. Without this fix, PM subsystem could send polling-mode messages while an interrupt-mode transfer was in progress, corrupting the shared channel state. Fixes hang on i.MX943 during I2C initialization when PM attempted to send power state notifications concurrently. Signed-off-by: Yongxu Wang --- drivers/firmware/scmi/core.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/firmware/scmi/core.c b/drivers/firmware/scmi/core.c index 547ee92f43e5..a18069d32444 100644 --- a/drivers/firmware/scmi/core.c +++ b/drivers/firmware/scmi/core.c @@ -115,6 +115,13 @@ static int scmi_send_message_polling(struct scmi_protocol *proto, int ret; int status; + /* wait for channel to be free */ + ret = k_mutex_lock(&proto->tx->lock, K_NO_WAIT); + if (ret < 0) { + LOG_ERR("failed to acquire chan lock"); + return -EBUSY; + } + /* * SCMI communication interrupt is enabled by default during setup_chan * to support interrupt-driven communication. When using polling mode @@ -152,6 +159,8 @@ static int scmi_send_message_polling(struct scmi_protocol *proto, scmi_interrupt_enable(proto->tx, true); } + k_mutex_unlock(&proto->tx->lock); + return ret; } From 17f746745c03ab7d04fe8ea5fa7f409c33d95a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Cab=C3=A9?= Date: Mon, 19 Jan 2026 17:52:41 +0100 Subject: [PATCH 030/143] UPSTREAM: drivers: firmware: scmi: inclusive language fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 93bf06a235dac35274f5f9946a564425ee13bdf1 upstream. As per coding guidelines, "sanity check" must be avoided. Signed-off-by: Benjamin Cabé --- drivers/firmware/scmi/clk.c | 12 ++++++------ drivers/firmware/scmi/nxp/cpu.c | 10 +++++----- drivers/firmware/scmi/pinctrl.c | 2 +- drivers/firmware/scmi/power.c | 4 ++-- drivers/firmware/scmi/shmem.c | 4 ++-- drivers/firmware/scmi/system.c | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/firmware/scmi/clk.c b/drivers/firmware/scmi/clk.c index e47e4efdfda2..646a719a8ceb 100644 --- a/drivers/firmware/scmi/clk.c +++ b/drivers/firmware/scmi/clk.c @@ -41,7 +41,7 @@ int scmi_clock_rate_get(struct scmi_protocol *proto, struct scmi_clock_rate_set_reply reply_buffer; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !rate) { return -EINVAL; } @@ -81,7 +81,7 @@ int scmi_clock_rate_set(struct scmi_protocol *proto, struct scmi_clock_rate_conf int status, ret; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } @@ -120,7 +120,7 @@ int scmi_clock_parent_get(struct scmi_protocol *proto, uint32_t clk_id, uint32_t struct scmi_clock_parent_get_reply reply_buffer; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !parent_id) { return -EINVAL; } @@ -161,7 +161,7 @@ int scmi_clock_parent_set(struct scmi_protocol *proto, uint32_t clk_id, uint32_t int status, ret; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto) { return -EINVAL; } @@ -196,7 +196,7 @@ int scmi_clock_config_set(struct scmi_protocol *proto, int status, ret; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } @@ -246,7 +246,7 @@ int scmi_clock_protocol_attributes(struct scmi_protocol *proto, uint32_t *attrib int ret; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !attributes) { return -EINVAL; } diff --git a/drivers/firmware/scmi/nxp/cpu.c b/drivers/firmware/scmi/nxp/cpu.c index a1b0021814b6..8695ae41eab6 100644 --- a/drivers/firmware/scmi/nxp/cpu.c +++ b/drivers/firmware/scmi/nxp/cpu.c @@ -23,7 +23,7 @@ int scmi_cpu_sleep_mode_set(struct scmi_cpu_sleep_mode_config *cfg) int status, ret; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } @@ -61,7 +61,7 @@ int scmi_cpu_pd_lpm_set(struct scmi_cpu_pd_lpm_config *cfg) int status, ret; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } @@ -95,7 +95,7 @@ int scmi_cpu_set_irq_mask(struct scmi_cpu_irq_mask_config *cfg) struct scmi_message msg, reply; int status, ret; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } @@ -127,7 +127,7 @@ int scmi_cpu_reset_vector(struct scmi_cpu_vector_config *cfg) struct scmi_message msg, reply; int status, ret; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } @@ -160,7 +160,7 @@ int scmi_cpu_info_get(uint32_t cpu_id, struct scmi_cpu_info *cfg) struct scmi_cpu_info_get_reply reply_buffer; int ret; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } diff --git a/drivers/firmware/scmi/pinctrl.c b/drivers/firmware/scmi/pinctrl.c index abfcae844a67..22508c6d7bda 100644 --- a/drivers/firmware/scmi/pinctrl.c +++ b/drivers/firmware/scmi/pinctrl.c @@ -20,7 +20,7 @@ int scmi_pinctrl_settings_configure(struct scmi_pinctrl_settings *settings) proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_PINCTRL); - /* sanity checks */ + /* input validation */ if (!settings) { return -EINVAL; } diff --git a/drivers/firmware/scmi/power.c b/drivers/firmware/scmi/power.c index 6396029071be..d1a7a869ba10 100644 --- a/drivers/firmware/scmi/power.c +++ b/drivers/firmware/scmi/power.c @@ -24,7 +24,7 @@ int scmi_power_state_get(uint32_t domain_id, uint32_t *power_state) int ret; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !power_state) { return -EINVAL; } @@ -65,7 +65,7 @@ int scmi_power_state_set(struct scmi_power_state_config *cfg) int status, ret; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } diff --git a/drivers/firmware/scmi/shmem.c b/drivers/firmware/scmi/shmem.c index 86b1a9659d49..2bdb23465126 100644 --- a/drivers/firmware/scmi/shmem.c +++ b/drivers/firmware/scmi/shmem.c @@ -68,7 +68,7 @@ int scmi_shmem_read_message(const struct device *shmem, struct scmi_message *msg cfg = shmem->config; layout = (struct scmi_shmem_layout *)data->regmap; - /* some sanity checks first */ + /* some input validation first */ if (!msg) { return -EINVAL; } @@ -120,7 +120,7 @@ int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *ms cfg = shmem->config; layout = (struct scmi_shmem_layout *)data->regmap; - /* some sanity checks first */ + /* some input validation first */ if (!msg) { return -EINVAL; } diff --git a/drivers/firmware/scmi/system.c b/drivers/firmware/scmi/system.c index d96efd27e3bb..46eb9a23e0de 100644 --- a/drivers/firmware/scmi/system.c +++ b/drivers/firmware/scmi/system.c @@ -47,7 +47,7 @@ int scmi_system_power_state_set(struct scmi_system_power_state_config *cfg) int ret; bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } From 2afd974234b760a2086497612366bfd3ca5f77bd Mon Sep 17 00:00:00 2001 From: Yongxu Wang Date: Fri, 16 Jan 2026 14:08:48 +0800 Subject: [PATCH 031/143] UPSTREAM: drivers: power_domain: add SCMI power domain driver commit 6df440012be5e18f4ccb0303b562af4e6b36b9ca upstream. Add ARM SCMI-based power domain driver for managing power domains through the SCMI protocol. Signed-off-by: Yongxu Wang --- drivers/power_domain/CMakeLists.txt | 3 + drivers/power_domain/Kconfig | 21 +++++ drivers/power_domain/power_domain_arm_scmi.c | 82 +++++++++++++++++++ .../power-domain/arm,scmi-power-domain.yaml | 13 +++ 4 files changed, 119 insertions(+) create mode 100644 drivers/power_domain/power_domain_arm_scmi.c create mode 100644 dts/bindings/power-domain/arm,scmi-power-domain.yaml diff --git a/drivers/power_domain/CMakeLists.txt b/drivers/power_domain/CMakeLists.txt index 2749a32d5d63..d1ae0fc8e952 100644 --- a/drivers/power_domain/CMakeLists.txt +++ b/drivers/power_domain/CMakeLists.txt @@ -3,6 +3,8 @@ zephyr_library() +# zephyr-keep-sorted-start +zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_ARM_SCMI power_domain_arm_scmi.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO power_domain_gpio.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO_MONITOR power_domain_gpio_monitor.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_INTEL_ADSP power_domain_intel_adsp.c) @@ -12,3 +14,4 @@ zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_NRFS_SWEXT power_domain_nrfs_sw zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_SOC_PM_STATE power_domain_soc_state_change.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_TISCI power_domain_tisci.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_SILABS_SIWX91X power_domain_silabs_siwx91x.c) +# zephyr-keep-sorted-stop diff --git a/drivers/power_domain/Kconfig b/drivers/power_domain/Kconfig index 3de9185761c0..ff3704b3d407 100644 --- a/drivers/power_domain/Kconfig +++ b/drivers/power_domain/Kconfig @@ -123,8 +123,29 @@ config SOC_POWER_DOMAIN_INIT endif #POWER_DOMAIN_TISCI +config POWER_DOMAIN_ARM_SCMI + bool "ARM SCMI Power Domain driver" + default y + depends on ARM_SCMI_POWER_DOMAIN_HELPERS + depends on DT_HAS_ARM_SCMI_POWER_DOMAIN_ENABLED + select DEVICE_DEPS + help + Enable SCMI-based power domain driver. + +if POWER_DOMAIN_ARM_SCMI + +config POWER_DOMAIN_ARM_SCMI_INIT_PRIORITY + int "ARM SCMI PD driver init priority" + default 10 + help + ARM SCMI PD driver initialization priority. + +endif #POWER_DOMAIN_ARM_SCMI + +# zephyr-keep-sorted-start rsource "Kconfig.nrfs_gdpwr" rsource "Kconfig.nrfs_swext" rsource "Kconfig.silabs_siwx91x" +# zephyr-keep-sorted-stop endif diff --git a/drivers/power_domain/power_domain_arm_scmi.c b/drivers/power_domain/power_domain_arm_scmi.c new file mode 100644 index 000000000000..2b7cca0a8530 --- /dev/null +++ b/drivers/power_domain/power_domain_arm_scmi.c @@ -0,0 +1,82 @@ +/* + * Copyright 2026 NXP + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT arm_scmi_power_domain + +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(scmi_power_domain, CONFIG_POWER_DOMAIN_LOG_LEVEL); + +struct scmi_pd_config { + uint32_t domain_id; +}; + +static int scmi_pd_pm_action(const struct device *dev, + enum pm_device_action action) +{ + const struct scmi_pd_config *cfg = dev->config; + struct scmi_power_state_config pwr_cfg; + int ret; + + LOG_INF("attempting PM action %d on domain %d", action, cfg->domain_id); + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + pwr_cfg.domain_id = cfg->domain_id; + pwr_cfg.flags = 0; + pwr_cfg.power_state = SCMI_POWER_STATE_GENERIC_ON; + + ret = scmi_power_state_set(&pwr_cfg); + if (ret < 0) { + return ret; + } + pm_device_children_action_run(dev, PM_DEVICE_ACTION_TURN_ON, NULL); + break; + case PM_DEVICE_ACTION_SUSPEND: + pm_device_children_action_run(dev, PM_DEVICE_ACTION_TURN_OFF, NULL); + + pwr_cfg.domain_id = cfg->domain_id; + pwr_cfg.flags = 0; + pwr_cfg.power_state = SCMI_POWER_STATE_GENERIC_OFF; + + ret = scmi_power_state_set(&pwr_cfg); + if (ret < 0) { + return ret; + } + break; + case PM_DEVICE_ACTION_TURN_ON: + break; + case PM_DEVICE_ACTION_TURN_OFF: + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int scmi_pd_init(const struct device *dev) +{ + return pm_device_driver_init(dev, scmi_pd_pm_action); +} +#define SCMI_PD_DEVICE(inst) \ + static const struct scmi_pd_config scmi_pd_cfg_##inst = { \ + .domain_id = DT_INST_REG_ADDR(inst), \ + }; \ + \ + PM_DEVICE_DT_INST_DEFINE(inst, scmi_pd_pm_action); \ + DEVICE_DT_INST_DEFINE(inst, scmi_pd_init, \ + PM_DEVICE_DT_INST_GET(inst), \ + NULL, \ + &scmi_pd_cfg_##inst, \ + PRE_KERNEL_2, \ + CONFIG_POWER_DOMAIN_ARM_SCMI_INIT_PRIORITY, \ + NULL); +DT_INST_FOREACH_STATUS_OKAY(SCMI_PD_DEVICE) diff --git a/dts/bindings/power-domain/arm,scmi-power-domain.yaml b/dts/bindings/power-domain/arm,scmi-power-domain.yaml new file mode 100644 index 000000000000..66a732af25e7 --- /dev/null +++ b/dts/bindings/power-domain/arm,scmi-power-domain.yaml @@ -0,0 +1,13 @@ +# Copyright 2026 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: ARM SCMI Power Domain + +compatible: "arm,scmi-power-domain" + +include: power-domain.yaml + +properties: + reg: + required: true + description: SCMI power domain ID From 0872029119b73b488160e3ae84832aac89b7f31b Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Wed, 21 Jan 2026 00:47:23 +0200 Subject: [PATCH 032/143] UPSTREAM: bindings: scmi: modify description format commit bf747b8e037eef1a873c998fc6642ba32a28d84b upstream. Modify the description format of all SCMI-related bindings such that the SCMI/SHMEM acronym is spelled out in between parentheses and not the other way around. This will make the generated documentation more compact. Signed-off-by: Laurentiu Mihalcea --- dts/bindings/firmware/arm,scmi-clock.yaml | 2 +- dts/bindings/firmware/arm,scmi-pinctrl.yaml | 2 +- dts/bindings/firmware/arm,scmi-power.yaml | 2 +- dts/bindings/firmware/arm,scmi-shmem.yaml | 2 +- dts/bindings/firmware/arm,scmi-system.yaml | 2 +- dts/bindings/firmware/arm,scmi.yaml | 4 ++-- dts/bindings/firmware/nxp,scmi-cpu.yaml | 2 +- dts/bindings/power-domain/arm,scmi-power-domain.yaml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dts/bindings/firmware/arm,scmi-clock.yaml b/dts/bindings/firmware/arm,scmi-clock.yaml index 5f0aa160d3fc..943f3015fb70 100644 --- a/dts/bindings/firmware/arm,scmi-clock.yaml +++ b/dts/bindings/firmware/arm,scmi-clock.yaml @@ -1,7 +1,7 @@ # Copyright 2024 NXP # SPDX-License-Identifier: Apache-2.0 -description: System Control and Management Interface (SCMI) clock protocol +description: SCMI (System Control and Management Interface) clock protocol compatible: "arm,scmi-clock" diff --git a/dts/bindings/firmware/arm,scmi-pinctrl.yaml b/dts/bindings/firmware/arm,scmi-pinctrl.yaml index f1e52b0fd209..74b3b55831a9 100644 --- a/dts/bindings/firmware/arm,scmi-pinctrl.yaml +++ b/dts/bindings/firmware/arm,scmi-pinctrl.yaml @@ -1,7 +1,7 @@ # Copyright 2024 NXP # SPDX-License-Identifier: Apache-2.0 -description: System Control and Management Interface (SCMI) pinctrl protocol +description: SCMI (System Control and Management Interface) pinctrl protocol compatible: "arm,scmi-pinctrl" diff --git a/dts/bindings/firmware/arm,scmi-power.yaml b/dts/bindings/firmware/arm,scmi-power.yaml index 41ad234681d0..2b5c5281a518 100644 --- a/dts/bindings/firmware/arm,scmi-power.yaml +++ b/dts/bindings/firmware/arm,scmi-power.yaml @@ -1,7 +1,7 @@ # Copyright 2024 NXP # SPDX-License-Identifier: Apache-2.0 -description: System Control and Management Interface (SCMI) power domain protocol +description: SCMI (System Control and Management Interface) power domain protocol compatible: "arm,scmi-power" diff --git a/dts/bindings/firmware/arm,scmi-shmem.yaml b/dts/bindings/firmware/arm,scmi-shmem.yaml index aa968a3e4e5f..4fe7453e9301 100644 --- a/dts/bindings/firmware/arm,scmi-shmem.yaml +++ b/dts/bindings/firmware/arm,scmi-shmem.yaml @@ -1,7 +1,7 @@ # Copyright 2024 NXP # SPDX-License-Identifier: Apache-2.0 -description: System Control and Management Interface (SCMI) shared memory (SHMEM) +description: SCMI (System Control and Management Interface) SHMEM (shared memory) compatible: "arm,scmi-shmem" diff --git a/dts/bindings/firmware/arm,scmi-system.yaml b/dts/bindings/firmware/arm,scmi-system.yaml index 77aba5abf176..f8b79a8fefd5 100644 --- a/dts/bindings/firmware/arm,scmi-system.yaml +++ b/dts/bindings/firmware/arm,scmi-system.yaml @@ -1,7 +1,7 @@ # Copyright 2025 NXP # SPDX-License-Identifier: Apache-2.0 -description: System Control and Management Interface (SCMI) system power protocol +description: SCMI (System Control and Management Interface) system power protocol compatible: "arm,scmi-system" diff --git a/dts/bindings/firmware/arm,scmi.yaml b/dts/bindings/firmware/arm,scmi.yaml index 5ece8e59201d..22766cb815df 100644 --- a/dts/bindings/firmware/arm,scmi.yaml +++ b/dts/bindings/firmware/arm,scmi.yaml @@ -2,8 +2,8 @@ # SPDX-License-Identifier: Apache-2.0 description: | - System Control and Management Interface (SCMI) with doorbell - and shared memory (SHMEM) transport. + SCMI (System Control and Management Interface) with doorbell + and SHMEM (shared memory) transport. Devicetree example: #include diff --git a/dts/bindings/firmware/nxp,scmi-cpu.yaml b/dts/bindings/firmware/nxp,scmi-cpu.yaml index 7032df08d982..d435eb47bfee 100644 --- a/dts/bindings/firmware/nxp,scmi-cpu.yaml +++ b/dts/bindings/firmware/nxp,scmi-cpu.yaml @@ -1,7 +1,7 @@ # Copyright 2025 NXP # SPDX-License-Identifier: Apache-2.0 -description: System Control and Management Interface (SCMI) cpu domain protocol +description: SCMI (System Control and Management Interface) cpu domain protocol compatible: "nxp,scmi-cpu" diff --git a/dts/bindings/power-domain/arm,scmi-power-domain.yaml b/dts/bindings/power-domain/arm,scmi-power-domain.yaml index 66a732af25e7..588d0c041e5a 100644 --- a/dts/bindings/power-domain/arm,scmi-power-domain.yaml +++ b/dts/bindings/power-domain/arm,scmi-power-domain.yaml @@ -1,7 +1,7 @@ # Copyright 2026 NXP # SPDX-License-Identifier: Apache-2.0 -description: ARM SCMI Power Domain +description: SCMI (System Control and Management Interface) power domain compatible: "arm,scmi-power-domain" From fd64db3ad76505178fdf68ff1823240fbdda8fd0 Mon Sep 17 00:00:00 2001 From: Biwen Li Date: Thu, 22 Jan 2026 15:51:22 +0800 Subject: [PATCH 033/143] UPSTREAM: drivers: power_domain: arm_scmi: update kernel level and priority commit f07d399de50f92e3a443abc7420c0a95104a4457 upstream. Update kernel level and priority(same with scmi clock control) On i.MX95, there is an irqsteer instance in display mix. when the irqsteer is enabled, must power on display mix before accessing registers of the irqsteer(call irqsteer_hw_init). the irqsteer drivers's kernel level is PRE_KERNEL_1 and it's priority is 40. So update kernel level to PRE_KERNEL_1 and update priority to 30 to ensure that the arm scmi power domain driver is initialized before initializing irqsteer driver. Signed-off-by: Biwen Li --- drivers/power_domain/Kconfig | 2 +- drivers/power_domain/power_domain_arm_scmi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power_domain/Kconfig b/drivers/power_domain/Kconfig index ff3704b3d407..e1b70edf08a3 100644 --- a/drivers/power_domain/Kconfig +++ b/drivers/power_domain/Kconfig @@ -136,7 +136,7 @@ if POWER_DOMAIN_ARM_SCMI config POWER_DOMAIN_ARM_SCMI_INIT_PRIORITY int "ARM SCMI PD driver init priority" - default 10 + default 30 help ARM SCMI PD driver initialization priority. diff --git a/drivers/power_domain/power_domain_arm_scmi.c b/drivers/power_domain/power_domain_arm_scmi.c index 2b7cca0a8530..f9bf7679c2b4 100644 --- a/drivers/power_domain/power_domain_arm_scmi.c +++ b/drivers/power_domain/power_domain_arm_scmi.c @@ -76,7 +76,7 @@ static int scmi_pd_init(const struct device *dev) PM_DEVICE_DT_INST_GET(inst), \ NULL, \ &scmi_pd_cfg_##inst, \ - PRE_KERNEL_2, \ + PRE_KERNEL_1, \ CONFIG_POWER_DOMAIN_ARM_SCMI_INIT_PRIORITY, \ NULL); DT_INST_FOREACH_STATUS_OKAY(SCMI_PD_DEVICE) From 200778ab2877be2fa9475b227803a334a6bbe92e Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Thu, 15 Jan 2026 16:20:33 +0200 Subject: [PATCH 034/143] UPSTREAM: firmware: scmi: core: don't allow mutex usage in PRE_KERNEL stage commit 32e75415dc2eec12ca9ca71f2ec0067237890770 upstream. While in PRE_KERNEL stage, scmi_send_message_polling() attempts to acquire the transmit channel mutex, which should not be allowed as the kernel is not fully initialized at this point. Since there's no multithreading at this point, there's no need to use a mutex anyways. Therefore, perform mutex acquire()/release() only if not in PRE_KERNEL phase. Note that the return value of k_mutex_lock() is already suppressed via an -EBUSY return value so no behavioral change here. Signed-off-by: Laurentiu Mihalcea --- drivers/firmware/scmi/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/scmi/core.c b/drivers/firmware/scmi/core.c index a18069d32444..43b662b1c62d 100644 --- a/drivers/firmware/scmi/core.c +++ b/drivers/firmware/scmi/core.c @@ -116,8 +116,7 @@ static int scmi_send_message_polling(struct scmi_protocol *proto, int status; /* wait for channel to be free */ - ret = k_mutex_lock(&proto->tx->lock, K_NO_WAIT); - if (ret < 0) { + if (!k_is_pre_kernel() && k_mutex_lock(&proto->tx->lock, K_NO_WAIT)) { LOG_ERR("failed to acquire chan lock"); return -EBUSY; } @@ -159,7 +158,9 @@ static int scmi_send_message_polling(struct scmi_protocol *proto, scmi_interrupt_enable(proto->tx, true); } - k_mutex_unlock(&proto->tx->lock); + if (!k_is_pre_kernel()) { + k_mutex_unlock(&proto->tx->lock); + } return ret; } From 633508fdeab0a86cd19166b8a17ff3222cd79217 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Tue, 3 Feb 2026 19:54:36 +0200 Subject: [PATCH 035/143] UPSTREAM: firmware: scmi: transport: hide consumer API from public docs commit 364b4e0aa13546ac7b118de0d7079989d9af7079 upstream. The consumer functions are only meant to be used by the SCMI core. Thus, there's no need to keep their documentation public. Transport driver writers should only care about the driver API documentation anyways. Signed-off-by: Laurentiu Mihalcea --- include/zephyr/drivers/firmware/scmi/transport.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/zephyr/drivers/firmware/scmi/transport.h b/include/zephyr/drivers/firmware/scmi/transport.h index 773b1e7d478f..2878a1bd9310 100644 --- a/include/zephyr/drivers/firmware/scmi/transport.h +++ b/include/zephyr/drivers/firmware/scmi/transport.h @@ -94,6 +94,8 @@ struct scmi_transport_api { uint32_t proto, bool tx); }; +/** @cond INTERNAL_HIDDEN */ + /** * @brief Request an SCMI channel dynamically * @@ -279,6 +281,8 @@ static inline bool scmi_transport_channel_is_free(const struct device *transport */ int scmi_core_transport_init(const struct device *transport); +/** @endcond */ + /** * @} */ From 085e030e786021b98429b12c1fcd669eadce9b83 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Tue, 3 Feb 2026 19:59:29 +0200 Subject: [PATCH 036/143] UPSTREAM: firmware: scmi: transport: hide private... commit b17297807f41d71288d4d04d8924f1c71d3057ab upstream. "struct scmi_channel" has a couple of fields, which are only meant to be used by the SCMI core. As such, there's no need to expose their documentation to the public since transport driver writers shouldn't have to care about them anyways. Thus, mark these fields as INTERNAL_HIDDEN. Note that the order of the structure fields is shuffled such that public and private fields can stay grouped together. Signed-off-by: Laurentiu Mihalcea --- .../zephyr/drivers/firmware/scmi/transport.h | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/include/zephyr/drivers/firmware/scmi/transport.h b/include/zephyr/drivers/firmware/scmi/transport.h index 2878a1bd9310..7608ac03b027 100644 --- a/include/zephyr/drivers/firmware/scmi/transport.h +++ b/include/zephyr/drivers/firmware/scmi/transport.h @@ -51,6 +51,16 @@ typedef void (*scmi_channel_cb)(struct scmi_channel *chan); * channels is represented by a `struct scmi_channel`. */ struct scmi_channel { + /** channel private data */ + void *data; + /** + * callback function. This is meant to be set by + * the SCMI core and should be called by the SCMI + * transport layer driver whenever a reply has + * been received. + */ + scmi_channel_cb cb; + /** @cond INTERNAL_HIDDEN */ /** * channel lock. This is meant to be initialized * and used only by the SCMI core to assure that @@ -58,23 +68,17 @@ struct scmi_channel { * through a channel at a given moment. */ struct k_mutex lock; + /** * binary semaphore. This is meant to be initialized * and used only by the SCMI core. Its purpose is to * signal that a reply has been received. */ struct k_sem sem; - /** channel private data */ - void *data; - /** - * callback function. This is meant to be set by - * the SCMI core and should be called by the SCMI - * transport layer driver whenever a reply has - * been received. - */ - scmi_channel_cb cb; + /** is the channel ready to be used by a protocol? */ bool ready; + /** @endcond */ }; struct scmi_transport_api { From c4d2e288e6e36b5909534ce74349b1e04afb7a69 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Tue, 3 Feb 2026 21:47:50 +0200 Subject: [PATCH 037/143] UPSTREAM: firmware: scmi: transport: document transport driver API commit fd3fa5d42774d402412c236a34fc36c8f8dd0458 upstream. Add documentation for the transport driver API. This is done by moving and rewriting the previously existing documentation bits from the transport consumer API. The documentation is rewritten such that it can be more concise. Signed-off-by: Laurentiu Mihalcea --- .../zephyr/drivers/firmware/scmi/transport.h | 181 ++++++++++-------- 1 file changed, 100 insertions(+), 81 deletions(-) diff --git a/include/zephyr/drivers/firmware/scmi/transport.h b/include/zephyr/drivers/firmware/scmi/transport.h index 7608ac03b027..088163006dbc 100644 --- a/include/zephyr/drivers/firmware/scmi/transport.h +++ b/include/zephyr/drivers/firmware/scmi/transport.h @@ -81,19 +81,113 @@ struct scmi_channel { /** @endcond */ }; +/** + * @struct scmi_transport_api + * @brief SCMI transport driver operations + * + * This structure contains the set of operations to be implemented by + * all transport drivers. + */ struct scmi_transport_api { + /** + * @brief Initialize the transport driver + * + * This operation can be left unimplemented if the driver + * requires no initialization. + * + * @note this operation is optional. + * + * @param transport transport device + * + * @retval 0 is successful + * @retval <0 negative errno code if failure + */ int (*init)(const struct device *transport); + + /** + * @brief Send a message to the platform + * + * Used to send a message to the platform over a given TX channel. + * + * @param transport transport device + * @param chan channel used to send the message + * @param msg message to send + * + * @retval 0 if successful + * @retval <0 negative errno code if failure + */ int (*send_message)(const struct device *transport, struct scmi_channel *chan, struct scmi_message *msg); + + /** + * @brief Prepare a channel for communication + * + * Perform any sort of initialization required by a channel + * to be able to send or receive data. + * + * @param transport transport device + * @param chan channel to prepare + * @param tx true if channel is TX, false if channel is RX + * + * @retval 0 if successful + * @retval <0 negative errno code if failure + */ int (*setup_chan)(const struct device *transport, struct scmi_channel *chan, bool tx); + + /** + * @brief Read a message from the platform + * + * Used to read/receive a message from the platform over a given + * RX channel. + * + * @param transport transport device + * @param chan channel used to receive the message + * @param msg message to receive + * + * @retval 0 if successful + * @retval <0 negative errno code if failure + */ int (*read_message)(const struct device *transport, struct scmi_channel *chan, struct scmi_message *msg); + + /** + * @brief Check if a TX channel is free + * + * Used to check if a TX channel allows sending data to the + * platform. If a message was previously sent to the platform, + * it is assumed that this function will indicate the availability + * of the message's reply. + * + * @param transport device + * @param chan TX channel to query + * + * @retval 0 if successful + * @retval <0 negative errno code if failure + */ bool (*channel_is_free)(const struct device *transport, struct scmi_channel *chan); + + /** + * @brief Request a channel dynamically + * + * If @kconfig{CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS} + * is enabled, this operation will be used to dynamically request + * a channel and bind it to a given protocol. Otherwise, operation + * can be left unimplemented. + * + * @note this operation is optional + * + * @param transport transport device + * @param proto ID of the protocol for which the channel is requested + * @param tx true if channel is TX, false if channel is RX + * + * @retval pointer to the channel bound to given protocol if successful + * @retval NULL if failure + */ struct scmi_channel *(*request_channel)(const struct device *transport, uint32_t proto, bool tx); }; @@ -101,24 +195,7 @@ struct scmi_transport_api { /** @cond INTERNAL_HIDDEN */ /** - * @brief Request an SCMI channel dynamically - * - * Whenever the SCMI transport layer driver doesn't support - * static channel allocation, the SCMI core will try to bind - * a channel to a protocol dynamically using this function. - * Note that no setup needs to be performed on the channel - * in this function as the core will also call the channel - * setup() function. - * - * @param transport pointer to the device structure for the - * transport layer - * @param proto ID of the protocol for which the core is - * requesting the channel - * @param tx true if the channel is TX, false if RX - * - * @retval pointer to SCMI channel that's to be bound - * to the protocol - * @retval NULL if operation was not successful + * See @ref scmi_transport_api::request_channel for more details. */ static inline struct scmi_channel * scmi_transport_request_channel(const struct device *transport, @@ -135,23 +212,7 @@ scmi_transport_request_channel(const struct device *transport, } /** - * @brief Perform initialization for the transport layer driver - * - * The transport layer driver can't be initialized directly - * (i.e via a call to its init() function) during system initialization. - * This is because the macro used to define an SCMI transport places - * `scmi_core_transport_init()` in the init section instead of the - * driver's init() function. As such, `scmi_core_transport_init()` - * needs to call this function to perfrom transport layer driver - * initialization if required. - * - * This operation is optional. - * - * @param transport pointer to the device structure for the - * transport layer - * - * @retval 0 if successful - * @retval negative errno code if failure + * See @ref scmi_transport_api::init for more details. */ static inline int scmi_transport_init(const struct device *transport) { @@ -166,20 +227,7 @@ static inline int scmi_transport_init(const struct device *transport) } /** - * @brief Setup an SCMI channel - * - * Before being able to send/receive messages, an SCMI channel needs - * to be prepared, which is what this function does. If it returns - * successfully, an SCMI protocol will be able to use this channel - * to send/receive messages. - * - * @param transport pointer to the device structure for the - * transport layer - * @param chan pointer to SCMI channel to be prepared - * @param tx true if the channel is TX, false if RX - * - * @retval 0 if successful - * @retval negative errno code if failure + * See @ref scmi_transport_api::setup_chan for more details. */ static inline int scmi_transport_setup_chan(const struct device *transport, struct scmi_channel *chan, @@ -196,19 +244,7 @@ static inline int scmi_transport_setup_chan(const struct device *transport, } /** - * @brief Send an SCMI channel - * - * Send an SCMI message using given SCMI channel. This function is - * not allowed to block. - * - * @param transport pointer to the device structure for the - * transport layer - * @param chan pointer to SCMI channel on which the message - * is to be sent - * @param msg pointer to message the caller wishes to send - * - * @retval 0 if successful - * @retval negative errno code if failure + * See @ref scmi_transport_api::send_message for more details. */ static inline int scmi_transport_send_message(const struct device *transport, struct scmi_channel *chan, @@ -225,16 +261,7 @@ static inline int scmi_transport_send_message(const struct device *transport, } /** - * @brief Read an SCMI message - * - * @param transport pointer to the device structure for the - * transport layer - * @param chan pointer to SCMI channel on which the message - * is to be read - * @param msg pointer to message the caller wishes to read - * - * @retval 0 if successful - * @retval negative errno code if failure + * See @ref scmi_transport_api::read_message for more details. */ static inline int scmi_transport_read_message(const struct device *transport, struct scmi_channel *chan, @@ -251,15 +278,7 @@ static inline int scmi_transport_read_message(const struct device *transport, } /** - * @brief Check if an SCMI channel is free - * - * @param transport pointer to the device structure for - * the transport layer - * @param chan pointer to SCMI channel the query is to be - * performed on - * - * @retval 0 if successful - * @retval negative errno code if failure + * See @ref scmi_transport_api::channel_is_free for more details. */ static inline bool scmi_transport_channel_is_free(const struct device *transport, struct scmi_channel *chan) From 8fe018a3b4190db6bb2c2efbaaacc94b2cab6ba4 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Wed, 28 Jan 2026 11:40:43 +0200 Subject: [PATCH 038/143] UPSTREAM: firmware: scmi: transport: add "use_polling" parameter commit aad91d7fe93777307d4f5c184b70115912621c5e upstream. Add a new parameter to the transport layer's send_message() function, which is meant to allow it to perform the required configuration whenever the SCMI stack user (via the SCMI core layer) wishes to poll for the reply. For now, don't implement any logic related to this. Thus, no functional change. This commit is part of #102904. Signed-off-by: Laurentiu Mihalcea --- drivers/firmware/scmi/core.c | 4 ++-- drivers/firmware/scmi/mailbox.c | 3 ++- include/zephyr/drivers/firmware/scmi/transport.h | 13 ++++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/scmi/core.c b/drivers/firmware/scmi/core.c index 43b662b1c62d..9a32f3092f08 100644 --- a/drivers/firmware/scmi/core.c +++ b/drivers/firmware/scmi/core.c @@ -129,7 +129,7 @@ static int scmi_send_message_polling(struct scmi_protocol *proto, */ status = scmi_interrupt_enable(proto->tx, false); - ret = scmi_transport_send_message(proto->transport, proto->tx, msg); + ret = scmi_transport_send_message(proto->transport, proto->tx, msg, true); if (ret < 0) { goto cleanup; } @@ -182,7 +182,7 @@ static int scmi_send_message_interrupt(struct scmi_protocol *proto, return ret; } - ret = scmi_transport_send_message(proto->transport, proto->tx, msg); + ret = scmi_transport_send_message(proto->transport, proto->tx, msg, false); if (ret < 0) { LOG_ERR("failed to send message"); goto out_release_mutex; diff --git a/drivers/firmware/scmi/mailbox.c b/drivers/firmware/scmi/mailbox.c index 2f18ce89cdce..07cca15805f8 100644 --- a/drivers/firmware/scmi/mailbox.c +++ b/drivers/firmware/scmi/mailbox.c @@ -23,7 +23,8 @@ static void scmi_mbox_cb(const struct device *mbox, static int scmi_mbox_send_message(const struct device *transport, struct scmi_channel *chan, - struct scmi_message *msg) + struct scmi_message *msg, + bool use_polling) { struct scmi_mbox_channel *mbox_chan; int ret; diff --git a/include/zephyr/drivers/firmware/scmi/transport.h b/include/zephyr/drivers/firmware/scmi/transport.h index 088163006dbc..72c1752b919f 100644 --- a/include/zephyr/drivers/firmware/scmi/transport.h +++ b/include/zephyr/drivers/firmware/scmi/transport.h @@ -109,16 +109,22 @@ struct scmi_transport_api { * * Used to send a message to the platform over a given TX channel. * + * @note the transport driver is in no way allowed to overwrite + * the value of the \p use_polling parameter and must comply with + * it. + * * @param transport transport device * @param chan channel used to send the message * @param msg message to send + * @param use_polling true if polling should be enabled, false otherwise * * @retval 0 if successful * @retval <0 negative errno code if failure */ int (*send_message)(const struct device *transport, struct scmi_channel *chan, - struct scmi_message *msg); + struct scmi_message *msg, + bool use_polling); /** * @brief Prepare a channel for communication @@ -248,7 +254,8 @@ static inline int scmi_transport_setup_chan(const struct device *transport, */ static inline int scmi_transport_send_message(const struct device *transport, struct scmi_channel *chan, - struct scmi_message *msg) + struct scmi_message *msg, + bool use_polling) { const struct scmi_transport_api *api = (const struct scmi_transport_api *)transport->api; @@ -257,7 +264,7 @@ static inline int scmi_transport_send_message(const struct device *transport, return -ENOSYS; } - return api->send_message(transport, chan, msg); + return api->send_message(transport, chan, msg, use_polling); } /** From 4d139c523ce34b870a3c252fd7e1e7bf9a534123 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Wed, 28 Jan 2026 11:47:24 +0200 Subject: [PATCH 039/143] UPSTREAM: firmware: scmi: shmem: add "use_polling" parameter commit d9db1a95162c840a9fa5ae9632f27ce23e062b3f upstream. Add a new parameter to SHMEM's write_message() functon, which is meant to allow it to perform the required configuration whenever the SCMI stack (via the SCMI transport layer) wishes to poll for the reply. For now, don't implement any logic related to this. Thus, no functional change. This commit is part of #102904. Signed-off-by: Laurentiu Mihalcea --- drivers/firmware/scmi/mailbox.c | 2 +- drivers/firmware/scmi/shmem.c | 4 +++- include/zephyr/drivers/firmware/scmi/shmem.h | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/scmi/mailbox.c b/drivers/firmware/scmi/mailbox.c index 07cca15805f8..85c4248dab06 100644 --- a/drivers/firmware/scmi/mailbox.c +++ b/drivers/firmware/scmi/mailbox.c @@ -31,7 +31,7 @@ static int scmi_mbox_send_message(const struct device *transport, mbox_chan = chan->data; - ret = scmi_shmem_write_message(mbox_chan->shmem, msg); + ret = scmi_shmem_write_message(mbox_chan->shmem, msg, use_polling); if (ret < 0) { LOG_ERR("failed to write message to shmem: %d", ret); return ret; diff --git a/drivers/firmware/scmi/shmem.c b/drivers/firmware/scmi/shmem.c index 2bdb23465126..a48bd5ff0c83 100644 --- a/drivers/firmware/scmi/shmem.c +++ b/drivers/firmware/scmi/shmem.c @@ -110,7 +110,9 @@ int scmi_shmem_read_message(const struct device *shmem, struct scmi_message *msg return 0; } -int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *msg) +int scmi_shmem_write_message(const struct device *shmem, + struct scmi_message *msg, + bool use_polling) { struct scmi_shmem_layout *layout; struct scmi_shmem_data *data; diff --git a/include/zephyr/drivers/firmware/scmi/shmem.h b/include/zephyr/drivers/firmware/scmi/shmem.h index ec984a47bd77..ed0617ec29b4 100644 --- a/include/zephyr/drivers/firmware/scmi/shmem.h +++ b/include/zephyr/drivers/firmware/scmi/shmem.h @@ -43,12 +43,14 @@ struct scmi_message; * * @param shmem pointer to shmem device * @param msg message to write + * @param use_polling true if polling should be used, false otherwise * * @retval 0 if successful * @retval negative errno if failure */ int scmi_shmem_write_message(const struct device *shmem, - struct scmi_message *msg); + struct scmi_message *msg, + bool use_polling); /** * @brief Read a message from a SHMEM area From c8a062b13db5a449ecd7ee1c84534d5d217ed955 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Wed, 28 Jan 2026 11:52:19 +0200 Subject: [PATCH 040/143] UPSTREAM: firmware: scmi: move polling logic to transport layer commit 28575ad1c0b4ce500837ec10f8fc9b0f0824de51 upstream. So far, the SCMI core has been in charge of enabling/disabling the "poll for message reply" logic by directly using functions from MBOX/SHMEM. This behavior should be abstracted away by a simple transport layer send_message() function, with the SCMI core not having to know (or care) about which kind of transport is being used. To fix this problem, move the polling logic to the transport layer. Now, based on the value of the "use_polling" parameter propagated from the SCMI core, SHMEM will set/clear the interrupt-related bit from the flags portion of the SHMEM area. Note that now we only alter the state of the SHMEM flags with the MBOX interrupts being enabled at all times. This should be enough to signal the SCMI platform that the agent is not interested in receiving interrupts whenever polling is enabled. This commit is part of #102904. Signed-off-by: Laurentiu Mihalcea --- drivers/firmware/scmi/core.c | 36 --------------------------------- drivers/firmware/scmi/mailbox.c | 5 ----- drivers/firmware/scmi/shmem.c | 2 ++ 3 files changed, 2 insertions(+), 41 deletions(-) diff --git a/drivers/firmware/scmi/core.c b/drivers/firmware/scmi/core.c index 9a32f3092f08..cecfc1f13de1 100644 --- a/drivers/firmware/scmi/core.c +++ b/drivers/firmware/scmi/core.c @@ -8,7 +8,6 @@ #include #include #include -#include "mailbox.h" LOG_MODULE_REGISTER(scmi_core); @@ -87,33 +86,11 @@ static int scmi_core_setup_chan(const struct device *transport, return 0; } -static int scmi_interrupt_enable(struct scmi_channel *chan, bool enable) -{ - struct scmi_mbox_channel *mbox_chan; - struct mbox_dt_spec *tx_reply; - bool comp_int; - - mbox_chan = chan->data; - comp_int = enable ? SCMI_SHMEM_CHAN_FLAG_IRQ_BIT : 0; - - if (mbox_chan->tx_reply.dev) { - tx_reply = &mbox_chan->tx_reply; - } else { - tx_reply = &mbox_chan->tx; - } - - /* re-set completion interrupt */ - scmi_shmem_update_flags(mbox_chan->shmem, SCMI_SHMEM_CHAN_FLAG_IRQ_BIT, comp_int); - - return mbox_set_enabled_dt(tx_reply, enable); -} - static int scmi_send_message_polling(struct scmi_protocol *proto, struct scmi_message *msg, struct scmi_message *reply) { int ret; - int status; /* wait for channel to be free */ if (!k_is_pre_kernel() && k_mutex_lock(&proto->tx->lock, K_NO_WAIT)) { @@ -121,14 +98,6 @@ static int scmi_send_message_polling(struct scmi_protocol *proto, return -EBUSY; } - /* - * SCMI communication interrupt is enabled by default during setup_chan - * to support interrupt-driven communication. When using polling mode - * it must be disabled to avoid unnecessary interrupts and - * ensure proper polling behavior. - */ - status = scmi_interrupt_enable(proto->tx, false); - ret = scmi_transport_send_message(proto->transport, proto->tx, msg, true); if (ret < 0) { goto cleanup; @@ -153,11 +122,6 @@ static int scmi_send_message_polling(struct scmi_protocol *proto, } cleanup: - /* restore scmi interrupt enable status when disable it pass */ - if (status >= 0) { - scmi_interrupt_enable(proto->tx, true); - } - if (!k_is_pre_kernel()) { k_mutex_unlock(&proto->tx->lock); } diff --git a/drivers/firmware/scmi/mailbox.c b/drivers/firmware/scmi/mailbox.c index 85c4248dab06..e513dd1f323f 100644 --- a/drivers/firmware/scmi/mailbox.c +++ b/drivers/firmware/scmi/mailbox.c @@ -97,11 +97,6 @@ static int scmi_mbox_setup_chan(const struct device *transport, LOG_ERR("failed to enable tx reply dbell"); } - /* enable interrupt-based communication */ - scmi_shmem_update_flags(mbox_chan->shmem, - SCMI_SHMEM_CHAN_FLAG_IRQ_BIT, - SCMI_SHMEM_CHAN_FLAG_IRQ_BIT); - return 0; } diff --git a/drivers/firmware/scmi/shmem.c b/drivers/firmware/scmi/shmem.c index a48bd5ff0c83..c12423fc3489 100644 --- a/drivers/firmware/scmi/shmem.c +++ b/drivers/firmware/scmi/shmem.c @@ -151,6 +151,8 @@ int scmi_shmem_write_message(const struct device *shmem, return -EINVAL; } + layout->chan_flags = !use_polling ? SCMI_SHMEM_CHAN_FLAG_IRQ_BIT : 0; + /* done, mark channel as busy and proceed */ layout->chan_status &= ~SCMI_SHMEM_CHAN_STATUS_BUSY_BIT; From 671b5b33fe9120657d2990377ea90e0371bdd972 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Wed, 28 Jan 2026 13:26:19 +0200 Subject: [PATCH 041/143] UPSTREAM: firmware: scmi: core: merge send_message() logic commit c92c8ab4611c087d33d01d072d7cb85b43b5f609 upstream. Currently, scmi_send_message()'s logic is split across two functions, each of which handling either polling or interrupt-based reply waiting. Since the two functions are pretty much similar, merge them together in a single scmi_send_message() function, which is able to handle both wait startegies. Signed-off-by: Laurentiu Mihalcea --- drivers/firmware/scmi/core.c | 104 ++++++++++++----------------------- 1 file changed, 35 insertions(+), 69 deletions(-) diff --git a/drivers/firmware/scmi/core.c b/drivers/firmware/scmi/core.c index cecfc1f13de1..e852dd419903 100644 --- a/drivers/firmware/scmi/core.c +++ b/drivers/firmware/scmi/core.c @@ -86,107 +86,73 @@ static int scmi_core_setup_chan(const struct device *transport, return 0; } -static int scmi_send_message_polling(struct scmi_protocol *proto, - struct scmi_message *msg, - struct scmi_message *reply) +static int scmi_core_wait_reply(struct scmi_protocol *proto, bool use_polling) { - int ret; - - /* wait for channel to be free */ - if (!k_is_pre_kernel() && k_mutex_lock(&proto->tx->lock, K_NO_WAIT)) { - LOG_ERR("failed to acquire chan lock"); - return -EBUSY; + if (!use_polling) { + return k_sem_take(&proto->tx->sem, + K_USEC(CONFIG_ARM_SCMI_CHAN_SEM_TIMEOUT_USEC)); } - ret = scmi_transport_send_message(proto->transport, proto->tx, msg, true); - if (ret < 0) { - goto cleanup; - } - - /* no kernel primitives, we're forced to poll here. - * - * Cortex-M quirk: no interrupts at this point => no timer => - * no timeout mechanism => this can block the whole system. - * - * Polling mode repeatedly checks the chan_status field in share memory - * to detect whether the remote side have completed message processing - * - * TODO: is there a better way to handle this? - */ while (!scmi_transport_channel_is_free(proto->transport, proto->tx)) { } - ret = scmi_transport_read_message(proto->transport, proto->tx, reply); - if (ret < 0) { - return ret; - } - -cleanup: - if (!k_is_pre_kernel()) { - k_mutex_unlock(&proto->tx->lock); - } - - return ret; + return 0; } -static int scmi_send_message_interrupt(struct scmi_protocol *proto, - struct scmi_message *msg, - struct scmi_message *reply) +int scmi_send_message(struct scmi_protocol *proto, struct scmi_message *msg, + struct scmi_message *reply, bool use_polling) { - int ret = 0; + int ret; if (!proto->tx) { return -ENODEV; } - /* wait for channel to be free */ - ret = k_mutex_lock(&proto->tx->lock, K_USEC(SCMI_CHAN_LOCK_TIMEOUT_USEC)); - if (ret < 0) { - LOG_ERR("failed to acquire chan lock"); - return ret; + if (!proto->tx->ready) { + return -EINVAL; } - ret = scmi_transport_send_message(proto->transport, proto->tx, msg, false); + /* + * while in PRE_KERNEL, interrupt-based messaging is forbidden + * because of the lack of kernel primitives (i.e. semaphores) + * (and potentially even interrupts, based on the architecture). + * Because of this, we ignore the caller's request and forcibly + * enable polling. + */ + use_polling = k_is_pre_kernel() ? true : use_polling; + + if (!k_is_pre_kernel()) { + ret = k_mutex_lock(&proto->tx->lock, K_USEC(SCMI_CHAN_LOCK_TIMEOUT_USEC)); + if (ret < 0) { + LOG_ERR("failed to acquire TX channel lock: %d", ret); + return ret; + } + } + + ret = scmi_transport_send_message(proto->transport, proto->tx, msg, use_polling); if (ret < 0) { - LOG_ERR("failed to send message"); + LOG_ERR("failed to send message at transport layer: %d", ret); goto out_release_mutex; } - /* only one protocol instance can wait for a message reply at a time */ - ret = k_sem_take(&proto->tx->sem, K_USEC(CONFIG_ARM_SCMI_CHAN_SEM_TIMEOUT_USEC)); + ret = scmi_core_wait_reply(proto, use_polling); if (ret < 0) { - LOG_ERR("failed to wait for msg reply"); + LOG_ERR("failed to wait for message reply: %d", ret); goto out_release_mutex; } ret = scmi_transport_read_message(proto->transport, proto->tx, reply); if (ret < 0) { - LOG_ERR("failed to read reply"); + LOG_ERR("failed to read message reply: %d", ret); goto out_release_mutex; } out_release_mutex: - k_mutex_unlock(&proto->tx->lock); - - return ret; -} - -int scmi_send_message(struct scmi_protocol *proto, struct scmi_message *msg, - struct scmi_message *reply, bool use_polling) -{ - if (!proto->tx) { - return -ENODEV; - } - - if (!proto->tx->ready) { - return -EINVAL; + if (!k_is_pre_kernel()) { + k_mutex_unlock(&proto->tx->lock); } - if (use_polling) { - return scmi_send_message_polling(proto, msg, reply); - } else { - return scmi_send_message_interrupt(proto, msg, reply); - } + return ret; } static int scmi_core_protocol_negotiate(struct scmi_protocol *proto) From 2438d77403051e076300756c3eef161211b9c774 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Wed, 28 Jan 2026 17:09:12 +0200 Subject: [PATCH 042/143] UPSTREAM: firmware: scmi: replace k_is_pre_kernel() with hardcoded value commit f096f2a82df5bea7e0acd8ea637d8ab96147b716 upstream. As of commit c92c8ab4611c ("firmware: scmi: core: merge send_message() logic"), the SCMI core is able to figure out that it needs to use polling-based messaging whenever in PRE_KERNEL stage. Thus, consumers are no longer required to query the kernel's state via a `k_is_pre_kernel()` call and pass its value to `scmi_send_message()`. Furthermore, said commit also changes the semantic of the "use_polling" parameter. Now, for interrupt-based messaging, the core may choose to ignore the caller's request and use polling instead. Thus, replace all usages of `k_is_pre_kernel()` with a hardcoded value. Signed-off-by: Laurentiu Mihalcea --- drivers/firmware/scmi/clk.c | 30 ++++++------------------------ drivers/firmware/scmi/common.c | 6 +++--- drivers/firmware/scmi/pinctrl.c | 5 +---- drivers/firmware/scmi/power.c | 10 ++-------- drivers/firmware/scmi/system.c | 5 +---- 5 files changed, 13 insertions(+), 43 deletions(-) diff --git a/drivers/firmware/scmi/clk.c b/drivers/firmware/scmi/clk.c index 646a719a8ceb..6d86562d03a8 100644 --- a/drivers/firmware/scmi/clk.c +++ b/drivers/firmware/scmi/clk.c @@ -39,7 +39,6 @@ int scmi_clock_rate_get(struct scmi_protocol *proto, struct scmi_message msg, reply; int ret; struct scmi_clock_rate_set_reply reply_buffer; - bool use_polling; /* input validation */ if (!proto || !rate) { @@ -59,9 +58,7 @@ int scmi_clock_rate_get(struct scmi_protocol *proto, reply.len = sizeof(reply_buffer); reply.content = &reply_buffer; - use_polling = k_is_pre_kernel(); - - ret = scmi_send_message(proto, &msg, &reply, use_polling); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } @@ -79,7 +76,6 @@ int scmi_clock_rate_set(struct scmi_protocol *proto, struct scmi_clock_rate_conf { struct scmi_message msg, reply; int status, ret; - bool use_polling; /* input validation */ if (!proto || !cfg) { @@ -103,9 +99,7 @@ int scmi_clock_rate_set(struct scmi_protocol *proto, struct scmi_clock_rate_conf reply.len = sizeof(status); reply.content = &status; - use_polling = k_is_pre_kernel(); - - ret = scmi_send_message(proto, &msg, &reply, use_polling); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } @@ -118,7 +112,6 @@ int scmi_clock_parent_get(struct scmi_protocol *proto, uint32_t clk_id, uint32_t struct scmi_message msg, reply; int ret; struct scmi_clock_parent_get_reply reply_buffer; - bool use_polling; /* input validation */ if (!proto || !parent_id) { @@ -138,9 +131,7 @@ int scmi_clock_parent_get(struct scmi_protocol *proto, uint32_t clk_id, uint32_t reply.len = sizeof(reply_buffer); reply.content = &reply_buffer; - use_polling = k_is_pre_kernel(); - - ret = scmi_send_message(proto, &msg, &reply, use_polling); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } @@ -159,7 +150,6 @@ int scmi_clock_parent_set(struct scmi_protocol *proto, uint32_t clk_id, uint32_t struct scmi_clock_parent_config cfg = {.clk_id = clk_id, .parent_id = parent_id}; struct scmi_message msg, reply; int status, ret; - bool use_polling; /* input validation */ if (!proto) { @@ -179,9 +169,7 @@ int scmi_clock_parent_set(struct scmi_protocol *proto, uint32_t clk_id, uint32_t reply.len = sizeof(status); reply.content = &status; - use_polling = k_is_pre_kernel(); - - ret = scmi_send_message(proto, &msg, &reply, use_polling); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } @@ -194,7 +182,6 @@ int scmi_clock_config_set(struct scmi_protocol *proto, { struct scmi_message msg, reply; int status, ret; - bool use_polling; /* input validation */ if (!proto || !cfg) { @@ -229,9 +216,7 @@ int scmi_clock_config_set(struct scmi_protocol *proto, reply.len = sizeof(status); reply.content = &status; - use_polling = k_is_pre_kernel(); - - ret = scmi_send_message(proto, &msg, &reply, use_polling); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } @@ -244,7 +229,6 @@ int scmi_clock_protocol_attributes(struct scmi_protocol *proto, uint32_t *attrib struct scmi_message msg, reply; struct scmi_clock_attributes_reply reply_buffer; int ret; - bool use_polling; /* input validation */ if (!proto || !attributes) { @@ -265,9 +249,7 @@ int scmi_clock_protocol_attributes(struct scmi_protocol *proto, uint32_t *attrib reply.len = sizeof(reply_buffer); reply.content = &reply_buffer; - use_polling = k_is_pre_kernel(); - - ret = scmi_send_message(proto, &msg, &reply, use_polling); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } diff --git a/drivers/firmware/scmi/common.c b/drivers/firmware/scmi/common.c index 11ff71b90f70..da1a65ccdf03 100644 --- a/drivers/firmware/scmi/common.c +++ b/drivers/firmware/scmi/common.c @@ -65,7 +65,7 @@ int scmi_protocol_get_version(struct scmi_protocol *proto, uint32_t *version) reply.len = sizeof(reply_buffer); reply.content = &reply_buffer; - ret = scmi_send_message(proto, &msg, &reply, k_is_pre_kernel()); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } @@ -94,7 +94,7 @@ int scmi_protocol_attributes_get(struct scmi_protocol *proto, uint32_t *attribut reply.len = sizeof(reply_buffer); reply.content = &reply_buffer; - ret = scmi_send_message(proto, &msg, &reply, k_is_pre_kernel()); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } @@ -153,7 +153,7 @@ int scmi_protocol_version_negotiate(struct scmi_protocol *proto, uint32_t versio reply.len = sizeof(status); reply.content = &status; - ret = scmi_send_message(proto, &msg, &reply, k_is_pre_kernel()); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } diff --git a/drivers/firmware/scmi/pinctrl.c b/drivers/firmware/scmi/pinctrl.c index 22508c6d7bda..b135e8c553fb 100644 --- a/drivers/firmware/scmi/pinctrl.c +++ b/drivers/firmware/scmi/pinctrl.c @@ -16,7 +16,6 @@ int scmi_pinctrl_settings_configure(struct scmi_pinctrl_settings *settings) struct scmi_message msg, reply; uint32_t config_num; int32_t status, ret; - bool use_polling; proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_PINCTRL); @@ -53,9 +52,7 @@ int scmi_pinctrl_settings_configure(struct scmi_pinctrl_settings *settings) reply.len = sizeof(status); reply.content = &status; - use_polling = k_is_pre_kernel(); - - ret = scmi_send_message(proto, &msg, &reply, use_polling); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } diff --git a/drivers/firmware/scmi/power.c b/drivers/firmware/scmi/power.c index d1a7a869ba10..01c1e01a549d 100644 --- a/drivers/firmware/scmi/power.c +++ b/drivers/firmware/scmi/power.c @@ -22,7 +22,6 @@ int scmi_power_state_get(uint32_t domain_id, uint32_t *power_state) struct scmi_power_state_get_reply reply_buffer; struct scmi_message msg, reply; int ret; - bool use_polling; /* input validation */ if (!proto || !power_state) { @@ -42,9 +41,7 @@ int scmi_power_state_get(uint32_t domain_id, uint32_t *power_state) reply.len = sizeof(reply_buffer); reply.content = &reply_buffer; - use_polling = k_is_pre_kernel(); - - ret = scmi_send_message(proto, &msg, &reply, use_polling); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } @@ -63,7 +60,6 @@ int scmi_power_state_set(struct scmi_power_state_config *cfg) struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_POWER_DOMAIN); struct scmi_message msg, reply; int status, ret; - bool use_polling; /* input validation */ if (!proto || !cfg) { @@ -88,9 +84,7 @@ int scmi_power_state_set(struct scmi_power_state_config *cfg) reply.len = sizeof(status); reply.content = &status; - use_polling = k_is_pre_kernel(); - - ret = scmi_send_message(proto, &msg, &reply, use_polling); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } diff --git a/drivers/firmware/scmi/system.c b/drivers/firmware/scmi/system.c index 46eb9a23e0de..1bfed61b0f91 100644 --- a/drivers/firmware/scmi/system.c +++ b/drivers/firmware/scmi/system.c @@ -45,7 +45,6 @@ int scmi_system_power_state_set(struct scmi_system_power_state_config *cfg) struct scmi_message msg, reply; int32_t status; int ret; - bool use_polling; /* input validation */ if (!proto || !cfg) { @@ -65,9 +64,7 @@ int scmi_system_power_state_set(struct scmi_system_power_state_config *cfg) reply.len = sizeof(status); reply.content = &status; - use_polling = k_is_pre_kernel(); - - ret = scmi_send_message(proto, &msg, &reply, use_polling); + ret = scmi_send_message(proto, &msg, &reply, false); if (ret < 0) { return ret; } From dff64303b5c133692f113fd023c4b100baeb1e98 Mon Sep 17 00:00:00 2001 From: Soumya Tripathy Date: Tue, 17 Mar 2026 17:46:14 +0530 Subject: [PATCH 043/143] UPSTREAM: drivers: firmware: scmi: add polling_only support for transport commit a132036b2d86edee23b146a7451eeb4a6b81a5ac upstream. Add polling_only flag to scmi_channel structure to support transports that require polling mode (e.g., SMC transport which uses synchronous SMC/HVC calls without interrupt support). Signed-off-by: Soumya Tripathy --- drivers/firmware/scmi/core.c | 13 +++++++------ include/zephyr/drivers/firmware/scmi/transport.h | 6 ++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/scmi/core.c b/drivers/firmware/scmi/core.c index e852dd419903..ebcf68bdbc45 100644 --- a/drivers/firmware/scmi/core.c +++ b/drivers/firmware/scmi/core.c @@ -113,13 +113,14 @@ int scmi_send_message(struct scmi_protocol *proto, struct scmi_message *msg, } /* - * while in PRE_KERNEL, interrupt-based messaging is forbidden - * because of the lack of kernel primitives (i.e. semaphores) - * (and potentially even interrupts, based on the architecture). - * Because of this, we ignore the caller's request and forcibly - * enable polling. + * Force polling mode if: + * 1. Channel requires polling-only operation (e.g., SMC transport), OR + * 2. Running in PRE_KERNEL state where interrupt-based messaging is + * forbidden due to lack of kernel primitives (i.e. semaphores) + * and potentially even interrupts, based on the architecture. + * Otherwise, use the caller's requested polling mode. */ - use_polling = k_is_pre_kernel() ? true : use_polling; + use_polling = (proto->tx->polling_only || k_is_pre_kernel()) ? true : use_polling; if (!k_is_pre_kernel()) { ret = k_mutex_lock(&proto->tx->lock, K_USEC(SCMI_CHAN_LOCK_TIMEOUT_USEC)); diff --git a/include/zephyr/drivers/firmware/scmi/transport.h b/include/zephyr/drivers/firmware/scmi/transport.h index 72c1752b919f..c14dfc847a4c 100644 --- a/include/zephyr/drivers/firmware/scmi/transport.h +++ b/include/zephyr/drivers/firmware/scmi/transport.h @@ -79,6 +79,12 @@ struct scmi_channel { /** is the channel ready to be used by a protocol? */ bool ready; /** @endcond */ + /** + * indicates if the channel requires polling-only operation. + * When set to true, the channel cannot use interrupt-based + * messaging and must always poll for responses. + */ + bool polling_only; }; /** From 8b7ac5391be41d6a344ccbb2c3684abfc0be2d9a Mon Sep 17 00:00:00 2001 From: Soumya Tripathy Date: Tue, 17 Mar 2026 17:44:39 +0530 Subject: [PATCH 044/143] UPSTREAM: drivers: firmware: scmi: add SMC transport support commit 62bdd9a7e2951662eca201ed58c01bb36ad5994a upstream. Add support for SCMI transport layer using ARM SMC (Secure Monitor Call) as the doorbell mechanism with shared memory for data transfer. Communication is performed synchronously so polling is the only supported mode. The implementation follows the SCMI specification for SMC/HVC based transport. Signed-off-by: Soumya Tripathy --- drivers/firmware/scmi/CMakeLists.txt | 1 + drivers/firmware/scmi/Kconfig | 10 ++ drivers/firmware/scmi/smc.c | 131 ++++++++++++++++++++ dts/bindings/firmware/arm,scmi-smc.yaml | 60 +++++++++ include/zephyr/drivers/firmware/scmi/util.h | 14 ++- 5 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 drivers/firmware/scmi/smc.c create mode 100644 dts/bindings/firmware/arm,scmi-smc.yaml diff --git a/drivers/firmware/scmi/CMakeLists.txt b/drivers/firmware/scmi/CMakeLists.txt index 1118c34090a6..e136bc1bb1d7 100644 --- a/drivers/firmware/scmi/CMakeLists.txt +++ b/drivers/firmware/scmi/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_ARM_SCMI core.c common.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_MAILBOX_TRANSPORT mailbox.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SHMEM shmem.c) +zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SMC_TRANSPORT smc.c) # SCMI protocol helper files zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_CLK_HELPERS clk.c) diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index a1fb0ae55ba9..e52cb76b5fe9 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -20,6 +20,16 @@ config ARM_SCMI_MAILBOX_TRANSPORT Enable support for SCMI transport based on shared memory and doorbells. +config ARM_SCMI_SMC_TRANSPORT + bool "SCMI transport based on shared memory and SMC" + default y + depends on DT_HAS_ARM_SCMI_SMC_ENABLED + depends on ARM_SCMI_SHMEM + select ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS + help + Enable support for SCMI transport based on shared memory + and ARM SMC as doorbell. + config ARM_SCMI_PINCTRL_HELPERS bool "Helper functions for SCMI pinctrl protocol" default y diff --git a/drivers/firmware/scmi/smc.c b/drivers/firmware/scmi/smc.c new file mode 100644 index 000000000000..a10f84053a00 --- /dev/null +++ b/drivers/firmware/scmi/smc.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2026 Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(scmi_smc); + +#define DT_DRV_COMPAT arm_scmi_smc + +/* SMC channel structure */ +struct scmi_smc_channel { + /* SHMEM area bound to the channel */ + const struct device *shmem; + /* SMC function ID */ + uint32_t func_id; +}; + +static int scmi_smc_send_message(const struct device *transport, struct scmi_channel *chan, + struct scmi_message *msg, bool use_polling) +{ + struct scmi_smc_channel *smc_chan; + struct arm_smccc_res res; + int ret; + + smc_chan = chan->data; + + ret = scmi_shmem_write_message(smc_chan->shmem, msg, use_polling); + if (ret < 0) { + LOG_ERR("failed to write message to shmem: %d", ret); + return ret; + } + + /* Make SMC call to notify ATF */ + arm_smccc_smc(smc_chan->func_id, 0, 0, 0, 0, 0, 0, 0, &res); + + /* Check SMC result */ + if (res.a0 != 0) { + LOG_ERR("SMC call failed: 0x%lx", res.a0); + return -ENOTSUP; + } + + return 0; +} + +static int scmi_smc_read_message(const struct device *transport, struct scmi_channel *chan, + struct scmi_message *msg) +{ + struct scmi_smc_channel *smc_chan; + + smc_chan = chan->data; + + return scmi_shmem_read_message(smc_chan->shmem, msg); +} + +static bool scmi_smc_channel_is_free(const struct device *transport, struct scmi_channel *chan) +{ + struct scmi_smc_channel *smc_chan = chan->data; + + /* Per SCMI spec: Bit[0]=1 means FREE, Bit[0]=0 means BUSY */ + return scmi_shmem_channel_status(smc_chan->shmem) & SCMI_SHMEM_CHAN_STATUS_BUSY_BIT; +} + +static int scmi_smc_setup_chan(const struct device *transport, struct scmi_channel *chan, bool tx) +{ + if (!tx) { + return -ENOTSUP; + } + + /* SMC/HVC calls are synchronous and so doesn't support interrupts. */ + chan->polling_only = true; + + LOG_DBG("SMC channel setup complete (polling mode)"); + + return 0; +} + +static struct scmi_transport_api scmi_smc_api = { + .setup_chan = scmi_smc_setup_chan, + .send_message = scmi_smc_send_message, + .read_message = scmi_smc_read_message, + .channel_is_free = scmi_smc_channel_is_free, +}; + +/* + * SMC transport uses a single shared channel for all protocols. + * Unlike mailbox transport, SMC doesn't support per-protocol shmem areas + * or function IDs - all protocols use the same SMC call and shared memory. + */ + +/* + * Define the SMC transport with a single base channel shared by all protocols. + * This creates: + * 1) One scmi_smc_channel structure containing shmem device and func_id + * 2) One scmi_channel structure for the base protocol + * All protocols will reference this single shared channel. + */ + +#define SCMI_SMC_CHAN_NAME(proto, idx) CONCAT(SCMI_TRANSPORT_CHAN_NAME(proto, idx), _, priv) + +#define SCMI_SMC_SHMEM_BY_IDX(node_id, idx) \ + COND_CODE_1(DT_PROP_HAS_IDX(node_id, shmem, idx), \ + (DEVICE_DT_GET(DT_PROP_BY_IDX(node_id, shmem, idx))), \ + (NULL)) + +#define SCMI_SMC_BASE_CHAN_DEFINE_PRIV(node_id, proto, idx) \ + static struct scmi_smc_channel SCMI_SMC_CHAN_NAME(proto, idx) = { \ + .shmem = SCMI_SMC_SHMEM_BY_IDX(node_id, 0), \ + .func_id = DT_PROP(node_id, arm_smc_id), \ + }; + +#define SCMI_SMC_CHAN_DEFINE(node_id, proto, idx) \ + SCMI_SMC_BASE_CHAN_DEFINE_PRIV(node_id, proto, idx); \ + DT_SCMI_TRANSPORT_CHAN_DEFINE(node_id, idx, proto, &(SCMI_SMC_CHAN_NAME(proto, idx))); + +#define DT_INST_SCMI_BASE_CHAN_DEFINE(inst) \ + SCMI_SMC_CHAN_DEFINE(DT_INST(inst, DT_DRV_COMPAT), SCMI_PROTOCOL_BASE, 0) + +#define DT_INST_SCMI_SMC_DEFINE(inst, level, prio, api) \ + DT_INST_SCMI_BASE_CHAN_DEFINE(inst) \ + DT_INST_SCMI_TRANSPORT_DEFINE(inst, NULL, NULL, NULL, level, prio, api) + +DT_INST_SCMI_SMC_DEFINE(0, PRE_KERNEL_1, CONFIG_ARM_SCMI_TRANSPORT_INIT_PRIORITY, &scmi_smc_api); diff --git a/dts/bindings/firmware/arm,scmi-smc.yaml b/dts/bindings/firmware/arm,scmi-smc.yaml new file mode 100644 index 000000000000..38a19dce8266 --- /dev/null +++ b/dts/bindings/firmware/arm,scmi-smc.yaml @@ -0,0 +1,60 @@ +# Copyright 2026 Texas Instruments Incorporated. +# SPDX-License-Identifier: Apache-2.0 + +description: | + SCMI (System Control and Management Interface) with ARM SMC as doorbell + and SHMEM (shared memory) transport. + +compatible: "arm,scmi-smc" + +include: [base.yaml] + +properties: + shmem: + type: phandle + required: true + description: | + Phandle to node describing TX channel shared memory area. + This translates to a **single** SCMI transmit channel. + + arm,smc-id: + required: true + type: int + description: + SMC id required when using smc or hvc transports + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +examples: + - | + #include + + scmi_res0: memory@44611000 { + compatible = "arm,scmi-shmem"; + reg = <0x44611000 0x80>; + }; + + scmi { + compatible = "arm,scmi-smc"; + arm,smc-id = <0x82004000>; + shmem = <&scmi_shmem>; + + #address-cells = <1>; + #size-cells = <0>; + + scmi_pds: protocol@11 { + compatible = "arm,scmi-power"; + reg = <0x11>; + #power-domain-cells = <1>; + }; + + scmi_clk: protocol@14 { + compatible = "arm,scmi-clock"; + reg = <0x14>; + #clock-cells = <1>; + }; + }; diff --git a/include/zephyr/drivers/firmware/scmi/util.h b/include/zephyr/drivers/firmware/scmi/util.h index 56b6d3cbc69a..4c7193495823 100644 --- a/include/zephyr/drivers/firmware/scmi/util.h +++ b/include/zephyr/drivers/firmware/scmi/util.h @@ -54,9 +54,19 @@ */ #define DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, idx)\ DT_PROP_HAS_IDX(node_id, shmem, idx) -#else /* CONFIG_ARM_SCMI_MAILBOX_TRANSPORT */ +#elif CONFIG_ARM_SCMI_SMC_TRANSPORT +/** @brief Check if a protocol node has an associated channel + * + * For SMC transport, all protocols share the base channel. + * Return 0, to make all protocols fall back to base channel. + * + * @param node_id protocol node identifier + * @idx channel index + */ +#define DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, idx) 0 +#else #error "Transport with static channels needs to define HAS_CHAN macro" -#endif /* CONFIG_ARM_SCMI_MAILBOX_TRANSPORT */ +#endif #define SCMI_TRANSPORT_CHAN_NAME(proto, idx) CONCAT(scmi_channel_, proto, _, idx) From 9efaaeb4aeaa1e2a9b01f5b762381577a64162bd Mon Sep 17 00:00:00 2001 From: Soumya Tripathy Date: Wed, 18 Mar 2026 17:20:25 +0530 Subject: [PATCH 045/143] UPSTREAM: dts: ti: am62l: add SCMI power domain definitions for peripherals commit 78fd8c725cb553111b64bf1d903186bcd6a1550c upstream. Add scmi power domain nodes for am62l soc peripherals. Signed-off-by: Soumya Tripathy --- dts/vendor/ti/am62l_power_domains.dtsi | 101 +++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 dts/vendor/ti/am62l_power_domains.dtsi diff --git a/dts/vendor/ti/am62l_power_domains.dtsi b/dts/vendor/ti/am62l_power_domains.dtsi new file mode 100644 index 000000000000..10f9c3185ab4 --- /dev/null +++ b/dts/vendor/ti/am62l_power_domains.dtsi @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025 Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ +/ { + power-domains { + #address-cells = <1>; + #size-cells = <0>; + + main_uart0_pd: power-domain@59 { + compatible = "arm,scmi-power-domain"; + reg = <89>; + #power-domain-cells = <0>; + }; + + main_uart1_pd: power-domain@4d { + compatible = "arm,scmi-power-domain"; + reg = <77>; + #power-domain-cells = <0>; + }; + + main_uart2_pd: power-domain@4e { + compatible = "arm,scmi-power-domain"; + reg = <78>; + #power-domain-cells = <0>; + }; + + main_uart3_pd: power-domain@4f { + compatible = "arm,scmi-power-domain"; + reg = <79>; + #power-domain-cells = <0>; + }; + + main_uart4_pd: power-domain@50 { + compatible = "arm,scmi-power-domain"; + reg = <80>; + #power-domain-cells = <0>; + }; + + main_uart5_pd: power-domain@51 { + compatible = "arm,scmi-power-domain"; + reg = <81>; + #power-domain-cells = <0>; + }; + + main_uart6_pd: power-domain@52 { + compatible = "arm,scmi-power-domain"; + reg = <82>; + #power-domain-cells = <0>; + }; + + main_spi0_pd: power-domain@48 { + compatible = "arm,scmi-power-domain"; + reg = <72>; + #power-domain-cells = <0>; + }; + + main_spi1_pd: power-domain@49 { + compatible = "arm,scmi-power-domain"; + reg = <73>; + #power-domain-cells = <0>; + }; + + main_spi2_pd: power-domain@4a { + compatible = "arm,scmi-power-domain"; + reg = <74>; + #power-domain-cells = <0>; + }; + + main_spi3_pd: power-domain@4b { + compatible = "arm,scmi-power-domain"; + reg = <75>; + #power-domain-cells = <0>; + }; + + main_i2c0_pd: power-domain@35 { + compatible = "arm,scmi-power-domain"; + reg = <53>; + #power-domain-cells = <0>; + }; + + main_i2c1_pd: power-domain@36 { + compatible = "arm,scmi-power-domain"; + reg = <54>; + #power-domain-cells = <0>; + }; + + main_i2c2_pd: power-domain@37 { + compatible = "arm,scmi-power-domain"; + reg = <55>; + #power-domain-cells = <0>; + }; + + main_i2c3_pd: power-domain@38 { + compatible = "arm,scmi-power-domain"; + reg = <56>; + #power-domain-cells = <0>; + }; + }; +}; From 26465c480c4bfcf63ba321a638e0fbac2e58eaf5 Mon Sep 17 00:00:00 2001 From: Soumya Tripathy Date: Wed, 18 Mar 2026 17:24:21 +0530 Subject: [PATCH 046/143] UPSTREAM: dts: ti: am62l: wire SCMI clock and power domains for peripherals commit 63e01458e48a2cd60eefd3e0f0e8786fb27ff01c upstream. Wire SCMI clock and power domains nodes to all UART, I2C, and SPI peripheral nodes for am62l soc. Signed-off-by: Soumya Tripathy --- dts/vendor/ti/am62l-main.dtsi | 46 ++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/dts/vendor/ti/am62l-main.dtsi b/dts/vendor/ti/am62l-main.dtsi index c1e42304a49f..76ae248c6835 100644 --- a/dts/vendor/ti/am62l-main.dtsi +++ b/dts/vendor/ti/am62l-main.dtsi @@ -6,11 +6,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "am62l_power_domains.dtsi" + / { main_uart0: serial@2800000 { compatible = "ns16550"; reg = <0x02800000 0x100>; - clock-frequency = <48000000>; + clock-frequency = ; + clocks = <&scmi_clk 358>; + power-domains = <&main_uart0_pd>; current-speed = <115200>; reg-shift = <2>; status = "disabled"; @@ -19,7 +23,9 @@ main_uart1: serial@2810000 { compatible = "ns16550"; reg = <0x02810000 0x100>; - clock-frequency = <48000000>; + clock-frequency = ; + clocks = <&scmi_clk 312>; + power-domains = <&main_uart1_pd>; current-speed = <115200>; reg-shift = <2>; status = "disabled"; @@ -28,7 +34,9 @@ main_uart2: serial@2820000 { compatible = "ns16550"; reg = <0x02820000 0x100>; - clock-frequency = <48000000>; + clock-frequency = ; + clocks = <&scmi_clk 314>; + power-domains = <&main_uart2_pd>; current-speed = <115200>; reg-shift = <2>; status = "disabled"; @@ -37,7 +45,9 @@ main_uart3: serial@2830000 { compatible = "ns16550"; reg = <0x02830000 0x100>; - clock-frequency = <48000000>; + clock-frequency = ; + clocks = <&scmi_clk 316>; + power-domains = <&main_uart3_pd>; current-speed = <115200>; reg-shift = <2>; status = "disabled"; @@ -46,7 +56,9 @@ main_uart4: serial@2840000 { compatible = "ns16550"; reg = <0x02840000 0x100>; - clock-frequency = <48000000>; + clock-frequency = ; + clocks = <&scmi_clk 318>; + power-domains = <&main_uart4_pd>; current-speed = <115200>; reg-shift = <2>; status = "disabled"; @@ -55,7 +67,9 @@ main_uart5: serial@2850000 { compatible = "ns16550"; reg = <0x02850000 0x100>; - clock-frequency = <48000000>; + clock-frequency = ; + clocks = <&scmi_clk 320>; + power-domains = <&main_uart5_pd>; current-speed = <115200>; reg-shift = <2>; status = "disabled"; @@ -64,7 +78,9 @@ main_uart6: serial@2860000 { compatible = "ns16550"; reg = <0x02860000 0x100>; - clock-frequency = <48000000>; + clock-frequency = ; + clocks = <&scmi_clk 322>; + power-domains = <&main_uart6_pd>; current-speed = <115200>; reg-shift = <2>; status = "disabled"; @@ -74,6 +90,8 @@ compatible = "ti,omap-i2c"; reg = <0x20000000 0x100>; clock-frequency = <100000>; + clocks = <&scmi_clk 246>; + power-domains = <&main_i2c0_pd>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -83,6 +101,8 @@ compatible = "ti,omap-i2c"; reg = <0x20010000 0x100>; clock-frequency = <100000>; + clocks = <&scmi_clk 250>; + power-domains = <&main_i2c1_pd>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -92,6 +112,8 @@ compatible = "ti,omap-i2c"; reg = <0x20020000 0x100>; clock-frequency = <100000>; + clocks = <&scmi_clk 254>; + power-domains = <&main_i2c2_pd>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -101,6 +123,8 @@ compatible = "ti,omap-i2c"; reg = <0x20030000 0x100>; clock-frequency = <100000>; + clocks = <&scmi_clk 258>; + power-domains = <&main_i2c3_pd>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; @@ -232,7 +256,7 @@ interrupt-parent = <&gic>; #address-cells = <1>; #size-cells = <0>; - power-domains = <&scmi_pds 72>; + power-domains = <&main_spi0_pd>; clock-frequency = ; clocks = <&scmi_clk 299>; ti,spi-num-cs = <4>; @@ -246,7 +270,7 @@ interrupt-parent = <&gic>; #address-cells = <1>; #size-cells = <0>; - power-domains = <&scmi_pds 73>; + power-domains = <&main_spi1_pd>; clock-frequency = ; clocks = <&scmi_clk 302>; ti,spi-num-cs = <4>; @@ -260,7 +284,7 @@ interrupt-parent = <&gic>; #address-cells = <1>; #size-cells = <0>; - power-domains = <&scmi_pds 74>; + power-domains = <&main_spi2_pd>; clock-frequency = ; clocks = <&scmi_clk 305>; ti,spi-num-cs = <4>; @@ -274,7 +298,7 @@ interrupt-parent = <&gic>; #address-cells = <1>; #size-cells = <0>; - power-domains = <&scmi_pds 75>; + power-domains = <&main_spi3_pd>; clock-frequency = ; clocks = <&scmi_clk 308>; ti,spi-num-cs = <4>; From 7bb7bc830d2cd60b161ab056c0935e2e2e7554c1 Mon Sep 17 00:00:00 2001 From: Soumya Tripathy Date: Wed, 18 Mar 2026 17:27:51 +0530 Subject: [PATCH 047/143] UPSTREAM: dts: ti: am62l: increase SCMI shared memory size commit b12ee59db8576f522a34d119e7099df1767ceb28 upstream. Increase SCMI shared memory region from 256 bytes to 4KB to support larger SCMI message payloads required for clock and power domain operations. Signed-off-by: Soumya Tripathy --- dts/arm64/ti/am62l3_a53.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dts/arm64/ti/am62l3_a53.dtsi b/dts/arm64/ti/am62l3_a53.dtsi index fa450878fc87..e72c0581db99 100644 --- a/dts/arm64/ti/am62l3_a53.dtsi +++ b/dts/arm64/ti/am62l3_a53.dtsi @@ -71,7 +71,7 @@ scmi_shmem: sram@0 { compatible = "arm,scmi-shmem"; - reg = <0x0 0x100>; + reg = <0x0 0x1000>; }; }; From 79a8b0c525706ea64e571403e3db71d83ea01b07 Mon Sep 17 00:00:00 2001 From: Soumya Tripathy Date: Wed, 18 Mar 2026 17:29:23 +0530 Subject: [PATCH 048/143] UPSTREAM: boards: ti: am62l_evm: enable SCMI clock and power domain support commit 59c1748c4d90dd3ef52531a06523b0397ea2b9d1 upstream. Enable SCMI configs for AM62L evm to enable clock control and power domain management through the SCMI protocol. Signed-off-by: Soumya Tripathy --- boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig b/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig index fb49deec39fc..29118e123ef8 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig @@ -20,6 +20,7 @@ CONFIG_AARCH64_IMAGE_HEADER=y CONFIG_ARMV8_A_NS=y CONFIG_ARM64_VA_BITS_36=y CONFIG_ARM64_PA_BITS_36=y +CONFIG_ARM_SCMI=y # Cache Options CONFIG_CACHE_MANAGEMENT=y @@ -30,3 +31,10 @@ CONFIG_ICACHE_LINE_SIZE_DETECT=y CONFIG_SMP=y CONFIG_PM_CPU_OPS=y CONFIG_MP_MAX_NUM_CPUS=2 + +# Clock Control +CONFIG_CLOCK_CONTROL=y + +# Power Domain +CONFIG_PM_DEVICE=y +CONFIG_POWER_DOMAIN=y From 907b8f1e76c758b6dc7cc3a2a0d43997ae61c386 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Thu, 12 Feb 2026 14:26:07 +0200 Subject: [PATCH 049/143] UPSTREAM: firmware: scmi: clk: implement CLOCK_ATTRIBUTES command commit 8139eb2e1feb9a677a104daab4e9baa6560302d4 upstream. Add function implementing the CLOCK_ATTRIBUTES command. Signed-off-by: Laurentiu Mihalcea --- drivers/firmware/scmi/clk.c | 31 ++++++++++++++++++++ include/zephyr/drivers/firmware/scmi/clk.h | 34 ++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/drivers/firmware/scmi/clk.c b/drivers/firmware/scmi/clk.c index 6d86562d03a8..02375dee250b 100644 --- a/drivers/firmware/scmi/clk.c +++ b/drivers/firmware/scmi/clk.c @@ -262,3 +262,34 @@ int scmi_clock_protocol_attributes(struct scmi_protocol *proto, uint32_t *attrib return 0; } + +int scmi_clock_attributes(struct scmi_protocol *proto, uint32_t clk_id, + struct scmi_clock_attributes *attributes) +{ + struct scmi_message msg, reply; + int ret; + + if (!proto || !attributes) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_CLOCK) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_CLK_MSG_CLOCK_ATTRIBUTES, + SCMI_COMMAND, proto->id, 0x0); + msg.len = sizeof(clk_id); + msg.content = &clk_id; + + reply.hdr = msg.hdr; + reply.len = sizeof(*attributes); + reply.content = attributes; + + ret = scmi_send_message(proto, &msg, &reply, false); + if (ret < 0) { + return ret; + } + + return scmi_status_to_errno(attributes->status); +} diff --git a/include/zephyr/drivers/firmware/scmi/clk.h b/include/zephyr/drivers/firmware/scmi/clk.h index 2554a9f2ac50..2b1ad31051e9 100644 --- a/include/zephyr/drivers/firmware/scmi/clk.h +++ b/include/zephyr/drivers/firmware/scmi/clk.h @@ -35,6 +35,12 @@ #define SCMI_CLK_PROTOCOL_SUPPORTED_VERSION 0x30000 +/** clock name length (short version) */ +#define SCMI_CLK_NAME_LEN 16 + +/** get the clock's enabled status based on given attributes */ +#define SCMI_CLK_ENABLED(attributes) ((attributes) & BIT(0)) + /** * @struct scmi_clock_config * @@ -59,6 +65,22 @@ struct scmi_clock_rate_config { uint32_t rate[2]; }; +/** + * @struct scmi_clock_attributes + * + * @brief Describes the content of the CLOCK_ATTRIBUTES command reply + */ +struct scmi_clock_attributes { + /** reply status */ + int32_t status; + /** clock attributes */ + uint32_t attributes; + /** clock name */ + uint8_t clock_name[SCMI_CLK_NAME_LEN]; + /** clock enable delay incurred by platform */ + uint32_t clock_enable_delay; +} __packed; + /** * @brief Clock protocol command message IDs */ @@ -157,6 +179,18 @@ int scmi_clock_parent_get(struct scmi_protocol *proto, uint32_t clk_id, uint32_t */ int scmi_clock_parent_set(struct scmi_protocol *proto, uint32_t clk_id, uint32_t parent_id); +/** + * @brief Send the CLOCK_ATTRIBUTES command and get its reply + * + * @param proto pointer to SCMI clock protocol data + * @param clk_id ID of the clock for which the query is done + * @param attributes clock attributes returned by the command + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_clock_attributes(struct scmi_protocol *proto, uint32_t clk_id, + struct scmi_clock_attributes *attributes); /** * @} */ From 4cafdd1fea0fa58479cc3cb3f6da44ccca19be05 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Tue, 10 Mar 2026 15:53:19 +0200 Subject: [PATCH 050/143] UPSTREAM: firmware: scmi: add support for shell commands commit 59f1ebdccf67cfa185ba9e1bb67094f5765fc628 upstream. Add initial support for SCMI shell commands. For now, this only includes clock protocol-related commands. Other SCMI protocols will be added incrementally. Part of #103633. Signed-off-by: Laurentiu Mihalcea --- drivers/firmware/scmi/CMakeLists.txt | 1 + drivers/firmware/scmi/Kconfig | 1 + drivers/firmware/scmi/shell/CMakeLists.txt | 11 + drivers/firmware/scmi/shell/Kconfig | 8 + drivers/firmware/scmi/shell/clk.c | 290 +++++++++++++++++++++ drivers/firmware/scmi/shell/core.c | 12 + 6 files changed, 323 insertions(+) create mode 100644 drivers/firmware/scmi/shell/CMakeLists.txt create mode 100644 drivers/firmware/scmi/shell/Kconfig create mode 100644 drivers/firmware/scmi/shell/clk.c create mode 100644 drivers/firmware/scmi/shell/core.c diff --git a/drivers/firmware/scmi/CMakeLists.txt b/drivers/firmware/scmi/CMakeLists.txt index e136bc1bb1d7..f349587d8285 100644 --- a/drivers/firmware/scmi/CMakeLists.txt +++ b/drivers/firmware/scmi/CMakeLists.txt @@ -16,3 +16,4 @@ zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_REBOOT reboot.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SYSTEM system.c) add_subdirectory_ifdef(CONFIG_ARM_SCMI nxp) +add_subdirectory_ifdef(CONFIG_ARM_SCMI_SHELL shell) diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index e52cb76b5fe9..4fc187e24203 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -97,5 +97,6 @@ config ARM_SCMI_CHAN_SEM_TIMEOUT_USEC scmi platform. source "drivers/firmware/scmi/nxp/Kconfig" +source "drivers/firmware/scmi/shell/Kconfig" endif # ARM_SCMI diff --git a/drivers/firmware/scmi/shell/CMakeLists.txt b/drivers/firmware/scmi/shell/CMakeLists.txt new file mode 100644 index 000000000000..dd5f9fc5ea1a --- /dev/null +++ b/drivers/firmware/scmi/shell/CMakeLists.txt @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Dependency on CONFIG_ARM_SCMI_SHELL implied by the root SCMI +# CMakeLists.txt, which includes this conditionally. +zephyr_library() + +# core module - implements root scmi command +zephyr_library_sources(core.c) + +# protocol module - implements protocol-related sub-commands +zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_CLK_HELPERS clk.c) diff --git a/drivers/firmware/scmi/shell/Kconfig b/drivers/firmware/scmi/shell/Kconfig new file mode 100644 index 000000000000..bf84d532d22e --- /dev/null +++ b/drivers/firmware/scmi/shell/Kconfig @@ -0,0 +1,8 @@ +# Copyright 2026 NXP +# SPDX-License-Identifier: Apache-2.0 + +config ARM_SCMI_SHELL + bool "ARM SCMI shell commands" + select SHELL + help + Enable support for the ARM SCMI shell commands. diff --git a/drivers/firmware/scmi/shell/clk.c b/drivers/firmware/scmi/shell/clk.c new file mode 100644 index 000000000000..575f9311fe1a --- /dev/null +++ b/drivers/firmware/scmi/shell/clk.c @@ -0,0 +1,290 @@ +/* + * Copyright 2026 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define SCMI_PROTOCOL_DECLARE(id) extern struct scmi_protocol SCMI_PROTOCOL_NAME(id); + +struct clk_info { + bool enabled; + uint32_t rate; + uint32_t parent_id; + char name[SCMI_CLK_NAME_LEN]; + char parent_name[SCMI_CLK_NAME_LEN]; +}; + +SCMI_PROTOCOL_DECLARE(SCMI_PROTOCOL_CLOCK); +static struct scmi_protocol *_proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_CLOCK); + +static int clk_get_info(const struct shell *sh, uint32_t clk_id, struct clk_info *info) +{ + struct scmi_clock_attributes attributes; + int ret; + + memset(info, 0, sizeof(*info)); + + ret = scmi_clock_attributes(_proto, clk_id, &attributes); + if (ret) { + return ret; + } + + memcpy(info->name, attributes.clock_name, SCMI_CLK_NAME_LEN); + info->enabled = SCMI_CLK_ENABLED(attributes.attributes); + + ret = scmi_clock_rate_get(_proto, clk_id, &info->rate); + if (ret) { + shell_error(sh, "failed to query clk %d rate: %d", clk_id, ret); + return ret; + } + + /* parent information is optional, thus the error suppression */ + ret = scmi_clock_parent_get(_proto, clk_id, &info->parent_id); + if (ret) { + return 0; + } + + /* no error suppression - parent clock ID should be a valid one */ + ret = scmi_clock_attributes(_proto, info->parent_id, &attributes); + if (ret) { + shell_error(sh, + "failed to query parent %d attributes: %d", + info->parent_id, ret); + return ret; + } + + memcpy(info->parent_name, attributes.clock_name, SCMI_CLK_NAME_LEN); + + return 0; +} + +static int get_clk_id(char *str, uint32_t *clk_id) +{ + uint32_t attributes; + char *endptr; + int ret; + + *clk_id = strtoul(str, &endptr, 0); + if (*endptr != '\0') { + return -EINVAL; + } + + ret = scmi_clock_protocol_attributes(_proto, &attributes); + if (ret) { + return ret; + } + + if (*clk_id >= SCMI_CLK_ATTRIBUTES_CLK_NUM(attributes)) { + return -ERANGE; + } + + return 0; +} + +static int cmd_clk_version(const struct shell *sh, size_t argc, char **argv) +{ + uint32_t version; + int ret; + + ret = scmi_protocol_get_version(_proto, &version); + if (ret) { + shell_error(sh, "failed to query protocol version: %d", ret); + return ret; + } + + shell_print(sh, "Clock protocol version: 0x%x", version); + + return 0; +} + +static int cmd_clk_summary(const struct shell *sh, size_t argc, char **argv) +{ + uint32_t clk_num, attributes; + struct clk_info info; + int ret, i; + + ret = scmi_clock_protocol_attributes(_proto, &attributes); + if (ret) { + shell_error(sh, "Failed to query protocol attributes: %d", ret); + return ret; + } + + clk_num = SCMI_CLK_ATTRIBUTES_CLK_NUM(attributes); + + shell_print(sh, + "+----------------------------------------------------------------------+"); + shell_print(sh, + "| ID | Name | Enabled | Rate(Hz) | Parent |"); + shell_print(sh, + "+----------------------------------------------------------------------+"); + + for (i = 0; i < clk_num; i++) { + ret = clk_get_info(sh, i, &info); + if (ret) { + continue; + } + + shell_print(sh, "|%3d | %16s | %c |%12u | %16s |", + i, info.name, + info.enabled ? 'Y' : 'N', + info.rate, info.parent_name[0] ? info.parent_name : "N/A"); + + shell_print(sh, + "+----------------------------------------------------------------------+"); + } + + return 0; +} + +static int cmd_clk_info(const struct shell *sh, size_t argc, char **argv) +{ + struct clk_info info; + int clk_id, ret; + + ret = get_clk_id(argv[1], &clk_id); + if (ret) { + shell_error(sh, "Failed to fetch clock ID: %d", ret); + return ret; + } + + ret = clk_get_info(sh, clk_id, &info); + if (ret) { + shell_error(sh, "Failed to query clk %d info: %d", clk_id, ret); + return ret; + } + + shell_print(sh, "Name: %s", info.name); + shell_print(sh, "Enabled status: %c", info.enabled ? 'Y' : 'N'); + shell_print(sh, "Rate (Hz): %u", info.rate); + + /* TODO: this needs to be replaced with the list of all possible parents */ + if (info.parent_name[0]) { + shell_print(sh, "Parent: %s [%d]", info.parent_name, info.parent_id); + } else { + shell_print(sh, "Parent: N/A"); + } + + return 0; +} + +static int cmd_clk_set_enabled(const struct shell *sh, size_t argc, char **argv) +{ + struct scmi_clock_config cfg = { 0 }; + uint32_t clk_id; + bool enable; + int ret; + + if (!strcmp(argv[2], "on")) { + enable = true; + } else if (!strcmp(argv[2], "off")) { + enable = false; + } else { + shell_error(sh, "Second parameter should be either \"on\" or \"off\""); + return -EINVAL; + } + + ret = get_clk_id(argv[1], &clk_id); + if (ret) { + shell_error(sh, "Failed to fetch clock ID: %d", ret); + return ret; + } + + cfg.attributes = SCMI_CLK_CONFIG_ENABLE_DISABLE(enable); + cfg.clk_id = clk_id; + + /* negative return code may be normal here (e.g. clock is not gateable) */ + ret = scmi_clock_config_set(_proto, &cfg); + if (ret) { + shell_error(sh, "Unable to enable/disable clock %d", clk_id); + return ret; + } + + return 0; +} + +static int cmd_clk_set_rate(const struct shell *sh, size_t argc, char **argv) +{ + struct scmi_clock_rate_config cfg = { 0 }; + uint32_t clk_id, rate; + char *endptr; + int ret; + + ret = get_clk_id(argv[1], &clk_id); + if (ret) { + shell_error(sh, "Failed to parse clock ID: %d", ret); + return ret; + } + + rate = strtoul(argv[2], &endptr, 0); + if (*endptr != '\0') { + shell_error(sh, "Failed to parse rate"); + return -EINVAL; + } + + cfg.clk_id = clk_id; + cfg.rate[0] = rate; + + /* negative return code may be normal (e.g. no support for rate change) */ + ret = scmi_clock_rate_set(_proto, &cfg); + if (ret) { + shell_error(sh, "Unable to change rate for clock %d", clk_id); + return ret; + } + + return 0; +} + +static int cmd_clk_set_parent(const struct shell *sh, size_t argc, char **argv) +{ + uint32_t clk_id, parent_id; + int ret; + + ret = get_clk_id(argv[1], &clk_id); + if (ret) { + shell_error(sh, "Failed to parse clock ID: %d", ret); + return ret; + } + + ret = get_clk_id(argv[2], &parent_id); + if (ret) { + shell_error(sh, "Failed to parse parent clock ID: %d", ret); + return ret; + } + + ret = scmi_clock_parent_set(_proto, clk_id, parent_id); + if (ret) { + shell_error(sh, "Unable to set clock %d parent to %d", clk_id, parent_id); + return ret; + } + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(clk_cmds, + SHELL_CMD(version, NULL, + SHELL_HELP("get protocol version", ""), + cmd_clk_version), + SHELL_CMD(summary, NULL, + SHELL_HELP("get clock tree summary", ""), + cmd_clk_summary), + SHELL_CMD_ARG(info, NULL, + SHELL_HELP("get detailed clock information", ""), + cmd_clk_info, 2, 0), + SHELL_CMD_ARG(set-enabled, NULL, + SHELL_HELP("enable/disable a clock", " on|off"), + cmd_clk_set_enabled, 3, 0), + SHELL_CMD_ARG(set-rate, NULL, + SHELL_HELP("set a clock's rate (in Hz)", " "), + cmd_clk_set_rate, 3, 0), + SHELL_CMD_ARG(set-parent, NULL, + SHELL_HELP("set a clock's parent", " "), + cmd_clk_set_parent, 3, 0), + SHELL_SUBCMD_SET_END +); + +SHELL_SUBCMD_ADD((scmi), clk, &clk_cmds, "Clock protocol commands", NULL, 0, 0); diff --git a/drivers/firmware/scmi/shell/core.c b/drivers/firmware/scmi/shell/core.c new file mode 100644 index 000000000000..c1846fdc18b5 --- /dev/null +++ b/drivers/firmware/scmi/shell/core.c @@ -0,0 +1,12 @@ +/* + * Copyright 2026 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/* filled in by each of the protocol modules */ +SHELL_SUBCMD_SET_CREATE(scmi_cmds, (scmi)); + +SHELL_CMD_REGISTER(scmi, &scmi_cmds, "ARM SCMI commands", NULL); From 17f00f2f88c57140b9d6c6d4b75847a797b796cb Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Mon, 16 Feb 2026 13:06:51 +0200 Subject: [PATCH 051/143] UPSTREAM: samples: drivers: add SCMI sample commit e57cbd607edadde47c565cbd32ef237a92e7a320 upstream. Add sample for ARM's SCMI. This is part of #103633. Signed-off-by: Laurentiu Mihalcea --- samples/drivers/firmware/index.rst | 5 ++ samples/drivers/firmware/scmi/CMakeLists.txt | 8 +++ samples/drivers/firmware/scmi/README.rst | 70 ++++++++++++++++++++ samples/drivers/firmware/scmi/prj.conf | 2 + samples/drivers/firmware/scmi/sample.yaml | 11 +++ samples/drivers/firmware/scmi/src/main.c | 10 +++ 6 files changed, 106 insertions(+) create mode 100644 samples/drivers/firmware/index.rst create mode 100644 samples/drivers/firmware/scmi/CMakeLists.txt create mode 100644 samples/drivers/firmware/scmi/README.rst create mode 100644 samples/drivers/firmware/scmi/prj.conf create mode 100644 samples/drivers/firmware/scmi/sample.yaml create mode 100644 samples/drivers/firmware/scmi/src/main.c diff --git a/samples/drivers/firmware/index.rst b/samples/drivers/firmware/index.rst new file mode 100644 index 000000000000..39f4b5c79713 --- /dev/null +++ b/samples/drivers/firmware/index.rst @@ -0,0 +1,5 @@ +.. zephyr:code-sample-category:: firmware + :name: Firmware Samples + :show-listing: + + Samples that demonstrate various firmware-specific features. diff --git a/samples/drivers/firmware/scmi/CMakeLists.txt b/samples/drivers/firmware/scmi/CMakeLists.txt new file mode 100644 index 000000000000..620922e6d94d --- /dev/null +++ b/samples/drivers/firmware/scmi/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(scmi) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/drivers/firmware/scmi/README.rst b/samples/drivers/firmware/scmi/README.rst new file mode 100644 index 000000000000..dc6fea33b027 --- /dev/null +++ b/samples/drivers/firmware/scmi/README.rst @@ -0,0 +1,70 @@ +.. zephyr:code-sample:: scmi + :name: SCMI Platform Interaction + + Interact with the SCMI platform using various protocols. + +Overview +******** + +This is a simple demo, which allows an SCMI agent running Zephyr to print information about and +configure parts of the system by interacting with the SCMI platform using various protocols. + +Building +******** + +This demo can be built as follows: + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/firmware/scmi + :board: imx95_evk/mimx9596/m7 + :goals: build + +Sample Output +============= + +.. code-block:: console + + uart:~$ scmi + scmi - ARM SCMI commands + Subcommands: + clk : Clock protocol commands + uart:~$ scmi clk + clk - Clock protocol commands + Subcommands: + version : get protocol version + Usage: version + summary : get clock tree summary + Usage: summary + info : get detailed clock information + Usage: info + set-enabled : enable/disable a clock + Usage: set-enabled on|off + set-rate : set a clock's rate (in Hz) + Usage: set-rate + set-parent : set a clock's parent + Usage: set-parent + uart:~$ scmi clk summary + +----------------------------------------------------------------------+ + | ID | Name | Enabled | Rate(Hz) | Parent | + +----------------------------------------------------------------------+ + | 0 | ext | Y | 25000000 | ext_gpr_sel | + +----------------------------------------------------------------------+ + | 1 | osc32k | Y | 32768 | N/A | + +----------------------------------------------------------------------+ + | 2 | osc24m | Y | 24000000 | N/A | + +----------------------------------------------------------------------+ + | 3 | fro | Y | 256000000 | N/A | + +----------------------------------------------------------------------+ + | 4 | syspll1_vco | Y | 4000000000 | N/A | + +----------------------------------------------------------------------+ + | 5 | syspll1_pfd0_un | Y | 1000000000 | syspll1_vco | + +----------------------------------------------------------------------+ + | 6 | syspll1_pfd0 | Y | 1000000000 | syspll1_pfd0_un | + +----------------------------------------------------------------------+ + | 7 | syspll1_pfd0_di | Y | 500000000 | syspll1_pfd0_un | + +----------------------------------------------------------------------+ + uart:~$ scmi clk info 50 + Name: lpspi2 + Enabled status: Y + Rate (Hz): 50000000 + Parent: syspll1_pfd1_di [10] diff --git a/samples/drivers/firmware/scmi/prj.conf b/samples/drivers/firmware/scmi/prj.conf new file mode 100644 index 000000000000..9e26bdec1078 --- /dev/null +++ b/samples/drivers/firmware/scmi/prj.conf @@ -0,0 +1,2 @@ +CONFIG_ARM_SCMI_SHELL=y +CONFIG_BOOT_BANNER=n diff --git a/samples/drivers/firmware/scmi/sample.yaml b/samples/drivers/firmware/scmi/sample.yaml new file mode 100644 index 000000000000..49a3ecf18457 --- /dev/null +++ b/samples/drivers/firmware/scmi/sample.yaml @@ -0,0 +1,11 @@ +sample: + name: scmi + description: This sample demonstrates the interaction between an agent + running Zephyr and the platform via SCMI. +tests: + sample.drivers.firmware.scmi: + build_only: true + filter: CONFIG_ARM_SCMI + integration_platforms: + - imx95_evk/mimx9596/m7 + - imx95_evk/mimx9596/a55 diff --git a/samples/drivers/firmware/scmi/src/main.c b/samples/drivers/firmware/scmi/src/main.c new file mode 100644 index 000000000000..ead06e898dff --- /dev/null +++ b/samples/drivers/firmware/scmi/src/main.c @@ -0,0 +1,10 @@ +/* + * Copyright 2026 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +int main(void) +{ + return 0; +} From ba79b0756e2615d973a1364a2a3894a893930edb Mon Sep 17 00:00:00 2001 From: James Roy Date: Mon, 3 Nov 2025 16:59:08 +0800 Subject: [PATCH 052/143] UPSTREAM: edtlib: binding: Add a examples keyword commit 44106ac05f4c939fc703b807918e5426941e8af0 upstream. Add an `examples` keyword to the binding to provide a minimal example node for the binding. Signed-off-by: James Roy --- doc/build/dts/bindings-syntax.rst | 32 +++++++++++++++++++ doc/build/dts/bindings-upstream.rst | 20 ++++++++++++ .../src/devicetree/edtlib.py | 18 ++++++++++- .../tests/test-bindings/defaults.yaml | 16 ++++++++++ .../python-devicetree/tests/test_edtlib.py | 17 ++++++++++ 5 files changed, 102 insertions(+), 1 deletion(-) diff --git a/doc/build/dts/bindings-syntax.rst b/doc/build/dts/bindings-syntax.rst index 4c2ccf32a718..b30a63d6a216 100644 --- a/doc/build/dts/bindings-syntax.rst +++ b/doc/build/dts/bindings-syntax.rst @@ -62,6 +62,13 @@ like this: # bindings. on-bus: spi + examples: + # You can put a sample node here showing how to use the binding. + # - | + # ... + # or + # - ... + foo-cells: # "Specifier" cell names for the 'foo' domain go here; example 'foo' # values are 'gpio', 'pwm', and 'dma'. See below for more information. @@ -678,6 +685,31 @@ Only ``sensor@79`` can have a ``use-clock-stretching`` property. The bus-sensitive logic ignores :file:`manufacturer,sensor-i2c.yaml` when searching for a binding for ``sensor@0``. +.. _dt-bindings-examples: + +Examples +******** + +If you feel you want to provide a minimal example for your binding, you can use +it like this: + +.. code-block:: yaml + + description: ... + + properties: + ... + + examples: + - | + leds { + compatible = "gpio-leds"; + + uled: led { + gpios = <&gpioe 12 GPIO_ACTIVE_HIGH>; + }; + }; + .. _dt-bindings-cells: Specifier cell names (\*-cells) diff --git a/doc/build/dts/bindings-upstream.rst b/doc/build/dts/bindings-upstream.rst index 6a9cb666a42f..bbd1e4aeed98 100644 --- a/doc/build/dts/bindings-upstream.rst +++ b/doc/build/dts/bindings-upstream.rst @@ -94,6 +94,26 @@ This ``|`` style prevents YAML parsers from removing the newlines in multi-line descriptions. This in turn makes these long strings display properly in the :ref:`devicetree_binding_index`. +If using the binding’s properties gets complicated, you can use examples +to provide a minimal node. e.g.: + +.. code-block:: yaml + + description: ... + + properties: + ... + + examples: + - | + leds { + compatible = "gpio-leds"; + + uled: led { + gpios = <&gpioe 12 GPIO_ACTIVE_HIGH>; + }; + }; + Naming conventions ================== diff --git a/scripts/dts/python-devicetree/src/devicetree/edtlib.py b/scripts/dts/python-devicetree/src/devicetree/edtlib.py index 183bb9ffb57a..e87e905408d3 100644 --- a/scripts/dts/python-devicetree/src/devicetree/edtlib.py +++ b/scripts/dts/python-devicetree/src/devicetree/edtlib.py @@ -137,6 +137,17 @@ class Binding: from node properties. It can also be None for Binding objects created using 'child-binding:' with no compatible. + examples: + Provides a minimal example node illustrating the binding (optional). + Like this: + + examples: + - | + / { + model = "This is a sample node"; + ... + }; + prop2specs: A dict mapping property names to PropertySpec objects describing those properties' values. @@ -290,6 +301,11 @@ def bus(self) -> Union[None, str, list[str]]: "See the class docstring" return self.raw.get('bus') + @property + def examples(self) -> Optional[list[str]]: + "See the class docstring" + return self.raw.get('examples') + @property def buses(self) -> list[str]: "See the class docstring" @@ -420,7 +436,7 @@ def _check(self, require_compatible: bool, require_description: bool, # Allowed top-level keys. The 'include' key should have been # removed by _load_raw() already. ok_top = {"title", "description", "compatible", "bus", - "on-bus", "properties", "child-binding"} + "on-bus", "properties", "child-binding", "examples"} # Descriptive errors for legacy bindings. legacy_errors = { diff --git a/scripts/dts/python-devicetree/tests/test-bindings/defaults.yaml b/scripts/dts/python-devicetree/tests/test-bindings/defaults.yaml index 0a9b63a87aec..79d134e0e4b0 100644 --- a/scripts/dts/python-devicetree/tests/test-bindings/defaults.yaml +++ b/scripts/dts/python-devicetree/tests/test-bindings/defaults.yaml @@ -36,3 +36,19 @@ properties: type: int required: false default: 123 + +examples: + - | + / { + leds { + compatible = "gpio-leds"; + + uled: led { + gpios = <&gpioe 12 GPIO_ACTIVE_HIGH>; + }; + }; + + aliases { + led0 = &uled; + }; + }; diff --git a/scripts/dts/python-devicetree/tests/test_edtlib.py b/scripts/dts/python-devicetree/tests/test_edtlib.py index f26b5fa15815..46c29a597a04 100644 --- a/scripts/dts/python-devicetree/tests/test_edtlib.py +++ b/scripts/dts/python-devicetree/tests/test_edtlib.py @@ -7,6 +7,7 @@ from logging import WARNING import os from pathlib import Path +import textwrap import pytest @@ -568,10 +569,26 @@ def test_binding_top_key(): title = binding.title description = binding.description compatible = binding.compatible + examples = binding.examples[0] assert title == "Test binding" assert description == "Property default value test" assert compatible == "defaults" + assert examples == textwrap.dedent("""\ + / { + leds { + compatible = "gpio-leds"; + + uled: led { + gpios = <&gpioe 12 GPIO_ACTIVE_HIGH>; + }; + }; + + aliases { + led0 = &uled; + }; + }; + """) def test_child_binding(): '''Test 'child-binding:' in bindings''' From a1cdd71aca5325faaa898b67e1e119a635327e56 Mon Sep 17 00:00:00 2001 From: James Roy Date: Fri, 12 Dec 2025 22:41:43 +0800 Subject: [PATCH 053/143] UPSTREAM: edtlib: Fix the accidental merging of examples in the binding commit 6524e670b347525a2bd0936967b52120696cdcfc upstream. Fix the example nodes in the examples block being accidentally merged and overwritten during build. Signed-off-by: James Roy --- scripts/dts/python-devicetree/src/devicetree/edtlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/dts/python-devicetree/src/devicetree/edtlib.py b/scripts/dts/python-devicetree/src/devicetree/edtlib.py index e87e905408d3..1d9896b300c0 100644 --- a/scripts/dts/python-devicetree/src/devicetree/edtlib.py +++ b/scripts/dts/python-devicetree/src/devicetree/edtlib.py @@ -2717,7 +2717,7 @@ def _bad_overwrite(to_dict: dict, from_dict: dict, prop: str, return False # These are overridden deliberately - if prop in {"title", "description", "compatible"}: + if prop in {"title", "description", "compatible", "examples"}: return False if prop == "required": From 804006b0f0bd1c0052219bb0b5f8b2979be3986c Mon Sep 17 00:00:00 2001 From: Soumya Tripathy Date: Thu, 5 Feb 2026 15:00:20 +0530 Subject: [PATCH 054/143] UPSTREAM: soc: ti: k3: am6x: enable TI K3 UART config for A53 cores commit f4293ce486eb82f8476b51d266e941b58d594536 upstream. Enable UART_NS16550_TI_K3 driver by default for AM6X A53 cores. This ensures the TI K3 specific UART variant is used on A53 cores, matching the configuration already enabled for M4 and R5 cores. Signed-off-by: Soumya Tripathy --- soc/ti/k3/am6x/Kconfig.defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/soc/ti/k3/am6x/Kconfig.defconfig b/soc/ti/k3/am6x/Kconfig.defconfig index 6d60583b71c5..6ea4ff65249d 100644 --- a/soc/ti/k3/am6x/Kconfig.defconfig +++ b/soc/ti/k3/am6x/Kconfig.defconfig @@ -33,6 +33,7 @@ config UART_NS16550 config UART_NS16550_TI_K3 default y if SOC_SERIES_AM6X_M4 default y if SOC_SERIES_AM6X_R5 + default y if SOC_SERIES_AM6X_A53 choice UART_NS16550_VARIANT default UART_NS16550_VARIANT_NS16750 From fba027cfcd96b0d2a86020d9aa2ee5148751c3fc Mon Sep 17 00:00:00 2001 From: Erik Tamlin Date: Thu, 22 Jan 2026 13:23:31 +0100 Subject: [PATCH 055/143] UPSTREAM: manifest: update percepio commit 1b1b5bfd7c5e5a87e08ea301a2654dd3a06175af upstream. Update the percepio module to use TraceRecorder v4.11.1 Signed-off-by: Erik Tamlin --- doc/services/tracing/index.rst | 2 +- modules/percepio/CMakeLists.txt | 25 ++++++++++++++++--------- west.yml | 2 +- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/doc/services/tracing/index.rst b/doc/services/tracing/index.rst index 41f1fc316192..a1bae6c39409 100644 --- a/doc/services/tracing/index.rst +++ b/doc/services/tracing/index.rst @@ -276,7 +276,7 @@ apply the following configuration options: CONFIG_PERCEPIO_TRACERECORDER=y CONFIG_PERCEPIO_TRC_START_MODE_START=y CONFIG_PERCEPIO_TRC_CFG_STREAM_PORT_ITM=y - CONFIG_PERCEPIO_TRC_CFG_ITM_PORT=1 + CONFIG_PERCEPIO_TRC_CFG_STREAM_PORT_ITM_PORT=1 Or if using menuconfig: diff --git a/modules/percepio/CMakeLists.txt b/modules/percepio/CMakeLists.txt index 3885337ef517..6f35a819b4d6 100644 --- a/modules/percepio/CMakeLists.txt +++ b/modules/percepio/CMakeLists.txt @@ -38,7 +38,7 @@ if(CONFIG_PERCEPIO_TRACERECORDER) ${TRACERECORDER_DIR}/trcTimestamp.c ${TRACERECORDER_DIR}/trcDependency.c ${TRACERECORDER_DIR}/trcRunnable.c - ) + ) if(CONFIG_PERCEPIO_TRC_CFG_STREAM_PORT_RTT) zephyr_library_sources( @@ -103,9 +103,17 @@ if(CONFIG_PERCEPIO_TRACERECORDER) ${TRACERECORDER_DIR}/include ) - set_property(GLOBAL APPEND PROPERTY extra_post_build_commands + if(CONFIG_PERCEPIO_TRC_CFG_USE_SYSCALL_EXTENSION) + set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${TRACERECORDER_DIR}/kernelports/Zephyr/scripts/tz_parse_syscalls.py ${CMAKE_BINARY_DIR} ${ZEPHYR_BASE} ) + endif() + + if(CONFIG_PERCEPIO_TRC_CFG_AUTOISR) + set_property(GLOBAL APPEND PROPERTY extra_post_build_commands + COMMAND ${PYTHON_EXECUTABLE} ${TRACERECORDER_DIR}/kernelports/Zephyr/scripts/tz_parse_irqs.py --xml-out ${CMAKE_BINARY_DIR}/AutoISR-v1.1.0.xml --edt-pickle ${EDT_PICKLE} + ) + endif() endif() @@ -118,11 +126,11 @@ if(CONFIG_PERCEPIO_DFM) ${DFM_DIR}/dfmAlert.c ${DFM_DIR}/dfmCloud.c ${DFM_DIR}/dfmEntry.c + ${DFM_DIR}/dfmRetainedMemory.c ${DFM_DIR}/dfmSession.c ${DFM_DIR}/dfmStorage.c - ${DFM_DIR}/dfmRetainedMemory.c ${DFM_DIR}/kernelports/Zephyr/dfmKernelPort.c - ) + ) if(CONFIG_PERCEPIO_DFM_CFG_STORAGEPORT_NONE) zephyr_library_sources( @@ -175,11 +183,10 @@ if(CONFIG_PERCEPIO_DFM) ) endif() - if(CONFIG_PERCEPIO_DFM_CFG_RETAINED_MEMORY) - zephyr_library_sources( - ${DFM_DIR}/kernelports/Zephyr/dfmRetainedMemoryPort.c - ) - endif() + zephyr_library_sources_ifdef( + CONFIG_PERCEPIO_DFM_CFG_RETAINED_MEMORY + ${DFM_DIR}/kernelports/Zephyr/dfmRetainedMemoryPort.c + ) zephyr_include_directories( ${DFM_DIR}/kernelports/Zephyr/config diff --git a/west.yml b/west.yml index f62791e5b0cf..7cb5bd77a3f8 100644 --- a/west.yml +++ b/west.yml @@ -352,7 +352,7 @@ manifest: path: modules/lib/openthread - name: percepio path: modules/debug/percepio - revision: 132ed87d617578a6cb70a2443f43e117c315e0f0 + revision: 94866e8a7016a044e70fb71094facae5fd82fdab groups: - debug - name: picolibc From c1abe449e359d285a81b3d5c30404b74b23dc9bf Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Wed, 29 Oct 2025 10:42:02 +0100 Subject: [PATCH 056/143] UPSTREAM: scripts: west_commands: Extend ruff config in pyproject.toml commit e81bacd361c4f58119ba2e23ac5d977436b03c42 upstream. Now that the west_commands directory has a pyproject.toml configuration file, it should consider local packages for first party imports. Make sure the top-level ruff config is extended, and fix some import sorting. Signed-off-by: Pieter De Gendt --- scripts/west_commands/blobs.py | 1 + scripts/west_commands/boards.py | 1 + scripts/west_commands/build.py | 3 ++- scripts/west_commands/build_helpers.py | 3 ++- scripts/west_commands/debug.py | 3 ++- scripts/west_commands/export.py | 1 + scripts/west_commands/flash.py | 3 ++- scripts/west_commands/packages.py | 2 ++ scripts/west_commands/patch.py | 1 + scripts/west_commands/pyproject.toml | 3 +++ scripts/west_commands/robot.py | 3 ++- scripts/west_commands/runners/intel_adsp.py | 3 +-- scripts/west_commands/shields.py | 1 + scripts/west_commands/simulate.py | 3 ++- scripts/west_commands/spdx.py | 1 + scripts/west_commands/tests/test_nxp_s32dbg.py | 1 + scripts/west_commands/tests/test_rfp.py | 1 + scripts/west_commands/tests/test_twister.py | 1 + scripts/west_commands/tests/west_build/test_dir_fmt.py | 1 + .../west_commands/tests/west_build/test_resolve_build_dir.py | 1 + 20 files changed, 29 insertions(+), 8 deletions(-) diff --git a/scripts/west_commands/blobs.py b/scripts/west_commands/blobs.py index d65bd01fb838..7ca43a408955 100644 --- a/scripts/west_commands/blobs.py +++ b/scripts/west_commands/blobs.py @@ -11,6 +11,7 @@ from urllib.parse import urlparse from west.commands import WestCommand + from zephyr_ext_common import ZEPHYR_BASE sys.path.append(os.fspath(Path(__file__).parent.parent)) diff --git a/scripts/west_commands/boards.py b/scripts/west_commands/boards.py index b47383988532..9a27068926c2 100644 --- a/scripts/west_commands/boards.py +++ b/scripts/west_commands/boards.py @@ -10,6 +10,7 @@ from pathlib import Path from west.commands import WestCommand + from zephyr_ext_common import ZEPHYR_BASE sys.path.append(os.fspath(Path(__file__).parent.parent)) diff --git a/scripts/west_commands/build.py b/scripts/west_commands/build.py index 3e63b97fc62a..4df2ce0e401b 100644 --- a/scripts/west_commands/build.py +++ b/scripts/west_commands/build.py @@ -10,11 +10,12 @@ import sys import yaml -from build_helpers import FIND_BUILD_DIR_DESCRIPTION, find_build_dir, is_zephyr_build, load_domains from west.commands import Verbosity from west.configuration import config from west.util import WestNotFound, west_topdir from west.version import __version__ + +from build_helpers import FIND_BUILD_DIR_DESCRIPTION, find_build_dir, is_zephyr_build, load_domains from zcmake import DEFAULT_CMAKE_GENERATOR, CMakeCache, run_build, run_cmake from zephyr_ext_common import Forceable diff --git a/scripts/west_commands/build_helpers.py b/scripts/west_commands/build_helpers.py index 640e7cffbc26..b225ec0e2630 100644 --- a/scripts/west_commands/build_helpers.py +++ b/scripts/west_commands/build_helpers.py @@ -14,11 +14,12 @@ import sys from pathlib import Path -import zcmake from west import log from west.configuration import config from west.util import escapes_directory +import zcmake + # Domains.py must be imported from the pylib directory, since # twister also uses the implementation script_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) diff --git a/scripts/west_commands/debug.py b/scripts/west_commands/debug.py index 1000e88bd84c..2c5236f616d1 100644 --- a/scripts/west_commands/debug.py +++ b/scripts/west_commands/debug.py @@ -8,9 +8,10 @@ from textwrap import dedent -from run_common import add_parser_common, do_run_common from west.commands import WestCommand +from run_common import add_parser_common, do_run_common + class Debug(WestCommand): diff --git a/scripts/west_commands/export.py b/scripts/west_commands/export.py index 913df1995586..5088598198c3 100644 --- a/scripts/west_commands/export.py +++ b/scripts/west_commands/export.py @@ -6,6 +6,7 @@ from pathlib import Path from west.commands import WestCommand + from zcmake import run_cmake EXPORT_DESCRIPTION = '''\ diff --git a/scripts/west_commands/flash.py b/scripts/west_commands/flash.py index 47d7e61e5c0d..01f7b923c657 100644 --- a/scripts/west_commands/flash.py +++ b/scripts/west_commands/flash.py @@ -8,9 +8,10 @@ from pathlib import Path -from run_common import add_parser_common, do_run_common, get_build_dir from west.commands import WestCommand +from run_common import add_parser_common, do_run_common, get_build_dir + class Flash(WestCommand): diff --git a/scripts/west_commands/packages.py b/scripts/west_commands/packages.py index 795f3d4fe139..e97bac1f537a 100644 --- a/scripts/west_commands/packages.py +++ b/scripts/west_commands/packages.py @@ -11,6 +11,8 @@ from pathlib import Path from west.commands import WestCommand +from west.util import quote_sh_list + from zephyr_ext_common import ZEPHYR_BASE sys.path.append(os.fspath(Path(__file__).parent.parent)) diff --git a/scripts/west_commands/patch.py b/scripts/west_commands/patch.py index c8e19e793379..71069ca1a5c0 100644 --- a/scripts/west_commands/patch.py +++ b/scripts/west_commands/patch.py @@ -19,6 +19,7 @@ sys.path.append(os.fspath(Path(__file__).parent.parent)) import zephyr_module + from zephyr_ext_common import ZEPHYR_BASE try: diff --git a/scripts/west_commands/pyproject.toml b/scripts/west_commands/pyproject.toml index 7a51a7119cf3..efdabedee778 100644 --- a/scripts/west_commands/pyproject.toml +++ b/scripts/west_commands/pyproject.toml @@ -4,3 +4,6 @@ mypy_path = "." [tool.pytest.ini_options] pythonpath = ["."] + +[tool.ruff] +extend = "../../.ruff.toml" diff --git a/scripts/west_commands/robot.py b/scripts/west_commands/robot.py index a2efd2895ba9..6ba871320c7c 100644 --- a/scripts/west_commands/robot.py +++ b/scripts/west_commands/robot.py @@ -2,9 +2,10 @@ # # SPDX-License-Identifier: Apache-2.0 -from run_common import add_parser_common, do_run_common from west.commands import WestCommand +from run_common import add_parser_common, do_run_common + EXPORT_DESCRIPTION = '''\ Run RobotFramework test suites with a runner of choice. ''' diff --git a/scripts/west_commands/runners/intel_adsp.py b/scripts/west_commands/runners/intel_adsp.py index 02b13fe6491f..4306e919695a 100644 --- a/scripts/west_commands/runners/intel_adsp.py +++ b/scripts/west_commands/runners/intel_adsp.py @@ -12,9 +12,8 @@ import shutil import sys -from zephyr_ext_common import ZEPHYR_BASE - from runners.core import RunnerCaps, ZephyrBinaryRunner +from zephyr_ext_common import ZEPHYR_BASE DEFAULT_CAVSTOOL='soc/intel/intel_adsp/tools/cavstool_client.py' diff --git a/scripts/west_commands/shields.py b/scripts/west_commands/shields.py index 6f5f42560a32..5db9bac06f7d 100644 --- a/scripts/west_commands/shields.py +++ b/scripts/west_commands/shields.py @@ -11,6 +11,7 @@ from pathlib import Path from west.commands import WestCommand + from zephyr_ext_common import ZEPHYR_BASE sys.path.append(os.fspath(Path(__file__).parent.parent)) diff --git a/scripts/west_commands/simulate.py b/scripts/west_commands/simulate.py index a189926472a9..bdc10e52230f 100644 --- a/scripts/west_commands/simulate.py +++ b/scripts/west_commands/simulate.py @@ -2,9 +2,10 @@ # # SPDX-License-Identifier: Apache-2.0 -from run_common import add_parser_common, do_run_common from west.commands import WestCommand +from run_common import add_parser_common, do_run_common + EXPORT_DESCRIPTION = '''\ Simulate the board on a runner of choice using generated artifacts. ''' diff --git a/scripts/west_commands/spdx.py b/scripts/west_commands/spdx.py index d28c99af1820..6d937818d642 100644 --- a/scripts/west_commands/spdx.py +++ b/scripts/west_commands/spdx.py @@ -6,6 +6,7 @@ import uuid from west.commands import WestCommand + from zspdx.sbom import SBOMConfig, makeSPDX, setupCmakeQuery from zspdx.version import SPDX_VERSION_2_3, SUPPORTED_SPDX_VERSIONS, parse diff --git a/scripts/west_commands/tests/test_nxp_s32dbg.py b/scripts/west_commands/tests/test_nxp_s32dbg.py index 0dd690d39ff1..243f923fd417 100644 --- a/scripts/west_commands/tests/test_nxp_s32dbg.py +++ b/scripts/west_commands/tests/test_nxp_s32dbg.py @@ -8,6 +8,7 @@ import pytest from conftest import RC_KERNEL_ELF + from runners.nxp_s32dbg import NXPS32DebugProbeConfig, NXPS32DebugProbeRunner TEST_DEVICE = 's32dbg' diff --git a/scripts/west_commands/tests/test_rfp.py b/scripts/west_commands/tests/test_rfp.py index 3a2dd38147c4..fc65cf1914a7 100644 --- a/scripts/west_commands/tests/test_rfp.py +++ b/scripts/west_commands/tests/test_rfp.py @@ -7,6 +7,7 @@ from unittest.mock import call, patch from conftest import RC_KERNEL_HEX + from runners.rfp import RfpBinaryRunner TEST_RFP_PORT = 'test-rfp-serial' diff --git a/scripts/west_commands/tests/test_twister.py b/scripts/west_commands/tests/test_twister.py index 8dcb74e58009..7abd02794eba 100644 --- a/scripts/west_commands/tests/test_twister.py +++ b/scripts/west_commands/tests/test_twister.py @@ -6,6 +6,7 @@ from argparse import Namespace import pytest + from twister_cmd import Twister TEST_CASES = [ diff --git a/scripts/west_commands/tests/west_build/test_dir_fmt.py b/scripts/west_commands/tests/west_build/test_dir_fmt.py index 871c0c98653e..531345a8fd55 100644 --- a/scripts/west_commands/tests/west_build/test_dir_fmt.py +++ b/scripts/west_commands/tests/west_build/test_dir_fmt.py @@ -9,6 +9,7 @@ from pathlib import Path import pytest + from build import Build ROOT = Path(Path.cwd().anchor) diff --git a/scripts/west_commands/tests/west_build/test_resolve_build_dir.py b/scripts/west_commands/tests/west_build/test_resolve_build_dir.py index 8ff301a0eb79..25db3237b4d1 100644 --- a/scripts/west_commands/tests/west_build/test_resolve_build_dir.py +++ b/scripts/west_commands/tests/west_build/test_resolve_build_dir.py @@ -5,6 +5,7 @@ from pathlib import Path import pytest + from build_helpers import _resolve_build_dir cwd = Path.cwd() From 77a91ff96a8d0134f8e18e10cc2739195cf5c09b Mon Sep 17 00:00:00 2001 From: Josuah Demangeon Date: Mon, 23 Mar 2026 10:44:38 +0530 Subject: [PATCH 057/143] UPSTREAM: drivers: power_domain: fix CMakeLists.txt sorted order commit 30950b888d368121e15b44e8348054e612b4ec16 upstream. Fix the alphabetical order in the zephyr-keep-sorted block to comply with KeepSorted compliance check. Partial cherry-pick of "style: drivers: sort Kconfig and CMake includes" affecting only drivers/power_domain/CMakeLists.txt. Signed-off-by: Josuah Demangeon --- drivers/power_domain/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power_domain/CMakeLists.txt b/drivers/power_domain/CMakeLists.txt index d1ae0fc8e952..8d1afcb2ef59 100644 --- a/drivers/power_domain/CMakeLists.txt +++ b/drivers/power_domain/CMakeLists.txt @@ -8,10 +8,10 @@ zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_ARM_SCMI power_domain_arm_scmi. zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO power_domain_gpio.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO_MONITOR power_domain_gpio_monitor.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_INTEL_ADSP power_domain_intel_adsp.c) -zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_NXP_SCU power_domain_nxp_scu.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_NRFS_GDPWR power_domain_nrfs_gdpwr.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_NRFS_SWEXT power_domain_nrfs_swext.c) +zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_NXP_SCU power_domain_nxp_scu.c) +zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_SILABS_SIWX91X power_domain_silabs_siwx91x.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_SOC_PM_STATE power_domain_soc_state_change.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_TISCI power_domain_tisci.c) -zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_SILABS_SIWX91X power_domain_silabs_siwx91x.c) # zephyr-keep-sorted-stop From 68d3ccba98b4306260c35ad6453eb02e7a6b39db Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Fri, 5 Dec 2025 10:37:38 +0100 Subject: [PATCH 058/143] UPSTREAM: scripts: west: packages: Print warning on windows or run... commit 7f97d3dd0a54869189856327e002d4fcc57ba5fd upstream. On non-Windows systems execute a new program, replacing the west packages call, when trying to install packages using pip. For Windows, update the documented way of installing python packages, or using 'west packages pip --install' print a warning. Signed-off-by: Pieter De Gendt --- doc/develop/beyond-GSG.rst | 39 ++++++++++++++++----- doc/develop/getting_started/index.rst | 22 ++++++++++-- scripts/utils/west-packages-pip-install.cmd | 27 ++++++++++++++ scripts/west_commands/packages.py | 38 +++++++++++++++++--- 4 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 scripts/utils/west-packages-pip-install.cmd diff --git a/doc/develop/beyond-GSG.rst b/doc/develop/beyond-GSG.rst index 3f2baea422ee..0447415a01e6 100644 --- a/doc/develop/beyond-GSG.rst +++ b/doc/develop/beyond-GSG.rst @@ -136,16 +136,39 @@ Keeping Zephyr updated To update the Zephyr project source code, you need to get the latest changes via ``git``. Afterwards, run ``west update`` as mentioned in the previous paragraph. -Additionally, in the case of updated or added Python dependencies, running -``west packages pip --install`` will make sure these are up-to-date. +Additionally, check for updated or added Python dependencies. -.. code-block:: console +.. tabs:: - # replace zephyrproject with the path you gave west init - cd zephyrproject/zephyr - git pull - west update - west packages pip --install + .. group-tab:: Linux/macOS + + .. code-block:: console + + # replace zephyrproject with the path you gave west init + cd zephyrproject/zephyr + git pull + west update + west packages pip --install + + .. group-tab:: Windows + + .. tabs:: + + .. code-tab:: bat + + :: replace zephyrproject with the path you gave west init + cd zephyrproject\zephyr + git pull + west update + cmd /c scripts\utils\west-packages-pip-install.cmd + + .. code-tab:: powershell + + # replace zephyrproject with the path you gave west init + cd zephyrproject\zephyr + git pull + west update + python -m pip install @((west packages pip) -split ' ') Export Zephyr CMake package *************************** diff --git a/doc/develop/getting_started/index.rst b/doc/develop/getting_started/index.rst index 9d081061cc04..c389ceb22f7f 100644 --- a/doc/develop/getting_started/index.rst +++ b/doc/develop/getting_started/index.rst @@ -266,6 +266,10 @@ chosen. You'll also install Zephyr's additional Python dependencies in a west packages pip --install + .. note:: + + This could downgrade or upgrade west itself. + .. group-tab:: macOS #. Create a new virtual environment: @@ -317,6 +321,10 @@ chosen. You'll also install Zephyr's additional Python dependencies in a west packages pip --install + .. note:: + + This could downgrade or upgrade west itself. + .. group-tab:: Windows #. Open a ``cmd.exe`` or PowerShell terminal window **as a regular user** @@ -389,9 +397,19 @@ chosen. You'll also install Zephyr's additional Python dependencies in a #. Install Python dependencies using ``west packages``. - .. code-block:: bat + .. tabs:: - west packages pip --install + .. code-tab:: bat + + cmd /c scripts\utils\west-packages-pip-install.cmd + + .. code-tab:: powershell + + python -m pip install @((west packages pip) -split ' ') + + .. note:: + + This could downgrade or upgrade west itself. Install the Zephyr SDK ********************** diff --git a/scripts/utils/west-packages-pip-install.cmd b/scripts/utils/west-packages-pip-install.cmd new file mode 100644 index 000000000000..e05495b7c8e4 --- /dev/null +++ b/scripts/utils/west-packages-pip-install.cmd @@ -0,0 +1,27 @@ +:: SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +:: SPDX-License-Identifier: Apache-2.0 + +@echo off +rem Collect packages from west and install them with a single pip call. +setlocal enabledelayedexpansion + +set "PACKAGES=" + +for /f "usebackq delims=" %%p in (`west packages pip`) do ( + if defined PACKAGES ( + set "PACKAGES=!PACKAGES! %%p" + ) else ( + set "PACKAGES=%%p" + ) +) + +if not defined PACKAGES ( + echo west packages pip returned no packages to install. + exit /b 0 +) + +echo Installing packages with: python.exe -m pip install %PACKAGES% +python.exe -m pip install %PACKAGES% +set "RESULT=%ERRORLEVEL%" + +endlocal & exit /b %RESULT% diff --git a/scripts/west_commands/packages.py b/scripts/west_commands/packages.py index e97bac1f537a..25cc66d8aad8 100644 --- a/scripts/west_commands/packages.py +++ b/scripts/west_commands/packages.py @@ -4,11 +4,12 @@ import argparse import os +import platform import subprocess import sys import textwrap from itertools import chain -from pathlib import Path +from pathlib import Path, PureWindowsPath from west.commands import WestCommand from west.util import quote_sh_list @@ -159,11 +160,38 @@ def do_run_pip(self, args, manager_args): self.die("Running pip install outside of a virtual environment") if len(requirements) > 0: - subprocess.check_call( - [sys.executable, "-m", "pip", "install"] - + list(chain.from_iterable([("-r", r) for r in requirements])) - + manager_args + cmd = [sys.executable, "-m", "pip", "install"] + cmd += chain.from_iterable([("-r", str(r)) for r in requirements]) + cmd += manager_args + self.dbg(quote_sh_list(cmd)) + + # Use os.execv to execute a new program, replacing the current west process, + # this unloads all python modules first and allows for pip to update packages safely + if platform.system() != 'Windows': + os.execv(cmd[0], cmd) + + # Only reachable on Windows systems + # Windows does not really support os.execv: + # https://github.com/python/cpython/issues/63323 + # https://github.com/python/cpython/issues/101191 + # Warn the users about permission errors as those reported in: + # https://github.com/zephyrproject-rtos/zephyr/issues/100296 + cmdscript = ( + PureWindowsPath(__file__).parents[1] / "utils" / "west-packages-pip-install.cmd" ) + self.wrn( + "Updating packages on Windows with 'west packages pip --install', that are " + "currently in use by west, results in permission errors. Leaving your " + "environment with conflicting package versions. Recommended is to start with " + "a new environment in that case.\n\n" + "To avoid this using powershell run the following command instead:\n" + f"{sys.executable} -m pip install @((west packages pip) -split ' ')\n\n" + "Using cmd.exe execute the helper script:\n" + f"cmd /c {cmdscript}\n\n" + "Running 'west packages pip --install -- --dry-run' can provide information " + "without actually updating the environment." + ) + subprocess.check_call(cmd) else: self.inf("Nothing to install") return From 6705c79cd13121c9335c7da24cbfbf7695eb59fc Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 13:50:43 +0530 Subject: [PATCH 059/143] UPSTREAM: drivers: firmware: Clock control TISCI driver support commit 3474deee3d40659b765e2d03f37bd27258fe5d2c upstream. Support added for clock control using TISCI for devices using the binding ti,k2g-sci-clk. This driver relies on the TISCI layer to make calls to the DMSC core to set and get the clock rate and retrieve clock status. Signed-off-by: Dave Joseph --- drivers/clock_control/CMakeLists.txt | 1 + drivers/clock_control/Kconfig | 2 + drivers/clock_control/Kconfig.tisci | 9 ++ drivers/clock_control/clock_control_tisci.c | 100 ++++++++++++++++++ dts/bindings/clock/ti,k2g-sci-clk.yaml | 22 ++++ .../clock_control/tisci_clock_control.h | 41 +++++++ 6 files changed, 175 insertions(+) create mode 100644 drivers/clock_control/Kconfig.tisci create mode 100644 drivers/clock_control/clock_control_tisci.c create mode 100644 dts/bindings/clock/ti,k2g-sci-clk.yaml create mode 100644 include/zephyr/drivers/clock_control/tisci_clock_control.h diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index b4ebce89ee00..8c0b16e7a7d1 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -133,4 +133,5 @@ endif() zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_AST10X0 clock_control_ast10x0.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MAX32 clock_control_max32.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_TISCI clock_control_tisci.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_WCH_RCC clock_control_wch_rcc.c) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index 2d5ed6198b84..e20d1c7c660f 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -114,6 +114,8 @@ source "drivers/clock_control/Kconfig.silabs" source "drivers/clock_control/Kconfig.siwx91x" +source "drivers/clock_control/Kconfig.tisci" + source "drivers/clock_control/Kconfig.wch_rcc" source "drivers/clock_control/Kconfig.it51xxx" diff --git a/drivers/clock_control/Kconfig.tisci b/drivers/clock_control/Kconfig.tisci new file mode 100644 index 000000000000..e56d5023b2ab --- /dev/null +++ b/drivers/clock_control/Kconfig.tisci @@ -0,0 +1,9 @@ +# Copyright 2024 Texas Instruments Incorporated. +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_CONTROL_TISCI + bool "TI SCI Clock Control driver" + default y + depends on DT_HAS_TI_K2G_SCI_CLK_ENABLED + help + Driver for TISCI based clock control. diff --git a/drivers/clock_control/clock_control_tisci.c b/drivers/clock_control/clock_control_tisci.c new file mode 100644 index 000000000000..7032c7b380d7 --- /dev/null +++ b/drivers/clock_control/clock_control_tisci.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025, Texas Instruments + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_k2g_sci_clk + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(ti_k2g_sci_clk, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +static const struct device *dmsc = DEVICE_DT_GET(DT_NODELABEL(dmsc)); + +static int tisci_get_rate(const struct device *dev, clock_control_subsys_t sys, uint32_t *rate) +{ + struct tisci_clock_config *req = (struct tisci_clock_config *)sys; + uint64_t temp_rate; + int ret; + + ret = tisci_cmd_clk_get_freq(dmsc, req->dev_id, req->clk_id, &temp_rate); + if (ret) { + LOG_ERR("Failed to get clock freq: dev_id=%u clk_id=%u err=%d", req->dev_id, + req->clk_id, ret); + return ret; + } + + *rate = (uint32_t)temp_rate; + + return 0; +} + +static int tisci_set_rate(const struct device *dev, void *sys, void *rate) +{ + struct tisci_clock_config *req = (struct tisci_clock_config *)sys; + uint64_t freq = *((uint64_t *)rate); + int ret; + + ret = tisci_cmd_clk_set_freq(dmsc, req->dev_id, req->clk_id, freq, freq, freq); + if (ret) { + LOG_ERR("Failed to set clock freq: dev_id=%u clk_id=%u freq=%llu err=%d", + req->dev_id, req->clk_id, freq, ret); + } + + return ret; +} + +static enum clock_control_status tisci_get_status(const struct device *dev, + clock_control_subsys_t sys) +{ + struct tisci_clock_config *req = (struct tisci_clock_config *)sys; + bool req_state = true; + bool curr_state = true; + int ret; + + ret = tisci_cmd_clk_is_on(dmsc, req->clk_id, req->dev_id, &req_state, &curr_state); + if (ret) { + LOG_ERR("Failed to get clock ON status: dev_id=%u clk_id=%u err=%d", req->dev_id, + req->clk_id, ret); + return CLOCK_CONTROL_STATUS_UNKNOWN; + } + if (curr_state) { + return CLOCK_CONTROL_STATUS_ON; + } + if (req_state && !curr_state) { + return CLOCK_CONTROL_STATUS_STARTING; + } + + curr_state = true; + + ret = tisci_cmd_clk_is_off(dmsc, req->clk_id, req->dev_id, NULL, &curr_state); + if (ret) { + LOG_ERR("Failed to get clock OFF status: dev_id=%u clk_id=%u err=%d", req->dev_id, + req->clk_id, ret); + return CLOCK_CONTROL_STATUS_UNKNOWN; + } + + if (curr_state) { + return CLOCK_CONTROL_STATUS_OFF; + } + + return CLOCK_CONTROL_STATUS_UNKNOWN; +} + +static DEVICE_API(clock_control, tisci_clock_driver_api) = { + .get_rate = tisci_get_rate, + .set_rate = tisci_set_rate, + .get_status = tisci_get_status +}; + +#define TI_K2G_SCI_CLK_INIT(_n) \ + DEVICE_DT_INST_DEFINE(_n, NULL, NULL, NULL, NULL, PRE_KERNEL_1, \ + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &tisci_clock_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(TI_K2G_SCI_CLK_INIT) diff --git a/dts/bindings/clock/ti,k2g-sci-clk.yaml b/dts/bindings/clock/ti,k2g-sci-clk.yaml new file mode 100644 index 000000000000..e7042d63a6fe --- /dev/null +++ b/dts/bindings/clock/ti,k2g-sci-clk.yaml @@ -0,0 +1,22 @@ +# Copyright 2025 Texas Instruments Incorporated. +# SPDX-License-Identifier: Apache-2.0 + +description: TI-SCI clock controller + +compatible: "ti,k2g-sci-clk" + +include: + - clock-controller.yaml + - base.yaml + +properties: + "#clock-cells": + type: int + required: true + description: > + Number of cells required to specify a clock provided by this controller. + const: 2 + +clock-cells: + - devid + - clkid diff --git a/include/zephyr/drivers/clock_control/tisci_clock_control.h b/include/zephyr/drivers/clock_control/tisci_clock_control.h new file mode 100644 index 000000000000..f9457c25875b --- /dev/null +++ b/include/zephyr/drivers/clock_control/tisci_clock_control.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Texas Instruments + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_TISCI_CLOCK_CONTROL_H_ +#define ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_TISCI_CLOCK_CONTROL_H_ + +#include + +/** + * @struct tisci_clock_config + * @brief Clock configuration structure + * + * This structure is used to define the configuration for a clock, including + * the device ID and clock ID. + * + * @param tisci_clock_config::dev_id + * Device ID associated with the clock. + * + * @param tisci_clock_config::clk_id + * Clock ID within the device. + */ +struct tisci_clock_config { + uint32_t dev_id; + uint32_t clk_id; +}; + +#define TISCI_GET_CLOCK(node_id) DEVICE_DT_GET(DT_PHANDLE(node_id, clocks)) + +#define TISCI_GET_CLOCK_DETAILS(node_id) \ + { \ + .dev_id = DT_CLOCKS_CELL(node_id, devid), \ + .clk_id = DT_CLOCKS_CELL(node_id, clkid) \ + } + +#define TISCI_GET_CLOCK_BY_INST(inst) TISCI_GET_CLOCK(DT_DRV_INST(inst)) + +#define TISCI_GET_CLOCK_DETAILS_BY_INST(DT_DRV_INST) TISCI_GET_CLOCK_DETAILS(DT_DRV_INST(inst)) + +#endif From e0a22a75f4b35e0271fe3f97c8ea1c5a322de87f Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Wed, 21 Jan 2026 01:09:45 +0900 Subject: [PATCH 060/143] UPSTREAM: drivers: clock_control: tisci: drop redundant curr_state check commit 2c0c5142a64fae294593c9e6e283011a355f6392 upstream. curr_state is known to be false when reaching the STARTING state check, making the explicit '!curr_state' condition redundant. Signed-off-by: Gaetan Perrot --- drivers/clock_control/clock_control_tisci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clock_control/clock_control_tisci.c b/drivers/clock_control/clock_control_tisci.c index 7032c7b380d7..06a724404f20 100644 --- a/drivers/clock_control/clock_control_tisci.c +++ b/drivers/clock_control/clock_control_tisci.c @@ -67,7 +67,7 @@ static enum clock_control_status tisci_get_status(const struct device *dev, if (curr_state) { return CLOCK_CONTROL_STATUS_ON; } - if (req_state && !curr_state) { + if (req_state) { return CLOCK_CONTROL_STATUS_STARTING; } From d78aa393681a339e26b42bd967c86df5b7189e2c Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Fri, 30 Jan 2026 09:55:02 +0900 Subject: [PATCH 061/143] UPSTREAM: drivers: mbox: ti_secproxy: drop redundant NULL check on dev p... commit 94830a2827b55812aed4b4ea4bb9f4d51c6a3ff3 upstream. The mailbox send callback is always invoked with a valid device pointer. Remove the redundant NULL check. Signed-off-by: Gaetan Perrot --- drivers/mbox/mbox_ti_secproxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mbox/mbox_ti_secproxy.c b/drivers/mbox/mbox_ti_secproxy.c index 01c2478659d6..b9a3ace8c8cc 100644 --- a/drivers/mbox/mbox_ti_secproxy.c +++ b/drivers/mbox/mbox_ti_secproxy.c @@ -214,7 +214,7 @@ static int secproxy_mailbox_send(const struct device *dev, uint32_t channel, mem_addr_t data_reg; k_spinlock_key_t key; - if (!dev || !msg || !msg->data) { + if (msg == NULL || msg->data == NULL) { LOG_ERR("Invalid parameters"); return -EINVAL; } From 310e3eb8fa970237a0dbe5a3a1cde371a7804a66 Mon Sep 17 00:00:00 2001 From: Gaetan Perrot Date: Sat, 31 Jan 2026 13:59:50 +0900 Subject: [PATCH 062/143] UPSTREAM: drivers: mbox: ti_secproxy: add missing SPDX license identifier commit a6fd608050073c1eb487db6ed1f3876d7852986e upstream. Add the SPDX-License-Identifier tag to the file header to properly declare the Apache-2.0 license, in line with Zephyr licensing requirements. Signed-off-by: Gaetan Perrot --- drivers/mbox/mbox_ti_secproxy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mbox/mbox_ti_secproxy.c b/drivers/mbox/mbox_ti_secproxy.c index b9a3ace8c8cc..a48dfe983953 100644 --- a/drivers/mbox/mbox_ti_secproxy.c +++ b/drivers/mbox/mbox_ti_secproxy.c @@ -2,6 +2,7 @@ * Copyright (c) 2025 Texas Instruments Incorporated. * * TI Secureproxy Mailbox driver for Zephyr's MBOX model. + * SPDX-License-Identifier: Apache-2.0 */ #include From ace7d6ab16d48a3f45c552647643f94ecab98933 Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 13:53:14 +0530 Subject: [PATCH 063/143] UPSTREAM: drivers: firmware: tisci: Reorganise macros commit 2581dd286e72953205b5916a2748b7ff3f914bfe upstream. - Add missing TISCI message type macros in tisci.h - Clean macro organisation - Add Secure indicator for message that require secure thread Signed-off-by: Dave Joseph --- drivers/firmware/tisci/tisci.h | 105 ++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 34 deletions(-) diff --git a/drivers/firmware/tisci/tisci.h b/drivers/firmware/tisci/tisci.h index 59d3898c7c46..59f2c05ddc3d 100644 --- a/drivers/firmware/tisci/tisci.h +++ b/drivers/firmware/tisci/tisci.h @@ -21,24 +21,21 @@ #define __packed __attribute__((__packed__)) #endif +/* Core System Messages */ #define TISCI_MSG_ENABLE_WDT 0x0000 #define TISCI_MSG_WAKE_RESET 0x0001 #define TISCI_MSG_VERSION 0x0002 #define TISCI_MSG_WAKE_REASON 0x0003 #define TISCI_MSG_GOODBYE 0x0004 #define TISCI_MSG_SYS_RESET 0x0005 -#define TISCI_MSG_BOARD_CONFIG 0x000b +#define TISCI_MSG_BOOT_NOTIFICATION 0x000a /* Secure */ +#define TISCI_MSG_BOARD_CONFIG 0x000b /* Secure */ #define TISCI_MSG_BOARD_CONFIG_RM 0x000c -#define TISCI_MSG_BOARD_CONFIG_SECURITY 0x000d +#define TISCI_MSG_BOARD_CONFIG_SECURITY 0x000d /* Secure */ #define TISCI_MSG_BOARD_CONFIG_PM 0x000e #define TISCI_MSG_QUERY_MSMC 0x0020 -/* Device requests */ -#define TISCI_MSG_SET_DEVICE_STATE 0x0200 -#define TISCI_MSG_GET_DEVICE_STATE 0x0201 -#define TISCI_MSG_SET_DEVICE_RESETS 0x0202 - -/* Clock requests */ +/* Clock Management */ #define TISCI_MSG_SET_CLOCK_STATE 0x0100 #define TISCI_MSG_GET_CLOCK_STATE 0x0101 #define TISCI_MSG_SET_CLOCK_PARENT 0x0102 @@ -48,46 +45,86 @@ #define TISCI_MSG_QUERY_CLOCK_FREQ 0x010d #define TISCI_MSG_GET_CLOCK_FREQ 0x010e -/* Processor Control Messages */ -#define TISCI_MSG_PROC_REQUEST 0xc000 -#define TISCI_MSG_PROC_RELEASE 0xc001 -#define TISCI_MSG_PROC_HANDOVER 0xc005 -#define TISCI_MSG_SET_PROC_BOOT_CONFIG 0xc100 -#define TISCI_MSG_SET_PROC_BOOT_CTRL 0xc101 -#define TISCI_MSG_PROC_AUTH_BOOT_IMAGE 0xc120 -#define TISCI_MSG_GET_PROC_BOOT_STATUS 0xc400 -#define TISCI_MSG_WAIT_PROC_BOOT_STATUS 0xc401 +/* Device Management */ +#define TISCI_MSG_SET_DEVICE_STATE 0x0200 +#define TISCI_MSG_GET_DEVICE_STATE 0x0201 +#define TISCI_MSG_SET_DEVICE_RESETS 0x0202 + +/* Low Power Mode Messages */ +#define TISCI_MSG_ENTER_SLEEP 0x0301 /* Secure */ /* Resource Management Requests */ -/* RM TISCI message to request a resource range assignment for a host */ -#define TISCI_MSG_GET_RESOURCE_RANGE 0x1500 /* RM TISCI message to set an IRQ between a peripheral and host processor */ -#define TISCI_MSG_RM_IRQ_SET (0x1000U) +#define TISCI_MSG_RM_IRQ_SET 0x1000 /* RM TISCI message to release a configured IRQ */ -#define TISCI_MSG_RM_IRQ_RELEASE (0x1001U) +#define TISCI_MSG_RM_IRQ_RELEASE 0x1001 -/* NAVSS resource management */ -/* Ringacc requests */ +/* Ring Accelerator */ #define TISCI_MSG_RM_RING_CFG 0x1110 -/* PSI-L requests */ -#define TISCI_MSG_RM_PSIL_PAIR 0x1280 -#define TISCI_MSG_RM_PSIL_UNPAIR 0x1281 +/* UDMAP Transmit Channels */ +#define TISCI_MSG_RM_UDMAP_TX_ALLOC 0x1200 +#define TISCI_MSG_RM_UDMAP_TX_FREE 0x1201 +#define TISCI_MSG_RM_UDMAP_TX_CH_CFG 0x1205 -#define TISCI_MSG_RM_UDMAP_TX_ALLOC 0x1200 -#define TISCI_MSG_RM_UDMAP_TX_FREE 0x1201 -#define TISCI_MSG_RM_UDMAP_RX_ALLOC 0x1210 -#define TISCI_MSG_RM_UDMAP_RX_FREE 0x1211 -#define TISCI_MSG_RM_UDMAP_FLOW_CFG 0x1220 -#define TISCI_MSG_RM_UDMAP_OPT_FLOW_CFG 0x1221 +/* UDMAP Receive Channels */ +#define TISCI_MSG_RM_UDMAP_RX_ALLOC 0x1210 +#define TISCI_MSG_RM_UDMAP_RX_FREE 0x1211 +#define TISCI_MSG_RM_UDMAP_RX_CH_CFG 0x1215 -#define TISCI_MSG_RM_UDMAP_TX_CH_CFG 0x1205 -#define TISCI_MSG_RM_UDMAP_RX_CH_CFG 0x1215 +/* UDMAP Flow Configuration */ +#define TISCI_MSG_RM_UDMAP_FLOW_CFG 0x1220 +#define TISCI_MSG_RM_UDMAP_OPT_FLOW_CFG 0x1221 #define TISCI_MSG_RM_UDMAP_FLOW_SIZE_THRESH_CFG 0x1231 +/* PSI-L Pairing */ +#define TISCI_MSG_RM_PSIL_PAIR 0x1280 +#define TISCI_MSG_RM_PSIL_UNPAIR 0x1281 + +/* RM TISCI message to request a resource range assignment for a host */ +#define TISCI_MSG_GET_RESOURCE_RANGE 0x1500 + +/* Security Messages */ +/* Firewall Configuration */ #define TISCI_MSG_FWL_SET 0x9000 #define TISCI_MSG_FWL_GET 0x9001 #define TISCI_MSG_FWL_CHANGE_OWNER 0x9002 +#define TISCI_MSG_OPEN_DEBUG_FWLS 0x900c /* Secure */ + +/* Cryptographic Key Management (DKEK) */ +#define TISCI_MSG_SA2UL_SET_DKEK 0x9003 /* Secure */ +#define TISCI_MSG_SA2UL_RELEASE_DKEK 0x9004 /* Secure */ +#define TISCI_MSG_SA2UL_GET_DKEK 0x9029 /* Secure */ + +/* OTP (One-Time Programmable) Memory */ +#define TISCI_MSG_READ_OTP_MMR 0x9022 /* Secure */ +#define TISCI_MSG_WRITE_OTP_ROW 0x9023 /* Secure */ +#define TISCI_MSG_LOCK_OTP_ROW 0x9024 /* Secure */ +#define TISCI_MSG_SOFT_LOCK_OTP_WRITE_GLOBAL 0x9025 /* Secure */ +#define TISCI_MSG_GET_OTP_ROW_LOCK_STATUS 0x9026 /* Secure */ + +/* Security Handover and Key Writing */ +#define TISCI_MSG_SEC_HANDOVER 0x9030 /* Secure */ +#define TISCI_MSG_KEY_WRITER 0x9031 /* Secure */ +#define TISCI_MSG_WRITE_SWREV 0x9032 /* Secure */ +#define TISCI_MSG_READ_SWREV 0x9033 /* Secure */ +#define TISCI_MSG_READ_KEYCNT_KEYREV 0x9034 /* Secure */ +#define TISCI_MSG_WRITE_KEYREV 0x9035 /* Secure */ + +/* Cryptographic Key Management (DSMEK) */ +#define TISCI_MSG_SA2UL_GET_DSMEK 0x9036 /* Secure */ +#define TISCI_MSG_SA2UL_SET_DSMEK 0x9037 /* Secure */ +#define TISCI_MSG_SA2UL_RELEASE_DSMEK 0x9038 /* Secure */ + +/* Processor Control Messages */ +#define TISCI_MSG_PROC_REQUEST 0xc000 +#define TISCI_MSG_PROC_RELEASE 0xc001 +#define TISCI_MSG_PROC_HANDOVER 0xc005 +#define TISCI_MSG_SET_PROC_BOOT_CONFIG 0xc100 +#define TISCI_MSG_SET_PROC_BOOT_CTRL 0xc101 +#define TISCI_MSG_PROC_AUTH_BOOT_IMAGE 0xc120 +#define TISCI_MSG_GET_PROC_BOOT_STATUS 0xc400 +#define TISCI_MSG_WAIT_PROC_BOOT_STATUS 0xc401 /** * @struct rx_msg From 489966e805df8e11d300732186b7659a8f9f36b7 Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 13:53:44 +0530 Subject: [PATCH 064/143] UPSTREAM: drivers: firmware: tisci: Add secure property commit b1bc08eb5a016bebffeba14042d9ac1f0426a58a upstream. - Add ti,is-secure device tree property to indicate secure host entities - Add is_secure field in device config Signed-off-by: Dave Joseph --- drivers/firmware/tisci/tisci.c | 2 ++ dts/bindings/firmware/ti,k2g-sci.yaml | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/firmware/tisci/tisci.c b/drivers/firmware/tisci/tisci.c index 0206289bcbed..d2f4c9a9ac49 100644 --- a/drivers/firmware/tisci/tisci.c +++ b/drivers/firmware/tisci/tisci.c @@ -31,6 +31,7 @@ struct tisci_config { uint32_t host_id; int max_msg_size; int max_rx_timeout_ms; + bool is_secure; }; /** @@ -1595,6 +1596,7 @@ static int tisci_init(const struct device *dev) .host_id = DT_INST_PROP(_n, ti_host_id), \ .max_msg_size = MAILBOX_MBOX_SIZE, \ .max_rx_timeout_ms = 10000, \ + .is_secure = DT_INST_PROP_OR(_n, ti_is_secure, false), \ }; \ DEVICE_DT_INST_DEFINE(_n, tisci_init, NULL, &tisci_data_##_n, &tisci_config_##_n, \ PRE_KERNEL_1, CONFIG_TISCI_INIT_PRIORITY, NULL); diff --git a/dts/bindings/firmware/ti,k2g-sci.yaml b/dts/bindings/firmware/ti,k2g-sci.yaml index ebdbcb97dd5b..39a3d7cdec61 100644 --- a/dts/bindings/firmware/ti,k2g-sci.yaml +++ b/dts/bindings/firmware/ti,k2g-sci.yaml @@ -16,6 +16,10 @@ properties: required: true description: Host ID for processor + ti,is-secure: + type: boolean + description: Indicates if the host is a secure entity + mboxes: description: phandle to the MBOX controller (TX and RX are required) required: true From aa8b2b40e17a7d64a57b4e5cfd2dd06aebf5343f Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 13:54:49 +0530 Subject: [PATCH 065/143] UPSTREAM: drivers: firmware: tisci: Add secure path support commit 3ad7b4707153c8a6e3f21958839b2295f1288ab7 upstream. Add support for secure TISCI communication paths to enable message exchange with secure contexts on TI SoCs. - Add secure header wrapping for outbound messages on secure path - Add secure header parsing for inbound messages on secure path Signed-off-by: Dave Joseph --- drivers/firmware/tisci/tisci.c | 53 +++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/tisci/tisci.c b/drivers/firmware/tisci/tisci.c index d2f4c9a9ac49..5ac1d3a2181d 100644 --- a/drivers/firmware/tisci/tisci.c +++ b/drivers/firmware/tisci/tisci.c @@ -139,12 +139,26 @@ static int tisci_get_response(const struct device *dev, struct tisci_xfer *xfer) return -EINVAL; } + if (config->is_secure) { + /* In secure mode, response includes 4-byte secure header */ + xfer->rx_message.size += sizeof(struct tisci_secure_msg_hdr); + } + if (data->rx_message.size < xfer->rx_message.size) { - LOG_ERR("rx_message.size [ %d ] < xfer->rx_message.size\n", data->rx_message.size); + LOG_ERR("rx_message.size [ %zu ] < xfer->rx_message.size [ %zu ]\n", + data->rx_message.size, xfer->rx_message.size); return -EINVAL; } - memcpy(xfer->rx_message.buf, data->rx_message.buf, xfer->rx_message.size); + if (config->is_secure) { + xfer->rx_message.size -= sizeof(struct tisci_secure_msg_hdr); + /* Skip secure header and copy tisci_msg_hdr + payload */ + memcpy(xfer->rx_message.buf, + (uint8_t *)data->rx_message.buf + sizeof(struct tisci_secure_msg_hdr), + xfer->rx_message.size); + } else { + memcpy(xfer->rx_message.buf, data->rx_message.buf, xfer->rx_message.size); + } hdr = (struct tisci_msg_hdr *)xfer->rx_message.buf; /* Sanity check for message response */ @@ -159,17 +173,48 @@ static int tisci_get_response(const struct device *dev, struct tisci_xfer *xfer) static int tisci_do_xfer(const struct device *dev, struct tisci_xfer *xfer) { - if (!dev) { + if (!dev || !xfer) { return -EINVAL; } + struct tisci_data *data = dev->data; const struct tisci_config *config = dev->config; struct mbox_msg *msg = &xfer->tx_message; int ret; + /* Stack buffer for secure messaging (max 60 bytes total) */ + uint8_t secure_buf[MAILBOX_MBOX_SIZE]; + struct mbox_msg secure_msg; + + if (config->is_secure) { + struct tisci_secure_msg_hdr secure_hdr; + + /* Verify message fits with secure header (already checked in max_msg_size) */ + if (msg->size + sizeof(struct tisci_secure_msg_hdr) > MAILBOX_MBOX_SIZE) { + LOG_ERR("Message too large for secure mailbox (%zu + %zu > %d)\n", + msg->size, sizeof(struct tisci_secure_msg_hdr), MAILBOX_MBOX_SIZE); + k_sem_give(&data->data_sem); + return -EMSGSIZE; + } + + /* Prepare secure header */ + secure_hdr.checksum = 0; + secure_hdr.reserved = 0; + + /* Copy header and message into secure buffer */ + memcpy(secure_buf, &secure_hdr, sizeof(struct tisci_secure_msg_hdr)); + memcpy(secure_buf + sizeof(struct tisci_secure_msg_hdr), msg->data, msg->size); + + /* Use temporary message structure to avoid modifying original */ + secure_msg.data = secure_buf; + secure_msg.size = msg->size + sizeof(struct tisci_secure_msg_hdr); + msg = &secure_msg; + } + ret = mbox_send_dt(&config->mbox_tx, msg); if (ret < 0) { - LOG_ERR("Could not send (%d)\n", ret); + LOG_ERR("Could not send on %s path\n", + config->is_secure ? "secure" : "non-secure"); return ret; } From d73605457bda1369368ddea77ca33dc476e7e7d4 Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 13:55:30 +0530 Subject: [PATCH 066/143] UPSTREAM: drivers: firmware: tisci: Fix semaphore cleanup commit 2f3af507e9c6d32829c4b0b3999294b5b90bf339 upstream. - Add proper semaphore cleanup in error paths where it was missing Signed-off-by: Dave Joseph --- drivers/firmware/tisci/tisci.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/firmware/tisci/tisci.c b/drivers/firmware/tisci/tisci.c index 5ac1d3a2181d..ed3f4bb16fac 100644 --- a/drivers/firmware/tisci/tisci.c +++ b/drivers/firmware/tisci/tisci.c @@ -75,6 +75,7 @@ static struct tisci_xfer *tisci_setup_one_xfer(const struct device *dev, uint16_ if (rx_message_size > config->max_msg_size || tx_message_size > config->max_msg_size || (rx_message_size > 0 && rx_message_size < sizeof(*hdr)) || tx_message_size < sizeof(*hdr)) { + k_sem_give(&data->data_sem); return NULL; } @@ -125,17 +126,20 @@ static int tisci_get_response(const struct device *dev, struct tisci_xfer *xfer) if (!xfer->rx_message.buf) { LOG_ERR("No response buffer provided"); + k_sem_give(&data->data_sem); return -EINVAL; } if (k_sem_take(data->rx_message.response_ready_sem, K_MSEC(config->max_rx_timeout_ms)) != 0) { LOG_ERR("Timeout waiting for response"); + k_sem_give(&data->data_sem); return -ETIMEDOUT; } if (xfer->rx_message.size > config->max_msg_size) { LOG_ERR("rx_message.size [ %d ] > max_msg_size\n", xfer->rx_message.size); + k_sem_give(&data->data_sem); return -EINVAL; } @@ -147,6 +151,7 @@ static int tisci_get_response(const struct device *dev, struct tisci_xfer *xfer) if (data->rx_message.size < xfer->rx_message.size) { LOG_ERR("rx_message.size [ %zu ] < xfer->rx_message.size [ %zu ]\n", data->rx_message.size, xfer->rx_message.size); + k_sem_give(&data->data_sem); return -EINVAL; } @@ -164,6 +169,7 @@ static int tisci_get_response(const struct device *dev, struct tisci_xfer *xfer) /* Sanity check for message response */ if (hdr->seq != data->seq) { LOG_ERR("HDR seq != data seq [%d != %d]\n", hdr->seq, data->seq); + k_sem_give(&data->data_sem); return -EINVAL; } @@ -215,6 +221,7 @@ static int tisci_do_xfer(const struct device *dev, struct tisci_xfer *xfer) if (ret < 0) { LOG_ERR("Could not send on %s path\n", config->is_secure ? "secure" : "non-secure"); + k_sem_give(&data->data_sem); return ret; } @@ -226,8 +233,12 @@ static int tisci_do_xfer(const struct device *dev, struct tisci_xfer *xfer) } if (!tisci_is_response_ack(xfer->rx_message.buf)) { LOG_ERR("TISCI Response in NACK\n"); + k_sem_give(&data->data_sem); return -ENODEV; } + } else { + /* No response requested, release semaphore */ + k_sem_give(&data->data_sem); } return 0; From 191109a5f55cc8805541a3ff6edefd8a8ebb2e3c Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Fri, 27 Feb 2026 19:19:14 +0530 Subject: [PATCH 067/143] UPSTREAM: clock_control: tisci: change initialization priority commit 92ed4ba2385fe79683fb69360756f3d5cc1283fe upstream. Change priority from KERNEL_INIT_PRIORITY_OBJECTS to KERNEL_INIT_PRIORITY_DEFAULT so that the priority dependency on TISCI protocol/firmware layer gets fulfilled. Signed-off-by: Amneesh Singh --- drivers/clock_control/Kconfig.tisci | 7 +++++++ drivers/clock_control/clock_control_tisci.c | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/clock_control/Kconfig.tisci b/drivers/clock_control/Kconfig.tisci index e56d5023b2ab..1d1045e4f7f1 100644 --- a/drivers/clock_control/Kconfig.tisci +++ b/drivers/clock_control/Kconfig.tisci @@ -7,3 +7,10 @@ config CLOCK_CONTROL_TISCI depends on DT_HAS_TI_K2G_SCI_CLK_ENABLED help Driver for TISCI based clock control. + +config CLOCK_CONTROL_TISCI_PRIORITY + int "TISCI Clock Controller Priority" + default KERNEL_INIT_PRIORITY_DEFAULT + help + TISCI clock controller priority should always be equal or greater + than the TISCI priority. diff --git a/drivers/clock_control/clock_control_tisci.c b/drivers/clock_control/clock_control_tisci.c index 06a724404f20..70ef61abeb1d 100644 --- a/drivers/clock_control/clock_control_tisci.c +++ b/drivers/clock_control/clock_control_tisci.c @@ -95,6 +95,6 @@ static DEVICE_API(clock_control, tisci_clock_driver_api) = { #define TI_K2G_SCI_CLK_INIT(_n) \ DEVICE_DT_INST_DEFINE(_n, NULL, NULL, NULL, NULL, PRE_KERNEL_1, \ - CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &tisci_clock_driver_api); + CONFIG_CLOCK_CONTROL_TISCI_PRIORITY, &tisci_clock_driver_api); DT_INST_FOREACH_STATUS_OKAY(TI_K2G_SCI_CLK_INIT) From c8d82cbcdc6e7f4bec43af37d79fbe35c37dc550 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Fri, 27 Feb 2026 19:18:09 +0530 Subject: [PATCH 068/143] UPSTREAM: firmware: tisci: change initialization priority commit f479c0f1e125554611096a2d2a88ab365f9a53ad upstream. Change priority from KERNEL_INIT_PRIORITY_OBJECTS to KERNEL_INIT_PRIORITY_DEFAULT so that the priority dependency on TI secure proxy mailbox fulfilled. Signed-off-by: Amneesh Singh --- drivers/firmware/tisci/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/tisci/Kconfig b/drivers/firmware/tisci/Kconfig index 2cce10593419..59c299fe9f9d 100644 --- a/drivers/firmware/tisci/Kconfig +++ b/drivers/firmware/tisci/Kconfig @@ -15,7 +15,7 @@ if TISCI config TISCI_INIT_PRIORITY int "TISCI init priority" - default KERNEL_INIT_PRIORITY_OBJECTS + default KERNEL_INIT_PRIORITY_DEFAULT help Init priority for the TISCI driver. From a207b52489a8a63ea7f017f6e0a6b49a69a83cda Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Fri, 27 Feb 2026 19:15:15 +0530 Subject: [PATCH 069/143] UPSTREAM: mbox: ti_secproxy: change initialization priority commit fe1103b044274aa8937e31a8d7a8458243b6c841 upstream. Remove MBOX_TI_SECURE_PROXY_PRIORITY in favor of MBOX_INIT_PRIORITY which by default is set to 40, which is desirable so that the priority dependency on interrupt controllers like ARM GICv3 gets fulfilled. This change is for convenience of building at default priorities. Signed-off-by: Amneesh Singh --- drivers/mbox/Kconfig.ti_secproxy | 6 ------ drivers/mbox/mbox_ti_secproxy.c | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/mbox/Kconfig.ti_secproxy b/drivers/mbox/Kconfig.ti_secproxy index 0f081fceea32..30c451e3513e 100644 --- a/drivers/mbox/Kconfig.ti_secproxy +++ b/drivers/mbox/Kconfig.ti_secproxy @@ -7,9 +7,3 @@ config MBOX_TI_SECURE_PROXY depends on DT_HAS_TI_SECURE_PROXY_ENABLED help Driver for TI Secure Proxy Mailbox. - -config MBOX_TI_SECURE_PROXY_PRIORITY - int "MBOX_TI_SECURE_PROXY_PRIORITY" - default KERNEL_INIT_PRIORITY_OBJECTS - help - Mbox secproxy initialization priority. diff --git a/drivers/mbox/mbox_ti_secproxy.c b/drivers/mbox/mbox_ti_secproxy.c index a48dfe983953..aa2aa4901a9c 100644 --- a/drivers/mbox/mbox_ti_secproxy.c +++ b/drivers/mbox/mbox_ti_secproxy.c @@ -381,7 +381,7 @@ static DEVICE_API(mbox, secproxy_mailbox_driver_api) = { } \ DEVICE_DT_INST_DEFINE(idx, secproxy_mailbox_##idx##_init, NULL, \ &secproxy_mailbox_##idx##_data, &secproxy_mailbox_##idx##_config, \ - PRE_KERNEL_1, CONFIG_MBOX_TI_SECURE_PROXY_PRIORITY, \ + PRE_KERNEL_1, CONFIG_MBOX_INIT_PRIORITY, \ &secproxy_mailbox_driver_api) #define MAILBOX_INST(idx) MAILBOX_INSTANCE_DEFINE(idx); From e23632ab392ca576b2f95d11ae23113e61802cf4 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Fri, 27 Feb 2026 18:42:11 +0530 Subject: [PATCH 070/143] UPSTREAM: clock_control: tisci: fix macro argument commit abdfea826fb5adc9e718eb8b87259a009b23188d upstream. TISCI_GET_CLOCK_DETAILS_BY_INST takes the instance number of the DT node, this is supposed to be denoted by `inst`. However, due to a typo it is currently DT_DRV_INST which makes no sense since that is a separate macro of its own. Hence, fix this typo. Signed-off-by: Amneesh Singh --- include/zephyr/drivers/clock_control/tisci_clock_control.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/zephyr/drivers/clock_control/tisci_clock_control.h b/include/zephyr/drivers/clock_control/tisci_clock_control.h index f9457c25875b..7708bdd014fd 100644 --- a/include/zephyr/drivers/clock_control/tisci_clock_control.h +++ b/include/zephyr/drivers/clock_control/tisci_clock_control.h @@ -36,6 +36,6 @@ struct tisci_clock_config { #define TISCI_GET_CLOCK_BY_INST(inst) TISCI_GET_CLOCK(DT_DRV_INST(inst)) -#define TISCI_GET_CLOCK_DETAILS_BY_INST(DT_DRV_INST) TISCI_GET_CLOCK_DETAILS(DT_DRV_INST(inst)) +#define TISCI_GET_CLOCK_DETAILS_BY_INST(inst) TISCI_GET_CLOCK_DETAILS(DT_DRV_INST(inst)) #endif From 85aed5296bf3a6c18c96c0291c435752693d6d74 Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 13:55:52 +0530 Subject: [PATCH 071/143] UPSTREAM: mbox: mbox_ti_secproxy: Make verify thread function non-blocking commit 70fec30e5eef9a2e466762896689a2fcdec0ea90 upstream. Remove busy wait from secproxy_verify_thread() to return immediately with error status. If mailbox is full, waiting for message processing is unnecessary and blocks execution until timeout, reporting a timeout error message reducing debuggability. The mailbox driver should only return the appropriate error code. Any required retry logic can be implemented in the caller layer. Signed-off-by: Dave Joseph --- drivers/mbox/mbox_ti_secproxy.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/mbox/mbox_ti_secproxy.c b/drivers/mbox/mbox_ti_secproxy.c index aa2aa4901a9c..d7190eeee775 100644 --- a/drivers/mbox/mbox_ti_secproxy.c +++ b/drivers/mbox/mbox_ti_secproxy.c @@ -32,7 +32,6 @@ LOG_MODULE_REGISTER(ti_secure_proxy); #define RT_THREAD_STATUS_CUR_CNT_MASK GENMASK(7, 0) #define SCFG_THREAD_CTRL 0x1000 -#define SCFG_THREAD_CTRL_DIR_SHIFT 31 #define SCFG_THREAD_CTRL_DIR_MASK BIT(31) #define SEC_PROXY_THREAD(base, x) ((base) + (0x1000 * (x))) @@ -46,8 +45,6 @@ LOG_MODULE_REGISTER(ti_secure_proxy); #define SEC_PROXY_DATA_START_OFFS 0x4 #define SEC_PROXY_DATA_END_OFFS 0x3c -#define SEC_PROXY_TIMEOUT_US 1000000 - #define GET_MSG_SEQ(buffer) ((uint32_t *)buffer)[1] struct secproxy_thread { mem_addr_t target_data; @@ -89,14 +86,15 @@ struct secproxy_mailbox_config { static inline int secproxy_verify_thread(struct secproxy_thread *spt, uint8_t dir) { /* Check for any errors already available */ - if (sys_read32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_ERROR_MASK) { + uint32_t status = sys_read32(spt->rt + RT_THREAD_STATUS); + + if (status & RT_THREAD_STATUS_ERROR_MASK) { LOG_ERR("Thread is corrupted, cannot send data.\n"); return -EINVAL; } /* Make sure thread is configured for right direction */ - if ((sys_read32(spt->scfg + SCFG_THREAD_CTRL) & SCFG_THREAD_CTRL_DIR_MASK) != - (dir << SCFG_THREAD_CTRL_DIR_SHIFT)) { + if (FIELD_GET(SCFG_THREAD_CTRL_DIR_MASK, sys_read32(spt->scfg + SCFG_THREAD_CTRL)) != dir) { if (dir == THREAD_IS_TX) { LOG_ERR("Trying to send data on RX Thread\n"); } else { @@ -105,18 +103,11 @@ static inline int secproxy_verify_thread(struct secproxy_thread *spt, uint8_t di return -EINVAL; } - /* Check the message queue before sending/receiving data */ - int timeout_ms = SEC_PROXY_TIMEOUT_US; - int waited_ms = 0; - const int poll_interval_ms = 1000; - - while (!(sys_read32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_CUR_CNT_MASK)) { - k_busy_wait(poll_interval_ms); - waited_ms += poll_interval_ms; - if (waited_ms >= timeout_ms) { - LOG_ERR("Timeout waiting for thread to %s\n", - (dir == THREAD_IS_TX) ? "empty" : "fill"); - return -ETIMEDOUT; + if ((status & RT_THREAD_STATUS_CUR_CNT_MASK) == 0) { + if (dir == THREAD_IS_TX) { + return -EBUSY; + } else { + return -ENODATA; } } From 6d72066e0629a299c0ee6ed10aab79f8bce5ac2f Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 13:56:08 +0530 Subject: [PATCH 072/143] UPSTREAM: mbox: mbox_ti_secproxy: Forward error codes in send path commit d1db9b1ab7abfd16ae191d7dc6ba6bd8ff04cf62 upstream. Forward actual error codes from secproxy_verify_thread() instead of masking all errors as -EBUSY. Now returns -EINVAL, -EBUSY, or -ENODATA from secproxy_mailbox_send() to aid debugging. Signed-off-by: Dave Joseph --- drivers/mbox/mbox_ti_secproxy.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mbox/mbox_ti_secproxy.c b/drivers/mbox/mbox_ti_secproxy.c index d7190eeee775..a0d0b2ba2761 100644 --- a/drivers/mbox/mbox_ti_secproxy.c +++ b/drivers/mbox/mbox_ti_secproxy.c @@ -205,6 +205,7 @@ static int secproxy_mailbox_send(const struct device *dev, uint32_t channel, struct secproxy_mailbox_data *data = DEV_DATA(dev); mem_addr_t data_reg; k_spinlock_key_t key; + uint32_t status; if (msg == NULL || msg->data == NULL) { LOG_ERR("Invalid parameters"); @@ -226,13 +227,13 @@ static int secproxy_mailbox_send(const struct device *dev, uint32_t channel, spt.rt = SEC_PROXY_THREAD(DEV_RT(dev), channel); spt.scfg = SEC_PROXY_THREAD(DEV_SCFG(dev), channel); - if (secproxy_verify_thread(&spt, THREAD_IS_TX)) { - LOG_ERR("Thread is in error state\n"); + status = secproxy_verify_thread(&spt, THREAD_IS_TX); + if (status != 0) { k_spin_unlock(&data->lock, key); - return -EBUSY; + return status; } - uint32_t status = sys_read32(spt.rt + RT_THREAD_STATUS); + status = sys_read32(spt.rt + RT_THREAD_STATUS); if ((status & RT_THREAD_STATUS_CUR_CNT_MASK) == (SECPROXY_MAILBOX_NUM_MSGS)) { k_spin_unlock(&data->lock, key); From 8da83a22ee7358d39fa34a9b2f73d49a458c9edd Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 13:56:29 +0530 Subject: [PATCH 073/143] UPSTREAM: mbox: mbox_ti_secproxy: Remove redundant error checks commit 27c3152710a81536a918d8f6332e0809aaf7da28 upstream. Remove duplicate error validation in ISR and send path that is already performed by secproxy_verify_thread(). Remove error mask checks, message queue fullness checks, and unused SECPROXY_MAILBOX_NUM_MSGS macro. Signed-off-by: Dave Joseph --- drivers/mbox/mbox_ti_secproxy.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/drivers/mbox/mbox_ti_secproxy.c b/drivers/mbox/mbox_ti_secproxy.c index a0d0b2ba2761..40f916baff72 100644 --- a/drivers/mbox/mbox_ti_secproxy.c +++ b/drivers/mbox/mbox_ti_secproxy.c @@ -38,9 +38,8 @@ LOG_MODULE_REGISTER(ti_secure_proxy); #define THREAD_IS_RX 1 #define THREAD_IS_TX 0 -#define SECPROXY_MAILBOX_NUM_MSGS 5 -#define MAILBOX_MAX_CHANNELS 32 -#define MAILBOX_MBOX_SIZE 60 +#define MAILBOX_MAX_CHANNELS 32 +#define MAILBOX_MBOX_SIZE 60 #define SEC_PROXY_DATA_START_OFFS 0x4 #define SEC_PROXY_DATA_END_OFFS 0x3c @@ -130,19 +129,7 @@ static void secproxy_mailbox_isr(const struct device *dev) spt.rt = SEC_PROXY_THREAD(DEV_RT(dev), i_channel); spt.scfg = SEC_PROXY_THREAD(DEV_SCFG(dev), i_channel); - uint32_t status = sys_read32(spt.rt + RT_THREAD_STATUS); - - if (status & RT_THREAD_STATUS_ERROR_MASK) { - LOG_ERR("Thread %d error state detected in ISR", i_channel); - continue; - } - if (secproxy_verify_thread(&spt, THREAD_IS_RX)) { - LOG_ERR("Thread %d is in error state\n", i_channel); - continue; - } - - if (!(sys_read32(spt.rt) & 0x7F)) { continue; } @@ -233,13 +220,6 @@ static int secproxy_mailbox_send(const struct device *dev, uint32_t channel, return status; } - status = sys_read32(spt.rt + RT_THREAD_STATUS); - - if ((status & RT_THREAD_STATUS_CUR_CNT_MASK) == (SECPROXY_MAILBOX_NUM_MSGS)) { - k_spin_unlock(&data->lock, key); - return -EBUSY; - } - if (msg->size > MAILBOX_MBOX_SIZE) { LOG_ERR("Message size %zu exceeds max size %d", msg->size, MAILBOX_MBOX_SIZE); k_spin_unlock(&data->lock, key); From f049d3d0ac0bd220cc5b164cd7504da3f3fdfb5c Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 13:56:54 +0530 Subject: [PATCH 074/143] UPSTREAM: mbox: mbox_ti_secproxy: Add interrupt-names to DT binding commit 05c18bbf325f57afaf42c5d6f0ff798d1564b573 upstream. Add interrupt-names property to binding to enable per-channel interrupt lookup via DT_INST_IRQ_BY_NAME(). Each channel's interrupt is identified by name: "rx_0", "rx_1", "rx_2", etc. Signed-off-by: Dave Joseph --- dts/bindings/mbox/ti,secure-proxy.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dts/bindings/mbox/ti,secure-proxy.yaml b/dts/bindings/mbox/ti,secure-proxy.yaml index 807f78f7a2a9..a0b2b10343b4 100644 --- a/dts/bindings/mbox/ti,secure-proxy.yaml +++ b/dts/bindings/mbox/ti,secure-proxy.yaml @@ -14,5 +14,8 @@ properties: interrupts: required: true + interrupt-names: + required: true + mbox-cells: - channel From 1fe01e660023e33153cfdc0baf38ace1f35601aa Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 14:09:16 +0530 Subject: [PATCH 075/143] UPSTREAM: mbox: mbox_ti_secproxy: Split AM64x R5F interrupts to per-core... commit 6a09fcea68bcfb50b496d045dc62b8efec926382 upstream. Split interrupt configuration from am64x_r5.dtsi into individual R5F core files (am64x_r5f0_0.dtsi, am64x_r5f0_1.dtsi, am64x_r5f1_0.dtsi, am64x_r5f1_1.dtsi) as each core uses different mailbox channels and VIM interrupt line mappings. Signed-off-by: Dave Joseph --- dts/arm/ti/am64x_r5.dtsi | 10 ---------- dts/arm/ti/am64x_r5f0_0.dtsi | 12 ++++++++++++ dts/arm/ti/am64x_r5f0_1.dtsi | 12 ++++++++++++ dts/arm/ti/am64x_r5f1_0.dtsi | 12 ++++++++++++ dts/arm/ti/am64x_r5f1_1.dtsi | 12 ++++++++++++ 5 files changed, 48 insertions(+), 10 deletions(-) diff --git a/dts/arm/ti/am64x_r5.dtsi b/dts/arm/ti/am64x_r5.dtsi index f95a2ef08739..3084c9afa44e 100644 --- a/dts/arm/ti/am64x_r5.dtsi +++ b/dts/arm/ti/am64x_r5.dtsi @@ -58,16 +58,6 @@ }; }; -&dmsc { - ti,host-id = <36>; - mboxes = <&secure_proxy_main 2>, <&secure_proxy_main 3>; -}; - -&secure_proxy_main { - interrupts = <0 65 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>; - interrupt-parent = <&vim>; -}; - &main_timer0 { interrupts = <0 152 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; interrupt-parent = <&vim>; diff --git a/dts/arm/ti/am64x_r5f0_0.dtsi b/dts/arm/ti/am64x_r5f0_0.dtsi index 58c8c00715a2..064e80d2c9fa 100644 --- a/dts/arm/ti/am64x_r5f0_0.dtsi +++ b/dts/arm/ti/am64x_r5f0_0.dtsi @@ -15,3 +15,15 @@ interrupts = <0 0 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; interrupt-parent = <&vim>; }; + +&dmsc { + ti,host-id = <36>; + mboxes = <&secure_proxy_main 2>, <&secure_proxy_main 3>; +}; + +&secure_proxy_main { + interrupt-names = "rx_0", "rx_2"; + interrupts = <0 64 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>, + <0 65 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; diff --git a/dts/arm/ti/am64x_r5f0_1.dtsi b/dts/arm/ti/am64x_r5f0_1.dtsi index 1af8efbfa1ef..82715b94f440 100644 --- a/dts/arm/ti/am64x_r5f0_1.dtsi +++ b/dts/arm/ti/am64x_r5f0_1.dtsi @@ -15,3 +15,15 @@ interrupts = <0 0 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; interrupt-parent = <&vim>; }; + +&dmsc { + ti,host-id = <38>; + mboxes = <&secure_proxy_main 6>, <&secure_proxy_main 7>; +}; + +&secure_proxy_main { + interrupt-names = "rx_4", "rx_6"; + interrupts = <0 66 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>, + <0 67 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; diff --git a/dts/arm/ti/am64x_r5f1_0.dtsi b/dts/arm/ti/am64x_r5f1_0.dtsi index 22a3d991881a..635346f638af 100644 --- a/dts/arm/ti/am64x_r5f1_0.dtsi +++ b/dts/arm/ti/am64x_r5f1_0.dtsi @@ -15,3 +15,15 @@ interrupts = <0 0 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; interrupt-parent = <&vim>; }; + +&dmsc { + ti,host-id = <41>; + mboxes = <&secure_proxy_main 20>, <&secure_proxy_main 21>; +}; + +&secure_proxy_main { + interrupt-names = "rx_18", "rx_20"; + interrupts = <0 64 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>, + <0 65 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; diff --git a/dts/arm/ti/am64x_r5f1_1.dtsi b/dts/arm/ti/am64x_r5f1_1.dtsi index a380eae0f8d4..3dd4956bb77a 100644 --- a/dts/arm/ti/am64x_r5f1_1.dtsi +++ b/dts/arm/ti/am64x_r5f1_1.dtsi @@ -15,3 +15,15 @@ interrupts = <0 0 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; interrupt-parent = <&vim>; }; + +&dmsc { + ti,host-id = <43>; + mboxes = <&secure_proxy_main 24>, <&secure_proxy_main 25>; +}; + +&secure_proxy_main { + interrupt-names = "rx_22", "rx_24"; + interrupts = <0 66 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>, + <0 67 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; From 8aa20345823a5a3c7edf8d6d9126030087d750aa Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 15:06:17 +0530 Subject: [PATCH 076/143] UPSTREAM: mbox: mbox_ti_secproxy: Implement per-channel interrupt handling commit aaf192466853aa869ec9eea6559548630359ceb4 upstream. Replace single shared interrupt with per-channel interrupt support. Change .irq to .interrupts[] array in config, use LISTIFY macros for ISR generation and connection, add channel_enable[] array for per-channel state tracking. Each rx channel now has independent ISR. Signed-off-by: Dave Joseph --- drivers/mbox/mbox_ti_secproxy.c | 179 +++++++++++++++++++------------- 1 file changed, 105 insertions(+), 74 deletions(-) diff --git a/drivers/mbox/mbox_ti_secproxy.c b/drivers/mbox/mbox_ti_secproxy.c index 40f916baff72..efb9981ae071 100644 --- a/drivers/mbox/mbox_ti_secproxy.c +++ b/drivers/mbox/mbox_ti_secproxy.c @@ -79,7 +79,7 @@ struct secproxy_mailbox_config { DEVICE_MMIO_NAMED_ROM(target_data); DEVICE_MMIO_NAMED_ROM(rt); DEVICE_MMIO_NAMED_ROM(scfg); - uint32_t irq; + int32_t interrupts[MAILBOX_MAX_CHANNELS]; }; static inline int secproxy_verify_thread(struct secproxy_thread *spt, uint8_t dir) @@ -113,75 +113,74 @@ static inline int secproxy_verify_thread(struct secproxy_thread *spt, uint8_t di return 0; } -static void secproxy_mailbox_isr(const struct device *dev) +static void secproxy_mailbox_isr(const struct device *dev, uint32_t channel) { struct secproxy_mailbox_data *data = DEV_DATA(dev); struct secproxy_thread spt; uint32_t data_word; mem_addr_t data_reg; - for (int i_channel = 0; i_channel < MAILBOX_MAX_CHANNELS; i_channel++) { - if (!data->channel_enable[i_channel]) { - continue; - } - - spt.target_data = SEC_PROXY_THREAD(DEV_TDATA(dev), i_channel); - spt.rt = SEC_PROXY_THREAD(DEV_RT(dev), i_channel); - spt.scfg = SEC_PROXY_THREAD(DEV_SCFG(dev), i_channel); + if (!data->channel_enable[channel]) { + return; + } - if (secproxy_verify_thread(&spt, THREAD_IS_RX)) { - continue; - } + spt.target_data = SEC_PROXY_THREAD(DEV_TDATA(dev), channel); + spt.rt = SEC_PROXY_THREAD(DEV_RT(dev), channel); + spt.scfg = SEC_PROXY_THREAD(DEV_SCFG(dev), channel); - data_reg = spt.target_data + SEC_PROXY_DATA_START_OFFS; - size_t msg_len = MAILBOX_MBOX_SIZE; - size_t num_words = msg_len / sizeof(uint32_t); - size_t i; - struct rx_msg *rx_data = data->user_data[i_channel]; + if (secproxy_verify_thread(&spt, THREAD_IS_RX)) { + /* Silent failure */ + return; + } - if (!rx_data || !rx_data->buf) { - LOG_ERR("No buffer provided for channel %d", i_channel); - continue; - } + data_reg = spt.target_data + SEC_PROXY_DATA_START_OFFS; + size_t msg_len = MAILBOX_MBOX_SIZE; + size_t num_words = msg_len / sizeof(uint32_t); + size_t i; + struct rx_msg *rx_data = data->user_data[channel]; + + if (!rx_data || !rx_data->buf) { + LOG_ERR("No buffer provided for channel %d", channel); + return; + } - if (rx_data->size < MAILBOX_MBOX_SIZE) { - LOG_ERR("Buffer too small for channel %d", i_channel); - continue; - } + if (rx_data->size < MAILBOX_MBOX_SIZE) { + LOG_ERR("Buffer too small for channel %d", channel); + return; + } - uint8_t *buf = (uint8_t *)rx_data->buf; + uint8_t *buf = (uint8_t *)rx_data->buf; - /* Copy full words */ - for (i = 0; i < num_words; i++) { - data_word = sys_read32(data_reg); - memcpy(&buf[i * 4], &data_word, sizeof(uint32_t)); - data_reg += sizeof(uint32_t); - } + /* Copy full words */ + for (i = 0; i < num_words; i++) { + data_word = sys_read32(data_reg); + memcpy(&buf[i * 4], &data_word, sizeof(uint32_t)); + data_reg += sizeof(uint32_t); + } - /* Handle trail bytes */ - size_t trail_bytes = msg_len % sizeof(uint32_t); + /* Handle trail bytes */ + size_t trail_bytes = msg_len % sizeof(uint32_t); - if (trail_bytes) { - uint32_t data_trail = sys_read32(data_reg); + if (trail_bytes) { + uint32_t data_trail = sys_read32(data_reg); - i = msg_len - trail_bytes; + i = msg_len - trail_bytes; - while (trail_bytes--) { - buf[i++] = data_trail & 0xff; - data_trail >>= 8; - } + while (trail_bytes--) { + buf[i++] = data_trail & 0xff; + data_trail >>= 8; } + } - /* Ensure we read the last register if we haven't already */ - if (data_reg <= (spt.target_data + SEC_PROXY_DATA_END_OFFS)) { - sys_read32(spt.target_data + SEC_PROXY_DATA_END_OFFS); - } + /* Ensure we read the last register if we haven't already */ + if (data_reg <= (spt.target_data + SEC_PROXY_DATA_END_OFFS)) { + sys_read32(spt.target_data + SEC_PROXY_DATA_END_OFFS); + } - rx_data->size = msg_len; - rx_data->seq = GET_MSG_SEQ(buf); - if (data->cb[i_channel]) { - data->cb[i_channel](dev, i_channel, data->user_data[i_channel], NULL); - } + rx_data->size = msg_len; + rx_data->seq = GET_MSG_SEQ(buf); + if (data->cb[channel]) { + data->cb[channel](dev, channel, data->user_data[channel], NULL); } } @@ -310,13 +309,18 @@ static int secproxy_mailbox_set_enabled(const struct device *dev, uint32_t chann return -EALREADY; } + if (cfg->interrupts[channel] < 0) { + LOG_ERR("No interrupt configured for channel %d", channel); + return -EINVAL; + } + key = k_spin_lock(&data->lock); data->channel_enable[channel] = enable; if (enable) { - irq_enable(cfg->irq); + irq_enable(cfg->interrupts[channel]); } else { - irq_disable(cfg->irq); + irq_disable(cfg->interrupts[channel]); } k_spin_unlock(&data->lock, key); @@ -332,27 +336,54 @@ static DEVICE_API(mbox, secproxy_mailbox_driver_api) = { .set_enabled = secproxy_mailbox_set_enabled, }; -#define MAILBOX_INSTANCE_DEFINE(idx) \ - static struct secproxy_mailbox_data secproxy_mailbox_##idx##_data; \ - const static struct secproxy_mailbox_config secproxy_mailbox_##idx##_config = { \ - DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(target_data, DT_DRV_INST(idx)), \ - DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(rt, DT_DRV_INST(idx)), \ - DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(scfg, DT_DRV_INST(idx)), \ - .irq = DT_INST_IRQN(idx), \ - }; \ - static int secproxy_mailbox_##idx##_init(const struct device *dev) \ - { \ - DEVICE_MMIO_NAMED_MAP(dev, target_data, K_MEM_CACHE_NONE); \ - DEVICE_MMIO_NAMED_MAP(dev, rt, K_MEM_CACHE_NONE); \ - DEVICE_MMIO_NAMED_MAP(dev, scfg, K_MEM_CACHE_NONE); \ - IRQ_CONNECT(DT_INST_IRQN(idx), DT_INST_IRQ(idx, priority), secproxy_mailbox_isr, \ - DEVICE_DT_INST_GET(idx), \ - COND_CODE_1(DT_INST_IRQ_HAS_CELL(idx, flags), \ - (DT_INST_IRQ(idx, flags)), (0))); \ - return 0; \ - } \ - DEVICE_DT_INST_DEFINE(idx, secproxy_mailbox_##idx##_init, NULL, \ - &secproxy_mailbox_##idx##_data, &secproxy_mailbox_##idx##_config, \ +#define SECPROXY_THREAD_ISR(i, idx) \ + COND_CODE_1(DT_INST_IRQ_HAS_NAME(idx, DT_CAT(rx_, i)), \ + ( \ + static void secproxy_mailbox_isr_##idx##_##i(const struct device *dev) \ + { \ + secproxy_mailbox_isr(dev, i); \ + } \ + ), \ + ()) + +/* Generate IRQ number or -1 for array initialization */ +#define SECPROXY_IRQ_OR_INVALID(i, inst) \ + COND_CODE_1(DT_INST_IRQ_HAS_NAME(inst, DT_CAT(rx_, i)), \ + (DT_INST_IRQ_BY_NAME(inst, DT_CAT(rx_, i), irq)), \ + (-1)) + +#define SECPROXY_IRQ_CONNECT(i, inst) \ + COND_CODE_1(DT_INST_IRQ_HAS_NAME(inst, DT_CAT(rx_, i)), \ + ( \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(inst, DT_CAT(rx_, i), irq), \ + DT_INST_IRQ_BY_NAME(inst, DT_CAT(rx_, i), priority), \ + secproxy_mailbox_isr_##inst##_##i, \ + DEVICE_DT_INST_GET(inst), \ + COND_CODE_1(DT_INST_IRQ_HAS_CELL(inst, flags), \ + (DT_INST_IRQ_BY_NAME(inst, DT_CAT(rx_, i), flags)), \ + (0))); \ + ), \ + ()) + +#define MAILBOX_INSTANCE_DEFINE(idx) \ + LISTIFY(MAILBOX_MAX_CHANNELS, SECPROXY_THREAD_ISR, (), idx) \ + static struct secproxy_mailbox_data secproxy_mailbox_##idx##_data; \ + const static struct secproxy_mailbox_config secproxy_mailbox_##idx##_config = { \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(target_data, DT_DRV_INST(idx)), \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(rt, DT_DRV_INST(idx)), \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(scfg, DT_DRV_INST(idx)), \ + .interrupts = {LISTIFY(MAILBOX_MAX_CHANNELS, \ + SECPROXY_IRQ_OR_INVALID, (,), idx) }}; \ + static int secproxy_mailbox_##idx##_init(const struct device *dev) \ + { \ + DEVICE_MMIO_NAMED_MAP(dev, target_data, K_MEM_CACHE_NONE); \ + DEVICE_MMIO_NAMED_MAP(dev, rt, K_MEM_CACHE_NONE); \ + DEVICE_MMIO_NAMED_MAP(dev, scfg, K_MEM_CACHE_NONE); \ + LISTIFY(MAILBOX_MAX_CHANNELS, SECPROXY_IRQ_CONNECT, (;), idx) \ + return 0; \ + } \ + DEVICE_DT_INST_DEFINE(idx, secproxy_mailbox_##idx##_init, NULL, \ + &secproxy_mailbox_##idx##_data, &secproxy_mailbox_##idx##_config, \ PRE_KERNEL_1, CONFIG_MBOX_INIT_PRIORITY, \ &secproxy_mailbox_driver_api) From 6d4d6fcfb6809c6f27fc24d0e663fe31af031d73 Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Wed, 18 Mar 2026 15:07:24 +0530 Subject: [PATCH 077/143] UPSTREAM: mbox: mbox_ti_secproxy: Flush rx thread on channel enable commit bfeb67b7342edb78c348937a7b72f14f478acf8d upstream. Add secproxy_mailbox_flush_thread() to clear stale messages from rx thread when enabling a channel. Read and discard all pending messages to ensure clean thread state and prevent errors from previous unclean state. Signed-off-by: Dave Joseph --- drivers/mbox/mbox_ti_secproxy.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/mbox/mbox_ti_secproxy.c b/drivers/mbox/mbox_ti_secproxy.c index efb9981ae071..910271123894 100644 --- a/drivers/mbox/mbox_ti_secproxy.c +++ b/drivers/mbox/mbox_ti_secproxy.c @@ -295,6 +295,21 @@ static uint32_t secproxy_mailbox_max_channels_get(const struct device *dev) return MAILBOX_MAX_CHANNELS; } +static void secproxy_mailbox_flush_thread(const struct device *dev, uint32_t channel) +{ + struct secproxy_thread spt; + + spt.target_data = SEC_PROXY_THREAD(DEV_TDATA(dev), channel); + spt.rt = SEC_PROXY_THREAD(DEV_RT(dev), channel); + spt.scfg = SEC_PROXY_THREAD(DEV_SCFG(dev), channel); + + /* Drain all pending messages from the thread */ + while ((sys_read32(spt.rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_CUR_CNT_MASK) > 0) { + /* Read from the last data register to consume one message */ + (void)sys_read32(spt.target_data + SEC_PROXY_DATA_END_OFFS); + } +} + static int secproxy_mailbox_set_enabled(const struct device *dev, uint32_t channel, bool enable) { const struct secproxy_mailbox_config *cfg = DEV_CFG(dev); @@ -318,6 +333,7 @@ static int secproxy_mailbox_set_enabled(const struct device *dev, uint32_t chann data->channel_enable[channel] = enable; if (enable) { + secproxy_mailbox_flush_thread(dev, channel); irq_enable(cfg->interrupts[channel]); } else { irq_disable(cfg->interrupts[channel]); From f7cdbd44133bfa698e766ecaf5c626f5cd0b5444 Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Mon, 23 Mar 2026 14:42:13 +0530 Subject: [PATCH 078/143] UPSTREAM-PEND: clock_control: tisci: Fix argument order in tisci_cmd_clk... Fix swapped dev_id and clk_id arguments in the calls to tisci_cmd_clk_is_on() and tisci_cmd_clk_is_off() within tisci_get_status(), aligning them with the TISCI API signature. PR: https://github.com/zephyrproject-rtos/zephyr/pull/106064 Signed-off-by: Dave Joseph --- drivers/clock_control/clock_control_tisci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clock_control/clock_control_tisci.c b/drivers/clock_control/clock_control_tisci.c index 70ef61abeb1d..47836acb163f 100644 --- a/drivers/clock_control/clock_control_tisci.c +++ b/drivers/clock_control/clock_control_tisci.c @@ -58,7 +58,7 @@ static enum clock_control_status tisci_get_status(const struct device *dev, bool curr_state = true; int ret; - ret = tisci_cmd_clk_is_on(dmsc, req->clk_id, req->dev_id, &req_state, &curr_state); + ret = tisci_cmd_clk_is_on(dmsc, req->dev_id, req->clk_id, &req_state, &curr_state); if (ret) { LOG_ERR("Failed to get clock ON status: dev_id=%u clk_id=%u err=%d", req->dev_id, req->clk_id, ret); @@ -73,7 +73,7 @@ static enum clock_control_status tisci_get_status(const struct device *dev, curr_state = true; - ret = tisci_cmd_clk_is_off(dmsc, req->clk_id, req->dev_id, NULL, &curr_state); + ret = tisci_cmd_clk_is_off(dmsc, req->dev_id, req->clk_id, NULL, &curr_state); if (ret) { LOG_ERR("Failed to get clock OFF status: dev_id=%u clk_id=%u err=%d", req->dev_id, req->clk_id, ret); From 374bc26a96c93fb52338dd973f6f82b3a850bc36 Mon Sep 17 00:00:00 2001 From: Utsav Munendra Date: Fri, 24 Oct 2025 09:57:12 -0700 Subject: [PATCH 079/143] UPSTREAM: drivers: flash_mspi_nor: Simplify perform_xfer function commit 41988ae62f44068a0dd1a64cfb74e300b15dad45 upstream. Remove unnecessary function argument, makes later commits also simpler. Signed-off-by: Utsav Munendra --- drivers/flash/flash_mspi_nor.c | 34 +++++++++++++-------------- drivers/flash/flash_mspi_nor_quirks.h | 8 +++---- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index 444509b695b5..595e02281f60 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -26,8 +26,7 @@ LOG_MODULE_REGISTER(flash_mspi_nor, CONFIG_FLASH_LOG_LEVEL); #define NON_XIP_DEV_CFG_MASK (MSPI_DEVICE_CONFIG_ALL & ~XIP_DEV_CFG_MASK) static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir); -static int perform_xfer(const struct device *dev, - uint8_t cmd, bool mem_access); +static int perform_xfer(const struct device *dev, uint8_t cmd); static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr); static int wait_until_ready(const struct device *dev, k_timeout_t poll_period); static int cmd_wren(const struct device *dev); @@ -77,12 +76,13 @@ static uint16_t get_extended_command(const struct device *dev, return ((uint16_t)cmd << 8) | cmd_extension; } -static int perform_xfer(const struct device *dev, - uint8_t cmd, bool mem_access) +static int perform_xfer(const struct device *dev, uint8_t cmd) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; const struct mspi_dev_cfg *cfg = NULL; + bool mem_access = (cmd == dev_data->cmd_info.read_cmd || + cmd == dev_data->cmd_info.pp_cmd); int rc; if (dev_data->cmd_info.cmd_extension != CMD_EXTENSION_NONE && @@ -158,7 +158,7 @@ static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr) } dev_data->packet.num_bytes = sizeof(uint8_t); dev_data->packet.data_buf = sr; - rc = perform_xfer(dev, op_code, false); + rc = perform_xfer(dev, op_code); if (rc < 0) { LOG_ERR("%s 0x%02x failed: %d", __func__, op_code, rc); return rc; @@ -194,7 +194,7 @@ static int cmd_wren(const struct device *dev) int rc; set_up_xfer(dev, MSPI_TX); - rc = perform_xfer(dev, SPI_NOR_CMD_WREN, false); + rc = perform_xfer(dev, SPI_NOR_CMD_WREN); if (rc < 0) { LOG_ERR("%s failed: %d", __func__, rc); return rc; @@ -217,7 +217,7 @@ static int cmd_wrsr(const struct device *dev, uint8_t op_code, set_up_xfer(dev, MSPI_TX); dev_data->packet.num_bytes = sr_cnt; dev_data->packet.data_buf = sr; - rc = perform_xfer(dev, op_code, false); + rc = perform_xfer(dev, op_code); if (rc < 0) { LOG_ERR("%s 0x%02x failed: %d", __func__, op_code, rc); return rc; @@ -368,7 +368,7 @@ static int api_read(const struct device *dev, off_t addr, void *dest, dev_data->xfer.rx_dummy = get_rx_dummy(dev); dev_data->packet.data_buf = dest; dev_data->packet.num_bytes = to_read; - rc = perform_xfer(dev, dev_data->cmd_info.read_cmd, true); + rc = perform_xfer(dev, dev_data->cmd_info.read_cmd); addr += to_read; dest = (uint8_t *)dest + to_read; @@ -419,7 +419,7 @@ static int api_write(const struct device *dev, off_t addr, const void *src, set_up_xfer_with_addr(dev, MSPI_TX, addr); dev_data->packet.data_buf = (uint8_t *)src; dev_data->packet.num_bytes = to_write; - rc = perform_xfer(dev, dev_data->cmd_info.pp_cmd, true); + rc = perform_xfer(dev, dev_data->cmd_info.pp_cmd); if (rc < 0) { LOG_ERR("Page program xfer failed: %d", rc); break; @@ -491,7 +491,7 @@ static int api_erase(const struct device *dev, off_t addr, size_t size) if (size == flash_size) { /* Chip erase. */ set_up_xfer(dev, MSPI_TX); - rc = perform_xfer(dev, SPI_NOR_CMD_CE, false); + rc = perform_xfer(dev, SPI_NOR_CMD_CE); size -= flash_size; } else { @@ -500,7 +500,7 @@ static int api_erase(const struct device *dev, off_t addr, size_t size) if (best_et != NULL) { set_up_xfer_with_addr(dev, MSPI_TX, addr); - rc = perform_xfer(dev, best_et->cmd, false); + rc = perform_xfer(dev, best_et->cmd); addr += BIT(best_et->exp); size -= BIT(best_et->exp); @@ -566,7 +566,7 @@ static int sfdp_read(const struct device *dev, off_t addr, void *dest, dev_data->packet.address = addr; dev_data->packet.data_buf = dest; dev_data->packet.num_bytes = size; - rc = perform_xfer(dev, JESD216_CMD_READ_SFDP, false); + rc = perform_xfer(dev, JESD216_CMD_READ_SFDP); if (rc < 0) { LOG_ERR("Read SFDP xfer failed: %d", rc); } @@ -587,7 +587,7 @@ static int read_jedec_id(const struct device *dev, uint8_t *id) } dev_data->packet.data_buf = id; dev_data->packet.num_bytes = JESD216_READ_ID_LEN; - rc = perform_xfer(dev, SPI_NOR_CMD_RDID, false); + rc = perform_xfer(dev, SPI_NOR_CMD_RDID); if (rc < 0) { LOG_ERR("Read JEDEC ID failed: %d", rc); } @@ -777,7 +777,7 @@ static int octal_enable_set(const struct device *dev, bool enable) dev_data->packet.address = 0x02; dev_data->packet.num_bytes = sizeof(uint8_t); dev_data->packet.data_buf = &status_reg; - rc = perform_xfer(dev, op_code, false); + rc = perform_xfer(dev, op_code); if (rc < 0) { LOG_ERR("cmd_rdsr 0x%02x failed: %d", op_code, rc); return rc; @@ -813,7 +813,7 @@ static int enter_4byte_addressing_mode(const struct device *dev) } set_up_xfer(dev, MSPI_TX); - rc = perform_xfer(dev, 0xB7, false); + rc = perform_xfer(dev, 0xB7); if (rc < 0) { LOG_ERR("Command 0xB7 failed: %d", rc); return rc; @@ -932,14 +932,14 @@ static int soft_reset_66_99(const struct device *dev) int rc; set_up_xfer(dev, MSPI_TX); - rc = perform_xfer(dev, SPI_NOR_CMD_RESET_EN, false); + rc = perform_xfer(dev, SPI_NOR_CMD_RESET_EN); if (rc < 0) { LOG_ERR("CMD_RESET_EN failed: %d", rc); return rc; } set_up_xfer(dev, MSPI_TX); - rc = perform_xfer(dev, SPI_NOR_CMD_RESET_MEM, false); + rc = perform_xfer(dev, SPI_NOR_CMD_RESET_MEM); if (rc < 0) { LOG_ERR("CMD_RESET_MEM failed: %d", rc); return rc; diff --git a/drivers/flash/flash_mspi_nor_quirks.h b/drivers/flash/flash_mspi_nor_quirks.h index d58fbea4ff9b..83480b4643a8 100644 --- a/drivers/flash/flash_mspi_nor_quirks.h +++ b/drivers/flash/flash_mspi_nor_quirks.h @@ -80,7 +80,7 @@ static inline int mxicy_mx25r_post_switch_mode(const struct device *dev) set_up_xfer(dev, MSPI_TX); dev_data->packet.data_buf = mxicy_mx25r_hp_payload; dev_data->packet.num_bytes = sizeof(mxicy_mx25r_hp_payload); - rc = perform_xfer(dev, SPI_NOR_CMD_WRSR, false); + rc = perform_xfer(dev, SPI_NOR_CMD_WRSR); if (rc < 0) { return rc; } @@ -101,7 +101,7 @@ static inline int mxicy_mx25r_post_switch_mode(const struct device *dev) set_up_xfer(dev, MSPI_RX); dev_data->packet.num_bytes = sizeof(config); dev_data->packet.data_buf = config; - rc = perform_xfer(dev, SPI_NOR_CMD_RDCR, false); + rc = perform_xfer(dev, SPI_NOR_CMD_RDCR); if (rc < 0) { return rc; } @@ -159,7 +159,7 @@ static inline int mxicy_mx25u_post_switch_mode(const struct device *dev) dev_data->packet.address = 0; dev_data->packet.data_buf = &opi_enable; dev_data->packet.num_bytes = sizeof(opi_enable); - return perform_xfer(dev, SPI_NOR_CMD_WR_CFGREG2, false); + return perform_xfer(dev, SPI_NOR_CMD_WR_CFGREG2); } static int mxicy_mx25u_pre_init(const struct device *dev) @@ -192,7 +192,7 @@ static int mxicy_mx25u_pre_init(const struct device *dev) dev_data->packet.address = 0x300; dev_data->packet.data_buf = &cfg_reg; dev_data->packet.num_bytes = sizeof(cfg_reg); - rc = perform_xfer(dev, SPI_NOR_CMD_RD_CFGREG2, false); + rc = perform_xfer(dev, SPI_NOR_CMD_RD_CFGREG2); if (rc < 0) { LOG_ERR("Failed to read Dummy Cycle from CFGREG2"); return rc; From 2fb621d21a86e2779812e61f0e457e7e38a1bb68 Mon Sep 17 00:00:00 2001 From: Utsav Munendra Date: Fri, 24 Oct 2025 09:57:52 -0700 Subject: [PATCH 080/143] UPSTREAM: drivers: flash_mspi_nor: Track last applied MSPI config commit 8bc4a92397de978d55bdc8ae27e34d731c50f7f4 upstream. Instead of just tracking in a bool whether the MSPI device is in Standard MSPI vs. QPI/OPI config, track the entire MSPI config which was last applied. This makes it easier later to track more than two configs to apply based on the next command to transceive. Signed-off-by: Utsav Munendra --- drivers/flash/flash_mspi_nor.c | 18 ++++++++++++------ drivers/flash/flash_mspi_nor.h | 1 + 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index 595e02281f60..b53b50d3663f 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -124,14 +124,14 @@ static int perform_xfer(const struct device *dev, uint8_t cmd) } } - if (cfg) { + if (cfg && cfg != dev_data->last_applied_cfg) { rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, MSPI_DEVICE_CONFIG_IO_MODE, cfg); if (rc < 0) { LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; } - + dev_data->last_applied_cfg = cfg; dev_data->in_target_io_mode = mem_access; } @@ -262,6 +262,7 @@ static int acquire(const struct device *dev) LOG_ERR("mspi_dev_config() failed: %d", rc); } else { if (dev_config->multiperipheral_bus) { + dev_data->last_applied_cfg = &dev_config->mspi_nor_cfg; dev_data->in_target_io_mode = true; } @@ -867,9 +868,14 @@ static int switch_to_target_io_mode(const struct device *dev) } } - return mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, NON_XIP_DEV_CFG_MASK, &dev_config->mspi_nor_cfg); + if (rc < 0) { + return rc; + } + dev_data->last_applied_cfg = &dev_config->mspi_nor_cfg; + return 0; } #if defined(WITH_SUPPLY_GPIO) @@ -965,7 +971,7 @@ static int soft_reset(const struct device *dev) LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; } - + dev_data->last_applied_cfg = &dev_config->mspi_nor_cfg; dev_data->in_target_io_mode = true; rc = soft_reset_66_99(dev); @@ -980,7 +986,7 @@ static int soft_reset(const struct device *dev) LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; } - + dev_data->last_applied_cfg = &dev_config->mspi_nor_init_cfg; dev_data->in_target_io_mode = false; } @@ -1010,7 +1016,7 @@ static int flash_chip_init(const struct device *dev) LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; } - + dev_data->last_applied_cfg = &dev_config->mspi_nor_init_cfg; dev_data->in_target_io_mode = false; #if defined(WITH_SUPPLY_GPIO) diff --git a/drivers/flash/flash_mspi_nor.h b/drivers/flash/flash_mspi_nor.h index 335af449b261..f86c82fa7b70 100644 --- a/drivers/flash/flash_mspi_nor.h +++ b/drivers/flash/flash_mspi_nor.h @@ -114,6 +114,7 @@ struct flash_mspi_nor_data { struct flash_mspi_nor_cmd_info cmd_info; struct flash_mspi_nor_switch_info switch_info; bool in_target_io_mode; + const struct mspi_dev_cfg *last_applied_cfg; }; #ifdef __cplusplus From 08f5ec6685d7582f0b142e23cb6a3b2a37239657 Mon Sep 17 00:00:00 2001 From: Utsav Munendra Date: Wed, 29 Oct 2025 07:01:50 -0700 Subject: [PATCH 081/143] UPSTREAM: drivers: flash_mspi_nor: Flash control commands to use thei... commit abd35f88a76eb5eb2ee86364d27e3dae9b631aee upstream. Also in preparation for allowing control command frequency to be different from the read/write frequency and initialization frequency. Signed-off-by: Utsav Munendra --- drivers/flash/flash_mspi_nor.c | 49 +++++++++++++++------------------- drivers/flash/flash_mspi_nor.h | 3 ++- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index b53b50d3663f..965fd385251f 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -94,10 +94,14 @@ static int perform_xfer(const struct device *dev, uint8_t cmd) dev_data->packet.cmd = cmd; } - if (dev_config->multi_io_cmd || - dev_config->mspi_nor_cfg.io_mode == MSPI_IO_MODE_SINGLE) { - /* If multiple IO lines are used in all the transfer phases - * or in none of them, there's no need to switch the IO mode. + if (!dev_data->chip_initialized || + dev_config->multi_io_cmd || + dev_config->mspi_nor_cfg.io_mode == MSPI_IO_MODE_SINGLE) { + /* Commands before chip is initialized manually apply a MSPI config + * which all flash chips support by JEDEC standard. Do not switch + * to device tree config yet. + * If multiple IO lines are used in all the transfer phases + * there's no need to switch the IO mode. */ } else if (mem_access) { /* For commands accessing the flash memory (read and program), @@ -107,26 +111,13 @@ static int perform_xfer(const struct device *dev, uint8_t cmd) cfg = &dev_config->mspi_nor_cfg; } } else { - /* For all other commands, switch to Single IO mode if a given - * command needs the data or address phase and in the target IO - * mode multiple IO lines are used in these phases. - */ - if (dev_data->in_target_io_mode) { - if (dev_data->packet.num_bytes != 0 || - (dev_data->xfer.addr_length != 0 && - !dev_config->single_io_addr)) { - /* Only the IO mode is to be changed, so the - * initial configuration structure can be used - * for this operation. - */ - cfg = &dev_config->mspi_nor_init_cfg; - } - } + /* For all other commands, use control command config */ + cfg = &dev_config->mspi_control_cfg; } if (cfg && cfg != dev_data->last_applied_cfg) { rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, - MSPI_DEVICE_CONFIG_IO_MODE, cfg); + MSPI_DEVICE_CONFIG_IO_MODE | MSPI_DEVICE_CONFIG_FREQUENCY, cfg); if (rc < 0) { LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; @@ -981,12 +972,12 @@ static int soft_reset(const struct device *dev) rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, MSPI_DEVICE_CONFIG_IO_MODE, - &dev_config->mspi_nor_init_cfg); + &dev_config->mspi_control_cfg); if (rc < 0) { LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; } - dev_data->last_applied_cfg = &dev_config->mspi_nor_init_cfg; + dev_data->last_applied_cfg = &dev_config->mspi_control_cfg; dev_data->in_target_io_mode = false; } @@ -1003,20 +994,23 @@ static int flash_chip_init(const struct device *dev) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; + struct mspi_dev_cfg mspi_nor_init_cfg; uint8_t id[JESD216_READ_ID_LEN] = {0}; uint16_t dts_cmd = 0; uint32_t sfdp_signature; bool flash_reset = false; int rc; + /* Do initial checks at max 50MHz required to be supported by JEDEC */ + memcpy(&mspi_nor_init_cfg, &dev_config->mspi_control_cfg, sizeof(mspi_nor_init_cfg)); + mspi_nor_init_cfg.freq = MIN(dev_config->mspi_control_cfg.freq, MHZ(50)); rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, MSPI_DEVICE_CONFIG_ALL, - &dev_config->mspi_nor_init_cfg); + &mspi_nor_init_cfg); if (rc < 0) { LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; } - dev_data->last_applied_cfg = &dev_config->mspi_nor_init_cfg; dev_data->in_target_io_mode = false; #if defined(WITH_SUPPLY_GPIO) @@ -1115,6 +1109,7 @@ static int flash_chip_init(const struct device *dev) LOG_ERR("Failed to switch to target io mode: %d", rc); return rc; } + dev_data->chip_initialized = true; dev_data->in_target_io_mode = true; @@ -1267,10 +1262,10 @@ static DEVICE_API(flash, drv_api) = { #endif }; -#define FLASH_INITIAL_CONFIG(inst) \ +#define FLASH_CONTROL_CMD_CONFIG(inst) \ { \ .ce_num = DT_INST_PROP_OR(inst, mspi_hardware_ce_num, 0), \ - .freq = MIN(DT_INST_PROP(inst, mspi_max_frequency), MHZ(50)), \ + .freq = DT_INST_PROP(inst, mspi_max_frequency), \ .io_mode = MSPI_IO_MODE_SINGLE, \ .data_rate = MSPI_DATA_RATE_SINGLE, \ .cpp = MSPI_CPP_MODE_0, \ @@ -1336,7 +1331,7 @@ BUILD_ASSERT((FLASH_SIZE(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ .page_size = FLASH_PAGE_SIZE(inst), \ .mspi_id = MSPI_DEVICE_ID_DT_INST(inst), \ .mspi_nor_cfg = MSPI_DEVICE_CONFIG_DT_INST(inst), \ - .mspi_nor_init_cfg = FLASH_INITIAL_CONFIG(inst), \ + .mspi_control_cfg = FLASH_CONTROL_CMD_CONFIG(inst), \ IF_ENABLED(CONFIG_MSPI_XIP, \ (.xip_cfg = MSPI_XIP_CONFIG_DT_INST(inst),)) \ IF_ENABLED(WITH_SUPPLY_GPIO, \ diff --git a/drivers/flash/flash_mspi_nor.h b/drivers/flash/flash_mspi_nor.h index f86c82fa7b70..a8c7bfaab547 100644 --- a/drivers/flash/flash_mspi_nor.h +++ b/drivers/flash/flash_mspi_nor.h @@ -75,7 +75,7 @@ struct flash_mspi_nor_config { uint16_t page_size; struct mspi_dev_id mspi_id; struct mspi_dev_cfg mspi_nor_cfg; - struct mspi_dev_cfg mspi_nor_init_cfg; + struct mspi_dev_cfg mspi_control_cfg; #if defined(CONFIG_MSPI_XIP) struct mspi_xip_cfg xip_cfg; #endif @@ -115,6 +115,7 @@ struct flash_mspi_nor_data { struct flash_mspi_nor_switch_info switch_info; bool in_target_io_mode; const struct mspi_dev_cfg *last_applied_cfg; + bool chip_initialized; }; #ifdef __cplusplus From 2d42f6a0769906e0a620e995cb58ad9e046219f3 Mon Sep 17 00:00:00 2001 From: Utsav Munendra Date: Wed, 29 Oct 2025 07:01:50 -0700 Subject: [PATCH 082/143] UPSTREAM: drivers: flash_mspi_nor: Remove bool tracking of target IO ... commit eae12761e1b4a5505e192e789feac110e3ddb288 upstream. Remove the Boolean tracking of MSPI IO mode as we can now rely on tracking the entire dev config applied to the MSPI device, multiple of which will exist in later commits. Signed-off-by: Utsav Munendra --- drivers/flash/flash_mspi_nor.c | 25 +++++++++++++------------ drivers/flash/flash_mspi_nor.h | 1 - 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index 965fd385251f..613ebfbab60c 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -35,6 +35,14 @@ static int cmd_wrsr(const struct device *dev, uint8_t op_code, #include "flash_mspi_nor_quirks.h" +static bool in_octal_io(const struct device *dev) +{ + struct flash_mspi_nor_data *dev_data = dev->data; + + return dev_data->last_applied_cfg && + dev_data->last_applied_cfg->io_mode == MSPI_IO_MODE_OCTAL; +} + static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir) { const struct flash_mspi_nor_config *dev_config = dev->config; @@ -86,7 +94,7 @@ static int perform_xfer(const struct device *dev, uint8_t cmd) int rc; if (dev_data->cmd_info.cmd_extension != CMD_EXTENSION_NONE && - dev_data->in_target_io_mode) { + in_octal_io(dev)) { dev_data->xfer.cmd_length = 2; dev_data->packet.cmd = get_extended_command(dev, cmd); } else { @@ -107,7 +115,7 @@ static int perform_xfer(const struct device *dev, uint8_t cmd) /* For commands accessing the flash memory (read and program), * ensure that the target IO mode is active. */ - if (!dev_data->in_target_io_mode) { + if (!in_octal_io(dev)) { cfg = &dev_config->mspi_nor_cfg; } } else { @@ -123,7 +131,6 @@ static int perform_xfer(const struct device *dev, uint8_t cmd) return rc; } dev_data->last_applied_cfg = cfg; - dev_data->in_target_io_mode = mem_access; } rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, @@ -142,7 +149,7 @@ static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr) int rc; set_up_xfer(dev, MSPI_RX); - if (dev_data->in_target_io_mode) { + if (in_octal_io(dev)) { dev_data->xfer.rx_dummy = dev_data->cmd_info.rdsr_dummy; dev_data->xfer.addr_length = dev_data->cmd_info.rdsr_addr_4 ? 4 : 0; @@ -254,7 +261,6 @@ static int acquire(const struct device *dev) } else { if (dev_config->multiperipheral_bus) { dev_data->last_applied_cfg = &dev_config->mspi_nor_cfg; - dev_data->in_target_io_mode = true; } return 0; @@ -546,7 +552,7 @@ static int sfdp_read(const struct device *dev, off_t addr, void *dest, int rc; set_up_xfer(dev, MSPI_RX); - if (dev_data->in_target_io_mode) { + if (in_octal_io(dev)) { dev_data->xfer.rx_dummy = dev_data->cmd_info.sfdp_dummy_20 ? 20 : 8; dev_data->xfer.addr_length = dev_data->cmd_info.sfdp_addr_4 @@ -572,7 +578,7 @@ static int read_jedec_id(const struct device *dev, uint8_t *id) int rc; set_up_xfer(dev, MSPI_RX); - if (dev_data->in_target_io_mode) { + if (in_octal_io(dev)) { dev_data->xfer.rx_dummy = dev_data->cmd_info.rdid_dummy; dev_data->xfer.addr_length = dev_data->cmd_info.rdid_addr_4 ? 4 : 0; @@ -963,7 +969,6 @@ static int soft_reset(const struct device *dev) return rc; } dev_data->last_applied_cfg = &dev_config->mspi_nor_cfg; - dev_data->in_target_io_mode = true; rc = soft_reset_66_99(dev); if (rc < 0) { @@ -978,7 +983,6 @@ static int soft_reset(const struct device *dev) return rc; } dev_data->last_applied_cfg = &dev_config->mspi_control_cfg; - dev_data->in_target_io_mode = false; } rc = soft_reset_66_99(dev); @@ -1011,7 +1015,6 @@ static int flash_chip_init(const struct device *dev) LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; } - dev_data->in_target_io_mode = false; #if defined(WITH_SUPPLY_GPIO) if (dev_config->supply.port) { @@ -1111,8 +1114,6 @@ static int flash_chip_init(const struct device *dev) } dev_data->chip_initialized = true; - dev_data->in_target_io_mode = true; - if (IS_ENABLED(CONFIG_FLASH_MSPI_NOR_USE_SFDP)) { /* Read the SFDP signature to test if communication with * the flash chip can be successfully performed after switching diff --git a/drivers/flash/flash_mspi_nor.h b/drivers/flash/flash_mspi_nor.h index a8c7bfaab547..7d95588c4721 100644 --- a/drivers/flash/flash_mspi_nor.h +++ b/drivers/flash/flash_mspi_nor.h @@ -113,7 +113,6 @@ struct flash_mspi_nor_data { struct jesd216_erase_type erase_types[JESD216_NUM_ERASE_TYPES]; struct flash_mspi_nor_cmd_info cmd_info; struct flash_mspi_nor_switch_info switch_info; - bool in_target_io_mode; const struct mspi_dev_cfg *last_applied_cfg; bool chip_initialized; }; From b9e953527366841df5321a458841f042ae78644b Mon Sep 17 00:00:00 2001 From: Utsav Munendra Date: Wed, 29 Oct 2025 07:01:50 -0700 Subject: [PATCH 083/143] UPSTREAM: driver: flash_mspi_nor: Allow specific read/write IO modes ... commit c696414f8f1f0c8986fe4ba6ba750f09f8c9678d upstream. The driver currently provides no way to use Dual IO Read and Single IO for the rest of the commands currently, and would erroneously use Single IO PP command in Dual IO mode. This PR fixes and adds support for that. Signed-off-by: Utsav Munendra --- drivers/flash/flash_mspi_nor.c | 94 ++++++++++++++++++++-------- drivers/flash/flash_mspi_nor.h | 8 +++ dts/bindings/mtd/jedec,mspi-nor.yaml | 56 +++++++++++++++++ 3 files changed, 132 insertions(+), 26 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index 613ebfbab60c..405fd8800b36 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -43,6 +43,18 @@ static bool in_octal_io(const struct device *dev) dev_data->last_applied_cfg->io_mode == MSPI_IO_MODE_OCTAL; } +static bool is_quad_enable_needed(const struct mspi_dev_cfg *cfg) +{ + return cfg && (cfg->io_mode == MSPI_IO_MODE_QUAD_1_1_4 || + cfg->io_mode == MSPI_IO_MODE_QUAD_1_4_4); +} + +static bool is_octal_enable_needed(const struct mspi_dev_cfg *cfg) +{ + return cfg && (cfg->io_mode == MSPI_IO_MODE_OCTAL_1_1_8 || + cfg->io_mode == MSPI_IO_MODE_OCTAL_1_8_8); +} + static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir) { const struct flash_mspi_nor_config *dev_config = dev->config; @@ -89,8 +101,6 @@ static int perform_xfer(const struct device *dev, uint8_t cmd) const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; const struct mspi_dev_cfg *cfg = NULL; - bool mem_access = (cmd == dev_data->cmd_info.read_cmd || - cmd == dev_data->cmd_info.pp_cmd); int rc; if (dev_data->cmd_info.cmd_extension != CMD_EXTENSION_NONE && @@ -102,25 +112,21 @@ static int perform_xfer(const struct device *dev, uint8_t cmd) dev_data->packet.cmd = cmd; } - if (!dev_data->chip_initialized || - dev_config->multi_io_cmd || - dev_config->mspi_nor_cfg.io_mode == MSPI_IO_MODE_SINGLE) { - /* Commands before chip is initialized manually apply a MSPI config - * which all flash chips support by JEDEC standard. Do not switch - * to device tree config yet. - * If multiple IO lines are used in all the transfer phases - * there's no need to switch the IO mode. - */ - } else if (mem_access) { - /* For commands accessing the flash memory (read and program), - * ensure that the target IO mode is active. - */ - if (!in_octal_io(dev)) { - cfg = &dev_config->mspi_nor_cfg; + /* Commands before chip is initialized manually apply a MSPI config + * which all flash chips support by JEDEC standard. Do not switch + * to device tree config yet. + * If multiple IO lines are used in all the transfer phases + * there's no need to switch the IO mode. + */ + if (dev_data->chip_initialized && !dev_config->multi_io_cmd) { + if (cmd == dev_data->cmd_info.read_cmd) { + cfg = dev_data->read_cfg; + } else if (cmd == dev_data->cmd_info.pp_cmd) { + cfg = dev_data->write_cfg; + } else { + /* For all other commands, use control command config */ + cfg = &dev_config->mspi_control_cfg; } - } else { - /* For all other commands, use control command config */ - cfg = &dev_config->mspi_control_cfg; } if (cfg && cfg != dev_data->last_applied_cfg) { @@ -824,12 +830,11 @@ static int switch_to_target_io_mode(const struct device *dev) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; - enum mspi_io_mode io_mode = dev_config->mspi_nor_cfg.io_mode; int rc = 0; if (dev_data->switch_info.quad_enable_req != JESD216_DW15_QER_VAL_NONE) { - bool quad_needed = io_mode == MSPI_IO_MODE_QUAD_1_1_4 || - io_mode == MSPI_IO_MODE_QUAD_1_4_4; + bool quad_needed = is_quad_enable_needed(dev_data->read_cfg) || + is_quad_enable_needed(dev_data->write_cfg); rc = quad_enable_set(dev, quad_needed); if (rc < 0) { @@ -839,8 +844,8 @@ static int switch_to_target_io_mode(const struct device *dev) } if (dev_data->switch_info.octal_enable_req != OCTAL_ENABLE_REQ_NONE) { - bool octal_needed = io_mode == MSPI_IO_MODE_OCTAL_1_1_8 || - io_mode == MSPI_IO_MODE_OCTAL_1_8_8; + bool octal_needed = is_octal_enable_needed(dev_data->read_cfg) || + is_octal_enable_needed(dev_data->write_cfg); rc = octal_enable_set(dev, octal_needed); if (rc < 0) { @@ -1089,6 +1094,33 @@ static int flash_chip_init(const struct device *dev) } } + /* If read/write commands and frequency do not match the default + * MSPI device configuration, store new ones for those commands + * specifically. + */ + if (dev_config->read_io_mode == dev_config->mspi_nor_cfg.io_mode && + dev_config->read_freq == dev_config->mspi_nor_cfg.freq) { + dev_data->read_cfg = &dev_config->mspi_nor_cfg; + } else { + memcpy(&dev_data->mspi_dev_read_cfg, &dev_config->mspi_nor_cfg, + sizeof(dev_config->mspi_nor_cfg)); + dev_data->mspi_dev_read_cfg.io_mode = dev_config->read_io_mode; + dev_data->mspi_dev_read_cfg.freq = dev_config->read_freq; + dev_data->read_cfg = &dev_data->mspi_dev_read_cfg; + } + + if (dev_config->write_io_mode == dev_config->mspi_nor_cfg.io_mode && + dev_config->write_freq == dev_config->mspi_nor_cfg.freq) { + dev_data->write_cfg = &dev_config->mspi_nor_cfg; + } else { + memcpy(&dev_data->mspi_dev_write_cfg, &dev_config->mspi_nor_cfg, + sizeof(dev_config->mspi_nor_cfg)); + dev_data->mspi_dev_write_cfg.io_mode = dev_config->write_io_mode; + dev_data->mspi_dev_write_cfg.freq = dev_config->write_freq; + dev_data->write_cfg = &dev_data->mspi_dev_write_cfg; + } + + if (dev_config->jedec_id_specified) { rc = read_jedec_id(dev, id); if (rc < 0) { @@ -1263,10 +1295,12 @@ static DEVICE_API(flash, drv_api) = { #endif }; +#define FLASH_MSPI_MAX_FREQ(inst) DT_INST_PROP(inst, mspi_max_frequency) + #define FLASH_CONTROL_CMD_CONFIG(inst) \ { \ .ce_num = DT_INST_PROP_OR(inst, mspi_hardware_ce_num, 0), \ - .freq = DT_INST_PROP(inst, mspi_max_frequency), \ + .freq = FLASH_MSPI_MAX_FREQ(inst), \ .io_mode = MSPI_IO_MODE_SINGLE, \ .data_rate = MSPI_DATA_RATE_SINGLE, \ .cpp = MSPI_CPP_MODE_0, \ @@ -1350,6 +1384,14 @@ BUILD_ASSERT((FLASH_SIZE(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ .default_erase_types = DEFAULT_ERASE_TYPES(inst), \ .default_cmd_info = DEFAULT_CMD_INFO(inst), \ .default_switch_info = DEFAULT_SWITCH_INFO(inst), \ + .read_freq = DT_INST_PROP_OR(inst, read_frequency, \ + FLASH_MSPI_MAX_FREQ(inst)), \ + .read_io_mode = DT_INST_ENUM_IDX_OR(inst, read_io_mode, \ + DT_INST_ENUM_IDX(inst, mspi_io_mode)), \ + .write_freq = DT_INST_PROP_OR(inst, write_frequency, \ + FLASH_MSPI_MAX_FREQ(inst)), \ + .write_io_mode = DT_INST_ENUM_IDX_OR(inst, write_io_mode, \ + DT_INST_ENUM_IDX(inst, mspi_io_mode)), \ .jedec_id_specified = DT_INST_NODE_HAS_PROP(inst, jedec_id), \ .rx_dummy_specified = DT_INST_NODE_HAS_PROP(inst, rx_dummy), \ .multiperipheral_bus = DT_PROP(DT_INST_BUS(inst), \ diff --git a/drivers/flash/flash_mspi_nor.h b/drivers/flash/flash_mspi_nor.h index 7d95588c4721..6252b838c0cd 100644 --- a/drivers/flash/flash_mspi_nor.h +++ b/drivers/flash/flash_mspi_nor.h @@ -96,6 +96,10 @@ struct flash_mspi_nor_config { const struct jesd216_erase_type *default_erase_types; struct flash_mspi_nor_cmd_info default_cmd_info; struct flash_mspi_nor_switch_info default_switch_info; + uint32_t read_freq; + enum mspi_io_mode read_io_mode; + uint32_t write_freq; + enum mspi_io_mode write_io_mode; bool jedec_id_specified : 1; bool rx_dummy_specified : 1; bool multiperipheral_bus : 1; @@ -115,6 +119,10 @@ struct flash_mspi_nor_data { struct flash_mspi_nor_switch_info switch_info; const struct mspi_dev_cfg *last_applied_cfg; bool chip_initialized; + const struct mspi_dev_cfg *read_cfg; + struct mspi_dev_cfg mspi_dev_read_cfg; + const struct mspi_dev_cfg *write_cfg; + struct mspi_dev_cfg mspi_dev_write_cfg; }; #ifdef __cplusplus diff --git a/dts/bindings/mtd/jedec,mspi-nor.yaml b/dts/bindings/mtd/jedec,mspi-nor.yaml index 348e11cd9833..e59fe117557e 100644 --- a/dts/bindings/mtd/jedec,mspi-nor.yaml +++ b/dts/bindings/mtd/jedec,mspi-nor.yaml @@ -21,6 +21,62 @@ include: - t-exit-dpd properties: + read-frequency: + type: int + description: | + Clock frequency of device to configure in Hz for read + command, when reads are desired to be in a different + frequency than the rest of the commands. + + read-io-mode: + type: string + enum: + - "MSPI_IO_MODE_SINGLE" + - "MSPI_IO_MODE_DUAL" + - "MSPI_IO_MODE_DUAL_1_1_2" + - "MSPI_IO_MODE_DUAL_1_2_2" + - "MSPI_IO_MODE_QUAD" + - "MSPI_IO_MODE_QUAD_1_1_4" + - "MSPI_IO_MODE_QUAD_1_4_4" + - "MSPI_IO_MODE_OCTAL" + - "MSPI_IO_MODE_OCTAL_1_1_8" + - "MSPI_IO_MODE_OCTAL_1_8_8" + - "MSPI_IO_MODE_HEX" + - "MSPI_IO_MODE_HEX_8_8_16" + - "MSPI_IO_MODE_HEX_8_16_16" + description: | + MSPI I/O mode setting for read command when desired to + be different from base device `mspi-io-mode` for the rest + of the commands. + + write-frequency: + type: int + description: | + Clock frequency of device to configure in Hz for write + command, when write are desired to be in a different + frequency than the rest of the commands. + + write-io-mode: + type: string + enum: + - "MSPI_IO_MODE_SINGLE" + - "MSPI_IO_MODE_DUAL" + - "MSPI_IO_MODE_DUAL_1_1_2" + - "MSPI_IO_MODE_DUAL_1_2_2" + - "MSPI_IO_MODE_QUAD" + - "MSPI_IO_MODE_QUAD_1_1_4" + - "MSPI_IO_MODE_QUAD_1_4_4" + - "MSPI_IO_MODE_OCTAL" + - "MSPI_IO_MODE_OCTAL_1_1_8" + - "MSPI_IO_MODE_OCTAL_1_8_8" + - "MSPI_IO_MODE_HEX" + - "MSPI_IO_MODE_HEX_8_8_16" + - "MSPI_IO_MODE_HEX_8_16_16" + description: | + MSPI I/O mode setting for write command when desired to + be different from base device `mspi-io-mode` for the rest + of the commands. + reset-gpios: type: phandle-array description: | From 9af876a3e1ce54366eb2fdf536855e762eaf0885 Mon Sep 17 00:00:00 2001 From: David Jewsbury Date: Tue, 10 Feb 2026 17:13:48 +0000 Subject: [PATCH 084/143] UPSTREAM: drivers: flash_mspi_nor: Add DMA transfer mode selection commit eb318914c26145c816df125d3ea6d282684b6dfa upstream. Add CONFIG_FLASH_MSPI_NOR_DMA_DATA_XFER and CONFIG_FLASH_MSPI_NOR_DMA_CONTROL_XFER as Kconfig options for MSPI NOR Flash driver. These options are used to select whether data and control transfers should be performed using DMA or PIO modes. Update flash_mspi_nor to store choices and apply them accordingly: use data-xfer-mode for read/write data transfers, and control-xfer-mode for command/register/configuration traffic. This selection is due to DMA transfer's having a larger setup overhead so is less beneficial for short control transfers. Signed-off-by: David Jewsbury --- drivers/flash/Kconfig.mspi | 20 +++++++++ drivers/flash/flash_mspi_nor.c | 64 +++++++++++++++++++-------- drivers/flash/flash_mspi_nor.h | 2 + drivers/flash/flash_mspi_nor_quirks.h | 8 ++-- 4 files changed, 72 insertions(+), 22 deletions(-) diff --git a/drivers/flash/Kconfig.mspi b/drivers/flash/Kconfig.mspi index b48b89d05ed4..2d558f238e26 100644 --- a/drivers/flash/Kconfig.mspi +++ b/drivers/flash/Kconfig.mspi @@ -79,6 +79,26 @@ config FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE Other options include the 32K-byte erase size (32768), the sector size (4096), or any non-zero multiple of the sector size. +config FLASH_MSPI_NOR_DMA_DATA_XFER + bool "Use DMA for Flash MSPI NOR data transfers" + depends on MSPI_DMA + default y + help + Transfer mode for MSPI read/write data transactions. Since read/write tends + to involve larger data transfers, it is generally recommended to use MSPI_DMA + to reduce CPU load and improve throughput. Will default to MSPI_DMA if + CONFIG_MSPI_DMA is enabled, otherwise it will default to MSPI_PIO. + +config FLASH_MSPI_NOR_DMA_CONTROL_XFER + bool "Use DMA for Flash MSPI NOR control transfers" + depends on MSPI_DMA + default n + help + Transfer mode for register reads/writes and command MSPI transactions. + Since these are shorter transfers, it is generally recommended to use MSPI_PIO + (programmed I/O) due to there being less overhead compared to setting up a + DMA transfer. + endif # FLASH_MSPI_NOR endmenu diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index 405fd8800b36..e5cb10108370 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -25,7 +25,8 @@ LOG_MODULE_REGISTER(flash_mspi_nor, CONFIG_FLASH_LOG_LEVEL); #define NON_XIP_DEV_CFG_MASK (MSPI_DEVICE_CONFIG_ALL & ~XIP_DEV_CFG_MASK) -static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir); +static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir, + enum mspi_xfer_mode xfer_mode); static int perform_xfer(const struct device *dev, uint8_t cmd); static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr); static int wait_until_ready(const struct device *dev, k_timeout_t poll_period); @@ -55,7 +56,8 @@ static bool is_octal_enable_needed(const struct mspi_dev_cfg *cfg) cfg->io_mode == MSPI_IO_MODE_OCTAL_1_8_8); } -static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir) +static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir, + enum mspi_xfer_mode xfer_mode) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; @@ -63,7 +65,7 @@ static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir) memset(&dev_data->xfer, 0, sizeof(dev_data->xfer)); memset(&dev_data->packet, 0, sizeof(dev_data->packet)); - dev_data->xfer.xfer_mode = MSPI_PIO; + dev_data->xfer.xfer_mode = xfer_mode; dev_data->xfer.packets = &dev_data->packet; dev_data->xfer.num_packet = 1; dev_data->xfer.timeout = dev_config->transfer_timeout; @@ -73,11 +75,12 @@ static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir) static void set_up_xfer_with_addr(const struct device *dev, enum mspi_xfer_direction dir, - uint32_t addr) + uint32_t addr, + enum mspi_xfer_mode xfer_mode) { struct flash_mspi_nor_data *dev_data = dev->data; - set_up_xfer(dev, dir); + set_up_xfer(dev, dir, xfer_mode); dev_data->xfer.addr_length = dev_data->cmd_info.uses_4byte_addr ? 4 : 3; dev_data->packet.address = addr; @@ -151,10 +154,11 @@ static int perform_xfer(const struct device *dev, uint8_t cmd) static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr) { + const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; int rc; - set_up_xfer(dev, MSPI_RX); + set_up_xfer(dev, MSPI_RX, dev_config->control_xfer_mode); if (in_octal_io(dev)) { dev_data->xfer.rx_dummy = dev_data->cmd_info.rdsr_dummy; dev_data->xfer.addr_length = dev_data->cmd_info.rdsr_addr_4 @@ -195,9 +199,10 @@ static int wait_until_ready(const struct device *dev, k_timeout_t poll_period) static int cmd_wren(const struct device *dev) { + const struct flash_mspi_nor_config *dev_config = dev->config; int rc; - set_up_xfer(dev, MSPI_TX); + set_up_xfer(dev, MSPI_TX, dev_config->control_xfer_mode); rc = perform_xfer(dev, SPI_NOR_CMD_WREN); if (rc < 0) { LOG_ERR("%s failed: %d", __func__, rc); @@ -210,6 +215,7 @@ static int cmd_wren(const struct device *dev) static int cmd_wrsr(const struct device *dev, uint8_t op_code, uint8_t sr_cnt, uint8_t *sr) { + const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; int rc; @@ -218,7 +224,7 @@ static int cmd_wrsr(const struct device *dev, uint8_t op_code, return rc; } - set_up_xfer(dev, MSPI_TX); + set_up_xfer(dev, MSPI_TX, dev_config->control_xfer_mode); dev_data->packet.num_bytes = sr_cnt; dev_data->packet.data_buf = sr; rc = perform_xfer(dev, op_code); @@ -368,7 +374,7 @@ static int api_read(const struct device *dev, off_t addr, void *dest, to_read = size; } - set_up_xfer_with_addr(dev, MSPI_RX, addr); + set_up_xfer_with_addr(dev, MSPI_RX, addr, dev_config->data_xfer_mode); dev_data->xfer.rx_dummy = get_rx_dummy(dev); dev_data->packet.data_buf = dest; dev_data->packet.num_bytes = to_read; @@ -392,6 +398,7 @@ static int api_read(const struct device *dev, off_t addr, void *dest, static int api_write(const struct device *dev, off_t addr, const void *src, size_t size) { + const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; const uint32_t flash_size = dev_flash_size(dev); const uint16_t page_size = dev_page_size(dev); @@ -420,7 +427,7 @@ static int api_write(const struct device *dev, off_t addr, const void *src, break; } - set_up_xfer_with_addr(dev, MSPI_TX, addr); + set_up_xfer_with_addr(dev, MSPI_TX, addr, dev_config->data_xfer_mode); dev_data->packet.data_buf = (uint8_t *)src; dev_data->packet.num_bytes = to_write; rc = perform_xfer(dev, dev_data->cmd_info.pp_cmd); @@ -466,6 +473,7 @@ static const struct jesd216_erase_type *find_best_erase_type( static int api_erase(const struct device *dev, off_t addr, size_t size) { + const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; const uint32_t flash_size = dev_flash_size(dev); int rc = 0; @@ -494,7 +502,7 @@ static int api_erase(const struct device *dev, off_t addr, size_t size) if (size == flash_size) { /* Chip erase. */ - set_up_xfer(dev, MSPI_TX); + set_up_xfer(dev, MSPI_TX, dev_config->control_xfer_mode); rc = perform_xfer(dev, SPI_NOR_CMD_CE); size -= flash_size; @@ -503,7 +511,8 @@ static int api_erase(const struct device *dev, off_t addr, size_t size) find_best_erase_type(dev, addr, size); if (best_et != NULL) { - set_up_xfer_with_addr(dev, MSPI_TX, addr); + set_up_xfer_with_addr(dev, MSPI_TX, addr, + dev_config->control_xfer_mode); rc = perform_xfer(dev, best_et->cmd); addr += BIT(best_et->exp); @@ -554,10 +563,11 @@ struct flash_parameters *api_get_parameters(const struct device *dev) static int sfdp_read(const struct device *dev, off_t addr, void *dest, size_t size) { + const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; int rc; - set_up_xfer(dev, MSPI_RX); + set_up_xfer(dev, MSPI_RX, dev_config->control_xfer_mode); if (in_octal_io(dev)) { dev_data->xfer.rx_dummy = dev_data->cmd_info.sfdp_dummy_20 ? 20 : 8; @@ -580,10 +590,11 @@ static int sfdp_read(const struct device *dev, off_t addr, void *dest, static int read_jedec_id(const struct device *dev, uint8_t *id) { + const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; int rc; - set_up_xfer(dev, MSPI_RX); + set_up_xfer(dev, MSPI_RX, dev_config->control_xfer_mode); if (in_octal_io(dev)) { dev_data->xfer.rx_dummy = dev_data->cmd_info.rdid_dummy; dev_data->xfer.addr_length = dev_data->cmd_info.rdid_addr_4 @@ -757,6 +768,7 @@ static int quad_enable_set(const struct device *dev, bool enable) static int octal_enable_set(const struct device *dev, bool enable) { + const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; uint8_t op_code; uint8_t oe_bit; @@ -775,7 +787,7 @@ static int octal_enable_set(const struct device *dev, bool enable) * byte 0x02 and one dummy byte. */ op_code = 0x65; - set_up_xfer(dev, MSPI_RX); + set_up_xfer(dev, MSPI_RX, dev_config->control_xfer_mode); dev_data->xfer.rx_dummy = 8; dev_data->xfer.addr_length = 1; dev_data->packet.address = 0x02; @@ -806,6 +818,7 @@ static int octal_enable_set(const struct device *dev, bool enable) static int enter_4byte_addressing_mode(const struct device *dev) { + const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; int rc; @@ -816,7 +829,7 @@ static int enter_4byte_addressing_mode(const struct device *dev) } } - set_up_xfer(dev, MSPI_TX); + set_up_xfer(dev, MSPI_TX, dev_config->control_xfer_mode); rc = perform_xfer(dev, 0xB7); if (rc < 0) { LOG_ERR("Command 0xB7 failed: %d", rc); @@ -937,16 +950,17 @@ static int gpio_reset(const struct device *dev) #if defined(WITH_SOFT_RESET) static int soft_reset_66_99(const struct device *dev) { + const struct flash_mspi_nor_config *dev_config = dev->config; int rc; - set_up_xfer(dev, MSPI_TX); + set_up_xfer(dev, MSPI_TX, dev_config->control_xfer_mode); rc = perform_xfer(dev, SPI_NOR_CMD_RESET_EN); if (rc < 0) { LOG_ERR("CMD_RESET_EN failed: %d", rc); return rc; } - set_up_xfer(dev, MSPI_TX); + set_up_xfer(dev, MSPI_TX, dev_config->control_xfer_mode); rc = perform_xfer(dev, SPI_NOR_CMD_RESET_MEM); if (rc < 0) { LOG_ERR("CMD_RESET_MEM failed: %d", rc); @@ -1349,6 +1363,18 @@ BUILD_ASSERT((FLASH_SIZE(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ #define PACKET_DATA_LIMIT(inst) \ DT_PROP_OR(DT_INST_BUS(inst), packet_data_limit, 0) +#if CONFIG_FLASH_MSPI_NOR_DMA_CONTROL_XFER +#define FLASH_MSPI_NOR_CONTROL_XFER_MODE MSPI_DMA +#else +#define FLASH_MSPI_NOR_CONTROL_XFER_MODE MSPI_PIO +#endif + +#if CONFIG_FLASH_MSPI_NOR_DMA_DATA_XFER +#define FLASH_MSPI_NOR_DATA_XFER_MODE MSPI_DMA +#else +#define FLASH_MSPI_NOR_DATA_XFER_MODE MSPI_PIO +#endif + #define FLASH_MSPI_NOR_INST(inst) \ BUILD_ASSERT(!PACKET_DATA_LIMIT(inst) || \ FLASH_PAGE_SIZE(inst) <= PACKET_DATA_LIMIT(inst), \ @@ -1398,6 +1424,8 @@ BUILD_ASSERT((FLASH_SIZE(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ software_multiperipheral), \ IO_MODE_FLAGS(DT_INST_ENUM_IDX(inst, mspi_io_mode)), \ .initial_soft_reset = DT_INST_PROP(inst, initial_soft_reset), \ + .control_xfer_mode = FLASH_MSPI_NOR_CONTROL_XFER_MODE, \ + .data_xfer_mode = FLASH_MSPI_NOR_DATA_XFER_MODE, \ }; \ FLASH_PAGE_LAYOUT_CHECK(inst) \ DEVICE_DT_INST_DEFINE(inst, \ diff --git a/drivers/flash/flash_mspi_nor.h b/drivers/flash/flash_mspi_nor.h index 6252b838c0cd..4e4f942c6aee 100644 --- a/drivers/flash/flash_mspi_nor.h +++ b/drivers/flash/flash_mspi_nor.h @@ -106,6 +106,8 @@ struct flash_mspi_nor_config { bool multi_io_cmd : 1; bool single_io_addr : 1; bool initial_soft_reset : 1; + enum mspi_xfer_mode control_xfer_mode; + enum mspi_xfer_mode data_xfer_mode; }; struct flash_mspi_nor_data { diff --git a/drivers/flash/flash_mspi_nor_quirks.h b/drivers/flash/flash_mspi_nor_quirks.h index 83480b4643a8..23325741b0fd 100644 --- a/drivers/flash/flash_mspi_nor_quirks.h +++ b/drivers/flash/flash_mspi_nor_quirks.h @@ -77,7 +77,7 @@ static inline int mxicy_mx25r_post_switch_mode(const struct device *dev) } /* Write status and config registers */ - set_up_xfer(dev, MSPI_TX); + set_up_xfer(dev, MSPI_TX, dev_config->control_xfer_mode); dev_data->packet.data_buf = mxicy_mx25r_hp_payload; dev_data->packet.num_bytes = sizeof(mxicy_mx25r_hp_payload); rc = perform_xfer(dev, SPI_NOR_CMD_WRSR); @@ -98,7 +98,7 @@ static inline int mxicy_mx25r_post_switch_mode(const struct device *dev) } /* Verify configuration registers */ - set_up_xfer(dev, MSPI_RX); + set_up_xfer(dev, MSPI_RX, dev_config->control_xfer_mode); dev_data->packet.num_bytes = sizeof(config); dev_data->packet.data_buf = config; rc = perform_xfer(dev, SPI_NOR_CMD_RDCR); @@ -154,7 +154,7 @@ static inline int mxicy_mx25u_post_switch_mode(const struct device *dev) } /* Write config register 2 */ - set_up_xfer(dev, MSPI_TX); + set_up_xfer(dev, MSPI_TX, dev_config->control_xfer_mode); dev_data->xfer.addr_length = 4; dev_data->packet.address = 0; dev_data->packet.data_buf = &opi_enable; @@ -187,7 +187,7 @@ static int mxicy_mx25u_pre_init(const struct device *dev) */ /* Read configured number of dummy cycles for memory reading commands. */ - set_up_xfer(dev, MSPI_RX); + set_up_xfer(dev, MSPI_RX, dev_config->control_xfer_mode); dev_data->xfer.addr_length = 4; dev_data->packet.address = 0x300; dev_data->packet.data_buf = &cfg_reg; From 16373c9084808fa075f4b177cc5a37e3cedf28e1 Mon Sep 17 00:00:00 2001 From: Fabrice DJIATSA Date: Fri, 13 Feb 2026 09:42:06 +0100 Subject: [PATCH 085/143] UPSTREAM: drivers: flash: mspi: avoid HAL macro conflicts commit f731bd0b001100da7906c3fe4ba26c1791aba859 upstream. Rename FLASH_SIZE(inst) to FLASH_SIZE_INST(inst) and FLASH_PAGE_SIZE(inst) to FLASH_PAGE_SIZE_INST(inst) in flash_mspi_nor_sfdp.h to avoid redefinition conflicts with STM32 HAL headers (stm32l562xx.h and stm32l5xx_hal_flash.h). Note: Several HAL STM32 files use this macro naming convention; this issue is not specific to STM32L5. Signed-off-by: Fabrice DJIATSA --- drivers/flash/flash_mspi_nor.c | 10 +++++----- drivers/flash/flash_mspi_nor_sfdp.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index e5cb10108370..fa9cb0cfe206 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -1342,11 +1342,11 @@ BUILD_ASSERT((CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE % 4096) == 0, #define FLASH_PAGE_LAYOUT_DEFINE(inst) \ .layout = { \ .pages_size = CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE, \ - .pages_count = FLASH_SIZE(inst) \ + .pages_count = FLASH_SIZE_INST(inst) \ / CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE, \ }, #define FLASH_PAGE_LAYOUT_CHECK(inst) \ -BUILD_ASSERT((FLASH_SIZE(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ +BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ "MSPI_NOR_FLASH_LAYOUT_PAGE_SIZE incompatible with flash size, instance " #inst); #else #define FLASH_PAGE_LAYOUT_DEFINE(inst) @@ -1377,7 +1377,7 @@ BUILD_ASSERT((FLASH_SIZE(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ #define FLASH_MSPI_NOR_INST(inst) \ BUILD_ASSERT(!PACKET_DATA_LIMIT(inst) || \ - FLASH_PAGE_SIZE(inst) <= PACKET_DATA_LIMIT(inst), \ + FLASH_PAGE_SIZE_INST(inst) <= PACKET_DATA_LIMIT(inst), \ "Page size for " DT_NODE_FULL_NAME(DT_DRV_INST(inst)) \ " exceeds controller packet data limit"); \ SFDP_BUILD_ASSERTS(inst); \ @@ -1388,8 +1388,8 @@ BUILD_ASSERT((FLASH_SIZE(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ .bus = DEVICE_DT_GET(DT_INST_BUS(inst)), \ .packet_data_limit = DT_PROP_OR(DT_INST_BUS(inst), \ packet_data_limit, 0), \ - .flash_size = FLASH_SIZE(inst), \ - .page_size = FLASH_PAGE_SIZE(inst), \ + .flash_size = FLASH_SIZE_INST(inst), \ + .page_size = FLASH_PAGE_SIZE_INST(inst), \ .mspi_id = MSPI_DEVICE_ID_DT_INST(inst), \ .mspi_nor_cfg = MSPI_DEVICE_CONFIG_DT_INST(inst), \ .mspi_control_cfg = FLASH_CONTROL_CMD_CONFIG(inst), \ diff --git a/drivers/flash/flash_mspi_nor_sfdp.h b/drivers/flash/flash_mspi_nor_sfdp.h index 8dbecff01e80..b1b9d5f60aea 100644 --- a/drivers/flash/flash_mspi_nor_sfdp.h +++ b/drivers/flash/flash_mspi_nor_sfdp.h @@ -320,14 +320,14 @@ ? BIT(MIN(31, (dw2 & BIT_MASK(31)) - 3)) \ : dw2 / 8) -#define FLASH_SIZE(inst) \ +#define FLASH_SIZE_INST(inst) \ (DT_INST_NODE_HAS_PROP(inst, size) \ ? DT_INST_PROP(inst, size) / 8 \ : BFP_FLASH_SIZE(SFDP_DW(inst, sfdp_bfp, 2))) #define BFP_FLASH_PAGE_EXP(inst) SFDP_FIELD(inst, sfdp_bfp, 11, GENMASK(7, 4)) -#define FLASH_PAGE_SIZE(inst) \ +#define FLASH_PAGE_SIZE_INST(inst) \ (BFP_FLASH_PAGE_EXP(inst) \ ? BIT(BFP_FLASH_PAGE_EXP(inst)) \ : SPI_NOR_PAGE_SIZE) @@ -412,9 +412,9 @@ .octal_enable_req = OCTAL_ENABLE_REQ_NONE, \ .enter_4byte_addr = ENTER_4BYTE_ADDR_NONE } -#define FLASH_SIZE(inst) (DT_INST_PROP(inst, size) / 8) +#define FLASH_SIZE_INST(inst) (DT_INST_PROP(inst, size) / 8) -#define FLASH_PAGE_SIZE(inst) SPI_NOR_PAGE_SIZE +#define FLASH_PAGE_SIZE_INST(inst) SPI_NOR_PAGE_SIZE #define SFDP_BUILD_ASSERTS(inst) From 78ec24a72571049e85127e68c5f4bd56d8363899 Mon Sep 17 00:00:00 2001 From: Mika Braunschweig Date: Tue, 25 Nov 2025 11:01:41 +0100 Subject: [PATCH 086/143] UPSTREAM-PEND: drivers: mspi: cdns: add initial MSPI driver Add a Cadence MSPI peripheral driver, used in the TI K3 platform. The driver was tested in 1S-1S-1S and 4S-4S-4S mode with the onboard infineon s25h flash of the am243x launchpad and a custom driver for the flash using this interface. The command and dummy cycles are always taken from the xfer request and never from the devicetree since different commands might have different latencies. The driver is somewhat basic for now and lacks e.g. callback implementation. This is something that can be added in the future. If a non-supported / invalid request is detected a error code is returned. PR: 102513 Signed-off-by: Mika Braunschweig --- drivers/mspi/CMakeLists.txt | 3 + drivers/mspi/Kconfig | 3 + drivers/mspi/Kconfig.cadence | 15 + drivers/mspi/mspi_cadence.c | 1043 +++++++++++++++++++ drivers/mspi/mspi_cadence.h | 360 +++++++ dts/bindings/mspi/cdns,mspi-controller.yaml | 35 + 6 files changed, 1459 insertions(+) create mode 100644 drivers/mspi/Kconfig.cadence create mode 100644 drivers/mspi/mspi_cadence.c create mode 100644 drivers/mspi/mspi_cadence.h create mode 100644 dts/bindings/mspi/cdns,mspi-controller.yaml diff --git a/drivers/mspi/CMakeLists.txt b/drivers/mspi/CMakeLists.txt index cb2a985ec073..3adfba56277f 100644 --- a/drivers/mspi/CMakeLists.txt +++ b/drivers/mspi/CMakeLists.txt @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# # SPDX-License-Identifier: Apache-2.0 zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/mspi.h) @@ -6,5 +8,6 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_MSPI_AMBIQ_AP3 mspi_ambiq_ap3.c) zephyr_library_sources_ifdef(CONFIG_MSPI_AMBIQ_AP5 mspi_ambiq_ap5.c) zephyr_library_sources_ifdef(CONFIG_MSPI_AMBIQ_TIMING_SCAN mspi_ambiq_timing_scan.c) +zephyr_library_sources_ifdef(CONFIG_MSPI_CDNS mspi_cadence.c) zephyr_library_sources_ifdef(CONFIG_MSPI_DW mspi_dw.c) zephyr_library_sources_ifdef(CONFIG_MSPI_EMUL mspi_emul.c) diff --git a/drivers/mspi/Kconfig b/drivers/mspi/Kconfig index a6442c1f70fe..26bf78f532dd 100644 --- a/drivers/mspi/Kconfig +++ b/drivers/mspi/Kconfig @@ -1,6 +1,8 @@ # MSPI driver configuration options # Copyright (c) 2024 Ambiq Micro Inc. +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# # SPDX-License-Identifier: Apache-2.0 # @@ -66,6 +68,7 @@ module-str = mspi source "subsys/logging/Kconfig.template.log_config" source "drivers/mspi/Kconfig.ambiq" +source "drivers/mspi/Kconfig.cadence" source "drivers/mspi/Kconfig.dw" source "drivers/mspi/Kconfig.mspi_emul" diff --git a/drivers/mspi/Kconfig.cadence b/drivers/mspi/Kconfig.cadence new file mode 100644 index 000000000000..44c4a9f623a5 --- /dev/null +++ b/drivers/mspi/Kconfig.cadence @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 Siemens Mobility GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config MSPI_CDNS + bool "Cadence MSPI driver" + default y + depends on DT_HAS_CDNS_MSPI_CONTROLLER_ENABLED + # GPIO is required for ce_gpios despite the field being ignored + select GPIO + select PINCTRL + select MSPI_TIMING + help + Enable driver for Cadence MSPI peripheral diff --git a/drivers/mspi/mspi_cadence.c b/drivers/mspi/mspi_cadence.c new file mode 100644 index 000000000000..bc738738ce77 --- /dev/null +++ b/drivers/mspi/mspi_cadence.c @@ -0,0 +1,1043 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 Siemens Mobility GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT cdns_mspi_controller + +#include "mspi_cadence.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(mspi_cadence, CONFIG_MSPI_LOG_LEVEL); + +struct mspi_cadence_config { + DEVICE_MMIO_ROM; + struct mspi_cfg mspi_config; + const struct pinctrl_dev_config *pinctrl; + const uint32_t fifo_addr; + const uint32_t sram_allocated_for_read; + const uint32_t reference_frequency; +}; + +struct mspi_cadence_data { + DEVICE_MMIO_RAM; + struct k_mutex access_lock; + struct k_sem transfer_lock; + const struct mspi_dev_id *current_peripheral; +}; + +/** + * Wait for the MSPI controller to enter idle with the default timeout + */ +static int mspi_cadence_wait_for_idle(const struct device *controller) +{ + const mem_addr_t base_addr = DEVICE_MMIO_GET(controller); + uint32_t config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + uint32_t idle = config_reg & CADENCE_MSPI_CONFIG_REG_IDLE_BIT; + uint32_t retries = CADENCE_MSPI_GET_NUM_RETRIES(CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE); + + while (idle == 0 && retries > 0) { + k_sleep(CADENCE_MSPI_TIME_BETWEEN_RETRIES); + config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + idle = config_reg & CADENCE_MSPI_CONFIG_REG_IDLE_BIT; + --retries; + } + if (retries == 0) { + LOG_ERR("Timeout while waiting for MSPI to enter idle"); + return -EIO; + } + return 0; +} + +/** + * Check whether a single request package is requesting something that the driver + * doesn't implement / the hardware doesn't support + */ +static int mspi_cadence_check_transfer_package(const struct mspi_xfer *request, uint32_t index) +{ + const struct mspi_xfer_packet *packet = &request->packets[index]; + /* Check that we won't truncate the address */ + if (((uint64_t) packet->address) >> (8 * request->addr_length)) { + LOG_ERR("Address too long for amount of address bytes"); + return -EINVAL; + } + if (packet->cb_mask != MSPI_BUS_NO_CB) { + LOG_ERR("Callbacks aren't implemented"); + return -ENOSYS; + } + if (packet->cmd >> 16) { + LOG_ERR("Commands over 2 byte long aren't supported"); + return -ENOTSUP; + } + if (packet->cmd >> 8) { + LOG_ERR("Support for dual byte opcodes hasn't been implemented"); + return -ENOSYS; + } + if (packet->num_bytes) { + __ASSERT(packet->data_buf != NULL, + "Request gave a NULL buffer when bytes should be transferred"); + } + return 0; +} + +/** + * Check whether a full request has invalid / not supported parts + */ +static int mspi_cadence_check_transfer_request(const struct mspi_xfer *request) +{ + if (request->async) { + LOG_ERR("Asynchronous requests are not implemented"); + return -ENOSYS; + } + + if (request->cmd_length == 2) { + LOG_ERR("Dual byte opcode is not implemented"); + return -ENOSYS; + } else if (request->cmd_length > 2) { + LOG_ERR("Cmds over 2 bytes long aren't supported"); + return -ENOTSUP; + } else if (request->cmd_length != 1) { + LOG_ERR("Can't handle transfer without cmd"); + return -ENOSYS; + } + + if (request->addr_length > 4) { + LOG_ERR("Address too long. Only up to 32 bit are supported"); + return -ENOTSUP; + } + + if (request->priority != 0) { + LOG_WRN("Ignoring request to give transfer higher priority"); + } + + if (request->num_packet == 0) { + LOG_ERR("Got transfer requests without packages"); + return -EINVAL; + } + __ASSERT(request->packets != NULL, "Packets in transfer request are NULL"); + + if (request->xfer_mode != MSPI_PIO) { + LOG_ERR("Other modes than PIO are not supported"); + return -ENOTSUP; + } + + if (request->rx_dummy > CADENCE_MSPI_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MAX_VALUE || + request->tx_dummy > CADENCE_MSPI_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MAX_VALUE) { + return -ENOTSUP; + } + + int ret = 0; + + for (uint32_t i = 0; i < request->num_packet; ++i) { + ret = mspi_cadence_check_transfer_package(request, i); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +static int mspi_cadence_init(const struct device *dev) +{ + DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); + const struct mspi_cadence_config *config = dev->config; + struct mspi_cadence_data *data = dev->data; + const mem_addr_t base_addr = DEVICE_MMIO_GET(dev); + int ret; + + k_mutex_init(&data->access_lock); + k_sem_init(&data->transfer_lock, 1, 1); + + ret = pinctrl_apply_state(config->pinctrl, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("Failed to apply pinctrl"); + return ret; + } + + /* Disable MSPI */ + uint32_t config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT; + + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + ret = mspi_cadence_wait_for_idle(dev); + if (ret < 0) { + return ret; + } + + config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + /* Disable direct access the driver always uses indirect accesses */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENB_DIR_ACC_CTRL_BIT; + + /* Disable DTR protocol */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENABLE_DTR_PROTOCOL_BIT; + + /* Leave XIP mode */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENTER_XIP_MODE_BIT; + + /* Only allow one CS to be active */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_PERIPH_SEL_DEC_BIT; + + /* CS selection is based on "manual" pin selection */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENABLE_AHB_DECODER_BIT; + + /* DQ3 should not be used as reset pin */ + config_reg |= CADENCE_MSPI_CONFIG_REG_RESET_CFG_BIT; + + /* Set baud rate division to 32; formula: (n + 1) * 2 */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK; + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK, 15); + + /* Disable dual byte opcodes */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_DUAL_BYTE_OPCODE_EN_BIT; + + /* Disable PHY pipeline mode */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_PIPELINE_PHY_BIT; + + /* Disable PHY module generally */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_PHY_MODE_ENABLE_BIT; + + /* Disable CRC */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_CRC_ENABLE_BIT; + + /* Disable DMA generally since it's not supported */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENB_DMA_IF_BIT; + + /* Disable automatic write protection disablement of MSPI peripherals */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_WR_PROT_FLASH_BIT; + + /* Disable possible reset pin */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_RESET_PIN_BIT; + + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + /* Set how many FSS0 SRAM locations are allocated for read; the other ones + * are allocated for writes + */ + uint32_t sram_partition = sys_read32(base_addr + CADENCE_MSPI_SRAM_PARTITION_CFG_OFFSET); + + sram_partition &= ~CADENCE_MSPI_SRAM_PARTITION_CFG_REG_ADDR_MASK; + sram_partition |= FIELD_PREP(CADENCE_MSPI_SRAM_PARTITION_CFG_REG_ADDR_MASK, + config->sram_allocated_for_read); + + sys_write32(sram_partition, base_addr + CADENCE_MSPI_SRAM_PARTITION_CFG_OFFSET); + + /* General clock cycle delays */ + uint32_t timing_config_reg = sys_read32(base_addr + CADENCE_MSPI_DEV_DELAY_OFFSET); + + timing_config_reg &= ~CADENCE_MSPI_DEV_DELAY_REG_D_NSS_MASK; + timing_config_reg |= + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_NSS_MASK, CADENCE_MSPI_DEFAULT_DELAY); + + timing_config_reg &= ~CADENCE_MSPI_DEV_DELAY_REG_D_BTWN_MASK; + timing_config_reg |= + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_BTWN_MASK, CADENCE_MSPI_DEFAULT_DELAY); + + timing_config_reg &= ~CADENCE_MSPI_DEV_DELAY_REG_D_AFTER_MASK; + timing_config_reg |= + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_AFTER_MASK, CADENCE_MSPI_DEFAULT_DELAY); + + timing_config_reg &= ~CADENCE_MSPI_DEV_DELAY_REG_D_INIT_MASK; + timing_config_reg |= + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_INIT_MASK, CADENCE_MSPI_DEFAULT_DELAY); + + sys_write32(timing_config_reg, base_addr + CADENCE_MSPI_DEV_DELAY_OFFSET); + + /* Set trigger reg address and range to 0 */ + uint32_t ind_ahb_addr_trigger = + sys_read32(base_addr + CADENCE_MSPI_IND_AHB_ADDR_TRIGGER_OFFSET); + + ind_ahb_addr_trigger &= ~CADENCE_MSPI_IND_AHB_ADDR_TRIGGER_OFFSET; + + sys_write32(ind_ahb_addr_trigger, base_addr + CADENCE_MSPI_IND_AHB_ADDR_TRIGGER_OFFSET); + + uint32_t indirect_trigger_addr_range = + sys_read32(base_addr + CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_OFFSET); + + indirect_trigger_addr_range &= + ~CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_REG_IND_RANGE_WIDTH_MASK; + + sys_write32(indirect_trigger_addr_range, + base_addr + CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_OFFSET); + + /* Disable loop-back via DQS */ + uint32_t rd_data_capture = sys_read32(base_addr + CADENCE_MSPI_RD_DATA_CAPTURE_OFFSET); + + rd_data_capture |= CADENCE_MSPI_RD_DATA_CAPTURE_REG_BYPASS_BIT; + + sys_write32(rd_data_capture, base_addr + CADENCE_MSPI_RD_DATA_CAPTURE_OFFSET); + + /* Disable auto polling for write completion */ + uint32_t write_completion_ctrl = + sys_read32(base_addr + CADENCE_MSPI_WRITE_COMPLETION_CTRL_OFFSET); + + write_completion_ctrl |= CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_DISABLE_POLLING_BIT; + + sys_write32(write_completion_ctrl, base_addr + CADENCE_MSPI_WRITE_COMPLETION_CTRL_OFFSET); + + /* Disable automatic write enable command before indirect write transactions */ + uint32_t device_write = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + device_write &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WEL_DIS_BIT; + + sys_write32(device_write, base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + /* Reset mode bit (hardware CRC checking on read, if supported) and disable DDR mode */ + uint32_t device_read = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + device_read &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_MODE_BIT_ENABLE_BIT; + device_read &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DDR_EN_BIT; + + sys_write32(device_read, base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + uint32_t val; + + /* Disable all interrupts via masking */ + val = sys_read32(base_addr + CADENCE_MSPI_IRQ_MASK_OFFSET); + val &= ~CADENCE_MSPI_IRQ_MASK_REG_ALL; + sys_write32(val, base_addr + CADENCE_MSPI_IRQ_MASK_OFFSET); + + /* Clear currently pending interrupts */ + val = sys_read32(base_addr + CADENCE_MSPI_IRQ_STATUS_OFFSET); + val |= CADENCE_MSPI_IRQ_STATUS_REG_ALL; + sys_write32(val, base_addr + CADENCE_MSPI_IRQ_STATUS_OFFSET); + + /* Re-enable MSPI controller */ + config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + config_reg |= CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT; + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + return 0; +} + +static int mspi_cadence_stig(const struct device *controller, const struct mspi_xfer *req, + uint32_t index, const uint64_t start_time) +{ + const mem_addr_t base_address = DEVICE_MMIO_GET(controller); + const struct mspi_xfer_packet *packet = &req->packets[index]; + uint32_t dummy_cycles = 0; + + /* Reset previous command configuration completely */ + sys_write32(0, base_address + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET); + + uint32_t flash_cmd_ctrl = 0; + + if (packet->dir == MSPI_RX) { + if (packet->num_bytes != 0) { + flash_cmd_ctrl |= CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_READ_DATA_BIT; + flash_cmd_ctrl |= + FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_RD_DATA_BYTES_MASK, + packet->num_bytes - 1); + } + dummy_cycles = req->rx_dummy; + } else { + if (packet->num_bytes != 0) { + flash_cmd_ctrl |= CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_WRITE_DATA_BIT; + flash_cmd_ctrl |= + FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_WR_DATA_BYTES_MASK, + packet->num_bytes - 1); + + if (packet->num_bytes > 4) { + uint32_t upper = 0; + + memcpy(&upper, &packet->data_buf[4], packet->num_bytes - 4); + sys_write32(upper, + base_address + CADENCE_MSPI_FLASH_WR_DATA_UPPER_OFFSET); + } + uint32_t lower = 0; + + memcpy(&lower, &packet->data_buf[0], MIN(packet->num_bytes, 4)); + sys_write32(lower, base_address + CADENCE_MSPI_FLASH_WR_DATA_LOWER_OFFSET); + } + dummy_cycles = req->tx_dummy; + } + + flash_cmd_ctrl |= FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_OPCODE_MASK, packet->cmd); + flash_cmd_ctrl |= + FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_DUMMY_CYCLES_MASK, dummy_cycles); + + if (req->addr_length) { + flash_cmd_ctrl |= CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_COMD_ADDR_BIT; + flash_cmd_ctrl |= FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_ADDR_BYTES_MASK, + req->addr_length - 1); + sys_write32(packet->address, base_address + CADENCE_MSPI_FLASH_CMD_ADDR_OFFSET); + } + + /* Start transaction */ + flash_cmd_ctrl |= CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_EXEC_BIT; + sys_write32(flash_cmd_ctrl, base_address + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET); + + uint32_t exec_status = sys_read32(base_address + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET) & + CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_EXEC_STATUS_BIT; + while (exec_status != 0 && k_uptime_get() - start_time < req->timeout) { + k_sleep(CADENCE_MSPI_TIME_BETWEEN_RETRIES); + exec_status = sys_read32(base_address + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET) & + CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_EXEC_STATUS_BIT; + } + if (exec_status != 0) { + LOG_ERR("Timeout while waiting for dedicated command to finish"); + return -EIO; + } + + if (packet->dir == MSPI_RX) { + if (packet->num_bytes > 4) { + uint32_t higher = + sys_read32(base_address + CADENCE_MSPI_FLASH_RD_DATA_UPPER_OFFSET); + + memcpy(&packet->data_buf[4], &higher, packet->num_bytes - 4); + } + uint32_t lower = sys_read32(base_address + CADENCE_MSPI_FLASH_RD_DATA_LOWER_OFFSET); + + memcpy(&packet->data_buf[0], &lower, MIN(packet->num_bytes, 4)); + } + + /* + * The STIG register must be reset after the transfer or weird things like + * skipping every 2nd byte can occur + */ + sys_write32(0, base_address + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET); + + return 0; +} + +static int mspi_cadence_indirect_read(const struct device *controller, const struct mspi_xfer *req, + uint32_t index, const uint64_t start_time) +{ + const mem_addr_t base_address = DEVICE_MMIO_GET(controller); + const struct mspi_cadence_config *config = controller->config; + const struct mspi_xfer_packet *packet = &req->packets[index]; + + uint32_t dev_instr_rd_cfg = + sys_read32(base_address + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + dev_instr_rd_cfg &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_RD_OPCODE_NON_XIP_MASK; + dev_instr_rd_cfg |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_RD_OPCODE_NON_XIP_MASK, + packet->cmd); + dev_instr_rd_cfg &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MASK; + dev_instr_rd_cfg |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MASK, req->rx_dummy); + + sys_write32(dev_instr_rd_cfg, base_address + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + sys_write32(packet->address, base_address + CADENCE_MSPI_INDIRECT_READ_XFER_START_OFFSET); + sys_write32(packet->num_bytes, + base_address + CADENCE_MSPI_INDIRECT_READ_XFER_NUM_BYTES_OFFSET); + + uint32_t dev_size_config = sys_read32(base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); + + dev_size_config &= ~CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK; + dev_size_config |= FIELD_PREP(CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK, + req->addr_length - 1); + + sys_write32(dev_size_config, base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); + + /* Start transfer */ + uint32_t indirect_read_ctrl = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + + indirect_read_ctrl |= CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_START_BIT; + + sys_write32(indirect_read_ctrl, base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + + uint32_t remaining_bytes = packet->num_bytes; + uint8_t *write_ptr = packet->data_buf; + uint32_t num_new_words = 0; + uint32_t current_new_word; + int bytes_to_copy_from_current_word; + + while (remaining_bytes > 0) { + if (k_uptime_get() - start_time > req->timeout) { + LOG_ERR("Timeout while receiving data from MSPI device"); + goto timeout; + } + uint32_t indac_read = sys_read32(base_address + CADENCE_MSPI_SRAM_FILL_OFFSET); + + num_new_words = FIELD_GET(CADENCE_MSPI_SRAM_FILL_REG_INDAC_READ_MASK, indac_read); + while (remaining_bytes > 0 && num_new_words > 0) { + current_new_word = sys_read32(config->fifo_addr); + bytes_to_copy_from_current_word = MIN(remaining_bytes, 4); + memcpy(write_ptr, ¤t_new_word, bytes_to_copy_from_current_word); + write_ptr += bytes_to_copy_from_current_word; + remaining_bytes -= bytes_to_copy_from_current_word; + --num_new_words; + } + } + + /* Wait until official indirect read completion */ + uint32_t done_status = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET) & + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + while (done_status == 0 && k_uptime_get() - start_time < req->timeout) { + k_sleep(CADENCE_MSPI_TIME_BETWEEN_RETRIES); + done_status = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET) & + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + } + if (done_status == 0) { + LOG_ERR("Timeout waiting for official indirect read done confirmation"); + goto timeout; + } + indirect_read_ctrl = sys_read32(base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + indirect_read_ctrl |= CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + sys_write32(indirect_read_ctrl, base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + + return 0; + +timeout: + indirect_read_ctrl = sys_read32(base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + indirect_read_ctrl |= CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_CANCEL_BIT; + sys_write32(indirect_read_ctrl, base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + return -EIO; +} + +static int mspi_cadence_indirect_write(const struct device *controller, const struct mspi_xfer *req, + uint32_t index, const uint64_t start_time) +{ + const mem_addr_t base_address = DEVICE_MMIO_GET(controller); + const struct mspi_cadence_config *config = controller->config; + const struct mspi_xfer_packet *packet = &req->packets[index]; + + uint32_t dev_instr_wr_cfg = + sys_read32(base_address + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + dev_instr_wr_cfg &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WR_OPCODE_NON_XIP_MASK; + dev_instr_wr_cfg |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WR_OPCODE_NON_XIP_MASK, + packet->cmd); + dev_instr_wr_cfg &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MASK; + dev_instr_wr_cfg |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MASK, req->tx_dummy); + + sys_write32(dev_instr_wr_cfg, base_address + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + sys_write32(packet->address, base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_START_OFFSET); + sys_write32(packet->num_bytes, + base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_NUM_BYTES_OFFSET); + + uint32_t dev_size_config = sys_read32(base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); + + dev_size_config &= ~CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK; + dev_size_config |= FIELD_PREP(CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK, + req->addr_length - 1); + + sys_write32(dev_size_config, base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); + + uint32_t indirect_write_ctrl = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + + indirect_write_ctrl |= CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_START_BIT; + + sys_write32(indirect_write_ctrl, + base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + + uint32_t read_offset = 0; + uint32_t remaining_bytes = packet->num_bytes; + uint32_t free_words = 0; + uint32_t current_word_to_write; + + while (remaining_bytes > 0) { + if (k_uptime_get() - start_time > req->timeout) { + LOG_ERR("Timeout while sending data to MSPI device"); + goto timeout; + } + uint32_t sram_fill = sys_read32(base_address + CADENCE_MSPI_SRAM_FILL_OFFSET); + + free_words = config->sram_allocated_for_read - + FIELD_GET(CADENCE_MSPI_SRAM_FILL_REG_INDAC_WRITE_MASK, sram_fill); + while (free_words > 0 && remaining_bytes > 0) { + current_word_to_write = 0; + memcpy(¤t_word_to_write, &packet->data_buf[read_offset], + MIN(remaining_bytes, 4)); + sys_write32(current_word_to_write, config->fifo_addr); + remaining_bytes = (remaining_bytes > 4 ? remaining_bytes - 4 : 0); + read_offset += 4; + --free_words; + } + } + + /* Wait for official finish */ + uint32_t done_status = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET) & + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + while (done_status == 0 && k_uptime_get() - start_time < req->timeout) { + k_sleep(CADENCE_MSPI_TIME_BETWEEN_RETRIES); + done_status = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET) & + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + } + if (done_status == 0) { + LOG_ERR("Timeout while waiting for official write done confirmation"); + goto timeout; + } + indirect_write_ctrl = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + indirect_write_ctrl |= CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + sys_write32(indirect_write_ctrl, + base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + + return 0; +timeout: + indirect_write_ctrl = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + indirect_write_ctrl |= CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_CANCEL_BIT; + sys_write32(indirect_write_ctrl, + base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + return -EIO; +} + +static int mspi_cadence_transceive(const struct device *controller, + const struct mspi_dev_id *dev_id, const struct mspi_xfer *req) +{ + uint64_t start_time = k_uptime_get(); + struct mspi_cadence_data *data = controller->data; + int ret = 0; + + if (data->current_peripheral != dev_id) { + LOG_ERR("Tried to send data over MSPI despite not having acquired the controller " + "beforehand by calling mspi_dev_config"); + return -EINVAL; + } + + ret = mspi_cadence_check_transfer_request(req); + if (ret) { + return ret; + } + + if (req->timeout > CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE) { + LOG_ERR("Request timeout exceeds configured maximum in Kconfig"); + return -EINVAL; + } + + ret = k_sem_take(&data->transfer_lock, K_MSEC(req->timeout)); + if (ret < 0) { + return ret; + } + + for (uint32_t i = 0; i < req->num_packet; ++i) { + const struct mspi_xfer_packet *packet = &req->packets[i]; + /* the FLASH_CMD_REGISTER is good for small transfers with only very little/no data + */ + if (packet->num_bytes <= 8) { + ret = mspi_cadence_stig(controller, req, i, start_time); + if (ret < 0) { + goto exit; + } + } else { + /* big transfer via indirect transfer mode */ + if (packet->dir == MSPI_RX) { + ret = mspi_cadence_indirect_read(controller, req, i, start_time); + } else { + ret = mspi_cadence_indirect_write(controller, req, i, start_time); + } + if (ret < 0) { + goto exit; + } + } + } + +exit: + k_sem_give(&data->transfer_lock); + return ret; +} + +static int mspi_cadence_set_opcode_lines(const mem_addr_t base_addr, enum mspi_io_mode io_mode) +{ + uint32_t rd_config = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + rd_config &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK; + + switch (io_mode) { + case MSPI_IO_MODE_SINGLE: + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_DUAL_1_2_2: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_QUAD_1_4_4: + case MSPI_IO_MODE_OCTAL_1_1_8: + case MSPI_IO_MODE_OCTAL_1_8_8: + rd_config |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK, 0); + break; + case MSPI_IO_MODE_DUAL: + rd_config |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK, 1); + break; + case MSPI_IO_MODE_QUAD: + rd_config |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK, 2); + break; + case MSPI_IO_MODE_OCTAL: + rd_config |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK, 3); + break; + default: + return -ENOTSUP; + } + + sys_write32(rd_config, base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + return 0; +} + +static int mspi_cadence_set_addr_lines(const mem_addr_t base_addr, enum mspi_io_mode io_mode) +{ + uint32_t rd_config = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + uint32_t wr_config = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + rd_config &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK; + wr_config &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK; + + switch (io_mode) { + case MSPI_IO_MODE_SINGLE: + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_OCTAL_1_1_8: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 0); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 0); + break; + case MSPI_IO_MODE_DUAL: + case MSPI_IO_MODE_DUAL_1_2_2: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 1); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 1); + break; + case MSPI_IO_MODE_QUAD: + case MSPI_IO_MODE_QUAD_1_4_4: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 2); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 2); + break; + case MSPI_IO_MODE_OCTAL: + case MSPI_IO_MODE_OCTAL_1_8_8: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 3); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 3); + break; + default: + return -ENOTSUP; + } + + sys_write32(rd_config, base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + sys_write32(wr_config, base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + return 0; +} + +static int mspi_cadence_set_data_lines(const mem_addr_t base_addr, enum mspi_io_mode io_mode) +{ + uint32_t rd_config = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + uint32_t wr_config = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + rd_config &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK; + wr_config &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK; + + switch (io_mode) { + case MSPI_IO_MODE_SINGLE: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 0); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 0); + break; + case MSPI_IO_MODE_DUAL: + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_DUAL_1_2_2: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 1); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 1); + break; + case MSPI_IO_MODE_QUAD: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_QUAD_1_4_4: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 2); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 2); + break; + case MSPI_IO_MODE_OCTAL: + case MSPI_IO_MODE_OCTAL_1_1_8: + case MSPI_IO_MODE_OCTAL_1_8_8: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 3); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 3); + break; + default: + return -ENOTSUP; + } + + sys_write32(rd_config, base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + sys_write32(wr_config, base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + return 0; +} + +static int mspi_cadence_dev_config(const struct device *controller, + const struct mspi_dev_id *dev_id, + const enum mspi_dev_cfg_mask param_mask, + const struct mspi_dev_cfg *dev_cfg) +{ + const mem_addr_t base_addr = DEVICE_MMIO_GET(controller); + const struct mspi_cadence_config *config = controller->config; + struct mspi_cadence_data *data = controller->data; + int ret = 0; + + if (data->current_peripheral != dev_id) { + ret = k_mutex_lock(&data->access_lock, + K_MSEC(CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE)); + if (ret < 0) { + LOG_ERR("Error waiting for MSPI controller lock for changing device " + "config"); + return ret; + } + data->current_peripheral = dev_id; + } + + if (param_mask & CADENCE_MSPI_IGNORED_DEV_CONFIG_PARAMS) { + if (param_mask == MSPI_DEVICE_CONFIG_ALL) { + LOG_ERR("Device configuration includes ignored / not implemented " + "parameters. For " + "better compatibility these are ignored without returning an error " + "due to the usage " + "of MSPI_DEVICE_CONFIG_ALL. To see which " + "parameters are explicitly ignored check mspi_cadence.h"); + } else { + LOG_ERR("Device configuration includes ignored / not implemented " + "parameters. Check mspi_cadence.h to figure out what isn't " + "supported"); + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_BREAK_TIME) { + if (dev_cfg->time_to_break != 0) { + LOG_ERR("Automatically breaking up transfers is not supported"); + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_MEM_BOUND) { + if (dev_cfg->mem_boundary != 0) { + LOG_ERR("Automatically breaking up transfers is not supported"); + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_ENDIAN) { + if (dev_cfg->endian != MSPI_XFER_LITTLE_ENDIAN) { + LOG_ERR("Only little Endian is supported for now"); + /* There is no hardware native support for big endian but it can be + * done in software + */ + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_CE_POL) { + if (dev_cfg->ce_polarity != MSPI_CE_ACTIVE_LOW) { + LOG_ERR("Non active low chip enable polarities haven't been implemented " + "yet"); + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_DQS) { + if (dev_cfg->dqs_enable) { + LOG_ERR("DQS is not implemented yet"); + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_DATA_RATE) { + if (dev_cfg->data_rate != MSPI_DATA_RATE_SINGLE) { + LOG_ERR("Only single data rate is supported for now"); + ret = -ENOSYS; + goto exit; + } + } + + /* Disable MSPI during configuration */ + uint32_t config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT; + + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + ret = mspi_cadence_wait_for_idle(controller); + if (ret < 0) { + goto exit; + } + + if (param_mask & MSPI_DEVICE_CONFIG_CE_NUM) { + uint32_t num; + + if (dev_cfg->ce_num > 3) { + LOG_ERR("Non implemented chip select. Only hardware CS 0 to 3 are " + "implemented"); + ret = -ENOSYS; + goto exit; + } + num = ~BIT(dev_cfg->ce_num) & BIT_MASK(4); + + config_reg &= ~CADENCE_MSPI_CONFIG_REG_PERIPH_CS_LINES_MASK; + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_PERIPH_CS_LINES_MASK, num); + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + } + if (param_mask & MSPI_DEVICE_CONFIG_IO_MODE) { + ret = mspi_cadence_set_opcode_lines(base_addr, dev_cfg->io_mode); + if (ret) { + goto exit; + } + ret = mspi_cadence_set_data_lines(base_addr, dev_cfg->io_mode); + if (ret) { + goto exit; + } + ret = mspi_cadence_set_addr_lines(base_addr, dev_cfg->io_mode); + if (ret) { + goto exit; + } + } + if (param_mask & MSPI_DEVICE_CONFIG_CPP) { + config_reg &= ~CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT; + config_reg &= ~CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT; + switch (dev_cfg->cpp) { + case MSPI_CPP_MODE_0: + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT, 0); + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT, 0); + break; + case MSPI_CPP_MODE_1: + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT, 0); + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT, 1); + break; + case MSPI_CPP_MODE_2: + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT, 1); + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT, 0); + break; + case MSPI_CPP_MODE_3: + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT, 1); + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT, 1); + break; + default: + LOG_ERR("Invalid clock polarity/phase configuration"); + ret = -ENOTSUP; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_FREQUENCY) { + config_reg &= ~CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK; + + /* + * This is underflow-safe (assuming the reference frequency is set) + * since required_div will be at least 1 leading to the second + * DIV_ROUND_UP also returning at least 1 before being subtracted 1 + * resulting in 0. + */ + uint32_t required_div = DIV_ROUND_UP(config->reference_frequency, dev_cfg->freq); + uint32_t actual_div = DIV_ROUND_UP(required_div, 2) - 1; + + if (actual_div > 15) { + LOG_ERR("Requested a frequency that is too low to achieve by dividing the " + "reference clock"); + ret = -ENOTSUP; + goto exit; + } + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK, actual_div); + } + + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + +exit: + /* Re-enable MSPI */ + config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + config_reg |= CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT; + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + /* Unlock in case of an error */ + if (ret != 0) { + k_mutex_unlock(&data->access_lock); + } + + return ret; +} + +static int mspi_cadence_get_channel_status(const struct device *controller, uint8_t channel) +{ + ARG_UNUSED(channel); + + const mem_addr_t base_addr = DEVICE_MMIO_GET(controller); + struct mspi_cadence_data *data = controller->data; + + if ((sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET) & + CADENCE_MSPI_CONFIG_REG_IDLE_BIT) == 0) { + return -EBUSY; + } + + if (k_sem_count_get(&data->transfer_lock) == 0) { + return -EBUSY; + } + + data->current_peripheral = NULL; + + k_mutex_unlock(&data->access_lock); + return 0; +} + +static DEVICE_API(mspi, mspi_cadence_driver_api) = { + .config = NULL, + .dev_config = mspi_cadence_dev_config, + .xip_config = NULL, + .scramble_config = NULL, + .timing_config = NULL, + .get_channel_status = mspi_cadence_get_channel_status, + .register_callback = NULL, + .transceive = mspi_cadence_transceive, +}; + +#define CADENCE_CHECK_MULTIPERIPHERAL(n) \ + BUILD_ASSERT(DT_PROP_OR(DT_DRV_INST(n), software_multiperipheral, 0) == 0, \ + "Multiperipherals arent's supported by the driver as of now") + +#define MSPI_CONFIG(n) \ + {.op_mode = DT_INST_ENUM_IDX_OR(n, op_mode, MSPI_OP_MODE_CONTROLLER), \ + .sw_multi_periph = DT_INST_PROP(n, software_multiperipheral)} + +#define CADENCE_MSPI_DEFINE(n) \ + CADENCE_CHECK_MULTIPERIPHERAL(n); \ + PINCTRL_DT_DEFINE(DT_DRV_INST(n)); \ + static const struct mspi_cadence_config mspi_cadence_config_##n = { \ + DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \ + .pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .mspi_config = MSPI_CONFIG(n), \ + .fifo_addr = DT_REG_ADDR_BY_IDX(DT_DRV_INST(n), 1), \ + .sram_allocated_for_read = DT_PROP(DT_DRV_INST(n), read_buffer_size), \ + .reference_frequency = DT_PROP(DT_DRV_INST(n), clock_frequency), \ + }; \ + static struct mspi_cadence_data mspi_cadence_data_##n = {}; \ + DEVICE_DT_INST_DEFINE(n, mspi_cadence_init, NULL, &mspi_cadence_data_##n, \ + &mspi_cadence_config_##n, PRE_KERNEL_2, CONFIG_MSPI_INIT_PRIORITY, \ + &mspi_cadence_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(CADENCE_MSPI_DEFINE) diff --git a/drivers/mspi/mspi_cadence.h b/drivers/mspi/mspi_cadence.h new file mode 100644 index 000000000000..ebba39a18e17 --- /dev/null +++ b/drivers/mspi/mspi_cadence.h @@ -0,0 +1,360 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 Siemens Mobility GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_MSPI_CDNS_H_ +#define ZEPHYR_DRIVERS_MSPI_CDNS_H_ + +#include +#include + +/* + * Implemented dev_cfg_bits. Implemented means that the value is at least + * checked for undesireable values. Changing any of these values might not be + * actually supported in the end! + */ +#define CADENCE_MSPI_IMPLEMENTED_DEV_CONFIG_PARAMS \ + (MSPI_DEVICE_CONFIG_BREAK_TIME | MSPI_DEVICE_CONFIG_MEM_BOUND | \ + MSPI_DEVICE_CONFIG_ENDIAN | MSPI_DEVICE_CONFIG_CE_POL | MSPI_DEVICE_CONFIG_DQS | \ + MSPI_DEVICE_CONFIG_DATA_RATE | MSPI_DEVICE_CONFIG_CE_NUM | MSPI_DEVICE_CONFIG_IO_MODE | \ + MSPI_DEVICE_CONFIG_CPP | MSPI_DEVICE_CONFIG_FREQUENCY | MSPI_DEVICE_CONFIG_CE_POL) + +/* + * Non-implemented dev_cfg_bits + * + * These configuration requests are not implemented by the driver. For + * compatibility no error is returned, if especially MSPI_DEVICE_CONFIG_ALL was + * requested. + */ +#define CADENCE_MSPI_IGNORED_DEV_CONFIG_PARAMS \ + (MSPI_DEVICE_CONFIG_ALL & ~CADENCE_MSPI_IMPLEMENTED_DEV_CONFIG_PARAMS) + +/* Default delay for time between clock enablement and chip select and other */ +#define CADENCE_MSPI_DEFAULT_DELAY 10 + +/* Timeout calculations and default timeout values */ +#define CADENCE_MSPI_TIME_BETWEEN_RETRIES_MS 10 +#define CADENCE_MSPI_TIME_BETWEEN_RETRIES K_MSEC(CADENCE_MSPI_TIME_BETWEEN_RETRIES_MS) +#define CADENCE_MSPI_GET_NUM_RETRIES(timeout_ms) (timeout_ms / CADENCE_MSPI_TIME_BETWEEN_RETRIES_MS) + +/* General register offsets */ +#define CADENCE_MSPI_CONFIG_OFFSET 0x0u +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET 0x4u +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET 0x8u +#define CADENCE_MSPI_DEV_DELAY_OFFSET 0xcu +#define CADENCE_MSPI_RD_DATA_CAPTURE_OFFSET 0x10u +#define CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET 0x14u +#define CADENCE_MSPI_SRAM_PARTITION_CFG_OFFSET 0x18u +#define CADENCE_MSPI_IND_AHB_ADDR_TRIGGER_OFFSET 0x1cu +#define CADENCE_MSPI_DMA_PERIPH_CONFIG_OFFSET 0x20u +#define CADENCE_MSPI_REMAP_ADDR_OFFSET 0x24u +#define CADENCE_MSPI_MODE_BIT_CONFIG_OFFSET 0x28u +#define CADENCE_MSPI_SRAM_FILL_OFFSET 0x2cu +#define CADENCE_MSPI_TX_THRESH_OFFSET 0x30u +#define CADENCE_MSPI_RX_THRESH_OFFSET 0x34u +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_OFFSET 0x38u +#define CADENCE_MSPI_NO_OF_POLLS_BEF_EXP_OFFSET 0x3cu +#define CADENCE_MSPI_IRQ_STATUS_OFFSET 0x40u +#define CADENCE_MSPI_IRQ_MASK_OFFSET 0x44u +#define CADENCE_MSPI_LOWER_WR_PROT_OFFSET 0x50u +#define CADENCE_MSPI_UPPER_WR_PROT_OFFSET 0x54u +#define CADENCE_MSPI_WR_PROT_CTRL_OFFSET 0x58u +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET 0x60u +#define CADENCE_MSPI_INDIRECT_READ_XFER_WATERMARK_OFFSET 0x64u +#define CADENCE_MSPI_INDIRECT_READ_XFER_START_OFFSET 0x68u +#define CADENCE_MSPI_INDIRECT_READ_XFER_NUM_BYTES_OFFSET 0x6cu +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET 0x70u +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_WATERMARK_OFFSET 0x74u +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_START_OFFSET 0x78u +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_NUM_BYTES_OFFSET 0x7cu +#define CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_OFFSET 0x80u +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_OFFSET 0x8cu +#define CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET 0x90u +#define CADENCE_MSPI_FLASH_CMD_ADDR_OFFSET 0x94u +#define CADENCE_MSPI_FLASH_RD_DATA_LOWER_OFFSET 0xa0u +#define CADENCE_MSPI_FLASH_RD_DATA_UPPER_OFFSET 0xa4u +#define CADENCE_MSPI_FLASH_WR_DATA_LOWER_OFFSET 0xa8u +#define CADENCE_MSPI_FLASH_WR_DATA_UPPER_OFFSET 0xacu +#define CADENCE_MSPI_POLLING_FLASH_STATUS_OFFSET 0xb0u +#define CADENCE_MSPI_PHY_CONFIGURATION_OFFSET 0xb4u +#define CADENCE_MSPI_PHY_MASTER_CONTROL_OFFSET 0xb8u +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_OFFSET 0xbcu +#define CADENCE_MSPI_DLL_OBSERVABLE_UPPER_OFFSET 0xc0u +#define CADENCE_MSPI_OPCODE_EXT_LOWER_OFFSET 0xe0u +#define CADENCE_MSPI_OPCODE_EXT_UPPER_OFFSET 0xe4u +#define CADENCE_MSPI_MODULE_ID_OFFSET 0xfcu + +/* CONFIG */ +#define CADENCE_MSPI_CONFIG_REG_IDLE_BIT BIT(31) +#define CADENCE_MSPI_CONFIG_REG_DUAL_BYTE_OPCODE_EN_BIT BIT(30) +#define CADENCE_MSPI_CONFIG_REG_CRC_ENABLE_BIT BIT(29) +#define CADENCE_MSPI_CONFIG_REG_PIPELINE_PHY_BIT BIT(25) +#define CADENCE_MSPI_CONFIG_REG_ENABLE_DTR_PROTOCOL_BIT BIT(24) +#define CADENCE_MSPI_CONFIG_REG_ENABLE_AHB_DECODER_BIT BIT(23) +#define CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK GENMASK(22, 19) +#define CADENCE_MSPI_CONFIG_REG_ENTER_XIP_MODE_IMM_BIT BIT(18) +#define CADENCE_MSPI_CONFIG_REG_ENTER_XIP_MODE_BIT BIT(17) +#define CADENCE_MSPI_CONFIG_REG_ENB_AHB_ADDR_REMAP_BIT BIT(16) +#define CADENCE_MSPI_CONFIG_REG_ENB_DMA_IF_BIT BIT(15) +#define CADENCE_MSPI_CONFIG_REG_WR_PROT_FLASH_BIT BIT(14) +#define CADENCE_MSPI_CONFIG_REG_PERIPH_CS_LINES_MASK GENMASK(13, 10) +#define CADENCE_MSPI_CONFIG_REG_PERIPH_SEL_DEC_BIT BIT(9) +#define CADENCE_MSPI_CONFIG_REG_ENB_LEGACY_IP_MODE_BIT BIT(8) +#define CADENCE_MSPI_CONFIG_REG_ENB_DIR_ACC_CTRL_BIT BIT(7) +#define CADENCE_MSPI_CONFIG_REG_RESET_CFG_BIT BIT(6) +#define CADENCE_MSPI_CONFIG_REG_RESET_PIN_BIT BIT(5) +#define CADENCE_MSPI_CONFIG_REG_HOLD_PIN_BIT BIT(4) +#define CADENCE_MSPI_CONFIG_REG_PHY_MODE_ENABLE_BIT BIT(3) +#define CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT BIT(2) +#define CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT BIT(1) +#define CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT BIT(0) + +/* DEV_INSTR_RD_CONFIG */ +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MASK GENMASK(28, 24) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_MODE_BIT_ENABLE_BIT BIT(20) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK GENMASK(17, 16) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK GENMASK(13, 12) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DDR_EN_BIT BIT(10) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK GENMASK(9, 8) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_RD_OPCODE_NON_XIP_MASK GENMASK(7, 0) + +/* DEV_INSTR_WR_CONFIG */ +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MASK GENMASK(28, 24) +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK GENMASK(17, 16) +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK GENMASK(13, 12) +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WEL_DIS_BIT BIT(8) +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WR_OPCODE_NON_XIP_MASK GENMASK(7, 0) + +/* Max value of DUMMY_RD_CLK_CYCLES_FLD / DUMMY_WR_CLK_CYCLES_FLD */ +#define CADENCE_MSPI_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MAX_VALUE 0x1F +#define CADENCE_MSPI_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MAX_VALUE 0x1F + +/* DEV_DELAY */ +#define CADENCE_MSPI_DEV_DELAY_REG_D_NSS_MASK GENMASK(31, 24) +#define CADENCE_MSPI_DEV_DELAY_REG_D_BTWN_MASK GENMASK(23, 16) +#define CADENCE_MSPI_DEV_DELAY_REG_D_AFTER_MASK GENMASK(15, 8) +#define CADENCE_MSPI_DEV_DELAY_REG_D_INIT_MASK GENMASK(7, 0) + +/* RD_DATA_CAPTURE */ +#define CADENCE_MSPI_RD_DATA_CAPTURE_REG_DDR_READ_DELAY_MASK GENMASK(19, 16) +#define CADENCE_MSPI_RD_DATA_CAPTURE_REG_DQS_ENABLE_BIT BIT(8) +#define CADENCE_MSPI_RD_DATA_CAPTURE_REG_SAMPLE_EDGE_SEL_BIT BIT(5) +#define CADENCE_MSPI_RD_DATA_CAPTURE_REG_DELAY_MASK GENMASK(4, 1) +#define CADENCE_MSPI_RD_DATA_CAPTURE_REG_BYPASS_BIT BIT(0) + +/* DEV_SIZE_CONFIG */ +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS3_MASK GENMASK(28, 27) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS2_MASK GENMASK(26, 25) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS1_MASK GENMASK(24, 23) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS0_MASK GENMASK(22, 21) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_BYTES_PER_SUBSECTOR_MASK GENMASK(20, 16) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_BYTES_PER_DEVICE_PAGE_MASK GENMASK(15, 4) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK GENMASK(3, 0) + +/* SRAM_PARTITION_CFG */ +#define CADENCE_MSPI_SRAM_PARTITION_CFG_REG_ADDR_MASK GENMASK(7, 0) + +/* INDIRECT_TRIGGER_ADDR_RANGE */ +#define CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_REG_IND_RANGE_WIDTH_MASK GENMASK(3, 0) + +/* REMAP_ADDR */ +#define CADENCE_MSPI_REMAP_ADDR_REG_VALUE_MASK GENMASK(31, 0) + +/* SRAM_FILL */ +#define CADENCE_MSPI_SRAM_FILL_REG_INDAC_WRITE_MASK GENMASK(31, 16) +#define CADENCE_MSPI_SRAM_FILL_REG_INDAC_READ_MASK GENMASK(15, 0) + +/* TX_THRESH */ +#define CADENCE_MSPI_TX_THRESH_REG_LEVEL_MASK GENMASK(4, 0) + +/* RX_THRESH */ +#define CADENCE_MSPI_RX_THRESH_REG_LEVEL_MASK GENMASK(4, 0) + +/* WRITE_COMPLETION_CTRL */ +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_POLL_REP_DELAY_MASK GENMASK(31, 24) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_POLL_COUNT_MASK GENMASK(23, 16) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_ENABLE_POLLING_EXP_BIT BIT(15) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_DISABLE_POLLING_BIT BIT(14) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_POLLING_POLARITY_BIT BIT(13) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_POLLING_BIT_INDEX_MASK GENMASK(10, 8) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_OPCODE_MASK GENMASK(7, 0) + +/* NO_OF_POLLS_BEF_EXP */ +#define CADENCE_MSPI_NO_OF_POLLS_BEF_EXP_REG_NO_OF_POLLS_BEF_EXP_MASK GENMASK(31, 0) + +/* IRQ_STATUS */ +#define CADENCE_MSPI_IRQ_STATUS_REG_ECC_FAIL_BIT BIT(19) +#define CADENCE_MSPI_IRQ_STATUS_REG_TX_CRC_CHUNK_BRK_BIT BIT(18) +#define CADENCE_MSPI_IRQ_STATUS_REG_RX_CRC_DATA_VAL_BIT BIT(17) +#define CADENCE_MSPI_IRQ_STATUS_REG_RX_CRC_DATA_ERR_BIT BIT(16) +#define CADENCE_MSPI_IRQ_STATUS_REG_STIG_REQ_INT_BIT BIT(14) +#define CADENCE_MSPI_IRQ_STATUS_REG_POLL_EXP_INT_BIT BIT(13) +#define CADENCE_MSPI_IRQ_STATUS_REG_INDRD_SRAM_FULL_BIT BIT(12) +#define CADENCE_MSPI_IRQ_STATUS_REG_RX_FIFO_FULL_BIT BIT(11) +#define CADENCE_MSPI_IRQ_STATUS_REG_RX_FIFO_NOT_EMPTY_BIT BIT(10) +#define CADENCE_MSPI_IRQ_STATUS_REG_TX_FIFO_FULL_BIT BIT(9) +#define CADENCE_MSPI_IRQ_STATUS_REG_TX_FIFO_NOT_FULL_BIT BIT(8) +#define CADENCE_MSPI_IRQ_STATUS_REG_RECV_OVERFLOW_BIT BIT(7) +#define CADENCE_MSPI_IRQ_STATUS_REG_INDIRECT_XFER_LEVEL_BREACH_BIT BIT(6) +#define CADENCE_MSPI_IRQ_STATUS_REG_ILLEGAL_ACCESS_DET_BIT BIT(5) +#define CADENCE_MSPI_IRQ_STATUS_REG_PROT_WR_ATTEMPT_BIT BIT(4) +#define CADENCE_MSPI_IRQ_STATUS_REG_INDIRECT_READ_REJECT_BIT BIT(3) +#define CADENCE_MSPI_IRQ_STATUS_REG_INDIRECT_OP_DONE_BIT BIT(2) +#define CADENCE_MSPI_IRQ_STATUS_REG_UNDERFLOW_DET_BIT BIT(1) +#define CADENCE_MSPI_IRQ_STATUS_REG_MODE_M_FAIL_BIT BIT(0) + +#define CADENCE_MSPI_IRQ_STATUS_REG_ALL (BIT_MASK(19) & ~BIT(15)) + +/* IRQ_MASK */ +#define CADENCE_MSPI_IRQ_BIT_REG_ECC_FAIL_MASK_MASK BIT(19) +#define CADENCE_MSPI_IRQ_BIT_REG_TX_CRC_CHUNK_BRK_MASK_MASK BIT(18) +#define CADENCE_MSPI_IRQ_BIT_REG_RX_CRC_DATA_VAL_MASK_MASK BIT(17) +#define CADENCE_MSPI_IRQ_BIT_REG_RX_CRC_DATA_ERR_MASK_MASK BIT(16) +#define CADENCE_MSPI_IRQ_BIT_REG_STIG_REQ_INT_MASK_MASK BIT(14) +#define CADENCE_MSPI_IRQ_BIT_REG_POLL_EXP_INT_MASK_MASK BIT(13) +#define CADENCE_MSPI_IRQ_BIT_REG_INDRD_SRAM_FULL_MASK_MASK BIT(12) +#define CADENCE_MSPI_IRQ_BIT_REG_RX_FIFO_FULL_MASK_MASK BIT(11) +#define CADENCE_MSPI_IRQ_BIT_REG_RX_FIFO_NOT_EMPTY_MASK_MASK BIT(10) +#define CADENCE_MSPI_IRQ_BIT_REG_TX_FIFO_FULL_MASK_MASK BIT(9) +#define CADENCE_MSPI_IRQ_BIT_REG_TX_FIFO_NOT_FULL_MASK_MASK BIT(8) +#define CADENCE_MSPI_IRQ_BIT_REG_RECV_OVERFLOW_MASK_MASK BIT(7) +#define CADENCE_MSPI_IRQ_BIT_REG_INDIRECT_XFER_LEVEL_BREACH_MASK_MASK BIT(6) +#define CADENCE_MSPI_IRQ_BIT_REG_ILLEGAL_ACCESS_DET_MASK_MASK BIT(5) +#define CADENCE_MSPI_IRQ_BIT_REG_PROT_WR_ATTEMPT_MASK_MASK BIT(4) +#define CADENCE_MSPI_IRQ_BIT_REG_INDIRECT_READ_REJECT_MASK_MASK BIT(3) +#define CADENCE_MSPI_IRQ_BIT_REG_INDIRECT_OP_DONE_MASK_MASK BIT(2) +#define CADENCE_MSPI_IRQ_BIT_REG_UNDERFLOW_DET_MASK_MASK BIT(1) +#define CADENCE_MSPI_IRQ_BIT_REG_MODE_M_FAIL_MASK_MASK BIT(0) + +#define CADENCE_MSPI_IRQ_MASK_REG_ALL (BIT_MASK(19) & ~BIT(15)) + +/* LOWER_WR_PROT */ +#define CADENCE_MSPI_LOWER_WR_PROT_REG_SUBSECTOR_MASK GENMASK(31, 0) + +/* UPPER_WR_PROT */ +#define CADENCE_MSPI_UPPER_WR_PROT_REG_SUBSECTOR_MASK GENMASK(31, 0) + +/* WR_PROT_CTRL */ +#define CADENCE_MSPI_WR_PROT_CTRL_REG_ENB_BIT BIT(1) +#define CADENCE_MSPI_WR_PROT_CTRL_REG_INV_BIT BIT(0) + +/* INDIRECT_READ_XFER_CTRL */ +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_NUM_IND_OPS_DONE_MASK GENMASK(7, 6) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT BIT(5) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_RD_QUEUED_BIT BIT(4) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_SRAM_FULL_BIT BIT(3) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_RD_STATUS_BIT BIT(2) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_CANCEL_BIT BIT(1) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_START_BIT BIT(0) + +/* INDIRECT_READ_XFER_WATERMARK */ +#define CADENCE_MSPI_INDIRECT_READ_XFER_WATERMARK_REG_LEVEL_MASK GENMASK(31, 0) + +/* INDIRECT_READ_XFER_START */ +#define CADENCE_MSPI_INDIRECT_READ_XFER_START_REG_ADDR_MASK GENMASK(31, 0) + +/* INDIRECT_READ_XFER_NUM_BYTES */ +#define CADENCE_MSPI_INDIRECT_READ_XFER_NUM_BYTES_REG_VALUE_MASK GENMASK(31, 0) + +/* INDIRECT_WRITE_XFER_CTRL_REG */ +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_NUM_IND_OPS_DONE_MASK GENMASK(7, 6) +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT BIT(5) +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_WR_QUEUED_BIT BIT(4) +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_WR_STATUS_BIT BIT(2) +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_CANCEL_BIT BIT(1) +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_START_BIT BIT(0) + +/* INDIRECT_WRITE_XFER_WATERMARK */ +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_WATERMARK_REG_LEVEL_MASK GENMASK(31, 0) + +/* INDIRECT_WRITE_XFER_START */ +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_START_REG_ADDR_MASK GENMASK(31, 0) + +/* INDIRECT_WRITE_XFER_NUM_BYTES */ +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_NUM_BYTES_REG_VALUE_MASK GENMASK(31, 0) + +/* IND_AHB_ADDR_TRIGGER */ +#define CADENCE_MSPI_IND_AHB_ADDR_TRIGGER_REG_ADDR_MASK GENMASK(31, 0) + +/* FLASH_COMMAND_CTRL_MEM */ +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_REG_MEM_BANK_ADDR_MASK GENMASK(28, 20) +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_REG_NB_OF_STIG_READ_BYTES_MASK GENMASK(18, 16) +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_REG_MEM_BANK_READ_DATA_MASK GENMASK(15, 8) +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_REG_MEM_BANK_REQ_IN_PROGRESS_BIT BIT(1) +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_REG_TRIGGER_MEM_BANK_REQ_BIT BIT(0) + +/* FLASH_CMD_CTRL */ +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_OPCODE_MASK GENMASK(31, 24) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_READ_DATA_BIT BIT(23) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_RD_DATA_BYTES_MASK GENMASK(22, 20) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_COMD_ADDR_BIT BIT(19) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_MODE_BIT_BIT BIT(18) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_ADDR_BYTES_MASK GENMASK(17, 16) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_WRITE_DATA_BIT BIT(15) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_WR_DATA_BYTES_MASK GENMASK(14, 12) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_DUMMY_CYCLES_MASK GENMASK(11, 7) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_STIG_MEM_BANK_EN_BIT BIT(2) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_EXEC_STATUS_BIT BIT(1) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_EXEC_BIT BIT(0) + +/* FLASH_CMD_ADDR */ +#define CADENCE_MSPI_FLASH_CMD_ADDR_REG_ADDR_MASK GENMASK(31, 0) + +/* FLASH_RD_DATA_LOWER */ +#define CADENCE_MSPI_FLASH_RD_DATA_LOWER_REG_DATA_MASK GENMASK(31, 0) + +/* FLASH_RD_DATA_UPPER */ +#define CADENCE_MSPI_FLASH_RD_DATA_UPPER_REG_DATA_MASK GENMASK(31, 0) + +/* FLASH_WR_DATA_LOWER */ +#define CADENCE_MSPI_FLASH_WR_DATA_LOWER_REG_DATA_MASK GENMASK(31, 0) + +/* FLASH_WR_DATA_UPPER */ +#define CADENCE_MSPI_FLASH_WR_DATA_UPPER_REG_DATA_MASK GENMASK(31, 0) + +/* POLLING_FLASH_STATUS */ +#define CADENCE_MSPI_POLLING_FLASH_STATUS_REG_DEVICE_STATUS_NB_DUMMY_MASK GENMASK(19, 16) +#define CADENCE_MSPI_POLLING_FLASH_STATUS_REG_DEVICE_STATUS_VALID_BIT BIT(8) +#define CADENCE_MSPI_POLLING_FLASH_STATUS_REG_DEVICE_STATUS_MASK GENMASK(7, 0) + +/* PHY_CONFIGURATION */ +#define CADENCE_MSPI_PHY_REG_CONFIGURATION_RESYNC_BIT BIT(31) +#define CADENCE_MSPI_PHY_REG_CONFIGURATION_RESET_BIT BIT(30) +#define CADENCE_MSPI_PHY_REG_CONFIGURATION_RX_DLL_BYPASS_BIT BIT(29) +#define CADENCE_MSPI_PHY_REG_CONFIGURATION_TX_DLL_DELAY_MASK GENMASK(22, 16) +#define CADENCE_MSPI_PHY_REG_CONFIGURATION_RX_DLL_DELAY_MASK GENMASK(6, 0) + +/* PHY_MASTER_CONTROL */ +#define CADENCE_MSPI_PHY_MASTER_CONTROL_REG_LOCK_MODE_BIT BIT(24) +#define CADENCE_MSPI_PHY_MASTER_CONTROL_REG_BYPASS_MODE_BIT BIT(23) +#define CADENCE_MSPI_PHY_MASTER_CONTROL_REG_PHASE_DETECT_SELECTOR_MASK GENMASK(22, 20) +#define CADENCE_MSPI_PHY_MASTER_CONTROL_REG_NB_INDICATIONS_MASK GENMASK(18, 16) +#define CADENCE_MSPI_PHY_MASTER_CONTROL_REG_INITIAL_DELAY_MASK GENMASK(6, 0) + +/* DLL_OBSERVABLE_LOWER */ +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_DLL_LOCK_INC_MASK GENMASK(31, 24) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_DLL_LOCK_DEC_MASK GENMASK(23, 16) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_LOOPBACK_LOCK_BIT BIT(15) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_LOCK_VALUE_MASK GENMASK(14, 8) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_UNLOCK_COUNTER_MASK GENMASK(7, 3) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_LOCK_MODE_MASK GENMASK(2, 1) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_DLL_LOCK_BIT BIT(0) + +/* DLL_OBSERVABLE_UPPER */ +#define CADENCE_MSPI_DLL_OBSERVABLE_UPPER_REG_TX_DECODER_OUTPUT_MASK GENMASK(22, 16) +#define CADENCE_MSPI_DLL_OBSERVABLE_UPPER_REG_RX_DECODER_OUTPUT_MASK GENMASK(6, 0) + +/* OPCODE_EXT_LOWER */ +#define CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_READ_OPCODE_MASK GENMASK(31, 24) +#define CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_WRITE_OPCODE_MASK GENMASK(23, 16) +#define CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_POLL_OPCODE_MASK GENMASK(15, 8) +#define CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_STIG_OPCODE_MASK GENMASK(7, 0) + +/* OPCODE_EXT_UPPER */ +#define CADENCE_MSPI_OPCODE_EXT_UPPER_REG_WEL_OPCODE_MASK GENMASK(31, 24) +#define CADENCE_MSPI_OPCODE_EXT_UPPER_REG_EXT_WEL_OPCODE_MASK GENMASK(23, 16) + +#endif /* ZEPHYR_DRIVERS_MSPI_CDNS_H_ */ diff --git a/dts/bindings/mspi/cdns,mspi-controller.yaml b/dts/bindings/mspi/cdns,mspi-controller.yaml new file mode 100644 index 000000000000..74123a15412a --- /dev/null +++ b/dts/bindings/mspi/cdns,mspi-controller.yaml @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 Siemens Mobility GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: Cadence MSPI Controller + +compatible: "cdns,mspi-controller" + +include: [mspi-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + description: | + Address and length of the MSPI configuration register and the indirect + write FIFO location + + clock-frequency: + required: true + + read-buffer-size: + required: true + type: int + description: | + Amount of address bits of the internal peripheral SRAM bus that are used + for indirects reads. The other address bits are used for indirect write + operations. The amount of available bits is dependent on the SRAM depth + configuration used inside the peripheral and the resulting amount of bytes + depends on the bus width, which may be specified in the SoC datasheet. + + It is recommended to look at the SoC datasheet/TRM and set this to half of + the maximum of the "SRAM Partition Configuration Register" value (which is + also its reset value), leading to half of the locations being used for + indirect reads and the other half for indirect writes From 90c15c96fa7c51e9bca227cbd104c1aaff92d20c Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Fri, 21 Nov 2025 18:46:17 +0530 Subject: [PATCH 087/143] UPSTREAM-PEND: mspi: cadence: allow DTR configuration Cadence OSPI/QSPI controller allows peripherals on its bus to have configurations like 8D-8D-8D or 4S-4D-4D which require DTR configuration for dual transfer rate. Hence, allow having DTR configuration in the driver. PR: 102513 Signed-off-by: Amneesh Singh --- drivers/mspi/mspi_cadence.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/mspi/mspi_cadence.c b/drivers/mspi/mspi_cadence.c index bc738738ce77..e27cd72b4ffc 100644 --- a/drivers/mspi/mspi_cadence.c +++ b/drivers/mspi/mspi_cadence.c @@ -870,14 +870,6 @@ static int mspi_cadence_dev_config(const struct device *controller, } } - if (param_mask & MSPI_DEVICE_CONFIG_DATA_RATE) { - if (dev_cfg->data_rate != MSPI_DATA_RATE_SINGLE) { - LOG_ERR("Only single data rate is supported for now"); - ret = -ENOSYS; - goto exit; - } - } - /* Disable MSPI during configuration */ uint32_t config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); @@ -890,6 +882,32 @@ static int mspi_cadence_dev_config(const struct device *controller, goto exit; } + if (param_mask & MSPI_DEVICE_CONFIG_DATA_RATE) { + uint32_t rd_config = + sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + switch (dev_cfg->data_rate) { + case MSPI_DATA_RATE_SINGLE: + /* disable DTR protocol */ + rd_config &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DDR_EN_BIT; + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENABLE_DTR_PROTOCOL_BIT; + break; + case MSPI_DATA_RATE_DUAL: + /* enable DTR protocol */ + config_reg |= CADENCE_MSPI_CONFIG_REG_ENABLE_DTR_PROTOCOL_BIT; + break; + case MSPI_DATA_RATE_S_D_D: + /* enable DDR */ + rd_config |= CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DDR_EN_BIT; + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENABLE_DTR_PROTOCOL_BIT; + break; + default: + LOG_ERR("Configured data rate is not supported"); + return -ENOSYS; + } + sys_write32(rd_config, base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + } + if (param_mask & MSPI_DEVICE_CONFIG_CE_NUM) { uint32_t num; From ef767274af024d299c2820d05a7a88f55b6d81b0 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Fri, 21 Nov 2025 18:41:33 +0530 Subject: [PATCH 088/143] UPSTREAM-PEND: mspi: cadence: allow dual byte opcodes Cadence OSPI controller allows dual byte opcodes which are used by the flash on the bus for modes such as Octal SDR/DDR. Hence, allow configuration for dual byte opcodes. PR: 102513 Signed-off-by: Amneesh Singh --- drivers/mspi/mspi_cadence.c | 88 +++++++++++++++++++++++++++++++------ drivers/mspi/mspi_cadence.h | 7 +-- 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/drivers/mspi/mspi_cadence.c b/drivers/mspi/mspi_cadence.c index e27cd72b4ffc..f8fa153e3690 100644 --- a/drivers/mspi/mspi_cadence.c +++ b/drivers/mspi/mspi_cadence.c @@ -63,6 +63,12 @@ static int mspi_cadence_wait_for_idle(const struct device *controller) return 0; } +static ALWAYS_INLINE bool mspi_cadence_is_dual_byte_cmd(uint32_t base_addr) +{ + return !!(sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET) & + CADENCE_MSPI_CONFIG_REG_DUAL_BYTE_OPCODE_EN_BIT); +} + /** * Check whether a single request package is requesting something that the driver * doesn't implement / the hardware doesn't support @@ -83,10 +89,6 @@ static int mspi_cadence_check_transfer_package(const struct mspi_xfer *request, LOG_ERR("Commands over 2 byte long aren't supported"); return -ENOTSUP; } - if (packet->cmd >> 8) { - LOG_ERR("Support for dual byte opcodes hasn't been implemented"); - return -ENOSYS; - } if (packet->num_bytes) { __ASSERT(packet->data_buf != NULL, "Request gave a NULL buffer when bytes should be transferred"); @@ -104,13 +106,10 @@ static int mspi_cadence_check_transfer_request(const struct mspi_xfer *request) return -ENOSYS; } - if (request->cmd_length == 2) { - LOG_ERR("Dual byte opcode is not implemented"); - return -ENOSYS; - } else if (request->cmd_length > 2) { + if (request->cmd_length > 2) { LOG_ERR("Cmds over 2 bytes long aren't supported"); return -ENOTSUP; - } else if (request->cmd_length != 1) { + } else if (request->cmd_length == 0) { LOG_ERR("Can't handle transfer without cmd"); return -ENOSYS; } @@ -369,7 +368,22 @@ static int mspi_cadence_stig(const struct device *controller, const struct mspi_ dummy_cycles = req->tx_dummy; } - flash_cmd_ctrl |= FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_OPCODE_MASK, packet->cmd); + if (mspi_cadence_is_dual_byte_cmd(base_address)) { + uint32_t opcode_ext_lower = + sys_read32(base_address + CADENCE_MSPI_OPCODE_EXT_LOWER_OFFSET); + + flash_cmd_ctrl |= FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_OPCODE_MASK, + packet->cmd >> 8); + opcode_ext_lower &= ~CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_STIG_OPCODE_MASK; + opcode_ext_lower |= FIELD_PREP( + CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_STIG_OPCODE_MASK, packet->cmd & 0xFF); + + sys_write32(opcode_ext_lower, base_address + CADENCE_MSPI_OPCODE_EXT_LOWER_OFFSET); + } else { + flash_cmd_ctrl |= + FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_OPCODE_MASK, packet->cmd); + } + flash_cmd_ctrl |= FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_DUMMY_CYCLES_MASK, dummy_cycles); @@ -428,8 +442,23 @@ static int mspi_cadence_indirect_read(const struct device *controller, const str sys_read32(base_address + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); dev_instr_rd_cfg &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_RD_OPCODE_NON_XIP_MASK; - dev_instr_rd_cfg |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_RD_OPCODE_NON_XIP_MASK, - packet->cmd); + if (mspi_cadence_is_dual_byte_cmd(base_address)) { + uint32_t opcode_ext_lower = + sys_read32(base_address + CADENCE_MSPI_OPCODE_EXT_LOWER_OFFSET); + + dev_instr_rd_cfg |= + FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_RD_OPCODE_NON_XIP_MASK, + packet->cmd >> 8); + opcode_ext_lower &= ~CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_READ_OPCODE_MASK; + opcode_ext_lower |= FIELD_PREP( + CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_READ_OPCODE_MASK, packet->cmd & 0xFF); + + sys_write32(opcode_ext_lower, base_address + CADENCE_MSPI_OPCODE_EXT_LOWER_OFFSET); + } else { + dev_instr_rd_cfg |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_RD_OPCODE_NON_XIP_MASK, packet->cmd); + } + dev_instr_rd_cfg &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MASK; dev_instr_rd_cfg |= FIELD_PREP( CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MASK, req->rx_dummy); @@ -518,8 +547,24 @@ static int mspi_cadence_indirect_write(const struct device *controller, const st sys_read32(base_address + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); dev_instr_wr_cfg &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WR_OPCODE_NON_XIP_MASK; - dev_instr_wr_cfg |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WR_OPCODE_NON_XIP_MASK, - packet->cmd); + if (mspi_cadence_is_dual_byte_cmd(base_address)) { + uint32_t opcode_ext_lower = + sys_read32(base_address + CADENCE_MSPI_OPCODE_EXT_LOWER_OFFSET); + + dev_instr_wr_cfg |= + FIELD_PREP(CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WR_OPCODE_NON_XIP_MASK, + packet->cmd >> 8); + opcode_ext_lower &= ~CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_WRITE_OPCODE_MASK; + opcode_ext_lower |= + FIELD_PREP(CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_WRITE_OPCODE_MASK, + packet->cmd & 0xFF); + + sys_write32(opcode_ext_lower, base_address + CADENCE_MSPI_OPCODE_EXT_LOWER_OFFSET); + } else { + dev_instr_wr_cfg |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WR_OPCODE_NON_XIP_MASK, packet->cmd); + } + dev_instr_wr_cfg &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MASK; dev_instr_wr_cfg |= FIELD_PREP( CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MASK, req->tx_dummy); @@ -964,6 +1009,21 @@ static int mspi_cadence_dev_config(const struct device *controller, } } + if (param_mask & MSPI_DEVICE_CONFIG_CMD_LEN) { + switch (dev_cfg->cmd_length) { + case 1: + config_reg &= ~CADENCE_MSPI_CONFIG_REG_DUAL_BYTE_OPCODE_EN_BIT; + break; + case 2: + config_reg |= CADENCE_MSPI_CONFIG_REG_DUAL_BYTE_OPCODE_EN_BIT; + break; + default: + LOG_ERR("Invalid command length: %u", dev_cfg->cmd_length); + ret = -ENOTSUP; + goto exit; + } + } + if (param_mask & MSPI_DEVICE_CONFIG_FREQUENCY) { config_reg &= ~CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK; diff --git a/drivers/mspi/mspi_cadence.h b/drivers/mspi/mspi_cadence.h index ebba39a18e17..0735c3fa8419 100644 --- a/drivers/mspi/mspi_cadence.h +++ b/drivers/mspi/mspi_cadence.h @@ -18,9 +18,10 @@ */ #define CADENCE_MSPI_IMPLEMENTED_DEV_CONFIG_PARAMS \ (MSPI_DEVICE_CONFIG_BREAK_TIME | MSPI_DEVICE_CONFIG_MEM_BOUND | \ - MSPI_DEVICE_CONFIG_ENDIAN | MSPI_DEVICE_CONFIG_CE_POL | MSPI_DEVICE_CONFIG_DQS | \ - MSPI_DEVICE_CONFIG_DATA_RATE | MSPI_DEVICE_CONFIG_CE_NUM | MSPI_DEVICE_CONFIG_IO_MODE | \ - MSPI_DEVICE_CONFIG_CPP | MSPI_DEVICE_CONFIG_FREQUENCY | MSPI_DEVICE_CONFIG_CE_POL) + MSPI_DEVICE_CONFIG_ENDIAN | MSPI_DEVICE_CONFIG_CE_POL | MSPI_DEVICE_CONFIG_CMD_LEN | \ + MSPI_DEVICE_CONFIG_DQS | MSPI_DEVICE_CONFIG_DATA_RATE | MSPI_DEVICE_CONFIG_CE_NUM | \ + MSPI_DEVICE_CONFIG_IO_MODE | MSPI_DEVICE_CONFIG_CPP | MSPI_DEVICE_CONFIG_FREQUENCY | \ + MSPI_DEVICE_CONFIG_CE_POL) /* * Non-implemented dev_cfg_bits From bfb5ab2a2421a9e9c0f3792968a1861c8b610438 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Fri, 21 Nov 2025 18:55:48 +0530 Subject: [PATCH 089/143] UPSTREAM-PEND: mspi: cadence: check for address length We configure address length as 'length - 1' in the controller. This currently does not check if the length is 0, in which case it causes unsigned integer underflow, so add a check for that. PR: 102513 Signed-off-by: Amneesh Singh --- drivers/mspi/mspi_cadence.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/mspi/mspi_cadence.c b/drivers/mspi/mspi_cadence.c index f8fa153e3690..0fd8f2e2ced4 100644 --- a/drivers/mspi/mspi_cadence.c +++ b/drivers/mspi/mspi_cadence.c @@ -465,15 +465,18 @@ static int mspi_cadence_indirect_read(const struct device *controller, const str sys_write32(dev_instr_rd_cfg, base_address + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); - sys_write32(packet->address, base_address + CADENCE_MSPI_INDIRECT_READ_XFER_START_OFFSET); sys_write32(packet->num_bytes, base_address + CADENCE_MSPI_INDIRECT_READ_XFER_NUM_BYTES_OFFSET); uint32_t dev_size_config = sys_read32(base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); dev_size_config &= ~CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK; - dev_size_config |= FIELD_PREP(CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK, - req->addr_length - 1); + if (req->addr_length) { + dev_size_config |= FIELD_PREP(CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK, + req->addr_length - 1); + sys_write32(packet->address, + base_address + CADENCE_MSPI_INDIRECT_READ_XFER_START_OFFSET); + } sys_write32(dev_size_config, base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); @@ -571,16 +574,18 @@ static int mspi_cadence_indirect_write(const struct device *controller, const st sys_write32(dev_instr_wr_cfg, base_address + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); - sys_write32(packet->address, base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_START_OFFSET); sys_write32(packet->num_bytes, base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_NUM_BYTES_OFFSET); uint32_t dev_size_config = sys_read32(base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); dev_size_config &= ~CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK; - dev_size_config |= FIELD_PREP(CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK, - req->addr_length - 1); - + if (req->addr_length) { + dev_size_config |= FIELD_PREP(CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK, + req->addr_length - 1); + sys_write32(packet->address, + base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_START_OFFSET); + } sys_write32(dev_size_config, base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); uint32_t indirect_write_ctrl = From 0a41ea7454d5603fcc3e0c0ccddcdab419ef694d Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 19 Feb 2026 16:30:02 +0530 Subject: [PATCH 090/143] UPSTREAM-PEND: mspi: cadence: change CS timing configuration Add a mechanism to configure the CS timing based on the configured clock frequency for the peripheral (SCLK) in reference clock cycles for the controller (RCLK). PR: 102513 Signed-off-by: Amneesh Singh --- drivers/mspi/mspi_cadence.c | 46 +++++++++++++++++++++ dts/bindings/mspi/cdns,mspi-controller.yaml | 28 +++++++++++++ 2 files changed, 74 insertions(+) diff --git a/drivers/mspi/mspi_cadence.c b/drivers/mspi/mspi_cadence.c index 0fd8f2e2ced4..10cab9249cb0 100644 --- a/drivers/mspi/mspi_cadence.c +++ b/drivers/mspi/mspi_cadence.c @@ -31,6 +31,10 @@ struct mspi_cadence_config { const uint32_t fifo_addr; const uint32_t sram_allocated_for_read; const uint32_t reference_frequency; + const uint32_t nss_delay_ns; + const uint32_t btwn_delay_ns; + const uint32_t after_delay_ns; + const uint32_t init_delay_ns; }; struct mspi_cadence_data { @@ -151,6 +155,38 @@ static int mspi_cadence_check_transfer_request(const struct mspi_xfer *request) return 0; } +static uint32_t mspi_cadence_ns_to_ticks(const uint32_t ref_clk_hz, const uint32_t ns) +{ + return DIV_ROUND_UP((ref_clk_hz / 1000) * ns, 1000000); +} + +static void mspi_cadence_configure_delays(const struct device *controller, const uint32_t tsclk) +{ + const struct mspi_cadence_config *config = controller->config; + const mem_addr_t base_addr = DEVICE_MMIO_GET(controller); + + uint32_t nss_delay = + mspi_cadence_ns_to_ticks(config->reference_frequency, config->nss_delay_ns); + uint32_t btwn_delay = + mspi_cadence_ns_to_ticks(config->reference_frequency, config->btwn_delay_ns); + uint32_t after_delay = + mspi_cadence_ns_to_ticks(config->reference_frequency, config->after_delay_ns); + uint32_t init_delay = + mspi_cadence_ns_to_ticks(config->reference_frequency, config->init_delay_ns); + + /* tsclk is the number of REFCLK ticks per SCLK tick. + * nss delay must be at least one SCLK tick. + */ + nss_delay = MAX(nss_delay, tsclk); + + uint32_t dev_delay = FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_NSS_MASK, nss_delay) | + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_BTWN_MASK, btwn_delay) | + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_AFTER_MASK, after_delay) | + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_INIT_MASK, init_delay); + + sys_write32(dev_delay, base_addr + CADENCE_MSPI_DEV_DELAY_OFFSET); +} + static int mspi_cadence_init(const struct device *dev) { DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); @@ -204,6 +240,9 @@ static int mspi_cadence_init(const struct device *dev) config_reg &= ~CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK; config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK, 15); + /* configure chipselect delays with 32 as tsclk */ + mspi_cadence_configure_delays(dev, 32); + /* Disable dual byte opcodes */ config_reg &= ~CADENCE_MSPI_CONFIG_REG_DUAL_BYTE_OPCODE_EN_BIT; @@ -1048,6 +1087,9 @@ static int mspi_cadence_dev_config(const struct device *controller, goto exit; } config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK, actual_div); + + /* configure delays */ + mspi_cadence_configure_delays(controller, required_div); } sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); @@ -1117,6 +1159,10 @@ static DEVICE_API(mspi, mspi_cadence_driver_api) = { .fifo_addr = DT_REG_ADDR_BY_IDX(DT_DRV_INST(n), 1), \ .sram_allocated_for_read = DT_PROP(DT_DRV_INST(n), read_buffer_size), \ .reference_frequency = DT_PROP(DT_DRV_INST(n), clock_frequency), \ + .nss_delay_ns = DT_INST_PROP(n, cdns_nss_delay_ns), \ + .btwn_delay_ns = DT_INST_PROP(n, cdns_btwn_delay_ns), \ + .after_delay_ns = DT_INST_PROP(n, cdns_after_delay_ns), \ + .init_delay_ns = DT_INST_PROP(n, cdns_init_delay_ns), \ }; \ static struct mspi_cadence_data mspi_cadence_data_##n = {}; \ DEVICE_DT_INST_DEFINE(n, mspi_cadence_init, NULL, &mspi_cadence_data_##n, \ diff --git a/dts/bindings/mspi/cdns,mspi-controller.yaml b/dts/bindings/mspi/cdns,mspi-controller.yaml index 74123a15412a..a0647ca55d5d 100644 --- a/dts/bindings/mspi/cdns,mspi-controller.yaml +++ b/dts/bindings/mspi/cdns,mspi-controller.yaml @@ -33,3 +33,31 @@ properties: the maximum of the "SRAM Partition Configuration Register" value (which is also its reset value), leading to half of the locations being used for indirect reads and the other half for indirect writes + + cdns,nss-delay-ns: + type: int + required: true + description: | + Amount of nanoseconds between the CS pin is deasserted between + transactions after startup. + + cdns,btwn-delay-ns: + type: int + required: true + description: | + Amount of nanoseconds no peripheral is selected during switching device + after startup. + + cdns,after-delay-ns: + type: int + required: true + description: | + Amount of nanoseconds the CS pin is continued being held after the last + bit was transferred. + + cdns,init-delay-ns: + type: int + required: true + description: | + Amount of nanoseconds between setting n_ss_out low and sending the first + bit of the transmission. From 27a366a79b965b7293644ab5a0c0397b38f09a66 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 15 Jan 2026 18:55:08 +0530 Subject: [PATCH 091/143] UPSTREAM-PEND: mspi: cadence: allow configuring read delay Add the read delay field in the timing configuration to allow configuring delay for read data capture. This is supposed to be set either by the peripheral using this controller at runtime or the user. Additionally, also make the related struct and parameters public by separating out the header. PR: 102513 Signed-off-by: Amneesh Singh --- drivers/mspi/mspi_cadence.c | 52 +++++++++++++++++++++- drivers/mspi/mspi_cadence.h | 1 + include/zephyr/drivers/mspi/mspi_cadence.h | 45 +++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 include/zephyr/drivers/mspi/mspi_cadence.h diff --git a/drivers/mspi/mspi_cadence.c b/drivers/mspi/mspi_cadence.c index 10cab9249cb0..263434a6add6 100644 --- a/drivers/mspi/mspi_cadence.c +++ b/drivers/mspi/mspi_cadence.c @@ -315,10 +315,14 @@ static int mspi_cadence_init(const struct device *dev) sys_write32(indirect_trigger_addr_range, base_addr + CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_OFFSET); - /* Disable loop-back via DQS */ uint32_t rd_data_capture = sys_read32(base_addr + CADENCE_MSPI_RD_DATA_CAPTURE_OFFSET); + /* Disable loop-back via DQS */ rd_data_capture |= CADENCE_MSPI_RD_DATA_CAPTURE_REG_BYPASS_BIT; + /* Set initial read delay to 0 */ + rd_data_capture &= ~CADENCE_MSPI_RD_DATA_CAPTURE_REG_DELAY_MASK; + /* Data outputs from flash memory are sampled on falling edge of the reference clock */ + rd_data_capture &= ~CADENCE_MSPI_RD_DATA_CAPTURE_REG_SAMPLE_EDGE_SEL_BIT; sys_write32(rd_data_capture, base_addr + CADENCE_MSPI_RD_DATA_CAPTURE_OFFSET); @@ -1130,12 +1134,58 @@ static int mspi_cadence_get_channel_status(const struct device *controller, uint return 0; } +#ifdef CONFIG_MSPI_TIMING +static int mspi_cadence_timing_config(const struct device *controller, + const struct mspi_dev_id *dev_id, const uint32_t param_mask, + void *timing_cfg) +{ + ARG_UNUSED(dev_id); + + const mem_addr_t base_addr = DEVICE_MMIO_GET(controller); + struct mspi_cadence_data *data = controller->data; + struct mspi_cadence_timing_cfg *timing = timing_cfg; + int ret = 0; + + /* Ensure no transfers in the meantime */ + ret = k_sem_take(&data->transfer_lock, K_MSEC(CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE)); + if (ret < 0) { + LOG_ERR("Error waiting for MSPI controller lock for changing timing"); + return ret; + } + + if (dev_id != data->current_peripheral) { + LOG_ERR("Tried chaning timing for another peripheral than the one the access lock " + "is held for"); + ret = -EINVAL; + goto exit; + } + + if (param_mask & MSPI_CADENCE_TIMING_PARAM_RD_DELAY) { + uint32_t rd_data_capture = + sys_read32(base_addr + CADENCE_MSPI_RD_DATA_CAPTURE_OFFSET); + rd_data_capture &= ~CADENCE_MSPI_RD_DATA_CAPTURE_REG_DELAY_MASK; + rd_data_capture |= + FIELD_PREP(CADENCE_MSPI_RD_DATA_CAPTURE_REG_DELAY_MASK, timing->rd_delay); + sys_write32(rd_data_capture, base_addr + CADENCE_MSPI_RD_DATA_CAPTURE_OFFSET); + } + +exit: + k_sem_give(&data->transfer_lock); + + return ret; +} +#endif /* CONFIG_MSPI_TIMING */ + static DEVICE_API(mspi, mspi_cadence_driver_api) = { .config = NULL, .dev_config = mspi_cadence_dev_config, .xip_config = NULL, .scramble_config = NULL, +#ifdef CONFIG_MSPI_TIMING + .timing_config = mspi_cadence_timing_config, +#else .timing_config = NULL, +#endif .get_channel_status = mspi_cadence_get_channel_status, .register_callback = NULL, .transceive = mspi_cadence_transceive, diff --git a/drivers/mspi/mspi_cadence.h b/drivers/mspi/mspi_cadence.h index 0735c3fa8419..4f4ae2e784a3 100644 --- a/drivers/mspi/mspi_cadence.h +++ b/drivers/mspi/mspi_cadence.h @@ -10,6 +10,7 @@ #include #include +#include /* * Implemented dev_cfg_bits. Implemented means that the value is at least diff --git a/include/zephyr/drivers/mspi/mspi_cadence.h b/include/zephyr/drivers/mspi/mspi_cadence.h new file mode 100644 index 000000000000..410b4f8fb977 --- /dev/null +++ b/include/zephyr/drivers/mspi/mspi_cadence.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2026 Texas Instruments + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_MSPI_CADENCE_H_ +#define ZEPHYR_INCLUDE_DRIVERS_MSPI_CADENCE_H_ + +/** + * @file mspi_cadence.h + * @brief Cadence MSPI controller specific definitions + * @ingroup mspi_cadence_interface + * + */ + +#include +#include + +/** + * @brief Configuration interfaces for Cadence MSPI controller + * @defgroup mspi_cadence_interface Cadence MSPI interface + * @ingroup mspi_configure_api + * @{ + */ + +/** + * @brief Timing configuration for the TI K3 MSPI peripheral. + */ +struct mspi_cadence_timing_cfg { + /** Amount of read data capture cycles that are applied to internal data capture circuit */ + uint8_t rd_delay; +}; + +/** + * @brief Enum which timing parameters should be modified + */ +enum mspi_cadence_timing_param { + /** Read delay timing parameter */ + MSPI_CADENCE_TIMING_PARAM_RD_DELAY = BIT(0) +}; + +/** @} */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MSPI_CADENCE_H_ */ From ee3a4edf741e8815c252657c197b2e67f82cc48e Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 15 Jan 2026 19:00:05 +0530 Subject: [PATCH 092/143] UPSTREAM-PEND: mspi: cadence: reset the STIG register during init Reset the STIG register configuration in case it was set before initializing the driver. PR: 102513 Signed-off-by: Amneesh Singh --- drivers/mspi/mspi_cadence.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mspi/mspi_cadence.c b/drivers/mspi/mspi_cadence.c index 263434a6add6..9c986f4c81e2 100644 --- a/drivers/mspi/mspi_cadence.c +++ b/drivers/mspi/mspi_cadence.c @@ -366,6 +366,9 @@ static int mspi_cadence_init(const struct device *dev) config_reg |= CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT; sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + /* Reset the STIG register for first time use */ + sys_write32(0, base_addr + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET); + return 0; } From ddc34a65c8729573e9e8662637ebcdb3c3dc73e6 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Wed, 25 Mar 2026 14:48:21 +0530 Subject: [PATCH 093/143] UPSTREAM-PEND: mspi: cadence: MMIO_MAP the FIFO for indirect access Cadence MSPI controller uses a FIFO for indirect access operations on the device on bus. Right now we directly use the physical address provided via DT. This does not work on cores with MMU like A53; hence, map the FIFO address the same way base contrller register space is mapped. PR: 102513 Signed-off-by: Amneesh Singh --- drivers/mspi/mspi_cadence.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/mspi/mspi_cadence.c b/drivers/mspi/mspi_cadence.c index 9c986f4c81e2..117f0317fc40 100644 --- a/drivers/mspi/mspi_cadence.c +++ b/drivers/mspi/mspi_cadence.c @@ -1,6 +1,7 @@ /* * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors * SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 Siemens Mobility GmbH + * SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 Texas Instruments * * SPDX-License-Identifier: Apache-2.0 */ @@ -24,11 +25,14 @@ LOG_MODULE_REGISTER(mspi_cadence, CONFIG_MSPI_LOG_LEVEL); +#define DEV_CFG(dev) ((const struct mspi_cadence_config *)dev->config) +#define DEV_DATA(dev) ((struct mspi_cadence_data *)dev->data) + struct mspi_cadence_config { DEVICE_MMIO_ROM; + DEVICE_MMIO_NAMED_ROM(fifo); struct mspi_cfg mspi_config; const struct pinctrl_dev_config *pinctrl; - const uint32_t fifo_addr; const uint32_t sram_allocated_for_read; const uint32_t reference_frequency; const uint32_t nss_delay_ns; @@ -39,6 +43,7 @@ struct mspi_cadence_config { struct mspi_cadence_data { DEVICE_MMIO_RAM; + DEVICE_MMIO_NAMED_RAM(fifo); struct k_mutex access_lock; struct k_sem transfer_lock; const struct mspi_dev_id *current_peripheral; @@ -190,6 +195,7 @@ static void mspi_cadence_configure_delays(const struct device *controller, const static int mspi_cadence_init(const struct device *dev) { DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); + DEVICE_MMIO_NAMED_MAP(dev, fifo, K_MEM_CACHE_NONE); const struct mspi_cadence_config *config = dev->config; struct mspi_cadence_data *data = dev->data; const mem_addr_t base_addr = DEVICE_MMIO_GET(dev); @@ -481,7 +487,7 @@ static int mspi_cadence_indirect_read(const struct device *controller, const str uint32_t index, const uint64_t start_time) { const mem_addr_t base_address = DEVICE_MMIO_GET(controller); - const struct mspi_cadence_config *config = controller->config; + const mem_addr_t fifo_address = DEVICE_MMIO_NAMED_GET(controller, fifo); const struct mspi_xfer_packet *packet = &req->packets[index]; uint32_t dev_instr_rd_cfg = @@ -549,7 +555,7 @@ static int mspi_cadence_indirect_read(const struct device *controller, const str num_new_words = FIELD_GET(CADENCE_MSPI_SRAM_FILL_REG_INDAC_READ_MASK, indac_read); while (remaining_bytes > 0 && num_new_words > 0) { - current_new_word = sys_read32(config->fifo_addr); + current_new_word = sys_read32(fifo_address); bytes_to_copy_from_current_word = MIN(remaining_bytes, 4); memcpy(write_ptr, ¤t_new_word, bytes_to_copy_from_current_word); write_ptr += bytes_to_copy_from_current_word; @@ -589,6 +595,7 @@ static int mspi_cadence_indirect_write(const struct device *controller, const st uint32_t index, const uint64_t start_time) { const mem_addr_t base_address = DEVICE_MMIO_GET(controller); + const mem_addr_t fifo_address = DEVICE_MMIO_NAMED_GET(controller, fifo); const struct mspi_cadence_config *config = controller->config; const struct mspi_xfer_packet *packet = &req->packets[index]; @@ -660,7 +667,7 @@ static int mspi_cadence_indirect_write(const struct device *controller, const st current_word_to_write = 0; memcpy(¤t_word_to_write, &packet->data_buf[read_offset], MIN(remaining_bytes, 4)); - sys_write32(current_word_to_write, config->fifo_addr); + sys_write32(current_word_to_write, fifo_address); remaining_bytes = (remaining_bytes > 4 ? remaining_bytes - 4 : 0); read_offset += 4; --free_words; @@ -1207,9 +1214,9 @@ static DEVICE_API(mspi, mspi_cadence_driver_api) = { PINCTRL_DT_DEFINE(DT_DRV_INST(n)); \ static const struct mspi_cadence_config mspi_cadence_config_##n = { \ DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(fifo, DT_DRV_INST(n)), \ .pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ .mspi_config = MSPI_CONFIG(n), \ - .fifo_addr = DT_REG_ADDR_BY_IDX(DT_DRV_INST(n), 1), \ .sram_allocated_for_read = DT_PROP(DT_DRV_INST(n), read_buffer_size), \ .reference_frequency = DT_PROP(DT_DRV_INST(n), clock_frequency), \ .nss_delay_ns = DT_INST_PROP(n, cdns_nss_delay_ns), \ From ec78c60de1647c312c6798a130b179d9fb1d3f52 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 15 Jan 2026 19:11:19 +0530 Subject: [PATCH 094/143] UPSTREAM-PEND: drivers: flash: flash_mspi_nor: fix octal cmd read DDR Octal SPI configurations require reading an even number of bytes at a time. This includes commands such as RDSR (Status Register) and RDID (JEDEC ID). Currently this can cause timing issues, hence use an even number of bytes for the both aforementioned commands if the data rate is dual. PR: 102513 Signed-off-by: Amneesh Singh --- drivers/flash/flash_mspi_nor.c | 37 ++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index fa9cb0cfe206..2dcddc6a2809 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -44,6 +44,15 @@ static bool in_octal_io(const struct device *dev) dev_data->last_applied_cfg->io_mode == MSPI_IO_MODE_OCTAL; } + +static bool in_ddr(const struct device *dev) +{ + struct flash_mspi_nor_data *dev_data = dev->data; + + return dev_data->last_applied_cfg && + dev_data->last_applied_cfg->data_rate == MSPI_DATA_RATE_DUAL; +} + static bool is_quad_enable_needed(const struct mspi_dev_cfg *cfg) { return cfg && (cfg->io_mode == MSPI_IO_MODE_QUAD_1_1_4 || @@ -156,6 +165,7 @@ static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; + static uint8_t sr_even[2] = {0}; int rc; set_up_xfer(dev, MSPI_RX, dev_config->control_xfer_mode); @@ -163,15 +173,24 @@ static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr) dev_data->xfer.rx_dummy = dev_data->cmd_info.rdsr_dummy; dev_data->xfer.addr_length = dev_data->cmd_info.rdsr_addr_4 ? 4 : 0; + dev_data->packet.address = 0; } - dev_data->packet.num_bytes = sizeof(uint8_t); - dev_data->packet.data_buf = sr; + + if (in_ddr(dev)) { + dev_data->packet.num_bytes = sizeof(uint8_t) * 2; + } else { + dev_data->packet.num_bytes = sizeof(uint8_t); + } + + dev_data->packet.data_buf = sr_even; rc = perform_xfer(dev, op_code); if (rc < 0) { LOG_ERR("%s 0x%02x failed: %d", __func__, op_code, rc); return rc; } + *sr = sr_even[0]; + return 0; } @@ -592,6 +611,7 @@ static int read_jedec_id(const struct device *dev, uint8_t *id) { const struct flash_mspi_nor_config *dev_config = dev->config; struct flash_mspi_nor_data *dev_data = dev->data; + static uint8_t id_even[JESD216_READ_ID_LEN + 1] = {0}; int rc; set_up_xfer(dev, MSPI_RX, dev_config->control_xfer_mode); @@ -599,14 +619,23 @@ static int read_jedec_id(const struct device *dev, uint8_t *id) dev_data->xfer.rx_dummy = dev_data->cmd_info.rdid_dummy; dev_data->xfer.addr_length = dev_data->cmd_info.rdid_addr_4 ? 4 : 0; + dev_data->packet.address = 0; } - dev_data->packet.data_buf = id; - dev_data->packet.num_bytes = JESD216_READ_ID_LEN; + + if (in_ddr(dev)) { + dev_data->packet.num_bytes = JESD216_READ_ID_LEN + 1; + } else { + dev_data->packet.num_bytes = JESD216_READ_ID_LEN; + } + + dev_data->packet.data_buf = id_even; rc = perform_xfer(dev, SPI_NOR_CMD_RDID); if (rc < 0) { LOG_ERR("Read JEDEC ID failed: %d", rc); } + memcpy(id, id_even, JESD216_READ_ID_LEN); + return rc; } From 2e86fc9a4efc7efb9347c0f5c5b626249583ce86 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 15 Jan 2026 19:29:26 +0530 Subject: [PATCH 095/143] UPSTREAM-PEND: drivers: flash: mspi_nor: fix initial MSPI configuration 1. Remove endianness configuraton, since some MSPI controllers may return -ENOSYS if they are configured as big or small endian. 2. Set default command (opcode) length to 1 byte for 1S-1S-1S (SPI) mode. PR: 102513 Signed-off-by: Amneesh Singh --- drivers/flash/flash_mspi_nor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index 2dcddc6a2809..08dd03be1291 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -1346,8 +1346,8 @@ static DEVICE_API(flash, drv_api) = { .freq = FLASH_MSPI_MAX_FREQ(inst), \ .io_mode = MSPI_IO_MODE_SINGLE, \ .data_rate = MSPI_DATA_RATE_SINGLE, \ + .cmd_length = 1, \ .cpp = MSPI_CPP_MODE_0, \ - .endian = MSPI_XFER_BIG_ENDIAN, \ .ce_polarity = MSPI_CE_ACTIVE_LOW, \ .dqs_enable = false, \ } From 1fba61f5df9cb6fe1aa763f28ba15481fb6f364d Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 15 Jan 2026 20:05:57 +0530 Subject: [PATCH 096/143] UPSTREAM-PEND: drivers: flash: mspi_nor: add s28hx512t flash - Add support for Infineon Semper S28HX512T NOR flash. This supports SPI mode (1S-1S-1S), 8S-8S-8S and 8D-8D-8D. - Expose set_up_xfer_with_addr, read_jedec_id and soft_reset_66_99 function declarations to the vendor specific quirks. - Add more macros to the spi_nor_s28hx512t.h header that can be used in addition to the already present macros. - Add a specific quirk that is only used when the parent controller for this flash is Cadence MSPI (OSPI). This involves calibrating a delay for capturing data on the controller by reading JEDEC IDs on different configurations. - Add DT binding for the flash to allow convenient Kconfig symbols that depend on the presence of the compatible flash node in the DT. - Add Kconfig option FLASH_MSPI_INFINEON_S28HX512T_TI_EARLY_FIXUP_RESET for TI K3 SoCs. This is required as the ROM bootloader on the SoCs may leave the flash in 8D-8D-8D state depending on what bootmode is set. This requires resetting the board beforehand in this very specific configuration. - Move FLASH_MSPI_QUIRKS_GET to the bottom of file in flash_mspi_nor_quirks.h header. PR: 102513 Signed-off-by: Amneesh Singh --- drivers/flash/Kconfig.ifx_cat1 | 21 ++ drivers/flash/flash_mspi_nor.c | 8 + drivers/flash/flash_mspi_nor_quirks.h | 22 +- ...flash_mspi_nor_quirks_infineon_s28hx512t.h | 335 ++++++++++++++++++ drivers/flash/spi_nor_s28hx512t.h | 7 + dts/bindings/mtd/infineon,s28hx512t-mspi.yaml | 10 + 6 files changed, 395 insertions(+), 8 deletions(-) create mode 100644 drivers/flash/flash_mspi_nor_quirks_infineon_s28hx512t.h create mode 100644 dts/bindings/mtd/infineon,s28hx512t-mspi.yaml diff --git a/drivers/flash/Kconfig.ifx_cat1 b/drivers/flash/Kconfig.ifx_cat1 index 2b35c448a48b..6411967bc7fa 100644 --- a/drivers/flash/Kconfig.ifx_cat1 +++ b/drivers/flash/Kconfig.ifx_cat1 @@ -34,3 +34,24 @@ config MPU_ALLOW_FLASH_WRITE bool "Add MPU access to write to flash" help Enable this to allow MPU RWX access to flash memory. + +if FLASH_MSPI_NOR +config FLASH_MSPI_INFINEON_S28HX512T + bool "MSPI Infineon S28HX512T driver" + default y + depends on DT_HAS_INFINEON_S28HX512T_ENABLED + select FLASH_HAS_PAGE_LAYOUT + select FLASH_HAS_EXPLICIT_ERASE + +if FLASH_MSPI_INFINEON_S28HX512T +config FLASH_MSPI_INFINEON_S28HX512T_EARLY_FIXUP_RESET + bool "Perform initial software reset at 8D-8D-8D" + default n + help + Some bootloaders may put the flash in the highest possible configuration + before transferring control to Zephyr. One such case is the TI ROM + bootloader. This property replaces the original software reset sequence + such that 8D-8D-8D configuration is applied before doing the software + reset to go back to 1S-1S-1S. +endif #FLASH_MSPI_INFINEON_S28HX512T +endif #FLASH_MSPI_NOR diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c index 08dd03be1291..6f3655089419 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -27,12 +27,20 @@ LOG_MODULE_REGISTER(flash_mspi_nor, CONFIG_FLASH_LOG_LEVEL); static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir, enum mspi_xfer_mode xfer_mode); +static void set_up_xfer_with_addr(const struct device *dev, + enum mspi_xfer_direction dir, + uint32_t addr, + enum mspi_xfer_mode xfer_mode); static int perform_xfer(const struct device *dev, uint8_t cmd); static int cmd_rdsr(const struct device *dev, uint8_t op_code, uint8_t *sr); static int wait_until_ready(const struct device *dev, k_timeout_t poll_period); static int cmd_wren(const struct device *dev); static int cmd_wrsr(const struct device *dev, uint8_t op_code, uint8_t sr_cnt, uint8_t *sr); +static int read_jedec_id(const struct device *dev, uint8_t *id); +#if defined(WITH_SOFT_RESET) +static int soft_reset_66_99(const struct device *dev); +#endif /* WITH_SOFT_RESET */ #include "flash_mspi_nor_quirks.h" diff --git a/drivers/flash/flash_mspi_nor_quirks.h b/drivers/flash/flash_mspi_nor_quirks.h index 23325741b0fd..fcfc657a1643 100644 --- a/drivers/flash/flash_mspi_nor_quirks.h +++ b/drivers/flash/flash_mspi_nor_quirks.h @@ -20,14 +20,6 @@ struct flash_mspi_nor_quirks { int (*post_switch_mode)(const struct device *dev); }; -/* Extend this macro when adding new flash chip with quirks */ -#define FLASH_MSPI_QUIRKS_GET(node) \ - COND_CODE_1(DT_NODE_HAS_COMPAT_STATUS(node, mxicy_mx25r, okay), \ - (&flash_quirks_mxicy_mx25r), \ - (COND_CODE_1(DT_NODE_HAS_COMPAT_STATUS(node, mxicy_mx25u, okay), \ - (&flash_quirks_mxicy_mx25u), \ - (NULL)))) - #if DT_HAS_COMPAT_STATUS_OKAY(mxicy_mx25r) #define MXICY_MX25R_LH_MASK BIT(1) @@ -211,4 +203,18 @@ struct flash_mspi_nor_quirks flash_quirks_mxicy_mx25u = { #endif /* DT_HAS_COMPAT_STATUS_OKAY(mxicy_mx25u) */ +#ifdef CONFIG_FLASH_MSPI_INFINEON_S28HX512T +#include "flash_mspi_nor_quirks_infineon_s28hx512t.h" +#endif /* CONFIG_FLASH_MSPI_INFINEON_S28HX512T */ + +/* Extend this macro when adding new flash chip with quirks */ +#define FLASH_MSPI_QUIRKS_GET(node) \ + COND_CODE_1(DT_NODE_HAS_COMPAT_STATUS(node, mxicy_mx25r, okay), \ + (&flash_quirks_mxicy_mx25r), \ + (COND_CODE_1(DT_NODE_HAS_COMPAT_STATUS(node, mxicy_mx25u, okay), \ + (&flash_quirks_mxicy_mx25u), \ + (COND_CODE_1(DT_NODE_HAS_COMPAT_STATUS(node, infineon_s28hx512t, okay), \ + (FLASH_QUIRKS_INFINEON_S28HX512T(node)), \ + (NULL)))))) + #endif /*__FLASH_MSPI_NOR_QUIRKS_H__*/ diff --git a/drivers/flash/flash_mspi_nor_quirks_infineon_s28hx512t.h b/drivers/flash/flash_mspi_nor_quirks_infineon_s28hx512t.h new file mode 100644 index 000000000000..34d391cf9e0f --- /dev/null +++ b/drivers/flash/flash_mspi_nor_quirks_infineon_s28hx512t.h @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2026 Texas Instruments + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_FLASH_MSPI_NOR_QUIRKS_INFINEON_H_ +#define ZEPHYR_DRIVERS_FLASH_MSPI_NOR_QUIRKS_INFINEON_H_ + +#include +#include "flash_mspi_nor.h" +#include "spi_nor_s28hx512t.h" + +#define S28HX512T_OCMD_READ_DDR (0xEE) +#define S28HX512T_OCMD_READ_SDR (0xEC) + +static bool infineon_s28hx512t_is_register_volatile(uint32_t reg) +{ + return reg >= S28HX512T_SPI_NOR_STR1V_ADDR; +} + +static int infineon_s28hx512t_read_register(const struct device *dev, uint32_t reg, uint8_t *value) +{ + struct flash_mspi_nor_data *dev_data = dev->data; + + set_up_xfer_with_addr(dev, MSPI_RX, reg, MSPI_PIO); + + if (infineon_s28hx512t_is_register_volatile(reg)) { + dev_data->xfer.rx_dummy = dev_data->cmd_info.rdsr_dummy; + } else { + uint8_t read_dummy_cycles = dev_data->cmd_info.read_dummy_cycles; + + dev_data->xfer.rx_dummy = (read_dummy_cycles != 0) ? read_dummy_cycles : 8; + } + + dev_data->packet.num_bytes = 1; + dev_data->packet.data_buf = value; + + return perform_xfer(dev, S28HX512T_SPI_NOR_CMD_RREG); +} + +static int infineon_s28hx512t_write_register(const struct device *dev, uint32_t reg, uint8_t value) +{ + struct flash_mspi_nor_data *dev_data = dev->data; + int rv = 0; + + rv = cmd_wren(dev); + if (rv < 0) { + return rv; + } + + set_up_xfer_with_addr(dev, MSPI_TX, reg, MSPI_PIO); + dev_data->packet.data_buf = &value; + dev_data->packet.num_bytes = 1; + + return perform_xfer(dev, S28HX512T_SPI_NOR_CMD_WR_WRARG); +} + +static int infineon_s28hx512t_disable_hybrid_sector(const struct device *dev) +{ + uint8_t conf3 = 0; + int rc = 0; + + rc = infineon_s28hx512t_read_register(dev, S28HX512T_SPI_NOR_CFR3V_ADDR, &conf3); + if (rc < 0) { + LOG_ERR("Error reading volatile configuration register 3"); + return rc; + } + + if ((conf3 & BIT(3)) == 0) { + LOG_INF("Flash is in hybrid sector mode. Changing non-volatile config to correct " + "this"); + + conf3 |= BIT(3); + + rc = infineon_s28hx512t_write_register(dev, S28HX512T_SPI_NOR_CFR3N_ADDR, conf3); + if (rc < 0) { + LOG_ERR("Error changing non-volatile configuration of flash"); + return rc; + } + + rc = wait_until_ready(dev, K_MSEC(S28HX512T_SPI_NOR_NV_WRITE_MAX_MSEC)); + if (rc < 0) { + LOG_ERR("Error waiting for flash to enter idle after disabling hybrid " + "sector mode by writing non volatile register"); + return rc; + } + } + + return 0; +} + +static int infineon_s28hx512t_pre_init(const struct device *dev) +{ + int rc; + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + + /* default mode is 3 byte addressing mode in 1S-1S-1S */ + dev_data->cmd_info.uses_4byte_addr = false; + + rc = infineon_s28hx512t_disable_hybrid_sector(dev); + if (rc < 0) { + return rc; + } + + /* 256k sector erase type */ + memset(dev_data->erase_types, 0, sizeof(dev_data->erase_types)); + dev_data->erase_types[0] = (struct jesd216_erase_type){ + .cmd = 0xDC, + .exp = 18, + }; + + /* enter 4 byte addressing mode if configured to use 4 byte addressing */ + if (dev_config->mspi_nor_cfg.io_mode == MSPI_IO_MODE_SINGLE) { + if (dev_config->mspi_nor_cfg.addr_length == 4) { + dev_data->cmd_info.uses_4byte_addr = true; + } + + if (dev_data->cmd_info.uses_4byte_addr == true) { + dev_data->switch_info.enter_4byte_addr = ENTER_4BYTE_ADDR_B7; + } + } + + /* set cmd extension for 2 byte opcodes for octal mode */ + dev_data->cmd_info.cmd_extension = CMD_EXTENSION_SAME; + + return 0; +} + +static int infineon_s28hx512t_switch_octal(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + const struct mspi_dev_cfg *mspi_nor_cfg = &dev_config->mspi_nor_cfg; + struct flash_mspi_nor_data *dev_data = dev->data; + const uint8_t memlat[16] = {5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 23, 24, 25, 26, 27, 28}; + const uint8_t vrglat[4] = {3, 4, 5, 6}; + uint8_t read_dummy = 0; + uint8_t cmd_dummy = 0; + uint8_t cfg_reg; + int rc = 0; + + if (mspi_nor_cfg->cmd_length != 2) { + LOG_ERR("Octal mode requires 2 byte command length"); + return -EINVAL; + } + + rc = infineon_s28hx512t_read_register(dev, S28HX512T_SPI_NOR_CFR2V_ADDR, &cfg_reg); + if (rc != 0) { + return rc; + } + read_dummy = memlat[FIELD_GET(GENMASK(3, 0), cfg_reg)]; + + rc = infineon_s28hx512t_read_register(dev, S28HX512T_SPI_NOR_CFR3V_ADDR, &cfg_reg); + if (rc != 0) { + return rc; + } + cmd_dummy = vrglat[FIELD_GET(GENMASK(7, 6), cfg_reg)]; + + rc = infineon_s28hx512t_read_register(dev, S28HX512T_SPI_NOR_CFR5V_ADDR, &cfg_reg); + if (rc != 0) { + return rc; + } + + cfg_reg |= S28HX512T_SPI_NOR_CFR5X_OPI_IT; + if (mspi_nor_cfg->data_rate == MSPI_DATA_RATE_SINGLE) { + cfg_reg &= ~S28HX512T_SPI_NOR_CFR5X_SDRDDR; + dev_data->cmd_info.read_cmd = S28HX512T_OCMD_READ_SDR; + } else if (mspi_nor_cfg->data_rate == MSPI_DATA_RATE_DUAL) { + cfg_reg |= S28HX512T_SPI_NOR_CFR5X_SDRDDR; + dev_data->cmd_info.read_cmd = S28HX512T_OCMD_READ_DDR; + } else { + LOG_ERR("data rate not supported"); + return -ENOTSUP; + } + + rc = infineon_s28hx512t_write_register(dev, S28HX512T_SPI_NOR_CFR5V_ADDR, cfg_reg); + if (rc != 0) { + return rc; + } + + dev_data->cmd_info.pp_cmd = SPI_NOR_CMD_PP_4B; + dev_data->cmd_info.uses_4byte_addr = true; + dev_data->cmd_info.read_mode_bit_cycles = 0; + dev_data->cmd_info.read_dummy_cycles = read_dummy; + dev_data->cmd_info.rdid_dummy = cmd_dummy; + dev_data->cmd_info.rdid_addr_4 = true; + dev_data->cmd_info.rdsr_dummy = cmd_dummy; + dev_data->cmd_info.rdsr_addr_4 = true; + dev_data->cmd_info.sfdp_addr_4 = true; + + /* configure dual byte opcode on the controller explicitly*/ + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, MSPI_DEVICE_CONFIG_CMD_LEN, + mspi_nor_cfg); + if (rc < 0) { + LOG_ERR("failed to configure MSPI controller for command length"); + return rc; + } + + return 0; +} + +static int infineon_s28hx512t_post_switch(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + + switch (dev_config->mspi_nor_cfg.io_mode) { + case MSPI_IO_MODE_SINGLE: { + /* Opcodes 0x03 and 0x13 only read for speeds <= 50 MHz, and use 0 read dummy + * cycles. Opcodes 0x0B and 0x0C read for speeds > 50 MHz, and use 8 read dummy + * cycles by default. + */ + if (dev_config->mspi_nor_cfg.freq <= MHZ(50)) { + dev_data->cmd_info.read_cmd = dev_data->cmd_info.uses_4byte_addr + ? SPI_NOR_CMD_READ_4B + : SPI_NOR_CMD_READ; + dev_data->cmd_info.read_dummy_cycles = 0; + } + + if (dev_data->cmd_info.uses_4byte_addr == false) { + LOG_WRN("page programming is only supported for 4byte addressing mode"); + } + + return 0; + } + case MSPI_IO_MODE_OCTAL: { + return infineon_s28hx512t_switch_octal(dev); + } + default: { + return -EINVAL; + } + } + + return 0; +} + +#define FLASH_HAS_CADENCE_PARENT(node, ...) \ + IF_ENABLED(DT_NODE_HAS_COMPAT(DT_PARENT(node), cdns_mspi_controller), (1)) + +#define INFINEON_HAS_CADENCE_PARENT \ + UTIL_NOT(IS_EMPTY( \ + DT_FOREACH_STATUS_OKAY_VARGS(infineon_s28hx512t, FLASH_HAS_CADENCE_PARENT))) + +#if INFINEON_HAS_CADENCE_PARENT +#include + +static int infineon_s28hx512t_configure_rd_delay_cadence_mspi(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + uint8_t id[JESD216_READ_ID_LEN] = {0}; + struct mspi_cadence_timing_cfg timing; + uint8_t max = 0xff; + uint8_t min = 0x00; + int rc = 0; + + for (int8_t rd_delay = 0xf; rd_delay >= 0x0; rd_delay--) { + timing.rd_delay = rd_delay; + + rc = mspi_timing_config(dev_config->bus, &dev_config->mspi_id, + MSPI_CADENCE_TIMING_PARAM_RD_DELAY, &timing); + if (rc < 0) { + LOG_ERR("failed to set read delay"); + return rc; + } + + rc = read_jedec_id(dev, id); + if (rc < 0) { + LOG_ERR("failed to read JEDEC ID: %d", rc); + return rc; + } + + if (memcmp(id, dev_config->jedec_id, sizeof(id)) == 0) { + if (max == 0xff) { + max = timing.rd_delay; + } + min = timing.rd_delay; + } else if (max != 0xff) { + break; + } + } + + if (max == 0xff) { + LOG_ERR("could not find a suitable value to set as read delay"); + return -ENODEV; + } + + timing.rd_delay = (max + min) / 2; + LOG_INF("setting read delay as 0x%x", timing.rd_delay); + + rc = mspi_timing_config(dev_config->bus, &dev_config->mspi_id, + MSPI_CADENCE_TIMING_PARAM_RD_DELAY, &timing); + if (rc < 0) { + LOG_ERR("failed to set read delay"); + return rc; + } + + return 0; +} + +static int infineon_s28hx512t_post_switch_cadence_mspi(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + int rc = 0; + + rc = infineon_s28hx512t_post_switch(dev); + if (rc != 0) { + return rc; + } + + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, NON_XIP_DEV_CFG_MASK, + &dev_config->mspi_nor_cfg); + if (rc < 0) { + return rc; + } + + dev_data->last_applied_cfg = &dev_config->mspi_nor_cfg; + + return infineon_s28hx512t_configure_rd_delay_cadence_mspi(dev); +} +#endif /* INFINEON_HAS_CADENCE_PARENT */ + +#define FLASH_INFINEON_S28HX512T_INST(node) \ + struct flash_mspi_nor_quirks flash_quirks_infineon_s28hx512t_##node = { \ + .pre_init = infineon_s28hx512t_pre_init, \ + COND_CODE_1(FLASH_HAS_CADENCE_PARENT(node), \ + (.post_switch_mode = infineon_s28hx512t_post_switch_cadence_mspi,), \ + (.post_switch_mode = infineon_s28hx512t_post_switch,)) }; + +DT_FOREACH_STATUS_OKAY(infineon_s28hx512t, FLASH_INFINEON_S28HX512T_INST) + +#define FLASH_QUIRKS_INFINEON_S28HX512T(node) (&flash_quirks_infineon_s28hx512t_##node) + +#endif /* ZEPHYR_DRIVER_FLASH_MSPI_NOR_QUIRKS_INFINEON_H_ */ diff --git a/drivers/flash/spi_nor_s28hx512t.h b/drivers/flash/spi_nor_s28hx512t.h index 138fc824af40..de40d68f7690 100644 --- a/drivers/flash/spi_nor_s28hx512t.h +++ b/drivers/flash/spi_nor_s28hx512t.h @@ -38,10 +38,17 @@ #define S28HX512T_SPI_NOR_DUMMY_RD_SFDP 8U #define S28HX512T_SPI_NOR_DUMMY_RD_SFDP_OCTAL 8U +#define S28HX512T_SPI_NOR_CFR3N_ADDR 0x00000004 +#define S28HX512T_SPI_NOR_STR1V_ADDR 0x00800000 #define S28HX512T_SPI_NOR_CFR1V_ADDR 0x00800002 #define S28HX512T_SPI_NOR_CFR2V_ADDR 0x00800003 #define S28HX512T_SPI_NOR_CFR3V_ADDR 0x00800004 #define S28HX512T_SPI_NOR_CFR4V_ADDR 0x00800005 #define S28HX512T_SPI_NOR_CFR5V_ADDR 0x00800006 +#define S28HX512T_SPI_NOR_CFR5X_OPI_IT BIT(0) +#define S28HX512T_SPI_NOR_CFR5X_SDRDDR BIT(1) + +#define S28HX512T_SPI_NOR_NV_WRITE_MAX_MSEC 360U + #endif /*__SPI_NOR_S28HX512T_H__*/ diff --git a/dts/bindings/mtd/infineon,s28hx512t-mspi.yaml b/dts/bindings/mtd/infineon,s28hx512t-mspi.yaml new file mode 100644 index 000000000000..390334b85388 --- /dev/null +++ b/dts/bindings/mtd/infineon,s28hx512t-mspi.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Texas Instruments +# SPDX-License-Identifier: Apache-2.0 + +description: Infineon S28HX512T Flash + +compatible: "infineon,s28hx512t" + +on-bus: mspi + +include: ["jedec,mspi-nor.yaml"] From e256c2a2e3d45e752af8124d474108d68a93195e Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Tue, 24 Mar 2026 15:44:32 +0530 Subject: [PATCH 097/143] UPSTREAM-PEND: soc: ti: k3: add OSPI flash 8D-8D-8D boot mode reset Add boot-time reset for K3 OSPI flash devices left in 8D-8D-8D mode by ROM bootloader. Issues reset commands to restore 1S-1S-1S mode before normal flash driver initialization. The priority is set to default flash priority minus one. The priority is a non-issue as long as MSPI bus (flash controller) is initialized before this. PR: 102513 Signed-off-by: Amneesh Singh --- soc/ti/k3/CMakeLists.txt | 2 + soc/ti/k3/Kconfig | 13 +++ soc/ti/k3/common/ospi_flash_boot_reset.c | 133 +++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 soc/ti/k3/common/ospi_flash_boot_reset.c diff --git a/soc/ti/k3/CMakeLists.txt b/soc/ti/k3/CMakeLists.txt index 9d3cdd0d7af3..ae5fb20dfe93 100644 --- a/soc/ti/k3/CMakeLists.txt +++ b/soc/ti/k3/CMakeLists.txt @@ -1,4 +1,6 @@ # Copyright (c) 2024 Nordic Semiconductor ASA # SPDX-License-Identifier: Apache-2.0 +zephyr_sources_ifdef(CONFIG_TI_K3_OSPI_FLASH_BOOT_MODE_RESET common/ospi_flash_boot_reset.c) + add_subdirectory(${SOC_SERIES}) diff --git a/soc/ti/k3/Kconfig b/soc/ti/k3/Kconfig index 4bc3bc6b2a97..700d65ed5967 100644 --- a/soc/ti/k3/Kconfig +++ b/soc/ti/k3/Kconfig @@ -3,6 +3,19 @@ if SOC_FAMILY_TI_K3 +config TI_K3_OSPI_FLASH_BOOT_MODE_RESET + bool "TI K3 OSPI flash boot mode reset" + depends on FLASH_MSPI_NOR + help + TI K3 ROM bootloader may leave OSPI flash devices in 8D-8D-8D mode + depending on the boot configuration. Enabling this option will + reset the flash in 8D-8D-8D mode so that it resets to the default + 1S-1S-1S mode. + + This is required when the ROM bootloader configures flash devices in + high-speed modes before transferring control to Zephyr, which would + cause normal flash initialization to fail. + rsource "*/Kconfig" endif # SOC_FAMILY_TI_K3 diff --git a/soc/ti/k3/common/ospi_flash_boot_reset.c b/soc/ti/k3/common/ospi_flash_boot_reset.c new file mode 100644 index 000000000000..9c72a97594e3 --- /dev/null +++ b/soc/ti/k3/common/ospi_flash_boot_reset.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2026 Texas Instruments + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "zephyr/drivers/firmware/tisci/tisci.h" +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(ti_k3_ospi_boot_reset, CONFIG_SOC_LOG_LEVEL); + +/* Generic 8D-8D-8D to 1S-1S-1S reset for any OSPI flash */ +static int ti_k3_reset_flash_from_8d8d8d(const struct device *mspi_dev, + const struct mspi_dev_id *dev_id, + uint32_t reset_recovery_us) +{ + int rc; + + struct mspi_dev_cfg boot_8d_cfg = { + .io_mode = MSPI_IO_MODE_OCTAL, + .data_rate = MSPI_DATA_RATE_DUAL, + .cmd_length = 2, + }; + + struct mspi_dev_cfg boot_1s_cfg = { + .io_mode = MSPI_IO_MODE_SINGLE, + .data_rate = MSPI_DATA_RATE_SINGLE, + .cmd_length = 1, + }; + + struct mspi_xfer_packet reset_packet = {.dir = MSPI_TX}; + + struct mspi_xfer reset_xfer = { + .xfer_mode = MSPI_PIO, + .packets = &reset_packet, + .num_packet = 1, + .cmd_length = 2, + }; + + rc = mspi_dev_config(mspi_dev, dev_id, + MSPI_DEVICE_CONFIG_IO_MODE | MSPI_DEVICE_CONFIG_CMD_LEN | + MSPI_DEVICE_CONFIG_DATA_RATE, + &boot_8d_cfg); + if (rc < 0) { + LOG_ERR("Failed to configure MSPI for 8D-8D-8D mode: %d", rc); + return rc; + } + + /* Reset enable (0x66) */ + reset_packet.cmd = 0x6666; + rc = mspi_transceive(mspi_dev, dev_id, &reset_xfer); + if (rc < 0) { + LOG_ERR("Reset enable command failed: %d", rc); + return rc; + } + + /* Reset (0x99) */ + reset_packet.cmd = 0x9999; + rc = mspi_transceive(mspi_dev, dev_id, &reset_xfer); + if (rc < 0) { + LOG_ERR("Reset command failed: %d", rc); + return rc; + } + + rc = mspi_dev_config(mspi_dev, dev_id, + MSPI_DEVICE_CONFIG_IO_MODE | MSPI_DEVICE_CONFIG_CMD_LEN | + MSPI_DEVICE_CONFIG_DATA_RATE, + &boot_1s_cfg); + if (rc < 0) { + LOG_ERR("Failed to configure MSPI for 1S-1S-1S mode: %d", rc); + return rc; + } + + if (reset_recovery_us > 0) { + k_busy_wait(reset_recovery_us); + } + + /* This releases the MSPI controller. */ + (void)mspi_get_channel_status(mspi_dev, 0); + + return 0; +} + +/* Platform initialization for chosen flash devices */ +static int ti_k3_ospi_flash_boot_reset_init(void) +{ + LOG_INF("TI K3 OSPI flash boot mode reset initialization"); + + /* Get flash controller from chosen node */ +#if DT_HAS_CHOSEN(zephyr_flash_controller) + const struct device *flash_controller = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)); + + if (!device_is_ready(flash_controller)) { + LOG_WRN("Flash controller not ready"); + return -ENODEV; + } + + /* Loop through all children of the MSPI controller and apply 8D-8D-8D reset */ +#define APPLY_RESET_TO_MSPI_CHILD(node_id) \ + do { \ + struct mspi_dev_id dev_id = { \ + .dev_idx = DT_REG_ADDR(node_id), \ + }; \ + LOG_INF("Attempting to reset flash from 8D-8D-8D mode for MSPI device at idx %d", \ + dev_id.dev_idx); \ + uint32_t reset_recovery_us = DT_PROP_OR(node_id, t_reset_recovery, 0); \ + int ret = ti_k3_reset_flash_from_8d8d8d(flash_controller, &dev_id, \ + reset_recovery_us); \ + if (ret < 0) { \ + LOG_WRN("Failed to reset MSPI device at idx %d: %d", dev_id.dev_idx, ret); \ + } else { \ + LOG_INF("Successfully reset MSPI device at idx %d", dev_id.dev_idx); \ + } \ + } while (0) + + /* Apply reset to all children under the flash controller */ + DT_FOREACH_CHILD_SEP(DT_CHOSEN(zephyr_flash_controller), APPLY_RESET_TO_MSPI_CHILD, (;)); + +#undef APPLY_RESET_TO_MSPI_CHILD +#else +#warning "No flash controller chosen" +#endif /* DT_HAS_CHOSEN(zephyr_flash_controller) */ + + return 0; +} + +#define TI_K3_FLASH_RESET_INIT_PRIORITY UTIL_DEC(CONFIG_FLASH_INIT_PRIORITY) + +SYS_INIT(ti_k3_ospi_flash_boot_reset_init, POST_KERNEL, TI_K3_FLASH_RESET_INIT_PRIORITY); From c56d1e982fb1ef14277a8feb7dfa384fd8517930 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Mon, 19 Jan 2026 18:35:05 +0530 Subject: [PATCH 098/143] UPSTREAM-PEND: boards: am243x_evm: add nodes for OSPI and nor flash Add Cadence MSPI controller and the S28HX512T NOR flash on its bus. PR: 102513 Signed-off-by: Amneesh Singh --- .../am243x_evm_am2434_r5f0_0-pinctrl.dtsi | 48 +++++++++++++++++++ .../am243x_evm/am243x_evm_am2434_r5f0_0.dts | 45 +++++++++++++++++ .../am243x_evm_am2434_r5f0_0_defconfig | 6 +++ dts/vendor/ti/am64x_main.dtsi | 16 +++++++ soc/ti/k3/am6x/r5/arm_mpu_regions.c | 7 +++ 5 files changed, 122 insertions(+) diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi index 6f12264fad22..7c9e757c89d5 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi @@ -72,6 +72,54 @@ spi0_d1: spi0_d1_default { pinmux = ; }; + + main_ospi0_clk: ospi0-clk { + pinmux = ; /* (N20) OSPI0_CLK */ + }; + + main_ospi0_csn0: ospi0-csn0 { + pinmux = ; /* (L19) OSPI0_CSN0 */ + }; + + main_ospi0_d0: ospi0-d0 { + pinmux = ; /* (M19) OSPI0_D0 */ + }; + + main_ospi0_d1: ospi0-d1 { + pinmux = ; /* (M18) OSPI0_D1 */ + }; + + main_ospi0_d2: ospi0-d2 { + pinmux = ; /* (M20) OSPI0_D2 */ + }; + + main_ospi0_d3: ospi0-d3 { + pinmux = ; /* (M21) OSPI0_D3 */ + }; + + main_ospi0_d4: ospi0-d4 { + pinmux = ; /* (P21) OSPI0_D4 */ + }; + + main_ospi0_d5: ospi0-d5 { + pinmux = ; /* (P20) OSPI0_D5 */ + }; + + main_ospi0_d6: ospi0-d6 { + pinmux = ; /* (N18) OSPI0_D6 */ + }; + + main_ospi0_d7: ospi0-d7 { + pinmux = ; /* (M17) OSPI0_D7 */ + }; + + main_ospi0_dqs: ospi0-dqs { + pinmux = ; /* (N19) OSPI0_DQS */ + }; + + main_ospi0_lbclko: ospi0-lbclko { + pinmux = ; /* (N21) OSPI0_LBCLKO */ + }; }; &mcu_pinctrl { diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts index 6993e8208a11..8ef55c43156e 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts @@ -20,6 +20,8 @@ zephyr,shell-uart = &main_uart0; zephyr,ipc = &ipc0; zephyr,ipc-shm = &ipc_shm0; + zephyr,flash-controller = &main_ospi0; + zephyr,flash = &flash0_memory; }; aliases { @@ -208,3 +210,46 @@ usr-id = <0>; status = "okay"; }; + +&main_ospi0 { + pinctrl-0 = <&main_ospi0_clk + &main_ospi0_d0 + &main_ospi0_d1 + &main_ospi0_d2 + &main_ospi0_d3 + &main_ospi0_d4 + &main_ospi0_d5 + &main_ospi0_d6 + &main_ospi0_d7 + &main_ospi0_dqs + &main_ospi0_lbclko + &main_ospi0_csn0>; + pinctrl-names = "default"; + status = "okay"; + + flash0: flash@0 { + compatible = "infineon,s28hx512t", "jedec,mspi-nor"; + status = "okay"; + reg = <0>; + jedec-id = [34 5b 1a]; + size = ; + mspi-max-frequency = ; + mspi-io-mode = "MSPI_IO_MODE_OCTAL"; + mspi-endian = "MSPI_LITTLE_ENDIAN"; + mspi-data-rate = "MSPI_DATA_RATE_DUAL"; + address-length = "ADDR_4_BYTE"; + command-length = "INSTR_2_BYTE"; + mspi-hardware-ce-num = <0>; + mspi-cpp-mode = "MSPI_CPP_MODE_0"; + mspi-ce-polarity = "MSPI_CE_ACTIVE_LOW"; + initial-soft-reset; + t-reset-recovery = <90000>; /* 90us */ + #address-cells = <1>; + #size-cells = <1>; + + flash0_memory: flash@60000000 { + compatible = "soc-nv-flash"; + reg = <0x60000000 DT_SIZE_M(128)>; + }; + }; +}; diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0_defconfig b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0_defconfig index 1b60b680f7a0..5425329f674b 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0_defconfig +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0_defconfig @@ -15,3 +15,9 @@ CONFIG_UART_CONSOLE=y # Enable MPU CONFIG_ARM_MPU=y + +# Enable OSPI flash boot mode reset +CONFIG_TI_K3_OSPI_FLASH_BOOT_MODE_RESET=y + +# Flash configuration +CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE=262144 diff --git a/dts/vendor/ti/am64x_main.dtsi b/dts/vendor/ti/am64x_main.dtsi index 95adba3b4106..f4a63ce1e3b4 100644 --- a/dts/vendor/ti/am64x_main.dtsi +++ b/dts/vendor/ti/am64x_main.dtsi @@ -383,4 +383,20 @@ #mbox-cells = <1>; status = "disabled"; }; + + main_ospi0: ospi@fc40000 { + compatible = "cdns,mspi-controller"; + reg = <0x0fc40000 0x100 0x60000000 0x4>; + reg-names = "base", "fifo"; + power-domains = <&fss0_ospi_0_pd>; + clock-frequency = ; + #address-cells = <1>; + #size-cells = <0>; + read-buffer-size = <0x80>; + cdns,nss-delay-ns = <60>; + cdns,btwn-delay-ns = <60>; + cdns,after-delay-ns = <60>; + cdns,init-delay-ns = <60>; + status = "disabled"; + }; }; diff --git a/soc/ti/k3/am6x/r5/arm_mpu_regions.c b/soc/ti/k3/am6x/r5/arm_mpu_regions.c index 7b4357a4f39f..8e6b5350ad53 100644 --- a/soc/ti/k3/am6x/r5/arm_mpu_regions.c +++ b/soc/ti/k3/am6x/r5/arm_mpu_regions.c @@ -16,6 +16,8 @@ #define PERM_Msk P_RW_U_NA_Msk #endif +#define FLASH_NODE DT_CHOSEN(zephyr_flash) + static const struct arm_mpu_region mpu_regions[] = { #if defined CONFIG_SOC_AM2434_R5F0_0 MPU_REGION_ENTRY("Device", 0x0, REGION_2G, {MPU_RASR_S_Msk | NOT_EXEC | PERM_Msk}), @@ -24,6 +26,11 @@ static const struct arm_mpu_region mpu_regions[] = { MPU_REGION_ENTRY( "SRAM", CONFIG_SRAM_BASE_ADDRESS, REGION_SRAM_SIZE, {NORMAL_OUTER_INNER_WRITE_BACK_WRITE_READ_ALLOCATE_NON_SHAREABLE | PERM_Msk}), + +#if DT_NODE_EXISTS(FLASH_NODE) + MPU_REGION_ENTRY("FSS0", CONFIG_FLASH_BASE_ADDRESS, REGION_FLASH_SIZE, + {P_RW_U_NA_Msk | STRONGLY_ORDERED_SHAREABLE}), +#endif }; const struct arm_mpu_config mpu_config = { From ddc830a05e14a838d64bf56f6477871f2c4eef76 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Wed, 25 Mar 2026 14:46:23 +0530 Subject: [PATCH 099/143] UPSTREAM-PEND: boards: am62l_evm: add nodes for OSPI and nor flash Add Cadence MSPI controller and the S28HX512T NOR flash on its bus. PR: 102513 Signed-off-by: Amneesh Singh --- .../am62l_evm_am62l3_a53-pinctrl.dtsi | 50 +++++++++++++++++++ boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts | 45 +++++++++++++++++ .../am62l_evm/am62l_evm_am62l3_a53_defconfig | 6 +++ dts/vendor/ti/am62l-main.dtsi | 17 +++++++ dts/vendor/ti/am62l_power_domains.dtsi | 6 +++ 5 files changed, 124 insertions(+) diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi index 16a3351009fb..61e1a6823ef9 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi @@ -4,9 +4,59 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include + &wkup_pinctrl { status = "okay"; + main_ospi0_clk: ospi0-clk { + pinmux = ; /* (D22) OSPI0_CLK */ + }; + + main_ospi0_csn0: ospi0-csn0 { + pinmux = ; /* (C20) OSPI0_CSN0 */ + }; + + main_ospi0_d0: ospi0-d0 { + pinmux = ; /* (C22) OSPI0_D0 */ + }; + + main_ospi0_d1: ospi0-d1 { + pinmux = ; /* (D21) OSPI0_D1 */ + }; + + main_ospi0_d2: ospi0-d2 { + pinmux = ; /* (E23) OSPI0_D2 */ + }; + + main_ospi0_d3: ospi0-d3 { + pinmux = ; /* (D23) OSPI0_D3 */ + }; + + main_ospi0_d4: ospi0-d4 { + pinmux = ; /* (F21) OSPI0_D4 */ + }; + + main_ospi0_d5: ospi0-d5 { + pinmux = ; /* (F19) OSPI0_D5 */ + }; + + main_ospi0_d6: ospi0-d6 { + pinmux = ; /* (G20) OSPI0_D6 */ + }; + + main_ospi0_d7: ospi0-d7 { + pinmux = ; /* (F20) OSPI0_D7 */ + }; + + main_ospi0_dqs: ospi0-dqs { + pinmux = ; /* (E22) OSPI0_DQS */ + }; + + main_ospi0_lbclko: ospi0-lbclko { + pinmux = ; /* (E18) OSPI0_LBCLKO */ + }; + uart0_rx_default: uart0_rx_default { pinmux = ; /* (D13) UART0_RXD */ }; diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts index 3c9170c6c30a..0c540b51321c 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts @@ -17,6 +17,8 @@ zephyr,console = &main_uart0; zephyr,shell-uart = &main_uart0; zephyr,sram = &ddr0; + zephyr,flash-controller = &main_ospi0; + zephyr,flash = &flash0_memory; }; cpus { @@ -41,3 +43,46 @@ pinctrl-names = "default"; status = "okay"; }; + +&main_ospi0 { + pinctrl-0 = <&main_ospi0_clk + &main_ospi0_d0 + &main_ospi0_d1 + &main_ospi0_d2 + &main_ospi0_d3 + &main_ospi0_d4 + &main_ospi0_d5 + &main_ospi0_d6 + &main_ospi0_d7 + &main_ospi0_dqs + &main_ospi0_lbclko + &main_ospi0_csn0>; + pinctrl-names = "default"; + status = "okay"; + + flash0: flash@0 { + compatible = "infineon,s28hx512t", "jedec,mspi-nor"; + status = "okay"; + reg = <0>; + jedec-id = [34 5b 1a]; + size = ; + mspi-max-frequency = ; + mspi-io-mode = "MSPI_IO_MODE_OCTAL"; + mspi-endian = "MSPI_LITTLE_ENDIAN"; + mspi-data-rate = "MSPI_DATA_RATE_DUAL"; + address-length = "ADDR_4_BYTE"; + command-length = "INSTR_2_BYTE"; + mspi-hardware-ce-num = <0>; + mspi-cpp-mode = "MSPI_CPP_MODE_0"; + mspi-ce-polarity = "MSPI_CE_ACTIVE_LOW"; + initial-soft-reset; + t-reset-recovery = <90000>; /* 90us */ + #address-cells = <1>; + #size-cells = <1>; + + flash0_memory: flash@60000000 { + compatible = "soc-nv-flash"; + reg = <0x60000000 DT_SIZE_M(128)>; + }; + }; +}; diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig b/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig index 29118e123ef8..4e82089b755a 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig @@ -38,3 +38,9 @@ CONFIG_CLOCK_CONTROL=y # Power Domain CONFIG_PM_DEVICE=y CONFIG_POWER_DOMAIN=y + +# Enable OSPI flash boot mode reset +CONFIG_TI_K3_OSPI_FLASH_BOOT_MODE_RESET=y + +# Flash configuration +CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE=262144 diff --git a/dts/vendor/ti/am62l-main.dtsi b/dts/vendor/ti/am62l-main.dtsi index 76ae248c6835..5a61e5ef6840 100644 --- a/dts/vendor/ti/am62l-main.dtsi +++ b/dts/vendor/ti/am62l-main.dtsi @@ -304,4 +304,21 @@ ti,spi-num-cs = <4>; status = "disabled"; }; + + main_ospi0: ospi@fc40000 { + compatible = "cdns,mspi-controller"; + reg = <0x0fc40000 0x100 0x60000000 0x4>; + reg-names = "base", "fifo"; + power-domains = <&main_fss0_pd>; + clock-frequency = ; + clocks = <&scmi_clk 136>; + #address-cells = <1>; + #size-cells = <0>; + read-buffer-size = <0x80>; + cdns,nss-delay-ns = <60>; + cdns,btwn-delay-ns = <60>; + cdns,after-delay-ns = <60>; + cdns,init-delay-ns = <60>; + status = "disabled"; + }; }; diff --git a/dts/vendor/ti/am62l_power_domains.dtsi b/dts/vendor/ti/am62l_power_domains.dtsi index 10f9c3185ab4..47e2b2569ea1 100644 --- a/dts/vendor/ti/am62l_power_domains.dtsi +++ b/dts/vendor/ti/am62l_power_domains.dtsi @@ -74,6 +74,12 @@ #power-domain-cells = <0>; }; + main_fss0_pd: power-domain@20 { + compatible = "arm,scmi-power-domain"; + reg = <32>; + #power-domain-cells = <0>; + }; + main_i2c0_pd: power-domain@35 { compatible = "arm,scmi-power-domain"; reg = <53>; From 140825ecf8a6ddb9c2cb6f1e74843a3a79c84f18 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Mon, 16 Jun 2025 19:20:31 +0530 Subject: [PATCH 100/143] UPSTREAM: drivers: sdhc: split caps into standard and extra commit 03ba08615a5594388768efab13718bfcf0ae4207 upstream. Make sure the capability bitfields fit the standard SD Association specification and move the extra capabilities to props. Affected Drivers: - sdhc_infineon - imx_usdhc - intel_emmc_host - rcar_mmc - sam_sdmmc - sdhc_ambiq - sdhc_esp32 - sdhc_max32 - sdhc_renesas_ra - sdhc_stm32 - xlnx_sdhc Affected Subsys - sd Reference: 2.2.26, SD Specifications, Part A2, SD Host Controller Simplified Specification, Version 4.20 URL: https://www.sdcard.org/downloads/pls/pdf/?p=PartA2_SD%20Host_Controller_Simplified_Specification_Ver4.20.jpg --- Backport notes: - Omitted the 4.4 Migration guide change - Omitted the subsystem change in the aforementioned affected drivers. Signed-off-by: Amneesh Singh --- include/zephyr/drivers/sdhc.h | 12 +++++++----- subsys/sd/mmc.c | 6 +++--- subsys/sd/sdmmc.c | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/zephyr/drivers/sdhc.h b/include/zephyr/drivers/sdhc.h index b2c4562d4e9b..159a312de9aa 100644 --- a/include/zephyr/drivers/sdhc.h +++ b/include/zephyr/drivers/sdhc.h @@ -159,16 +159,16 @@ enum sd_voltage { * @brief SD host controller capabilities * * SD host controller capability flags. These flags should be set by the SDHC - * driver, using the @ref sdhc_get_host_props api. + * driver, using the @ref sdhc_get_host_props api. These are packed to fit the capabilities register + * in the standard specification by the SD Association. */ struct sdhc_host_caps { - unsigned int timeout_clk_freq: 5; /**< Timeout clock frequency */ + unsigned int timeout_clk_freq: 6; /**< Timeout clock frequency */ unsigned int _rsvd_6: 1; /**< Reserved */ unsigned int timeout_clk_unit: 1; /**< Timeout clock unit */ unsigned int sd_base_clk: 8; /**< SD base clock frequency */ unsigned int max_blk_len: 2; /**< Max block length */ unsigned int bus_8_bit_support: 1; /**< 8-bit Support for embedded device */ - unsigned int bus_4_bit_support: 1; /**< 4 bit bus support */ unsigned int adma_2_support: 1; /**< ADMA2 support */ unsigned int _rsvd_20: 1; /**< Reserved */ unsigned int high_spd_support: 1; /**< High speed support */ @@ -190,6 +190,7 @@ struct sdhc_host_caps { unsigned int drv_type_d_support: 1; /**< Driver type D support */ unsigned int _rsvd_39: 1; /**< Reserved */ unsigned int retune_timer_count: 4; /**< Timer count for re-tuning */ + unsigned int _rsvd_44: 1; /**< Reserved */ unsigned int sdr50_needs_tuning: 1; /**< Use tuning for SDR50 */ unsigned int retuning_mode: 2; /**< Re-tuning mode */ unsigned int clk_multiplier: 8; /**< Clock multiplier */ @@ -197,8 +198,6 @@ struct sdhc_host_caps { unsigned int adma3_support: 1; /**< ADMA3 support */ unsigned int vdd2_180_support: 1; /**< 1.8V VDD2 support */ unsigned int _rsvd_61: 3; /**< Reserved */ - unsigned int hs200_support: 1; /**< HS200 support */ - unsigned int hs400_support: 1; /**< HS400 support */ }; /** @@ -231,6 +230,9 @@ struct sdhc_host_props { uint32_t max_current_330; /*!< Max current (in mA) at 3.3V */ uint32_t max_current_300; /*!< Max current (in mA) at 3.0V */ uint32_t max_current_180; /*!< Max current (in mA) at 1.8V */ + bool bus_4_bit_support; /**< 4 bit bus support */ + bool hs200_support; /**< HS200 support */ + bool hs400_support; /**< HS400 support */ bool is_spi; /*!< Is the host using SPI mode */ }; diff --git a/subsys/sd/mmc.c b/subsys/sd/mmc.c index 02216d3700a5..4a062616dc45 100644 --- a/subsys/sd/mmc.c +++ b/subsys/sd/mmc.c @@ -375,7 +375,7 @@ static int mmc_set_bus_width(struct sd_card *card) if (card->host_props.host_caps.bus_8_bit_support && card->bus_width == 8) { cmd.arg = MMC_SWITCH_8_BIT_BUS_ARG; card->bus_io.bus_width = SDHC_BUS_WIDTH8BIT; - } else if (card->host_props.host_caps.bus_4_bit_support && card->bus_width >= 4) { + } else if (card->host_props.bus_4_bit_support && card->bus_width >= 4) { cmd.arg = MMC_SWITCH_4_BIT_BUS_ARG; card->bus_io.bus_width = SDHC_BUS_WIDTH4BIT; } else { @@ -454,7 +454,7 @@ static int mmc_set_timing(struct sd_card *card, struct mmc_ext_csd *ext) /* Timing depends on EXT_CSD register information */ if ((ext->device_type.MMC_HS200_SDR_1200MV || ext->device_type.MMC_HS200_SDR_1800MV) && - (card->host_props.host_caps.hs200_support) && + (card->host_props.hs200_support) && (card->bus_io.signal_voltage == SD_VOL_1_8_V) && (card->bus_io.bus_width >= SDHC_BUS_WIDTH4BIT)) { ret = mmc_set_hs_timing(card); @@ -513,7 +513,7 @@ static int mmc_set_timing(struct sd_card *card, struct mmc_ext_csd *ext) /* Switch to HS400 if applicable */ if ((ext->device_type.MMC_HS400_DDR_1200MV || ext->device_type.MMC_HS400_DDR_1800MV) && - (card->host_props.host_caps.hs400_support) && + (card->host_props.hs400_support) && (card->bus_io.bus_width == SDHC_BUS_WIDTH8BIT)) { /* Switch back to regular HS timing */ ret = mmc_set_hs_timing(card); diff --git a/subsys/sd/sdmmc.c b/subsys/sd/sdmmc.c index ebcc1236291f..7c04083cb4a5 100644 --- a/subsys/sd/sdmmc.c +++ b/subsys/sd/sdmmc.c @@ -591,7 +591,7 @@ static int sdmmc_init_hs(struct sd_card *card) LOG_ERR("Failed to switch card to HS mode"); return ret; } - if (card->host_props.host_caps.bus_4_bit_support && (card->flags & SD_4BITS_WIDTH)) { + if (card->host_props.bus_4_bit_support && (card->flags & SD_4BITS_WIDTH)) { /* Raise bus width to 4 bits */ ret = sdmmc_set_bus_width(card, SDHC_BUS_WIDTH4BIT); if (ret) { From b255f4bef03e0979a7a5c494ed7eb9a599b8d40a Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Fri, 26 Sep 2025 18:08:38 +0530 Subject: [PATCH 101/143] UPSTREAM-PEND: drivers: sdhc: add TI AM654 SDHCI driver Add the driver for TI's interrupt based AM654 SD Host Controller that can be used with both eMMC and SD cards. PR: 97172 Signed-off-by: Amneesh Singh --- drivers/sdhc/CMakeLists.txt | 1 + drivers/sdhc/Kconfig | 1 + drivers/sdhc/Kconfig.ti | 47 + drivers/sdhc/sdhc_ti_am654.c | 1978 +++++++++++++++++++++++++ dts/bindings/sdhc/ti,am654-sdhci.yaml | 124 ++ 5 files changed, 2151 insertions(+) create mode 100644 drivers/sdhc/Kconfig.ti create mode 100644 drivers/sdhc/sdhc_ti_am654.c create mode 100644 dts/bindings/sdhc/ti,am654-sdhci.yaml diff --git a/drivers/sdhc/CMakeLists.txt b/drivers/sdhc/CMakeLists.txt index 4b57a4f0d09f..10997e6502a7 100644 --- a/drivers/sdhc/CMakeLists.txt +++ b/drivers/sdhc/CMakeLists.txt @@ -16,6 +16,7 @@ zephyr_library_sources_ifdef(CONFIG_SDHC_ESP32 sdhc_esp32.c) zephyr_library_sources_ifdef(CONFIG_SDHC_RENESAS_RA sdhc_renesas_ra.c) zephyr_library_sources_ifdef(CONFIG_SDHC_MAX32 sdhc_max32.c) zephyr_library_sources_ifdef(CONFIG_SDHC_AMBIQ sdhc_ambiq.c) +zephyr_library_sources_ifdef(CONFIG_SDHC_TI_AM654 sdhc_ti_am654.c) zephyr_library_sources_ifdef(CONFIG_XLNX_SDHC xlnx_sdhc.c) zephyr_library_sources_ifdef(CONFIG_SDHC_STM32_SDIO sdhc_stm32.c) endif() diff --git a/drivers/sdhc/Kconfig b/drivers/sdhc/Kconfig index 475f2342a563..fcdcfbe5542b 100644 --- a/drivers/sdhc/Kconfig +++ b/drivers/sdhc/Kconfig @@ -21,6 +21,7 @@ source "drivers/sdhc/Kconfig.esp32" source "drivers/sdhc/Kconfig.renesas_ra" source "drivers/sdhc/Kconfig.max32" source "drivers/sdhc/Kconfig.ambiq" +source "drivers/sdhc/Kconfig.ti" source "drivers/sdhc/Kconfig.xlnx" source "drivers/sdhc/Kconfig.stm32" diff --git a/drivers/sdhc/Kconfig.ti b/drivers/sdhc/Kconfig.ti new file mode 100644 index 000000000000..b95c08856b7e --- /dev/null +++ b/drivers/sdhc/Kconfig.ti @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments +# SPDX-License-Identifier: Apache-2.0 + +config SDHC_TI_AM654 + bool "TI AM654 SDHC driver" + select SDHC_SUPPORTS_NATIVE_MODE + select SDHC_SUPPORTS_UHS + select EVENTS + select REGULATOR + default y + depends on DT_HAS_TI_AM654_SDHCI_ENABLED + + help + Interrupt based TI AM654 SD/eMMC Host controller + +if SDHC_TI_AM654 +config SDHC_TI_AM654_ENABLE_ADMA + bool "ADMA2" + default y + select CACHE_MANAGEMENT if CPU_HAS_DCACHE + help + Enable ADMA2 for TI AM654 SDHC driver + +config SDHC_TI_AM654_ENABLE_AUTO_STOP + bool "Auto-stop commands" + default y + help + Enable auto-CMD12/CMD23 during multi-block transactions + +config SDHC_TI_AM654_ADMA_DESC_LEN + int "Maximum ADMA descriptor lines" + default 8 + help + Maximum number of ADMA descriptor lines allowed + +config SDHC_TI_AM654_LEGACY_PHY + bool "Legacy PHY configuration" + help + Enable legacy PHY configuration for older IP versions. + + Early TI SDHC IP implementations use narrower register fields: + - DLL frequency register field is 2-bit wide (vs 3-bit in newer IP) + - Strobe select field is 4-bit wide (vs 8-bit in newer IP) + + Enable this for older hardware, disable for newer implementations + like J721E which use wider register fields. +endif diff --git a/drivers/sdhc/sdhc_ti_am654.c b/drivers/sdhc/sdhc_ti_am654.c new file mode 100644 index 000000000000..33c19f46acca --- /dev/null +++ b/drivers/sdhc/sdhc_ti_am654.c @@ -0,0 +1,1978 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_am654_sdhci + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(ti_am654_sdhc, CONFIG_SDHC_LOG_LEVEL); + +struct ti_am654_ss_regs { + uint8_t RESERVED_1[0x14]; /**< Reserved, offset: 0x00-0x14 */ + volatile uint32_t CTL_CFG_2; /**< Controller Config 2, offset: 0x14 */ + uint8_t RESERVED_4[0xE8]; /**< Reserved, offset: 0x18-0x100 */ + volatile uint32_t PHY_CTRL_1; /**< PHY Control 1, offset: 0x100 */ + uint8_t RESERVED_2[0x08]; /**< Reserved, offset: 0x104-0x10C */ + volatile uint32_t PHY_CTRL_4; /**< PHY Control 4, offset: 0x10C */ + volatile uint32_t PHY_CTRL_5; /**< PHY Control 5, offset: 0x110 */ + uint8_t RESERVED_3[0x1C]; /**< Reserved, offset: 0x114-0x130 */ + volatile uint32_t PHY_STAT_1; /**< PHY Status 1, offset: 0x130 */ +}; + +/* Controller Config 2 Register */ +#define TI_AM654_CTL_CFG_2_SLOTTYPE GENMASK(31, 30) + +/* PHY Control 1 Register */ +#define TI_AM654_PHY_CTRL_1_IOMUX_ENABLE BIT(31) +#define TI_AM654_PHY_CTRL_1_DR_TY GENMASK(22, 20) +#define TI_AM654_PHY_CTRL_1_DR_TY_VAL_50_OHMS (0x0) +#define TI_AM654_PHY_CTRL_1_DR_TY_VAL_33_OHMS (0x1) +#define TI_AM654_PHY_CTRL_1_DR_TY_VAL_66_OHMS (0x2) +#define TI_AM654_PHY_CTRL_1_DR_TY_VAL_100_OHMS (0x3) +#define TI_AM654_PHY_CTRL_1_DR_TY_VAL_40_OHMS (0x4) +#define TI_AM654_PHY_CTRL_1_EN_RTRIM BIT(16) +#define TI_AM654_PHY_CTRL_1_DLL_TRM_ICP GENMASK(7, 4) +#define TI_AM654_PHY_CTRL_1_ENDLL BIT(1) +#define TI_AM654_PHY_CTRL_1_PDB BIT(0) + +/* PHY Control 4 Register */ +#define TI_AM654_PHY_CTRL_4_STRBSEL GENMASK(31, 24) +#define TI_AM654_PHY_CTRL_4_STRBSEL_4BIT GENMASK(27, 24) +#define TI_AM654_PHY_CTRL_4_OTAPDLYENA BIT(20) +#define TI_AM654_PHY_CTRL_4_OTAPDLYSEL GENMASK(15, 12) +#define TI_AM654_PHY_CTRL_4_ITAPCHGWIN BIT(9) +#define TI_AM654_PHY_CTRL_4_ITAPDLYENA BIT(8) +#define TI_AM654_PHY_CTRL_4_ITAPDLYSEL GENMASK(4, 0) +#define TI_AM654_PHY_CTRL_4_ITAPDLYSEL_VAL_MAX (31) + +/* PHY Control 5 Register */ +#define TI_AM654_PHY_CTRL_5_SETDLYTXCLK BIT(17) +#define TI_AM654_PHY_CTRL_5_SETDLYRXCLK BIT(16) +#define TI_AM654_PHY_CTRL_5_FRQSEL GENMASK(10, 8) +#define TI_AM654_PHY_CTRL_5_FRQSEL_VAL_200_170_MHZ (0x0) +#define TI_AM654_PHY_CTRL_5_FRQSEL_VAL_170_140_MHZ (0x1) +#define TI_AM654_PHY_CTRL_5_FRQSEL_VAL_140_110_MHZ (0x2) +#define TI_AM654_PHY_CTRL_5_FRQSEL_VAL_110_80_MHZ (0x3) +#define TI_AM654_PHY_CTRL_5_FRQSEL_VAL_80_50_MHZ (0x4) +#define TI_AM654_PHY_CTRL_5_FRQSEL100 BIT(9) +#define TI_AM654_PHY_CTRL_5_FRQSEL50 BIT(8) +#define TI_AM654_PHY_CTRL_5_CLKBUFSEL GENMASK(2, 0) + +/* PHY Status 1 Register */ +#define TI_AM654_PHY_STAT_1_CALDONE BIT(1) +#define TI_AM654_PHY_STAT_1_DLLRDY BIT(0) + +struct ti_am654_hc_regs { + volatile uint32_t SYS_ADDR; /**< SDMA System Address, offset: 0x00 */ + volatile uint16_t BLOCK_SIZE; /**< Block Size, offset: 0x04 */ + volatile uint16_t BLOCK_COUNT; /**< Block Count, offset: 0x06 */ + volatile uint32_t ARGUMENT1; /**< Argument1, offset: 0x08 */ + volatile uint16_t TRANSFER_MODE; /**< Transfer Mode, offset: 0x0C */ + volatile uint16_t COMMAND; /**< Command, offset: 0x0E */ + volatile uint32_t RESPONSE[4]; /**< Response Register 0-3, offset: 0x10-0x1C */ + volatile uint32_t DATA_PORT; /**< Buffer Data Port, offset: 0x20 */ + volatile uint32_t PRESENTSTATE; /**< Present State, offset: 0x24 */ + volatile uint8_t HOST_CONTROL1; /**< Host Control 1, offset: 0x28 */ + volatile uint8_t POWER_CONTROL; /**< Power Control, offset: 0x29 */ + volatile uint8_t BLOCK_GAP_CONTROL; /**< Block Gap Control, offset: 0x2A */ + volatile uint8_t WAKEUP_CONTROL; /**< Wakeup Control, offset: 0x2B */ + volatile uint16_t CLOCK_CONTROL; /**< Clock Control, offset: 0x2C */ + volatile uint8_t TIMEOUT_CONTROL; /**< Timeout Control, offset: 0x2E */ + volatile uint8_t SOFTWARE_RESET; /**< Software Reset, offset: 0x2F */ + volatile uint16_t NORMAL_INTR_STS; /**< Normal IRQ Status, offset: 0x30 */ + volatile uint16_t ERROR_INTR_STS; /**< Error IRQ Status, offset: 0x32 */ + volatile uint16_t NORMAL_INTR_STS_ENA; /**< Normal IRQ Status Enable, offset: 0x34 */ + volatile uint16_t ERROR_INTR_STS_ENA; /**< Error IRQ Status Enable, offset: 0x36 */ + volatile uint16_t NORMAL_INTR_SIG_ENA; /**< Normal IRQ Signal Enable, offset: 0x38 */ + volatile uint16_t ERROR_INTR_SIG_ENA; /**< Error IRQ Signal Enable, offset: 0x3A */ + volatile uint16_t AUTOCMD_ERR_STS; /**< Auto CMD Error Status, offset: 0x3C */ + volatile uint16_t HOST_CONTROL2; /**< Host Control 2, offset: 0x3E */ + volatile uint64_t CAPABILITIES; /**< Capabilities, offset: 0x40 */ + volatile uint64_t MAX_CURRENT_CAP; /**< Maximum Current Capabilities, offset: 0x48 */ + uint8_t RESERVED_1[0x8]; /**< Reserved, offset: 0x50-0x58 */ + volatile uint64_t ADMA_SYS_ADDRESS; /**< ADMA System Address, offset: 0x58 */ + uint8_t RESERVED_2[0x20]; /**< Reserved, offset: 0x60-0x80 */ +}; + +/* Block Size */ +#define TI_AM654_BLOCK_SIZE_XFER_BLK_SIZE GENMASK(11, 0) + +/* Transfer Mode */ +#define TI_AM654_TRANSFER_MODE_MULTI_BLK_SEL BIT(5) +#define TI_AM654_TRANSFER_MODE_DATA_XFER_DIR BIT(4) +#define TI_AM654_TRANSFER_MODE_AUTO_CMD_ENA GENMASK(3, 2) +#define TI_AM654_TRANSFER_MODE_AUTO_CMD_ENA_VAL_CMD12 (0x1) +#define TI_AM654_TRANSFER_MODE_AUTO_CMD_ENA_VAL_CMD23 (0x2) +#define TI_AM654_TRANSFER_MODE_BLK_CNT_ENA BIT(1) +#define TI_AM654_TRANSFER_MODE_DMA_ENA BIT(0) + +/* Command */ +#define TI_AM654_COMMAND_CMD_INDEX GENMASK(13, 8) +#define TI_AM654_COMMAND_CMD_TYPE GENMASK(7, 6) +#define TI_AM654_COMMAND_CMD_TYPE_VAL_NORMAL (0x0) +#define TI_AM654_COMMAND_DATA_PRESENT BIT(5) +#define TI_AM654_COMMAND_CMD_INDEX_CHK_ENA BIT(4) +#define TI_AM654_COMMAND_CMD_CRC_CHK_ENA BIT(3) +#define TI_AM654_COMMAND_RESP_TYPE_SEL GENMASK(1, 0) +#define TI_AM654_COMMAND_RESP_TYPE_SEL_VAL_NONE (0x0) +#define TI_AM654_COMMAND_RESP_TYPE_SEL_VAL_LEN_136 (0x1) +#define TI_AM654_COMMAND_RESP_TYPE_SEL_VAL_LEN_48 (0x2) +#define TI_AM654_COMMAND_RESP_TYPE_SEL_VAL_LEN_48_BUSY (0x3) + +/* Present State */ +#define TI_AM654_PRESENTSTATE_SDIF_DAT3IN BIT(23) +#define TI_AM654_PRESENTSTATE_SDIF_DAT2IN BIT(22) +#define TI_AM654_PRESENTSTATE_SDIF_DAT1IN BIT(21) +#define TI_AM654_PRESENTSTATE_SDIF_DAT0IN BIT(20) +#define TI_AM654_PRESENTSTATE_CARD_INSERTED BIT(16) +#define TI_AM654_PRESENTSTATE_SDIF_DAT7IN BIT(7) +#define TI_AM654_PRESENTSTATE_SDIF_DAT6IN BIT(6) +#define TI_AM654_PRESENTSTATE_SDIF_DAT5IN BIT(5) +#define TI_AM654_PRESENTSTATE_SDIF_DAT4IN BIT(4) +#define TI_AM654_PRESENTSTATE_INHIBIT_DAT BIT(1) +#define TI_AM654_PRESENTSTATE_INHIBIT_CMD BIT(0) + +/* Host Control 1 */ +#define TI_AM654_HOST_CONTROL1_CD_SIG_SEL BIT(7) +#define TI_AM654_HOST_CONTROL1_CD_TEST_LEVEL BIT(6) +#define TI_AM654_HOST_CONTROL1_EXT_DATA_WIDTH BIT(5) +#define TI_AM654_HOST_CONTROL1_HIGH_SPEED_ENA BIT(2) +#define TI_AM654_HOST_CONTROL1_DATA_WIDTH BIT(1) +#define TI_AM654_HOST_CONTROL1_DATA_WIDTH BIT(1) +#define TI_AM654_HOST_CONTROL1_DMA_SELECT GENMASK(4, 3) +#define TI_AM654_HOST_CONTROL1_DMA_SELECT_VAL_ADMA2 (0x2) + +/* Power Control */ +#define TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE GENMASK(3, 1) +#define TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE_VAL_V3P3 (0x7) +#define TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE_VAL_V3P0 (0x6) +#define TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE_VAL_V1P8 (0x5) +#define TI_AM654_POWER_CONTROL_SD_BUS_POWER BIT(0) + +/* Clock Control */ +#define TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL GENMASK(15, 8) +#define TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL_UPBITS GENMASK(7, 6) +#define TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL_VAL_MAX (0x3FF) +#define TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL_VAL_MASK_HI (0x300) +#define TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL_VAL_MASK_LO (0x0FF) +#define TI_AM654_CLOCK_CONTROL_CLKGEN_SEL BIT(5) +#define TI_AM654_CLOCK_CONTROL_PLL_ENA BIT(3) +#define TI_AM654_CLOCK_CONTROL_SD_CLK_ENA BIT(2) +#define TI_AM654_CLOCK_CONTROL_INT_CLK_STABLE BIT(1) +#define TI_AM654_CLOCK_CONTROL_INT_CLK_ENA BIT(0) + +/* Software Reset */ +#define TI_AM654_SOFTWARE_RESET_SWRST_FOR_DAT BIT(2) +#define TI_AM654_SOFTWARE_RESET_SWRST_FOR_CMD BIT(1) +#define TI_AM654_SOFTWARE_RESET_SWRST_FOR_ALL BIT(0) + +/* Normal Interrupt Bits (common to several registers) */ +#define TI_AM654_NORMAL_INTR_CARD_REMOVAL BIT(7) +#define TI_AM654_NORMAL_INTR_CARD_INSERTION BIT(6) +#define TI_AM654_NORMAL_INTR_BUF_RD_READY BIT(5) +#define TI_AM654_NORMAL_INTR_BUF_WR_READY BIT(4) +#define TI_AM654_NORMAL_INTR_DMA_INTERRUPT BIT(3) +#define TI_AM654_NORMAL_INTR_XFER_COMPLETE BIT(1) +#define TI_AM654_NORMAL_INTR_CMD_COMPLETE BIT(0) + +/* Error interrupt bits */ +#define TI_AM654_ERROR_INTR_ALL GENMASK(15, 0) + +/* Host Control 2 */ +#define TI_AM654_HOST_CONTROL2_BIT64_ADDRESSING BIT(13) +#define TI_AM654_HOST_CONTROL2_HOST_VER40_ENA BIT(12) +#define TI_AM654_HOST_CONTROL2_ADMA2_LEN_MODE BIT(10) +#define TI_AM654_HOST_CONTROL2_SAMPLING_CLK_SELECT BIT(7) +#define TI_AM654_HOST_CONTROL2_EXECUTE_TUNING BIT(6) +#define TI_AM654_HOST_CONTROL2_V1P8_SIGNAL_ENA BIT(3) +#define TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT GENMASK(2, 0) +#define TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_SDR12 (0x0) +#define TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_SDR25 (0x1) +#define TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_SDR50 (0x2) +#define TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_SDR104 (0x3) +#define TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_DDR50 (0x4) +#define TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_HS400 (0x5) + +/* Capabilities */ +#define TI_AM654_CAPABILITIES_BUS_HS400_SUPPORT BIT64(63) + +/* Max Current Capabilities */ +#define TI_AM654_MAX_CURRENT_CAP_VDD2_1P8V GENMASK64(39, 32) +#define TI_AM654_MAX_CURRENT_CAP_VDD1_1P8V GENMASK64(23, 16) +#define TI_AM654_MAX_CURRENT_CAP_VDD1_3P0V GENMASK64(15, 8) +#define TI_AM654_MAX_CURRENT_CAP_VDD1_3P3V GENMASK64(7, 0) + +/* Miscellaneous */ +#define TI_AM654_ADMA2_DESC_ACTION_TRAN (0b100) +#define TI_AM654_ADMA2_DESC_LENGTH_MAX (GENMASK(25, 0)) +#define TI_AM654_TIMING_MODE_NUM (SDHC_TIMING_HS400 + 1) + +#if defined(CONFIG_DCACHE) && defined(CONFIG_DCACHE_LINE_SIZE) +#define SDHC_CACHE_ALIGNMENT CONFIG_DCACHE_LINE_SIZE +#else +#define SDHC_CACHE_ALIGNMENT (128) /* safe default */ +#endif + +static const uint8_t ti_am654_tuning_blk_8_bit[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, + 0xee, 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, + 0xff, 0xff, 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, 0x77, 0x77, 0xff, 0x77, + 0xbb, 0xdd, 0xee, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, 0xff, + 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; + +static const uint8_t ti_am654_tuning_blk_4_bit[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, 0xc3, 0x3c, 0xcc, 0xff, 0xfe, + 0xff, 0xfe, 0xef, 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, 0xbf, 0xff, + 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, + 0x3c, 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, 0xff, 0xfd, 0xff, 0xfd, + 0xdf, 0xff, 0xbf, 0xff, 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; + +struct ti_am654_adma2_descriptor { + bool valid: 1; + bool end: 1; + bool interrupt: 1; + uint8_t action: 3; + uint32_t length_hi: 10; + uint32_t length_lo: 16; + uint32_t addr_lo: 32; + uint32_t addr_hi: 32; + uint32_t: 32; + +} __packed __aligned(4); + +struct ti_am654_tap_delay_config { + bool itap_delay_enable: 1; + uint8_t itap_delay_value: 5; + bool otap_delay_enable: 1; + uint8_t otap_delay_value: 5; +}; + +enum ti_am654_reset_type { + TI_AM654_RESET_TYPE_DAT = TI_AM654_SOFTWARE_RESET_SWRST_FOR_DAT, + TI_AM654_RESET_TYPE_CMD = TI_AM654_SOFTWARE_RESET_SWRST_FOR_CMD, + TI_AM654_RESET_TYPE_ALL = TI_AM654_SOFTWARE_RESET_SWRST_FOR_ALL, +}; + +struct ti_am654_cmd_cfg { + uint8_t resp_type: 2; + bool crc_chk: 1; + bool idx_chk: 1; + bool data_present: 1; +}; + +struct ti_am654_tuning_window { + uint8_t start; + uint8_t end; + uint8_t length; +}; + +/* SDHC configuration. */ +struct ti_am654_config { + DEVICE_MMIO_NAMED_ROM(host); + DEVICE_MMIO_NAMED_ROM(subsys); + const struct pinctrl_dev_config *pinctrl; + void (*irq_func)(const struct device *dev); + const struct device *vmmc; + const struct device *vqmmc; + bool dll_present; + bool is_embedded; + bool fails_without_test_cd; + uint8_t clkbuf_sel; + uint8_t strobe_sel; + uint8_t drive_impedance; + uint8_t current_trim; +}; + +struct ti_am654_data { + DEVICE_MMIO_NAMED_RAM(host); + DEVICE_MMIO_NAMED_RAM(subsys); + struct ti_am654_tap_delay_config delay_config[TI_AM654_TIMING_MODE_NUM]; + struct sdhc_host_props props; + struct sdhc_io ios; + struct k_event irq_event; + sdhc_interrupt_cb_t callback; + void *user_data; + +/* ADMA specific fields */ +#ifdef CONFIG_SDHC_TI_AM654_ENABLE_ADMA + /* ADMA descriptors */ + struct ti_am654_adma2_descriptor descs[CONFIG_SDHC_TI_AM654_ADMA_DESC_LEN]; + /* Cache-aligned residual buffers for DMA */ + uint8_t residual_start[SDHC_CACHE_ALIGNMENT] __aligned(SDHC_CACHE_ALIGNMENT); + uint8_t residual_end[SDHC_CACHE_ALIGNMENT] __aligned(SDHC_CACHE_ALIGNMENT); +#endif /* CONFIG_SDHC_TI_AM654_ENABLE_ADMA */ +}; + +#define TI_AM654_K_EVENT_ERRORS(n) (n << 16) +#define TI_AM654_K_EVENT_ALL_ERRORS TI_AM654_K_EVENT_ERRORS(TI_AM654_ERROR_INTR_ALL) + +#define DEV_CFG(dev) ((const struct ti_am654_config *)dev->config) +#define DEV_DATA(dev) ((struct ti_am654_data *)dev->data) +#define DEV_HC_REGS(dev) ((struct ti_am654_hc_regs *)DEVICE_MMIO_NAMED_GET(dev, host)) +#define DEV_SS_REGS(dev) ((struct ti_am654_ss_regs *)DEVICE_MMIO_NAMED_GET(dev, subsys)) + +#define TI_AM654_REG_POLL_RETRIES (100) +#define TI_AM654_REG_POLL_TIME_BETWEEN_RETRIES_US (10) +#define TI_AM654_TUNING_RETRIES (5) + +static int ti_am654_request(const struct device *dev, struct sdhc_command *cmd, + struct sdhc_data *dat); + +static int ti_am654_reset(const struct device *dev, enum ti_am654_reset_type type) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + int retries = TI_AM654_REG_POLL_RETRIES; + + /* do software reset */ + hc_regs->SOFTWARE_RESET |= type; + + /* wait for completion */ + while (hc_regs->SOFTWARE_RESET & type) { + + if (retries-- == 0) { + LOG_ERR("SDHC software reset timed out"); + return -ETIMEDOUT; + } + k_usleep(TI_AM654_REG_POLL_TIME_BETWEEN_RETRIES_US); + } + + return 0; +} + +static int ti_am654_reset_all(const struct device *dev) +{ + return ti_am654_reset(dev, TI_AM654_RESET_TYPE_ALL); +} + +static ALWAYS_INLINE k_timeout_t ti_am654_timeout_from_msec(int timeout) +{ + return (timeout == SDHC_TIMEOUT_FOREVER ? K_FOREVER : K_MSEC(timeout)); +} + +static void ti_am654_read_cmd_resp(const struct device *dev, struct sdhc_command *cmd) +{ + enum sd_rsp_type type = cmd->response_type; + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + + uint32_t r0 = hc_regs->RESPONSE[0]; + uint32_t r1 = hc_regs->RESPONSE[1]; + uint32_t r2 = hc_regs->RESPONSE[2]; + uint32_t r3 = hc_regs->RESPONSE[3]; + + switch (type & SDHC_NATIVE_RESPONSE_MASK) { + case SD_RSP_TYPE_NONE: + cmd->response[3] = 0; + cmd->response[2] = 0; + cmd->response[1] = 0; + cmd->response[0] = 0; + break; + case SD_RSP_TYPE_R2: + /* REP[119:0] */ + /* shift by 1 byte to make it [127:8] for parsing */ + cmd->response[3] = ((r3 & GENMASK(23, 0)) << 8) | (r2 >> 24); + cmd->response[2] = (r2 << 8) | (r1 >> 24); + cmd->response[1] = (r1 << 8) | (r0 >> 24); + cmd->response[0] = (r0 << 8); + break; + default: + /* REP[31:0] */ + cmd->response[3] = 0; + cmd->response[2] = 0; + cmd->response[1] = 0; + cmd->response[0] = r0; + } +} + +static int ti_am654_request_cmd_send(const struct device *dev, struct sdhc_command *cmd, + struct ti_am654_cmd_cfg *cfg) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + struct ti_am654_data *data = DEV_DATA(dev); + uint32_t tries = cmd->retries + 1; + uint32_t events; + + while (tries--) { + k_event_clear(&data->irq_event, TI_AM654_NORMAL_INTR_CMD_COMPLETE); + + hc_regs->ARGUMENT1 = cmd->arg; + + hc_regs->COMMAND = FIELD_PREP(TI_AM654_COMMAND_CMD_INDEX, cmd->opcode) | + FIELD_PREP(TI_AM654_COMMAND_CMD_TYPE, + TI_AM654_COMMAND_CMD_TYPE_VAL_NORMAL) | + FIELD_PREP(TI_AM654_COMMAND_RESP_TYPE_SEL, cfg->resp_type) | + FIELD_PREP(TI_AM654_COMMAND_CMD_INDEX_CHK_ENA, cfg->idx_chk) | + FIELD_PREP(TI_AM654_COMMAND_CMD_CRC_CHK_ENA, cfg->crc_chk) | + FIELD_PREP(TI_AM654_COMMAND_DATA_PRESENT, cfg->data_present); + + events = k_event_wait(&data->irq_event, + TI_AM654_NORMAL_INTR_CMD_COMPLETE | + TI_AM654_K_EVENT_ALL_ERRORS, + false, ti_am654_timeout_from_msec(cmd->timeout_ms)); + + if (events & TI_AM654_K_EVENT_ALL_ERRORS) { + /* any error */ + LOG_DBG("Command Error Status: 0x%x", events >> 16); + + ti_am654_reset(dev, TI_AM654_RESET_TYPE_CMD); + + if (cfg->data_present) { + ti_am654_reset(dev, TI_AM654_RESET_TYPE_DAT); + } + + return -EIO; + + } else if (events & TI_AM654_NORMAL_INTR_CMD_COMPLETE) { + /* command transmission successful */ + ti_am654_read_cmd_resp(dev, cmd); + return 0; + } + } + + return -ETIMEDOUT; +} + +#ifdef CONFIG_SDHC_TI_AM654_ENABLE_ADMA +static size_t ti_am654_get_dcache_line_size(void) +{ + size_t cache_line_size = sys_cache_data_line_size_get(); + + if (cache_line_size != 0) { + return cache_line_size; + } + + return SDHC_CACHE_ALIGNMENT; +} + +static void ti_am654_writeback_residuals(const struct device *dev, struct sdhc_data *dat) +{ + struct ti_am654_data *data = DEV_DATA(dev); + size_t length = dat->block_size * dat->blocks; + uintptr_t address = (uintptr_t)dat->data; + uintptr_t end_addr = address + length; + size_t cache_line_size = ti_am654_get_dcache_line_size(); + size_t start_residual_bytes = (-address) & (cache_line_size - 1); + size_t end_residual_bytes = end_addr & (cache_line_size - 1); + + if (start_residual_bytes != 0) { + if (start_residual_bytes < length) { + memcpy(dat->data, data->residual_start, start_residual_bytes); + } else { + memcpy(dat->data, data->residual_start, length); + return; + } + } + + if (end_residual_bytes != 0) { + memcpy((void *)(end_addr - end_residual_bytes), data->residual_end, + end_residual_bytes); + } +} + +static ALWAYS_INLINE struct ti_am654_adma2_descriptor +ti_am654_create_descriptor(uint64_t address, size_t length, bool end) +{ + return (struct ti_am654_adma2_descriptor){ + .valid = true, + .end = end, + .interrupt = false, + .action = TI_AM654_ADMA2_DESC_ACTION_TRAN, + .length_hi = FIELD_GET(GENMASK(25, 16), length), + .length_lo = FIELD_GET(GENMASK(15, 0), length), + .addr_lo = FIELD_GET(GENMASK64(31, 0), address), + .addr_hi = FIELD_GET(GENMASK64(63, 32), address), + }; +} + +static int ti_am654_request_data_setup_adma(const struct device *dev, struct sdhc_data *dat, + bool is_write) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + struct ti_am654_data *data = DEV_DATA(dev); + size_t length_left = dat->block_size * dat->blocks; + uint64_t address = (uintptr_t)dat->data; + uintptr_t end_addr = address + length_left; + int i = 0; + size_t cache_line_size = ti_am654_get_dcache_line_size(); + size_t start_residual_bytes = (-address) & (cache_line_size - 1); + size_t end_residual_bytes = end_addr & (cache_line_size - 1); + + if (ARRAY_SIZE(data->descs) * TI_AM654_ADMA2_DESC_LENGTH_MAX < length_left) { + LOG_ERR("number of descriptors %zu is less than required", ARRAY_SIZE(data->descs)); + return -EINVAL; + } + + /* if not cache aligned at the start, separate buffer, for rx */ + if ((!is_write) && start_residual_bytes != 0) { + uintptr_t residual = (uintptr_t)data->residual_start; + + if (start_residual_bytes < length_left) { + data->descs[i++] = + ti_am654_create_descriptor(residual, start_residual_bytes, false); + } else { + /* exit early if this is the only descriptor */ + data->descs[i++] = ti_am654_create_descriptor(residual, length_left, true); + goto start_adma; + } + + /* invalidate if read */ + sys_cache_data_invd_range(data->residual_start, sizeof(data->residual_start)); + length_left -= start_residual_bytes; + address += start_residual_bytes; + } + + /* descriptors that require max descriptor length */ + while (length_left > TI_AM654_ADMA2_DESC_LENGTH_MAX) { + data->descs[i++] = + ti_am654_create_descriptor(address, TI_AM654_ADMA2_DESC_LENGTH_MAX, false); + + length_left -= TI_AM654_ADMA2_DESC_LENGTH_MAX; + address += TI_AM654_ADMA2_DESC_LENGTH_MAX; + } + + /* if not cache aligned at the end, separate buffer after last aligned descriptor for rx */ + if ((!is_write) && end_residual_bytes != 0) { + if (end_residual_bytes < length_left) { + /* last descriptor with cache aligned data */ + data->descs[i++] = ti_am654_create_descriptor( + address, length_left - end_residual_bytes, false); + } + + /* invalidate if read */ + sys_cache_data_invd_range(data->residual_end, sizeof(data->residual_end)); + address = (uintptr_t)data->residual_end; + length_left = end_residual_bytes; + } + + /* last descriptor */ + data->descs[i++] = ti_am654_create_descriptor(address, length_left, true); + +start_adma: + if (IS_ENABLED(CONFIG_CACHE_MANAGEMENT) && IS_ENABLED(CONFIG_DCACHE)) { + /* flush the descriptors */ + sys_cache_data_flush_range(data->descs, sizeof(data->descs[0]) * i); + + if (is_write) { + sys_cache_data_flush_range(dat->data, dat->blocks * dat->block_size); + } else { + uintptr_t aligned_region = (uintptr_t)dat->data + start_residual_bytes; + size_t aligned_len = (dat->blocks * dat->block_size) - + start_residual_bytes - end_residual_bytes; + + /* invd the aligned region, the residual buffers are already invalidated */ + sys_cache_data_invd_range((void *)aligned_region, aligned_len); + } + } + + /* write descriptor address */ + hc_regs->ADMA_SYS_ADDRESS = (uintptr_t)data->descs; + + return 0; +} + +#else /* !CONFIG_SDHC_TI_AM654_ENABLE_ADMA */ + +static int ti_am654_request_data_write(const struct device *dev, struct sdhc_data *dat) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + struct ti_am654_data *data = DEV_DATA(dev); + uint32_t *data_32; + uint8_t *data_8 = dat->data; + uint32_t block_cnt = dat->blocks; + uint32_t events; + + dat->bytes_xfered = 0; + + while (block_cnt > 0) { + data_32 = (uint32_t *)(data_8 + dat->bytes_xfered); + + events = k_event_wait(&data->irq_event, TI_AM654_NORMAL_INTR_BUF_WR_READY, false, + ti_am654_timeout_from_msec(dat->timeout_ms)); + k_event_clear(&data->irq_event, TI_AM654_NORMAL_INTR_BUF_WR_READY); + + if ((events & TI_AM654_NORMAL_INTR_BUF_WR_READY) == 0) { + LOG_ERR("data port is not ready for writing"); + return -ETIMEDOUT; + } + + for (int i = 0; i < DIV_ROUND_UP(dat->block_size, 4); i++) { + hc_regs->DATA_PORT = *(data_32++); + } + + dat->bytes_xfered += dat->block_size; + block_cnt--; + }; + + return 0; +} + +static int ti_am654_request_data_read(const struct device *dev, struct sdhc_data *dat) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + struct ti_am654_data *data = DEV_DATA(dev); + uint32_t *data_32; + uint8_t *data_8 = dat->data; + uint32_t block_cnt = dat->blocks; + uint32_t events; + + dat->bytes_xfered = 0; + + while (block_cnt > 0) { + data_32 = (uint32_t *)(data_8 + dat->bytes_xfered); + + events = k_event_wait(&data->irq_event, TI_AM654_NORMAL_INTR_BUF_RD_READY, false, + ti_am654_timeout_from_msec(dat->timeout_ms)); + k_event_clear(&data->irq_event, TI_AM654_NORMAL_INTR_BUF_RD_READY); + if ((events & TI_AM654_NORMAL_INTR_BUF_RD_READY) == 0) { + LOG_ERR("data port is not ready for reading"); + return -ETIMEDOUT; + } + + for (int i = 0; i < DIV_ROUND_UP(dat->block_size, 4); i++) { + *(data_32++) = hc_regs->DATA_PORT; + } + + dat->bytes_xfered += dat->block_size; + block_cnt--; + }; + + return 0; +} +#endif /* CONFIG_SDHC_TI_AM654_ENABLE_ADMA */ + +static int ti_am654_request_data_setup(const struct device *dev, struct sdhc_data *dat, + bool is_write) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + uint16_t transfer_mode = 0; + + hc_regs->BLOCK_SIZE = FIELD_PREP(TI_AM654_BLOCK_SIZE_XFER_BLK_SIZE, dat->block_size); + +#ifdef CONFIG_SDHC_TI_AM654_ENABLE_ADMA + transfer_mode |= TI_AM654_TRANSFER_MODE_DMA_ENA; + int rv = ti_am654_request_data_setup_adma(dev, dat, is_write); + + if (rv != 0) { + return rv; + } +#endif /* CONFIG_SDHC_TI_AM654_ENABLE_ADMA */ + + if (!is_write) { + transfer_mode |= TI_AM654_TRANSFER_MODE_DATA_XFER_DIR; + } + + if (dat->blocks > 1) { + transfer_mode |= TI_AM654_TRANSFER_MODE_BLK_CNT_ENA; + transfer_mode |= TI_AM654_TRANSFER_MODE_MULTI_BLK_SEL; + hc_regs->SYS_ADDR = dat->blocks; /* 32 bit block count in ver4 */ + + /* mandatory for sdr104 */ + if (IS_ENABLED(CONFIG_SDHC_TI_AM654_ENABLE_AUTO_STOP) && + IS_ENABLED(CONFIG_SDHC_TI_AM654_ENABLE_ADMA) && + DEV_DATA(dev)->ios.timing == SDHC_TIMING_SDR104) { + transfer_mode |= FIELD_PREP(TI_AM654_TRANSFER_MODE_AUTO_CMD_ENA, + TI_AM654_TRANSFER_MODE_AUTO_CMD_ENA_VAL_CMD23); + } else { + transfer_mode |= FIELD_PREP(TI_AM654_TRANSFER_MODE_AUTO_CMD_ENA, + TI_AM654_TRANSFER_MODE_AUTO_CMD_ENA_VAL_CMD12); + } + } + + hc_regs->TRANSFER_MODE = transfer_mode; + + return 0; +} + +static int ti_am654_wait_for_dat0_high(const struct device *dev) +{ + int retries = TI_AM654_REG_POLL_RETRIES; + + while ((DEV_HC_REGS(dev)->PRESENTSTATE & TI_AM654_PRESENTSTATE_SDIF_DAT0IN) == 0) { + if (retries-- == 0) { + return -ETIMEDOUT; + } + k_usleep(TI_AM654_REG_POLL_TIME_BETWEEN_RETRIES_US); + } + + return 0; +} + +static void ti_am654_init_cmd_cfg(struct ti_am654_cmd_cfg *cfg, struct sdhc_command *cmd, + struct sdhc_data *dat) +{ + cfg->data_present = dat != NULL; + + switch (cmd->response_type & SDHC_NATIVE_RESPONSE_MASK) { + case SD_RSP_TYPE_NONE: + cfg->resp_type = TI_AM654_COMMAND_RESP_TYPE_SEL_VAL_NONE; + cfg->crc_chk = false; + cfg->idx_chk = false; + break; + case SD_RSP_TYPE_R2: + cfg->resp_type = TI_AM654_COMMAND_RESP_TYPE_SEL_VAL_LEN_136; + cfg->crc_chk = true; + cfg->idx_chk = false; + break; + case SD_RSP_TYPE_R3: + case SD_RSP_TYPE_R4: + cfg->resp_type = TI_AM654_COMMAND_RESP_TYPE_SEL_VAL_LEN_48; + cfg->crc_chk = false; + cfg->idx_chk = false; + break; + case SD_RSP_TYPE_R1: + case SD_RSP_TYPE_R6: + case SD_RSP_TYPE_R5: + case SD_RSP_TYPE_R7: + cfg->resp_type = TI_AM654_COMMAND_RESP_TYPE_SEL_VAL_LEN_48; + cfg->crc_chk = true; + cfg->idx_chk = true; + break; + case SD_RSP_TYPE_R1b: + case SD_RSP_TYPE_R5b: + cfg->resp_type = TI_AM654_COMMAND_RESP_TYPE_SEL_VAL_LEN_48_BUSY; + cfg->crc_chk = true; + cfg->idx_chk = true; + break; + default: + LOG_ERR("invalid response type"); + } +} + +#ifndef CONFIG_SDHC_TI_AM654_ENABLE_AUTO_STOP +static int ti_am654_request_stop_transmission(const struct device *dev) +{ + int rv; + struct sdhc_command stop_cmd = { + .opcode = SD_STOP_TRANSMISSION, + .arg = 0, + .response_type = SD_RSP_TYPE_NONE, + .retries = 0, + .timeout_ms = 1000, + }; + + rv = ti_am654_request(dev, &stop_cmd, NULL); + if (rv != 0) { + LOG_ERR("failed to stop transmission"); + } + + return rv; +} +#endif /* !CONFIG_SDHC_TI_AM654_ENABLE_AUTO_STOP */ + +static ALWAYS_INLINE bool ti_am654_is_cmd_write(uint32_t opcode) +{ + return opcode == SD_WRITE_SINGLE_BLOCK || opcode == SD_WRITE_MULTIPLE_BLOCK; +} + +static int ti_am654_request(const struct device *dev, struct sdhc_command *cmd, + struct sdhc_data *dat) +{ + struct ti_am654_data *data = DEV_DATA(dev); + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + bool is_write = ti_am654_is_cmd_write(cmd->opcode); + struct ti_am654_cmd_cfg cfg; + int rv; + + k_event_clear(&data->irq_event, GENMASK(31, 0)); + ti_am654_init_cmd_cfg(&cfg, cmd, dat); + + rv = WAIT_FOR(((hc_regs->PRESENTSTATE & TI_AM654_PRESENTSTATE_INHIBIT_CMD) == 0), + cmd->timeout_ms * 1000, NULL); + if (rv == false) { + LOG_ERR("timed out waiting for command line to be idle"); + return -ETIMEDOUT; + } + + if (cfg.data_present) { + rv = ti_am654_request_data_setup(dev, dat, is_write); + if (rv != 0) { + return rv; + } + + rv = WAIT_FOR(((hc_regs->PRESENTSTATE & TI_AM654_PRESENTSTATE_INHIBIT_DAT) == 0), + dat->timeout_ms * 1000, NULL); + if (rv == false) { + LOG_ERR("timed out waiting for data line to be idle"); + return -ETIMEDOUT; + } + } + + rv = ti_am654_request_cmd_send(dev, cmd, &cfg); + if (rv != 0) { + return rv; + } + + if (cfg.data_present) { + uint32_t events; + +#ifndef CONFIG_SDHC_TI_AM654_ENABLE_ADMA + if (is_write) { + rv = ti_am654_request_data_write(dev, dat); + } else { + rv = ti_am654_request_data_read(dev, dat); + } + + if (rv != 0) { + ti_am654_reset(dev, TI_AM654_RESET_TYPE_CMD | TI_AM654_RESET_TYPE_DAT); + return rv; + } +#endif /* !CONFIG_SDHC_TI_AM654_ENABLE_ADMA */ + + events = k_event_wait(&data->irq_event, + TI_AM654_NORMAL_INTR_XFER_COMPLETE | + TI_AM654_K_EVENT_ALL_ERRORS, + false, ti_am654_timeout_from_msec(dat->timeout_ms)); + + if (events & TI_AM654_K_EVENT_ALL_ERRORS) { + /* any error */ + LOG_DBG("Xfer Error Status: 0x%x", events >> 16); + ti_am654_reset(dev, TI_AM654_RESET_TYPE_CMD | TI_AM654_RESET_TYPE_DAT); + return -EIO; + + } else if (events & TI_AM654_NORMAL_INTR_XFER_COMPLETE) { + /* xfer completed successfully */ +#ifndef CONFIG_SDHC_TI_AM654_ENABLE_AUTO_STOP + if (dat->blocks > 1) { + rv = ti_am654_request_stop_transmission(dev); + if (rv != 0) { + ti_am654_reset(dev, TI_AM654_RESET_TYPE_CMD | + TI_AM654_RESET_TYPE_DAT); + return rv; + } + } +#endif /* !CONFIG_SDHC_TI_AM654_ENABLE_AUTO_STOP */ + +#ifdef CONFIG_SDHC_TI_AM654_ENABLE_ADMA + if (!is_write) { + ti_am654_writeback_residuals(dev, dat); + } +#endif /* CONFIG_SDHC_TI_AM654_ENABLE_ADMA */ + + if (cfg.resp_type == TI_AM654_COMMAND_RESP_TYPE_SEL_VAL_LEN_48_BUSY) { + rv = ti_am654_wait_for_dat0_high(dev); + + if (rv == -ETIMEDOUT) { + LOG_ERR("Timed out while waiting for DAT0 to go high"); + ti_am654_reset(dev, TI_AM654_RESET_TYPE_CMD | + TI_AM654_RESET_TYPE_DAT); + return -EIO; + } + } + + return 0; + + /* event timed out */ + } else { + return -ETIMEDOUT; + } + } + + return 0; +} + +static int ti_am654_wait_for_internal_clock_stable(const struct device *dev) +{ + int retries = TI_AM654_REG_POLL_RETRIES; + + while ((DEV_HC_REGS(dev)->CLOCK_CONTROL & TI_AM654_CLOCK_CONTROL_INT_CLK_STABLE) == 0) { + if (retries-- == 0) { + return -ETIMEDOUT; + } + k_usleep(TI_AM654_REG_POLL_TIME_BETWEEN_RETRIES_US); + } + + return 0; +} + +static int ti_am654_configure_clock(const struct device *dev, enum sdhc_clock_speed clock) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + struct ti_am654_data *data = DEV_DATA(dev); + uint32_t multiplier = data->props.host_caps.clk_multiplier; + uint32_t base = MHZ(data->props.host_caps.sd_base_clk); + bool prog_clk_mode = false; + uint16_t frqsel = 0; + uint16_t divisor = 0; + int rv; + + /* disable DLL for now */ + DEV_SS_REGS(dev)->PHY_CTRL_1 &= ~TI_AM654_PHY_CTRL_1_ENDLL; + + hc_regs->CLOCK_CONTROL = 0; + + if (clock == 0) { + return 0; + } + + /* Programmable Clock Mode */ + if (multiplier != 0) { + for (frqsel = 0; frqsel <= TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL_VAL_MAX; frqsel++) { + divisor = frqsel + 1; + if ((base * multiplier) / divisor <= clock) { + prog_clk_mode = true; + break; + } + } + /* 10-bit Divided Clock Mode */ + } else if (base <= clock) { + frqsel = 0; + divisor = 1; + } else { + for (frqsel = 1; frqsel <= TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL_VAL_MAX; frqsel++) { + divisor = frqsel << 1; + if (base / divisor <= clock) { + break; + } + } + + if (frqsel > TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL_VAL_MAX) { + LOG_ERR("Configured clock speed %uHz is too low", clock); + return -EINVAL; + } + } + + LOG_DBG("clock divisor: %u, frqsel: %u", divisor, frqsel); + + hc_regs->CLOCK_CONTROL = + /* frqsel lo bits */ + FIELD_PREP(TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL, + FIELD_GET(TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL_VAL_MASK_LO, frqsel)) | + /* freqsel hi bits */ + FIELD_PREP(TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL_UPBITS, + FIELD_GET(TI_AM654_CLOCK_CONTROL_SDCLK_FRQSEL_VAL_MASK_HI, frqsel)) | + /* programmable clock mode */ + FIELD_PREP(TI_AM654_CLOCK_CONTROL_CLKGEN_SEL, prog_clk_mode) | + TI_AM654_CLOCK_CONTROL_INT_CLK_ENA; + + /* wait for internal clock to become stable */ + rv = ti_am654_wait_for_internal_clock_stable(dev); + if (rv == -ETIMEDOUT) { + LOG_ERR("timed out while waiting for internal clock to become stable"); + return -EIO; + } + + /* enable PLL for SD */ + if (!DEV_CFG(dev)->is_embedded) { + hc_regs->CLOCK_CONTROL |= TI_AM654_CLOCK_CONTROL_PLL_ENA; + + /* wait for internal clock to become stable */ + rv = ti_am654_wait_for_internal_clock_stable(dev); + if (rv == -ETIMEDOUT) { + LOG_ERR("timed out while waiting for internal clock to become " + "stable"); + return -EIO; + } + } + + hc_regs->CLOCK_CONTROL |= TI_AM654_CLOCK_CONTROL_SD_CLK_ENA; + + return 0; +} + +static int ti_am654_configure_delay_locked_loop(const struct device *dev, + enum sdhc_timing_mode mode, + enum sdhc_clock_speed clock) +{ + struct ti_am654_ss_regs *ss_regs = DEV_SS_REGS(dev); + const struct ti_am654_config *config = DEV_CFG(dev); + uint32_t phy_ctrl_5; + uint32_t phy_ctrl_1; + uint8_t impedance_val; + int retries; + + /* read phy_ctrl5 */ + phy_ctrl_5 = ss_regs->PHY_CTRL_5; + + /* modify phy_ctrl5 */ + if (IS_ENABLED(CONFIG_SDHC_TI_AM654_LEGACY_PHY)) { + switch (clock) { + case MHZ(200): + phy_ctrl_5 &= ~TI_AM654_PHY_CTRL_5_FRQSEL100; + phy_ctrl_5 &= ~TI_AM654_PHY_CTRL_5_FRQSEL50; + break; + case MHZ(100): + phy_ctrl_5 |= TI_AM654_PHY_CTRL_5_FRQSEL100; + phy_ctrl_5 &= ~TI_AM654_PHY_CTRL_5_FRQSEL50; + break; + default: + phy_ctrl_5 &= ~TI_AM654_PHY_CTRL_5_FRQSEL100; + phy_ctrl_5 |= TI_AM654_PHY_CTRL_5_FRQSEL50; + } + } else { + uint8_t frqsel; + + if (clock <= MHZ(200) && clock > MHZ(170)) { + frqsel = TI_AM654_PHY_CTRL_5_FRQSEL_VAL_200_170_MHZ; + } else if (clock <= MHZ(170) && clock > MHZ(140)) { + frqsel = TI_AM654_PHY_CTRL_5_FRQSEL_VAL_170_140_MHZ; + } else if (clock <= MHZ(140) && clock > MHZ(110)) { + frqsel = TI_AM654_PHY_CTRL_5_FRQSEL_VAL_140_110_MHZ; + } else if (clock <= MHZ(110) && clock > MHZ(80)) { + frqsel = TI_AM654_PHY_CTRL_5_FRQSEL_VAL_110_80_MHZ; + } else { + frqsel = TI_AM654_PHY_CTRL_5_FRQSEL_VAL_80_50_MHZ; + } + phy_ctrl_5 &= ~TI_AM654_PHY_CTRL_5_FRQSEL; + phy_ctrl_5 |= FIELD_PREP(TI_AM654_PHY_CTRL_5_FRQSEL, frqsel); + } + + switch (config->drive_impedance) { + case 33: + impedance_val = TI_AM654_PHY_CTRL_1_DR_TY_VAL_33_OHMS; + break; + case 40: + impedance_val = TI_AM654_PHY_CTRL_1_DR_TY_VAL_40_OHMS; + break; + case 50: + impedance_val = TI_AM654_PHY_CTRL_1_DR_TY_VAL_50_OHMS; + break; + case 66: + impedance_val = TI_AM654_PHY_CTRL_1_DR_TY_VAL_66_OHMS; + break; + case 100: + impedance_val = TI_AM654_PHY_CTRL_1_DR_TY_VAL_100_OHMS; + break; + default: + LOG_ERR("invalid impedance"); + return -EINVAL; + } + + /* write phy_ctrl5 */ + ss_regs->PHY_CTRL_5 = phy_ctrl_5; + + /* read phy_ctrl1 */ + phy_ctrl_1 = ss_regs->PHY_CTRL_1; + + /* modify phy_ctrl1 */ + phy_ctrl_1 &= ~(TI_AM654_PHY_CTRL_1_DR_TY | TI_AM654_PHY_CTRL_1_DLL_TRM_ICP); + phy_ctrl_1 |= FIELD_PREP(TI_AM654_PHY_CTRL_1_DR_TY, impedance_val) | + FIELD_PREP(TI_AM654_PHY_CTRL_1_DLL_TRM_ICP, config->current_trim) | + TI_AM654_PHY_CTRL_1_ENDLL; + + /* write phy_ctrl1 */ + ss_regs->PHY_CTRL_1 = phy_ctrl_1; + + /* poll ready state */ + retries = TI_AM654_REG_POLL_RETRIES; + while ((ss_regs->PHY_STAT_1 & TI_AM654_PHY_STAT_1_DLLRDY) == 0) { + if (retries-- == 0) { + LOG_ERR("Timed out while waiting for DLL to be ready"); + return -ETIMEDOUT; + } + k_usleep(TI_AM654_REG_POLL_TIME_BETWEEN_RETRIES_US); + } + + return 0; +} + +static void ti_am654_configure_delay_chain(const struct device *dev, enum sdhc_timing_mode mode) +{ + struct ti_am654_ss_regs *ss_regs = DEV_SS_REGS(dev); + struct ti_am654_data *data = DEV_DATA(dev); + struct ti_am654_tap_delay_config delay_config = data->delay_config[mode]; + uint32_t phy_ctrl_5; + + /* read */ + phy_ctrl_5 = ss_regs->PHY_CTRL_5; + + /* modify */ + if (delay_config.itap_delay_enable) { + phy_ctrl_5 |= TI_AM654_PHY_CTRL_5_SETDLYRXCLK; + } else { + phy_ctrl_5 &= ~TI_AM654_PHY_CTRL_5_SETDLYRXCLK; + } + + if (delay_config.otap_delay_enable) { + phy_ctrl_5 |= TI_AM654_PHY_CTRL_5_SETDLYTXCLK; + } else { + phy_ctrl_5 &= ~TI_AM654_PHY_CTRL_5_SETDLYTXCLK; + } + + /* write */ + ss_regs->PHY_CTRL_5 = phy_ctrl_5; +} + +static void ti_am654_configure_tap_delays(const struct device *dev, + const struct ti_am654_tap_delay_config *delay_config) +{ + struct ti_am654_ss_regs *ss_regs = DEV_SS_REGS(dev); + uint32_t phy_ctrl_4; + + /* read phy_ctrl4 */ + phy_ctrl_4 = ss_regs->PHY_CTRL_4; + + /* modify phy_ctrl4 */ + phy_ctrl_4 &= ~(TI_AM654_PHY_CTRL_4_ITAPDLYENA | TI_AM654_PHY_CTRL_4_ITAPDLYSEL | + TI_AM654_PHY_CTRL_4_OTAPDLYENA | TI_AM654_PHY_CTRL_4_OTAPDLYSEL); + phy_ctrl_4 |= FIELD_PREP(TI_AM654_PHY_CTRL_4_ITAPDLYENA, delay_config->itap_delay_enable) | + FIELD_PREP(TI_AM654_PHY_CTRL_4_ITAPDLYSEL, delay_config->itap_delay_value) | + FIELD_PREP(TI_AM654_PHY_CTRL_4_OTAPDLYENA, delay_config->otap_delay_enable) | + FIELD_PREP(TI_AM654_PHY_CTRL_4_OTAPDLYSEL, delay_config->otap_delay_value); + + /* write phy_ctrl4 */ + ss_regs->PHY_CTRL_4 |= TI_AM654_PHY_CTRL_4_ITAPCHGWIN; + ss_regs->PHY_CTRL_4 = phy_ctrl_4; + ss_regs->PHY_CTRL_4 &= ~TI_AM654_PHY_CTRL_4_ITAPCHGWIN; +} + +static int ti_am654_configure_timing_has_dll(const struct device *dev, enum sdhc_timing_mode mode, + enum sdhc_clock_speed clock) +{ + struct ti_am654_ss_regs *ss_regs = DEV_SS_REGS(dev); + const struct ti_am654_config *config = DEV_CFG(dev); + struct ti_am654_data *data = DEV_DATA(dev); + uint32_t phy_ctrl_4; + uint32_t phy_ctrl_5; + int rv; + + /* configure itap and otap delay */ + ti_am654_configure_tap_delays(dev, &data->delay_config[mode]); + + /* read phy_ctrl4 and phy_ctrl5 */ + phy_ctrl_4 = ss_regs->PHY_CTRL_4; + phy_ctrl_5 = ss_regs->PHY_CTRL_5; + + /* modify phy_ctrl4 */ + if (mode == SDHC_TIMING_HS400) { + uint32_t strobe_sel_mask; + + if (IS_ENABLED(CONFIG_SDHC_TI_AM654_LEGACY_PHY)) { + strobe_sel_mask = TI_AM654_PHY_CTRL_4_STRBSEL_4BIT; + } else { + strobe_sel_mask = TI_AM654_PHY_CTRL_4_STRBSEL; + } + + phy_ctrl_4 &= ~strobe_sel_mask; + phy_ctrl_4 |= FIELD_PREP(strobe_sel_mask, config->strobe_sel); + } + + /* modify phy_ctrl5 */ + phy_ctrl_5 &= ~TI_AM654_PHY_CTRL_5_CLKBUFSEL; + phy_ctrl_5 |= FIELD_PREP(TI_AM654_PHY_CTRL_5_CLKBUFSEL, config->clkbuf_sel); + + /* write phy_ctrl4 and phy_ctrl5 */ + ss_regs->PHY_CTRL_4 = phy_ctrl_4; + ss_regs->PHY_CTRL_5 = phy_ctrl_5; + + switch (mode) { + case SDHC_TIMING_LEGACY: + case SDHC_TIMING_HS: + case SDHC_TIMING_SDR12: + case SDHC_TIMING_SDR25: { + ti_am654_configure_delay_chain(dev, mode); + break; + } + case SDHC_TIMING_SDR50: + case SDHC_TIMING_SDR104: + case SDHC_TIMING_DDR50: + case SDHC_TIMING_DDR52: + case SDHC_TIMING_HS200: + case SDHC_TIMING_HS400: { + rv = ti_am654_configure_delay_locked_loop(dev, mode, clock); + if (rv != 0) { + return rv; + } + break; + } + default: { + LOG_ERR("invalid tuning mode"); + return -EINVAL; + } + } + + return 0; +} + +static int ti_am654_configure_timing_non_dll(const struct device *dev, enum sdhc_timing_mode mode) +{ + struct ti_am654_ss_regs *ss_regs = DEV_SS_REGS(dev); + const struct ti_am654_config *config = DEV_CFG(dev); + struct ti_am654_data *data = DEV_DATA(dev); + uint32_t phy_ctrl_5; + + /* configure itap and otap delay */ + ti_am654_configure_tap_delays(dev, &data->delay_config[mode]); + + /* read phy_ctrl5 */ + phy_ctrl_5 = ss_regs->PHY_CTRL_5; + + /* modify phy_ctrl5 */ + phy_ctrl_5 &= ~TI_AM654_PHY_CTRL_5_CLKBUFSEL; + phy_ctrl_5 |= FIELD_PREP(TI_AM654_PHY_CTRL_5_CLKBUFSEL, config->clkbuf_sel); + + /* write phy_ctrl5 */ + ss_regs->PHY_CTRL_5 = phy_ctrl_5; + + return 0; +} + +static int ti_am654_configure_timing(const struct device *dev, enum sdhc_timing_mode mode, + enum sdhc_clock_speed clock) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + uint8_t uhs_mode = 0; + + if (clock == 0) { + return 0; + } + + switch (mode) { + case SDHC_TIMING_LEGACY: + case SDHC_TIMING_HS: + break; + case SDHC_TIMING_SDR12: + uhs_mode = TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_SDR12; + break; + case SDHC_TIMING_SDR25: + uhs_mode = TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_SDR25; + break; + case SDHC_TIMING_SDR50: + uhs_mode = TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_SDR50; + break; + case SDHC_TIMING_HS200: + case SDHC_TIMING_SDR104: + uhs_mode = TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_SDR104; + break; + case SDHC_TIMING_DDR50: + case SDHC_TIMING_DDR52: + uhs_mode = TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_DDR50; + break; + case SDHC_TIMING_HS400: + uhs_mode = TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT_VAL_HS400; + break; + default: + LOG_ERR("invalid tuning mode"); + return -EINVAL; + } + + hc_regs->HOST_CONTROL2 &= ~TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT; + if (mode >= SDHC_TIMING_SDR12) { + hc_regs->HOST_CONTROL2 |= + FIELD_PREP(TI_AM654_HOST_CONTROL2_UHS_MODE_SELECT, uhs_mode); + hc_regs->HOST_CONTROL1 |= TI_AM654_HOST_CONTROL1_HIGH_SPEED_ENA; + } else { + hc_regs->HOST_CONTROL1 &= ~TI_AM654_HOST_CONTROL1_HIGH_SPEED_ENA; + } + + /* configure timing mode */ + if (DEV_CFG(dev)->dll_present) { + return ti_am654_configure_timing_has_dll(dev, mode, clock); + } + return ti_am654_configure_timing_non_dll(dev, mode); +} + +static void ti_am654_configure_bus_width(const struct device *dev, enum sdhc_bus_width width) +{ + uint8_t host_control1 = DEV_HC_REGS(dev)->HOST_CONTROL1; + + switch (width) { + case SDHC_BUS_WIDTH1BIT: + host_control1 &= ~TI_AM654_HOST_CONTROL1_EXT_DATA_WIDTH; + host_control1 &= ~TI_AM654_HOST_CONTROL1_DATA_WIDTH; + break; + case SDHC_BUS_WIDTH4BIT: + host_control1 &= ~TI_AM654_HOST_CONTROL1_EXT_DATA_WIDTH; + host_control1 |= TI_AM654_HOST_CONTROL1_DATA_WIDTH; + break; + case SDHC_BUS_WIDTH8BIT: + host_control1 |= TI_AM654_HOST_CONTROL1_EXT_DATA_WIDTH; + break; + default: + LOG_ERR("invalid bus width"); + } + + DEV_HC_REGS(dev)->HOST_CONTROL1 = host_control1; +} + +static int ti_am654_configure_voltage(const struct device *dev, enum sd_voltage voltage, + enum sdhc_timing_mode mode) +{ + const struct ti_am654_config *config = DEV_CFG(dev); + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + uint8_t power_control = hc_regs->POWER_CONTROL; + uint16_t host_control2 = hc_regs->HOST_CONTROL2; + uint32_t uV = 0; + int rv; + + power_control &= ~TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE; + + switch (voltage) { + case SD_VOL_1_8_V: { + uV = 1800000; + + power_control |= FIELD_PREP(TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE, + TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE_VAL_V1P8); + host_control2 |= TI_AM654_HOST_CONTROL2_V1P8_SIGNAL_ENA; + break; + } + case SD_VOL_3_0_V: { + uV = 3000000; + + power_control |= FIELD_PREP(TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE, + TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE_VAL_V3P0); + host_control2 &= ~TI_AM654_HOST_CONTROL2_V1P8_SIGNAL_ENA; + break; + } + case SD_VOL_3_3_V: { + uV = 3300000; + + power_control |= FIELD_PREP(TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE, + TI_AM654_POWER_CONTROL_SD_BUS_VOLTAGE_VAL_V3P3); + host_control2 &= ~TI_AM654_HOST_CONTROL2_V1P8_SIGNAL_ENA; + break; + } + case SD_VOL_1_2_V: { + LOG_ERR("1.2V not supported"); + return -ENOTSUP; + } + default: { + LOG_ERR("invalid bus voltage"); + return -EINVAL; + } + } + + if (config->vqmmc != NULL && regulator_is_supported_voltage(config->vqmmc, uV, uV)) { + rv = regulator_set_voltage(config->vqmmc, uV, uV); + if (rv != 0) { + LOG_ERR("failed to change regulator voltage"); + return rv; + } + } + + if (!config->is_embedded) { + hc_regs->HOST_CONTROL2 = host_control2; + /* wait for signal */ + k_msleep(5); + } + + hc_regs->POWER_CONTROL = power_control; + + return 0; +} + +static int ti_am654_configure_power(const struct device *dev, enum sdhc_power power_mode) +{ + const struct ti_am654_config *config = DEV_CFG(dev); + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + int rv; + + switch (power_mode) { + case SDHC_POWER_ON: { + if (config->vmmc != NULL) { + rv = regulator_enable(config->vmmc); + if (rv != 0) { + LOG_ERR("Failed to enable regulator"); + return rv; + } + } + + /* enable power */ + hc_regs->POWER_CONTROL |= TI_AM654_POWER_CONTROL_SD_BUS_POWER; + + break; + } + + case SDHC_POWER_OFF: { + if (config->vmmc != NULL) { + rv = regulator_disable(config->vmmc); + if (rv != 0) { + LOG_ERR("Failed to disable regulator"); + return rv; + } + } + + /* disable power */ + hc_regs->POWER_CONTROL &= ~TI_AM654_POWER_CONTROL_SD_BUS_POWER; + + break; + } + default: { + LOG_ERR("invalid power mode"); + return -EINVAL; + } + } + + return 0; +} + +static int ti_am654_set_io(const struct device *dev, struct sdhc_io *ios) +{ + struct ti_am654_data *data = DEV_DATA(dev); + int rv; + + LOG_DBG("SDHC I/O: bus width %u, clk %uHz, power %s, voltage %s", ios->bus_width, + ios->clock, ios->power_mode == SDHC_POWER_ON ? "ON" : "OFF", + ios->signal_voltage == SD_VOL_1_8_V ? "1.8V" : "3.3V"); + + if (ios->clock < data->props.f_min || ios->clock > data->props.f_max) { + LOG_ERR("Invalid clock frequency: %uHz", ios->clock); + return -EINVAL; + } + + if (ios->bus_width == SDHC_BUS_WIDTH8BIT && + data->props.host_caps.bus_8_bit_support == false) { + LOG_ERR("Bus width not supported"); + return -ENOTSUP; + } + + if (ios->bus_mode == SDHC_BUSMODE_OPENDRAIN) { + LOG_ERR("Open drain is not supported"); + return -ENOTSUP; + } + + /* configure bus width */ + if (ios->bus_width != data->ios.bus_width) { + ti_am654_configure_bus_width(dev, ios->bus_width); + } + + /* configure voltage */ + if (ios->signal_voltage != data->ios.signal_voltage || ios->timing != data->ios.timing) { + rv = ti_am654_configure_voltage(dev, ios->signal_voltage, ios->timing); + if (rv != 0) { + return rv; + } + } + + /* set clock */ + if (ios->clock != data->ios.clock || ios->clock == 0) { + rv = ti_am654_configure_clock(dev, ios->clock); + if (rv != 0) { + return rv; + } + } + + /* configure timing */ + if (ios->timing != data->ios.timing || ios->clock != data->ios.clock) { + rv = ti_am654_configure_timing(dev, ios->timing, ios->clock); + if (rv != 0) { + return rv; + } + } + + /* configure power */ + if (ios->power_mode != data->ios.power_mode) { + rv = ti_am654_configure_power(dev, ios->power_mode); + if (rv != 0) { + return rv; + } + } + + /* save */ + data->ios = *ios; + + return 0; +} + +static int ti_am654_send_tuning_data(const struct device *dev) +{ + struct ti_am654_data *data = DEV_DATA(dev); + const uint8_t *expected_buf = NULL; + struct sdhc_command cmd; + struct sdhc_data dat; + uint8_t rd_buf[sizeof(ti_am654_tuning_blk_8_bit)] = {0}; + int rv = 0; + + if (DEV_CFG(dev)->is_embedded) { + cmd.opcode = MMC_SEND_TUNING_BLOCK; + } else { + cmd.opcode = SD_SEND_TUNING_BLOCK; + } + cmd.response_type = SD_RSP_TYPE_R1; + cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; + cmd.retries = 0; + cmd.arg = 0; + + if (data->ios.bus_width == SDHC_BUS_WIDTH8BIT) { + expected_buf = ti_am654_tuning_blk_8_bit; + dat.block_size = sizeof(ti_am654_tuning_blk_8_bit); + } else { + expected_buf = ti_am654_tuning_blk_4_bit; + dat.block_size = sizeof(ti_am654_tuning_blk_4_bit); + } + + dat.data = rd_buf; + dat.timeout_ms = CONFIG_SD_DATA_TIMEOUT; + dat.blocks = 1; + + rv = ti_am654_request(dev, &cmd, &dat); + + if (rv != 0) { + return rv; + } + + return memcmp(expected_buf, dat.data, dat.block_size); +} + +static int ti_am654_calculate_itap(struct ti_am654_tuning_window *fail_window, uint8_t num_fails) +{ + struct ti_am654_tuning_window pass_window = {0, 0, 0}; + uint8_t start_fail = 0; + uint8_t end_fail = 0; + uint8_t pass_length = 0; + int prev_fail_end = -1; + + if (num_fails == 0) { + LOG_ERR("no failing region found, retry tuning"); + return -EIO; + } + + if (fail_window->length == TI_AM654_PHY_CTRL_4_ITAPDLYSEL_VAL_MAX + 1) { + LOG_ERR("no passing itapdly found, retry tuning"); + return -EIO; + } + + for (int i = 0; i < num_fails; i++) { + start_fail = fail_window[i].start; + end_fail = fail_window[i].end; + pass_length = start_fail - (prev_fail_end + 1); + + if (pass_length > pass_window.length) { + pass_window.start = prev_fail_end + 1; + pass_window.length = pass_length; + } + prev_fail_end = end_fail; + } + + return (pass_window.start + (pass_window.length >> 1)) % + (TI_AM654_PHY_CTRL_4_ITAPDLYSEL_VAL_MAX + 1); +} + +static int ti_am654_execute_manual_tuning(const struct device *dev) +{ + struct ti_am654_data *data = DEV_DATA(dev); + enum sdhc_timing_mode timing = data->ios.timing; + struct ti_am654_tap_delay_config delay_config = data->delay_config[timing]; + struct ti_am654_tuning_window fail_window[TI_AM654_PHY_CTRL_4_ITAPDLYSEL_VAL_MAX + 1]; + uint8_t fail_idx = 0; + int currPass = 0; + int prevPass = 1; + + memset(fail_window, 0, sizeof(fail_window)); + + /* Try different itap values */ + delay_config.itap_delay_enable = true; + for (int itap = 0; itap <= TI_AM654_PHY_CTRL_4_ITAPDLYSEL_VAL_MAX; itap++) { + delay_config.itap_delay_value = itap; + + /* configure itap value */ + ti_am654_configure_tap_delays(dev, &delay_config); + + /* send tuning block */ + currPass = !ti_am654_send_tuning_data(dev); + + if (!currPass && prevPass) { + fail_window[fail_idx].start = itap; + } + + if (!currPass) { + fail_window[fail_idx].end = itap; + fail_window[fail_idx].length++; + } + + if (currPass && !prevPass) { + fail_idx++; + } + + prevPass = currPass; + } + + if (fail_window[fail_idx].length != 0U) { + fail_idx++; + } + + return ti_am654_calculate_itap(fail_window, fail_idx); +} + +static int ti_am654_execute_tuning(const struct device *dev) +{ + struct ti_am654_data *data = DEV_DATA(dev); + enum sdhc_timing_mode timing = data->ios.timing; + struct ti_am654_tap_delay_config *delay_config = &data->delay_config[timing]; + int rv; + + switch (timing) { + case SDHC_TIMING_SDR104: + case SDHC_TIMING_HS200: + break; + case SDHC_TIMING_SDR50: + if (data->props.host_caps.sdr50_needs_tuning) { + break; + } + default: + LOG_ERR("invalid timing mode for tuning"); + return -ENOTSUP; + } + + for (int i = 0; i < TI_AM654_TUNING_RETRIES; i++) { + rv = ti_am654_execute_manual_tuning(dev); + + if (rv >= 0) { + delay_config->itap_delay_enable = true; + delay_config->itap_delay_value = rv; + + ti_am654_configure_tap_delays(dev, delay_config); + + LOG_DBG("tuned with itap: %u", rv); + + return 0; + } + } + + return rv; +} + +static int ti_am654_get_card_present(const struct device *dev) +{ + return !!(DEV_HC_REGS(dev)->PRESENTSTATE & TI_AM654_PRESENTSTATE_CARD_INSERTED); +} + +static int ti_am654_card_busy(const struct device *dev) +{ + uint32_t presentstate = DEV_HC_REGS(dev)->PRESENTSTATE; + uint32_t lines = TI_AM654_PRESENTSTATE_SDIF_DAT0IN | TI_AM654_PRESENTSTATE_SDIF_DAT1IN | + TI_AM654_PRESENTSTATE_SDIF_DAT2IN | TI_AM654_PRESENTSTATE_SDIF_DAT3IN; + + if (DEV_CFG(dev)->is_embedded) { + lines |= TI_AM654_PRESENTSTATE_SDIF_DAT4IN | TI_AM654_PRESENTSTATE_SDIF_DAT5IN | + TI_AM654_PRESENTSTATE_SDIF_DAT6IN | TI_AM654_PRESENTSTATE_SDIF_DAT7IN; + } + + return !(presentstate & lines); +} + +static int ti_am654_enable_interrupt(const struct device *dev, sdhc_interrupt_cb_t callback, + int sources, void *user_data) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + struct ti_am654_data *data = DEV_DATA(dev); + + data->callback = callback; + data->user_data = user_data; + + if (sources & SDHC_INT_SDIO) { + return -ENOTSUP; + } + + if (sources & SDHC_INT_INSERTED) { + hc_regs->NORMAL_INTR_SIG_ENA |= TI_AM654_NORMAL_INTR_CARD_INSERTION; + hc_regs->NORMAL_INTR_STS_ENA |= TI_AM654_NORMAL_INTR_CARD_INSERTION; + } + + if (sources & SDHC_INT_REMOVED) { + hc_regs->NORMAL_INTR_SIG_ENA |= TI_AM654_NORMAL_INTR_CARD_REMOVAL; + hc_regs->NORMAL_INTR_STS_ENA |= TI_AM654_NORMAL_INTR_CARD_REMOVAL; + } + + return 0; +} + +static int ti_am654_disable_interrupt(const struct device *dev, int sources) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + + if (sources & SDHC_INT_SDIO) { + return -ENOTSUP; + } + + if (sources & SDHC_INT_INSERTED) { + hc_regs->NORMAL_INTR_SIG_ENA &= ~TI_AM654_NORMAL_INTR_CARD_INSERTION; + hc_regs->NORMAL_INTR_STS_ENA &= ~TI_AM654_NORMAL_INTR_CARD_INSERTION; + } + + if (sources & SDHC_INT_REMOVED) { + hc_regs->NORMAL_INTR_SIG_ENA &= ~TI_AM654_NORMAL_INTR_CARD_REMOVAL; + hc_regs->NORMAL_INTR_STS_ENA &= ~TI_AM654_NORMAL_INTR_CARD_REMOVAL; + } + + return 0; +} + +static void ti_am654_init_host_props(const struct device *dev) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + struct ti_am654_data *data = dev->data; + struct sdhc_host_props *props = &data->props; + uint64_t max_current_caps = hc_regs->MAX_CURRENT_CAP; + uint64_t caps = hc_regs->CAPABILITIES; + + /* max current */ + props->max_current_180 = FIELD_GET(TI_AM654_MAX_CURRENT_CAP_VDD1_1P8V, max_current_caps) + << 2; + props->max_current_300 = FIELD_GET(TI_AM654_MAX_CURRENT_CAP_VDD1_3P0V, max_current_caps) + << 2; + props->max_current_330 = FIELD_GET(TI_AM654_MAX_CURRENT_CAP_VDD1_3P3V, max_current_caps) + << 2; + + /* copy capabilities to bitfield struct */ + BUILD_ASSERT(sizeof(props->host_caps) == sizeof(caps), + "SDHCI host capabilities do not fit the register"); + props->host_caps = *(const struct sdhc_host_caps *)(&caps); + + /* extra capabilities */ + if (!(caps & TI_AM654_CAPABILITIES_BUS_HS400_SUPPORT)) { + props->hs400_support = false; + } + props->bus_4_bit_support = true; +} + +static int ti_am654_get_host_props(const struct device *dev, struct sdhc_host_props *props) +{ + struct ti_am654_data *data = dev->data; + + *props = data->props; + + return 0; +} + +static int ti_am654_phy_calib(const struct device *dev) +{ + struct ti_am654_ss_regs *ss_regs = DEV_SS_REGS(dev); + int retries = TI_AM654_REG_POLL_RETRIES; + + ss_regs->PHY_CTRL_1 |= TI_AM654_PHY_CTRL_1_EN_RTRIM; + + while ((ss_regs->PHY_CTRL_1 & TI_AM654_PHY_CTRL_1_EN_RTRIM) == 0) { + if (retries-- == 0) { + LOG_ERR("Timed out while waiting for rtrim enable"); + return -ETIMEDOUT; + } + k_usleep(TI_AM654_REG_POLL_TIME_BETWEEN_RETRIES_US); + } + + retries = TI_AM654_REG_POLL_RETRIES; + ss_regs->PHY_CTRL_1 |= TI_AM654_PHY_CTRL_1_PDB; + + while ((ss_regs->PHY_STAT_1 & TI_AM654_PHY_STAT_1_CALDONE) == 0) { + if (retries-- == 0) { + LOG_ERR("Timed out while waiting for calibration"); + return -ETIMEDOUT; + } + k_usleep(TI_AM654_REG_POLL_TIME_BETWEEN_RETRIES_US); + } + + return 0; +} + +static int ti_am654_init(const struct device *dev) +{ + const struct ti_am654_config *config = DEV_CFG(dev); + struct ti_am654_data *data = DEV_DATA(dev); + struct ti_am654_hc_regs *hc_regs; + struct ti_am654_ss_regs *ss_regs; + uint16_t normal_intr; + uint16_t error_intr; + uint32_t ctl_cfg_2; + int rv; + + DEVICE_MMIO_NAMED_MAP(dev, host, K_MEM_CACHE_NONE); + DEVICE_MMIO_NAMED_MAP(dev, subsys, K_MEM_CACHE_NONE); + + hc_regs = DEV_HC_REGS(dev); + ss_regs = DEV_SS_REGS(dev); + + config->irq_func(dev); + k_event_init(&data->irq_event); + + rv = ti_am654_reset_all(dev); + if (rv != 0) { + LOG_ERR("failed to reset the controller"); + return rv; + } + + ti_am654_init_host_props(dev); + + if (config->dll_present) { + rv = ti_am654_phy_calib(dev); + if (rv != 0) { + LOG_ERR("failed to calibrate"); + return rv; + } + } else { + ss_regs->PHY_CTRL_1 &= ~TI_AM654_PHY_CTRL_1_IOMUX_ENABLE; + + rv = pinctrl_apply_state(config->pinctrl, PINCTRL_STATE_DEFAULT); + if (rv < 0) { + LOG_ERR("failed to apply pinctrl"); + return rv; + } + } + + /* set slot type */ + ctl_cfg_2 = ss_regs->CTL_CFG_2; + ctl_cfg_2 &= ~TI_AM654_CTL_CFG_2_SLOTTYPE; + ctl_cfg_2 |= FIELD_PREP(TI_AM654_CTL_CFG_2_SLOTTYPE, data->props.host_caps.slot_type); + ss_regs->CTL_CFG_2 = ctl_cfg_2; + + /* enable version 4 */ + hc_regs->HOST_CONTROL2 |= TI_AM654_HOST_CONTROL2_HOST_VER40_ENA; + + /* force card detect if required */ + if (config->fails_without_test_cd) { + hc_regs->HOST_CONTROL1 |= + (TI_AM654_HOST_CONTROL1_CD_TEST_LEVEL | TI_AM654_HOST_CONTROL1_CD_SIG_SEL); + } + + if (IS_ENABLED(CONFIG_SDHC_TI_AM654_ENABLE_ADMA)) { + hc_regs->HOST_CONTROL1 &= ~TI_AM654_HOST_CONTROL1_DMA_SELECT; + hc_regs->HOST_CONTROL1 |= FIELD_PREP(TI_AM654_HOST_CONTROL1_DMA_SELECT, + TI_AM654_HOST_CONTROL1_DMA_SELECT_VAL_ADMA2); + + /* 64 bit addressing and 26 bit length mode */ + hc_regs->HOST_CONTROL2 |= (TI_AM654_HOST_CONTROL2_ADMA2_LEN_MODE | + TI_AM654_HOST_CONTROL2_BIT64_ADDRESSING); + } + + /* enable interrupts */ + normal_intr = TI_AM654_NORMAL_INTR_CMD_COMPLETE | TI_AM654_NORMAL_INTR_XFER_COMPLETE | + TI_AM654_NORMAL_INTR_BUF_RD_READY | TI_AM654_NORMAL_INTR_BUF_WR_READY; + error_intr = TI_AM654_ERROR_INTR_ALL; + + hc_regs->NORMAL_INTR_SIG_ENA |= normal_intr; + hc_regs->NORMAL_INTR_STS_ENA |= normal_intr; + hc_regs->ERROR_INTR_SIG_ENA |= error_intr; + hc_regs->ERROR_INTR_STS_ENA |= error_intr; + + return 0; +} + +static void ti_am654_isr(const struct device *dev) +{ + struct ti_am654_hc_regs *hc_regs = DEV_HC_REGS(dev); + struct ti_am654_data *data = DEV_DATA(dev); + + uint16_t nstatus = hc_regs->NORMAL_INTR_STS; + uint16_t estatus = hc_regs->ERROR_INTR_STS; + + if (estatus) { + hc_regs->ERROR_INTR_STS |= estatus; + k_event_post(&data->irq_event, TI_AM654_K_EVENT_ERRORS(estatus)); + } + + if (nstatus & TI_AM654_NORMAL_INTR_CMD_COMPLETE) { + hc_regs->NORMAL_INTR_STS = TI_AM654_NORMAL_INTR_CMD_COMPLETE; + k_event_post(&data->irq_event, TI_AM654_NORMAL_INTR_CMD_COMPLETE); + } + + if (nstatus & TI_AM654_NORMAL_INTR_XFER_COMPLETE) { + hc_regs->NORMAL_INTR_STS = TI_AM654_NORMAL_INTR_XFER_COMPLETE; + k_event_post(&data->irq_event, TI_AM654_NORMAL_INTR_XFER_COMPLETE); + } + + if (nstatus & TI_AM654_NORMAL_INTR_BUF_WR_READY) { + hc_regs->NORMAL_INTR_STS = TI_AM654_NORMAL_INTR_BUF_WR_READY; + k_event_post(&data->irq_event, TI_AM654_NORMAL_INTR_BUF_WR_READY); + } + + if (nstatus & TI_AM654_NORMAL_INTR_BUF_RD_READY) { + hc_regs->NORMAL_INTR_STS = TI_AM654_NORMAL_INTR_BUF_RD_READY; + k_event_post(&data->irq_event, TI_AM654_NORMAL_INTR_BUF_RD_READY); + } + + if (nstatus & TI_AM654_NORMAL_INTR_CARD_INSERTION) { + hc_regs->NORMAL_INTR_STS |= TI_AM654_NORMAL_INTR_CARD_INSERTION; + + if (data->callback != NULL && ti_am654_get_card_present(dev)) { + data->callback(dev, SDHC_INT_INSERTED, data->user_data); + } + } + + if (nstatus & TI_AM654_NORMAL_INTR_CARD_REMOVAL) { + hc_regs->NORMAL_INTR_STS |= TI_AM654_NORMAL_INTR_CARD_REMOVAL; + + if (data->callback != NULL && !ti_am654_get_card_present(dev)) { + data->callback(dev, SDHC_INT_REMOVED, data->user_data); + } + } +} + +static DEVICE_API(sdhc, ti_am654_api) = { + .reset = ti_am654_reset_all, + .request = ti_am654_request, + .set_io = ti_am654_set_io, + .enable_interrupt = ti_am654_enable_interrupt, + .disable_interrupt = ti_am654_disable_interrupt, + .get_card_present = ti_am654_get_card_present, + .execute_tuning = ti_am654_execute_tuning, + .card_busy = ti_am654_card_busy, + .get_host_props = ti_am654_get_host_props, +}; + +#define TI_AM654_TIMING_DELAY(n, timing) \ + { \ + .itap_delay_enable = DT_INST_NODE_HAS_PROP(n, ti_itap_del_sel_##timing), \ + .itap_delay_value = DT_INST_PROP_OR(n, ti_itap_del_sel_##timing, 0), \ + .otap_delay_enable = DT_INST_NODE_HAS_PROP(n, ti_otap_del_sel_##timing), \ + .otap_delay_value = DT_INST_PROP_OR(n, ti_otap_del_sel_##timing, 0), \ + } + +#define TI_AM654_TIMING_DELAY_LIST(n) \ + { \ + [SDHC_TIMING_LEGACY] = TI_AM654_TIMING_DELAY(n, legacy), \ + [SDHC_TIMING_HS] = TI_AM654_TIMING_DELAY(n, hs), \ + [SDHC_TIMING_SDR12] = TI_AM654_TIMING_DELAY(n, sdr12), \ + [SDHC_TIMING_SDR25] = TI_AM654_TIMING_DELAY(n, sdr25), \ + [SDHC_TIMING_SDR50] = TI_AM654_TIMING_DELAY(n, sdr50), \ + [SDHC_TIMING_SDR104] = TI_AM654_TIMING_DELAY(n, sdr104), \ + [SDHC_TIMING_DDR50] = TI_AM654_TIMING_DELAY(n, ddr50), \ + [SDHC_TIMING_DDR52] = TI_AM654_TIMING_DELAY(n, ddr52), \ + [SDHC_TIMING_HS200] = TI_AM654_TIMING_DELAY(n, hs200), \ + [SDHC_TIMING_HS400] = TI_AM654_TIMING_DELAY(n, hs400), \ + } + +#define TI_AM654_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + static void ti_am654_##n##_irq_func(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), ti_am654_isr, \ + DEVICE_DT_INST_GET(n), DT_INST_IRQ(0, flags)); \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static const struct ti_am654_config ti_am654_##n##_config = { \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(host, DT_DRV_INST(n)), \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(subsys, DT_DRV_INST(n)), \ + .is_embedded = DT_INST_PROP(n, ti_is_embedded), \ + .dll_present = DT_INST_PROP(n, ti_dll_present), \ + .fails_without_test_cd = DT_INST_PROP(n, ti_fails_without_test_cd), \ + .pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .irq_func = ti_am654_##n##_irq_func, \ + .clkbuf_sel = DT_INST_PROP(n, ti_clkbuf_sel), \ + .strobe_sel = DT_INST_PROP_OR(n, ti_strobe_sel, 0), \ + .drive_impedance = DT_INST_PROP_OR(n, ti_driver_strength_ohm, 0), \ + .current_trim = DT_INST_PROP_OR(n, ti_trm_icp, 0), \ + .vmmc = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(DT_DRV_INST(n), vmmc_supply)), \ + .vqmmc = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(DT_DRV_INST(n), vqmmc_supply)), \ + }; \ + \ + static struct ti_am654_data ti_am654_##n##_data = { \ + .delay_config = TI_AM654_TIMING_DELAY_LIST(n), \ + .props = \ + { \ + .f_min = DT_INST_PROP(n, min_bus_freq), \ + .f_max = DT_INST_PROP(n, max_bus_freq), \ + .power_delay = DT_INST_PROP(n, power_delay_ms), \ + .hs200_support = DT_INST_PROP(n, mmc_hs200_1_8v), \ + .hs400_support = DT_INST_PROP(n, mmc_hs400_1_8v), \ + }, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, &ti_am654_init, NULL, &ti_am654_##n##_data, \ + &ti_am654_##n##_config, POST_KERNEL, CONFIG_SDHC_INIT_PRIORITY, \ + &ti_am654_api); + +DT_INST_FOREACH_STATUS_OKAY(TI_AM654_INIT) diff --git a/dts/bindings/sdhc/ti,am654-sdhci.yaml b/dts/bindings/sdhc/ti,am654-sdhci.yaml new file mode 100644 index 000000000000..e8600b6db83a --- /dev/null +++ b/dts/bindings/sdhc/ti,am654-sdhci.yaml @@ -0,0 +1,124 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments +# SPDX-License-Identifier: Apache-2.0 + +description: TI SD/eMMC Host Controller + +compatible: "ti,am654-sdhci" + +include: [sdhc.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + ti,is-embedded: + description: Whether the slot type is embedded or not + type: boolean + + ti,dll-present: + description: Whether PHY requires DLL configuration or not + type: boolean + + ti,fails-without-test-cd: + description: + When present, indicates that the CD line is not connected and the + controller must be put into Test mode to force the Card Detect + signal high by setting the TESTCD bit. + type: boolean + + ti,clkbuf-sel: + description: Clock Delay Buffer Select + type: int + required: true + + ti,strobe-sel: + description: Strobe Select Delay + type: int + + ti,driver-strength-ohm: + description: DLL drive strength in ohms + type: int + enum: + - 33 + - 40 + - 50 + - 66 + - 100 + + ti,trm-icp: + description: DLL trim select + type: int + + ti,itap-del-sel-legacy: + description: Input tap delay for SD/MMC legacy timing + type: int + + ti,otap-del-sel-legacy: + description: Output tap delay for SD/MMC legacy timing + type: int + + ti,itap-del-sel-hs: + description: Input tap delay for SD/MMC High Speed timing + type: int + + ti,otap-del-sel-hs: + description: Output tap delay for SD/MMC High Speed timing + type: int + + ti,itap-del-sel-sdr12: + description: Input tap delay for SD UHS SDR12 timing + type: int + + ti,otap-del-sel-sdr12: + description: Output tap delay for SD UHS SDR12 timing + type: int + + ti,itap-del-sel-sdr25: + description: Input tap delay for SD UHS SDR25 timing + type: int + + ti,otap-del-sel-sdr25: + description: Output tap delay for SD UHS SDR25 timing + type: int + + ti,otap-del-sel-sdr50: + description: Output tap delay for SD UHS SDR50 timing + type: int + + ti,itap-del-sel-ddr50: + description: Input tap delay for SD UHS DDR50 timing + type: int + + ti,otap-del-sel-ddr50: + description: Output tap delay for SD UHS DDR50 timing + type: int + + ti,itap-del-sel-ddr52: + description: Input tap delay for eMMC DDR52 timing + type: int + + ti,otap-del-sel-ddr52: + description: Output tap delay for eMMC DDR52 timing + type: int + + # SDR104 input tap delay is calculated during tuning + ti,otap-del-sel-sdr104: + description: Output tap delay for SD UHS SDR104 timing + type: int + + # HS200 input tap delay is calculated during tuning + ti,otap-del-sel-hs200: + description: Output tap delay for eMMC HS200 timing + type: int + + vmmc-supply: + type: phandle + description: | + Phandle to the regulator that provides the main power supply voltage + to the MMC/SD card. + + vqmmc-supply: + type: phandle + description: | + Phandle to the regulator that provides the I/O signaling voltage + (VDDIO) to the MMC/SD card. From f7b93eb220d71ed7b955f315692601f21a38d559 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Fri, 26 Sep 2025 18:25:33 +0530 Subject: [PATCH 102/143] UPSTREAM-PEND: boards: am243x_evm/am2434/r5f0_0: add SDHC nodes Add SD host controller nodes and also assign them interrupts and power domains. Additionally also setup eMMC and SD card as disks. PR: 97172 Signed-off-by: Amneesh Singh --- .../am243x_evm_am2434_r5f0_0-pinctrl.dtsi | 36 ++++++++++++++++ .../am243x_evm/am243x_evm_am2434_r5f0_0.dts | 32 ++++++++++++++ dts/arm/ti/am64x_r5.dtsi | 10 +++++ dts/vendor/ti/am64x_main.dtsi | 43 +++++++++++++++++++ 4 files changed, 121 insertions(+) diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi index 7c9e757c89d5..9fc3aca3b0e2 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi @@ -120,6 +120,42 @@ main_ospi0_lbclko: ospi0-lbclko { pinmux = ; /* (N21) OSPI0_LBCLKO */ }; + + mmc1_cmd: mmc1_cmd_default { + pinmux = ; /* (J19) MMC1_CMD */ + }; + + mmc1_clk: mmc1_clk_default { + pinmux = ; /* (L20) MMC1_CLK */ + }; + + mmc1_dat0: mmc1_dat0_default { + pinmux = ; /* (K21) MMC1_DAT0 */ + }; + + mmc1_dat1: mmc1_dat1_default { + pinmux = ; /* (L21) MMC1_DAT1 */ + }; + + mmc1_dat2: mmc1_dat2_default { + pinmux = ; /* (K19) MMC1_DAT2 */ + }; + + mmc1_dat3: mmc1_dat3_default { + pinmux = ; /* (K18) MMC1_DAT3 */ + }; + + mmc1_sdcd: mmc1_sdcd_default { + pinmux = ; /* (D19) MMC1_SDCD */ + }; + + mmc1_sdwp: mmc1_sdwp_default { + pinmux = ; /* (C20) MMC1_SDWP */ + }; + + mmc1_clklb: mmc1_clklb_default { + pinmux = ; + }; }; &mcu_pinctrl { diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts index 8ef55c43156e..819bd2f861fa 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts @@ -253,3 +253,35 @@ }; }; }; + +/* eMMC */ +&main_sdhci0 { + status = "okay"; + + mmc { + compatible = "zephyr,mmc-disk"; + bus-width = <8>; + disk-name = "emmc"; + }; +}; + +/* SD */ +&main_sdhci1 { + status = "okay"; + + pinctrl-0 = <&mmc1_cmd + &mmc1_clk + &mmc1_dat0 + &mmc1_dat1 + &mmc1_dat2 + &mmc1_dat2 + &mmc1_dat3 + &mmc1_sdcd + &mmc1_clklb>; + pinctrl-names = "default"; + + sd { + compatible = "zephyr,sdmmc-disk"; + disk-name = "sd"; + }; +}; diff --git a/dts/arm/ti/am64x_r5.dtsi b/dts/arm/ti/am64x_r5.dtsi index 3084c9afa44e..e4b6580861cb 100644 --- a/dts/arm/ti/am64x_r5.dtsi +++ b/dts/arm/ti/am64x_r5.dtsi @@ -237,3 +237,13 @@ interrupts = <0 62 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; interrupt-parent = <&vim>; }; + +&main_sdhci0 { + interrupts = <0 165 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_sdhci1 { + interrupts = <0 166 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; diff --git a/dts/vendor/ti/am64x_main.dtsi b/dts/vendor/ti/am64x_main.dtsi index f4a63ce1e3b4..6855f46355dd 100644 --- a/dts/vendor/ti/am64x_main.dtsi +++ b/dts/vendor/ti/am64x_main.dtsi @@ -399,4 +399,47 @@ cdns,init-delay-ns = <60>; status = "disabled"; }; + + main_sdhci0: mmc@fa10000 { + compatible = "ti,am654-sdhci"; + reg = <0xfa10000 DT_SIZE_K(4)>, <0xfa18000 DT_SIZE_K(1)>; + reg-names = "host", "subsys"; + power-domains = <&mmcsd0_pd>; + max-bus-freq = ; + min-bus-freq = ; + ti,is-embedded; + ti,dll-present; + ti,driver-strength-ohm = <50>; + ti,clkbuf-sel = <0x7>; + ti,trm-icp = <0x2>; + mmc-hs200-1_8v; + ti,otap-del-sel-hs200 = <0x7>; + ti,otap-del-sel-ddr52 = <0x6>; + ti,itap-del-sel-legacy = <0x10>; + ti,itap-del-sel-hs = <0xa>; + ti,itap-del-sel-ddr52 = <0x3>; + status = "disabled"; + }; + + main_sdhci1: mmc@fa00000 { + compatible = "ti,am654-sdhci"; + reg = <0xfa00000 DT_SIZE_K(4)>, <0xfa08000 DT_SIZE_K(1)>; + reg-names = "host", "subsys"; + power-domains = <&mmcsd1_pd>; + max-bus-freq = ; + min-bus-freq = ; + ti,clkbuf-sel = <0x7>; + ti,itap-del-sel-legacy = <0x0>; + ti,itap-del-sel-hs = <0x0>; + ti,itap-del-sel-sdr12 = <0x0>; + ti,itap-del-sel-sdr25 = <0x0>; + ti,otap-del-sel-legacy = <0x0>; + ti,otap-del-sel-hs = <0x0>; + ti,otap-del-sel-sdr12 = <0xf>; + ti,otap-del-sel-sdr25 = <0xf>; + ti,otap-del-sel-sdr50 = <0xc>; + ti,otap-del-sel-sdr104 = <0x6>; + ti,otap-del-sel-ddr50 = <0x9>; + status = "disabled"; + }; }; From 29a5fd431469e9f6b7893151d279ccf53b4317d8 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Fri, 23 Jan 2026 18:55:06 +0530 Subject: [PATCH 103/143] UPSTREAM-PEND: boards: sk_am62/am6234/a53: add SDHC nodes Add SD host controller nodes and also assign them interrupts. Additionally also setup eMMC and SD card as disks, and set up a node for the SDIO instance. PR: 97172 Signed-off-by: Amneesh Singh --- .../ti/sk_am62/sk-am62_am6254_a53-pinctrl.dts | 96 +++++++++++++++++++ boards/ti/sk_am62/sk_am62_am6254_a53.dts | 54 +++++++++++ dts/arm64/ti/ti_am62x_a53.dtsi | 15 +++ dts/vendor/ti/k3-am62-main.dtsi | 56 +++++++++++ 4 files changed, 221 insertions(+) diff --git a/boards/ti/sk_am62/sk-am62_am6254_a53-pinctrl.dts b/boards/ti/sk_am62/sk-am62_am6254_a53-pinctrl.dts index 2bbcb890d717..7052e8195cdf 100644 --- a/boards/ti/sk_am62/sk-am62_am6254_a53-pinctrl.dts +++ b/boards/ti/sk_am62/sk-am62_am6254_a53-pinctrl.dts @@ -12,4 +12,100 @@ uart0_tx_default: uart0_tx_default { pinmux = ; }; + + mmc0_cmd: mmc0_cmd_default { + pinmux = ; /* (Y3) MMC0_CMD */ + }; + + mmc0_clk: mmc0_clk_default { + pinmux = ; /* (AB1) MMC0_CLK */ + }; + + mmc0_dat0: mmc0_dat0_default { + pinmux = ; /* (AA2) MMC0_DAT0 */ + }; + + mmc0_dat1: mmc0_dat1_default { + pinmux = ; /* (AA1) MMC0_DAT1 */ + }; + + mmc0_dat2: mmc0_dat2_default { + pinmux = ; /* (AA3) MMC0_DAT2 */ + }; + + mmc0_dat3: mmc0_dat3_default { + pinmux = ; /* (Y4) MMC0_DAT3 */ + }; + + mmc0_dat4: mmc0_dat4_default { + pinmux = ; /* (AB2) MMC0_DAT4 */ + }; + + mmc0_dat5: mmc0_dat5_default { + pinmux = ; /* (AC1) MMC0_DAT5 */ + }; + + mmc0_dat6: mmc0_dat6_default { + pinmux = ; /* (AD2) MMC0_DAT6 */ + }; + + mmc0_dat7: mmc0_dat7_default { + pinmux = ; /* (AC2) MMC0_DAT7 */ + }; + + mmc1_cmd: mmc1_cmd_default { + pinmux = ; /* (A21) MMC1_CMD */ + }; + + mmc1_clk: mmc1_clk_default { + pinmux = ; /* (B22) MMC1_CLK */ + }; + + mmc1_sdcd: mmc1_sdcd_default { + pinmux = ; /* (D17) MMC1_SDCD */ + }; + + mmc1_dat0: mmc1_dat0_default { + pinmux = ; /* (A22) MMC1_DAT0 */ + }; + + mmc1_dat1: mmc1_dat1_default { + pinmux = ; /* (B21) MMC1_DAT1 */ + }; + + mmc1_dat2: mmc1_dat2_default { + pinmux = ; /* (C21) MMC1_DAT2 */ + }; + + mmc1_dat3: mmc1_dat3_default { + pinmux = ; /* (D22) MMC1_DAT3 */ + }; + + mmc2_cmd: mmc2_cmd_default { + pinmux = ; /* (C24) MMC2_CMD */ + }; + + mmc2_clk: mmc2_clk_default { + pinmux = ; /* (D25) MMC2_CLK */ + }; + + mmc2_clklb: mmc2_clklb_default { + pinmux = ; /* () MMC2_CLKLB */ + }; + + mmc2_dat0: mmc2_dat0_default { + pinmux = ; /* (B24) MMC2_DAT0 */ + }; + + mmc2_dat1: mmc2_dat1_default { + pinmux = ; /* (C25) MMC2_DAT1 */ + }; + + mmc2_dat2: mmc2_dat2_default { + pinmux = ; /* (E23) MMC2_DAT2 */ + }; + + mmc2_dat3: mmc2_dat3_default { + pinmux = ; /* (D24) MMC2_DAT3 */ + }; }; diff --git a/boards/ti/sk_am62/sk_am62_am6254_a53.dts b/boards/ti/sk_am62/sk_am62_am6254_a53.dts index c31554f27d96..f6baa119150f 100644 --- a/boards/ti/sk_am62/sk_am62_am6254_a53.dts +++ b/boards/ti/sk_am62/sk_am62_am6254_a53.dts @@ -49,3 +49,57 @@ pinctrl-names = "default"; status = "okay"; }; + +/* eMMC */ +&main_sdhci0 { + pinctrl-0 = <&mmc0_cmd + &mmc0_clk + &mmc0_dat0 + &mmc0_dat1 + &mmc0_dat2 + &mmc0_dat3 + &mmc0_dat4 + &mmc0_dat5 + &mmc0_dat6 + &mmc0_dat7>; + pinctrl-names = "default"; + status = "okay"; + + mmc { + compatible = "zephyr,mmc-disk"; + bus-width = <8>; + disk-name = "emmc"; + }; +}; + +/* SD */ +&main_sdhci1 { + pinctrl-0 = <&mmc1_cmd + &mmc1_clk + &mmc1_dat0 + &mmc1_dat1 + &mmc1_dat2 + &mmc1_dat3 + &mmc1_sdcd>; + pinctrl-names = "default"; + status = "okay"; + + sd { + compatible = "zephyr,sdmmc-disk"; + disk-name = "sd"; + }; +}; + +/* SDIO */ +&main_sdhci2 { + pinctrl-0 = <&mmc2_cmd + &mmc2_clk + &mmc2_clklb + &mmc2_dat0 + &mmc2_dat1 + &mmc2_dat2 + &mmc2_dat3>; + pinctrl-names = "default"; + ti,fails-without-test-cd; + status = "disabled"; +}; diff --git a/dts/arm64/ti/ti_am62x_a53.dtsi b/dts/arm64/ti/ti_am62x_a53.dtsi index ec0a2b6746e3..8a124e689ec3 100644 --- a/dts/arm64/ti/ti_am62x_a53.dtsi +++ b/dts/arm64/ti/ti_am62x_a53.dtsi @@ -130,3 +130,18 @@ interrupts = ; interrupt-parent = <&gic>; }; + +&main_sdhci0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_sdhci1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_sdhci2 { + interrupts = ; + interrupt-parent = <&gic>; +}; diff --git a/dts/vendor/ti/k3-am62-main.dtsi b/dts/vendor/ti/k3-am62-main.dtsi index cb5ebde7c2d9..17a4f32ff1c3 100644 --- a/dts/vendor/ti/k3-am62-main.dtsi +++ b/dts/vendor/ti/k3-am62-main.dtsi @@ -160,4 +160,60 @@ clock-frequency = ; status = "disabled"; }; + + main_sdhci0: mmc@fa10000 { + compatible = "ti,am654-sdhci"; + reg = <0xfa10000 DT_SIZE_K(4)>, <0xfa18000 DT_SIZE_K(1)>; + reg-names = "host", "subsys"; + max-bus-freq = ; + min-bus-freq = ; + ti,is-embedded; + ti,driver-strength-ohm = <50>; + ti,clkbuf-sel = <0x7>; + mmc-hs200-1_8v; + ti,otap-del-sel-legacy = <0x00>; + ti,otap-del-sel-hs = <0x0>; + ti,otap-del-sel-hs200 = <0x6>; + status = "disabled"; + }; + + main_sdhci1: mmc@fa00000 { + compatible = "ti,am654-sdhci"; + reg = <0xfa00000 DT_SIZE_K(4)>, <0xfa08000 DT_SIZE_K(1)>; + reg-names = "host", "subsys"; + max-bus-freq = ; + min-bus-freq = ; + ti,clkbuf-sel = <0x7>; + ti,otap-del-sel-legacy = <0x0>; + ti,otap-del-sel-hs = <0x0>; + ti,otap-del-sel-sdr12 = <0xf>; + ti,otap-del-sel-sdr25 = <0xf>; + ti,otap-del-sel-sdr50 = <0xc>; + ti,otap-del-sel-ddr50 = <0x9>; + ti,otap-del-sel-sdr104 = <0x6>; + ti,itap-del-sel-legacy = <0x0>; + ti,itap-del-sel-hs = <0x0>; + ti,itap-del-sel-sdr12 = <0x0>; + ti,itap-del-sel-sdr25 = <0x0>; + status = "disabled"; + }; + + main_sdhci2: mmc@fa20000 { + compatible = "ti,am654-sdhci"; + reg = <0xfa20000 DT_SIZE_K(4)>, <0xfa28000 DT_SIZE_K(1)>; + reg-names = "host", "subsys"; + max-bus-freq = ; + min-bus-freq = ; + ti,clkbuf-sel = <0x7>; + ti,otap-del-sel-sdr12 = <0xf>; + ti,otap-del-sel-sdr25 = <0xf>; + ti,otap-del-sel-sdr50 = <0xc>; + ti,otap-del-sel-ddr50 = <0x9>; + ti,otap-del-sel-sdr104 = <0x6>; + ti,itap-del-sel-legacy = <0x0>; + ti,itap-del-sel-hs = <0x0>; + ti,itap-del-sel-sdr12 = <0x0>; + ti,itap-del-sel-sdr25 = <0x0>; + status = "disabled"; + }; }; From 8f0535acb597ab87a4b59aa1edd22971efc75cc1 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Wed, 25 Mar 2026 19:18:19 +0530 Subject: [PATCH 104/143] UPSTREAM-PEND: boards: am62l_evm/am62l3/a53: add SDHC nodes Add SD host controller nodes and also assign them interrupts and power domains. Additionally also setup eMMC and SD card as disks, and set up a node for the SDIO instance. PR: 97172 Signed-off-by: Amneesh Singh --- .../am62l_evm_am62l3_a53-pinctrl.dtsi | 96 +++++++++++++++++++ boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts | 54 +++++++++++ dts/arm64/ti/am62l3_a53.dtsi | 15 +++ dts/vendor/ti/am62l-main.dtsi | 63 ++++++++++++ dts/vendor/ti/am62l_power_domains.dtsi | 18 ++++ 5 files changed, 246 insertions(+) diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi index 61e1a6823ef9..69748730bbce 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi @@ -64,4 +64,100 @@ uart0_tx_default: uart0_tx_default { pinmux = ; /* (C13) UART0_TXD */ }; + + mmc0_cmd: mmc0_cmd_default { + pinmux = ; /* (D2) MMC0_CMD */ + }; + + mmc0_clk: mmc0_clk_default { + pinmux = ; /* (B2) MMC0_CLK */ + }; + + mmc0_dat0: mmc0_dat0_default { + pinmux = ; /* (D3) MMC0_DAT0 */ + }; + + mmc0_dat1: mmc0_dat1_default { + pinmux = ; /* (D4) MMC0_DAT1 */ + }; + + mmc0_dat2: mmc0_dat2_default { + pinmux = ; /* (C1) MMC0_DAT2 */ + }; + + mmc0_dat3: mmc0_dat3_default { + pinmux = ; /* (C2) MMC0_DAT3 */ + }; + + mmc0_dat4: mmc0_dat4_default { + pinmux = ; /* (C4) MMC0_DAT4 */ + }; + + mmc0_dat5: mmc0_dat5_default { + pinmux = ; /* (B3) MMC0_DAT5 */ + }; + + mmc0_dat6: mmc0_dat6_default { + pinmux = ; /* (A3) MMC0_DAT6 */ + }; + + mmc0_dat7: mmc0_dat7_default { + pinmux = ; /* (B4) MMC0_DAT7 */ + }; + + mmc1_cmd: mmc1_cmd_default { + pinmux = ; /* (Y3) MMC1_CMD */ + }; + + mmc1_clk: mmc1_clk_default { + pinmux = ; /* (Y2) MMC1_CLK */ + }; + + mmc1_sdcd: mmc1_sdcd_default { + pinmux = ; /* (B6) MMC1_SDCD */ + }; + + mmc1_dat0: mmc1_dat0_default { + pinmux = ; /* (AA1) MMC1_DAT0 */ + }; + + mmc1_dat1: mmc1_dat1_default { + pinmux = ; /* (Y4) MMC1_DAT1 */ + }; + + mmc1_dat2: mmc1_dat2_default { + pinmux = ; /* (AA2) MMC1_DAT2 */ + }; + + mmc1_dat3: mmc1_dat3_default { + pinmux = ; /* (AB2) MMC1_DAT3 */ + }; + + mmc2_cmd: mmc2_cmd_default { + pinmux = ; /* (U23) MMC2_CMD */ + }; + + mmc2_clk: mmc2_clk_default { + pinmux = ; /* (R23) MMC2_CLK */ + }; + + mmc2_clklb: mmc2_clklb_default { + pinmux = ; /* () MMC2_CLKLB */ + }; + + mmc2_dat0: mmc2_dat0_default { + pinmux = ; /* (U22) MMC2_DAT0 */ + }; + + mmc2_dat1: mmc2_dat1_default { + pinmux = ; /* (T22) MMC2_DAT1 */ + }; + + mmc2_dat2: mmc2_dat2_default { + pinmux = ; /* (T23) MMC2_DAT2 */ + }; + + mmc2_dat3: mmc2_dat3_default { + pinmux = ; /* (R22) MMC2_DAT3 */ + }; }; diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts index 0c540b51321c..3c449a773a10 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts @@ -86,3 +86,57 @@ }; }; }; + +/* eMMC */ +&main_sdhci0 { + pinctrl-0 = <&mmc0_cmd + &mmc0_clk + &mmc0_dat0 + &mmc0_dat1 + &mmc0_dat2 + &mmc0_dat3 + &mmc0_dat4 + &mmc0_dat5 + &mmc0_dat6 + &mmc0_dat7>; + pinctrl-names = "default"; + status = "okay"; + + mmc { + compatible = "zephyr,mmc-disk"; + bus-width = <8>; + disk-name = "emmc"; + }; +}; + +/* SD */ +&main_sdhci1 { + pinctrl-0 = <&mmc1_cmd + &mmc1_clk + &mmc1_dat0 + &mmc1_dat1 + &mmc1_dat2 + &mmc1_dat3 + &mmc1_sdcd>; + pinctrl-names = "default"; + status = "okay"; + + sd { + compatible = "zephyr,sdmmc-disk"; + disk-name = "sd"; + }; +}; + +/* SDIO */ +&main_sdhci2 { + pinctrl-0 = <&mmc2_cmd + &mmc2_clk + &mmc2_clklb + &mmc2_dat0 + &mmc2_dat1 + &mmc2_dat2 + &mmc2_dat3>; + pinctrl-names = "default"; + ti,fails-without-test-cd; + status = "disabled"; +}; diff --git a/dts/arm64/ti/am62l3_a53.dtsi b/dts/arm64/ti/am62l3_a53.dtsi index e72c0581db99..efbf67811b94 100644 --- a/dts/arm64/ti/am62l3_a53.dtsi +++ b/dts/arm64/ti/am62l3_a53.dtsi @@ -171,3 +171,18 @@ interrupts = ; interrupt-parent = <&gic>; }; + +&main_sdhci0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_sdhci1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_sdhci2 { + interrupts = ; + interrupt-parent = <&gic>; +}; diff --git a/dts/vendor/ti/am62l-main.dtsi b/dts/vendor/ti/am62l-main.dtsi index 5a61e5ef6840..46ed533cb686 100644 --- a/dts/vendor/ti/am62l-main.dtsi +++ b/dts/vendor/ti/am62l-main.dtsi @@ -321,4 +321,67 @@ cdns,init-delay-ns = <60>; status = "disabled"; }; + + main_sdhci0: mmc@fa10000 { + compatible = "ti,am654-sdhci"; + reg = <0xfa10000 DT_SIZE_K(4)>, <0xfa18000 DT_SIZE_K(1)>; + reg-names = "host", "subsys"; + power-domains = <&main_mmcsd0_pd>; + max-bus-freq = ; + min-bus-freq = ; + ti,is-embedded; + ti,driver-strength-ohm = <50>; + ti,clkbuf-sel = <0x7>; + mmc-hs200-1_8v; + ti,otap-del-sel-sdr12 = <0xf>; + ti,otap-del-sel-sdr25 = <0xf>; + ti,otap-del-sel-ddr50 = <0x15>; + ti,otap-del-sel-hs200 = <0x6>; + ti,itap-del-sel-legacy = <0x0>; + ti,itap-del-sel-hs = <0x0>; + ti,itap-del-sel-sdr12 = <0x0>; + ti,itap-del-sel-sdr25 = <0x0>; + ti,itap-del-sel-ddr50 = <0x2>; + status = "disabled"; + }; + + main_sdhci1: mmc@fa00000 { + compatible = "ti,am654-sdhci"; + reg = <0xfa00000 DT_SIZE_K(4)>, <0xfa08000 DT_SIZE_K(1)>; + reg-names = "host", "subsys"; + power-domains = <&main_mmcsd1_pd>; + max-bus-freq = ; + min-bus-freq = ; + ti,clkbuf-sel = <0x7>; + ti,otap-del-sel-sdr12 = <0xf>; + ti,otap-del-sel-sdr25 = <0xf>; + ti,otap-del-sel-sdr50 = <0xc>; + ti,otap-del-sel-sdr104 = <0x6>; + ti,otap-del-sel-ddr50 = <0x9>; + ti,itap-del-sel-legacy = <0x0>; + ti,itap-del-sel-hs = <0x0>; + ti,itap-del-sel-sdr12 = <0x0>; + ti,itap-del-sel-sdr25 = <0x0>; + status = "disabled"; + }; + + main_sdhci2: mmc@fa20000 { + compatible = "ti,am654-sdhci"; + reg = <0xfa20000 DT_SIZE_K(4)>, <0xfa28000 DT_SIZE_K(1)>; + reg-names = "host", "subsys"; + power-domains = <&main_mmcsd2_pd>; + max-bus-freq = ; + min-bus-freq = ; + ti,clkbuf-sel = <0x7>; + ti,otap-del-sel-sdr12 = <0xf>; + ti,otap-del-sel-sdr25 = <0xf>; + ti,otap-del-sel-sdr50 = <0xc>; + ti,otap-del-sel-ddr50 = <0x9>; + ti,otap-del-sel-sdr104 = <0x6>; + ti,itap-del-sel-legacy = <0x0>; + ti,itap-del-sel-hs = <0x0>; + ti,itap-del-sel-sdr12 = <0x0>; + ti,itap-del-sel-sdr25 = <0x0>; + status = "disabled"; + }; }; diff --git a/dts/vendor/ti/am62l_power_domains.dtsi b/dts/vendor/ti/am62l_power_domains.dtsi index 47e2b2569ea1..dc390638ebba 100644 --- a/dts/vendor/ti/am62l_power_domains.dtsi +++ b/dts/vendor/ti/am62l_power_domains.dtsi @@ -103,5 +103,23 @@ reg = <56>; #power-domain-cells = <0>; }; + + main_mmcsd0_pd: power-domain@1c { + compatible = "arm,scmi-power-domain"; + reg = <28>; + #power-domain-cells = <0>; + }; + + main_mmcsd1_pd: power-domain@1a { + compatible = "arm,scmi-power-domain"; + reg = <26>; + #power-domain-cells = <0>; + }; + + main_mmcsd2_pd: power-domain@1b { + compatible = "arm,scmi-power-domain"; + reg = <27>; + #power-domain-cells = <0>; + }; }; }; From 13362351cbea6b0c1583d8e52c5c19e57f6a3f4c Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sat, 1 Nov 2025 12:45:28 +0800 Subject: [PATCH 105/143] UPSTREAM: sys: device_mmio: add devcie_map for !DEVICE_MMIO_IS_IN_RAM commit fdca7582708c93c46788f6977ceb19a266918146 upstream. On !MMU && !PCIE && !EXTERNAL_ADDRESS_TRANSLATION platforms, the device_map() isn't defined, thus brings below ramconsole build error: ram_console.c: In function 'ram_console_init': ram_console.c:49:9: error: implicit declaration of function 'device_map' Previously, I solved this issue in a workaround style[1] as below: +#ifndef DEVICE_MMIO_IS_IN_RAM +#define device_map(virt, phys, size, flags) *(virt) = (phys) +#endif /* DEVICE_MMIO_IS_IN_RAM */ But during the PR review progress, pathcing device_mmio.h is suggested. This patch implements that suggestion. Link: https://github.com/zephyrproject-rtos/zephyr/pull/97894 [1] Signed-off-by: Jisheng Zhang --- include/zephyr/sys/device_mmio.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/zephyr/sys/device_mmio.h b/include/zephyr/sys/device_mmio.h index e6971c96ebed..3e1e9b31c76e 100644 --- a/include/zephyr/sys/device_mmio.h +++ b/include/zephyr/sys/device_mmio.h @@ -132,6 +132,15 @@ struct z_device_mmio_rom { .addr = (mm_reg_t)DT_REG_ADDR_BY_NAME_U64(node_id, name) \ } +__boot_func +static inline void device_map(mm_reg_t *virt_addr, uintptr_t phys_addr, + size_t size, uint32_t flags) +{ + ARG_UNUSED(size); + ARG_UNUSED(flags); + *virt_addr = phys_addr; +} + #endif /* DEVICE_MMIO_IS_IN_RAM */ #endif /* !_ASMLANGUAGE */ /** @} */ From 6c82610d6148b58709d40ac406ecbb82b7770137 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Mon, 2 Feb 2026 14:10:41 +0530 Subject: [PATCH 106/143] UPSTREAM: dts: bindings: syscon: introduce ti,control-module commit 7af410d8971b6135e985a0b1e7441732eed7614c upstream. This binding is for TI devices that use control modules for configuring registers and information related to control MMRs, power, reset and clocking. These control modules consist of regions that require writing specific values to specific kick registers to unlock that region. This is provided as ti,unlock-offsets property. Signed-off-by: Amneesh Singh --- dts/bindings/syscon/ti,control-module.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 dts/bindings/syscon/ti,control-module.yaml diff --git a/dts/bindings/syscon/ti,control-module.yaml b/dts/bindings/syscon/ti,control-module.yaml new file mode 100644 index 000000000000..b2725f513862 --- /dev/null +++ b/dts/bindings/syscon/ti,control-module.yaml @@ -0,0 +1,20 @@ +# Copyright (c) 2026 Texas Instruments +# SPDX-License-Identifier: Apache-2.0 + +description: TI-K3 device level control register module + +compatible: "ti,control-module" + +include: syscon.yaml + +properties: + ti,unlock-offsets: + type: array + description: | + Unlock configuration consisting of offsets to the KICK0 registers for + the partitions that require unlocking. Two 32-bit magic numbers will + be written at this location to unlock the partition. + + Example: + ti,unlock-offsets = <0x1008 /* Partition 1 */ + 0x5008>; /* Partition 2 */ From 760714a0e8c9ef2e9de8bdfcfc7b79bd39e64afc Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Mon, 2 Feb 2026 14:15:13 +0530 Subject: [PATCH 107/143] UPSTREAM: soc: ti/k3: am6x: use ti,control-module for partition unloc... commit e67ecb40b8d4ce99b8b1366864a678ce379e1b73 upstream. Replace hardcoded control partition addresses with devicetree-based approach using the ti,control-module compatible. This provides a more flexible and maintainable solution for unlocking control partitions across different TI K3 AM6x variants. The new implementation: - Uses DT_FOREACH_STATUS_OKAY to iterate over ti,control-module nodes - Dynamically maps and unmaps memory regions - Uses ti,unlock-offsets property to determine partition kick registers - Maintains the same KICK0/KICK1 unlock sequence Signed-off-by: Amneesh Singh --- soc/ti/k3/am6x/common/ctrl_partitions.c | 59 ++++++++++--------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/soc/ti/k3/am6x/common/ctrl_partitions.c b/soc/ti/k3/am6x/common/ctrl_partitions.c index e2dc6ed35c6b..8387d0284722 100644 --- a/soc/ti/k3/am6x/common/ctrl_partitions.c +++ b/soc/ti/k3/am6x/common/ctrl_partitions.c @@ -4,53 +4,40 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include -#define KICK0_OFFSET (0x1008) -#define KICK1_OFFSET (0x100C) #define KICK0_UNLOCK_VAL (0x68EF3490U) #define KICK1_UNLOCK_VAL (0xD172BC5AU) -#define KICK_LOCK_VAL (0x0U) -#define CTRL_PARTITION_SIZE (0x4000) -#define CTRL_PARTITION(base, part) ((base) + (part) * CTRL_PARTITION_SIZE) - -#if defined CONFIG_SOC_AM6442_M4 || defined CONFIG_SOC_AM2434_M4 -#define MCU_PADCFG_BASE (0x4080000) -#elif defined CONFIG_SOC_AM6234_M4 | defined CONFIG_SOC_AM6232_M4 | defined CONFIG_SOC_AM6254_M4 -#define WKUP_PADCFG_BASE (0x4080000) -#elif defined CONFIG_SOC_AM2434_R5F0_0 -#define MCU_PADCFG_BASE (0x4080000) -#define MAIN_PADCFG_BASE (0xf0000) -#endif - -static const uintptr_t ctrl_partitions[] = { -#if defined CONFIG_SOC_AM6442_M4 || defined CONFIG_SOC_AM2434_M4 - CTRL_PARTITION(MCU_PADCFG_BASE, 0), - CTRL_PARTITION(MCU_PADCFG_BASE, 1), -#elif defined CONFIG_SOC_AM6234_M4 | defined CONFIG_SOC_AM6232_M4 | defined CONFIG_SOC_AM6254_M4 - CTRL_PARTITION(WKUP_PADCFG_BASE, 0), - CTRL_PARTITION(WKUP_PADCFG_BASE, 1), -#elif defined CONFIG_SOC_AM2434_R5F0_0 - CTRL_PARTITION(MAIN_PADCFG_BASE, 0), - CTRL_PARTITION(MAIN_PADCFG_BASE, 1), - CTRL_PARTITION(MCU_PADCFG_BASE, 0), - CTRL_PARTITION(MCU_PADCFG_BASE, 1), +#define K3_UNLOCK_CONTROL_MODULE_(node_id) \ + const uint32_t conf_##node_id[] = DT_PROP(node_id, ti_unlock_offsets); \ + base = DT_REG_ADDR(node_id); \ + device_map(&base, base, DT_REG_SIZE(node_id), K_MEM_CACHE_NONE); \ + ARRAY_FOR_EACH(conf_##node_id, i) { \ + k3_unlock_partition(conf_##node_id[i] + base); \ + } \ + IF_ENABLED(CONFIG_MMU, (k_mem_unmap_phys_bare((uint8_t *)base, DT_REG_SIZE(node_id));)) + +#define K3_UNLOCK_CONTROL_MODULE(node_id) \ + IF_ENABLED(DT_NODE_HAS_PROP(node_id, ti_unlock_offsets), \ + (K3_UNLOCK_CONTROL_MODULE_(node_id))) + +#if DT_HAS_COMPAT_STATUS_OKAY(ti_control_module) +static void k3_unlock_partition(mem_addr_t kick0_address) +{ + sys_write32(KICK0_UNLOCK_VAL, kick0_address); + sys_write32(KICK1_UNLOCK_VAL, kick0_address + 4); +} #endif -}; void k3_unlock_all_ctrl_partitions(void) { - ARRAY_FOR_EACH(ctrl_partitions, i) { - mm_reg_t base_addr = ctrl_partitions[i]; +#if DT_HAS_COMPAT_STATUS_OKAY(ti_control_module) + uintptr_t base; -#ifdef DEVICE_MMIO_IS_IN_RAM - device_map(&base_addr, base_addr, sizeof(base_addr), K_MEM_CACHE_NONE); + DT_FOREACH_STATUS_OKAY(ti_control_module, K3_UNLOCK_CONTROL_MODULE) #endif - - sys_write32(KICK0_UNLOCK_VAL, base_addr + KICK0_OFFSET); - sys_write32(KICK1_UNLOCK_VAL, base_addr + KICK1_OFFSET); - } } From f2ac64adee23b106af7d908b10f431d8eb9b112a Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Mon, 2 Feb 2026 14:18:34 +0530 Subject: [PATCH 108/143] UPSTREAM: soc: ti/k3: am6x: add early init hook for a53 cores commit e82959fba1cac677d60e8aa25f4fbe0168436b2d upstream. Add early initialization hook for TI K3 AM6x A53 cores to unlock control module partitions before device tree initialization. This ensures that pinctrl and other peripherals wrapped in ti,control-module nodes can access their configuration registers early in the boot process. Signed-off-by: Amneesh Singh --- soc/ti/k3/am6x/CMakeLists.txt | 1 + soc/ti/k3/am6x/Kconfig | 1 + soc/ti/k3/am6x/a53/soc.c | 13 +++++++++++++ 3 files changed, 15 insertions(+) create mode 100644 soc/ti/k3/am6x/a53/soc.c diff --git a/soc/ti/k3/am6x/CMakeLists.txt b/soc/ti/k3/am6x/CMakeLists.txt index 206e041f725c..1f36a43121fa 100644 --- a/soc/ti/k3/am6x/CMakeLists.txt +++ b/soc/ti/k3/am6x/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_sources(common/ctrl_partitions.c) if(CONFIG_SOC_AM6234_A53 OR CONFIG_SOC_AM6232_A53 OR CONFIG_SOC_AM6254_A53 OR CONFIG_SOC_AM62L3_A53) zephyr_sources_ifdef(CONFIG_ARM_MMU a53/mmu_regions.c) + zephyr_sources(a53/soc.c) set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm64/scripts/linker.ld CACHE INTERNAL "") elseif(CONFIG_SOC_SERIES_AM6X_M4) diff --git a/soc/ti/k3/am6x/Kconfig b/soc/ti/k3/am6x/Kconfig index a619a02a1a56..e167dea2bd97 100644 --- a/soc/ti/k3/am6x/Kconfig +++ b/soc/ti/k3/am6x/Kconfig @@ -5,6 +5,7 @@ config SOC_SERIES_AM6X_A53 select ARM64 select CPU_CORTEX_A53 select ARM_ARCH_TIMER if SYS_CLOCK_EXISTS + select SOC_EARLY_INIT_HOOK config SOC_SERIES_AM6X_M4 select ARM diff --git a/soc/ti/k3/am6x/a53/soc.c b/soc/ti/k3/am6x/a53/soc.c new file mode 100644 index 000000000000..b61cf8d49112 --- /dev/null +++ b/soc/ti/k3/am6x/a53/soc.c @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2026 Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +void soc_early_init_hook(void) +{ + k3_unlock_all_ctrl_partitions(); +} From b282a93c40dfaeb54f865d8c19cadedfb2805ff7 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Mon, 2 Feb 2026 14:21:24 +0530 Subject: [PATCH 109/143] UPSTREAM: dts: ti: am64x_{main/mcu}.dtsi: wrap pinctrl with ti,contro... commit 197b68a8d20f426e860dd5b1a19418df017494a8 upstream. Wrap AM64x main and MCU domain pinctrl nodes with ti,control-module compatible to enable proper partition unlocking before pinctrl initialization. This ensures pinctrl registers are accessible by unlocking the appropriate control module partitions using the ti,unlock-offsets property. Signed-off-by: Amneesh Singh --- dts/vendor/ti/am64x_main.dtsi | 17 +++++++++++++---- dts/vendor/ti/am64x_mcu.dtsi | 17 +++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/dts/vendor/ti/am64x_main.dtsi b/dts/vendor/ti/am64x_main.dtsi index 6855f46355dd..729922879e11 100644 --- a/dts/vendor/ti/am64x_main.dtsi +++ b/dts/vendor/ti/am64x_main.dtsi @@ -27,10 +27,19 @@ status = "disabled"; }; - main_pinctrl: pinctrl@f4000 { - compatible = "ti,k3-pinctrl"; - reg = <0xf4000 0x2ac>; - status = "disabled"; + main_padcfg0: syscon@f0000 { + compatible = "ti,control-module"; + reg = <0xf0000 DT_SIZE_K(32)>; + ranges = <0x00 0xf0000 DT_SIZE_K(32)>; + ti,unlock-offsets = <0x1008 0x5008>; + #address-cells = <1>; + #size-cells = <1>; + + main_pinctrl: pinctrl@4000 { + compatible = "ti,k3-pinctrl"; + reg = <0x4000 0x2ac>; + status = "disabled"; + }; }; main_timer0: timer@2400000 { diff --git a/dts/vendor/ti/am64x_mcu.dtsi b/dts/vendor/ti/am64x_mcu.dtsi index 5b770bd0f2cc..2cff07ed6ad6 100644 --- a/dts/vendor/ti/am64x_mcu.dtsi +++ b/dts/vendor/ti/am64x_mcu.dtsi @@ -11,10 +11,19 @@ #address-cells = <1>; #size-cells = <1>; - mcu_pinctrl: pinctrl@4084000 { - compatible = "ti,k3-pinctrl"; - reg = <0x04084000 0x88>; - status = "disabled"; + mcu_padcfg0: syscon@4080000 { + compatible = "ti,control-module"; + reg = <0x4080000 DT_SIZE_K(32)>; + ranges = <0x00 0x4080000 DT_SIZE_K(32)>; + ti,unlock-offsets = <0x1008 0x5008>; + #address-cells = <1>; + #size-cells = <1>; + + mcu_pinctrl: pinctrl@4000 { + compatible = "ti,k3-pinctrl"; + reg = <0x4000 0x88>; + status = "disabled"; + }; }; mcu_uart0: serial@4a00000 { From 266c6bb636b710c0337580f22b6f6232ba970fde Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Mon, 2 Feb 2026 14:23:32 +0530 Subject: [PATCH 110/143] UPSTREAM: dts: ti: am62x_{a53/m4}.dtsi: wrap pinctrl with ti,control-... commit fcd47869f75b3d690756b90ea2d598cf60ca7134 upstream. Wrap AM62x A53 and M4 domain pinctrl nodes with ti,control-module compatible to enable proper partition unlocking before pinctrl initialization. This ensures pinctrl registers are accessible by unlocking the appropriate control module partitions using the ti,unlock-offsets property. Signed-off-by: Amneesh Singh --- dts/arm/ti/am62x_m4.dtsi | 17 +++++++++++++---- dts/arm64/ti/ti_am62x_a53.dtsi | 17 +++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/dts/arm/ti/am62x_m4.dtsi b/dts/arm/ti/am62x_m4.dtsi index 74fd0e08d3a5..37b578e83f1b 100644 --- a/dts/arm/ti/am62x_m4.dtsi +++ b/dts/arm/ti/am62x_m4.dtsi @@ -65,10 +65,19 @@ #mbox-cells = <1>; }; - pinctrl: pinctrl@4084000 { - compatible = "ti,k3-pinctrl"; - reg = <0x04084000 0x88>; - status = "okay"; + mcu_padcfg0: syscon@4080000 { + compatible = "ti,control-module"; + reg = <0x4080000 DT_SIZE_K(32)>; + ranges = <0x00 0x4080000 DT_SIZE_K(32)>; + ti,unlock-offsets = <0x1008 0x5008>; + #address-cells = <1>; + #size-cells = <1>; + + pinctrl: pinctrl@4000 { + compatible = "ti,k3-pinctrl"; + reg = <0x4000 0x88>; + status = "okay"; + }; }; uart0: serial@4a00000 { diff --git a/dts/arm64/ti/ti_am62x_a53.dtsi b/dts/arm64/ti/ti_am62x_a53.dtsi index 8a124e689ec3..2e20926e4746 100644 --- a/dts/arm64/ti/ti_am62x_a53.dtsi +++ b/dts/arm64/ti/ti_am62x_a53.dtsi @@ -60,10 +60,19 @@ status = "okay"; }; - pinctrl: pinctrl@f4000 { - compatible = "ti,k3-pinctrl"; - reg = <0x000f4000 0x2ac>; - status = "okay"; + main_padcfg0: syscon@f0000 { + compatible = "ti,control-module"; + reg = <0xf0000 DT_SIZE_K(32)>; + ranges = <0x00 0xf0000 DT_SIZE_K(32)>; + ti,unlock-offsets = <0x1008 0x5008>; + #address-cells = <1>; + #size-cells = <1>; + + pinctrl: pinctrl@4000 { + compatible = "ti,k3-pinctrl"; + reg = <0x4000 0x2ac>; + status = "okay"; + }; }; mbox0: mailbox0@29000000 { From 561f729cb0ca1ff0c4408419a8867f6bb28562e8 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 26 Mar 2026 20:38:12 +0530 Subject: [PATCH 111/143] UPSTREAM: soc: ti: k3: am6x: prevent control module warning commit 18c008af082e7aa66b9e0eabd92fc056433bf473 upstream. Prevent unused function warning that comes from having a control module node but no ti,unlock-offsets property. Do this by checking for the aforementioned property alongside already present compat string. Signed-off-by: Amneesh Singh --- soc/ti/k3/am6x/common/ctrl_partitions.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soc/ti/k3/am6x/common/ctrl_partitions.c b/soc/ti/k3/am6x/common/ctrl_partitions.c index 8387d0284722..28bc69d19533 100644 --- a/soc/ti/k3/am6x/common/ctrl_partitions.c +++ b/soc/ti/k3/am6x/common/ctrl_partitions.c @@ -25,7 +25,7 @@ IF_ENABLED(DT_NODE_HAS_PROP(node_id, ti_unlock_offsets), \ (K3_UNLOCK_CONTROL_MODULE_(node_id))) -#if DT_HAS_COMPAT_STATUS_OKAY(ti_control_module) +#if DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(ti_control_module, ti_unlock_offsets) static void k3_unlock_partition(mem_addr_t kick0_address) { sys_write32(KICK0_UNLOCK_VAL, kick0_address); @@ -35,7 +35,7 @@ static void k3_unlock_partition(mem_addr_t kick0_address) void k3_unlock_all_ctrl_partitions(void) { -#if DT_HAS_COMPAT_STATUS_OKAY(ti_control_module) +#if DT_ANY_COMPAT_HAS_PROP_STATUS_OKAY(ti_control_module, ti_unlock_offsets) uintptr_t base; DT_FOREACH_STATUS_OKAY(ti_control_module, K3_UNLOCK_CONTROL_MODULE) From 109949bba41deff8ac66f394b6edae9024bf4451 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 9 Apr 2026 17:56:48 +0530 Subject: [PATCH 112/143] UPSTREAM-PEND: drivers: pwm: introduce ti_am3352_ecap This patch adds driver support for TI ECAP module which can capture an external input or work as a standalone APWM generator. PR: 88860 Signed-off-by: Amneesh Singh --- Backport notes: - Fixed conflicts in Kconfig and CMakeLists.txt --- drivers/pwm/CMakeLists.txt | 1 + drivers/pwm/Kconfig | 2 + drivers/pwm/Kconfig.ti_am3352_ecap | 11 + drivers/pwm/pwm_ti_am3352_ecap.c | 339 +++++++++++++++++++++++++++ dts/bindings/pwm/ti,am3352-ecap.yaml | 27 +++ 5 files changed, 380 insertions(+) create mode 100644 drivers/pwm/Kconfig.ti_am3352_ecap create mode 100644 drivers/pwm/pwm_ti_am3352_ecap.c create mode 100644 dts/bindings/pwm/ti,am3352-ecap.yaml diff --git a/drivers/pwm/CMakeLists.txt b/drivers/pwm/CMakeLists.txt index 3497f51688ec..0dcd3a8e86dd 100644 --- a/drivers/pwm/CMakeLists.txt +++ b/drivers/pwm/CMakeLists.txt @@ -69,3 +69,4 @@ zephyr_library_sources_ifdef(CONFIG_USERSPACE pwm_handlers.c) zephyr_library_sources_ifdef(CONFIG_PWM_CAPTURE pwm_capture.c) zephyr_library_sources_ifdef(CONFIG_PWM_SHELL pwm_shell.c) zephyr_library_sources_ifdef(CONFIG_PWM_REALTEK_RTS5912 pwm_realtek_rts5912.c) +zephyr_library_sources_ifdef(CONFIG_PWM_TI_AM3352_ECAP pwm_ti_am3352_ecap.c) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 3b9911b2de94..49ea5958a3e7 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -142,4 +142,6 @@ source "drivers/pwm/Kconfig.wch" source "drivers/pwm/Kconfig.ambiq_timer" +source "drivers/pwm/Kconfig.ti_am3352_ecap" + endif # PWM diff --git a/drivers/pwm/Kconfig.ti_am3352_ecap b/drivers/pwm/Kconfig.ti_am3352_ecap new file mode 100644 index 000000000000..59454408adc9 --- /dev/null +++ b/drivers/pwm/Kconfig.ti_am3352_ecap @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments +# SPDX-License-Identifier: Apache-2.0 + +config PWM_TI_AM3352_ECAP + bool "TI ECAP based PWM controller" + default y + depends on DT_HAS_TI_AM3352_ECAP_ENABLED + depends on CLOCK_CONTROL + select PINCTRL + help + Enable ECAP controller for TI SoCs diff --git a/drivers/pwm/pwm_ti_am3352_ecap.c b/drivers/pwm/pwm_ti_am3352_ecap.c new file mode 100644 index 000000000000..00ebd703b9f4 --- /dev/null +++ b/drivers/pwm/pwm_ti_am3352_ecap.c @@ -0,0 +1,339 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_CLOCK_CONTROL_TISCI +#include +#endif + +LOG_MODULE_REGISTER(ti_ecap); + +#define DT_DRV_COMPAT ti_am3352_ecap + +struct ti_ecap_regs { + uint8_t RESERVED_1[0x10]; /**< Reserved, offset: 0x00 - 0x10 */ + volatile uint32_t CAP3; /**< Capture-3 Register, offset: 0x10 */ + volatile uint32_t CAP4; /**< Capture-4 Register, offset: 0x14 */ + uint8_t RESERVED_2[0x10]; /**< Reserved, offset: 0x18 - 0x28 */ + volatile uint32_t ECCTL; /**< ECAP Control Register, offset: 0x28 */ + volatile uint32_t ECINT_EN_FLG; /**< ECAP Interrupt Enable & Flag Register, offset: 0x2C */ + volatile uint32_t ECINT_CLR_FRC; /**< ECAP Interrupt Clear & Force Register, offset: 0x30 */ +}; + +/* ECAP Control Register */ +#define TI_ECAP_ECCTL_APWMPOL BIT(26) +#define TI_ECAP_ECCTL_CAP_APWM BIT(25) +#define TI_ECAP_ECCTL_SYNCO_SEL GENMASK(23, 22) +#define TI_ECAP_ECCTL_TSCNTSTP BIT(20) +#define TI_ECAP_ECCTL_REARM_RESET BIT(19) +#define TI_ECAP_ECCTL_STOPVALUE GENMASK(18, 17) +#define TI_ECAP_ECCTL_STOPVALUE_EVT4 (0x3) +#define TI_ECAP_ECCTL_CONT_ONESHT BIT(16) +#define TI_ECAP_ECCTL_CAPLDEN BIT(8) +#define TI_ECAP_ECCTL_CTRRST4 BIT(7) +#define TI_ECAP_ECCTL_CAP4POL BIT(6) +#define TI_ECAP_ECCTL_CTRRST3 BIT(5) +#define TI_ECAP_ECCTL_CAP3POL BIT(4) +#define TI_ECAP_ECCTL_CTRRST2 BIT(3) +#define TI_ECAP_ECCTL_CAP2POL BIT(2) +#define TI_ECAP_ECCTL_CTRRST1 BIT(1) +#define TI_ECAP_ECCTL_CAP1POL BIT(0) + +/* ECAP Interrupt Enable and Flag Register */ +#define TI_ECAP_ECINT_EN_CNTOVF_FLG BIT(21) +#define TI_ECAP_ECINT_EN_CEVT4_FLG BIT(20) +#define TI_ECAP_ECINT_EN_CNTOVF BIT(5) +#define TI_ECAP_ECINT_EN_CEVT4 BIT(4) + +/* ECAP Interrupt Clear and Force Register */ +#define TI_ECAP_ECINT_CLR_CNTOVF BIT(5) +#define TI_ECAP_ECINT_CLR_CEVT4 BIT(4) +#define TI_ECAP_ECINT_CLR_INT BIT(0) + +#define DEV_CFG(dev) ((const struct ti_ecap_cfg *)(dev)->config) +#define DEV_DATA(dev) ((struct ti_ecap_data *)(dev)->data) +#define DEV_REGS(dev) ((struct ti_ecap_regs *)DEVICE_MMIO_GET(dev)) + +struct ti_ecap_capture_data { + pwm_capture_callback_handler_t callback; + void *user_data; + bool capture_period; + bool capture_pulse; + bool continuous; +}; + +struct ti_ecap_cfg { + DEVICE_MMIO_ROM; + void (*irq_config_func)(); + const struct device *clock_dev; + clock_control_subsys_t clock_subsys; + uint32_t clock_frequency; + const struct pinctrl_dev_config *pcfg; +}; + +struct ti_ecap_data { + DEVICE_MMIO_RAM; + struct ti_ecap_capture_data cpt; + bool busy; +}; + +static int ti_ecap_set_cycles(const struct device *dev, uint32_t channel, uint32_t period_cycles, + uint32_t pulse_cycles, pwm_flags_t flags) +{ + ARG_UNUSED(channel); + struct ti_ecap_regs *regs = DEV_REGS(dev); + uint32_t ecctl; + + ecctl = regs->ECCTL; + + /* enable apwm and counter */ + ecctl |= (TI_ECAP_ECCTL_TSCNTSTP | TI_ECAP_ECCTL_CAP_APWM); + + /* polarity */ + if ((flags & PWM_POLARITY_MASK) == PWM_POLARITY_NORMAL) { + ecctl &= ~TI_ECAP_ECCTL_APWMPOL; + } else { + ecctl |= TI_ECAP_ECCTL_APWMPOL; + } + + /* update shadow period and shadow cmp registers in apwm mode */ + regs->CAP3 = period_cycles; + regs->CAP4 = pulse_cycles; + + /* update ecctl */ + regs->ECCTL = ecctl; + + return 0; +} + +static int ti_ecap_get_cycles_per_sec(const struct device *dev, uint32_t channel, uint64_t *cycles) +{ + ARG_UNUSED(channel); + const struct ti_ecap_cfg *cfg = DEV_CFG(dev); + + return clock_control_get_rate(cfg->clock_dev, cfg->clock_subsys, (uint32_t *)cycles); +} + +#ifdef CONFIG_PWM_CAPTURE +static int ti_ecap_enable_capture(const struct device *dev, uint32_t channel) +{ + ARG_UNUSED(channel); + struct ti_ecap_data *data = DEV_DATA(dev); + struct ti_ecap_regs *regs = DEV_REGS(dev); + + if (data->busy) { + return -EBUSY; + } + + /* enable interrupt for 3rd event */ + regs->ECINT_EN_FLG |= (TI_ECAP_ECINT_EN_CEVT4 | TI_ECAP_ECINT_EN_CNTOVF); + + /* start counter and enable loading capture registers */ + regs->ECCTL |= (TI_ECAP_ECCTL_TSCNTSTP | TI_ECAP_ECCTL_CAPLDEN); + + if (!data->cpt.continuous) { + /* rearms one shot */ + regs->ECCTL |= TI_ECAP_ECCTL_REARM_RESET; + } + + data->busy = true; + + return 0; +} + +static int ti_ecap_disable_capture(const struct device *dev, uint32_t channel) +{ + ARG_UNUSED(channel); + struct ti_ecap_data *data = DEV_DATA(dev); + struct ti_ecap_regs *regs = DEV_REGS(dev); + + /* disable interrupts */ + regs->ECINT_EN_FLG &= ~(TI_ECAP_ECINT_EN_CEVT4 | TI_ECAP_ECINT_EN_CNTOVF); + + /* stop counter and disable loading capture registers */ + regs->ECCTL &= ~(TI_ECAP_ECCTL_TSCNTSTP | TI_ECAP_ECCTL_CAPLDEN); + + data->busy = false; + + return 0; +} + +static int ti_ecap_configure_capture(const struct device *dev, uint32_t channel, pwm_flags_t flags, + pwm_capture_callback_handler_t cb, void *user_data) +{ + ARG_UNUSED(channel); + struct ti_ecap_data *data = DEV_DATA(dev); + struct ti_ecap_capture_data *cpt = &data->cpt; + struct ti_ecap_regs *regs = DEV_REGS(dev); + uint32_t ecctl; + + if (data->busy) { + return -EBUSY; + } + + cpt->callback = cb; + cpt->user_data = user_data; + cpt->capture_period = !!(flags & PWM_CAPTURE_TYPE_PERIOD); + cpt->capture_pulse = !!(flags & PWM_CAPTURE_TYPE_PULSE); + cpt->continuous = !!(flags & PWM_CAPTURE_MODE_CONTINUOUS); + + /* disable interrupts */ + regs->ECINT_EN_FLG &= ~(TI_ECAP_ECINT_EN_CEVT4 | TI_ECAP_ECINT_EN_CNTOVF); + + /* clear event flags */ + regs->ECINT_CLR_FRC |= + (TI_ECAP_ECINT_CLR_CNTOVF | TI_ECAP_ECINT_CLR_CEVT4 | TI_ECAP_ECINT_CLR_INT); + + ecctl = regs->ECCTL; + + if (cpt->continuous) { + /* continuous */ + ecctl &= ~TI_ECAP_ECCTL_CONT_ONESHT; + } else { + /* single shot */ + ecctl |= TI_ECAP_ECCTL_CONT_ONESHT; + } + + /* we only care about first 3 events */ + ecctl &= ~TI_ECAP_ECCTL_STOPVALUE; + ecctl |= FIELD_PREP(TI_ECAP_ECCTL_STOPVALUE, TI_ECAP_ECCTL_STOPVALUE_EVT4); + + /* configure capture timestamp to reset after edge to capture delta */ + ecctl |= (TI_ECAP_ECCTL_CTRRST1 | TI_ECAP_ECCTL_CTRRST2 | TI_ECAP_ECCTL_CTRRST3 | + TI_ECAP_ECCTL_CTRRST4); + + if ((flags & PWM_POLARITY_MASK) == PWM_POLARITY_NORMAL) { + /* active high */ + ecctl &= ~TI_ECAP_ECCTL_CAP1POL; /* cap 1 - rising edge */ + ecctl |= TI_ECAP_ECCTL_CAP2POL; /* cap 2 - falling edge */ + ecctl &= ~TI_ECAP_ECCTL_CAP3POL; /* cap 3 - rising edge */ + ecctl |= TI_ECAP_ECCTL_CAP4POL; /* cap 4 - falling edge */ + } else { + ecctl |= TI_ECAP_ECCTL_CAP1POL; /* cap 1 - falling edge */ + ecctl &= ~TI_ECAP_ECCTL_CAP2POL; /* cap 2 - rising edge */ + ecctl |= TI_ECAP_ECCTL_CAP3POL; /* cap 3 - falling edge */ + ecctl &= ~TI_ECAP_ECCTL_CAP4POL; /* cap 4 - rising edge */ + } + + /* stop counter and disable loading capture registers */ + ecctl &= ~(TI_ECAP_ECCTL_TSCNTSTP | TI_ECAP_ECCTL_CAPLDEN); + + /* enable capture mode and disable APWM */ + ecctl &= ~TI_ECAP_ECCTL_CAP_APWM; + + regs->ECCTL = ecctl; + + return 0; +} +#endif /* CONFIG_PWM_CAPTURE */ + +static int ti_ecap_init(const struct device *dev) +{ + const struct ti_ecap_cfg *cfg = DEV_CFG(dev); + int ret; + + DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); + + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("Fail to configure pinctrl\n"); + return ret; + } + + cfg->irq_config_func(); + + return 0; +} + +static void ti_ecap_isr(const struct device *dev) +{ + struct ti_ecap_capture_data *cpt = &DEV_DATA(dev)->cpt; + struct ti_ecap_regs *regs = DEV_REGS(dev); + uint32_t period = 0; + uint32_t pulse = 0; + uint32_t ecint_en_flg = regs->ECINT_EN_FLG; + + if (ecint_en_flg & TI_ECAP_ECINT_EN_CNTOVF_FLG) { + + if (cpt->callback != NULL) { + cpt->callback(dev, 0, period, pulse, -ERANGE, cpt->user_data); + } + + /* clear event */ + regs->ECINT_CLR_FRC |= TI_ECAP_ECINT_CLR_CNTOVF; + } else if (ecint_en_flg & TI_ECAP_ECINT_EN_CEVT4_FLG) { + uint32_t cap3 = regs->CAP3; + uint32_t cap4 = regs->CAP4; + + if (cpt->capture_period) { + period = cap3 + cap4; + } + + if (cpt->capture_pulse) { + pulse = cap4; + } + + if (cpt->callback != NULL) { + cpt->callback(dev, 0, period, pulse, 0, cpt->user_data); + } + + /* clear event */ + regs->ECINT_CLR_FRC |= TI_ECAP_ECINT_CLR_CEVT4; + } + + /* clear global interrupt */ + regs->ECINT_CLR_FRC |= TI_ECAP_ECINT_CLR_INT; +} + +static DEVICE_API(pwm, ti_ecap_api) = { + .set_cycles = ti_ecap_set_cycles, + .get_cycles_per_sec = ti_ecap_get_cycles_per_sec, +#ifdef CONFIG_PWM_CAPTURE + .enable_capture = ti_ecap_enable_capture, + .disable_capture = ti_ecap_disable_capture, + .configure_capture = ti_ecap_configure_capture, +#endif /* CONFIG_PWM_CAPTURE */ +}; + +#define TI_ECAP_DEFINE_CLK_SUBSYS(n) \ + COND_CODE_1(CONFIG_CLOCK_CONTROL_TISCI, ( \ + static struct tisci_clock_config tisci_fclk_##n = \ + TISCI_GET_CLOCK_DETAILS_BY_INST(n); \ + static const clock_control_subsys_t ti_ecap_clk_subsys_##n = \ + &tisci_fclk_##n; \ + ), (COND_CODE_1(CONFIG_CLOCK_CONTROL_ARM_SCMI, \ + (static const clock_control_subsys_t ti_ecap_clk_subsys_##n = \ + (clock_control_subsys_t)DT_INST_PHA(n, clocks, name); \ + ), (BUILD_ASSERT(0, "Unsupported clock controller");)))) + +#define TI_ECAP_INIT(n) \ + TI_ECAP_DEFINE_CLK_SUBSYS(n); \ + PINCTRL_DT_INST_DEFINE(n); \ + static void ti_ecap_irq_config_func_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), ti_ecap_isr, \ + DEVICE_DT_INST_GET(n), DT_INST_IRQ(n, flags)); \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + static struct ti_ecap_cfg ti_ecap_config_##n = { \ + DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .clock_subsys = ti_ecap_clk_subsys_##n, \ + .irq_config_func = ti_ecap_irq_config_func_##n, \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + }; \ + \ + static struct ti_ecap_data ti_ecap_data_##n; \ + \ + DEVICE_DT_INST_DEFINE(n, ti_ecap_init, NULL, &ti_ecap_data_##n, &ti_ecap_config_##n, \ + POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, &ti_ecap_api); + +DT_INST_FOREACH_STATUS_OKAY(TI_ECAP_INIT) diff --git a/dts/bindings/pwm/ti,am3352-ecap.yaml b/dts/bindings/pwm/ti,am3352-ecap.yaml new file mode 100644 index 000000000000..cf1ede443cf6 --- /dev/null +++ b/dts/bindings/pwm/ti,am3352-ecap.yaml @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments +# SPDX-License-Identifier: Apache-2.0 + +description: TI SOC ECAP based PWM controller + +include: [pwm-controller.yaml, base.yaml, pinctrl-device.yaml] + +compatible: "ti,am3352-ecap" + +properties: + reg: + required: true + + interrupts: + required: true + + clock-frequency: + type: int + description: Optional peripheral frequency to use if the clock is absent + + "#pwm-cells": + const: 3 + +pwm-cells: + - channel + - period + - flags From efaeb6636112b1e47c9a435590c361f022359ff8 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 26 Mar 2026 20:13:45 +0530 Subject: [PATCH 113/143] UPSTREAM-PEND: dts: ti: am64x: add TISCI clock controller Add the TISCI clock controller with the node label k3_clks. PR: 88860 Signed-off-by: Amneesh Singh --- dts/vendor/ti/am64x_main.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dts/vendor/ti/am64x_main.dtsi b/dts/vendor/ti/am64x_main.dtsi index 729922879e11..d9e81bd80126 100644 --- a/dts/vendor/ti/am64x_main.dtsi +++ b/dts/vendor/ti/am64x_main.dtsi @@ -17,6 +17,11 @@ reg-names = "debug_messages"; reg = <0x44043000 0xfe0>; status = "disabled"; + + k3_clks: clock-controller { + compatible = "ti,k2g-sci-clk"; + #clock-cells = <2>; + }; }; secure_proxy_main: mailbox0@4d000000 { From 45370b563b23074eb27225d5c4848f2e0f5e9841 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Mon, 30 Mar 2026 11:02:45 +0530 Subject: [PATCH 114/143] UPSTREAM-PEND: am243x_evm: am2434: r5f0_0: add support for TI ECAP Add DT nodes for TI's Enhanced Capture module. PR: 88860 Signed-off-by: Amneesh Singh --- Backport notes: - Resolved device tree conflicts --- .../am243x_evm_am2434_r5f0_0-pinctrl.dtsi | 4 +++ .../am243x_evm/am243x_evm_am2434_r5f0_0.dts | 6 +++++ dts/arm/ti/am64x_r5.dtsi | 15 +++++++++++ dts/vendor/ti/am64x_main.dtsi | 27 +++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi index 9fc3aca3b0e2..578e04be3b1e 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi @@ -156,6 +156,10 @@ mmc1_clklb: mmc1_clklb_default { pinmux = ; }; + + main_ecap0_in: ecap0_in_default { + pinmux = ; /* (D18) ECAP0_IN_APWM_OUT */ + }; }; &mcu_pinctrl { diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts index 819bd2f861fa..848053a5f23c 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts @@ -285,3 +285,9 @@ disk-name = "sd"; }; }; + +&main_ecap0 { + pinctrl-0 = <&main_ecap0_in>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/dts/arm/ti/am64x_r5.dtsi b/dts/arm/ti/am64x_r5.dtsi index e4b6580861cb..374fc2e94d6b 100644 --- a/dts/arm/ti/am64x_r5.dtsi +++ b/dts/arm/ti/am64x_r5.dtsi @@ -247,3 +247,18 @@ interrupts = <0 166 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; interrupt-parent = <&vim>; }; + +&main_ecap0 { + interrupts = <0 140 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_ecap1 { + interrupts = <0 141 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_ecap2 { + interrupts = <0 142 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; diff --git a/dts/vendor/ti/am64x_main.dtsi b/dts/vendor/ti/am64x_main.dtsi index d9e81bd80126..75246da90d84 100644 --- a/dts/vendor/ti/am64x_main.dtsi +++ b/dts/vendor/ti/am64x_main.dtsi @@ -456,4 +456,31 @@ ti,otap-del-sel-ddr50 = <0x9>; status = "disabled"; }; + + main_ecap0: pwm@23100000 { + compatible = "ti,am3352-ecap"; + reg = <0x23100000 0x100>; + #pwm-cells = <3>; + power-domains = <&ecap0_pd>; + clocks = <&k3_clks 51 0>; + status = "disabled"; + }; + + main_ecap1: pwm@23110000 { + compatible = "ti,am3352-ecap"; + reg = <0x23110000 0x100>; + #pwm-cells = <3>; + power-domains = <&ecap1_pd>; + clocks = <&k3_clks 52 0>; + status = "disabled"; + }; + + main_ecap2: pwm@23120000 { + compatible = "ti,am3352-ecap"; + reg = <0x23120000 0x100>; + #pwm-cells = <3>; + power-domains = <&ecap2_pd>; + clocks = <&k3_clks 53 0>; + status = "disabled"; + }; }; From 55a3ce9cb94e326a1b4083bf30a3471395fac97a Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Mon, 30 Mar 2026 11:03:46 +0530 Subject: [PATCH 115/143] UPSTREAM-PEND: am62l_evm: am62l3: a53: add support for TI ECAP Add DT nodes for TI's Enhanced Capture module. PR: 88860 Signed-off-by: Amneesh Singh --- Backport notes: - Resolved device tree conflicts --- .../am62l_evm_am62l3_a53-pinctrl.dtsi | 4 +++ boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts | 6 +++++ dts/arm64/ti/am62l3_a53.dtsi | 15 +++++++++++ dts/vendor/ti/am62l-main.dtsi | 27 +++++++++++++++++++ dts/vendor/ti/am62l_power_domains.dtsi | 18 +++++++++++++ 5 files changed, 70 insertions(+) diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi index 69748730bbce..e305272a03f0 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi @@ -160,4 +160,8 @@ mmc2_dat3: mmc2_dat3_default { pinmux = ; /* (R22) MMC2_DAT3 */ }; + + main_ecap0_in: ecap0_in_default { + pinmux = ; /* (L22) ECAP0_IN_APWM_OUT */ + }; }; diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts index 3c449a773a10..75b52610065b 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts @@ -140,3 +140,9 @@ ti,fails-without-test-cd; status = "disabled"; }; + +&main_ecap0 { + pinctrl-0 = <&main_ecap0_in>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/dts/arm64/ti/am62l3_a53.dtsi b/dts/arm64/ti/am62l3_a53.dtsi index efbf67811b94..bb2991f2f960 100644 --- a/dts/arm64/ti/am62l3_a53.dtsi +++ b/dts/arm64/ti/am62l3_a53.dtsi @@ -186,3 +186,18 @@ interrupts = ; interrupt-parent = <&gic>; }; + +&main_ecap0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_ecap1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_ecap2 { + interrupts = ; + interrupt-parent = <&gic>; +}; diff --git a/dts/vendor/ti/am62l-main.dtsi b/dts/vendor/ti/am62l-main.dtsi index 46ed533cb686..c416528e4904 100644 --- a/dts/vendor/ti/am62l-main.dtsi +++ b/dts/vendor/ti/am62l-main.dtsi @@ -384,4 +384,31 @@ ti,itap-del-sel-sdr25 = <0x0>; status = "disabled"; }; + + main_ecap0: pwm@23100000 { + compatible = "ti,am3352-ecap"; + reg = <0x23100000 0x100>; + #pwm-cells = <3>; + power-domains = <&main_ecap0_pd>; + clocks = <&scmi_clk 99>; + status = "disabled"; + }; + + main_ecap1: pwm@23110000 { + compatible = "ti,am3352-ecap"; + reg = <0x23110000 0x100>; + #pwm-cells = <3>; + power-domains = <&main_ecap1_pd>; + clocks = <&scmi_clk 100>; + status = "disabled"; + }; + + main_ecap2: pwm@23120000 { + compatible = "ti,am3352-ecap"; + reg = <0x23120000 0x100>; + #pwm-cells = <3>; + power-domains = <&main_ecap2_pd>; + clocks = <&scmi_clk 101>; + status = "disabled"; + }; }; diff --git a/dts/vendor/ti/am62l_power_domains.dtsi b/dts/vendor/ti/am62l_power_domains.dtsi index dc390638ebba..8939c330fa8f 100644 --- a/dts/vendor/ti/am62l_power_domains.dtsi +++ b/dts/vendor/ti/am62l_power_domains.dtsi @@ -121,5 +121,23 @@ reg = <27>; #power-domain-cells = <0>; }; + + main_ecap0_pd: power-domain@16 { + compatible = "arm,scmi-power-domain"; + reg = <22>; + #power-domain-cells = <0>; + }; + + main_ecap1_pd: power-domain@17 { + compatible = "arm,scmi-power-domain"; + reg = <23>; + #power-domain-cells = <0>; + }; + + main_ecap2_pd: power-domain@18 { + compatible = "arm,scmi-power-domain"; + reg = <24>; + #power-domain-cells = <0>; + }; }; }; From 28475b03dbeaaec1d6c996f8ebf9081807871b87 Mon Sep 17 00:00:00 2001 From: Abderrahmane JARMOUNI Date: Mon, 27 Oct 2025 14:38:52 +0100 Subject: [PATCH 116/143] UPSTREAM: include: drivers: fix Doxygen \retval command usage commit 3d78a7665040c378a1cefadcc1235d7f3d6e8856 upstream. Fix how Doxygen special commands \retval & \return are used in doc text. \retval: Starts a description for a function's return value with name , which needs to be attached and to not be a word that is part of the sentence, and each return val should have its own \retval. Otherwise \retval is replaced with \return. Signed-off-by: Abderrahmane JARMOUNI --- include/zephyr/drivers/adc.h | 2 +- include/zephyr/drivers/can.h | 8 ++++---- include/zephyr/drivers/cellular.h | 12 +++++------ include/zephyr/drivers/charger.h | 4 ++-- include/zephyr/drivers/clock_control.h | 4 ++-- include/zephyr/drivers/counter.h | 6 +++--- include/zephyr/drivers/dai.h | 6 +++--- include/zephyr/drivers/display.h | 20 +++++++++--------- include/zephyr/drivers/dma.h | 28 +++++++++++++------------- include/zephyr/drivers/entropy.h | 2 +- include/zephyr/drivers/espi_emul.h | 2 +- include/zephyr/drivers/flash.h | 6 +++--- include/zephyr/drivers/fpga.h | 8 ++++---- include/zephyr/drivers/gpio.h | 2 +- include/zephyr/drivers/hwinfo.h | 20 +++++++++--------- include/zephyr/drivers/hwspinlock.h | 2 +- include/zephyr/drivers/i2c.h | 2 +- include/zephyr/drivers/i2s.h | 2 +- include/zephyr/drivers/i3c.h | 2 +- include/zephyr/drivers/led_strip.h | 2 +- include/zephyr/drivers/peci.h | 8 ++++---- include/zephyr/drivers/pm_cpu_ops.h | 6 +++--- include/zephyr/drivers/ps2.h | 10 ++++----- include/zephyr/drivers/retained_mem.h | 8 ++++---- include/zephyr/drivers/sdhc.h | 20 +++++++++--------- include/zephyr/drivers/spi.h | 4 ++-- include/zephyr/drivers/tee.h | 2 +- include/zephyr/drivers/video.h | 4 ++-- 28 files changed, 101 insertions(+), 101 deletions(-) diff --git a/include/zephyr/drivers/adc.h b/include/zephyr/drivers/adc.h index 8334f504ba14..c49a9f7d5ba8 100644 --- a/include/zephyr/drivers/adc.h +++ b/include/zephyr/drivers/adc.h @@ -1309,7 +1309,7 @@ static inline int adc_sequence_init_dt(const struct adc_dt_spec *spec, * * @param spec ADC specification from devicetree * - * @retval true if the ADC device is ready for use and false otherwise. + * @return true if the ADC device is ready for use and false otherwise. */ static inline bool adc_is_ready_dt(const struct adc_dt_spec *spec) { diff --git a/include/zephyr/drivers/can.h b/include/zephyr/drivers/can.h index 4cfa440b705b..de60bae43303 100644 --- a/include/zephyr/drivers/can.h +++ b/include/zephyr/drivers/can.h @@ -1002,7 +1002,7 @@ static inline const struct can_timing *z_impl_can_get_timing_data_max(const stru * @param sample_pnt Sample point for the data phase in permille of the entire bit * time or 0 for automatic sample point location. * - * @retval 0 or positive sample point error on success. + * @return 0 or positive sample point error on success. * @retval -EINVAL if the requested bitrate or sample point is out of range. * @retval -ENOTSUP if the requested bitrate is not supported. * @retval -EIO if @a can_get_core_clock() is not available. @@ -1387,7 +1387,7 @@ static inline void z_impl_can_remove_rx_filter(const struct device *dev, int fil * @param ide Get the maximum standard (11-bit) CAN ID filters if false, or extended (29-bit) CAN ID * filters if true. * - * @retval Positive number of maximum concurrent filters. + * @retval >=0 number of maximum concurrent filters. * @retval -EIO General input/output error. * @retval -ENOSYS If this function is not implemented by the driver. */ @@ -1684,7 +1684,7 @@ static inline uint32_t z_impl_can_stats_get_rx_overruns(const struct device *dev * * @param dlc Data Length Code (DLC). * - * @retval Number of bytes. + * @return Number of bytes. */ static inline uint8_t can_dlc_to_bytes(uint8_t dlc) { @@ -1699,7 +1699,7 @@ static inline uint8_t can_dlc_to_bytes(uint8_t dlc) * * @param num_bytes Number of bytes. * - * @retval Data Length Code (DLC). + * @return Data Length Code (DLC). */ static inline uint8_t can_bytes_to_dlc(uint8_t num_bytes) { diff --git a/include/zephyr/drivers/cellular.h b/include/zephyr/drivers/cellular.h index ba78207a340c..2df3af846167 100644 --- a/include/zephyr/drivers/cellular.h +++ b/include/zephyr/drivers/cellular.h @@ -237,7 +237,7 @@ __subsystem struct cellular_driver_api { * @retval 0 if successful. * @retval -EINVAL if any provided cellular network configuration is invalid or unsupported. * @retval -ENOTSUP if API is not supported by cellular network device. - * @retval Negative errno-code otherwise. + * @retval <0 Negative errno-code otherwise. */ static inline int cellular_configure_networks(const struct device *dev, const struct cellular_network *networks, uint8_t size) @@ -260,7 +260,7 @@ static inline int cellular_configure_networks(const struct device *dev, * * @retval 0 if successful. * @retval -ENOTSUP if API is not supported by cellular network device. - * @retval Negative errno-code otherwise. + * @retval <0 Negative errno-code otherwise. */ static inline int cellular_get_supported_networks(const struct device *dev, const struct cellular_network **networks, @@ -285,7 +285,7 @@ static inline int cellular_get_supported_networks(const struct device *dev, * @retval 0 if successful. * @retval -ENOTSUP if API is not supported by cellular network device. * @retval -ENODATA if device is not in a state where signal can be polled - * @retval Negative errno-code otherwise. + * @retval <0 Negative errno-code otherwise. */ static inline int cellular_get_signal(const struct device *dev, const enum cellular_signal_type type, int16_t *value) @@ -310,7 +310,7 @@ static inline int cellular_get_signal(const struct device *dev, * @retval 0 if successful. * @retval -ENOTSUP if API is not supported by cellular network device. * @retval -ENODATA if modem does not provide info requested - * @retval Negative errno-code from chat module otherwise. + * @retval <0 Negative errno-code from chat module otherwise. */ static inline int cellular_get_modem_info(const struct device *dev, const enum cellular_modem_info_type type, char *info, @@ -335,7 +335,7 @@ static inline int cellular_get_modem_info(const struct device *dev, * @retval 0 if successful. * @retval -ENOSYS if API is not supported by cellular network device. * @retval -ENODATA if modem does not provide info requested - * @retval Negative errno-code from chat module otherwise. + * @retval <0 Negative errno-code from chat module otherwise. */ static inline int cellular_get_registration_status(const struct device *dev, enum cellular_access_technology tech, @@ -364,7 +364,7 @@ static inline int cellular_get_registration_status(const struct device *dev, * @retval -EINVAL if APN string invalid or too long. * @retval -EALREADY if APN identical to current one, nothing to do * @retval -EBUSY if modem is already dialled, APN cannot be changed - * @retval Negative errno-code otherwise. + * @retval <0 Negative errno-code otherwise. */ static inline int cellular_set_apn(const struct device *dev, const char *apn) { diff --git a/include/zephyr/drivers/charger.h b/include/zephyr/drivers/charger.h index 27e4602560e2..a49a7a61b5d0 100644 --- a/include/zephyr/drivers/charger.h +++ b/include/zephyr/drivers/charger.h @@ -376,7 +376,7 @@ __subsystem struct charger_driver_api { * @param val Pointer to charger_propval union * * @retval 0 if successful - * @retval < 0 if getting property failed + * @retval <0 if getting property failed */ __syscall int charger_get_prop(const struct device *dev, const charger_prop_t prop, union charger_propval *val); @@ -397,7 +397,7 @@ static inline int z_impl_charger_get_prop(const struct device *dev, const charge * @param val Pointer to charger_propval union * * @retval 0 if successful - * @retval < 0 if setting property failed + * @retval <0 if setting property failed */ __syscall int charger_set_prop(const struct device *dev, const charger_prop_t prop, const union charger_propval *val); diff --git a/include/zephyr/drivers/clock_control.h b/include/zephyr/drivers/clock_control.h index d09daf963860..1ef3cd01f88b 100644 --- a/include/zephyr/drivers/clock_control.h +++ b/include/zephyr/drivers/clock_control.h @@ -174,7 +174,7 @@ static inline int clock_control_off(const struct device *dev, * @retval -EALREADY if clock was already started and is starting or running. * @retval -ENOTSUP If the requested mode of operation is not supported. * @retval -ENOSYS if the interface is not implemented. - * @retval other negative errno on vendor specific error. + * @retval <0 other negative errno on vendor specific error. */ static inline int clock_control_async_on(const struct device *dev, clock_control_subsys_t sys, @@ -252,7 +252,7 @@ static inline int clock_control_get_rate(const struct device *dev, * @retval -EALREADY if clock was already in the given rate. * @retval -ENOTSUP If the requested mode of operation is not supported. * @retval -ENOSYS if the interface is not implemented. - * @retval other negative errno on vendor specific error. + * @retval <0 other negative errno on vendor specific error. */ static inline int clock_control_set_rate(const struct device *dev, clock_control_subsys_t sys, diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index ca3a35183f41..cc0bf024980e 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -377,7 +377,7 @@ static inline uint32_t z_impl_counter_get_max_top_value(const struct device *dev * @param dev Pointer to the device structure for the driver instance. * * @retval 0 If successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ __syscall int counter_start(const struct device *dev); @@ -414,7 +414,7 @@ static inline int z_impl_counter_stop(const struct device *dev) * @param ticks Pointer to where to store the current counter value * * @retval 0 If successful. - * @retval Negative error code on failure getting the counter value + * @retval <0 Negative error code on failure getting the counter value */ __syscall int counter_get_value(const struct device *dev, uint32_t *ticks); @@ -433,7 +433,7 @@ static inline int z_impl_counter_get_value(const struct device *dev, * @param ticks Pointer to where to store the current counter value * * @retval 0 If successful. - * @retval Negative error code on failure getting the counter value + * @retval <0 Negative error code on failure getting the counter value */ __syscall int counter_get_value_64(const struct device *dev, uint64_t *ticks); diff --git a/include/zephyr/drivers/dai.h b/include/zephyr/drivers/dai.h index f80ce20811d3..1adce5eceb1f 100644 --- a/include/zephyr/drivers/dai.h +++ b/include/zephyr/drivers/dai.h @@ -410,7 +410,7 @@ static inline int dai_config_set(const struct device *dev, * @param dev Pointer to the device structure for the driver instance * @param cfg Pointer to the config structure to be filled by the instance * @param dir Stream direction: RX or TX as defined by DAI_DIR_* - * @retval 0 if success, negative if invalid parameters or DAI un-configured + * @return 0 if success, negative if invalid parameters or DAI un-configured */ static inline int dai_config_get(const struct device *dev, struct dai_config *cfg, @@ -428,7 +428,7 @@ static inline int dai_config_get(const struct device *dev, * @param dir Stream direction: RX or TX as defined by DAI_DIR_* * @param stream_id Stream id: some drivers may have stream specific * properties, this id specifies the stream. - * @retval Pointer to the structure containing properties, + * @return Pointer to the structure containing properties, * or NULL if error or no properties */ static inline const struct dai_properties *dai_get_properties(const struct device *dev, @@ -565,7 +565,7 @@ static inline int dai_ts_get(const struct device *dev, struct dai_ts_cfg *cfg, * * @retval 0 If successful. * @retval -ENOSYS If the configuration update operation is not implemented. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ static inline int dai_config_update(const struct device *dev, const void *bespoke_cfg, diff --git a/include/zephyr/drivers/display.h b/include/zephyr/drivers/display.h index 08d55557ae97..39911d08baad 100644 --- a/include/zephyr/drivers/display.h +++ b/include/zephyr/drivers/display.h @@ -275,7 +275,7 @@ __subsystem struct display_driver_api { * @param desc Pointer to a structure describing the buffer layout * @param buf Pointer to buffer array * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. */ static inline int display_write(const struct device *dev, const uint16_t x, const uint16_t y, @@ -297,7 +297,7 @@ static inline int display_write(const struct device *dev, const uint16_t x, * @param desc Pointer to a structure describing the buffer layout * @param buf Pointer to buffer array * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. * @retval -ENOSYS if not implemented. */ static inline int display_read(const struct device *dev, const uint16_t x, @@ -320,7 +320,7 @@ static inline int display_read(const struct device *dev, const uint16_t x, * * @param dev Pointer to device structure * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. * @retval -ENOSYS if not implemented. */ static inline int display_clear(const struct device *dev) @@ -340,7 +340,7 @@ static inline int display_clear(const struct device *dev) * * @param dev Pointer to device structure * - * @retval Pointer to frame buffer or NULL if direct framebuffer access + * @return Pointer to frame buffer or NULL if direct framebuffer access * is not supported * */ @@ -372,7 +372,7 @@ static inline void *display_get_framebuffer(const struct device *dev) * * @param dev Pointer to device structure * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. * @retval -ENOSYS if not implemented. */ static inline int display_blanking_on(const struct device *dev) @@ -396,7 +396,7 @@ static inline int display_blanking_on(const struct device *dev) * * @param dev Pointer to device structure * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. * @retval -ENOSYS if not implemented. */ static inline int display_blanking_off(const struct device *dev) @@ -420,7 +420,7 @@ static inline int display_blanking_off(const struct device *dev) * @param dev Pointer to device structure * @param brightness Brightness in steps of 1/256 * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. * @retval -ENOSYS if not implemented. */ static inline int display_set_brightness(const struct device *dev, @@ -445,7 +445,7 @@ static inline int display_set_brightness(const struct device *dev, * @param dev Pointer to device structure * @param contrast Contrast in steps of 1/256 * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. * @retval -ENOSYS if not implemented. */ static inline int display_set_contrast(const struct device *dev, uint8_t contrast) @@ -482,7 +482,7 @@ static inline void display_get_capabilities(const struct device *dev, * @param dev Pointer to device structure * @param pixel_format Pixel format to be used by display * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. * @retval -ENOSYS if not implemented. */ static inline int @@ -505,7 +505,7 @@ display_set_pixel_format(const struct device *dev, * @param dev Pointer to device structure * @param orientation Orientation to be used by display * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. * @retval -ENOSYS if not implemented. */ static inline int display_set_orientation(const struct device *dev, diff --git a/include/zephyr/drivers/dma.h b/include/zephyr/drivers/dma.h index 7a0954e248a2..51ace1bec8e8 100644 --- a/include/zephyr/drivers/dma.h +++ b/include/zephyr/drivers/dma.h @@ -346,7 +346,7 @@ typedef int (*dma_api_get_attribute)(const struct device *dev, uint32_t type, ui * @param channel the channel id to use * @param filter_param filter function parameter, can be NULL * - * @retval True on filter matched otherwise return False. + * @return True on filter matched otherwise return False. */ typedef bool (*dma_api_chan_filter)(const struct device *dev, int channel, void *filter_param); @@ -390,7 +390,7 @@ __subsystem struct dma_driver_api { * selected channel * * @retval 0 if successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ static inline int dma_config(const struct device *dev, uint32_t channel, struct dma_config *config) @@ -412,7 +412,7 @@ static inline int dma_config(const struct device *dev, uint32_t channel, * @param size size of DMA transfer * * @retval 0 if successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ #ifdef CONFIG_DMA_64BIT static inline int dma_reload(const struct device *dev, uint32_t channel, @@ -449,7 +449,7 @@ static inline int dma_reload(const struct device *dev, uint32_t channel, * be processed * * @retval 0 if successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ static inline int dma_start(const struct device *dev, uint32_t channel) { @@ -475,7 +475,7 @@ static inline int dma_start(const struct device *dev, uint32_t channel) * being processed * * @retval 0 if successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ static inline int dma_stop(const struct device *dev, uint32_t channel) { @@ -551,8 +551,8 @@ static inline int dma_resume(const struct device *dev, uint32_t channel) * @param dev Pointer to the device structure for the driver instance. * @param filter_param filter function parameter * - * @retval dma channel if successful. - * @retval Negative errno code if failure. + * @return dma channel if successful. + * @retval <0 Negative errno code if failure. */ static inline int dma_request_channel(const struct device *dev, void *filter_param) { @@ -624,7 +624,7 @@ static inline void dma_release_channel(const struct device *dev, uint32_t channe * @param channel channel number * @param filter_param filter attribute * - * @retval Negative errno code if not support + * @retval <0 Negative errno code if not support * */ static inline int dma_chan_filter(const struct device *dev, int channel, void *filter_param) @@ -652,8 +652,8 @@ static inline int dma_chan_filter(const struct device *dev, int channel, void *f * being processed * @param stat a non-NULL dma_status object for storing DMA status * - * @retval non-negative if successful. - * @retval Negative errno code if failure. + * @retval >=0 non-negative if successful. + * @retval <0 Negative errno code if failure. */ static inline int dma_get_status(const struct device *dev, uint32_t channel, struct dma_status *stat) @@ -682,8 +682,8 @@ static inline int dma_get_status(const struct device *dev, uint32_t channel, * @param type Numeric identification of the attribute * @param value A non-NULL pointer to the variable where the read value is to be placed * - * @retval non-negative if successful. - * @retval Negative errno code if failure. + * @retval >=0 non-negative if successful. + * @retval <0 Negative errno code if failure. */ static inline int dma_get_attribute(const struct device *dev, uint32_t type, uint32_t *value) { @@ -707,7 +707,7 @@ static inline int dma_get_attribute(const struct device *dev, uint32_t type, uin * * @param size: width of bus (in bytes) * - * @retval common DMA index to be placed into registers. + * @return common DMA index to be placed into registers. */ static inline uint32_t dma_width_index(uint32_t size) { @@ -736,7 +736,7 @@ static inline uint32_t dma_width_index(uint32_t size) * * @param burst: number of bytes to be sent in a single burst * - * @retval common DMA index to be placed into registers. + * @return common DMA index to be placed into registers. */ static inline uint32_t dma_burst_index(uint32_t burst) { diff --git a/include/zephyr/drivers/entropy.h b/include/zephyr/drivers/entropy.h index 185b0d8f019f..5fd24f2833fa 100644 --- a/include/zephyr/drivers/entropy.h +++ b/include/zephyr/drivers/entropy.h @@ -103,7 +103,7 @@ static inline int z_impl_entropy_get_entropy(const struct device *dev, * @param buffer Buffer to fill with entropy. * @param length Buffer length. * @param flags Flags to modify the behavior of the call. - * @retval number of bytes filled with entropy or -error. + * @return number of bytes filled with entropy or -error. */ static inline int entropy_get_entropy_isr(const struct device *dev, uint8_t *buffer, diff --git a/include/zephyr/drivers/espi_emul.h b/include/zephyr/drivers/espi_emul.h index 1b572cef7112..7e8ed4e23b62 100644 --- a/include/zephyr/drivers/espi_emul.h +++ b/include/zephyr/drivers/espi_emul.h @@ -69,7 +69,7 @@ typedef int (*emul_espi_api_get_vw)(const struct emul *target, enum espi_vwire_s * * @param target The device Emulator instance * - * @retval The address of the memory. + * @return The address of the memory. */ typedef uintptr_t (*emul_espi_api_get_acpi_shm)(const struct emul *target); #endif diff --git a/include/zephyr/drivers/flash.h b/include/zephyr/drivers/flash.h index d8f39c4278be..91a8d199231b 100644 --- a/include/zephyr/drivers/flash.h +++ b/include/zephyr/drivers/flash.h @@ -525,7 +525,7 @@ void flash_page_foreach(const struct device *dev, flash_page_cb cb, * * @retval 0 on success * @retval -ENOTSUP if the flash driver does not support SFDP access - * @retval negative values for other errors. + * @retval <0 negative values for other errors. */ __syscall int flash_sfdp_read(const struct device *dev, off_t offset, void *data, size_t len); @@ -553,7 +553,7 @@ static inline int z_impl_flash_sfdp_read(const struct device *dev, * * @retval 0 on successful store of 3-byte JEDEC id * @retval -ENOTSUP if flash driver doesn't support this function - * @retval negative values for other errors + * @retval <0 negative values for other errors */ __syscall int flash_read_jedec_id(const struct device *dev, uint8_t *id); @@ -637,7 +637,7 @@ static inline const struct flash_parameters *z_impl_flash_get_parameters(const s * @retval 0 on success. * @retval -ENOTSUP if given device doesn't support extended operation. * @retval -ENOSYS if support for extended operations is not enabled in Kconfig - * @retval negative value on extended operation errors. + * @retval <0 negative value on extended operation errors. */ __syscall int flash_ex_op(const struct device *dev, uint16_t code, const uintptr_t in, void *out); diff --git a/include/zephyr/drivers/fpga.h b/include/zephyr/drivers/fpga.h index 5c175f31c706..f8198706a77a 100644 --- a/include/zephyr/drivers/fpga.h +++ b/include/zephyr/drivers/fpga.h @@ -89,7 +89,7 @@ static inline enum FPGA_status fpga_get_status(const struct device *dev) * @param dev FPGA device structure. * * @retval 0 if successful. - * @retval Failed Otherwise. + * @return Failed Otherwise. */ static inline int fpga_reset(const struct device *dev) { @@ -111,7 +111,7 @@ static inline int fpga_reset(const struct device *dev) * @param img_size Bitstream size in bytes. * * @retval 0 if successful. - * @retval Failed Otherwise. + * @return Failed Otherwise. */ static inline int fpga_load(const struct device *dev, uint32_t *image_ptr, uint32_t img_size) @@ -132,7 +132,7 @@ static inline int fpga_load(const struct device *dev, uint32_t *image_ptr, * @param dev FPGA device structure. * * @retval 0 if successful. - * @retval negative errno code on failure. + * @retval <0 negative errno code on failure. */ static inline int fpga_on(const struct device *dev) { @@ -173,7 +173,7 @@ static inline const char *fpga_get_info(const struct device *dev) * @param dev FPGA device structure. * * @retval 0 if successful. - * @retval negative errno code on failure. + * @retval <0 negative errno code on failure. */ static inline int fpga_off(const struct device *dev) { diff --git a/include/zephyr/drivers/gpio.h b/include/zephyr/drivers/gpio.h index 33962528136f..ff65b4e7713e 100644 --- a/include/zephyr/drivers/gpio.h +++ b/include/zephyr/drivers/gpio.h @@ -1892,7 +1892,7 @@ static inline int gpio_remove_callback_dt(const struct gpio_dt_spec *spec, * * @param dev Pointer to the device structure for the driver instance. * - * @retval status != 0 if at least one gpio interrupt is pending. + * @return status != 0 if at least one gpio interrupt is pending. * @retval 0 if no gpio interrupt is pending. * @retval -ENOSYS If driver does not implement the operation */ diff --git a/include/zephyr/drivers/hwinfo.h b/include/zephyr/drivers/hwinfo.h index 55bd5f0a7d92..b777b3ae40c1 100644 --- a/include/zephyr/drivers/hwinfo.h +++ b/include/zephyr/drivers/hwinfo.h @@ -91,9 +91,9 @@ extern "C" { * @param buffer Buffer to write the ID to. * @param length Max length of the buffer. * - * @retval size of the device ID copied. + * @return size of the device ID copied. * @retval -ENOSYS if there is no implementation for the particular device. - * @retval any negative value on driver specific errors. + * @retval <0 any negative value on driver specific errors. */ __syscall ssize_t hwinfo_get_device_id(uint8_t *buffer, size_t length); @@ -107,9 +107,9 @@ ssize_t z_impl_hwinfo_get_device_id(uint8_t *buffer, size_t length); * * @param buffer Buffer of 8 bytes to write the ID to. * - * @retval zero if successful. + * @retval 0 if successful. * @retval -ENOSYS if there is no implementation for the particular device. - * @retval any negative value on driver specific errors. + * @retval <0 any negative value on driver specific errors. */ __syscall int hwinfo_get_device_eui64(uint8_t *buffer); @@ -131,9 +131,9 @@ int z_impl_hwinfo_get_device_eui64(uint8_t *buffer); * Successive calls to this routine will return the same value, unless * `hwinfo_clear_reset_cause` has been called. * - * @retval zero if successful. + * @retval 0 if successful. * @retval -ENOSYS if there is no implementation for the particular device. - * @retval any negative value on driver specific errors. + * @retval <0 any negative value on driver specific errors. */ __syscall int hwinfo_get_reset_cause(uint32_t *cause); @@ -144,9 +144,9 @@ int z_impl_hwinfo_get_reset_cause(uint32_t *cause); * * Clears reset cause flags. * - * @retval zero if successful. + * @retval 0 if successful. * @retval -ENOSYS if there is no implementation for the particular device. - * @retval any negative value on driver specific errors. + * @retval <0 any negative value on driver specific errors. */ __syscall int hwinfo_clear_reset_cause(void); @@ -159,9 +159,9 @@ int z_impl_hwinfo_clear_reset_cause(void); * * Retrieves all `reset_cause` flags that are supported by this device. * - * @retval zero if successful. + * @retval 0 if successful. * @retval -ENOSYS if there is no implementation for the particular device. - * @retval any negative value on driver specific errors. + * @retval <0 any negative value on driver specific errors. */ __syscall int hwinfo_get_supported_reset_cause(uint32_t *supported); diff --git a/include/zephyr/drivers/hwspinlock.h b/include/zephyr/drivers/hwspinlock.h index 25d9d4a2d246..4d40fb8ae6fe 100644 --- a/include/zephyr/drivers/hwspinlock.h +++ b/include/zephyr/drivers/hwspinlock.h @@ -337,7 +337,7 @@ static inline void hw_spin_unlock(const struct device *dev, hwspinlock_ctx_t *ct * * @param dev HW spinlock device instance. * - * @retval HW spinlock max ID. + * @return HW spinlock max ID. */ static inline uint32_t hw_spinlock_get_max_id(const struct device *dev) { diff --git a/include/zephyr/drivers/i2c.h b/include/zephyr/drivers/i2c.h index 28fb25e08cb1..6a9c6ff3c7bd 100644 --- a/include/zephyr/drivers/i2c.h +++ b/include/zephyr/drivers/i2c.h @@ -982,7 +982,7 @@ static inline int i2c_transfer_cb_dt(const struct i2c_dt_spec *spec, * @param userdata Userdata passed to callback. * * @retval 0 if successful - * @retval negative on error. + * @retval <0 negative on error. */ static inline int i2c_write_read_cb(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr, const void *write_buf, diff --git a/include/zephyr/drivers/i2s.h b/include/zephyr/drivers/i2s.h index 406789c365da..ddecf758ebd7 100644 --- a/include/zephyr/drivers/i2s.h +++ b/include/zephyr/drivers/i2s.h @@ -373,7 +373,7 @@ static inline int z_impl_i2s_configure(const struct device *dev, * * @param dev Pointer to the device structure for the driver instance * @param dir Stream direction: RX or TX as defined by I2S_DIR_* - * @retval Pointer to the structure containing configuration parameters, + * @return Pointer to the structure containing configuration parameters, * or NULL if un-configured */ static inline const struct i2s_config *i2s_config_get(const struct device *dev, diff --git a/include/zephyr/drivers/i3c.h b/include/zephyr/drivers/i3c.h index 72a061528fad..4b39ef05efb0 100644 --- a/include/zephyr/drivers/i3c.h +++ b/include/zephyr/drivers/i3c.h @@ -2596,7 +2596,7 @@ void i3c_sec_handoffed(struct k_work *work); * * This allocates memory from a mem slab for a i3c_device_desc * - * @retval Pointer to allocated i3c_device_desc + * @return Pointer to allocated i3c_device_desc * @retval NULL if no mem slabs available */ struct i3c_device_desc *i3c_device_desc_alloc(void); diff --git a/include/zephyr/drivers/led_strip.h b/include/zephyr/drivers/led_strip.h index 3932a6737b33..4f5e50030f0c 100644 --- a/include/zephyr/drivers/led_strip.h +++ b/include/zephyr/drivers/led_strip.h @@ -159,7 +159,7 @@ static inline int led_strip_update_channels(const struct device *dev, * * @param dev LED strip device. * - * @retval Length of LED strip device. + * @return Length of LED strip device. */ static inline size_t led_strip_length(const struct device *dev) { diff --git a/include/zephyr/drivers/peci.h b/include/zephyr/drivers/peci.h index c7a9a28f8ec6..58e21cf32210 100644 --- a/include/zephyr/drivers/peci.h +++ b/include/zephyr/drivers/peci.h @@ -271,7 +271,7 @@ __subsystem struct peci_driver_api { * @param bitrate the selected bitrate expressed in Kbps. * * @retval 0 If successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ __syscall int peci_config(const struct device *dev, uint32_t bitrate); @@ -290,7 +290,7 @@ static inline int z_impl_peci_config(const struct device *dev, * @param dev Pointer to the device structure for the driver instance. * * @retval 0 If successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ __syscall int peci_enable(const struct device *dev); @@ -308,7 +308,7 @@ static inline int z_impl_peci_enable(const struct device *dev) * @param dev Pointer to the device structure for the driver instance. * * @retval 0 If successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ __syscall int peci_disable(const struct device *dev); @@ -327,7 +327,7 @@ static inline int z_impl_peci_disable(const struct device *dev) * @param msg Structure representing a PECI transaction. * * @retval 0 If successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ __syscall int peci_transfer(const struct device *dev, struct peci_msg *msg); diff --git a/include/zephyr/drivers/pm_cpu_ops.h b/include/zephyr/drivers/pm_cpu_ops.h index ac69ac52e640..677154ab0363 100644 --- a/include/zephyr/drivers/pm_cpu_ops.h +++ b/include/zephyr/drivers/pm_cpu_ops.h @@ -35,7 +35,7 @@ extern "C" { * This call is intended for use in hotplug. A core that is powered down by * cpu_off can only be powered up again in response to a cpu_on * - * @retval The call does not return when successful + * @return The call does not return when successful * @retval -ENOTSUP If the operation is not supported */ int pm_cpu_off(void); @@ -50,7 +50,7 @@ int pm_cpu_off(void); * @param cpuid CPU id to power on * @param entry_point Address at which the core must commence execution * - * @retval 0 on success, a negative errno otherwise + * @return 0 on success, a negative errno otherwise * @retval -ENOTSUP If the operation is not supported */ int pm_cpu_on(unsigned long cpuid, uintptr_t entry_point); @@ -62,7 +62,7 @@ int pm_cpu_on(unsigned long cpuid, uintptr_t entry_point); * * @param reset system reset type, cold or warm. * - * @retval 0 on success, a negative errno otherwise + * @return 0 on success, a negative errno otherwise */ int pm_system_reset(unsigned char reset); diff --git a/include/zephyr/drivers/ps2.h b/include/zephyr/drivers/ps2.h index cd71aa3e49fc..691d68cf5371 100644 --- a/include/zephyr/drivers/ps2.h +++ b/include/zephyr/drivers/ps2.h @@ -74,7 +74,7 @@ __subsystem struct ps2_driver_api { * command or when a mouse/keyboard send data to the client application. * * @retval 0 If successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ __syscall int ps2_config(const struct device *dev, ps2_callback_t callback_isr); @@ -95,7 +95,7 @@ static inline int z_impl_ps2_config(const struct device *dev, * @param value Data for the PS2 device. * * @retval 0 If successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ __syscall int ps2_write(const struct device *dev, uint8_t value); @@ -113,7 +113,7 @@ static inline int z_impl_ps2_write(const struct device *dev, uint8_t value) * @param value Pointer used for reading the PS/2 device. * * @retval 0 If successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ __syscall int ps2_read(const struct device *dev, uint8_t *value); @@ -130,7 +130,7 @@ static inline int z_impl_ps2_read(const struct device *dev, uint8_t *value) * @param dev Pointer to the device structure for the driver instance. * * @retval 0 If successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ __syscall int ps2_enable_callback(const struct device *dev); @@ -151,7 +151,7 @@ static inline int z_impl_ps2_enable_callback(const struct device *dev) * @param dev Pointer to the device structure for the driver instance. * * @retval 0 If successful. - * @retval Negative errno code if failure. + * @retval <0 Negative errno code if failure. */ __syscall int ps2_disable_callback(const struct device *dev); diff --git a/include/zephyr/drivers/retained_mem.h b/include/zephyr/drivers/retained_mem.h index d265341e9fe4..45650baf89fc 100644 --- a/include/zephyr/drivers/retained_mem.h +++ b/include/zephyr/drivers/retained_mem.h @@ -90,7 +90,7 @@ __subsystem struct retained_mem_driver_api { * * @param dev Retained memory device to use. * - * @retval Positive value indicating size in bytes on success, else negative errno + * @return Positive value indicating size in bytes on success, else negative errno * code. */ __syscall ssize_t retained_mem_size(const struct device *dev); @@ -110,7 +110,7 @@ static inline ssize_t z_impl_retained_mem_size(const struct device *dev) * @param buffer Buffer to store read data in. * @param size Size of data to read. * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. */ __syscall int retained_mem_read(const struct device *dev, off_t offset, uint8_t *buffer, size_t size); @@ -144,7 +144,7 @@ static inline int z_impl_retained_mem_read(const struct device *dev, off_t offse * @param buffer Data to write. * @param size Size of data to be written. * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. */ __syscall int retained_mem_write(const struct device *dev, off_t offset, const uint8_t *buffer, size_t size); @@ -174,7 +174,7 @@ static inline int z_impl_retained_mem_write(const struct device *dev, off_t offs * * @param dev Retained memory device to use. * - * @retval 0 on success else negative errno code. + * @return 0 on success else negative errno code. */ __syscall int retained_mem_clear(const struct device *dev); diff --git a/include/zephyr/drivers/sdhc.h b/include/zephyr/drivers/sdhc.h index 159a312de9aa..37d9b977b9c1 100644 --- a/include/zephyr/drivers/sdhc.h +++ b/include/zephyr/drivers/sdhc.h @@ -285,8 +285,8 @@ __subsystem struct sdhc_driver_api { * * @param dev: SD host controller device * @retval 0 reset succeeded - * @retval -ETIMEDOUT: controller reset timed out - * @retval -EIO: reset failed + * @retval -ETIMEDOUT controller reset timed out + * @retval -EIO reset failed */ __syscall int sdhc_hw_reset(const struct device *dev); @@ -313,7 +313,7 @@ static inline int z_impl_sdhc_hw_reset(const struct device *dev) * @retval 0 command was sent successfully * @retval -ETIMEDOUT command timed out while sending * @retval -ENOTSUP host controller does not support command - * @retval -EIO: I/O error + * @retval -EIO I/O error */ __syscall int sdhc_request(const struct device *dev, struct sdhc_command *cmd, struct sdhc_data *data); @@ -389,9 +389,9 @@ static inline int z_impl_sdhc_card_present(const struct device *dev) * allows an application to request the SD host controller to tune the card. * @param dev: SDHC device * @retval 0 tuning succeeded, card is ready for commands - * @retval -ETIMEDOUT: tuning failed after timeout - * @retval -ENOTSUP: controller does not support tuning - * @retval -EIO: I/O error while tuning + * @retval -ETIMEDOUT tuning failed after timeout + * @retval -ENOTSUP controller does not support tuning + * @retval -EIO I/O error while tuning */ __syscall int sdhc_execute_tuning(const struct device *dev); @@ -468,8 +468,8 @@ static inline int z_impl_sdhc_get_host_props(const struct device *dev, * indicating which interrupts should produce a callback * @param user_data: parameter that will be passed to callback function * @retval 0 interrupts were enabled, and callback was installed - * @retval -ENOTSUP: controller does not support this function - * @retval -EIO: I/O error + * @retval -ENOTSUP controller does not support this function + * @retval -EIO I/O error */ __syscall int sdhc_enable_interrupt(const struct device *dev, sdhc_interrupt_cb_t callback, @@ -497,8 +497,8 @@ static inline int z_impl_sdhc_enable_interrupt(const struct device *dev, * @param sources: bitmask of @ref sdhc_interrupt_source values * indicating which interrupts should be disabled. * @retval 0 interrupts were disabled - * @retval -ENOTSUP: controller does not support this function - * @retval -EIO: I/O error + * @retval -ENOTSUP controller does not support this function + * @retval -EIO I/O error */ __syscall int sdhc_disable_interrupt(const struct device *dev, int sources); diff --git a/include/zephyr/drivers/spi.h b/include/zephyr/drivers/spi.h index 3d129780b2d0..6e1759b6db90 100644 --- a/include/zephyr/drivers/spi.h +++ b/include/zephyr/drivers/spi.h @@ -149,7 +149,7 @@ extern "C" { * @brief Get SPI word size in bits from a @ref spi_operation_t * * @param operation A @ref spi_operation_t from which to get the configured word size. - * @retval The size (in bits) of a spi word for the operation. + * @return The size (in bits) of a spi word for the operation. */ #define SPI_WORD_SIZE_GET(operation) \ (((operation) & SPI_WORD_SIZE_MASK) >> SPI_WORD_SIZE_SHIFT) @@ -158,7 +158,7 @@ extern "C" { * @brief Get a bitmask to set the word size in a @ref spi_operation_t * * @param word_size The size of a SPI data frame in bits. - * @retval A bitmask to apply to a @ref spi_operation_t + * @return A bitmask to apply to a @ref spi_operation_t */ #define SPI_WORD_SET(word_size) \ ((word_size) << SPI_WORD_SIZE_SHIFT) diff --git a/include/zephyr/drivers/tee.h b/include/zephyr/drivers/tee.h index 11ae15bab05a..04d46cebc869 100644 --- a/include/zephyr/drivers/tee.h +++ b/include/zephyr/drivers/tee.h @@ -640,7 +640,7 @@ static inline int z_impl_tee_suppl_recv(const struct device *dev, uint32_t *func * @param param List of the params for send/receive * * @retval -ENOSYS If callback was not implemented - * @retval Return value for sent request + * @return Return value for sent request * * @retval 0 On success, negative on error */ diff --git a/include/zephyr/drivers/video.h b/include/zephyr/drivers/video.h index ded70a80ff89..cf66a674c15a 100644 --- a/include/zephyr/drivers/video.h +++ b/include/zephyr/drivers/video.h @@ -870,7 +870,7 @@ static inline int video_get_selection(const struct device *dev, struct video_sel * @param align Alignment of the requested memory, must be a power of two. * @param timeout Timeout duration or K_NO_WAIT * - * @retval pointer to allocated video buffer + * @return pointer to allocated video buffer */ struct video_buffer *video_buffer_aligned_alloc(size_t size, size_t align, k_timeout_t timeout); @@ -880,7 +880,7 @@ struct video_buffer *video_buffer_aligned_alloc(size_t size, size_t align, k_tim * @param size Size of the video buffer (in bytes). * @param timeout Timeout duration or K_NO_WAIT * - * @retval pointer to allocated video buffer + * @return pointer to allocated video buffer */ struct video_buffer *video_buffer_alloc(size_t size, k_timeout_t timeout); From 1fd24cf281919b9bffb473fbcaaf08223d2915f5 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Wed, 5 Nov 2025 06:13:20 -0500 Subject: [PATCH 117/143] UPSTREAM: drivers: promote APIs from unstable to stable commit 4704c6d24d95c38968e079b0532a0de48ada736c upstream. Quite a few well established APIs are stuck in unstable status, move them to stable. Signed-off-by: Anas Nashif --- include/zephyr/drivers/counter.h | 2 +- include/zephyr/drivers/dac.h | 2 +- include/zephyr/drivers/mipi_dbi.h | 2 +- include/zephyr/drivers/mipi_dsi.h | 2 +- include/zephyr/drivers/retained_mem.h | 2 +- include/zephyr/drivers/uart.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index cc0bf024980e..cad0909ef078 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -18,7 +18,7 @@ * @brief Interfaces for counters. * @defgroup counter_interface Counter * @since 1.14 - * @version 0.8.0 + * @version 1.0.0 * @ingroup io_interfaces * @{ */ diff --git a/include/zephyr/drivers/dac.h b/include/zephyr/drivers/dac.h index d603f8701cef..1a2365fc2bf3 100644 --- a/include/zephyr/drivers/dac.h +++ b/include/zephyr/drivers/dac.h @@ -23,7 +23,7 @@ extern "C" { * @brief Interfaces for Digital-to-Analog Converters. * @defgroup dac_interface DAC * @since 2.3 - * @version 0.8.0 + * @version 1.0.0 * @ingroup io_interfaces * @{ */ diff --git a/include/zephyr/drivers/mipi_dbi.h b/include/zephyr/drivers/mipi_dbi.h index 85ed9e58475f..b47c9b1749ae 100644 --- a/include/zephyr/drivers/mipi_dbi.h +++ b/include/zephyr/drivers/mipi_dbi.h @@ -26,7 +26,7 @@ * @brief Interfaces for MIPI-DBI (Display Bus Interface). * @defgroup mipi_dbi_interface MIPI-DBI * @since 3.6 - * @version 0.8.0 + * @version 1.0.0 * @ingroup display_interface * @{ */ diff --git a/include/zephyr/drivers/mipi_dsi.h b/include/zephyr/drivers/mipi_dsi.h index 3e26436317a3..c726b0942b90 100644 --- a/include/zephyr/drivers/mipi_dsi.h +++ b/include/zephyr/drivers/mipi_dsi.h @@ -17,7 +17,7 @@ * @brief Interfaces for MIPI-DSI (Display Serial Interface). * @defgroup mipi_dsi_interface MIPI-DSI * @since 3.1 - * @version 0.8.0 + * @version 1.0.0 * @ingroup display_interface * @{ */ diff --git a/include/zephyr/drivers/retained_mem.h b/include/zephyr/drivers/retained_mem.h index 45650baf89fc..fe2deca73040 100644 --- a/include/zephyr/drivers/retained_mem.h +++ b/include/zephyr/drivers/retained_mem.h @@ -32,7 +32,7 @@ BUILD_ASSERT(!(sizeof(off_t) > sizeof(size_t)), * @brief Interfaces for retained memory. * @defgroup retained_mem_interface Retained memory * @since 3.4 - * @version 0.8.0 + * @version 1.0.0 * @ingroup io_interfaces * @{ */ diff --git a/include/zephyr/drivers/uart.h b/include/zephyr/drivers/uart.h index b4edee131099..1009a9d1d12a 100644 --- a/include/zephyr/drivers/uart.h +++ b/include/zephyr/drivers/uart.h @@ -147,7 +147,7 @@ typedef void (*uart_irq_callback_user_data_t)(const struct device *dev, * * @defgroup uart_async Async UART API * @since 1.14 - * @version 0.8.0 + * @version 1.0.0 * @{ */ From 2649d1efe654662a3ac8ff242d940e2853fc43fc Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Thu, 14 Aug 2025 16:40:18 -0700 Subject: [PATCH 118/143] UPSTREAM: drivers: counter: introduce counter set value api commit 34befefd939f669039c2e6dce266fc01e0a3ae56 upstream. Introduce a counter set value api to set the ticks for 32b and 64b. Signed-off-by: Ryan McClelland --- include/zephyr/drivers/counter.h | 52 ++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index cad0909ef078..871bc33cbaa3 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -217,6 +217,10 @@ typedef int (*counter_api_get_value)(const struct device *dev, typedef int (*counter_api_get_value_64)(const struct device *dev, uint64_t *ticks); typedef int (*counter_api_reset)(const struct device *dev); +typedef int (*counter_api_set_value)(const struct device *dev, + uint32_t ticks); +typedef int (*counter_api_set_value_64)(const struct device *dev, + uint64_t ticks); typedef int (*counter_api_set_alarm)(const struct device *dev, uint8_t chan_id, const struct counter_alarm_cfg *alarm_cfg); @@ -239,6 +243,8 @@ __subsystem struct counter_driver_api { counter_api_get_value get_value; counter_api_get_value_64 get_value_64; counter_api_reset reset; + counter_api_set_value set_value; + counter_api_set_value_64 set_value_64; counter_api_set_alarm set_alarm; counter_api_cancel_alarm cancel_alarm; counter_api_set_top_value set_top_value; @@ -471,6 +477,52 @@ static inline int z_impl_counter_reset(const struct device *dev) return api->reset(dev); } +/** + * @brief Set current counter value. + * @param dev Pointer to the device structure for the driver instance. + * @param ticks Tick value to set + * + * @retval 0 If successful. + * @retval Negative error code on failure setting the counter value + */ +__syscall int counter_set_value(const struct device *dev, uint32_t ticks); + +static inline int z_impl_counter_set_value(const struct device *dev, + uint32_t ticks) +{ + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (!api->set_value) { + return -ENOSYS; + } + + return api->set_value(dev, ticks); +} + +/** + * @brief Set current counter 64-bit value. + * @param dev Pointer to the device structure for the driver instance. + * @param ticks Tick value to set + * + * @retval 0 If successful. + * @retval Negative error code on failure setting the counter value + */ +__syscall int counter_set_value_64(const struct device *dev, uint64_t ticks); + +static inline int z_impl_counter_set_value_64(const struct device *dev, + uint64_t ticks) +{ + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (!api->set_value_64) { + return -ENOSYS; + } + + return api->set_value_64(dev, ticks); +} + /** * @brief Set a single shot alarm on a channel. * From 178541ea0a00dfdd45aa0f43fe345dc9acc675fb Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Thu, 20 Nov 2025 19:10:53 -0800 Subject: [PATCH 119/143] UPSTREAM: drivers: counter: add ns to ticks helper commit 849ba3a33f33fb5ceea71f2abb7a751c9b6be909 upstream. There is already a helper for converting ticks to ns. Add one that does the opposite. Signed-off-by: Ryan McClelland --- include/zephyr/drivers/counter.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index 871bc33cbaa3..603ada6fcd6f 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -344,6 +344,23 @@ static inline uint64_t z_impl_counter_ticks_to_us(const struct device *dev, return ((uint64_t)ticks * USEC_PER_SEC) / z_impl_counter_get_frequency(dev); } +/** + * @brief Function to convert nanoseconds to ticks. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * @param[in] ns Nanoseconds. + * + * @return Converted ticks. Ticks will be saturated if exceed 32 bits. + */ +__syscall uint32_t counter_ns_to_ticks(const struct device *dev, uint64_t ns); + +static inline uint32_t z_impl_counter_ns_to_ticks(const struct device *dev, uint64_t ns) +{ + uint64_t ticks = (ns * get_frequency(dev)) / NSEC_PER_SEC; + + return (ticks > (uint64_t)UINT32_MAX) ? UINT32_MAX : ticks; +} + /** * @brief Function to convert ticks to nanoseconds. * From 6123f01fb15853255d268661eac95619ed2b8e97 Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Sat, 26 Apr 2025 00:24:04 -0500 Subject: [PATCH 120/143] UPSTREAM: drivers: counter: add kconfig for enabling... commit fd72085d9bc16225f15ee6ffaf2296ec9478f68b upstream Some counters, such as those that use fractional adders, tick at resolutions greater than 4294967295Hz. This changes the freq in the common info to a uint64_t. This adds a KConfig that can be selected by a driver. This also has the counter ticks to sec (and vice versa) helper functions call the get frequency for 64 bits if the kconfig is selected. Signed-off-by: Ryan McClelland --- drivers/counter/Kconfig | 7 +++ include/zephyr/drivers/counter.h | 93 ++++++++++++++++++++++++++++---- 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 5f1b8d29ac6f..d32a97ffefac 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -22,6 +22,13 @@ config COUNTER_SHELL help Enable Shell Commands for Counter and Timer +config COUNTER_64BITS_FREQ + bool + help + Select this option if the counter driver should use 64-bits for frequency. + This is typically set by drivers that have frequencies greater than + 4,294,967,295 Hz. + module = COUNTER module-str = counter source "subsys/logging/Kconfig.template.log_config" diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index 603ada6fcd6f..8ab877274a10 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -190,14 +190,18 @@ struct counter_top_cfg { /** @brief Structure with generic counter features. */ struct counter_config_info { - /** - * Maximal (default) top value on which counter is reset (cleared or reloaded). - */ - uint32_t max_top_value; /** * Frequency of the source clock if synchronous events are counted. */ +#ifdef CONFIG_COUNTER_64BITS_FREQ + uint64_t freq; +#else uint32_t freq; +#endif + /** + * Maximal (default) top value on which counter is reset (cleared or reloaded). + */ + uint32_t max_top_value; /** * Flags (see @ref COUNTER_FLAGS). */ @@ -236,6 +240,7 @@ typedef int (*counter_api_set_guard_period)(const struct device *dev, uint32_t ticks, uint32_t flags); typedef uint32_t (*counter_api_get_freq)(const struct device *dev); +typedef uint64_t (*counter_api_get_freq_64)(const struct device *dev); __subsystem struct counter_driver_api { counter_api_start start; @@ -253,6 +258,9 @@ __subsystem struct counter_driver_api { counter_api_get_guard_period get_guard_period; counter_api_set_guard_period set_guard_period; counter_api_get_freq get_freq; +#ifdef CONFIG_COUNTER_64BITS_FREQ + counter_api_get_freq_64 get_freq_64; +#endif /* CONFIG_COUNTER_64BITS_FREQ */ }; /** @@ -290,16 +298,43 @@ static inline uint8_t z_impl_counter_get_num_of_channels(const struct device *de return config->channels; } +__syscall uint32_t counter_get_frequency(const struct device *dev); + +#ifdef CONFIG_COUNTER_64BITS_FREQ /** * @brief Function to get counter frequency. * * @param[in] dev Pointer to the device structure for the driver instance. * * @return Frequency of the counter in Hz, or zero if the counter does - * not have a fixed frequency. + * not have a fixed frequency, or UINT32_MAX if the counter frequency + * is higher or equal to UINT32_MAX, in which case it is recommended to + * use counter_get_frequency_64(). */ -__syscall uint32_t counter_get_frequency(const struct device *dev); +static inline uint32_t z_impl_counter_get_frequency(const struct device *dev) +{ + const struct counter_config_info *config = + (const struct counter_config_info *)dev->config; + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + if (api->get_freq) { + return api->get_freq(dev); + } else { + return config->freq > UINT32_MAX ? UINT32_MAX : (uint32_t)config->freq; + } +} + +#else + +/** + * @brief Function to get counter frequency. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * + * @return Frequency of the counter in Hz, or zero if the counter does + * not have a fixed frequency. + */ static inline uint32_t z_impl_counter_get_frequency(const struct device *dev) { const struct counter_config_info *config = @@ -309,6 +344,44 @@ static inline uint32_t z_impl_counter_get_frequency(const struct device *dev) return api->get_freq ? api->get_freq(dev) : config->freq; } +#endif + +/** + * @brief Function to get counter frequency in 64bits. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * + * @return Frequency of the counter in Hz, or zero if the counter does + * not have a fixed frequency. + */ +__syscall uint64_t counter_get_frequency_64(const struct device *dev); + +static inline uint64_t z_impl_counter_get_frequency_64(const struct device *dev) +{ +#ifdef CONFIG_COUNTER_64BITS_FREQ + const struct counter_config_info *config = + (const struct counter_config_info *)dev->config; + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (api->get_freq_64) { + return api->get_freq_64(dev); + } else if (api->get_freq) { + return (uint64_t)api->get_freq(dev); + } else { + return config->freq; + } +#else + ARG_UNUSED(dev); + return -ENOTSUP; +#endif +} + +#ifdef CONFIG_COUNTER_64BITS_FREQ +#define z_counter_get_frequency z_impl_counter_get_frequency_64 +#else +#define z_counter_get_frequency z_impl_counter_get_frequency +#endif /** * @brief Function to convert microseconds to ticks. @@ -323,7 +396,7 @@ __syscall uint32_t counter_us_to_ticks(const struct device *dev, uint64_t us); static inline uint32_t z_impl_counter_us_to_ticks(const struct device *dev, uint64_t us) { - uint64_t ticks = (us * z_impl_counter_get_frequency(dev)) / USEC_PER_SEC; + uint64_t ticks = (us * z_counter_get_frequency(dev)) / USEC_PER_SEC; return (ticks > (uint64_t)UINT32_MAX) ? UINT32_MAX : ticks; } @@ -341,7 +414,7 @@ __syscall uint64_t counter_ticks_to_us(const struct device *dev, uint32_t ticks) static inline uint64_t z_impl_counter_ticks_to_us(const struct device *dev, uint32_t ticks) { - return ((uint64_t)ticks * USEC_PER_SEC) / z_impl_counter_get_frequency(dev); + return ((uint64_t)ticks * USEC_PER_SEC) / z_counter_get_frequency(dev); } /** @@ -356,7 +429,7 @@ __syscall uint32_t counter_ns_to_ticks(const struct device *dev, uint64_t ns); static inline uint32_t z_impl_counter_ns_to_ticks(const struct device *dev, uint64_t ns) { - uint64_t ticks = (ns * get_frequency(dev)) / NSEC_PER_SEC; + uint64_t ticks = (ns * z_counter_get_frequency(dev)) / NSEC_PER_SEC; return (ticks > (uint64_t)UINT32_MAX) ? UINT32_MAX : ticks; } @@ -374,7 +447,7 @@ __syscall uint64_t counter_ticks_to_ns(const struct device *dev, uint32_t ticks) static inline uint64_t z_impl_counter_ticks_to_ns(const struct device *dev, uint32_t ticks) { - return ((uint64_t)ticks * NSEC_PER_SEC) / z_impl_counter_get_frequency(dev); + return ((uint64_t)ticks * NSEC_PER_SEC) / z_counter_get_frequency(dev); } /** From ee5ad2af0f7d4706bbd3b98ece941ffb4ce12467 Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Thu, 20 Nov 2025 20:20:48 -0800 Subject: [PATCH 121/143] UPSTREAM: drivers: counter: run clang-format on counter.h commit ac8f2200ae6005459abe3ed8b1595f3297324caf upstream. Run clang-format on counter.h Signed-off-by: Ryan McClelland --- include/zephyr/drivers/counter.h | 156 +++++++++++-------------------- 1 file changed, 53 insertions(+), 103 deletions(-) diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index 8ab877274a10..c660225a0e39 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -93,7 +93,7 @@ extern "C" { * * Alarm callback must be called from the same context as if it was set on time. */ -#define COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE BIT(1) +#define COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE BIT(1) /**@} */ @@ -120,8 +120,7 @@ extern "C" { * @param ticks Counter value that triggered the alarm. * @param user_data User data. */ -typedef void (*counter_alarm_callback_t)(const struct device *dev, - uint8_t chan_id, uint32_t ticks, +typedef void (*counter_alarm_callback_t)(const struct device *dev, uint8_t chan_id, uint32_t ticks, void *user_data); /** @brief Alarm callback structure. @@ -163,8 +162,7 @@ struct counter_alarm_cfg { * @param dev Pointer to the device structure for the driver instance. * @param user_data User data provided in @ref counter_set_top_value. */ -typedef void (*counter_top_callback_t)(const struct device *dev, - void *user_data); +typedef void (*counter_top_callback_t)(const struct device *dev, void *user_data); /** @brief Top value configuration structure. */ @@ -216,29 +214,21 @@ struct counter_config_info { typedef int (*counter_api_start)(const struct device *dev); typedef int (*counter_api_stop)(const struct device *dev); -typedef int (*counter_api_get_value)(const struct device *dev, - uint32_t *ticks); -typedef int (*counter_api_get_value_64)(const struct device *dev, - uint64_t *ticks); +typedef int (*counter_api_get_value)(const struct device *dev, uint32_t *ticks); +typedef int (*counter_api_get_value_64)(const struct device *dev, uint64_t *ticks); typedef int (*counter_api_reset)(const struct device *dev); -typedef int (*counter_api_set_value)(const struct device *dev, - uint32_t ticks); -typedef int (*counter_api_set_value_64)(const struct device *dev, - uint64_t ticks); -typedef int (*counter_api_set_alarm)(const struct device *dev, - uint8_t chan_id, +typedef int (*counter_api_set_value)(const struct device *dev, uint32_t ticks); +typedef int (*counter_api_set_value_64)(const struct device *dev, uint64_t ticks); +typedef int (*counter_api_set_alarm)(const struct device *dev, uint8_t chan_id, const struct counter_alarm_cfg *alarm_cfg); -typedef int (*counter_api_cancel_alarm)(const struct device *dev, - uint8_t chan_id); +typedef int (*counter_api_cancel_alarm)(const struct device *dev, uint8_t chan_id); typedef int (*counter_api_set_top_value)(const struct device *dev, const struct counter_top_cfg *cfg); typedef uint32_t (*counter_api_get_pending_int)(const struct device *dev); typedef uint32_t (*counter_api_get_top_value)(const struct device *dev); -typedef uint32_t (*counter_api_get_guard_period)(const struct device *dev, - uint32_t flags); -typedef int (*counter_api_set_guard_period)(const struct device *dev, - uint32_t ticks, - uint32_t flags); +typedef uint32_t (*counter_api_get_guard_period)(const struct device *dev, uint32_t flags); +typedef int (*counter_api_set_guard_period)(const struct device *dev, uint32_t ticks, + uint32_t flags); typedef uint32_t (*counter_api_get_freq)(const struct device *dev); typedef uint64_t (*counter_api_get_freq_64)(const struct device *dev); @@ -275,8 +265,7 @@ __syscall bool counter_is_counting_up(const struct device *dev); static inline bool z_impl_counter_is_counting_up(const struct device *dev) { - const struct counter_config_info *config = - (const struct counter_config_info *)dev->config; + const struct counter_config_info *config = (const struct counter_config_info *)dev->config; return config->flags & COUNTER_CONFIG_INFO_COUNT_UP; } @@ -292,8 +281,7 @@ __syscall uint8_t counter_get_num_of_channels(const struct device *dev); static inline uint8_t z_impl_counter_get_num_of_channels(const struct device *dev) { - const struct counter_config_info *config = - (const struct counter_config_info *)dev->config; + const struct counter_config_info *config = (const struct counter_config_info *)dev->config; return config->channels; } @@ -313,10 +301,8 @@ __syscall uint32_t counter_get_frequency(const struct device *dev); */ static inline uint32_t z_impl_counter_get_frequency(const struct device *dev) { - const struct counter_config_info *config = - (const struct counter_config_info *)dev->config; - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_config_info *config = (const struct counter_config_info *)dev->config; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; if (api->get_freq) { return api->get_freq(dev); @@ -337,10 +323,8 @@ static inline uint32_t z_impl_counter_get_frequency(const struct device *dev) */ static inline uint32_t z_impl_counter_get_frequency(const struct device *dev) { - const struct counter_config_info *config = - (const struct counter_config_info *)dev->config; - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_config_info *config = (const struct counter_config_info *)dev->config; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; return api->get_freq ? api->get_freq(dev) : config->freq; } @@ -359,10 +343,8 @@ __syscall uint64_t counter_get_frequency_64(const struct device *dev); static inline uint64_t z_impl_counter_get_frequency_64(const struct device *dev) { #ifdef CONFIG_COUNTER_64BITS_FREQ - const struct counter_config_info *config = - (const struct counter_config_info *)dev->config; - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_config_info *config = (const struct counter_config_info *)dev->config; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; if (api->get_freq_64) { return api->get_freq_64(dev); @@ -393,8 +375,7 @@ static inline uint64_t z_impl_counter_get_frequency_64(const struct device *dev) */ __syscall uint32_t counter_us_to_ticks(const struct device *dev, uint64_t us); -static inline uint32_t z_impl_counter_us_to_ticks(const struct device *dev, - uint64_t us) +static inline uint32_t z_impl_counter_us_to_ticks(const struct device *dev, uint64_t us) { uint64_t ticks = (us * z_counter_get_frequency(dev)) / USEC_PER_SEC; @@ -411,8 +392,7 @@ static inline uint32_t z_impl_counter_us_to_ticks(const struct device *dev, */ __syscall uint64_t counter_ticks_to_us(const struct device *dev, uint32_t ticks); -static inline uint64_t z_impl_counter_ticks_to_us(const struct device *dev, - uint32_t ticks) +static inline uint64_t z_impl_counter_ticks_to_us(const struct device *dev, uint32_t ticks) { return ((uint64_t)ticks * USEC_PER_SEC) / z_counter_get_frequency(dev); } @@ -444,8 +424,7 @@ static inline uint32_t z_impl_counter_ns_to_ticks(const struct device *dev, uint */ __syscall uint64_t counter_ticks_to_ns(const struct device *dev, uint32_t ticks); -static inline uint64_t z_impl_counter_ticks_to_ns(const struct device *dev, - uint32_t ticks) +static inline uint64_t z_impl_counter_ticks_to_ns(const struct device *dev, uint32_t ticks) { return ((uint64_t)ticks * NSEC_PER_SEC) / z_counter_get_frequency(dev); } @@ -461,8 +440,7 @@ __syscall uint32_t counter_get_max_top_value(const struct device *dev); static inline uint32_t z_impl_counter_get_max_top_value(const struct device *dev) { - const struct counter_config_info *config = - (const struct counter_config_info *)dev->config; + const struct counter_config_info *config = (const struct counter_config_info *)dev->config; return config->max_top_value; } @@ -479,8 +457,7 @@ __syscall int counter_start(const struct device *dev); static inline int z_impl_counter_start(const struct device *dev) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; return api->start(dev); } @@ -498,8 +475,7 @@ __syscall int counter_stop(const struct device *dev); static inline int z_impl_counter_stop(const struct device *dev) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; return api->stop(dev); } @@ -514,11 +490,9 @@ static inline int z_impl_counter_stop(const struct device *dev) */ __syscall int counter_get_value(const struct device *dev, uint32_t *ticks); -static inline int z_impl_counter_get_value(const struct device *dev, - uint32_t *ticks) +static inline int z_impl_counter_get_value(const struct device *dev, uint32_t *ticks) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; return api->get_value(dev, ticks); } @@ -533,11 +507,9 @@ static inline int z_impl_counter_get_value(const struct device *dev, */ __syscall int counter_get_value_64(const struct device *dev, uint64_t *ticks); -static inline int z_impl_counter_get_value_64(const struct device *dev, - uint64_t *ticks) +static inline int z_impl_counter_get_value_64(const struct device *dev, uint64_t *ticks) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; if (!api->get_value_64) { return -ENOSYS; @@ -557,8 +529,7 @@ __syscall int counter_reset(const struct device *dev); static inline int z_impl_counter_reset(const struct device *dev) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; if (!api->reset) { return -ENOSYS; @@ -577,11 +548,9 @@ static inline int z_impl_counter_reset(const struct device *dev) */ __syscall int counter_set_value(const struct device *dev, uint32_t ticks); -static inline int z_impl_counter_set_value(const struct device *dev, - uint32_t ticks) +static inline int z_impl_counter_set_value(const struct device *dev, uint32_t ticks) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; if (!api->set_value) { return -ENOSYS; @@ -600,11 +569,9 @@ static inline int z_impl_counter_set_value(const struct device *dev, */ __syscall int counter_set_value_64(const struct device *dev, uint64_t ticks); -static inline int z_impl_counter_set_value_64(const struct device *dev, - uint64_t ticks) +static inline int z_impl_counter_set_value_64(const struct device *dev, uint64_t ticks) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; if (!api->set_value_64) { return -ENOSYS; @@ -633,16 +600,13 @@ static inline int z_impl_counter_set_value_64(const struct device *dev, * @retval -ETIME if absolute alarm was set too late. * @retval -EBUSY if alarm is already active. */ -__syscall int counter_set_channel_alarm(const struct device *dev, - uint8_t chan_id, +__syscall int counter_set_channel_alarm(const struct device *dev, uint8_t chan_id, const struct counter_alarm_cfg *alarm_cfg); -static inline int z_impl_counter_set_channel_alarm(const struct device *dev, - uint8_t chan_id, +static inline int z_impl_counter_set_channel_alarm(const struct device *dev, uint8_t chan_id, const struct counter_alarm_cfg *alarm_cfg) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; if (chan_id >= counter_get_num_of_channels(dev)) { return -ENOTSUP; @@ -663,14 +627,11 @@ static inline int z_impl_counter_set_channel_alarm(const struct device *dev, * @retval -ENOTSUP if request is not supported or the counter was not started * yet. */ -__syscall int counter_cancel_channel_alarm(const struct device *dev, - uint8_t chan_id); +__syscall int counter_cancel_channel_alarm(const struct device *dev, uint8_t chan_id); -static inline int z_impl_counter_cancel_channel_alarm(const struct device *dev, - uint8_t chan_id) +static inline int z_impl_counter_cancel_channel_alarm(const struct device *dev, uint8_t chan_id) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; if (chan_id >= counter_get_num_of_channels(dev)) { return -ENOTSUP; @@ -703,15 +664,12 @@ static inline int z_impl_counter_cancel_channel_alarm(const struct device *dev, * @retval -ETIME if @ref COUNTER_TOP_CFG_DONT_RESET was set and new top value * is smaller than current counter value (counter counting up). */ -__syscall int counter_set_top_value(const struct device *dev, - const struct counter_top_cfg *cfg); +__syscall int counter_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg); static inline int z_impl_counter_set_top_value(const struct device *dev, - const struct counter_top_cfg - *cfg) + const struct counter_top_cfg *cfg) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; if (cfg->ticks > counter_get_max_top_value(dev)) { return -EINVAL; @@ -737,8 +695,7 @@ __syscall int counter_get_pending_int(const struct device *dev); static inline int z_impl_counter_get_pending_int(const struct device *dev) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; return api->get_pending_int(dev); } @@ -754,8 +711,7 @@ __syscall uint32_t counter_get_top_value(const struct device *dev); static inline uint32_t z_impl_counter_get_top_value(const struct device *dev) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; return api->get_top_value(dev); } @@ -812,15 +768,12 @@ static inline uint32_t z_impl_counter_get_top_value(const struct device *dev) * @retval -ENOSYS if function or flags are not supported. * @retval -EINVAL if ticks value is invalid. */ -__syscall int counter_set_guard_period(const struct device *dev, - uint32_t ticks, - uint32_t flags); +__syscall int counter_set_guard_period(const struct device *dev, uint32_t ticks, uint32_t flags); -static inline int z_impl_counter_set_guard_period(const struct device *dev, - uint32_t ticks, uint32_t flags) +static inline int z_impl_counter_set_guard_period(const struct device *dev, uint32_t ticks, + uint32_t flags) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; if (!api->set_guard_period) { return -ENOSYS; @@ -840,14 +793,11 @@ static inline int z_impl_counter_set_guard_period(const struct device *dev, * @return Guard period given in counter ticks or 0 if function or flags are * not supported. */ -__syscall uint32_t counter_get_guard_period(const struct device *dev, - uint32_t flags); +__syscall uint32_t counter_get_guard_period(const struct device *dev, uint32_t flags); -static inline uint32_t z_impl_counter_get_guard_period(const struct device *dev, - uint32_t flags) +static inline uint32_t z_impl_counter_get_guard_period(const struct device *dev, uint32_t flags) { - const struct counter_driver_api *api = - (struct counter_driver_api *)dev->api; + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; return (api->get_guard_period) ? api->get_guard_period(dev, flags) : 0; } From b03f1c464e9369491853c00fa48e033e81bedc59 Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Sun, 9 Nov 2025 11:33:43 -0800 Subject: [PATCH 122/143] UPSTREAM: drivers: counter: add 64bit apis commit 779383e769071367e447425e994c7811900fe5ed upstream. Add apis for 64 bits. Signed-off-by: Ryan McClelland --- drivers/counter/Kconfig | 7 + drivers/counter/Kconfig.ace | 2 + drivers/counter/Kconfig.cc23x0_rtc | 1 + drivers/counter/Kconfig.esp32_tmr | 1 + drivers/counter/Kconfig.stm32_rtc | 1 + drivers/counter/counter_esp32_tmr.c | 122 +++++-- drivers/counter/counter_handlers.c | 2 + drivers/sensor/sensor_clock_external.c | 5 +- include/zephyr/drivers/counter.h | 436 ++++++++++++++++++++++--- 9 files changed, 508 insertions(+), 69 deletions(-) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index d32a97ffefac..66a0017285a7 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -29,6 +29,13 @@ config COUNTER_64BITS_FREQ This is typically set by drivers that have frequencies greater than 4,294,967,295 Hz. +config COUNTER_64BITS_TICKS + bool + help + Select this option if the counter driver should use 64-bits for ticks. + This is typically set by drivers that require extended + tick ranges beyond the 32-bit limit. + module = COUNTER module-str = counter source "subsys/logging/Kconfig.template.log_config" diff --git a/drivers/counter/Kconfig.ace b/drivers/counter/Kconfig.ace index a7c5d611a92c..d3f24cb24e74 100644 --- a/drivers/counter/Kconfig.ace +++ b/drivers/counter/Kconfig.ace @@ -5,6 +5,7 @@ config ACE_V1X_ART_COUNTER bool "DSP ART Wall Clock for ACE V1X" depends on DT_HAS_INTEL_ACE_ART_COUNTER_ENABLED + select COUNTER_64BITS_TICKS default y help DSP ART Wall Clock used by ACE V1X. @@ -12,6 +13,7 @@ config ACE_V1X_ART_COUNTER config ACE_V1X_RTC_COUNTER bool "DSP RTC Wall Clock for ACE V1X" depends on DT_HAS_INTEL_ACE_RTC_COUNTER_ENABLED + select COUNTER_64BITS_TICKS default y help DSP RTC Wall Clock used by ACE V1X. diff --git a/drivers/counter/Kconfig.cc23x0_rtc b/drivers/counter/Kconfig.cc23x0_rtc index ce4e9ad6b2c1..0f04e9fb29c1 100644 --- a/drivers/counter/Kconfig.cc23x0_rtc +++ b/drivers/counter/Kconfig.cc23x0_rtc @@ -6,5 +6,6 @@ config COUNTER_CC23X0_RTC default y depends on DT_HAS_TI_CC23X0_RTC_ENABLED depends on CC23X0_SYSTIM_TIMER + select COUNTER_64BITS_TICKS help Enable counter driver based on RTC timer for cc23x0 diff --git a/drivers/counter/Kconfig.esp32_tmr b/drivers/counter/Kconfig.esp32_tmr index 099c1d820640..86d39871fc25 100644 --- a/drivers/counter/Kconfig.esp32_tmr +++ b/drivers/counter/Kconfig.esp32_tmr @@ -7,6 +7,7 @@ config COUNTER_TMR_ESP32 bool "ESP32 Counter Driver based on GP-Timers" default y depends on DT_HAS_ESPRESSIF_ESP32_COUNTER_ENABLED + select COUNTER_64BITS_TICKS help Enables the Counter driver API based on Espressif's General Purpose Timers for ESP32 series devices. diff --git a/drivers/counter/Kconfig.stm32_rtc b/drivers/counter/Kconfig.stm32_rtc index 4273d1c49313..83ba82b57f80 100644 --- a/drivers/counter/Kconfig.stm32_rtc +++ b/drivers/counter/Kconfig.stm32_rtc @@ -23,6 +23,7 @@ config COUNTER_RTC_STM32_SAVE_VALUE_BETWEEN_RESETS config COUNTER_RTC_STM32_SUBSECONDS bool "Use the subseconds as a basic tick." depends on !SOC_SERIES_STM32F1X + select COUNTER_64BITS_TICKS help Use the subseconds as the basic time tick. It increases resolution of the counter. The frequency of the time is RTC Source Clock divided diff --git a/drivers/counter/counter_esp32_tmr.c b/drivers/counter/counter_esp32_tmr.c index 995b5101c2b1..49ab808526da 100644 --- a/drivers/counter/counter_esp32_tmr.c +++ b/drivers/counter/counter_esp32_tmr.c @@ -26,10 +26,10 @@ typedef bool (*timer_isr_t)(void *); struct counter_esp32_top_data { counter_top_callback_t callback; - uint32_t ticks; + uint64_t ticks; void *user_data; bool auto_reload; - uint32_t guard_period; + uint64_t guard_period; }; struct counter_esp32_config { @@ -45,9 +45,10 @@ struct counter_esp32_config { }; struct counter_esp32_data { - struct counter_alarm_cfg alarm_cfg; + struct counter_alarm_cfg_64 alarm_cfg; + counter_alarm_callback_t alarm_callback_32; struct counter_esp32_top_data top_data; - uint32_t ticks; + uint64_t ticks; uint32_t clock_src_hz; timer_hal_context_t hal_ctx; }; @@ -140,18 +141,18 @@ static int counter_esp32_get_value_64(const struct device *dev, uint64_t *ticks) return 0; } -static int counter_esp32_set_alarm(const struct device *dev, uint8_t chan_id, - const struct counter_alarm_cfg *alarm_cfg) +static int counter_esp32_set_alarm_64(const struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg_64 *alarm_cfg) { ARG_UNUSED(chan_id); struct counter_esp32_data *data = dev->data; bool absolute = alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE; - uint32_t ticks = alarm_cfg->ticks; - uint32_t top = data->top_data.ticks; - uint32_t max_rel_val = data->top_data.ticks; + uint64_t ticks = alarm_cfg->ticks; + uint64_t top = data->top_data.ticks; + uint64_t max_rel_val = data->top_data.ticks; uint64_t now; uint64_t target; - uint32_t diff; + uint64_t diff; int err = 0; bool irq_on_late = 0; @@ -177,7 +178,7 @@ static int counter_esp32_set_alarm(const struct device *dev, uint8_t chan_id, timer_ll_set_alarm_value(data->hal_ctx.dev, data->hal_ctx.timer_id, target); - diff = (alarm_cfg->ticks - (uint32_t)now); + diff = (alarm_cfg->ticks - now); if (diff > max_rel_val) { if (absolute) { err = -ETIME; @@ -216,21 +217,22 @@ static int counter_esp32_cancel_alarm(const struct device *dev, uint8_t chan_id) return 0; } -static int counter_esp32_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg) +static int counter_esp32_set_top_value_64(const struct device *dev, + const struct counter_top_cfg_64 *cfg) { const struct counter_esp32_config *config = dev->config; struct counter_esp32_data *data = dev->data; - uint32_t now; + uint64_t now; if (data->alarm_cfg.callback) { return -EBUSY; } - if (cfg->ticks > config->counter_info.max_top_value) { + if (cfg->ticks > config->counter_info.max_top_value_64) { return -ENOTSUP; } - counter_esp32_get_value(dev, &now); + counter_esp32_get_value_64(dev, &now); if (!(cfg->flags & COUNTER_TOP_CFG_DONT_RESET)) { timer_hal_set_counter_value(&data->hal_ctx, 0); @@ -267,7 +269,7 @@ static uint32_t counter_esp32_get_pending_int(const struct device *dev) return timer_ll_get_intr_status(data->hal_ctx.dev); } -static uint32_t counter_esp32_get_top_value(const struct device *dev) +static uint64_t counter_esp32_get_top_value_64(const struct device *dev) { struct counter_esp32_data *data = dev->data; @@ -291,7 +293,7 @@ static int counter_esp32_reset(const struct device *dev) return 0; } -static uint32_t counter_esp32_get_guard_period(const struct device *dev, uint32_t flags) +static uint64_t counter_esp32_get_guard_period_64(const struct device *dev, uint32_t flags) { struct counter_esp32_data *data = dev->data; @@ -300,7 +302,8 @@ static uint32_t counter_esp32_get_guard_period(const struct device *dev, uint32_ return data->top_data.guard_period; } -static int counter_esp32_set_guard_period(const struct device *dev, uint32_t ticks, uint32_t flags) +static int counter_esp32_set_guard_period_64(const struct device *dev, uint64_t ticks, + uint32_t flags) { struct counter_esp32_data *data = dev->data; @@ -314,6 +317,76 @@ static int counter_esp32_set_guard_period(const struct device *dev, uint32_t tic return 0; } +static int counter_esp32_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg) +{ + struct counter_top_cfg_64 alarm_cfg_64 = { + .callback = cfg->callback, + .ticks = (uint64_t)cfg->ticks, + .user_data = cfg->user_data, + .flags = cfg->flags, + }; + + return counter_esp32_set_top_value_64(dev, &alarm_cfg_64); +} + +static void counter_esp32_callback_32_trampoline(const struct device *dev, + uint8_t chan_id, + uint64_t ticks, + void *user_data) +{ + struct counter_esp32_data *data = dev->data; + + /* Safely call the original 32-bit callback */ + data->alarm_callback_32(dev, chan_id, (uint32_t)ticks, user_data); +} + +static int counter_esp32_set_alarm(const struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg *alarm_cfg) +{ + struct counter_esp32_data *data = dev->data; + struct counter_alarm_cfg_64 alarm_cfg_64 = { + .callback = counter_esp32_callback_32_trampoline, + .ticks = (uint64_t)alarm_cfg->ticks, + .user_data = alarm_cfg->user_data, + .flags = alarm_cfg->flags, + }; + + /* use trampoline function to handle the function pointer type difference */ + data->alarm_callback_32 = alarm_cfg->callback; + + return counter_esp32_set_alarm_64(dev, chan_id, &alarm_cfg_64); +} + +static uint32_t counter_esp32_get_top_value(const struct device *dev) +{ + struct counter_esp32_data *data = dev->data; + + return (uint32_t)data->top_data.ticks; +} + +static uint32_t counter_esp32_get_guard_period(const struct device *dev, uint32_t flags) +{ + struct counter_esp32_data *data = dev->data; + + ARG_UNUSED(flags); + + return (uint32_t)data->top_data.guard_period; +} + +static int counter_esp32_set_guard_period(const struct device *dev, uint32_t ticks, uint32_t flags) +{ + struct counter_esp32_data *data = dev->data; + + ARG_UNUSED(flags); + + if (ticks > data->top_data.ticks) { + return -EINVAL; + } + + data->top_data.guard_period = (uint64_t)ticks; + return 0; +} + static DEVICE_API(counter, counter_api) = { .start = counter_esp32_start, .stop = counter_esp32_stop, @@ -321,24 +394,29 @@ static DEVICE_API(counter, counter_api) = { .reset = counter_esp32_reset, .get_value_64 = counter_esp32_get_value_64, .set_alarm = counter_esp32_set_alarm, + .set_alarm_64 = counter_esp32_set_alarm_64, .cancel_alarm = counter_esp32_cancel_alarm, .set_top_value = counter_esp32_set_top_value, + .set_top_value_64 = counter_esp32_set_top_value_64, .get_pending_int = counter_esp32_get_pending_int, .get_top_value = counter_esp32_get_top_value, + .get_top_value_64 = counter_esp32_get_top_value_64, .get_freq = counter_esp32_get_freq, .get_guard_period = counter_esp32_get_guard_period, + .get_guard_period_64 = counter_esp32_get_guard_period_64, .set_guard_period = counter_esp32_set_guard_period, + .set_guard_period_64 = counter_esp32_set_guard_period_64, }; static void counter_esp32_isr(void *arg) { const struct device *dev = (const struct device *)arg; struct counter_esp32_data *data = dev->data; - counter_alarm_callback_t cb = data->alarm_cfg.callback; + counter_alarm_callback_64_t cb = data->alarm_cfg.callback; void *cb_data = data->alarm_cfg.user_data; - uint32_t now; + uint64_t now; - counter_esp32_get_value(dev, &now); + counter_esp32_get_value_64(dev, &now); if (cb) { timer_ll_enable_intr(data->hal_ctx.dev, @@ -374,7 +452,7 @@ static void counter_esp32_isr(void *arg) static struct counter_esp32_data counter_data_##idx; \ \ static const struct counter_esp32_config counter_config_##idx = { \ - .counter_info = {.max_top_value = UINT32_MAX, \ + .counter_info = {.max_top_value_64 = UINT64_MAX, \ .flags = COUNTER_CONFIG_INFO_COUNT_UP, \ .channels = 1}, \ .config = \ diff --git a/drivers/counter/counter_handlers.c b/drivers/counter/counter_handlers.c index 697d8d41f963..263e764bb21d 100644 --- a/drivers/counter/counter_handlers.c +++ b/drivers/counter/counter_handlers.c @@ -73,6 +73,7 @@ static inline int z_vrfy_counter_get_value(const struct device *dev, } #include +#ifdef CONFIG_COUNTER_64BITS_TICKS static inline int z_vrfy_counter_get_value_64(const struct device *dev, uint64_t *ticks) { @@ -81,6 +82,7 @@ static inline int z_vrfy_counter_get_value_64(const struct device *dev, return z_impl_counter_get_value_64((const struct device *)dev, ticks); } #include +#endif /* CONFIG_COUNTER_64BITS_TICKS */ static inline int z_vrfy_counter_set_channel_alarm(const struct device *dev, uint8_t chan_id, diff --git a/drivers/sensor/sensor_clock_external.c b/drivers/sensor/sensor_clock_external.c index 5baf1ac15639..f86e9425572e 100644 --- a/drivers/sensor/sensor_clock_external.c +++ b/drivers/sensor/sensor_clock_external.c @@ -40,12 +40,15 @@ int sensor_clock_get_cycles(uint64_t *cycles) __ASSERT_NO_MSG(counter_is_counting_up(external_sensor_clock)); int rc; +#ifdef COUNTER_64BITS_TICKS const struct counter_driver_api *api = (const struct counter_driver_api *)external_sensor_clock->api; if (api->get_value_64) { rc = counter_get_value_64(external_sensor_clock, cycles); - } else { + } else +#endif /* COUNTER_64BITS_TICKS */ + { uint32_t result_32; rc = counter_get_value(external_sensor_clock, &result_32); diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index c660225a0e39..92695f5d77c7 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -199,7 +199,22 @@ struct counter_config_info { /** * Maximal (default) top value on which counter is reset (cleared or reloaded). */ +#ifdef CONFIG_COUNTER_64BITS_TICKS + union { + uint64_t max_top_value_64; + struct { +#ifdef CONFIG_BIG_ENDIAN + uint32_t reserved; + uint32_t max_top_value; +#else + uint32_t max_top_value; + uint32_t reserved; +#endif /* CONFIG_BIG_ENDIAN */ + }; + }; +#else uint32_t max_top_value; +#endif /* CONFIG_COUNTER_64BITS_TICKS */ /** * Flags (see @ref COUNTER_FLAGS). */ @@ -212,13 +227,76 @@ struct counter_config_info { uint8_t channels; }; +/** @brief Alarm callback + * + * @param dev Pointer to the device structure for the driver instance. + * @param chan_id Channel ID. + * @param ticks Counter value that triggered the alarm. + * @param user_data User data. + */ +typedef void (*counter_alarm_callback_64_t)(const struct device *dev, uint8_t chan_id, + uint64_t ticks, void *user_data); + +/** @brief Alarm callback structure. + */ +struct counter_alarm_cfg_64 { + /** + * Number of ticks that triggers the alarm. + * + * It can be relative (to now) or an absolute value (see @ref + * COUNTER_ALARM_CFG_ABSOLUTE). Both, relative and absolute, alarm + * values can be any value between zero and the current top value (see + * @ref counter_get_top_value). When setting an absolute alarm value + * close to the current counter value there is a risk that the counter + * will have counted past the given absolute value before the driver + * manages to activate the alarm. Therefore a guard period can be + * defined that lets the driver decide unambiguously whether it is late + * or not (see @ref counter_set_guard_period). If the counter is clock + * driven then ticks can be converted to microseconds (see @ref + * counter_ticks_to_us). Alternatively, the counter implementation may + * count asynchronous events. + */ + uint64_t ticks; + /** + * Callback called on alarm (cannot be NULL). + */ + counter_alarm_callback_64_t callback; + /** + * User data returned in callback. + */ + void *user_data; + /** + * Alarm flags (see @ref COUNTER_ALARM_FLAGS). + */ + uint32_t flags; +}; + +/** @brief Top value configuration structure. + */ +struct counter_top_cfg_64 { + /** + * Top value. + */ + uint64_t ticks; + /** + * Callback function (can be NULL). + */ + counter_top_callback_t callback; + /** + * User data passed to callback function (not valid if callback is NULL). + */ + void *user_data; + /** + * Flags (see @ref COUNTER_TOP_FLAGS). + */ + uint32_t flags; +}; + typedef int (*counter_api_start)(const struct device *dev); typedef int (*counter_api_stop)(const struct device *dev); typedef int (*counter_api_get_value)(const struct device *dev, uint32_t *ticks); -typedef int (*counter_api_get_value_64)(const struct device *dev, uint64_t *ticks); typedef int (*counter_api_reset)(const struct device *dev); typedef int (*counter_api_set_value)(const struct device *dev, uint32_t ticks); -typedef int (*counter_api_set_value_64)(const struct device *dev, uint64_t ticks); typedef int (*counter_api_set_alarm)(const struct device *dev, uint8_t chan_id, const struct counter_alarm_cfg *alarm_cfg); typedef int (*counter_api_cancel_alarm)(const struct device *dev, uint8_t chan_id); @@ -232,14 +310,23 @@ typedef int (*counter_api_set_guard_period)(const struct device *dev, uint32_t t typedef uint32_t (*counter_api_get_freq)(const struct device *dev); typedef uint64_t (*counter_api_get_freq_64)(const struct device *dev); +typedef int (*counter_api_get_value_64)(const struct device *dev, uint64_t *ticks); +typedef int (*counter_api_set_value_64)(const struct device *dev, uint64_t ticks); +typedef int (*counter_api_set_alarm_64)(const struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg_64 *alarm_cfg); +typedef uint64_t (*counter_api_get_guard_period_64)(const struct device *dev, uint32_t flags); +typedef int (*counter_api_set_guard_period_64)(const struct device *dev, uint64_t ticks, + uint32_t flags); +typedef uint64_t (*counter_api_get_top_value_64)(const struct device *dev); +typedef int (*counter_api_set_top_value_64)(const struct device *dev, + const struct counter_top_cfg_64 *cfg); + __subsystem struct counter_driver_api { counter_api_start start; counter_api_stop stop; counter_api_get_value get_value; - counter_api_get_value_64 get_value_64; counter_api_reset reset; counter_api_set_value set_value; - counter_api_set_value_64 set_value_64; counter_api_set_alarm set_alarm; counter_api_cancel_alarm cancel_alarm; counter_api_set_top_value set_top_value; @@ -251,6 +338,15 @@ __subsystem struct counter_driver_api { #ifdef CONFIG_COUNTER_64BITS_FREQ counter_api_get_freq_64 get_freq_64; #endif /* CONFIG_COUNTER_64BITS_FREQ */ +#ifdef CONFIG_COUNTER_64BITS_TICKS + counter_api_get_value_64 get_value_64; + counter_api_set_value_64 set_value_64; + counter_api_set_alarm_64 set_alarm_64; + counter_api_get_guard_period_64 get_guard_period_64; + counter_api_set_guard_period_64 set_guard_period_64; + counter_api_get_top_value_64 get_top_value_64; + counter_api_set_top_value_64 set_top_value_64; +#endif /* CONFIG_COUNTER_64BITS_TICKS */ }; /** @@ -434,6 +530,10 @@ static inline uint64_t z_impl_counter_ticks_to_ns(const struct device *dev, uint * * @param[in] dev Pointer to the device structure for the driver instance. * + * @warning With `CONFIG_COUNTER_64BITS_TICKS` enabled this function returns only the lower + * 32 bits of the max top value. Use `counter_get_max_top_value_64()` to get + * the full 64 bit value if it is expected to exceed 32 bits. + * * @return Max top value. */ __syscall uint32_t counter_get_max_top_value(const struct device *dev); @@ -497,27 +597,6 @@ static inline int z_impl_counter_get_value(const struct device *dev, uint32_t *t return api->get_value(dev, ticks); } -/** - * @brief Get current counter 64-bit value. - * @param dev Pointer to the device structure for the driver instance. - * @param ticks Pointer to where to store the current counter value - * - * @retval 0 If successful. - * @retval <0 Negative error code on failure getting the counter value - */ -__syscall int counter_get_value_64(const struct device *dev, uint64_t *ticks); - -static inline int z_impl_counter_get_value_64(const struct device *dev, uint64_t *ticks) -{ - const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; - - if (!api->get_value_64) { - return -ENOSYS; - } - - return api->get_value_64(dev, ticks); -} - /** * @brief Reset the counter to the initial value. * @param dev Pointer to the device structure for the driver instance. @@ -559,27 +638,6 @@ static inline int z_impl_counter_set_value(const struct device *dev, uint32_t ti return api->set_value(dev, ticks); } -/** - * @brief Set current counter 64-bit value. - * @param dev Pointer to the device structure for the driver instance. - * @param ticks Tick value to set - * - * @retval 0 If successful. - * @retval Negative error code on failure setting the counter value - */ -__syscall int counter_set_value_64(const struct device *dev, uint64_t ticks); - -static inline int z_impl_counter_set_value_64(const struct device *dev, uint64_t ticks) -{ - const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; - - if (!api->set_value_64) { - return -ENOSYS; - } - - return api->set_value_64(dev, ticks); -} - /** * @brief Set a single shot alarm on a channel. * @@ -653,6 +711,10 @@ static inline int z_impl_counter_cancel_channel_alarm(const struct device *dev, * outside the new top value. In that case, error is returned and optionally * driver can reset the counter (see @ref COUNTER_TOP_CFG_RESET_WHEN_LATE). * + * @warning With `CONFIG_COUNTER_64BITS_TICKS` enabled this function sets the lower + * 32 bits of the max top value. Use `counter_set_top_value_64()` to get + * the full 64 bit value if it is expected to exceed 32 bits. + * * @param dev Pointer to the device structure for the driver instance. * @param cfg Configuration. Cannot be NULL. * @@ -802,6 +864,288 @@ static inline uint32_t z_impl_counter_get_guard_period(const struct device *dev, return (api->get_guard_period) ? api->get_guard_period(dev, flags) : 0; } +/** + * @brief Function to retrieve maximum top value that can be set for 64 bits. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * + * @return Max top value in 64 bits. + */ +__syscall uint64_t counter_get_max_top_value_64(const struct device *dev); + +static inline uint64_t z_impl_counter_get_max_top_value_64(const struct device *dev) +{ +#ifdef CONFIG_COUNTER_64BITS_TICKS + const struct counter_config_info *config = (const struct counter_config_info *)dev->config; + + return config->max_top_value_64; +#else + ARG_UNUSED(dev); + return -ENOTSUP; +#endif +} + +/** + * @brief Set counter top value for 64 bits. + * + * Function sets top value and optionally resets the counter to 0 or top value + * depending on counter direction. On turnaround, counter can be reset and + * optional callback is periodically called. Top value can only be changed when + * there is no active channel alarm. + * + * @ref COUNTER_TOP_CFG_DONT_RESET prevents counter reset. When counter is + * running while top value is updated, it is possible that counter progresses + * outside the new top value. In that case, error is returned and optionally + * driver can reset the counter (see @ref COUNTER_TOP_CFG_RESET_WHEN_LATE). + * + * @param dev Pointer to the device structure for the driver instance. + * @param cfg Configuration. Cannot be NULL. + * + * @retval 0 If successful. + * @retval -ENOTSUP if request is not supported (e.g. top value cannot be + * changed or counter cannot/must be reset during top value + update). + * @retval -EBUSY if any alarm is active. + * @retval -ETIME if @ref COUNTER_TOP_CFG_DONT_RESET was set and new top value + * is smaller than current counter value (counter counting up). + */ +__syscall int counter_set_top_value_64(const struct device *dev, + const struct counter_top_cfg_64 *cfg); + +static inline int z_impl_counter_set_top_value_64(const struct device *dev, + const struct counter_top_cfg_64 *cfg) +{ +#ifdef CONFIG_COUNTER_64BITS_TICKS + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; + + if (cfg->ticks > counter_get_max_top_value_64(dev)) { + return -EINVAL; + } + + return api->set_top_value_64(dev, cfg); +#else + ARG_UNUSED(dev); + ARG_UNUSED(cfg); + return -ENOTSUP; +#endif +} + +/** + * @brief Set a single shot alarm on a channel for 64 bits. + * + * After expiration alarm can be set again, disabling is not needed. When alarm + * expiration handler is called, channel is considered available and can be + * set again in that context. + * + * @note API is not thread safe. + * + * @param dev Pointer to the device structure for the driver instance. + * @param chan_id Channel ID. + * @param alarm_cfg Alarm configuration. + * + * @retval 0 If successful. + * @retval -ENOTSUP if request is not supported (device does not support + * interrupts or requested channel). + * @retval -EINVAL if alarm settings are invalid. + * @retval -ETIME if absolute alarm was set too late. + * @retval -EBUSY if alarm is already active. + */ +__syscall int counter_set_channel_alarm_64(const struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg_64 *alarm_cfg); + +static inline int z_impl_counter_set_channel_alarm_64(const struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg_64 *alarm_cfg) +{ +#ifdef CONFIG_COUNTER_64BITS_TICKS + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; + + if (chan_id >= counter_get_num_of_channels(dev)) { + return -ENOTSUP; + } + + return api->set_alarm_64(dev, chan_id, alarm_cfg); +#else + ARG_UNUSED(dev); + ARG_UNUSED(chan_id); + ARG_UNUSED(alarm_cfg); + return -ENOTSUP; +#endif +} + +/** + * @brief Function to retrieve current top value for 64 bits. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * + * @return Top value in 64 bits. + */ +__syscall uint64_t counter_get_top_value_64(const struct device *dev); + +static inline uint64_t z_impl_counter_get_top_value_64(const struct device *dev) +{ +#ifdef CONFIG_COUNTER_64BITS_TICKS + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; + + return api->get_top_value_64(dev); +#else + ARG_UNUSED(dev); + return 0; +#endif +} + +/** + * @brief Set guard period in counter ticks for 64 bits. + * + * When setting an absolute alarm value close to the current counter value there + * is a risk that the counter will have counted past the given absolute value + * before the driver manages to activate the alarm. If this would go unnoticed + * then the alarm would only expire after the timer has wrapped and reached the + * given absolute value again after a full timer period. This could take a long + * time in case of a 32 bit timer. Setting a sufficiently large guard period will + * help the driver detect unambiguously whether it is late or not. + * + * The guard period should be as many counter ticks as the driver will need at + * most to actually activate the alarm after the driver API has been called. If + * the driver finds that the counter has just passed beyond the given absolute + * tick value but is still close enough to fall within the guard period, it will + * assume that it is "late", i.e. that the intended expiry time has already passed. + * Depending on the @ref COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE flag the driver will + * either ignore the alarm or expire it immediately in such a case. + * + * If, however, the counter is past the given absolute tick value but outside + * the guard period, then the driver will assume that this is intentional and + * let the counter wrap around to/from zero before it expires. + * + * More precisely: + * + * - When counting upwards (see @ref COUNTER_CONFIG_INFO_COUNT_UP) the given + * absolute tick value must be above (now + guard_period) % top_value to be + * accepted by the driver. + * - When counting downwards, the given absolute tick value must be less than + * (now + top_value - guard_period) % top_value to be accepted. + * + * Examples: + * + * - counting upwards, now = 4950, top value = 5000, guard period = 100: + * absolute tick value >= (4950 + 100) % 5000 = 50 + * - counting downwards, now = 50, top value = 5000, guard period = 100: + * absolute tick value <= (50 + 5000 - * 100) % 5000 = 4950 + * + * If you need only short alarm periods, you can set the guard period very high + * (e.g. half of the counter top value) which will make it highly unlikely that + * the counter will ever unintentionally wrap. + * + * The guard period is set to 0 on initialization (no protection). + * + * @param dev Pointer to the device structure for the driver instance. + * @param ticks Guard period in counter ticks of 64 bits. + * @param flags See @ref COUNTER_GUARD_PERIOD_FLAGS. + * + * @retval 0 if successful. + * @retval -ENOSYS if function or flags are not supported. + * @retval -EINVAL if ticks value is invalid. + */ +__syscall int counter_set_guard_period_64(const struct device *dev, uint64_t ticks, uint32_t flags); + +static inline int z_impl_counter_set_guard_period_64(const struct device *dev, uint64_t ticks, + uint32_t flags) +{ +#ifdef CONFIG_COUNTER_64BITS_TICKS + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; + + if (!api->set_guard_period_64) { + return -ENOSYS; + } + + return api->set_guard_period_64(dev, ticks, flags); +#else + ARG_UNUSED(dev); + ARG_UNUSED(ticks); + ARG_UNUSED(flags); + return -ENOTSUP; +#endif +} + +/** + * @brief Return guard period for 64 bits. + * + * @see counter_set_guard_period_64. + * + * @param dev Pointer to the device structure for the driver instance. + * @param flags See @ref COUNTER_GUARD_PERIOD_FLAGS. + * + * @return Guard period given in counter ticks or 0 if function or flags are + * not supported. + */ +__syscall uint64_t counter_get_guard_period_64(const struct device *dev, uint32_t flags); + +static inline uint64_t z_impl_counter_get_guard_period_64(const struct device *dev, uint32_t flags) +{ +#ifdef CONFIG_COUNTER_64BITS_TICKS + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; + + return (api->get_guard_period_64) ? api->get_guard_period_64(dev, flags) : 0; +#else + ARG_UNUSED(dev); + ARG_UNUSED(flags); + return -ENOTSUP; +#endif +} + +/** + * @brief Get current counter 64-bit value. + * @param dev Pointer to the device structure for the driver instance. + * @param ticks Pointer to where to store the current counter value in 64 bits. + * + * @retval 0 If successful. + * @retval <0 Negative error code on failure getting the counter value + */ +__syscall int counter_get_value_64(const struct device *dev, uint64_t *ticks); + +static inline int z_impl_counter_get_value_64(const struct device *dev, uint64_t *ticks) +{ +#ifdef CONFIG_COUNTER_64BITS_TICKS + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; + + if (!api->get_value_64) { + return -ENOSYS; + } + + return api->get_value_64(dev, ticks); +#else + ARG_UNUSED(dev); + ARG_UNUSED(ticks); + return -ENOTSUP; +#endif +} + +/** + * @brief Set current counter 64-bit value. + * @param dev Pointer to the device structure for the driver instance. + * @param ticks Tick value to set in 64 bits + * + * @retval 0 If successful. + * @retval <0 Negative error code on failure setting the counter value + */ +__syscall int counter_set_value_64(const struct device *dev, uint64_t ticks); + +static inline int z_impl_counter_set_value_64(const struct device *dev, uint64_t ticks) +{ +#ifdef CONFIG_COUNTER_64BITS_TICKS + const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; + + if (!api->set_value_64) { + return -ENOSYS; + } + + return api->set_value_64(dev, ticks); +#else + ARG_UNUSED(dev); + ARG_UNUSED(ticks); + return -ENOTSUP; +#endif +} + #ifdef __cplusplus } #endif From 70d253c9db71dd45b740f5f56acfb8b32367dc4c Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Thu, 20 Nov 2025 20:02:53 -0800 Subject: [PATCH 123/143] UPSTREAM: drivers: counter: add inline helpers for(n/u)sec to ticks in 64b commit c07454ff7ca27f74b1dcbc5a229af195bbff0a48 upstream. Add inline helpers to convert nanoseconds or microseconds to 64b ticks and vice versa. Signed-off-by: Ryan McClelland --- include/zephyr/drivers/counter.h | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index 92695f5d77c7..bb6ae869141d 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -478,6 +478,21 @@ static inline uint32_t z_impl_counter_us_to_ticks(const struct device *dev, uint return (ticks > (uint64_t)UINT32_MAX) ? UINT32_MAX : ticks; } +/** + * @brief Function to convert microseconds to ticks with 64 bits. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * @param[in] us Microseconds. + * + * @return Converted ticks as a uint64_t + */ +__syscall uint64_t counter_us_to_ticks_64(const struct device *dev, uint64_t us); + +static inline uint64_t z_impl_counter_us_to_ticks_64(const struct device *dev, uint64_t us) +{ + return (us * z_counter_get_frequency(dev)) / USEC_PER_SEC; +} + /** * @brief Function to convert ticks to microseconds. * @@ -493,6 +508,21 @@ static inline uint64_t z_impl_counter_ticks_to_us(const struct device *dev, uint return ((uint64_t)ticks * USEC_PER_SEC) / z_counter_get_frequency(dev); } +/** + * @brief Function to convert ticks with 64 bits to microseconds. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * @param[in] ticks Ticks in 64 bits. + * + * @return Converted microseconds. + */ +__syscall uint64_t counter_ticks_to_us_64(const struct device *dev, uint64_t ticks); + +static inline uint64_t z_impl_counter_ticks_to_us_64(const struct device *dev, uint64_t ticks) +{ + return (ticks * USEC_PER_SEC) / z_counter_get_frequency(dev); +} + /** * @brief Function to convert nanoseconds to ticks. * @@ -510,6 +540,21 @@ static inline uint32_t z_impl_counter_ns_to_ticks(const struct device *dev, uint return (ticks > (uint64_t)UINT32_MAX) ? UINT32_MAX : ticks; } +/** + * @brief Function to convert nanoseconds to ticks with 64 bits. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * @param[in] ns Nanoseconds. + * + * @return Converted ticks as a uint64_t. + */ +__syscall uint64_t counter_ns_to_ticks_64(const struct device *dev, uint64_t ns); + +static inline uint64_t z_impl_counter_ns_to_ticks_64(const struct device *dev, uint64_t ns) +{ + return (ns * z_counter_get_frequency(dev)) / NSEC_PER_SEC; +} + /** * @brief Function to convert ticks to nanoseconds. * @@ -525,6 +570,21 @@ static inline uint64_t z_impl_counter_ticks_to_ns(const struct device *dev, uint return ((uint64_t)ticks * NSEC_PER_SEC) / z_counter_get_frequency(dev); } +/** + * @brief Function to convert ticks with 64 bits to nanoseconds. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * @param[in] ticks Ticks in 64 bits. + * + * @return Converted nanoseconds. + */ +__syscall uint64_t counter_ticks_to_ns_64(const struct device *dev, uint64_t ticks); + +static inline uint64_t z_impl_counter_ticks_to_ns_64(const struct device *dev, uint64_t ticks) +{ + return (ticks * NSEC_PER_SEC) / z_counter_get_frequency(dev); +} + /** * @brief Function to retrieve maximum top value that can be set. * From 8fdb4b87966cc8b0089c2c808aa8aefab93f442b Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Fri, 13 Feb 2026 13:48:05 +0530 Subject: [PATCH 124/143] UPSTREAM-PEND: dts: bindings: counter: add TI RTC counter - This is the counter based rtc module used in k3 devices. PR: 104048 Signed-off-by: Sunil Hegde --- dts/bindings/counter/ti,k3-rtc-counter.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 dts/bindings/counter/ti,k3-rtc-counter.yaml diff --git a/dts/bindings/counter/ti,k3-rtc-counter.yaml b/dts/bindings/counter/ti,k3-rtc-counter.yaml new file mode 100644 index 000000000000..71134b70bf96 --- /dev/null +++ b/dts/bindings/counter/ti,k3-rtc-counter.yaml @@ -0,0 +1,19 @@ +# Copyright 2026 Texas Instruments Inc. +# SPDX-License-Identifier: Apache-2.0 + +description: Counter based Real Time Clock (RTC) available in the TI K3 generation of processors. + +compatible: "ti,k3-rtc-counter" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + clock-frequency: + type: int + required: true From 4650246889f629b07a6f6f13cca97260feb5397e Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Fri, 13 Feb 2026 13:52:29 +0530 Subject: [PATCH 125/143] UPSTREAM-PEND: drivers: counter: k3_rtc: Add TI RTC... This commit adds the counter RTC driver used in TI k3 devices. PR: 104048 Signed-off-by: Sunil Hegde --- drivers/counter/CMakeLists.txt | 1 + drivers/counter/Kconfig | 2 + drivers/counter/Kconfig.k3_rtc_counter | 9 + drivers/counter/counter_k3_rtc.c | 527 +++++++++++++++++++++++++ 4 files changed, 539 insertions(+) create mode 100644 drivers/counter/Kconfig.k3_rtc_counter create mode 100644 drivers/counter/counter_k3_rtc.c diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index d45a8d7cfa27..36892039fe2e 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -68,3 +68,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_WUT_MAX32 counter_max32_wu zephyr_library_sources_ifdef(CONFIG_COUNTER_CC23X0_RTC counter_cc23x0_rtc.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_CC23X0_LGPT counter_cc23x0_lgpt.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_MSPM0_TIMER counter_mspm0_timer.c) +zephyr_library_sources_ifdef(CONFIG_COUNTER_TI_K3_RTC counter_k3_rtc.c) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 66a0017285a7..8224c97aa81a 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -146,4 +146,6 @@ source "drivers/counter/Kconfig.cc23x0_lgpt" source "drivers/counter/Kconfig.mspm0" +source "drivers/counter/Kconfig.k3_rtc_counter" + endif # COUNTER diff --git a/drivers/counter/Kconfig.k3_rtc_counter b/drivers/counter/Kconfig.k3_rtc_counter new file mode 100644 index 000000000000..130bef27194c --- /dev/null +++ b/drivers/counter/Kconfig.k3_rtc_counter @@ -0,0 +1,9 @@ +# Copyright (c) 2026 Texas Instruments Inc. +# SPDX-License-Identifier: Apache-2.0 + +config COUNTER_TI_K3_RTC + bool "RTC Counter Driver for TI K3 SoCs" + default y + depends on DT_HAS_TI_K3_RTC_COUNTER_ENABLED + help + Enable the RTC driver for TI K3 SoCs. diff --git a/drivers/counter/counter_k3_rtc.c b/drivers/counter/counter_k3_rtc.c new file mode 100644 index 000000000000..d05ca1ab8d4e --- /dev/null +++ b/drivers/counter/counter_k3_rtc.c @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2026 Texas Instruments Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_k3_rtc_counter + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(counter_ti_k3_rtc, CONFIG_COUNTER_LOG_LEVEL); + +#define RTC_MAX_TOP_VALUE UINT32_MAX +#define RTC_MAX_TOP_VALUE_64 BIT_MASK(48) + +#define K3_RTC_KICK0_UNLOCK 0x83e70b13 +#define K3_RTC_KICK1_UNLOCK 0x95a4f1e0 + +#define RTC_CNT_FMODE_MASK BIT(25) +#define RTC_UNLOCK_MASK BIT(23) +#define RTC_O32K_OSC_MASK BIT(21) +#define RTC_SYNCPEND_STATUS_MASK BIT_MASK(2) + +#define RTC_IRQ_ALARM1_MASK BIT(0) +#define RTC_IRQ_ALARM2_MASK BIT(1) +#define RTC_IRQ_STATUS_MASK BIT_MASK(2) + +#define DEV_CFG(dev) ((const struct k3_rtc_counter_cfg *)(dev)->config) +#define DEV_DATA(dev) ((struct k3_rtc_counter_data *)(dev)->data) +#define DEV_REGS(dev) ((k3_rtc_regs_t *)DEVICE_MMIO_NAMED_GET(dev, reg_base)) + +typedef struct { + volatile uint32_t MOD_VER; /* Module ID and Version - 0h */ + volatile uint32_t SUB_S_CNT; /* SubSecondCount - 4h*/ + volatile uint32_t S_CNT_LSW; /* SecondCount_31_0 - 8h */ + volatile uint32_t S_CNT_MSW; /* SecondCount_47_32 - Ch */ + volatile uint32_t COMP; /* Compensation - 10h */ + uint8_t Resv_24[4]; + volatile uint32_t OFF_ON_S_CNT_LSW; /* OffOnSCnt_31_0 - 18h */ + volatile uint32_t OFF_ON_S_CNT_MSW; /* OffOnSCnt_47_32 - 1Ch */ + volatile uint32_t ON_OFF_S_CNT_LSW; /* OnOffSCnt_31_0 - 20h */ + volatile uint32_t ON_OFF_S_CNT_MSW; /* OnOffSCnt_47_32 - 24h */ + volatile uint32_t DEBOUNCE; /* Debounce - 28h */ + volatile uint32_t ANALOG; /* AnalogCfg - 2Ch */ + volatile uint32_t SCRATCH[8]; /* Scratch Storage X - 30h */ + volatile uint32_t GENRAL_CTL; /* GeneralControl - 50h */ + volatile uint32_t IRQSTATUS_RAW_SYS; /* Interrupt Raw Status Register - 54h */ + volatile uint32_t IRQSTATUS_SYS; /* Interrupt Status Register - 58h */ + volatile uint32_t IRQENABLE_SET_SYS; /* Interrupt Enable Set Register - 5Ch */ + volatile uint32_t IRQENABLE_CLR_SYS; /* Interrupt Enable Clear Register - 60h */ + uint8_t Resv_104[4]; /* SyncPending - 68h */ + volatile uint32_t SYNCPEND; + uint8_t Resv_112[4]; + volatile uint32_t KICK0; /* Kick0 - 70h */ + volatile uint32_t KICK1; /* Kick1 - 74h */ + uint8_t Resv_128[8]; +} k3_rtc_regs_t; + +struct k3_rtc_counter_cfg { + struct counter_config_info counter_info; + + DEVICE_MMIO_NAMED_ROM(reg_base); + + void (*irq_config_func)(void); + uint32_t osc_freq; +}; + +struct k3_rtc_counter_data { + DEVICE_MMIO_NAMED_RAM(reg_base); +#ifdef CONFIG_COUNTER_64BITS_TICKS + struct counter_alarm_cfg_64 alarm_cfg[2]; +#else + struct counter_alarm_cfg alarm_cfg[2]; +#endif /* CONFIG_COUNTER_64BITS_TICKS */ + uint32_t sync_timeout_us; +}; + +static int k3_rtc_fence(const struct device *dev) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + struct k3_rtc_counter_data *data = DEV_DATA(dev); + + /* Add slight delay in order to wait for register writes to sync */ + k_usleep(data->sync_timeout_us); + int timeout = WAIT_FOR((rtc_regs->SYNCPEND & RTC_SYNCPEND_STATUS_MASK) != 0, + data->sync_timeout_us, + k_busy_wait(1)); + + return timeout; +} + +static int k3_rtc_unlock_status(k3_rtc_regs_t *rtc_regs) +{ + return !!(rtc_regs->GENRAL_CTL & RTC_UNLOCK_MASK); +} + +static int k3_rtc_unlock(const struct device *dev) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + int ret; + + if (k3_rtc_unlock_status(rtc_regs) == 0) { + rtc_regs->KICK0 = K3_RTC_KICK0_UNLOCK; + rtc_regs->KICK1 = K3_RTC_KICK1_UNLOCK; + + ret = k3_rtc_fence(dev); + if (ret != 0) { + LOG_ERR("Failed to sync after unlock"); + return ret; + } + + if (k3_rtc_unlock_status(rtc_regs) == 0) { + LOG_ERR("RTC unlock failed!"); + return -EIO; + } + } + + return 0; +} + +static int k3_counter_start(const struct device *dev) +{ + ARG_UNUSED(dev); + /* RTC counter runs after power-on reset */ + return 0; +} + +static int k3_counter_stop(const struct device *dev) +{ + ARG_UNUSED(dev); + /* RTC counter can only be reset, not stopped */ + return 0; +} + +static int k3_counter_get_value(const struct device *dev, uint32_t *ticks) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + + if (ticks == NULL) { + return -EINVAL; + } + + *ticks = rtc_regs->S_CNT_LSW; + + return 0; +} + +static int k3_counter_set_value(const struct device *dev, uint32_t ticks) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + int ret; + + rtc_regs->S_CNT_LSW = ticks; + rtc_regs->S_CNT_MSW = 0; + + ret = k3_rtc_fence(dev); + if (ret != 0) { + LOG_ERR("Failed to sync after set_value"); + return ret; + } + + return 0; +} + +static int k3_counter_reset(const struct device *dev) +{ + int ret; + + ret = k3_counter_set_value(dev, 0); + if (ret != 0) { + LOG_ERR("Failed to sync after reset"); + return ret; + } + + return 0; +} + +static int k3_counter_set_alarm(const struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg *alarm_cfg) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + struct k3_rtc_counter_data *data = DEV_DATA(dev); + int ret, key; + + if (chan_id > 1) { + LOG_ERR("Invalid alarm channel"); + return -EINVAL; + } + + if (alarm_cfg == NULL) { + LOG_ERR("NULL alarm config"); + return -EINVAL; + } + + if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) != 0) { + data->alarm_cfg[chan_id].ticks = alarm_cfg->ticks; + } else { + uint32_t now; + + ret = k3_counter_get_value(dev, &now); + if (ret != 0) { + return ret; + } + data->alarm_cfg[chan_id].ticks = now + alarm_cfg->ticks; + } + + key = irq_lock(); + + if (chan_id == 0) { + rtc_regs->ON_OFF_S_CNT_LSW = (uint32_t)data->alarm_cfg[chan_id].ticks; + rtc_regs->ON_OFF_S_CNT_MSW = 0; + rtc_regs->IRQSTATUS_SYS = RTC_IRQ_ALARM1_MASK; + rtc_regs->IRQENABLE_SET_SYS = RTC_IRQ_ALARM1_MASK; + } else { + rtc_regs->OFF_ON_S_CNT_LSW = (uint32_t)data->alarm_cfg[chan_id].ticks; + rtc_regs->OFF_ON_S_CNT_MSW = 0; + rtc_regs->IRQSTATUS_SYS = RTC_IRQ_ALARM2_MASK; + rtc_regs->IRQENABLE_SET_SYS = RTC_IRQ_ALARM2_MASK; + } + ret = k3_rtc_fence(dev); + if (ret != 0) { + LOG_ERR("Failed to sync after alarm set: %d", ret); + irq_unlock(key); + return ret; + } + + data->alarm_cfg[chan_id].callback = alarm_cfg->callback; + data->alarm_cfg[chan_id].user_data = alarm_cfg->user_data; + + irq_unlock(key); + + return 0; +} + +static int k3_counter_cancel_alarm(const struct device *dev, uint8_t chan_id) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + struct k3_rtc_counter_data *data = DEV_DATA(dev); + int ret, key; + + if (chan_id > 1) { + LOG_ERR("Invalid alarm channel"); + return -EINVAL; + } + + key = irq_lock(); + if (chan_id == 0) { + rtc_regs->IRQENABLE_CLR_SYS = RTC_IRQ_ALARM1_MASK; + rtc_regs->IRQSTATUS_SYS = RTC_IRQ_ALARM1_MASK; + } else { + rtc_regs->IRQENABLE_CLR_SYS = RTC_IRQ_ALARM2_MASK; + rtc_regs->IRQSTATUS_SYS = RTC_IRQ_ALARM2_MASK; + } + ret = k3_rtc_fence(dev); + if (ret != 0) { + LOG_ERR("Failed to sync after alarm cancel: %d", ret); + irq_unlock(key); + return ret; + } + + data->alarm_cfg[chan_id].callback = NULL; + data->alarm_cfg[chan_id].user_data = NULL; + data->alarm_cfg[chan_id].flags = 0; + + irq_unlock(key); + + return 0; +} + +static uint32_t k3_counter_get_pending_int(const struct device *dev) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + + return rtc_regs->IRQSTATUS_SYS & RTC_IRQ_STATUS_MASK; +} + +static uint32_t k3_counter_get_top_value(const struct device *dev) +{ + ARG_UNUSED(dev); + return RTC_MAX_TOP_VALUE; +} + +static uint32_t k3_counter_get_frequency(const struct device *dev) +{ + const struct k3_rtc_counter_cfg *config = DEV_CFG(dev); + + return config->counter_info.freq; +} + +#ifdef CONFIG_COUNTER_64BITS_TICKS +static int k3_counter_get_value_64(const struct device *dev, uint64_t *ticks) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + + if (ticks == NULL) { + return -EINVAL; + } + + uint32_t seconds_low = rtc_regs->S_CNT_LSW; + uint32_t seconds_high = rtc_regs->S_CNT_MSW; + + *ticks = ((uint64_t)seconds_high << 32) | (uint64_t)seconds_low; + + return 0; +} + +static int k3_counter_set_value_64(const struct device *dev, uint64_t ticks) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + int ret; + + rtc_regs->S_CNT_LSW = ticks; + rtc_regs->S_CNT_MSW = (uint32_t)(ticks >> 32); + + ret = k3_rtc_fence(dev); + if (ret != 0) { + LOG_ERR("Failed to sync after set_value"); + return ret; + } + + return 0; +} + +static uint64_t k3_counter_get_top_value_64(const struct device *dev) +{ + ARG_UNUSED(dev); + return RTC_MAX_TOP_VALUE_64; +} + +static int k3_counter_set_alarm_64(const struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg_64 *alarm_cfg) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + struct k3_rtc_counter_data *data = DEV_DATA(dev); + int ret, key; + + if (chan_id > 1) { + LOG_ERR("Invalid alarm channel"); + return -EINVAL; + } + + if (alarm_cfg == NULL) { + LOG_ERR("NULL alarm config"); + return -EINVAL; + } + + if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) != 0) { + data->alarm_cfg[chan_id].ticks = alarm_cfg->ticks; + } else { + uint64_t now; + + ret = k3_counter_get_value_64(dev, &now); + if (ret != 0) { + return ret; + } + data->alarm_cfg[chan_id].ticks = now + alarm_cfg->ticks; + } + + key = irq_lock(); + + if (chan_id == 0) { + rtc_regs->ON_OFF_S_CNT_LSW = (uint32_t)data->alarm_cfg[chan_id].ticks; + rtc_regs->ON_OFF_S_CNT_MSW = (uint32_t)(data->alarm_cfg[chan_id].ticks >> 32); + rtc_regs->IRQSTATUS_SYS = RTC_IRQ_ALARM1_MASK; + rtc_regs->IRQENABLE_SET_SYS = RTC_IRQ_ALARM1_MASK; + } else { + rtc_regs->OFF_ON_S_CNT_LSW = (uint32_t)data->alarm_cfg[chan_id].ticks; + rtc_regs->OFF_ON_S_CNT_MSW = (uint32_t)(data->alarm_cfg[chan_id].ticks >> 32); + rtc_regs->IRQSTATUS_SYS = RTC_IRQ_ALARM2_MASK; + rtc_regs->IRQENABLE_SET_SYS = RTC_IRQ_ALARM2_MASK; + } + ret = k3_rtc_fence(dev); + if (ret != 0) { + LOG_ERR("Failed to sync after alarm set: %d", ret); + irq_unlock(key); + return ret; + } + + data->alarm_cfg[chan_id].callback = alarm_cfg->callback; + data->alarm_cfg[chan_id].user_data = alarm_cfg->user_data; + data->alarm_cfg[chan_id].flags = alarm_cfg->flags; + + irq_unlock(key); + + return 0; +} +#endif /* CONFIG_COUNTER_64BITS_TICKS */ + +static void k3_rtc_isr(const struct device *dev) +{ + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + struct k3_rtc_counter_data *data = DEV_DATA(dev); + uint32_t status; + int ret; +#ifdef CONFIG_COUNTER_64BITS_TICKS + uint64_t now; +#else + uint32_t now; +#endif /* CONFIG_COUNTER_64BITS_TICKS */ + + status = rtc_regs->IRQSTATUS_SYS; + if (status == 0) { + return; + } + + rtc_regs->IRQSTATUS_SYS = status; + + if ((status & RTC_IRQ_ALARM1_MASK) != 0 && data->alarm_cfg[0].callback != NULL) { +#ifdef CONFIG_COUNTER_64BITS_TICKS + ret = k3_counter_get_value_64(dev, &now); +#else + ret = k3_counter_get_value(dev, &now); +#endif /* CONFIG_COUNTER_64BITS_TICKS */ + if (ret != 0) { + LOG_ERR("Invalid ticks: %d", ret); + return; + } + data->alarm_cfg[0].callback(dev, 0, now, data->alarm_cfg[0].user_data); + } + + if ((status & RTC_IRQ_ALARM2_MASK) != 0 && data->alarm_cfg[1].callback != NULL) { +#ifdef CONFIG_COUNTER_64BITS_TICKS + k3_counter_get_value_64(dev, &now); +#else + k3_counter_get_value(dev, &now); +#endif /* CONFIG_COUNTER_64BITS_TICKS */ + data->alarm_cfg[1].callback(dev, 1, now, data->alarm_cfg[1].user_data); + } +} + +static int k3_counter_init(const struct device *dev) +{ + DEVICE_MMIO_NAMED_MAP(dev, reg_base, K_MEM_CACHE_NONE); + const struct k3_rtc_counter_cfg *config = DEV_CFG(dev); + struct k3_rtc_counter_data *data = DEV_DATA(dev); + k3_rtc_regs_t *rtc_regs = DEV_REGS(dev); + int ret; + + ret = k3_rtc_unlock(dev); + if (ret != 0) { + LOG_ERR("Failed to unlock RTC"); + return ret; + } + + /* 4 clock cycles for sync */ + data->sync_timeout_us = (USEC_PER_SEC / config->osc_freq) * 4; + + /* Enable 32k oscillator dependency */ + rtc_regs->GENRAL_CTL |= RTC_O32K_OSC_MASK; + + /* Enable freeze mode for atomic 48-bit reads */ + rtc_regs->GENRAL_CTL |= RTC_CNT_FMODE_MASK; + + /* Clear and disable all interrupts */ + rtc_regs->IRQSTATUS_SYS = RTC_IRQ_STATUS_MASK; + rtc_regs->IRQENABLE_CLR_SYS = RTC_IRQ_STATUS_MASK; + + ret = k3_rtc_fence(dev); + if (ret != 0) { + LOG_ERR("Failed sync in init"); + return ret; + } + + config->irq_config_func(); + + return 0; +} + +static const struct counter_driver_api k3_counter_api = { + .start = k3_counter_start, + .stop = k3_counter_stop, + .get_value = k3_counter_get_value, + .reset = k3_counter_reset, + .set_value = k3_counter_set_value, + .set_alarm = k3_counter_set_alarm, + .cancel_alarm = k3_counter_cancel_alarm, + .get_pending_int = k3_counter_get_pending_int, + .get_top_value = k3_counter_get_top_value, + .get_freq = k3_counter_get_frequency, +#ifdef CONFIG_COUNTER_64BITS_TICKS + .get_value_64 = k3_counter_get_value_64, + .set_value_64 = k3_counter_set_value_64, + .set_alarm_64 = k3_counter_set_alarm_64, + .get_top_value_64 = k3_counter_get_top_value_64, +#endif /* CONFIG_COUNTER_64BITS_TICKS */ +}; + +#define TI_K3_COUNTER_INIT(i) \ + static void ti_k3_counter_irq_config_##i(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(i), \ + DT_INST_IRQ(i, priority), \ + k3_rtc_isr, \ + DEVICE_DT_INST_GET(i), 0); \ + irq_enable(DT_INST_IRQN(i)); \ + } \ + static struct k3_rtc_counter_data k3_counter_data_##i = {}; \ + static struct k3_rtc_counter_cfg k3_counter_cfg_##i = { \ + .counter_info = \ + { \ + .freq = 1, \ + COND_CODE_1(CONFIG_COUNTER_64BITS_TICKS, \ + (.max_top_value_64 = RTC_MAX_TOP_VALUE_64,), \ + (.max_top_value = RTC_MAX_TOP_VALUE,)) \ + .flags = COUNTER_CONFIG_INFO_COUNT_UP, \ + .channels = 2, \ + }, \ + DEVICE_MMIO_NAMED_ROM_INIT(reg_base, DT_DRV_INST(i)), \ + .irq_config_func = ti_k3_counter_irq_config_##i, \ + .osc_freq = DT_INST_PROP(i, clock_frequency) \ + }; \ + DEVICE_DT_INST_DEFINE(i, \ + k3_counter_init, \ + NULL, \ + &k3_counter_data_##i, \ + &k3_counter_cfg_##i, \ + POST_KERNEL, \ + CONFIG_COUNTER_INIT_PRIORITY, \ + &k3_counter_api \ + ); + +DT_INST_FOREACH_STATUS_OKAY(TI_K3_COUNTER_INIT) From ab3919798be6f196b1b44ec20467f726f8295cba Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Fri, 13 Feb 2026 13:39:33 +0530 Subject: [PATCH 126/143] UPSTREAM-PEND: dts: ti: Add RTC node and interrupts - Add RTC node in am62-main.dtsi which can also be used as a counter - Add child counter_rtc node which can be used as a virtual rtc. - Add interrupts in am62x_a53.dtsi PR: 104048 Signed-off-by: Sunil Hegde --- dts/arm64/ti/ti_am62x_a53.dtsi | 5 +++++ dts/vendor/ti/k3-am62-main.dtsi | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/dts/arm64/ti/ti_am62x_a53.dtsi b/dts/arm64/ti/ti_am62x_a53.dtsi index 2e20926e4746..4cf5a54df41e 100644 --- a/dts/arm64/ti/ti_am62x_a53.dtsi +++ b/dts/arm64/ti/ti_am62x_a53.dtsi @@ -154,3 +154,8 @@ interrupts = ; interrupt-parent = <&gic>; }; + +&main_rtc0 { + interrupts = ; + interrupt-parent = <&gic>; +}; diff --git a/dts/vendor/ti/k3-am62-main.dtsi b/dts/vendor/ti/k3-am62-main.dtsi index 17a4f32ff1c3..e2e86b578fc9 100644 --- a/dts/vendor/ti/k3-am62-main.dtsi +++ b/dts/vendor/ti/k3-am62-main.dtsi @@ -216,4 +216,17 @@ ti,itap-del-sel-sdr25 = <0x0>; status = "disabled"; }; + + main_rtc0: rtc@2b1f0000 { + compatible = "ti,k3-rtc-counter"; + reg = <0x2b1f0000 0x64>; + clock-frequency = <32768>; + status = "disabled"; + + counter_rtc0: counter_rtc { + compatible = "zephyr,rtc-counter"; + alarms-count = <2>; + status = "disabled"; + }; + }; }; From ce091a29eba2862c5f12c9c6604c02e6ad91e7bc Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Fri, 13 Feb 2026 14:46:47 +0530 Subject: [PATCH 127/143] UPSTREAM-PEND: boards: ti: sk_am62: a53.dts: Enable RTC node Enable RTC node for a53 core. PR: 104048 Signed-off-by: Sunil Hegde --- boards/ti/sk_am62/sk_am62_am6254_a53.dts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/boards/ti/sk_am62/sk_am62_am6254_a53.dts b/boards/ti/sk_am62/sk_am62_am6254_a53.dts index f6baa119150f..fb3b26696671 100644 --- a/boards/ti/sk_am62/sk_am62_am6254_a53.dts +++ b/boards/ti/sk_am62/sk_am62_am6254_a53.dts @@ -19,6 +19,10 @@ zephyr,sram = &ddr0; }; + aliases { + rtc = &counter_rtc0; + }; + cpus { cpu@0 { status = "okay"; @@ -103,3 +107,11 @@ ti,fails-without-test-cd; status = "disabled"; }; + +&main_rtc0 { + status = "okay"; +}; + +&counter_rtc0 { + status = "okay"; +}; From 85418492d5c95eb39251f23a6ffe3264361870c4 Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Wed, 18 Feb 2026 14:39:35 +0530 Subject: [PATCH 128/143] UPSTREAM_PEND: samples: drivers: rtc: boards: add config for... - Set RTC init priority to initialise zephyr,rtc-counter driver after counter driver is initialsed whose default priority is 60. PR: 104048 Signed-off-by: Sunil Hegde --- samples/drivers/rtc/boards/sk_am62_am6254_a53.conf | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 samples/drivers/rtc/boards/sk_am62_am6254_a53.conf diff --git a/samples/drivers/rtc/boards/sk_am62_am6254_a53.conf b/samples/drivers/rtc/boards/sk_am62_am6254_a53.conf new file mode 100644 index 000000000000..01028be7938c --- /dev/null +++ b/samples/drivers/rtc/boards/sk_am62_am6254_a53.conf @@ -0,0 +1,7 @@ +# +# Copyright 2026 Texas Instruments Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_RTC_INIT_PRIORITY=70 From eacbdf21b05f0ff322aa4ecd8736ffb2dd758779 Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Mon, 9 Mar 2026 12:41:44 +0530 Subject: [PATCH 129/143] UPSTREAM-PEND: dts: vendor: ti: Add RTC node and power domain for AM62L PR: 105112 - Add SCMI power domain for RTC - Add RTC node and a counter child node which can use rtc-counter wrapper to use the RTC node. Signed-off-by: Sunil Hegde --- dts/vendor/ti/am62l-main.dtsi | 17 +++++++++++++++++ dts/vendor/ti/am62l_power_domains.dtsi | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/dts/vendor/ti/am62l-main.dtsi b/dts/vendor/ti/am62l-main.dtsi index c416528e4904..b81a3c9a0689 100644 --- a/dts/vendor/ti/am62l-main.dtsi +++ b/dts/vendor/ti/am62l-main.dtsi @@ -411,4 +411,21 @@ clocks = <&scmi_clk 101>; status = "disabled"; }; + + main_rtc0: rtc@2b1f0000 { + compatible = "ti,k3-rtc-counter"; + reg = <0x2b1f0000 0x64>; + clock-frequency = ; + clocks = <&scmi_clk 272>; + interrupts = ; + interrupt-parent = <&gic>; + power-domains = <&main_rtc0_pd>; + status = "disabled"; + + counter_rtc0: counter_rtc { + compatible = "zephyr,rtc-counter"; + alarms-count = <2>; + status = "disabled"; + }; + }; }; diff --git a/dts/vendor/ti/am62l_power_domains.dtsi b/dts/vendor/ti/am62l_power_domains.dtsi index 8939c330fa8f..66e4b27d28fd 100644 --- a/dts/vendor/ti/am62l_power_domains.dtsi +++ b/dts/vendor/ti/am62l_power_domains.dtsi @@ -139,5 +139,11 @@ reg = <24>; #power-domain-cells = <0>; }; + + main_rtc0_pd: power-domain@39 { + compatible = "arm,scmi-power-domain"; + reg = <59>; + #power-domain-cells = <0>; + }; }; }; From 0cc0d10394c53cb3fbb78f91ee96e31c0ef7e4c1 Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Mon, 9 Mar 2026 12:42:27 +0530 Subject: [PATCH 130/143] UPSTREAM-PEND: boards: ti: am62l_evm: Enable RTC node PR: 105112 - Enable rtc node for AM62L board Signed-off-by: Sunil Hegde --- boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts index 75b52610065b..dfbb39b441d3 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts @@ -146,3 +146,7 @@ pinctrl-names = "default"; status = "okay"; }; + +&main_rtc0 { + status = "okay"; +}; From 33dc161ae65f11472b2c906fd5329ebb5bdfd41a Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Mon, 23 Mar 2026 14:18:45 +0530 Subject: [PATCH 131/143] UPSTREAM-PEND: boards: ti: am62l_evm: a53.dts: Add GPIO led nodes PR: 106162 - Add GPIO led node and pinmux Signed-off-by: Sunil Hegde --- .../am62l_evm_am62l3_a53-pinctrl.dtsi | 4 ++++ boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi index e305272a03f0..70dd8d70c381 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi @@ -164,4 +164,8 @@ main_ecap0_in: ecap0_in_default { pinmux = ; /* (L22) ECAP0_IN_APWM_OUT */ }; + + main_gpio0_3_led: main_gpio0_led_default { + pinmux = ; /* (D6) MMC1_SDWP */ + }; }; diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts index dfbb39b441d3..363948161847 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts @@ -21,6 +21,18 @@ zephyr,flash = &flash0_memory; }; + aliases { + led0 = &ld9; + }; + + leds: leds { + compatible = "gpio-leds"; + + ld9: led_0 { + gpios = <&main_gpio0_3 27 GPIO_ACTIVE_HIGH>; + }; + }; + cpus { cpu@0 { status = "okay"; @@ -150,3 +162,9 @@ &main_rtc0 { status = "okay"; }; + +&main_gpio0_3 { + pinctrl-0 = <&main_gpio0_3_led>; + pinctrl-names = "default"; + status = "okay"; +}; From db362fd936c6800874a706e755da4f834faaf192 Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Tue, 24 Mar 2026 16:27:25 +0530 Subject: [PATCH 132/143] UPSTREAM-PEND: boards: ti: am62l_evm: a53.dts: Add EEPROM and... PR: 106162 - Add I2C0 and I2C1 pinmux - Add EEPROM and IO expander as child nodes of I2C0 and I2C1 respectively. Signed-off-by: Sunil Hegde --- .../am62l_evm_am62l3_a53-pinctrl.dtsi | 16 ++++++++++ boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts | 29 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi index 70dd8d70c381..d273ea0793f3 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi @@ -168,4 +168,20 @@ main_gpio0_3_led: main_gpio0_led_default { pinmux = ; /* (D6) MMC1_SDWP */ }; + + main_i2c0_scl: i2c0_scl_default { + pinmux = ; /* (B7) I2C0_SCL */ + }; + + main_i2c0_sda: i2c0_sda_default { + pinmux = ; /* (A7) I2C0_SDA */ + }; + + main_i2c1_scl: i2c1_scl_default { + pinmux = ; /* (D7) I2C1_SCL */ + }; + + main_i2c1_sda: i2c1_sda_default { + pinmux = ; /* (A6) I2C1_SDA */ + }; }; diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts index 363948161847..2e3881d1b8e3 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts @@ -168,3 +168,32 @@ pinctrl-names = "default"; status = "okay"; }; + +&main_i2c0 { + pinctrl-0 = <&main_i2c0_scl &main_i2c0_sda>; + pinctrl-names = "default"; + status = "okay"; + + eeprom0: eeprom@51 { + compatible = "atmel,at24"; + reg = <0x51>; + address-width = <16>; + pagesize = <128>; + size = <65536>; + timeout = <5>; + }; +}; + +&main_i2c1 { + pinctrl-0 = <&main_i2c1_scl &main_i2c1_sda>; + pinctrl-names = "default"; + status = "okay"; + + gpio_expander: tca6424a@22 { + compatible = "ti,tca6424a"; + reg = <0x22>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <24>; + }; +}; From bf72a822861dc79c081212415f859e9013a5e42d Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Mon, 23 Mar 2026 16:56:27 +0530 Subject: [PATCH 133/143] UPSTREAM-PEND: dts: vendor: ti: am62l-main: Add DM Timer nodes and PD PR: 106162 Add DM timer nodes for main domain with scmi clocks, pds and Interrupts. Signed-off-by: Sunil Hegde --- dts/arm64/ti/am62l3_a53.dtsi | 20 ++++++++++++++ dts/vendor/ti/am62l-main.dtsi | 36 ++++++++++++++++++++++++++ dts/vendor/ti/am62l_power_domains.dtsi | 24 +++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/dts/arm64/ti/am62l3_a53.dtsi b/dts/arm64/ti/am62l3_a53.dtsi index bb2991f2f960..b70f934cf6b5 100644 --- a/dts/arm64/ti/am62l3_a53.dtsi +++ b/dts/arm64/ti/am62l3_a53.dtsi @@ -201,3 +201,23 @@ interrupts = ; interrupt-parent = <&gic>; }; + +&main_timer0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_timer1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_timer2 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_timer3 { + interrupts = ; + interrupt-parent = <&gic>; +}; diff --git a/dts/vendor/ti/am62l-main.dtsi b/dts/vendor/ti/am62l-main.dtsi index b81a3c9a0689..02fb6ba4f96a 100644 --- a/dts/vendor/ti/am62l-main.dtsi +++ b/dts/vendor/ti/am62l-main.dtsi @@ -428,4 +428,40 @@ status = "disabled"; }; }; + + main_timer0: timer@2400000 { + compatible = "ti,am654-timer"; + reg = <0x2400000 DT_SIZE_K(1)>; + clocks = <&scmi_clk 58>; + clock-names = "fck"; + power-domains = <&main_timer0_pd>; + status = "disabled"; + }; + + main_timer1: timer@2410000 { + compatible = "ti,am654-timer"; + reg = <0x2410000 DT_SIZE_K(1)>; + clocks = <&scmi_clk 63>; + clock-names = "fck"; + power-domains = <&main_timer1_pd>; + status = "disabled"; + }; + + main_timer2: timer@2420000 { + compatible = "ti,am654-timer"; + reg = <0x2420000 DT_SIZE_K(1)>; + clocks = <&scmi_clk 77>; + clock-names = "fck"; + power-domains = <&main_timer2_pd>; + status = "disabled"; + }; + + main_timer3: timer@2430000 { + compatible = "ti,am654-timer"; + reg = <0x2430000 DT_SIZE_K(1)>; + clocks = <&scmi_clk 82>; + clock-names = "fck"; + power-domains = <&main_timer3_pd>; + status = "disabled"; + }; }; diff --git a/dts/vendor/ti/am62l_power_domains.dtsi b/dts/vendor/ti/am62l_power_domains.dtsi index 66e4b27d28fd..ba8e8860518f 100644 --- a/dts/vendor/ti/am62l_power_domains.dtsi +++ b/dts/vendor/ti/am62l_power_domains.dtsi @@ -145,5 +145,29 @@ reg = <59>; #power-domain-cells = <0>; }; + + main_timer0_pd: power-domain@0f { + compatible = "arm,scmi-power-domain"; + reg = <15>; + #power-domain-cells = <0>; + }; + + main_timer1_pd: power-domain@10 { + compatible = "arm,scmi-power-domain"; + reg = <16>; + #power-domain-cells = <0>; + }; + + main_timer2_pd: power-domain@11 { + compatible = "arm,scmi-power-domain"; + reg = <17>; + #power-domain-cells = <0>; + }; + + main_timer3_pd: power-domain@12 { + compatible = "arm,scmi-power-domain"; + reg = <18>; + #power-domain-cells = <0>; + }; }; }; From 256d2458d7d570fc50ca2f7e8abedfa37ca62d57 Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Wed, 25 Mar 2026 17:42:59 +0530 Subject: [PATCH 134/143] UPSTREAM-PEND: dts: bindings: watchdog: ti,j7-rti-wdt: Add... PR: 105913 Currently all boards use the driver which only configures NMI mode for a window violation. With this change: - If interrupts are present in the DT, hardware is NMI capable - If reset-capable property is true, hardware can generate a reset By this we can define the capabilities of the hardware based on which the driver can configure NMI or Reset mode. The existing nodes in the boards which support NMI will not be affected since all of them provide IRQ numbers. Signed-off-by: Sunil Hegde --- dts/bindings/watchdog/ti,j7-rti-wdt.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dts/bindings/watchdog/ti,j7-rti-wdt.yaml b/dts/bindings/watchdog/ti,j7-rti-wdt.yaml index 3baed1e3744c..f8ed7ca978d1 100644 --- a/dts/bindings/watchdog/ti,j7-rti-wdt.yaml +++ b/dts/bindings/watchdog/ti,j7-rti-wdt.yaml @@ -14,3 +14,16 @@ properties: clock-frequency: type: int required: true + + interrupts: + description: + Watchdog interrupt line, if present NMI mode is supported. + + reset-capable: + type: boolean + description: + Indicates reset mode is supported by hardware. + If set, watchdog timeout will trigger a reset. + The SoC can only be configured for either NMI + or Reset mode at a time. Support for NMI or Reset is SoC + specific. From e82fde7e289c192e8de1d829763ebcd7e0b1ae68 Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Wed, 18 Mar 2026 12:36:30 +0530 Subject: [PATCH 135/143] UPSTREAM-PEND: drivers: watchdog: wdt_ti_rtc: Configure... PR: 105913 If DT has IRQ defined, the board supports NMI mode, if reset-capable property is set then the board can support reset as well. Based on these DT properties and the flags passed by the application, NMI mode or Reset mode can be set. Signed-off-by: Sunil Hegde --- drivers/watchdog/wdt_ti_rti.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/watchdog/wdt_ti_rti.c b/drivers/watchdog/wdt_ti_rti.c index bb644a4b806f..51f3f53b2975 100644 --- a/drivers/watchdog/wdt_ti_rti.c +++ b/drivers/watchdog/wdt_ti_rti.c @@ -71,6 +71,8 @@ struct wdt_ti_rti_config { DEVICE_MMIO_ROM; uint64_t freq; + bool nmi_supported; + bool reset_supported; void (*irq_config_func)(void); }; @@ -146,8 +148,17 @@ static int wdt_ti_rti_timeout(const struct device *dev, const struct wdt_timeout return -EINVAL; } - /* Only NMI is supported */ if (cfg->flags == WDT_FLAG_RESET_SOC) { + /* Reset mode requested */ + if (!config->reset_supported) { + return -ENOTSUP; + } + regs->WWDRXNCTRL = RTIWWDRX_RESET; + } else { + /* NMI mode requested */ + if (!config->nmi_supported) { + return -ENOTSUP; + } regs->WWDRXNCTRL = RTIWWDRX_NMI; } @@ -171,6 +182,7 @@ static int wdt_ti_rti_feed(const struct device *dev, int channel_id) return 0; } +#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(interrupts) static void wdt_ti_rti_isr(const struct device *dev) { struct wdt_ti_rti_data *data = DEV_DATA(dev); @@ -184,6 +196,7 @@ static void wdt_ti_rti_isr(const struct device *dev) regs->WDKEY = WDKEY_SEQ0; regs->WDKEY = WDKEY_SEQ1; } +#endif static int wdt_ti_rti_init(const struct device *dev) { @@ -197,7 +210,9 @@ static int wdt_ti_rti_init(const struct device *dev) return -EINVAL; } - config->irq_config_func(); + if (config->nmi_supported) { + config->irq_config_func(); + } return 0; } @@ -211,15 +226,19 @@ static DEVICE_API(wdt, wdt_ti_rti_api) = { #define WDT_TI_RTI_INIT(i) \ static void wdt_ti_rti_irq_config_##i(void) \ { \ - IRQ_CONNECT(DT_INST_IRQN(i), DT_INST_IRQ(i, priority), wdt_ti_rti_isr, \ - DEVICE_DT_INST_GET(i), 0); \ - irq_enable(DT_INST_IRQN(i)); \ + IF_ENABLED(DT_INST_IRQ_HAS_IDX(i, 0), ( \ + IRQ_CONNECT(DT_INST_IRQN(i), DT_INST_IRQ(i, priority), wdt_ti_rti_isr, \ + DEVICE_DT_INST_GET(i), 0); \ + irq_enable(DT_INST_IRQN(i)); \ + )); \ }; \ static struct wdt_ti_rti_data wdt_ti_rti_data_##i = {}; \ \ static struct wdt_ti_rti_config wdt_ti_rti_config_##i = { \ DEVICE_MMIO_ROM_INIT(DT_DRV_INST(i)), \ .freq = DT_INST_PROP(i, clock_frequency), \ + .nmi_supported = DT_INST_IRQ_HAS_IDX(i, 0), \ + .reset_supported = DT_INST_PROP(i, reset_capable), \ .irq_config_func = wdt_ti_rti_irq_config_##i, \ }; \ \ From 625254092907245fea05c9c5a8d6d553cf21a336 Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Wed, 18 Mar 2026 12:45:59 +0530 Subject: [PATCH 136/143] UPSTREAM-PEND: dts: vendor: ti: am62l-main: Add SCMI... PR: 105913 - Add scmi clocks for both RTI0 and RTI1 nodes. - Add reset-capable property so that the driver can configure reset mode supported by AM62L boards. Signed-off-by: Sunil Hegde --- dts/vendor/ti/am62l-main.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dts/vendor/ti/am62l-main.dtsi b/dts/vendor/ti/am62l-main.dtsi index 02fb6ba4f96a..0f9269d636bb 100644 --- a/dts/vendor/ti/am62l-main.dtsi +++ b/dts/vendor/ti/am62l-main.dtsi @@ -239,6 +239,8 @@ compatible = "ti,j7-rti-wdt"; reg = <0x0e000000 0x100>; clock-frequency = ; + clocks = <&scmi_clk 277>; + reset-capable; status = "disabled"; }; @@ -246,6 +248,8 @@ compatible = "ti,j7-rti-wdt"; reg = <0x0e010000 0x100>; clock-frequency = ; + clocks = <&scmi_clk 283>; + reset-capable; status = "disabled"; }; From 5c8f9cb8c4913170823ce61a3073a4dfd2c40ad7 Mon Sep 17 00:00:00 2001 From: Sunil Hegde Date: Fri, 20 Mar 2026 12:26:53 +0530 Subject: [PATCH 137/143] UPSTREAM-PEND: boards: ti: am62l_evm: a53.dts: Enable RTI0 PR: 105913 Set status for watchdog node and also add an alias for watchdog. Signed-off-by: Sunil Hegde --- boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts index 2e3881d1b8e3..1d2a5ab97c46 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts @@ -23,6 +23,7 @@ aliases { led0 = &ld9; + watchdog0 = &main_rti0; }; leds: leds { @@ -197,3 +198,7 @@ ngpios = <24>; }; }; + +&main_rti0 { + status = "okay"; +}; From 30b0238e18af849c5cb3c4bc71205c5fc601f18c Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 26 Mar 2026 20:03:55 +0530 Subject: [PATCH 138/143] UPSTREAM-PEND: drivers: syscon: use uint32_t for register offsets Use uint32_t to allow larger offsets in place of the existing uint16_t configuration. PR: 106755 Signed-off-by: Amneesh Singh --- Backport notes: - Dropped syscon_shell.c changes since it is not present on 4.3.0 --- drivers/syscon/syscon.c | 4 ++-- drivers/syscon/syscon_bflb_efuse.c | 2 +- drivers/syscon/syscon_common.h | 2 +- include/zephyr/drivers/syscon.h | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/syscon/syscon.c b/drivers/syscon/syscon.c index 1d2ce4b12630..f655993ba478 100644 --- a/drivers/syscon/syscon.c +++ b/drivers/syscon/syscon.c @@ -33,7 +33,7 @@ static int syscon_generic_get_base(const struct device *dev, uintptr_t *addr) return 0; } -static int syscon_generic_read_reg(const struct device *dev, uint16_t reg, uint32_t *val) +static int syscon_generic_read_reg(const struct device *dev, uint32_t reg, uint32_t *val) { const struct syscon_generic_config *config = dev->config; struct syscon_generic_data *data = dev->data; @@ -66,7 +66,7 @@ static int syscon_generic_read_reg(const struct device *dev, uint16_t reg, uint3 return 0; } -static int syscon_generic_write_reg(const struct device *dev, uint16_t reg, uint32_t val) +static int syscon_generic_write_reg(const struct device *dev, uint32_t reg, uint32_t val) { const struct syscon_generic_config *config = dev->config; struct syscon_generic_data *data = dev->data; diff --git a/drivers/syscon/syscon_bflb_efuse.c b/drivers/syscon/syscon_bflb_efuse.c index e8d368dbef51..2ff60952731c 100644 --- a/drivers/syscon/syscon_bflb_efuse.c +++ b/drivers/syscon/syscon_bflb_efuse.c @@ -175,7 +175,7 @@ static void efuse_bflb_cache(const struct device *dev) irq_unlock(key); } -static int efuse_bflb_read(const struct device *dev, uint16_t reg, uint32_t *val) +static int efuse_bflb_read(const struct device *dev, uint32_t reg, uint32_t *val) { struct efuse_bflb_data *data = dev->data; diff --git a/drivers/syscon/syscon_common.h b/drivers/syscon/syscon_common.h index 4cc23d21388c..28dfdf83f98a 100644 --- a/drivers/syscon/syscon_common.h +++ b/drivers/syscon/syscon_common.h @@ -22,7 +22,7 @@ extern "C" { * @retval 0 if the register read is valid. * @retval -EINVAL if the read is invalid. */ -static inline int syscon_sanitize_reg(uint16_t *reg, size_t reg_size, uint8_t reg_width) +static inline int syscon_sanitize_reg(uint32_t *reg, size_t reg_size, uint8_t reg_width) { /* Avoid unaligned readings */ *reg = ROUND_DOWN(*reg, reg_width); diff --git a/include/zephyr/drivers/syscon.h b/include/zephyr/drivers/syscon.h index 888db3763e2f..50e3cd554823 100644 --- a/include/zephyr/drivers/syscon.h +++ b/include/zephyr/drivers/syscon.h @@ -41,14 +41,14 @@ typedef int (*syscon_api_get_base)(const struct device *dev, uintptr_t *addr); * * @see syscon_read_reg */ -typedef int (*syscon_api_read_reg)(const struct device *dev, uint16_t reg, uint32_t *val); +typedef int (*syscon_api_read_reg)(const struct device *dev, uint32_t reg, uint32_t *val); /** * API template to write a single register. * * @see syscon_write_reg */ -typedef int (*syscon_api_write_reg)(const struct device *dev, uint16_t reg, uint32_t val); +typedef int (*syscon_api_write_reg)(const struct device *dev, uint32_t reg, uint32_t val); /** * API template to get the size of the syscon register. @@ -102,9 +102,9 @@ static inline int z_impl_syscon_get_base(const struct device *dev, uintptr_t *ad * @retval 0 on success. * @retval -ENOSYS If the API or function isn't implemented. */ -__syscall int syscon_read_reg(const struct device *dev, uint16_t reg, uint32_t *val); +__syscall int syscon_read_reg(const struct device *dev, uint32_t reg, uint32_t *val); -static inline int z_impl_syscon_read_reg(const struct device *dev, uint16_t reg, uint32_t *val) +static inline int z_impl_syscon_read_reg(const struct device *dev, uint32_t reg, uint32_t *val) { const struct syscon_driver_api *api = (const struct syscon_driver_api *)dev->api; @@ -128,9 +128,9 @@ static inline int z_impl_syscon_read_reg(const struct device *dev, uint16_t reg, * @retval 0 on success. * @retval -ENOSYS If the API or function isn't implemented. */ -__syscall int syscon_write_reg(const struct device *dev, uint16_t reg, uint32_t val); +__syscall int syscon_write_reg(const struct device *dev, uint32_t reg, uint32_t val); -static inline int z_impl_syscon_write_reg(const struct device *dev, uint16_t reg, uint32_t val) +static inline int z_impl_syscon_write_reg(const struct device *dev, uint32_t reg, uint32_t val) { const struct syscon_driver_api *api = (const struct syscon_driver_api *)dev->api; From 8ccd80636da9a5edada292ec3361bb00b44c6d4f Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 26 Mar 2026 20:01:51 +0530 Subject: [PATCH 139/143] UPSTREAM-PEND: dts: ti,control-module: add epwm-tbclk space-specifier Add a specifier space for configuring timebase clock for TI's Enhanced PWM via syscon. PR: 88757 Signed-off-by: Amneesh Singh --- dts/bindings/syscon/ti,control-module.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dts/bindings/syscon/ti,control-module.yaml b/dts/bindings/syscon/ti,control-module.yaml index b2725f513862..c9c8af2b11de 100644 --- a/dts/bindings/syscon/ti,control-module.yaml +++ b/dts/bindings/syscon/ti,control-module.yaml @@ -18,3 +18,13 @@ properties: Example: ti,unlock-offsets = <0x1008 /* Partition 1 */ 0x5008>; /* Partition 2 */ + + "#epwm-tbclk-cells": + type: int + const: 2 + description: | + Number of items to expect in a time-base clock specifier for TI Enhanced PWM. + +epwm-tbclk-cells: + - offset + - bit From 8e6f658b6c7aea90ffd296e194f99459b772f37d Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 26 Mar 2026 20:45:49 +0530 Subject: [PATCH 140/143] UPSTREAM-PEND: drivers: pwm: introduce ti_am3352_ehrpwm This is the driver for EPWM/EHRPWM module of TI SoCs. It only uses Action Qualifier submodule currently. PR: 88757 Signed-off-by: Amneesh Singh --- Backport notes: - Fixed conflicts in Kconfig and CMakeLists.txt --- drivers/pwm/CMakeLists.txt | 1 + drivers/pwm/Kconfig | 2 + drivers/pwm/Kconfig.ti_am3352_ehrpwm | 12 + drivers/pwm/pwm_ti_am3352_ehrpwm.c | 433 +++++++++++++++++++++++++ dts/bindings/pwm/ti,am3352-ehrpwm.yaml | 37 +++ 5 files changed, 485 insertions(+) create mode 100644 drivers/pwm/Kconfig.ti_am3352_ehrpwm create mode 100644 drivers/pwm/pwm_ti_am3352_ehrpwm.c create mode 100644 dts/bindings/pwm/ti,am3352-ehrpwm.yaml diff --git a/drivers/pwm/CMakeLists.txt b/drivers/pwm/CMakeLists.txt index 0dcd3a8e86dd..2f03d2025cbd 100644 --- a/drivers/pwm/CMakeLists.txt +++ b/drivers/pwm/CMakeLists.txt @@ -70,3 +70,4 @@ zephyr_library_sources_ifdef(CONFIG_PWM_CAPTURE pwm_capture.c) zephyr_library_sources_ifdef(CONFIG_PWM_SHELL pwm_shell.c) zephyr_library_sources_ifdef(CONFIG_PWM_REALTEK_RTS5912 pwm_realtek_rts5912.c) zephyr_library_sources_ifdef(CONFIG_PWM_TI_AM3352_ECAP pwm_ti_am3352_ecap.c) +zephyr_library_sources_ifdef(CONFIG_PWM_TI_AM3352_EHRPWM pwm_ti_am3352_ehrpwm.c) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 49ea5958a3e7..03655e96afd5 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -144,4 +144,6 @@ source "drivers/pwm/Kconfig.ambiq_timer" source "drivers/pwm/Kconfig.ti_am3352_ecap" +source "drivers/pwm/Kconfig.ti_am3352_ehrpwm" + endif # PWM diff --git a/drivers/pwm/Kconfig.ti_am3352_ehrpwm b/drivers/pwm/Kconfig.ti_am3352_ehrpwm new file mode 100644 index 000000000000..6dbfe830b2a6 --- /dev/null +++ b/drivers/pwm/Kconfig.ti_am3352_ehrpwm @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments +# SPDX-License-Identifier: Apache-2.0 + +config PWM_TI_AM3352_EHRPWM + bool "TI EHRPWM based PWM controller" + default y + depends on DT_HAS_TI_AM3352_EHRPWM_ENABLED + depends on CLOCK_CONTROL + select PINCTRL + select SYSCON + help + Enable EHRPWM controller for TI SoCs diff --git a/drivers/pwm/pwm_ti_am3352_ehrpwm.c b/drivers/pwm/pwm_ti_am3352_ehrpwm.c new file mode 100644 index 000000000000..920134c840cc --- /dev/null +++ b/drivers/pwm/pwm_ti_am3352_ehrpwm.c @@ -0,0 +1,433 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#ifdef CONFIG_CLOCK_CONTROL_TISCI +#include +#endif + +LOG_MODULE_REGISTER(ti_ehrpwm); + +#define DT_DRV_COMPAT ti_am3352_ehrpwm + +#define TI_EHRPWM_PERIOD_CYCLES_MAX (0xFFFF) +#define TI_EHRPWM_NUM_CHANNELS (2) + +struct ti_ehrpwm_regs { + volatile uint16_t TBCTL; /**< Time-Base Control Register, offset: 0x00 */ + uint8_t RESERVED_1[0x8]; /**< Reserved, offset: 0x04 - 0x0A */ + volatile uint16_t TBPRD; /**< Time-Base Period Register, offest: 0x0A */ + uint8_t RESERVED_2[0x6]; /**< Reserved, offset: 0x0E - 0x12 */ + volatile uint16_t CMPA; /**< Counter-Compare A Register, offest: 0x12 */ + volatile uint16_t CMPB; /**< Counter-Compare B Register, offest: 0x14 */ + volatile uint16_t AQCTLA; /**< AQ Control Register for Output A, offset: 0x16 */ + volatile uint16_t AQCTLB; /**< AQ Control Register for Output B, offset: 0x18 */ + volatile uint16_t AQSFRC; /**< AQ Software Force Register, offset: 0x1A */ + volatile uint16_t AQCSFRC /**< AQ Software Continuous Force Register, offset: 0x1C */; +}; + +/* Time Based Control Register */ +#define TI_EHRPWM_TBCTL_CLKDIV GENMASK(12, 10) +#define TI_EHRPWM_TBCTL_CLKDIV_MAX (7) +#define TI_EHRPWM_TBCTL_HSPCLKDIV GENMASK(9, 7) +#define TI_EHRPWM_TBCTL_HSPCLKDIV_MAX (7) +#define TI_EHRPWM_TBCTL_PRDLD BIT(3) +#define TI_EHRPWM_TBCTL_CTRMODE GENMASK(1, 0) +#define TI_EHRPWM_TBCTL_CTRMODE_UP_ONLY (0) +#define TI_EHRPWM_TBCTL_CTRMODE_UP_DOWN (2) + +/* Action Qualifier Control Register */ +#define TI_EHRPWM_AQCTL_CBD GENMASK(11, 10) +#define TI_EHRPWM_AQCTL_CBU GENMASK(9, 8) +#define TI_EHRPWM_AQCTL_CAD GENMASK(7, 6) +#define TI_EHRPWM_AQCTL_CAU GENMASK(5, 4) +#define TI_EHRPWM_AQCTL_PRD GENMASK(3, 2) +#define TI_EHRPWM_AQCTL_ZRO GENMASK(1, 0) + +/* Action Qualifier Software Force Register */ +#define TI_EHRPWM_AQSFRC_RLDCSF GENMASK(7, 6) + +/* Action Qualifier Continuous Software Force Register */ +#define TI_EHRPWM_AQCSFRC_CSFB GENMASK(3, 2) +#define TI_EHRPWM_AQCSFRC_CSFA GENMASK(1, 0) +#define TI_EHRPWM_AQCSFRC_CSF_LOW (1) + +/* Action Qualifier Control Register */ +#define TI_EHRPWM_AQCTL_ZRO GENMASK(1, 0) +#define TI_EHRPWM_AQCTL_PRD GENMASK(3, 2) +#define TI_EHRPWM_AQCTL_CAU GENMASK(5, 4) +#define TI_EHRPWM_AQCTL_CBU GENMASK(9, 8) +#define TI_EHRPWM_AQCTL_FLD_CLR (1) +#define TI_EHRPWM_AQCTL_FLD_SET (2) + +#define DEV_CFG(dev) ((const struct ti_ehrpwm_cfg *)(dev)->config) +#define DEV_DATA(dev) ((struct ti_ehrpwm_data *)(dev)->data) +#define DEV_REGS(dev) ((struct ti_ehrpwm_regs *)DEVICE_MMIO_GET(dev)) + +struct ti_ehrpwm_cfg { + DEVICE_MMIO_ROM; + const struct device *clock_dev; + clock_control_subsys_t clock_subsys; + const struct device *tbclk_syscon; + uint32_t tbclk_offset; + uint8_t tbclk_bit; + clock_control_subsys_t tbclk_subsys; + const struct pinctrl_dev_config *pcfg; +}; + +struct ti_ehrpwm_data { + DEVICE_MMIO_RAM; + uint32_t period_cycles[TI_EHRPWM_NUM_CHANNELS]; + uint32_t prescale_div[TI_EHRPWM_NUM_CHANNELS]; + bool symmetric; + bool enabled; +}; + +static int ti_ehrpwm_configure_tbctl(const struct device *dev, uint32_t channel, + uint32_t period_cycles) +{ + struct ti_ehrpwm_regs *regs = DEV_REGS(dev); + struct ti_ehrpwm_data *data = DEV_DATA(dev); + uint16_t prescale_div; + uint16_t tbctl; + + /* already configured */ + if (data->period_cycles[channel] == period_cycles) { + return 0; + } + + tbctl = regs->TBCTL; + + /* configure shadow loading on period register (=0h) */ + tbctl &= ~TI_EHRPWM_TBCTL_PRDLD; + + /* configure counter mode */ + tbctl &= ~TI_EHRPWM_TBCTL_CTRMODE; + if (data->symmetric) { + tbctl |= FIELD_PREP(TI_EHRPWM_TBCTL_CTRMODE, TI_EHRPWM_TBCTL_CTRMODE_UP_DOWN); + } else { + tbctl |= FIELD_PREP(TI_EHRPWM_TBCTL_CTRMODE, TI_EHRPWM_TBCTL_CTRMODE_UP_ONLY); + } + + /* find the minimum prescaler that will allow configuring period_cycles */ + for (uint16_t clkdiv = 0; clkdiv <= TI_EHRPWM_TBCTL_CLKDIV_MAX; clkdiv++) { + for (uint16_t hspclkdiv = 0; hspclkdiv <= TI_EHRPWM_TBCTL_HSPCLKDIV_MAX; + hspclkdiv++) { + + prescale_div = (1 << clkdiv) * (hspclkdiv ? (hspclkdiv * 2) : 1); + + if (prescale_div > period_cycles / TI_EHRPWM_PERIOD_CYCLES_MAX) { + tbctl &= ~(TI_EHRPWM_TBCTL_CLKDIV | TI_EHRPWM_TBCTL_HSPCLKDIV); + tbctl |= FIELD_PREP(TI_EHRPWM_TBCTL_HSPCLKDIV, hspclkdiv) | + FIELD_PREP(TI_EHRPWM_TBCTL_CLKDIV, clkdiv); + + /* write tbctl */ + regs->TBCTL = tbctl; + + /* save period_cycles */ + data->period_cycles[channel] = period_cycles; + data->prescale_div[channel] = prescale_div; + + /* early return */ + return 0; + } + } + } + + /* period is too long for configuration */ + return -1; +} + +static int ti_ehrpwm_configure_aq(const struct device *dev, uint32_t channel, bool polarity) +{ + struct ti_ehrpwm_data *data = DEV_DATA(dev); + struct ti_ehrpwm_regs *regs = DEV_REGS(dev); + uint16_t aqctl_upmask; + uint16_t aqctl_downmask; + uint16_t aqctl; + + if (channel == 0) { + aqctl = regs->AQCTLA; + aqctl_upmask = TI_EHRPWM_AQCTL_CAU; + aqctl_downmask = TI_EHRPWM_AQCTL_CAD; + } else { + aqctl = regs->AQCTLB; + aqctl_upmask = TI_EHRPWM_AQCTL_CBU; + aqctl_downmask = TI_EHRPWM_AQCTL_CBD; + } + + aqctl &= ~(TI_EHRPWM_AQCTL_ZRO | TI_EHRPWM_AQCTL_PRD | aqctl_upmask | aqctl_downmask); + if (polarity == PWM_POLARITY_NORMAL) { + /* active-high */ + aqctl |= FIELD_PREP(aqctl_upmask, TI_EHRPWM_AQCTL_FLD_CLR); + + aqctl &= ~TI_EHRPWM_AQCTL_PRD; + + if (data->symmetric) { + aqctl &= ~TI_EHRPWM_AQCTL_ZRO; + aqctl |= FIELD_PREP(aqctl_downmask, TI_EHRPWM_AQCTL_FLD_SET); + } else { + aqctl |= FIELD_PREP(TI_EHRPWM_AQCTL_ZRO, TI_EHRPWM_AQCTL_FLD_SET); + aqctl &= ~aqctl_downmask; + } + } else { + /* active-low */ + aqctl |= FIELD_PREP(aqctl_upmask, TI_EHRPWM_AQCTL_FLD_SET); + + aqctl &= ~TI_EHRPWM_AQCTL_ZRO; + + if (data->symmetric) { + aqctl &= ~TI_EHRPWM_AQCTL_PRD; + aqctl |= FIELD_PREP(aqctl_downmask, TI_EHRPWM_AQCTL_FLD_CLR); + } else { + aqctl |= FIELD_PREP(TI_EHRPWM_AQCTL_PRD, TI_EHRPWM_AQCTL_FLD_CLR); + aqctl &= ~aqctl_downmask; + } + } + + if (channel == 0) { + regs->AQCTLA = aqctl; + } else { + regs->AQCTLB = aqctl; + } + + return 0; +} + +static int ti_ehrpwm_enable(const struct device *dev, uint32_t channel) +{ + const struct ti_ehrpwm_cfg *cfg = DEV_CFG(dev); + struct ti_ehrpwm_data *data = DEV_DATA(dev); + struct ti_ehrpwm_regs *regs = DEV_REGS(dev); + uint32_t tbclk_reg; + uint16_t aqcsfrc; + int err; + + /* already enabled */ + if (data->enabled == true) { + return 0; + } + + /* disable forced action qualifier */ + aqcsfrc = regs->AQCSFRC; + if (channel == 0) { + aqcsfrc &= ~TI_EHRPWM_AQCSFRC_CSFA; + } else { + aqcsfrc &= ~TI_EHRPWM_AQCSFRC_CSFB; + } + + /* configure shadow register */ + regs->AQSFRC &= ~TI_EHRPWM_AQSFRC_RLDCSF; + regs->AQCSFRC = aqcsfrc; + + /* enable TBCLK */ + err = syscon_read_reg(cfg->tbclk_syscon, cfg->tbclk_offset, &tbclk_reg); + if (err != 0) { + LOG_ERR("failed to read timebase clock register"); + return err; + } + tbclk_reg |= BIT(cfg->tbclk_bit); + err = syscon_write_reg(cfg->tbclk_syscon, cfg->tbclk_offset, tbclk_reg); + if (err != 0) { + LOG_ERR("failed to write timebase clock register"); + return err; + } + data->enabled = true; + + return 0; +} + +static int ti_ehrpwm_disable(const struct device *dev, uint32_t channel) +{ + const struct ti_ehrpwm_cfg *cfg = DEV_CFG(dev); + struct ti_ehrpwm_data *data = DEV_DATA(dev); + struct ti_ehrpwm_regs *regs = DEV_REGS(dev); + uint16_t aqcsfrc; + uint32_t tbclk_reg; + int err; + + /* already disabled */ + if (data->enabled == false) { + return 0; + } + + /* force continuous low on aq submodule */ + aqcsfrc = regs->AQCSFRC; + if (channel == 0) { + aqcsfrc &= ~TI_EHRPWM_AQCSFRC_CSFA; + aqcsfrc |= FIELD_PREP(TI_EHRPWM_AQCSFRC_CSFA, TI_EHRPWM_AQCSFRC_CSF_LOW); + } else { + aqcsfrc &= ~TI_EHRPWM_AQCSFRC_CSFB; + aqcsfrc |= FIELD_PREP(TI_EHRPWM_AQCSFRC_CSFB, TI_EHRPWM_AQCSFRC_CSF_LOW); + } + + /* configure shadow register */ + regs->AQSFRC &= ~TI_EHRPWM_AQSFRC_RLDCSF; + regs->AQCSFRC = aqcsfrc; + + /* configure active register */ + regs->AQSFRC |= TI_EHRPWM_AQSFRC_RLDCSF; + regs->AQCSFRC = aqcsfrc; + + /* disable TBCLK */ + err = syscon_read_reg(cfg->tbclk_syscon, cfg->tbclk_offset, &tbclk_reg); + if (err != 0) { + LOG_ERR("failed to read timebase clock register"); + return err; + } + tbclk_reg &= ~BIT(cfg->tbclk_bit); + err = syscon_write_reg(cfg->tbclk_syscon, cfg->tbclk_offset, tbclk_reg); + if (err != 0) { + LOG_ERR("failed to write timebase clock register"); + return err; + } + + data->period_cycles[channel] = 0; + data->enabled = false; + + return 0; +} + +static int ti_ehrpwm_set_cycles(const struct device *dev, uint32_t channel, uint32_t period_cycles, + uint32_t pulse_cycles, pwm_flags_t flags) +{ + struct ti_ehrpwm_regs *regs = DEV_REGS(dev); + struct ti_ehrpwm_data *data = DEV_DATA(dev); + int err = 0; + + if (channel >= TI_EHRPWM_NUM_CHANNELS) { + LOG_ERR("invalid channel number %u", channel); + return -EINVAL; + } + + /* there is a common period register, so the period should be same for all channels */ + for (uint32_t i = 0; i < TI_EHRPWM_NUM_CHANNELS; i++) { + if (i != channel && data->period_cycles[i] != 0 && + data->period_cycles[i] != period_cycles) { + LOG_ERR("period value must be same as other channels"); + return -EINVAL; + } + } + + /* force constant low */ + if (period_cycles == 0) { + err = ti_ehrpwm_disable(dev, channel); + if (err != 0) { + LOG_ERR("failed to disable ehrpwm module"); + return err; + } + + /* early return after disable */ + return 0; + } + + err = ti_ehrpwm_enable(dev, channel); + if (err != 0) { + return err; + } + + /* configure action qualifier */ + err = ti_ehrpwm_configure_aq(dev, channel, flags & PWM_POLARITY_MASK); + if (err != 0) { + LOG_ERR("failed to configure action qualifier"); + return err; + } + + /* configure tbctl and prescaler */ + err = ti_ehrpwm_configure_tbctl(dev, channel, period_cycles); + if (err != 0) { + LOG_ERR("failed to configure clock prescaler values"); + return err; + } + + /* update cycles */ + period_cycles /= data->prescale_div[channel]; + pulse_cycles /= data->prescale_div[channel]; + + if (data->symmetric) { + period_cycles /= 2; + pulse_cycles /= 2; + } + + /* write period cycles */ + regs->TBPRD = period_cycles; + + /* write duty cycles */ + if (channel == 0) { + regs->CMPA = pulse_cycles; + } else { + regs->CMPB = pulse_cycles; + } + + return 0; +} + +static int ti_ehrpwm_get_cycles_per_sec(const struct device *dev, uint32_t channel, + uint64_t *cycles) +{ + ARG_UNUSED(channel); + const struct ti_ehrpwm_cfg *cfg = DEV_CFG(dev); + + return clock_control_get_rate(cfg->clock_dev, cfg->clock_subsys, (uint32_t *)cycles); +} + +static int ti_ehrpwm_init(const struct device *dev) +{ + const struct ti_ehrpwm_cfg *cfg = DEV_CFG(dev); + int ret; + + DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); + + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("Fail to configure pinctrl\n"); + return ret; + } + + return 0; +} + +static DEVICE_API(pwm, ti_ehrpwm_api) = { + .set_cycles = ti_ehrpwm_set_cycles, + .get_cycles_per_sec = ti_ehrpwm_get_cycles_per_sec, +}; + +#define TI_EHRPWM_DEFINE_CLK_SUBSYS(n) \ + COND_CODE_1(CONFIG_CLOCK_CONTROL_TISCI, ( \ + static struct tisci_clock_config tisci_fclk_##n = \ + TISCI_GET_CLOCK_DETAILS_BY_INST(n); \ + static const clock_control_subsys_t ti_ehrpwm_clk_subsys_##n = \ + &tisci_fclk_##n; \ + ), (COND_CODE_1(CONFIG_CLOCK_CONTROL_ARM_SCMI, \ + (static const clock_control_subsys_t ti_ehrpwm_clk_subsys_##n = \ + (clock_control_subsys_t)DT_INST_PHA(n, clocks, name); \ + ), (BUILD_ASSERT(0, "Unsupported clock controller");)))) + +#define TI_EHRPWM_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + TI_EHRPWM_DEFINE_CLK_SUBSYS(n); \ + static struct ti_ehrpwm_cfg ti_ehrpwm_config_##n = { \ + DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .clock_subsys = ti_ehrpwm_clk_subsys_##n, \ + .tbclk_syscon = DEVICE_DT_GET(DT_INST_PHANDLE(n, tbclk)), \ + .tbclk_offset = DT_INST_PHA(n, tbclk, offset), \ + .tbclk_bit = DT_INST_PHA(n, tbclk, bit), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + }; \ + \ + static struct ti_ehrpwm_data ti_ehrpwm_data_##n = { \ + .symmetric = DT_INST_PROP(n, symmetric), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, ti_ehrpwm_init, NULL, &ti_ehrpwm_data_##n, &ti_ehrpwm_config_##n, \ + POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, &ti_ehrpwm_api); + +DT_INST_FOREACH_STATUS_OKAY(TI_EHRPWM_INIT) diff --git a/dts/bindings/pwm/ti,am3352-ehrpwm.yaml b/dts/bindings/pwm/ti,am3352-ehrpwm.yaml new file mode 100644 index 000000000000..75c8e5c962e4 --- /dev/null +++ b/dts/bindings/pwm/ti,am3352-ehrpwm.yaml @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 Texas Instruments +# SPDX-License-Identifier: Apache-2.0 + +description: TI SOC EHRPWM based PWM controller + +include: [pwm-controller.yaml, base.yaml, pinctrl-device.yaml] + +compatible: "ti,am3352-ehrpwm" + +properties: + reg: + required: true + + clocks: + required: true + + symmetric: + type: boolean + description: Generate a symmetric PWM by using up-down count mode + + tbclk: + type: phandle-array + specifier-space: epwm-tbclk + description: | + Offset and bit configuration for selecting the time-base clock + Format: <&syscon_phandle offset bit> + - syscon_phandle: reference to syscon device + - offset: register offset within syscon for clock selection + - value: bit within the register that controls the timebase clock + + "#pwm-cells": + const: 3 + +pwm-cells: + - channel + - period + - flags From 0ac0b74ce7822fb7d88940da93b80bf4289e0cf9 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 26 Mar 2026 20:14:39 +0530 Subject: [PATCH 141/143] UPSTREAM-PEND: am243x_evm: am2434: r5f0_0: add support for TI EPWM Add DT nodes for TI's Enhanced PWM. This requires addition of a syscon node as well to configure the timebase clock. PR: 88757 Signed-off-by: Amneesh Singh --- Backport notes: - Resolved device tree conflicts --- .../am243x_evm_am2434_r5f0_0-pinctrl.dtsi | 8 ++ .../am243x_evm/am243x_evm_am2434_r5f0_0.dts | 6 ++ dts/arm/ti/am64x_r5.dtsi | 45 ++++++++ dts/vendor/ti/am64x_main.dtsi | 100 ++++++++++++++++++ 4 files changed, 159 insertions(+) diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi index 578e04be3b1e..69b7d9d81b5d 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi @@ -160,6 +160,14 @@ main_ecap0_in: ecap0_in_default { pinmux = ; /* (D18) ECAP0_IN_APWM_OUT */ }; + + main_epwm0_a: epwm0_a_default { + pinmux = ; /* (U20) EHRPWM0_A */ + }; + + main_epwm0_b: epwm0_b_default { + pinmux = ; /* (U18) EHRPWM0_A */ + }; }; &mcu_pinctrl { diff --git a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts index 848053a5f23c..7b4292ab088e 100644 --- a/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts +++ b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0.dts @@ -291,3 +291,9 @@ pinctrl-names = "default"; status = "okay"; }; + +&main_epwm0 { + pinctrl-0 = <&main_epwm0_a &main_epwm0_b>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/dts/arm/ti/am64x_r5.dtsi b/dts/arm/ti/am64x_r5.dtsi index 374fc2e94d6b..9902f463569e 100644 --- a/dts/arm/ti/am64x_r5.dtsi +++ b/dts/arm/ti/am64x_r5.dtsi @@ -262,3 +262,48 @@ interrupts = <0 142 IRQ_TYPE_EDGE IRQ_DEFAULT_PRIORITY>; interrupt-parent = <&vim>; }; + +&main_epwm0 { + interrupts = <0 108 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_epwm1 { + interrupts = <0 110 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_epwm2 { + interrupts = <0 112 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_epwm3 { + interrupts = <0 114 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_epwm4 { + interrupts = <0 116 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_epwm5 { + interrupts = <0 118 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_epwm6 { + interrupts = <0 146 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_epwm7 { + interrupts = <0 148 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; + +&main_epwm8 { + interrupts = <0 150 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; + interrupt-parent = <&vim>; +}; diff --git a/dts/vendor/ti/am64x_main.dtsi b/dts/vendor/ti/am64x_main.dtsi index 75246da90d84..5e9b5b297502 100644 --- a/dts/vendor/ti/am64x_main.dtsi +++ b/dts/vendor/ti/am64x_main.dtsi @@ -47,6 +47,16 @@ }; }; + main_mmr_cfg0: syscon@43000000 { + compatible = "ti,control-module", "syscon"; + reg = <0x43000000 DT_SIZE_K(128)>; + ranges = <0x00 0x43000000 DT_SIZE_K(128)>; + ti,unlock-offsets = <0x5008>; + #address-cells = <1>; + #size-cells = <1>; + #epwm-tbclk-cells = <2>; + }; + main_timer0: timer@2400000 { compatible = "ti,am654-timer"; reg = <0x2400000 DT_SIZE_K(1)>; @@ -483,4 +493,94 @@ clocks = <&k3_clks 53 0>; status = "disabled"; }; + + main_epwm0: pwm@23000000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23000000 0x100>; + tbclk = <&main_mmr_cfg0 0x4130 0>; + #pwm-cells = <3>; + power-domains = <&epwm0_pd>; + clocks = <&k3_clks 86 0>; + status = "disabled"; + }; + + main_epwm1: pwm@23010000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23010000 0x100>; + tbclk = <&main_mmr_cfg0 0x4130 1>; + #pwm-cells = <3>; + power-domains = <&epwm1_pd>; + clocks = <&k3_clks 87 0>; + status = "disabled"; + }; + + main_epwm2: pwm@23020000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23020000 0x100>; + tbclk = <&main_mmr_cfg0 0x4130 2>; + #pwm-cells = <3>; + power-domains = <&epwm2_pd>; + clocks = <&k3_clks 88 0>; + status = "disabled"; + }; + + main_epwm3: pwm@23030000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23030000 0x100>; + tbclk = <&main_mmr_cfg0 0x4130 3>; + #pwm-cells = <3>; + power-domains = <&epwm3_pd>; + clocks = <&k3_clks 89 0>; + status = "disabled"; + }; + + main_epwm4: pwm@23040000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23040000 0x100>; + tbclk = <&main_mmr_cfg0 0x4130 4>; + #pwm-cells = <3>; + power-domains = <&epwm4_pd>; + clocks = <&k3_clks 90 0>; + status = "disabled"; + }; + + main_epwm5: pwm@23050000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23050000 0x100>; + tbclk = <&main_mmr_cfg0 0x4130 5>; + #pwm-cells = <3>; + power-domains = <&epwm5_pd>; + clocks = <&k3_clks 91 0>; + status = "disabled"; + }; + + main_epwm6: pwm@23060000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23060000 0x100>; + tbclk = <&main_mmr_cfg0 0x4130 6>; + #pwm-cells = <3>; + power-domains = <&epwm6_pd>; + clocks = <&k3_clks 92 0>; + status = "disabled"; + }; + + main_epwm7: pwm@23070000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23070000 0x100>; + tbclk = <&main_mmr_cfg0 0x4130 7>; + #pwm-cells = <3>; + power-domains = <&epwm7_pd>; + clocks = <&k3_clks 93 0>; + status = "disabled"; + }; + + main_epwm8: pwm@23080000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23080000 0x100>; + tbclk = <&main_mmr_cfg0 0x4130 8>; + #pwm-cells = <3>; + power-domains = <&epwm8_pd>; + clocks = <&k3_clks 94 0>; + status = "disabled"; + }; }; From f0cd5b6f540d23649930afc5b1850d91de0a2dfd Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Thu, 26 Mar 2026 20:17:41 +0530 Subject: [PATCH 142/143] UPSTREAM-PEND: am62l_evm: am62l3: a53: add support for TI EPWM Add DT nodes for TI's Enhanced PWM. This requires addition of a syscon node as well to configure the timebase clock. PR: 88757 Signed-off-by: Amneesh Singh --- Backport notes: - Resolved device tree conflicts --- .../am62l_evm_am62l3_a53-pinctrl.dtsi | 8 ++++ boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts | 6 +++ dts/arm64/ti/am62l3_a53.dtsi | 15 +++++++ dts/vendor/ti/am62l-main.dtsi | 39 +++++++++++++++++++ dts/vendor/ti/am62l_power_domains.dtsi | 18 +++++++++ 5 files changed, 86 insertions(+) diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi index d273ea0793f3..be2e7b062750 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi @@ -165,6 +165,14 @@ pinmux = ; /* (L22) ECAP0_IN_APWM_OUT */ }; + main_epwm0_a: epwm0_a_default { + pinmux = ; /* (G22) EHRPWM0_A */ + }; + + main_epwm0_b: epwm0_b_default { + pinmux = ; /* (F22) EHRPWM0_B */ + }; + main_gpio0_3_led: main_gpio0_led_default { pinmux = ; /* (D6) MMC1_SDWP */ }; diff --git a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts index 1d2a5ab97c46..ebe33e28194d 100644 --- a/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts @@ -160,6 +160,12 @@ status = "okay"; }; +&main_epwm0 { + pinctrl-0 = <&main_epwm0_a &main_epwm0_b>; + pinctrl-names = "default"; + status = "okay"; +}; + &main_rtc0 { status = "okay"; }; diff --git a/dts/arm64/ti/am62l3_a53.dtsi b/dts/arm64/ti/am62l3_a53.dtsi index b70f934cf6b5..60092c6b683a 100644 --- a/dts/arm64/ti/am62l3_a53.dtsi +++ b/dts/arm64/ti/am62l3_a53.dtsi @@ -202,6 +202,21 @@ interrupt-parent = <&gic>; }; +&main_epwm0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_epwm1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_epwm2 { + interrupts = ; + interrupt-parent = <&gic>; +}; + &main_timer0 { interrupts = ; interrupt-parent = <&gic>; diff --git a/dts/vendor/ti/am62l-main.dtsi b/dts/vendor/ti/am62l-main.dtsi index 0f9269d636bb..3c49750bd0b9 100644 --- a/dts/vendor/ti/am62l-main.dtsi +++ b/dts/vendor/ti/am62l-main.dtsi @@ -9,6 +9,15 @@ #include "am62l_power_domains.dtsi" / { + main_mmr_cfg3: syscon@9180000 { + compatible = "ti,control-module", "syscon"; + reg = <0x9180000 DT_SIZE_K(512)>; + ranges = <0x00 0x9180000 DT_SIZE_K(512)>; + #address-cells = <1>; + #size-cells = <1>; + #epwm-tbclk-cells = <2>; + }; + main_uart0: serial@2800000 { compatible = "ns16550"; reg = <0x02800000 0x100>; @@ -416,6 +425,36 @@ status = "disabled"; }; + main_epwm0: pwm@23000000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23000000 0x100>; + tbclk = <&main_mmr_cfg3 0x69100 0>; + #pwm-cells = <3>; + power-domains = <&main_epwm0_pd>; + clocks = <&scmi_clk 164>; + status = "disabled"; + }; + + main_epwm1: pwm@23010000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23010000 0x100>; + tbclk = <&main_mmr_cfg3 0x69100 1>; + #pwm-cells = <3>; + power-domains = <&main_epwm1_pd>; + clocks = <&scmi_clk 165>; + status = "disabled"; + }; + + main_epwm2: pwm@23020000 { + compatible = "ti,am3352-ehrpwm"; + reg = <0x23020000 0x100>; + tbclk = <&main_mmr_cfg3 0x69100 2>; + #pwm-cells = <3>; + power-domains = <&main_epwm2_pd>; + clocks = <&scmi_clk 166>; + status = "disabled"; + }; + main_rtc0: rtc@2b1f0000 { compatible = "ti,k3-rtc-counter"; reg = <0x2b1f0000 0x64>; diff --git a/dts/vendor/ti/am62l_power_domains.dtsi b/dts/vendor/ti/am62l_power_domains.dtsi index ba8e8860518f..e77292ebdce9 100644 --- a/dts/vendor/ti/am62l_power_domains.dtsi +++ b/dts/vendor/ti/am62l_power_domains.dtsi @@ -68,6 +68,24 @@ #power-domain-cells = <0>; }; + main_epwm0_pd: power-domain@28 { + compatible = "arm,scmi-power-domain"; + reg = <40>; + #power-domain-cells = <0>; + }; + + main_epwm1_pd: power-domain@29 { + compatible = "arm,scmi-power-domain"; + reg = <41>; + #power-domain-cells = <0>; + }; + + main_epwm2_pd: power-domain@2a { + compatible = "arm,scmi-power-domain"; + reg = <42>; + #power-domain-cells = <0>; + }; + main_spi3_pd: power-domain@4b { compatible = "arm,scmi-power-domain"; reg = <75>; From 749bfc9167a8b7479531007759f16818fc19b530 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sat, 19 Jul 2025 21:13:03 +0530 Subject: [PATCH 143/143] UPSTREAM: dts: arm64: ti: ti_am62x_a53: Use GPIO Proxy commit 9f4a0153b89e0738a9b117592e3312b36840d571 upstream. After trying out a few different approaches, I think having separate nodes for each bank in devicetree and having a GPIO proxy node for the controller is the best solution right now. To be more specific, GPIO proxy solution still allows for bank level GPIO toggling. The linux kernel GPIO davinci driver is currently limited to single pin operations. This might not be a problem in Linux, but I feel like embedded systems would prefer having support for bank level operations. This also does not pose a problem for the future implementation of interrupts due to that fact that the interrupt router is separate device and will require a separate driver. In fact, the GPIO interrupt router is shared between main_gpio0 and main_gpio1. This patch also reverts the previous bank offset related work on GPIO davinci driver. The patch has been tested on PocketBeagle 2. Signed-off-by: Ayush Singh --- .../pocketbeagle_2_am62_a53-common.dtsi | 18 ++- drivers/gpio/gpio_davinci.c | 27 ++-- dts/arm/ti/am62x_m4.dtsi | 4 +- dts/arm/ti/j722s_main.dtsi | 8 +- dts/arm/ti/j722s_mcu.dtsi | 4 +- dts/vendor/ti/am64x_main.dtsi | 8 +- dts/vendor/ti/am64x_mcu.dtsi | 4 +- dts/vendor/ti/k3-am62-main.dtsi | 122 +++++++++++++++++- 8 files changed, 155 insertions(+), 40 deletions(-) diff --git a/boards/beagle/pocketbeagle_2/pocketbeagle_2_am62_a53-common.dtsi b/boards/beagle/pocketbeagle_2/pocketbeagle_2_am62_a53-common.dtsi index 2a7b490a9e2d..532d56d43545 100644 --- a/boards/beagle/pocketbeagle_2/pocketbeagle_2_am62_a53-common.dtsi +++ b/boards/beagle/pocketbeagle_2/pocketbeagle_2_am62_a53-common.dtsi @@ -93,12 +93,28 @@ status = "okay"; }; -&main_gpio0 { +&main_gpio0_0 { pinctrl-0 = <&led_pins_default>; pinctrl-names = "default"; status = "okay"; }; +&main_gpio0_1 { + status = "okay"; +}; + +&main_gpio0_2 { + status = "okay"; +}; + +&main_gpio1_0 { + status = "okay"; +}; + +&main_gpio1_1 { + status = "okay"; +}; + &main_rti0 { status = "okay"; }; diff --git a/drivers/gpio/gpio_davinci.c b/drivers/gpio/gpio_davinci.c index 42ae7ad7f353..65bf182bde08 100644 --- a/drivers/gpio/gpio_davinci.c +++ b/drivers/gpio/gpio_davinci.c @@ -27,6 +27,7 @@ LOG_MODULE_REGISTER(gpio_davinci, CONFIG_GPIO_LOG_LEVEL); #define DEV_CFG(dev) \ ((const struct gpio_davinci_config *)((dev)->config)) #define DEV_DATA(dev) ((struct gpio_davinci_data *)(dev)->data) +#define DEV_REGS(dev) ((struct gpio_davinci_regs *)DEVICE_MMIO_NAMED_GET(dev, port_base)) #define GPIO_DAVINCI_DIR_RESET_VAL (0xFFFFFFFF) @@ -61,22 +62,10 @@ struct gpio_davinci_config { const struct pinctrl_dev_config *pcfg; }; -const unsigned int offset_array[5] = {0x10, 0x38, 0x60, 0x88, 0xb0}; -#define MAX_REGS_BANK ARRAY_SIZE(offset_array) - -#define BANK0 0 - -static struct gpio_davinci_regs *gpio_davinci_get_regs(const struct device *dev, uint8_t bank) -{ - __ASSERT(bank < MAX_REGS_BANK, "Invalid bank"); - return (struct gpio_davinci_regs *)((uint8_t *)DEVICE_MMIO_NAMED_GET(dev, port_base) + - offset_array[bank]); -} - static int gpio_davinci_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) { - volatile struct gpio_davinci_regs *regs = gpio_davinci_get_regs(dev, BANK0); + volatile struct gpio_davinci_regs *regs = DEV_REGS(dev); if ((flags & GPIO_SINGLE_ENDED) != 0) { return -ENOTSUP; @@ -103,7 +92,7 @@ static int gpio_davinci_configure(const struct device *dev, gpio_pin_t pin, static int gpio_davinci_port_get_raw(const struct device *dev, gpio_port_value_t *value) { - volatile struct gpio_davinci_regs *regs = gpio_davinci_get_regs(dev, BANK0); + volatile struct gpio_davinci_regs *regs = DEV_REGS(dev); *value = regs->in_data; @@ -113,7 +102,7 @@ static int gpio_davinci_port_get_raw(const struct device *dev, static int gpio_davinci_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, gpio_port_value_t value) { - volatile struct gpio_davinci_regs *regs = gpio_davinci_get_regs(dev, BANK0); + volatile struct gpio_davinci_regs *regs = DEV_REGS(dev); regs->out_data = (regs->out_data & (~mask)) | (mask & value); @@ -123,7 +112,7 @@ static int gpio_davinci_port_set_masked_raw(const struct device *dev, static int gpio_davinci_port_set_bits_raw(const struct device *dev, gpio_port_pins_t mask) { - volatile struct gpio_davinci_regs *regs = gpio_davinci_get_regs(dev, BANK0); + volatile struct gpio_davinci_regs *regs = DEV_REGS(dev); regs->set_data = mask; @@ -133,7 +122,7 @@ static int gpio_davinci_port_set_bits_raw(const struct device *dev, static int gpio_davinci_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t mask) { - volatile struct gpio_davinci_regs *regs = gpio_davinci_get_regs(dev, BANK0); + volatile struct gpio_davinci_regs *regs = DEV_REGS(dev); regs->clr_data = mask; @@ -143,7 +132,7 @@ static int gpio_davinci_port_clear_bits_raw(const struct device *dev, static int gpio_davinci_port_toggle_bits(const struct device *dev, gpio_port_pins_t mask) { - volatile struct gpio_davinci_regs *regs = gpio_davinci_get_regs(dev, BANK0); + volatile struct gpio_davinci_regs *regs = DEV_REGS(dev); regs->out_data ^= mask; @@ -179,7 +168,7 @@ static int gpio_davinci_init(const struct device *dev) #define GPIO_DAVINCI_INIT_FUNC(n) \ static void gpio_davinci_bank_##n##_config(const struct device *dev) \ { \ - volatile struct gpio_davinci_regs *regs = gpio_davinci_get_regs(dev, BANK0); \ + volatile struct gpio_davinci_regs *regs = DEV_REGS(dev); \ regs->dir = GPIO_DAVINCI_DIR_RESET_VAL; \ } diff --git a/dts/arm/ti/am62x_m4.dtsi b/dts/arm/ti/am62x_m4.dtsi index 37b578e83f1b..ca486a472e1c 100644 --- a/dts/arm/ti/am62x_m4.dtsi +++ b/dts/arm/ti/am62x_m4.dtsi @@ -101,9 +101,9 @@ status = "disabled"; }; - gpio0: gpio@4201000 { + gpio0: gpio@4201010 { compatible = "ti,davinci-gpio"; - reg = <0x4201000 0x100>; + reg = <0x4201010 0x28>; gpio-controller; #gpio-cells = <2>; ngpios = <24>; diff --git a/dts/arm/ti/j722s_main.dtsi b/dts/arm/ti/j722s_main.dtsi index 473e327c0fb7..724afcf1230b 100644 --- a/dts/arm/ti/j722s_main.dtsi +++ b/dts/arm/ti/j722s_main.dtsi @@ -19,18 +19,18 @@ status = "okay"; }; - gpio0: gpio@600000 { + gpio0: gpio@600010 { compatible = "ti,davinci-gpio"; - reg = <0x00600000 0x100>; + reg = <0x00600010 0x28>; gpio-controller; #gpio-cells = <2>; ngpios = <92>; status = "disabled"; }; - gpio1: gpio@601000 { + gpio1: gpio@601010 { compatible = "ti,davinci-gpio"; - reg = <0x00601000 0x100>; + reg = <0x00601010 0x28>; gpio-controller; #gpio-cells = <2>; ngpios = <52>; diff --git a/dts/arm/ti/j722s_mcu.dtsi b/dts/arm/ti/j722s_mcu.dtsi index ec32c29ef747..987ee4e7b3cd 100644 --- a/dts/arm/ti/j722s_mcu.dtsi +++ b/dts/arm/ti/j722s_mcu.dtsi @@ -13,9 +13,9 @@ #address-cells = <1>; #size-cells = <1>; - mcu_gpio0: gpio@4201000 { + mcu_gpio0: gpio@4201010 { compatible = "ti,davinci-gpio"; - reg = <0x4201000 0x100>; + reg = <0x4201010 0x28>; gpio-controller; #gpio-cells = <2>; ngpios = <24>; diff --git a/dts/vendor/ti/am64x_main.dtsi b/dts/vendor/ti/am64x_main.dtsi index 5e9b5b297502..7eee796322dc 100644 --- a/dts/vendor/ti/am64x_main.dtsi +++ b/dts/vendor/ti/am64x_main.dtsi @@ -221,17 +221,17 @@ status = "disabled"; }; - main_gpio0: gpio@600000 { + main_gpio0: gpio@600010 { compatible = "ti,davinci-gpio"; - reg = <0x600000 0x100>; + reg = <0x600010 0x28>; gpio-controller; #gpio-cells = <2>; status = "disabled"; }; - main_gpio1: gpio@601000 { + main_gpio1: gpio@601010 { compatible = "ti,davinci-gpio"; - reg = <0x601000 0x100>; + reg = <0x601010 0x28>; gpio-controller; #gpio-cells = <2>; status = "disabled"; diff --git a/dts/vendor/ti/am64x_mcu.dtsi b/dts/vendor/ti/am64x_mcu.dtsi index 2cff07ed6ad6..72f927f00d67 100644 --- a/dts/vendor/ti/am64x_mcu.dtsi +++ b/dts/vendor/ti/am64x_mcu.dtsi @@ -62,9 +62,9 @@ status = "disabled"; }; - mcu_gpio0: gpio@4201000 { + mcu_gpio0: gpio@4201010 { compatible = "ti,davinci-gpio"; - reg = <0x4201000 0x100>; + reg = <0x4201010 0x28>; gpio-controller; #gpio-cells = <2>; ngpios = <23>; diff --git a/dts/vendor/ti/k3-am62-main.dtsi b/dts/vendor/ti/k3-am62-main.dtsi index e2e86b578fc9..a40e047dce7b 100644 --- a/dts/vendor/ti/k3-am62-main.dtsi +++ b/dts/vendor/ti/k3-am62-main.dtsi @@ -106,26 +106,136 @@ status = "disabled"; }; - main_gpio0: gpio@600000 { + main_gpio0: main-gpio0 { + #gpio-cells = <2>; + gpio-map = <0 0 &main_gpio0_0 0 0>, <1 0 &main_gpio0_0 1 0>, + <2 0 &main_gpio0_0 2 0>, <3 0 &main_gpio0_0 3 0>, + <4 0 &main_gpio0_0 4 0>, <5 0 &main_gpio0_0 5 0>, + <6 0 &main_gpio0_0 6 0>, <7 0 &main_gpio0_0 7 0>, + <8 0 &main_gpio0_0 8 0>, <9 0 &main_gpio0_0 9 0>, + <10 0 &main_gpio0_0 10 0>, <11 0 &main_gpio0_0 11 0>, + <12 0 &main_gpio0_0 12 0>, <13 0 &main_gpio0_0 13 0>, + <14 0 &main_gpio0_0 14 0>, <15 0 &main_gpio0_0 15 0>, + <16 0 &main_gpio0_0 16 0>, <17 0 &main_gpio0_0 17 0>, + <18 0 &main_gpio0_0 18 0>, <19 0 &main_gpio0_0 19 0>, + <20 0 &main_gpio0_0 20 0>, <21 0 &main_gpio0_0 21 0>, + <22 0 &main_gpio0_0 22 0>, <23 0 &main_gpio0_0 23 0>, + <24 0 &main_gpio0_0 24 0>, <25 0 &main_gpio0_0 25 0>, + <26 0 &main_gpio0_0 26 0>, <27 0 &main_gpio0_0 27 0>, + <28 0 &main_gpio0_0 28 0>, <29 0 &main_gpio0_0 29 0>, + <30 0 &main_gpio0_0 30 0>, <31 0 &main_gpio0_0 31 0>, + <32 0 &main_gpio0_1 0 0>, <33 0 &main_gpio0_1 1 0>, + <34 0 &main_gpio0_1 2 0>, <35 0 &main_gpio0_1 3 0>, + <36 0 &main_gpio0_1 4 0>, <37 0 &main_gpio0_1 5 0>, + <38 0 &main_gpio0_1 6 0>, <39 0 &main_gpio0_1 7 0>, + <40 0 &main_gpio0_1 8 0>, <41 0 &main_gpio0_1 9 0>, + <42 0 &main_gpio0_1 10 0>, <43 0 &main_gpio0_1 11 0>, + <44 0 &main_gpio0_1 12 0>, <45 0 &main_gpio0_1 13 0>, + <46 0 &main_gpio0_1 14 0>, <47 0 &main_gpio0_1 15 0>, + <48 0 &main_gpio0_1 16 0>, <49 0 &main_gpio0_1 17 0>, + <50 0 &main_gpio0_1 18 0>, <51 0 &main_gpio0_1 19 0>, + <52 0 &main_gpio0_1 20 0>, <53 0 &main_gpio0_1 21 0>, + <54 0 &main_gpio0_1 22 0>, <55 0 &main_gpio0_1 23 0>, + <56 0 &main_gpio0_1 24 0>, <57 0 &main_gpio0_1 25 0>, + <58 0 &main_gpio0_1 26 0>, <59 0 &main_gpio0_1 27 0>, + <60 0 &main_gpio0_1 28 0>, <61 0 &main_gpio0_1 29 0>, + <62 0 &main_gpio0_1 30 0>, <63 0 &main_gpio0_1 31 0>, + <64 0 &main_gpio0_2 0 0>, <65 0 &main_gpio0_2 1 0>, + <66 0 &main_gpio0_2 2 0>, <67 0 &main_gpio0_2 3 0>, + <68 0 &main_gpio0_2 4 0>, <69 0 &main_gpio0_2 5 0>, + <70 0 &main_gpio0_2 6 0>, <71 0 &main_gpio0_2 7 0>, + <72 0 &main_gpio0_2 8 0>, <73 0 &main_gpio0_2 9 0>, + <74 0 &main_gpio0_2 10 0>, <75 0 &main_gpio0_2 11 0>, + <76 0 &main_gpio0_2 12 0>, <77 0 &main_gpio0_2 13 0>, + <78 0 &main_gpio0_2 14 0>, <79 0 &main_gpio0_2 15 0>, + <80 0 &main_gpio0_2 16 0>, <81 0 &main_gpio0_2 17 0>, + <82 0 &main_gpio0_2 18 0>, <83 0 &main_gpio0_2 17 0>, + <84 0 &main_gpio0_2 20 0>, <85 0 &main_gpio0_2 21 0>, + <86 0 &main_gpio0_2 22 0>, <87 0 &main_gpio0_2 23 0>, + <88 0 &main_gpio0_2 24 0>, <89 0 &main_gpio0_2 25 0>, + <90 0 &main_gpio0_2 26 0>, <91 0 &main_gpio0_2 27 0>; + gpio-map-mask = <0xffff 0x0>; + gpio-map-pass-thru = <0x0 0x1>; + }; + + main_gpio0_0: gpio@600010 { compatible = "ti,davinci-gpio"; - reg = <0x00600000 0x100>; + reg = <0x00600010 0x28>; gpio-controller; #gpio-cells = <2>; - /* FIXME: Enable all 92 GPIOs */ ngpios = <32>; status = "disabled"; }; - main_gpio1: gpio@601000 { + main_gpio0_1: gpio@600038 { compatible = "ti,davinci-gpio"; - reg = <0x00601000 0x100>; + reg = <0x00600038 0x28>; gpio-controller; #gpio-cells = <2>; - /* FIXME: Enable all 52 GPIOs */ ngpios = <32>; status = "disabled"; }; + main_gpio0_2: gpio@600060 { + compatible = "ti,davinci-gpio"; + reg = <0x00600060 0x28>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <28>; + status = "disabled"; + }; + + main_gpio1: main-gpio1 { + #gpio-cells = <2>; + gpio-map = <0 0 &main_gpio1_0 0 0>, <1 0 &main_gpio1_0 1 0>, + <2 0 &main_gpio1_0 2 0>, <3 0 &main_gpio1_0 3 0>, + <4 0 &main_gpio1_0 4 0>, <5 0 &main_gpio1_0 5 0>, + <6 0 &main_gpio1_0 6 0>, <7 0 &main_gpio1_0 7 0>, + <8 0 &main_gpio1_0 8 0>, <9 0 &main_gpio1_0 9 0>, + <10 0 &main_gpio1_0 10 0>, <11 0 &main_gpio1_0 11 0>, + <12 0 &main_gpio1_0 12 0>, <13 0 &main_gpio1_0 13 0>, + <14 0 &main_gpio1_0 14 0>, <15 0 &main_gpio1_0 15 0>, + <16 0 &main_gpio1_0 16 0>, <17 0 &main_gpio1_0 17 0>, + <18 0 &main_gpio1_0 18 0>, <19 0 &main_gpio1_0 19 0>, + <20 0 &main_gpio1_0 20 0>, <21 0 &main_gpio1_0 21 0>, + <22 0 &main_gpio1_0 22 0>, <23 0 &main_gpio1_0 23 0>, + <24 0 &main_gpio1_0 24 0>, <25 0 &main_gpio1_0 25 0>, + <26 0 &main_gpio1_0 26 0>, <27 0 &main_gpio1_0 27 0>, + <28 0 &main_gpio1_0 28 0>, <29 0 &main_gpio1_0 29 0>, + <30 0 &main_gpio1_0 30 0>, <31 0 &main_gpio1_0 31 0>, + <32 0 &main_gpio1_1 0 0>, <33 0 &main_gpio1_1 1 0>, + <34 0 &main_gpio1_1 2 0>, <35 0 &main_gpio1_1 3 0>, + <36 0 &main_gpio1_1 4 0>, <37 0 &main_gpio1_1 5 0>, + <38 0 &main_gpio1_1 6 0>, <39 0 &main_gpio1_1 7 0>, + <40 0 &main_gpio1_1 8 0>, <41 0 &main_gpio1_1 9 0>, + <42 0 &main_gpio1_1 10 0>, <43 0 &main_gpio1_1 11 0>, + <44 0 &main_gpio1_1 12 0>, <45 0 &main_gpio1_1 13 0>, + <46 0 &main_gpio1_1 14 0>, <47 0 &main_gpio1_1 15 0>, + <48 0 &main_gpio1_1 16 0>, <49 0 &main_gpio1_1 17 0>, + <50 0 &main_gpio1_1 18 0>, <51 0 &main_gpio1_1 19 0>, + <52 0 &main_gpio1_1 20 0>; + gpio-map-mask = <0xffff 0x0>; + gpio-map-pass-thru = <0x0 0x1>; + }; + + main_gpio1_0: gpio@601010 { + compatible = "ti,davinci-gpio"; + reg = <0x00601010 0x28>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + status = "disabled"; + }; + + main_gpio1_1: gpio@601038 { + compatible = "ti,davinci-gpio"; + reg = <0x00601038 0x28>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <20>; + status = "disabled"; + }; + main_rti0: watchdog@e000000 { compatible = "ti,j7-rti-wdt"; reg = <0x0e000000 0x100>;