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/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-pinctrl.dtsi b/boards/ti/am243x_evm/am243x_evm_am2434_r5f0_0-pinctrl.dtsi index ebb531a86293..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 @@ -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 = ; }; @@ -40,6 +72,102 @@ 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 */ + }; + + 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 = ; + }; + + 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 b498b214c257..7b4292ab088e 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" / { @@ -20,11 +20,14 @@ zephyr,shell-uart = &main_uart0; zephyr,ipc = &ipc0; zephyr,ipc-shm = &ipc_shm0; + zephyr,flash-controller = &main_ospi0; + zephyr,flash = &flash0_memory; }; aliases { led0 = &ld26; adc0 = &main_adc0; + watchdog0 = &main_rti8; }; leds: leds { @@ -97,8 +100,16 @@ }; &main_adc0 { - ti,vrefp = <1800>; 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>; @@ -106,84 +117,183 @@ 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>; }; }; +&main_rti8 { + status = "okay"; +}; + &main_mbox6 { 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)>; + }; + }; +}; + +/* 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"; + }; +}; + +&main_ecap0 { + pinctrl-0 = <&main_ecap0_in>; + pinctrl-names = "default"; + status = "okay"; +}; + +&main_epwm0 { + pinctrl-0 = <&main_epwm0_a &main_epwm0_b>; + pinctrl-names = "default"; + status = "okay"; +}; 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/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..be2e7b062750 --- /dev/null +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53-pinctrl.dtsi @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ + * + * 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 */ + }; + + 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 */ + }; + + main_ecap0_in: ecap0_in_default { + 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 */ + }; + + 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 new file mode 100644 index 000000000000..ebe33e28194d --- /dev/null +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53.dts @@ -0,0 +1,210 @@ +/* + * 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; + zephyr,flash-controller = &main_ospi0; + zephyr,flash = &flash0_memory; + }; + + aliases { + led0 = &ld9; + watchdog0 = &main_rti0; + }; + + leds: leds { + compatible = "gpio-leds"; + + ld9: led_0 { + gpios = <&main_gpio0_3 27 GPIO_ACTIVE_HIGH>; + }; + }; + + 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"; +}; + +&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)>; + }; + }; +}; + +/* 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"; +}; + +&main_ecap0 { + pinctrl-0 = <&main_ecap0_in>; + pinctrl-names = "default"; + status = "okay"; +}; + +&main_epwm0 { + pinctrl-0 = <&main_epwm0_a &main_epwm0_b>; + pinctrl-names = "default"; + status = "okay"; +}; + +&main_rtc0 { + status = "okay"; +}; + +&main_gpio0_3 { + pinctrl-0 = <&main_gpio0_3_led>; + 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>; + }; +}; + +&main_rti0 { + 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..4e82089b755a --- /dev/null +++ b/boards/ti/am62l_evm/am62l_evm_am62l3_a53_defconfig @@ -0,0 +1,46 @@ +# 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 +CONFIG_ARM_SCMI=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 + +# Clock Control +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/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 000000000000..d9245a843127 Binary files /dev/null and b/boards/ti/am62l_evm/doc/img/am62l_evm.webp differ diff --git a/boards/ti/am62l_evm/doc/index.rst b/boards/ti/am62l_evm/doc/index.rst new file mode 100644 index 000000000000..05b8b0efeefd --- /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/sprujb4 + +.. _TI AM62L Product Page: + https://www.ti.com/product/AM62L + +.. _WIC: + 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 + +.. _build OpenOCD from source: + https://docs.u-boot.org/en/latest/board/ti/k3.html#building-openocd-from-source 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..64a6264d1bdb 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`. @@ -118,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 ********* @@ -129,7 +145,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 +153,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_am6234_a53-pinctrl.dts deleted file mode 100644 index 2bbcb890d717..000000000000 --- a/boards/ti/sk_am62/sk-am62_am6234_a53-pinctrl.dts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2025 Texas Instruments Incorporated - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&pinctrl { - uart0_rx_default: uart0_rx_default { - pinmux = ; - }; - - uart0_tx_default: uart0_tx_default { - pinmux = ; - }; -}; diff --git a/boards/ti/sk_am62/sk-am62_am6254_a53-pinctrl.dts b/boards/ti/sk_am62/sk-am62_am6254_a53-pinctrl.dts new file mode 100644 index 000000000000..7052e8195cdf --- /dev/null +++ b/boards/ti/sk_am62/sk-am62_am6254_a53-pinctrl.dts @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025 Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + uart0_rx_default: uart0_rx_default { + pinmux = ; + }; + + 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_am6234_a53.dts b/boards/ti/sk_am62/sk_am62_am6234_a53.dts deleted file mode 100644 index c2e6746d8757..000000000000 --- a/boards/ti/sk_am62/sk_am62_am6234_a53.dts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2025 Texas Instruments Incorporated - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/dts-v1/; - -#include -#include "sk-am62_am6234_a53-pinctrl.dts" - -/ { - model = "TI AM62X STARTER KIT (SK) EVALUATION MODULE (EVM)"; - compatible = "ti,am62x_a53_sk"; - - chosen { - zephyr,console = &main_uart0; - zephyr,shell-uart = &main_uart0; - zephyr,sram = &ddr0; - }; - - cpus { - cpu@0 { - status = "okay"; - }; - - cpu@1 { - status = "disabled"; - }; - - cpu@2 { - status = "disabled"; - }; - - cpu@3 { - status = "disabled"; - }; - }; - - ddr0: memory@82000000 { - /* Note: This board actually has 2GB DRAM available */ - reg = <0x82000000 DT_SIZE_M(1)>; - }; -}; - -&main_uart0 { - current-speed = <115200>; - pinctrl-0 = <&uart0_rx_default &uart0_tx_default>; - pinctrl-names = "default"; - status = "okay"; -}; diff --git a/boards/ti/sk_am62/sk_am62_am6254_a53.dts b/boards/ti/sk_am62/sk_am62_am6254_a53.dts new file mode 100644 index 000000000000..fb3b26696671 --- /dev/null +++ b/boards/ti/sk_am62/sk_am62_am6254_a53.dts @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2025 Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; + +#include +#include "sk-am62_am6254_a53-pinctrl.dts" + +/ { + model = "TI AM62X STARTER KIT (SK) EVALUATION MODULE (EVM)"; + compatible = "ti,am62x_a53_sk"; + + chosen { + zephyr,console = &main_uart0; + zephyr,shell-uart = &main_uart0; + zephyr,sram = &ddr0; + }; + + aliases { + rtc = &counter_rtc0; + }; + + cpus { + cpu@0 { + status = "okay"; + }; + + cpu@1 { + status = "okay"; + }; + + cpu@2 { + status = "okay"; + }; + + cpu@3 { + status = "okay"; + }; + }; + + ddr0: memory@82000000 { + /* Note: This board actually has 2GB DRAM available */ + reg = <0x82000000 DT_SIZE_M(1)>; + }; +}; + +&main_uart0 { + current-speed = <115200>; + pinctrl-0 = <&uart0_rx_default &uart0_tx_default>; + 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"; +}; + +&main_rtc0 { + status = "okay"; +}; + +&counter_rtc0 { + status = "okay"; +}; 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 52% rename from boards/ti/sk_am62/sk_am62_am6234_a53_defconfig rename to boards/ti/sk_am62/sk_am62_am6254_a53_defconfig index 5f0314b43918..ddb02b1e6f9f 100644 --- a/boards/ti/sk_am62/sk_am62_am6234_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 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/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/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/doc/hardware/arch/arm-scmi.rst b/doc/hardware/arch/arm-scmi.rst index 1055f5d6b896..41ceebd46528 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,21 @@ 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** +#. **System power 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 +159,22 @@ 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 -************************* +.. doxygengroup:: scmi_power + +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. + +.. doxygengroup:: scmi_system + +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 +186,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 @@ -180,8 +200,10 @@ 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 -*************************** +--------------------------- This protocol is intended for management of cpu states. This is done via a set of functions implementing various commands, for 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/drivers/adc/adc_ti_am335x.c b/drivers/adc/adc_ti_am335x.c index 80cd63b60c61..e46740e63277 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]; @@ -191,6 +193,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; @@ -288,10 +296,19 @@ 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"); + return ret; + } + cfg->irq_func(dev); if (cfg->fifo == 0) { @@ -374,7 +391,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) \ @@ -384,8 +401,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/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..1d1045e4f7f1 --- /dev/null +++ b/drivers/clock_control/Kconfig.tisci @@ -0,0 +1,16 @@ +# 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. + +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_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/clock_control/clock_control_tisci.c b/drivers/clock_control/clock_control_tisci.c new file mode 100644 index 000000000000..47836acb163f --- /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->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); + return CLOCK_CONTROL_STATUS_UNKNOWN; + } + if (curr_state) { + return CLOCK_CONTROL_STATUS_ON; + } + if (req_state) { + return CLOCK_CONTROL_STATUS_STARTING; + } + + curr_state = true; + + 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); + 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_TISCI_PRIORITY, &tisci_clock_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(TI_K2G_SCI_CLK_INIT) 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 5f1b8d29ac6f..8224c97aa81a 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -22,6 +22,20 @@ 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. + +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" @@ -132,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.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.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/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/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) diff --git a/drivers/firmware/scmi/CMakeLists.txt b/drivers/firmware/scmi/CMakeLists.txt index 60ea86a5cb69..f349587d8285 100644 --- a/drivers/firmware/scmi/CMakeLists.txt +++ b/drivers/firmware/scmi/CMakeLists.txt @@ -3,13 +3,17 @@ 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) +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) 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) +add_subdirectory_ifdef(CONFIG_ARM_SCMI_SHELL shell) diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index 1f59ab872e67..4fc187e24203 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 @@ -35,6 +45,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 @@ -48,6 +66,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 @@ -72,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/clk.c b/drivers/firmware/scmi/clk.c index e47e4efdfda2..02375dee250b 100644 --- a/drivers/firmware/scmi/clk.c +++ b/drivers/firmware/scmi/clk.c @@ -39,9 +39,8 @@ 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; - /* sanity checks */ + /* input validation */ if (!proto || !rate) { return -EINVAL; } @@ -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,9 +76,8 @@ 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; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } @@ -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,9 +112,8 @@ 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; - /* sanity checks */ + /* input validation */ if (!proto || !parent_id) { return -EINVAL; } @@ -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,9 +150,8 @@ 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; - /* sanity checks */ + /* input validation */ if (!proto) { return -EINVAL; } @@ -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,9 +182,8 @@ int scmi_clock_config_set(struct scmi_protocol *proto, { struct scmi_message msg, reply; int status, ret; - bool use_polling; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } @@ -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,9 +229,8 @@ 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; - /* sanity checks */ + /* input validation */ if (!proto || !attributes) { return -EINVAL; } @@ -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; } @@ -280,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/drivers/firmware/scmi/common.c b/drivers/firmware/scmi/common.c new file mode 100644 index 000000000000..da1a65ccdf03 --- /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, false); + 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, false); + 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, true); + 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, false); + if (ret < 0) { + return ret; + } + + return scmi_status_to_errno(status); +} diff --git a/drivers/firmware/scmi/core.c b/drivers/firmware/scmi/core.c index 378b654c5ec6..ebcf68bdbc45 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,132 +86,113 @@ static int scmi_core_setup_chan(const struct device *transport, return 0; } -static int scmi_interrupt_enable(struct scmi_channel *chan, bool enable) +static int scmi_core_wait_reply(struct scmi_protocol *proto, bool use_polling) { - 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; + if (!use_polling) { + return k_sem_take(&proto->tx->sem, + K_USEC(CONFIG_ARM_SCMI_CHAN_SEM_TIMEOUT_USEC)); } - /* 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; - - /* - * 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); - 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: - /* restore scmi interrupt enable status when disable it pass */ - if (status >= 0) { - scmi_interrupt_enable(proto->tx, true); - } - - 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; + } + + /* + * 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 = (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)); + 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); + 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); + if (!k_is_pre_kernel()) { + 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) +static int scmi_core_protocol_negotiate(struct scmi_protocol *proto) + { - if (!proto->tx) { - return -ENODEV; + uint32_t agent_version, platform_version; + int ret; + + if (!proto) { + return -EINVAL; } - if (!proto->tx->ready) { + agent_version = proto->version; + + if (!agent_version) { + LOG_ERR("Protocol 0x%X: Agent version not specified", proto->id); return -EINVAL; } - if (use_polling) { - return scmi_send_message_polling(proto, msg, reply); - } else { - return scmi_send_message_interrupt(proto, msg, reply); + 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) @@ -235,6 +215,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; diff --git a/drivers/firmware/scmi/mailbox.c b/drivers/firmware/scmi/mailbox.c index 2f18ce89cdce..e513dd1f323f 100644 --- a/drivers/firmware/scmi/mailbox.c +++ b/drivers/firmware/scmi/mailbox.c @@ -23,14 +23,15 @@ 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; 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; @@ -96,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/nxp/cpu.c b/drivers/firmware/scmi/nxp/cpu.c index babd201a4465..8695ae41eab6 100644 --- a/drivers/firmware/scmi/nxp/cpu.c +++ b/drivers/firmware/scmi/nxp/cpu.c @@ -8,7 +8,13 @@ #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; + struct scmi_cpu_info data; +}; int scmi_cpu_sleep_mode_set(struct scmi_cpu_sleep_mode_config *cfg) { @@ -17,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; } @@ -55,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; } @@ -89,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; } @@ -114,3 +120,70 @@ 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; + + /* input validation */ + 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); +} + +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; + + /* input validation */ + 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/drivers/firmware/scmi/pinctrl.c b/drivers/firmware/scmi/pinctrl.c index 73ed000daa0e..b135e8c553fb 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) { @@ -15,11 +16,10 @@ 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); - /* sanity checks */ + /* input validation */ if (!settings) { return -EINVAL; } @@ -52,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 b560ce484571..01c1e01a549d 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; @@ -21,9 +22,8 @@ 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; - /* sanity checks */ + /* input validation */ if (!proto || !power_state) { return -EINVAL; } @@ -41,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; } @@ -62,9 +60,8 @@ 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; - /* sanity checks */ + /* input validation */ if (!proto || !cfg) { return -EINVAL; } @@ -87,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/reboot.c b/drivers/firmware/scmi/reboot.c new file mode 100644 index 000000000000..18b02a082439 --- /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); + +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); +} 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); diff --git a/drivers/firmware/scmi/shmem.c b/drivers/firmware/scmi/shmem.c index 86b1a9659d49..c12423fc3489 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; } @@ -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; @@ -120,7 +122,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; } @@ -149,6 +151,8 @@ int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *ms 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; 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/drivers/firmware/scmi/system.c b/drivers/firmware/scmi/system.c new file mode 100644 index 000000000000..1bfed61b0f91 --- /dev/null +++ b/drivers/firmware/scmi/system.c @@ -0,0 +1,73 @@ +/* + * 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; + + /* input validation */ + 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; + + ret = scmi_send_message(proto, &msg, &reply, false); + if (ret < 0) { + return ret; + } + + return scmi_status_to_errno(status); +} 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. diff --git a/drivers/firmware/tisci/tisci.c b/drivers/firmware/tisci/tisci.c index 0206289bcbed..ed3f4bb16fac 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; }; /** @@ -74,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; } @@ -124,31 +126,50 @@ 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; } + 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); + k_sem_give(&data->data_sem); 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 */ 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; } @@ -158,17 +179,49 @@ 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"); + k_sem_give(&data->data_sem); return ret; } @@ -180,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; @@ -1595,6 +1652,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/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 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/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 444509b695b5..6f3655089419 100644 --- a/drivers/flash/flash_mspi_nor.c +++ b/drivers/flash/flash_mspi_nor.c @@ -25,18 +25,56 @@ 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 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" -static void set_up_xfer(const struct device *dev, enum mspi_xfer_direction dir) +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 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 || + 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, + 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; @@ -44,7 +82,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; @@ -54,11 +92,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; @@ -77,8 +116,7 @@ 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; @@ -86,7 +124,7 @@ static int perform_xfer(const struct device *dev, 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 { @@ -94,45 +132,31 @@ static int perform_xfer(const struct device *dev, 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. - */ - } else if (mem_access) { - /* For commands accessing the flash memory (read and program), - * ensure that the target IO mode is active. - */ - if (!dev_data->in_target_io_mode) { - 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; - } + /* 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; } } - 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); + MSPI_DEVICE_CONFIG_IO_MODE | MSPI_DEVICE_CONFIG_FREQUENCY, cfg); if (rc < 0) { LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; } - - dev_data->in_target_io_mode = mem_access; + dev_data->last_applied_cfg = cfg; } rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, @@ -147,23 +171,34 @@ static int perform_xfer(const struct device *dev, 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); - if (dev_data->in_target_io_mode) { + 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 ? 4 : 0; + dev_data->packet.address = 0; } - dev_data->packet.num_bytes = sizeof(uint8_t); - dev_data->packet.data_buf = sr; - rc = perform_xfer(dev, op_code, false); + + 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; } @@ -191,10 +226,11 @@ 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); - rc = perform_xfer(dev, SPI_NOR_CMD_WREN, false); + 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); return rc; @@ -206,6 +242,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; @@ -214,10 +251,10 @@ 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, false); + rc = perform_xfer(dev, op_code); if (rc < 0) { LOG_ERR("%s 0x%02x failed: %d", __func__, op_code, rc); return rc; @@ -262,7 +299,7 @@ static int acquire(const struct device *dev) LOG_ERR("mspi_dev_config() failed: %d", rc); } else { if (dev_config->multiperipheral_bus) { - dev_data->in_target_io_mode = true; + dev_data->last_applied_cfg = &dev_config->mspi_nor_cfg; } return 0; @@ -364,11 +401,11 @@ 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; - 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; @@ -388,6 +425,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); @@ -416,10 +454,10 @@ 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, true); + rc = perform_xfer(dev, dev_data->cmd_info.pp_cmd); if (rc < 0) { LOG_ERR("Page program xfer failed: %d", rc); break; @@ -462,6 +500,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; @@ -490,8 +529,8 @@ 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); + set_up_xfer(dev, MSPI_TX, dev_config->control_xfer_mode); + rc = perform_xfer(dev, SPI_NOR_CMD_CE); size -= flash_size; } else { @@ -499,8 +538,9 @@ 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); - rc = perform_xfer(dev, best_et->cmd, false); + 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); size -= BIT(best_et->exp); @@ -550,11 +590,12 @@ 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); - if (dev_data->in_target_io_mode) { + 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; dev_data->xfer.addr_length = dev_data->cmd_info.sfdp_addr_4 @@ -566,7 +607,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); } @@ -576,22 +617,33 @@ 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; + static uint8_t id_even[JESD216_READ_ID_LEN + 1] = {0}; int rc; - set_up_xfer(dev, MSPI_RX); - if (dev_data->in_target_io_mode) { + 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 ? 4 : 0; + dev_data->packet.address = 0; } - dev_data->packet.data_buf = id; - dev_data->packet.num_bytes = JESD216_READ_ID_LEN; - rc = perform_xfer(dev, SPI_NOR_CMD_RDID, false); + + 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; } @@ -753,6 +805,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; @@ -771,13 +824,13 @@ 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; 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; @@ -802,6 +855,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; @@ -812,8 +866,8 @@ static int enter_4byte_addressing_mode(const struct device *dev) } } - set_up_xfer(dev, MSPI_TX); - rc = perform_xfer(dev, 0xB7, false); + 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); return rc; @@ -826,12 +880,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) { @@ -841,8 +894,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) { @@ -867,9 +920,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) @@ -929,17 +987,18 @@ 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); - rc = perform_xfer(dev, SPI_NOR_CMD_RESET_EN, false); + 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); - rc = perform_xfer(dev, SPI_NOR_CMD_RESET_MEM, false); + 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); return rc; @@ -965,8 +1024,7 @@ static int soft_reset(const struct device *dev) LOG_ERR("%s: dev_config() failed: %d", __func__, rc); return rc; } - - dev_data->in_target_io_mode = true; + dev_data->last_applied_cfg = &dev_config->mspi_nor_cfg; rc = soft_reset_66_99(dev); if (rc < 0) { @@ -975,13 +1033,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->in_target_io_mode = false; + dev_data->last_applied_cfg = &dev_config->mspi_control_cfg; } rc = soft_reset_66_99(dev); @@ -997,22 +1054,24 @@ 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->in_target_io_mode = false; - #if defined(WITH_SUPPLY_GPIO) if (dev_config->supply.port) { rc = power_supply(dev); @@ -1086,6 +1145,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) { @@ -1109,8 +1195,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->in_target_io_mode = true; + dev_data->chip_initialized = true; if (IS_ENABLED(CONFIG_FLASH_MSPI_NOR_USE_SFDP)) { /* Read the SFDP signature to test if communication with @@ -1261,14 +1346,16 @@ static DEVICE_API(flash, drv_api) = { #endif }; -#define FLASH_INITIAL_CONFIG(inst) \ +#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 = MIN(DT_INST_PROP(inst, mspi_max_frequency), MHZ(50)), \ + .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, \ } @@ -1292,11 +1379,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) @@ -1313,9 +1400,21 @@ 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), \ + 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); \ @@ -1326,11 +1425,11 @@ 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_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, \ @@ -1348,12 +1447,22 @@ 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), \ 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 335af449b261..4e4f942c6aee 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 @@ -96,12 +96,18 @@ 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; 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 { @@ -113,7 +119,12 @@ 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; + 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/drivers/flash/flash_mspi_nor_quirks.h b/drivers/flash/flash_mspi_nor_quirks.h index d58fbea4ff9b..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) @@ -77,10 +69,10 @@ 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, false); + rc = perform_xfer(dev, SPI_NOR_CMD_WRSR); if (rc < 0) { return rc; } @@ -98,10 +90,10 @@ 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, false); + rc = perform_xfer(dev, SPI_NOR_CMD_RDCR); if (rc < 0) { return rc; } @@ -154,12 +146,12 @@ 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; 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) @@ -187,12 +179,12 @@ 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; 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; @@ -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/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) 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/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/drivers/i2c/i2c_omap.c b/drivers/i2c/i2c_omap.c index 7deea477ed2a..d9d5d6e82a23 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" @@ -21,11 +22,14 @@ 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 #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 { @@ -163,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; @@ -277,6 +281,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--; } } @@ -451,7 +456,8 @@ 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) { stat &= ~(I2C_OMAP_STAT_XDR | I2C_OMAP_STAT_XRDY); @@ -479,9 +485,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 +496,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; @@ -532,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 */ @@ -572,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) { 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 01c2478659d6..910271123894 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 @@ -31,22 +32,18 @@ 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))) #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 -#define SEC_PROXY_TIMEOUT_US 1000000 - #define GET_MSG_SEQ(buffer) ((uint32_t *)buffer)[1] struct secproxy_thread { mem_addr_t target_data; @@ -82,20 +79,21 @@ 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) { /* 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 { @@ -104,105 +102,85 @@ 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; } } 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); - - 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 (!data->channel_enable[channel]) { + return; + } - if (!(sys_read32(spt.rt) & 0x7F)) { - 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); } } @@ -213,8 +191,9 @@ 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 (!dev || !msg || !msg->data) { + if (msg == NULL || msg->data == NULL) { LOG_ERR("Invalid parameters"); return -EINVAL; } @@ -234,17 +213,10 @@ 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"); - k_spin_unlock(&data->lock, key); - return -EBUSY; - } - - uint32_t status = sys_read32(spt.rt + RT_THREAD_STATUS); - - if ((status & RT_THREAD_STATUS_CUR_CNT_MASK) == (SECPROXY_MAILBOX_NUM_MSGS)) { + status = secproxy_verify_thread(&spt, THREAD_IS_TX); + if (status != 0) { k_spin_unlock(&data->lock, key); - return -EBUSY; + return status; } if (msg->size > MAILBOX_MBOX_SIZE) { @@ -323,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); @@ -337,13 +324,19 @@ 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); + secproxy_mailbox_flush_thread(dev, channel); + irq_enable(cfg->interrupts[channel]); } else { - irq_disable(cfg->irq); + irq_disable(cfg->interrupts[channel]); } k_spin_unlock(&data->lock, key); @@ -359,28 +352,55 @@ 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, \ - PRE_KERNEL_1, CONFIG_MBOX_TI_SECURE_PROXY_PRIORITY, \ +#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) #define MAILBOX_INST(idx) MAILBOX_INSTANCE_DEFINE(idx); 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..117f0317fc40 --- /dev/null +++ b/drivers/mspi/mspi_cadence.c @@ -0,0 +1,1232 @@ +/* + * 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 + */ + +#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); + +#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 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 { + 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; +}; + +/** + * 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; +} + +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 + */ +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->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("Cmds over 2 bytes long aren't supported"); + return -ENOTSUP; + } else if (request->cmd_length == 0) { + 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 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); + 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); + 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); + + /* 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; + + /* 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); + + 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); + + /* 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); + + /* Reset the STIG register for first time use */ + sys_write32(0, base_addr + CADENCE_MSPI_FLASH_CMD_CTRL_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; + } + + 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); + + 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 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 = + 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; + 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); + + sys_write32(dev_instr_rd_cfg, base_address + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_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; + 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); + + /* 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(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; + 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 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]; + + 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; + 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); + + sys_write32(dev_instr_wr_cfg, base_address + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_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; + 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 = + 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, fifo_address); + 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; + } + } + + /* 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_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; + + 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_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; + + /* + * 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); + + /* configure delays */ + mspi_cadence_configure_delays(controller, required_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; +} + +#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, +}; + +#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)), \ + 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), \ + .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, \ + &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..4f4ae2e784a3 --- /dev/null +++ b/drivers/mspi/mspi_cadence.h @@ -0,0 +1,362 @@ +/* + * 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 +#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_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 + * + * 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/drivers/power_domain/CMakeLists.txt b/drivers/power_domain/CMakeLists.txt index 2749a32d5d63..8d1afcb2ef59 100644 --- a/drivers/power_domain/CMakeLists.txt +++ b/drivers/power_domain/CMakeLists.txt @@ -3,12 +3,15 @@ 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) -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 diff --git a/drivers/power_domain/Kconfig b/drivers/power_domain/Kconfig index 3de9185761c0..e1b70edf08a3 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 30 + 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..f9bf7679c2b4 --- /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_1, \ + CONFIG_POWER_DOMAIN_ARM_SCMI_INIT_PRIORITY, \ + NULL); +DT_INST_FOREACH_STATUS_OKAY(SCMI_PD_DEVICE) diff --git a/drivers/pwm/CMakeLists.txt b/drivers/pwm/CMakeLists.txt index 3497f51688ec..2f03d2025cbd 100644 --- a/drivers/pwm/CMakeLists.txt +++ b/drivers/pwm/CMakeLists.txt @@ -69,3 +69,5 @@ 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) +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 3b9911b2de94..03655e96afd5 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -142,4 +142,8 @@ source "drivers/pwm/Kconfig.wch" 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_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/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_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/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/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/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/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/drivers/watchdog/wdt_ti_rti.c b/drivers/watchdog/wdt_ti_rti.c index 3644dd2ccbc4..51f3f53b2975 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,17 @@ 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; + bool nmi_supported; + bool reset_supported; + void (*irq_config_func)(void); }; static int wdt_ti_rti_setup(const struct device *dev, uint8_t options) @@ -79,6 +88,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 +130,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; @@ -135,6 +149,16 @@ static int wdt_ti_rti_timeout(const struct device *dev, const struct wdt_timeout } 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; } @@ -158,8 +182,25 @@ 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); + 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; +} +#endif + 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 +210,9 @@ static int wdt_ti_rti_init(const struct device *dev) return -EINVAL; } + if (config->nmi_supported) { + config->irq_config_func(); + } return 0; } @@ -180,11 +224,22 @@ static DEVICE_API(wdt, wdt_ti_rti_api) = { }; #define WDT_TI_RTI_INIT(i) \ + static void wdt_ti_rti_irq_config_##i(void) \ + { \ + 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, \ }; \ \ DEVICE_DT_INST_DEFINE(i, wdt_ti_rti_init, NULL, &wdt_ti_rti_data_##i, \ diff --git a/dts/arm/ti/am62x_m4.dtsi b/dts/arm/ti/am62x_m4.dtsi index 74fd0e08d3a5..ca486a472e1c 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 { @@ -92,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/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_r5.dtsi b/dts/arm/ti/am64x_r5.dtsi index f95a2ef08739..9902f463569e 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>; @@ -247,3 +237,73 @@ 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>; +}; + +&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>; +}; + +&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/arm/ti/am64x_r5f0_0.dtsi b/dts/arm/ti/am64x_r5f0_0.dtsi index 0a2f943748d6..064e80d2c9fa 100644 --- a/dts/arm/ti/am64x_r5f0_0.dtsi +++ b/dts/arm/ti/am64x_r5f0_0.dtsi @@ -10,3 +10,20 @@ usr-id = <0>; status = "okay"; }; + +&main_rti8 { + 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 427adad04cf3..82715b94f440 100644 --- a/dts/arm/ti/am64x_r5f0_1.dtsi +++ b/dts/arm/ti/am64x_r5f0_1.dtsi @@ -10,3 +10,20 @@ usr-id = <1>; status = "okay"; }; + +&main_rti9 { + 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 cf9ab7896918..635346f638af 100644 --- a/dts/arm/ti/am64x_r5f1_0.dtsi +++ b/dts/arm/ti/am64x_r5f1_0.dtsi @@ -10,3 +10,20 @@ usr-id = <0>; status = "okay"; }; + +&main_rti10 { + 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 92022c357247..3dd4956bb77a 100644 --- a/dts/arm/ti/am64x_r5f1_1.dtsi +++ b/dts/arm/ti/am64x_r5f1_1.dtsi @@ -10,3 +10,20 @@ usr-id = <1>; status = "okay"; }; + +&main_rti11 { + 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>; +}; 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/arm64/ti/am62l3_a53.dtsi b/dts/arm64/ti/am62l3_a53.dtsi new file mode 100644 index 000000000000..60092c6b683a --- /dev/null +++ b/dts/arm64/ti/am62l3_a53.dtsi @@ -0,0 +1,238 @@ +/* + * 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 0x1000>; + }; + }; + + 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>; +}; + +&main_sdhci0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_sdhci1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_sdhci2 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_ecap0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_ecap1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_ecap2 { + interrupts = ; + 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>; +}; + +&main_timer1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_timer2 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_timer3 { + interrupts = ; + interrupt-parent = <&gic>; +}; diff --git a/dts/arm64/ti/ti_am62x_a53.dtsi b/dts/arm64/ti/ti_am62x_a53.dtsi index ec0a2b6746e3..4cf5a54df41e 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 { @@ -130,3 +139,23 @@ interrupts = ; interrupt-parent = <&gic>; }; + +&main_sdhci0 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_sdhci1 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_sdhci2 { + interrupts = ; + interrupt-parent = <&gic>; +}; + +&main_rtc0 { + interrupts = ; + interrupt-parent = <&gic>; +}; diff --git a/dts/bindings/adc/ti,am335x-adc.yaml b/dts/bindings/adc/ti,am335x-adc.yaml index d9ef025c4c7a..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: @@ -14,7 +14,6 @@ properties: ti,vrefp: type: int - required: true description: Reference Voltage (in mV) ti,fifo: 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/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 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-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/dts/bindings/firmware/arm,scmi-system.yaml b/dts/bindings/firmware/arm,scmi-system.yaml new file mode 100644 index 000000000000..f8b79a8fefd5 --- /dev/null +++ b/dts/bindings/firmware/arm,scmi-system.yaml @@ -0,0 +1,13 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: SCMI (System Control and Management Interface) system power protocol + +compatible: "arm,scmi-system" + +include: [base.yaml] + +properties: + reg: + required: true + const: [0x12] 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/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 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 diff --git a/dts/bindings/mspi/cdns,mspi-controller.yaml b/dts/bindings/mspi/cdns,mspi-controller.yaml new file mode 100644 index 000000000000..a0647ca55d5d --- /dev/null +++ b/dts/bindings/mspi/cdns,mspi-controller.yaml @@ -0,0 +1,63 @@ +# 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 + + 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. 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"] 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: | 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..588d0c041e5a --- /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: SCMI (System Control and Management Interface) power domain + +compatible: "arm,scmi-power-domain" + +include: power-domain.yaml + +properties: + reg: + required: true + description: SCMI power domain ID 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 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 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. diff --git a/dts/bindings/syscon/ti,control-module.yaml b/dts/bindings/syscon/ti,control-module.yaml new file mode 100644 index 000000000000..c9c8af2b11de --- /dev/null +++ b/dts/bindings/syscon/ti,control-module.yaml @@ -0,0 +1,30 @@ +# 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 */ + + "#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 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. diff --git a/dts/vendor/ti/am62l-main.dtsi b/dts/vendor/ti/am62l-main.dtsi new file mode 100644 index 000000000000..3c49750bd0b9 --- /dev/null +++ b/dts/vendor/ti/am62l-main.dtsi @@ -0,0 +1,510 @@ +/* + * 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 + */ + +#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>; + clock-frequency = ; + clocks = <&scmi_clk 358>; + power-domains = <&main_uart0_pd>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart1: serial@2810000 { + compatible = "ns16550"; + reg = <0x02810000 0x100>; + clock-frequency = ; + clocks = <&scmi_clk 312>; + power-domains = <&main_uart1_pd>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart2: serial@2820000 { + compatible = "ns16550"; + reg = <0x02820000 0x100>; + clock-frequency = ; + clocks = <&scmi_clk 314>; + power-domains = <&main_uart2_pd>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart3: serial@2830000 { + compatible = "ns16550"; + reg = <0x02830000 0x100>; + clock-frequency = ; + clocks = <&scmi_clk 316>; + power-domains = <&main_uart3_pd>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart4: serial@2840000 { + compatible = "ns16550"; + reg = <0x02840000 0x100>; + clock-frequency = ; + clocks = <&scmi_clk 318>; + power-domains = <&main_uart4_pd>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart5: serial@2850000 { + compatible = "ns16550"; + reg = <0x02850000 0x100>; + clock-frequency = ; + clocks = <&scmi_clk 320>; + power-domains = <&main_uart5_pd>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_uart6: serial@2860000 { + compatible = "ns16550"; + reg = <0x02860000 0x100>; + clock-frequency = ; + clocks = <&scmi_clk 322>; + power-domains = <&main_uart6_pd>; + current-speed = <115200>; + reg-shift = <2>; + status = "disabled"; + }; + + main_i2c0: i2c@20000000 { + 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"; + }; + + main_i2c1: i2c@20010000 { + 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"; + }; + + main_i2c2: i2c@20020000 { + 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"; + }; + + main_i2c3: i2c@20030000 { + 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"; + }; + + 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 = ; + clocks = <&scmi_clk 277>; + reset-capable; + status = "disabled"; + }; + + main_rti1: watchdog@e010000 { + compatible = "ti,j7-rti-wdt"; + reg = <0x0e010000 0x100>; + clock-frequency = ; + clocks = <&scmi_clk 283>; + reset-capable; + 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 = <&main_spi0_pd>; + 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 = <&main_spi1_pd>; + 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 = <&main_spi2_pd>; + 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 = <&main_spi3_pd>; + clock-frequency = ; + clocks = <&scmi_clk 308>; + 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"; + }; + + 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"; + }; + + 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"; + }; + + 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>; + 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"; + }; + }; + + 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-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/dts/vendor/ti/am62l_power_domains.dtsi b/dts/vendor/ti/am62l_power_domains.dtsi new file mode 100644 index 000000000000..e77292ebdce9 --- /dev/null +++ b/dts/vendor/ti/am62l_power_domains.dtsi @@ -0,0 +1,191 @@ +/* + * 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_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>; + #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>; + #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>; + }; + + 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>; + }; + + 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>; + }; + + main_rtc0_pd: power-domain@39 { + compatible = "arm,scmi-power-domain"; + 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>; + }; + }; +}; diff --git a/dts/vendor/ti/am64x_main.dtsi b/dts/vendor/ti/am64x_main.dtsi index b77e13a8bdd4..7eee796322dc 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 { @@ -27,10 +32,29 @@ 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_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 { @@ -197,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"; @@ -272,6 +296,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"; @@ -335,4 +407,180 @@ #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"; + }; + + 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"; + }; + + 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"; + }; + + 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"; + }; }; diff --git a/dts/vendor/ti/am64x_mcu.dtsi b/dts/vendor/ti/am64x_mcu.dtsi index 5e3b14a6cb25..72f927f00d67 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 { @@ -53,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>; @@ -81,4 +90,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"; + }; }; diff --git a/dts/vendor/ti/k3-am62-main.dtsi b/dts/vendor/ti/k3-am62-main.dtsi index cb5ebde7c2d9..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 = <0x00600010 0x28>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + status = "disabled"; + }; + + main_gpio0_1: gpio@600038 { compatible = "ti,davinci-gpio"; - reg = <0x00600000 0x100>; + reg = <0x00600038 0x28>; gpio-controller; #gpio-cells = <2>; - /* FIXME: Enable all 92 GPIOs */ ngpios = <32>; status = "disabled"; }; - main_gpio1: gpio@601000 { + 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 = <0x00601000 0x100>; + reg = <0x00601010 0x28>; gpio-controller; #gpio-cells = <2>; - /* FIXME: Enable all 52 GPIOs */ 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>; @@ -160,4 +270,73 @@ 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"; + }; + + 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"; + }; + }; }; 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/clock_control/tisci_clock_control.h b/include/zephyr/drivers/clock_control/tisci_clock_control.h new file mode 100644 index 000000000000..7708bdd014fd --- /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(inst) TISCI_GET_CLOCK_DETAILS(DT_DRV_INST(inst)) + +#endif diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index ca3a35183f41..bb6ae869141d 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 * @{ */ @@ -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. */ @@ -190,14 +188,33 @@ 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). + */ +#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). */ @@ -210,35 +227,106 @@ 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_get_value)(const struct device *dev, uint32_t *ticks); typedef int (*counter_api_reset)(const struct device *dev); -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_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); + +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_alarm set_alarm; counter_api_cancel_alarm cancel_alarm; counter_api_set_top_value set_top_value; @@ -247,6 +335,18 @@ __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 */ +#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 */ }; /** @@ -261,8 +361,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; } @@ -278,31 +377,89 @@ __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; } +__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 = - (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; } +#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. @@ -314,14 +471,28 @@ static inline uint32_t z_impl_counter_get_frequency(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_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; } +/** + * @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. * @@ -332,10 +503,56 @@ 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_impl_counter_get_frequency(dev); + 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. + * + * @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 * z_counter_get_frequency(dev)) / NSEC_PER_SEC; + + 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; } /** @@ -348,10 +565,24 @@ static inline uint64_t z_impl_counter_ticks_to_us(const struct device *dev, */ __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_impl_counter_get_frequency(dev); + 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); } /** @@ -359,14 +590,17 @@ static inline uint64_t z_impl_counter_ticks_to_ns(const struct device *dev, * * @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); 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; } @@ -377,14 +611,13 @@ 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); 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); } @@ -402,8 +635,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); } @@ -414,61 +646,56 @@ 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); -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); } /** - * @brief Get current counter 64-bit value. + * @brief Reset the counter to the initial 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 Negative error code on failure getting the counter value + * @retval -errno Negative error code on failure resetting the counter value. */ -__syscall int counter_get_value_64(const struct device *dev, uint64_t *ticks); +__syscall int counter_reset(const struct device *dev); -static inline int z_impl_counter_get_value_64(const struct device *dev, - uint64_t *ticks) +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->get_value_64) { + if (!api->reset) { return -ENOSYS; } - return api->get_value_64(dev, ticks); + return api->reset(dev); } /** - * @brief Reset the counter to the initial value. + * @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 -errno Negative error code on failure resetting the counter value. + * @retval Negative error code on failure setting the counter value */ -__syscall int counter_reset(const struct device *dev); +__syscall int counter_set_value(const struct device *dev, uint32_t ticks); -static inline int z_impl_counter_reset(const struct device *dev) +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->reset) { + if (!api->set_value) { return -ENOSYS; } - return api->reset(dev); + return api->set_value(dev, ticks); } /** @@ -491,16 +718,13 @@ static inline int z_impl_counter_reset(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; @@ -521,14 +745,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; @@ -550,6 +771,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. * @@ -561,15 +786,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; @@ -595,8 +817,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); } @@ -612,8 +833,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); } @@ -670,15 +890,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; @@ -698,18 +915,297 @@ 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; } +/** + * @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 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/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/firmware/scmi/clk.h b/include/zephyr/drivers/firmware/scmi/clk.h index d13cfd136794..2b1ad31051e9 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) @@ -25,6 +33,14 @@ #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 + +/** 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 * @@ -49,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 */ @@ -147,4 +179,20 @@ 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); +/** + * @} + */ + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_CLK_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/nxp/cpu.h b/include/zephyr/drivers/firmware/scmi/nxp/cpu.h index 7d7f742a6aaf..34711e8a175b 100644 --- a/include/zephyr/drivers/firmware/scmi/nxp/cpu.h +++ b/include/zephyr/drivers/firmware/scmi/nxp/cpu.h @@ -25,6 +25,17 @@ #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) + +#define SCMI_NXP_CPU_PROTOCOL_SUPPORTED_VERSION 0x10000 + /** * @struct scmi_cpu_sleep_mode_config * @@ -66,6 +77,30 @@ 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; +}; + +/** + * @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 */ @@ -117,4 +152,25 @@ 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); + +/** + * @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_ */ 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/pinctrl.h b/include/zephyr/drivers/firmware/scmi/pinctrl.h index 3906b91db681..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 @@ -29,6 +37,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 */ @@ -100,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 649209c74690..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,8 +15,53 @@ #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 + +/** + * @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 * @@ -65,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 920aaf5c435d..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 @@ -66,6 +74,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 * @@ -80,6 +98,8 @@ struct scmi_protocol { const struct device *transport; /** protocol private data */ void *data; + /** protocol supported version */ + uint32_t version; }; /** @@ -126,4 +146,60 @@ 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); + +/** + * @} + */ + +/** + * @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..ed0617ec29b4 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) @@ -35,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 @@ -93,4 +103,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 new file mode 100644 index 000000000000..c8c44189f9fc --- /dev/null +++ b/include/zephyr/drivers/firmware/scmi/system.h @@ -0,0 +1,165 @@ +/* + * 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 + * @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 + +/** + * @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_ */ diff --git a/include/zephyr/drivers/firmware/scmi/transport.h b/include/zephyr/drivers/firmware/scmi/transport.h index 2cb72a2b9042..c14dfc847a4c 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; @@ -43,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 @@ -50,61 +68,146 @@ 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 */ + /** + * 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; }; +/** + * @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. + * + * @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 + * + * 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); }; +/** @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, @@ -121,23 +224,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) { @@ -152,20 +239,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, @@ -182,23 +256,12 @@ 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, - struct scmi_message *msg) + struct scmi_message *msg, + bool use_polling) { const struct scmi_transport_api *api = (const struct scmi_transport_api *)transport->api; @@ -207,20 +270,11 @@ 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); } /** - * @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, @@ -237,15 +291,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) @@ -271,4 +317,10 @@ static inline bool scmi_transport_channel_is_free(const struct device *transport */ int scmi_core_transport_init(const struct device *transport); +/** @endcond */ + +/** + * @} + */ + #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 cd05f984ac20..4c7193495823 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 * @@ -46,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) @@ -146,12 +164,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 +228,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 +250,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 +265,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 @@ -283,4 +304,8 @@ #define SCMI_PROTOCOL_PCAP_MONITOR 24 #define SCMI_PROTOCOL_PINCTRL 25 +/** + * @} + */ + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_UTIL_H_ */ 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/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/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_ */ 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..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 * @{ */ @@ -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 b2c4562d4e9b..37d9b977b9c1 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 */ }; @@ -283,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); @@ -311,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); @@ -387,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); @@ -466,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, @@ -495,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/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; 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/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 * @{ */ 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); 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 */ /** @} */ 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/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; +} 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 diff --git a/scripts/dts/python-devicetree/src/devicetree/edtlib.py b/scripts/dts/python-devicetree/src/devicetree/edtlib.py index 183bb9ffb57a..1d9896b300c0 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 = { @@ -2701,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": 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''' 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/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..25cc66d8aad8 100644 --- a/scripts/west_commands/packages.py +++ b/scripts/west_commands/packages.py @@ -4,13 +4,16 @@ 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 + from zephyr_ext_common import ZEPHYR_BASE sys.path.append(os.fspath(Path(__file__).parent.parent)) @@ -157,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 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() 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/am6x/CMakeLists.txt b/soc/ti/k3/am6x/CMakeLists.txt index c20bed681437..1f36a43121fa 100644 --- a/soc/ti/k3/am6x/CMakeLists.txt +++ b/soc/ti/k3/am6x/CMakeLists.txt @@ -4,8 +4,9 @@ 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) + 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 8a5513d7458e..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 @@ -39,6 +40,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.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 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/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(); +} diff --git a/soc/ti/k3/am6x/common/ctrl_partitions.c b/soc/ti/k3/am6x/common/ctrl_partitions.c index a5c8e54563de..28bc69d19533 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 -#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 - 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_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); + 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_ANY_COMPAT_HAS_PROP_STATUS_OKAY(ti_control_module, ti_unlock_offsets) + 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); - } } 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 = { 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); 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 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) { 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