From: Christian Marangi Date: Thu, 17 Oct 2024 13:54:52 +0000 (+0200) Subject: airoha: Introduce EN7581 SoC support X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=9131cb44ffc39372eb77bd068ccb2b349f1a5fa7;p=openwrt%2Fstaging%2Fstintel.git airoha: Introduce EN7581 SoC support Introduce EN7581 SoC support with currently rfb board supported. This is a new 64bit SoC from Airoha that is currently almost fully supported upstream with only the DTS missing. Setting source-only waiting for the full upstream support to be completed. Link: https://github.com/openwrt/openwrt/pull/16730 Signed-off-by: Christian Marangi --- diff --git a/target/linux/airoha/Makefile b/target/linux/airoha/Makefile index f9ced4d45d..58d5f1f78a 100644 --- a/target/linux/airoha/Makefile +++ b/target/linux/airoha/Makefile @@ -3,8 +3,8 @@ include $(TOPDIR)/rules.mk ARCH:=arm BOARD:=airoha BOARDNAME:=Airoha ARM -SUBTARGETS:=en7523 -FEATURES:=dt squashfs nand ramdisk gpio source-only +SUBTARGETS:=en7523 en7581 +FEATURES:=dt squashfs nand ramdisk gpio KERNEL_PATCHVER:=6.6 diff --git a/target/linux/airoha/dts/en7581-evb-emmc.dts b/target/linux/airoha/dts/en7581-evb-emmc.dts new file mode 100644 index 0000000000..04c09e77b3 --- /dev/null +++ b/target/linux/airoha/dts/en7581-evb-emmc.dts @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/dts-v1/; + +/* Bootloader installs ATF here */ +/memreserve/ 0x80000000 0x200000; + +#include +#include +#include "en7581.dtsi" + +/ { + model = "Airoha EN7581 Evaluation Board"; + compatible = "airoha,en7581-evb", "airoha,en7581"; + + aliases { + serial0 = &uart1; + }; + + chosen { + bootargs = "console=ttyS0,115200 earlycon"; + stdout-path = "serial0:115200n8"; + linux,usable-memory-range = <0x0 0x80200000 0x0 0x1fe00000>; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x2 0x00000000>; + }; +}; + +&en7581_pinctrl { + gpio-ranges = <&en7581_pinctrl 0 13 47>; + + mdio_pins: mdio-pins { + mux { + function = "mdio"; + groups = "mdio"; + }; + + conf { + pins = "gpio2"; + output-high; + }; + }; + + pcie0_rst_pins: pcie0-rst-pins { + conf { + pins = "pcie_reset0"; + drive-open-drain = <1>; + }; + }; + + pcie1_rst_pins: pcie1-rst-pins { + conf { + pins = "pcie_reset1"; + drive-open-drain = <1>; + }; + }; + + gswp1_led0_pins: gswp1-led0-pins { + mux { + function = "phy1_led0"; + pins = "gpio33"; + }; + }; + + gswp2_led0_pins: gswp2-led0-pins { + mux { + function = "phy2_led0"; + pins = "gpio34"; + }; + }; + + gswp3_led0_pins: gswp3-led0-pins { + mux { + function = "phy3_led0"; + pins = "gpio35"; + }; + }; + + gswp4_led0_pins: gswp4-led0-pins { + mux { + function = "phy4_led0"; + pins = "gpio42"; + }; + }; + + pwm_gpio18_idx10_pins: pwm-gpio18-idx10-pins { + function = "pwm"; + pins = "gpio18"; + output-enable; + }; + + mmc_pins: mmc-pins { + mux { + function = "emmc"; + groups = "emmc"; + }; + }; +}; + +&mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc_pins>; + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + + card@0 { + compatible = "mmc-card"; + reg = <0>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + bootloader@0 { + label = "bootloader"; + reg = <0x00000000 0x00080000>; + }; + + tclinux@80000 { + label = "tclinux"; + reg = <0x00080000 0x02800000>; + }; + + tclinux_slave@2880000 { + label = "tclinux_slave"; + reg = <0x02880000 0x02800000>; + }; + + rootfs_data@5080000 { + label = "rootfs_data"; + reg = <0x5080000 0x00800000>; + }; + }; + }; +}; + +&i2c0 { + status = "okay"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_rst_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_rst_pins>; + status = "okay"; +}; + +ð { + status = "okay"; +}; + +&gdm1 { + status = "okay"; +}; + +&switch { + pinctrl-names = "default"; + pinctrl-0 = <&mdio_pins>; + status = "okay"; +}; + +&gsw_phy1 { + pinctrl-names = "led"; + pinctrl-0 = <&gswp1_led0_pins>; + status = "okay"; +}; + +&gsw_phy1_led0 { + status = "okay"; +}; + +&gsw_phy2 { + pinctrl-names = "led"; + pinctrl-0 = <&gswp2_led0_pins>; + status = "okay"; +}; + +&gsw_phy2_led0 { + status = "okay"; +}; + +&gsw_phy3 { + pinctrl-names = "led"; + pinctrl-0 = <&gswp3_led0_pins>; + status = "okay"; +}; + +&gsw_phy3_led0 { + status = "okay"; +}; + +&gsw_phy4 { + pinctrl-names = "led"; + pinctrl-0 = <&gswp4_led0_pins>; + status = "okay"; +}; + +&gsw_phy4_led0 { + status = "okay"; +}; diff --git a/target/linux/airoha/dts/en7581-evb.dts b/target/linux/airoha/dts/en7581-evb.dts new file mode 100644 index 0000000000..630cd76ae8 --- /dev/null +++ b/target/linux/airoha/dts/en7581-evb.dts @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/dts-v1/; + +/* Bootloader installs ATF here */ +/memreserve/ 0x80000000 0x200000; + +#include +#include +#include +#include "en7581.dtsi" + +/ { + model = "Airoha EN7581 Evaluation Board"; + compatible = "airoha,en7581-evb", "airoha,en7581"; + + aliases { + serial0 = &uart1; + }; + + chosen { + bootargs = "console=ttyS0,115200 earlycon"; + stdout-path = "serial0:115200n8"; + linux,usable-memory-range = <0x0 0x80200000 0x0 0x1fe00000>; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x2 0x00000000>; + }; + + gpio-keys-polled { + compatible = "gpio-keys-polled"; + poll-interval = <100>; + btn0 { + label = "reset"; + linux,code = ; + gpios = <&en7581_pinctrl 0 GPIO_ACTIVE_LOW>; + }; + }; + + pwmleds { + compatible = "pwm-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pwm_gpio18_idx10_pins>; + lan4_green { + label = "pon:green"; + pwms = <&en7581_pwm 10 4000000 0>; + max-brightness = <255>; + active-low; + }; + }; + + + leds { + compatible = "gpio-leds"; + + pwr_led: led-0 { + label = "pwr"; + color = ; + function = LED_FUNCTION_POWER; + gpios = <&en7581_pinctrl 17 GPIO_ACTIVE_LOW>; + default-state = "on"; + }; + + los_led: led-2 { + label = "los"; + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&en7581_pinctrl 19 GPIO_ACTIVE_LOW>; + default-state = "on"; + }; + + internet_led: led-3 { + label = "internet"; + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&en7581_pinctrl 20 GPIO_ACTIVE_LOW>; + default-state = "on"; + }; + }; +}; + +&en7581_pinctrl { + gpio-ranges = <&en7581_pinctrl 0 13 47>; + + mdio_pins: mdio-pins { + mux { + function = "mdio"; + groups = "mdio"; + }; + + conf { + pins = "gpio2"; + output-high; + }; + }; + + pcie0_rst_pins: pcie0-rst-pins { + conf { + pins = "pcie_reset0"; + drive-open-drain = <1>; + }; + }; + + pcie1_rst_pins: pcie1-rst-pins { + conf { + pins = "pcie_reset1"; + drive-open-drain = <1>; + }; + }; + + gswp1_led0_pins: gswp1-led0-pins { + mux { + function = "phy1_led0"; + pins = "gpio33"; + }; + }; + + gswp2_led0_pins: gswp2-led0-pins { + mux { + function = "phy2_led0"; + pins = "gpio34"; + }; + }; + + gswp3_led0_pins: gswp3-led0-pins { + mux { + function = "phy3_led0"; + pins = "gpio35"; + }; + }; + + gswp4_led0_pins: gswp4-led0-pins { + mux { + function = "phy4_led0"; + pins = "gpio42"; + }; + }; + + pwm_gpio18_idx10_pins: pwm-gpio18-idx10-pins { + function = "pwm"; + pins = "gpio18"; + output-enable; + }; +}; + +&snfi { + status = "okay"; +}; + +&spi_nand { + partitions { + compatible = "airoha,fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + bootloader@0 { + label = "bootloader"; + reg = <0x00000000 0x00080000>; + }; + + tclinux@80000 { + label = "tclinux"; + compatible = "denx,fit"; + reg = <0x00080000 0x02800000>; + }; + + tclinux_slave@2880000 { + label = "tclinux_slave"; + reg = <0x02880000 0x02800000>; + }; + + rootfs_data@5080000 { + label = "rootfs_data"; + reg = <0x5080000 0x00800000>; + }; + + art@ffffffff { + compatible = "airoha,dynamic-art"; + label = "art"; + reg = <0xffffffff 0x00300000>; + }; + }; +}; + +&i2c0 { + status = "okay"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_rst_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_rst_pins>; + status = "okay"; +}; + +ð { + status = "okay"; +}; + +&gdm1 { + status = "okay"; +}; + +&switch { + pinctrl-names = "default"; + pinctrl-0 = <&mdio_pins>; + status = "okay"; +}; + +&gsw_phy1 { + pinctrl-names = "led"; + pinctrl-0 = <&gswp1_led0_pins>; + status = "okay"; +}; + +&gsw_phy1_led0 { + status = "okay"; +}; + +&gsw_phy2 { + pinctrl-names = "led"; + pinctrl-0 = <&gswp2_led0_pins>; + status = "okay"; +}; + +&gsw_phy2_led0 { + status = "okay"; +}; + +&gsw_phy3 { + pinctrl-names = "led"; + pinctrl-0 = <&gswp3_led0_pins>; + status = "okay"; +}; + +&gsw_phy3_led0 { + status = "okay"; +}; + +&gsw_phy4 { + pinctrl-names = "led"; + pinctrl-0 = <&gswp4_led0_pins>; + status = "okay"; +}; + +&gsw_phy4_led0 { + status = "okay"; +}; diff --git a/target/linux/airoha/dts/en7581.dtsi b/target/linux/airoha/dts/en7581.dtsi new file mode 100644 index 0000000000..8abd736875 --- /dev/null +++ b/target/linux/airoha/dts/en7581.dtsi @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +#include +#include +#include +#include +#include +#include + +/ { + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + npu-binary@84000000 { + no-map; + reg = <0x0 0x84000000 0x0 0xa00000>; + }; + + npu-flag@84b0000 { + no-map; + reg = <0x0 0x84b00000 0x0 0x100000>; + }; + + npu-pkt@85000000 { + no-map; + reg = <0x0 0x85000000 0x0 0x1a00000>; + }; + + npu-phyaddr@86b00000 { + no-map; + reg = <0x0 0x86b00000 0x0 0x100000>; + }; + + npu-rxdesc@86d00000 { + no-map; + reg = <0x0 0x86d00000 0x0 0x100000>; + }; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu-map { + cluster0 { + core0 { + cpu = <&cpu0>; + }; + + core1 { + cpu = <&cpu1>; + }; + + core2 { + cpu = <&cpu2>; + }; + + core3 { + cpu = <&cpu3>; + }; + }; + }; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0>; + operating-points-v2 = <&cpu_opp_table>; + enable-method = "psci"; + clock-frequency = <80000000>; + next-level-cache = <&l2>; + #cooling-cells = <2>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1>; + operating-points-v2 = <&cpu_opp_table>; + enable-method = "psci"; + clock-frequency = <80000000>; + next-level-cache = <&l2>; + #cooling-cells = <2>; + }; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x2>; + operating-points-v2 = <&cpu_opp_table>; + enable-method = "psci"; + clock-frequency = <80000000>; + next-level-cache = <&l2>; + #cooling-cells = <2>; + }; + + cpu3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x3>; + operating-points-v2 = <&cpu_opp_table>; + enable-method = "psci"; + clock-frequency = <80000000>; + next-level-cache = <&l2>; + #cooling-cells = <2>; + }; + + l2: l2-cache { + compatible = "cache"; + cache-size = <0x80000>; + cache-line-size = <64>; + cache-level = <2>; + cache-unified; + }; + }; + + cpu_opp_table: opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-500000000 { + opp-hz = /bits/ 64 <500000000>; + }; + + opp-550000000 { + opp-hz = /bits/ 64 <550000000>; + }; + + opp-600000000 { + opp-hz = /bits/ 64 <600000000>; + }; + + opp-650000000 { + opp-hz = /bits/ 64 <650000000>; + }; + + opp-7000000000 { + opp-hz = /bits/ 64 <700000000>; + }; + + opp-7500000000 { + opp-hz = /bits/ 64 <750000000>; + }; + + opp-8000000000 { + opp-hz = /bits/ 64 <800000000>; + }; + + opp-8500000000 { + opp-hz = /bits/ 64 <850000000>; + }; + + opp-9000000000 { + opp-hz = /bits/ 64 <900000000>; + }; + + opp-9500000000 { + opp-hz = /bits/ 64 <950000000>; + }; + + opp-10000000000 { + opp-hz = /bits/ 64 <1000000000>; + }; + + opp-10500000000 { + opp-hz = /bits/ 64 <1050000000>; + }; + + opp-11000000000 { + opp-hz = /bits/ 64 <1100000000>; + }; + + opp-11500000000 { + opp-hz = /bits/ 64 <1150000000>; + }; + + opp-12000000000 { + opp-hz = /bits/ 64 <1200000000>; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + }; + + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + + thermal-sensors = <&thermal 0>; + + trips { + cpu_hot: cpu-hot { + temperature = <95000>; + hysteresis = <1000>; + type = "hot"; + }; + + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_hot>; + cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gic: interrupt-controller@9000000 { + compatible = "arm,gic-v3"; + interrupt-controller; + #interrupt-cells = <3>; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x09000000 0x0 0x20000>, + <0x0 0x09080000 0x0 0x80000>, + <0x0 0x09400000 0x0 0x2000>, + <0x0 0x09500000 0x0 0x2000>, + <0x0 0x09600000 0x0 0x20000>; + interrupts = ; + }; + + uart1: serial@1fbf0000 { + compatible = "ns16550"; + reg = <0x0 0x1fbf0000 0x0 0x30>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = ; + clock-frequency = <1843200>; + }; + + watchdog@1fbf0100 { + compatible = "airoha,en7581-wdt"; + reg = <0x0 0x1fbf0100 0x0 0x38>; + + clocks = <&scuclk EN7523_CLK_BUS>; + clock-names = "bus"; + }; + + uart2: serial@1fbf0300 { + compatible = "airoha,en7523-uart"; + reg = <0x0 0x1fbf0300 0x0 0x30>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = ; + clock-frequency = <7372800>; + + status = "disabled"; + }; + + hsuart3: serial@1fbe1000 { + compatible = "airoha,en7523-uart"; + reg = <0x0 0x1fbe1000 0x0 0x40>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = ; + clock-frequency = <7372800>; + + status = "disabled"; + }; + + uart4: serial@1fbf0600 { + compatible = "airoha,en7523-uart"; + reg = <0x0 0x1fbf0600 0x0 0x30>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = ; + clock-frequency = <7372800>; + + status = "disabled"; + }; + + uart5: serial@1fbf0700 { + compatible = "airoha,en7523-uart"; + reg = <0x0 0x1fbf0700 0x0 0x30>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = ; + clock-frequency = <7372800>; + + status = "disabled"; + }; + + chip_scu: syscon@1fa20000 { + compatible = "airoha,en7581-chip-scu", "syscon"; + reg = <0x0 0x1fa20000 0x0 0x388>; + }; + + syscon@1fbe3400 { + compatible = "airoha,en7581-pbus-csr", "syscon"; + reg = <0x0 0x1fbe3400 0x0 0xff>; + }; + + scuclk: clock-controller@1fa20000 { + compatible = "airoha,en7581-scu"; + reg = <0x0 0x1fb00000 0x0 0x970>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + rng@1faa1000 { + compatible = "airoha,en7581-trng"; + reg = <0x0 0x1faa1000 0x0 0xc04>; + interrupts = ; + }; + + crypto@1e004000 { + compatible = "inside-secure,safexcel-eip93ies"; + reg = <0x0 0x1fb70000 0x0 0x1000>; + + interrupts = ; + }; + + thermal: thermal-sensor@1efbd800 { + compatible = "airoha,en7581-thermal"; + reg = <0x0 0x1efbd000 0x0 0xd5c>; + interrupts = ; + airoha,chip-scu = <&chip_scu>; + + #thermal-sensor-cells = <0>; + }; + + system-controller@1fbf0200 { + compatible = "syscon", "simple-mfd"; + reg = <0x0 0x1fbf0200 0x0 0xc0>; + + en7581_pinctrl: pinctrl { + compatible = "airoha,en7581-pinctrl"; + + interrupt-parent = <&gic>; + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + en7581_pwm: pwm { + compatible = "airoha,en7581-pwm"; + + #pwm-cells = <3>; + }; + }; + + i2cclock: i2cclock@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; + + /* 20 MHz */ + clock-frequency = <20000000>; + }; + + i2c0: i2c0@1fbf8000 { + compatible = "mediatek,mt7621-i2c"; + reg = <0x0 0x1fbf8000 0x0 0x100>; + + clocks = <&i2cclock>; + + /* 100 kHz */ + clock-frequency = <100000>; + #address-cells = <1>; + #size-cells = <0>; + + status = "disable"; + }; + + i2c1: i2c1@1fbf8100 { + compatible = "mediatek,mt7621-i2c"; + reg = <0x0 0x1fbf8100 0x0 0x100>; + + clocks = <&i2cclock>; + + /* 100 kHz */ + clock-frequency = <100000>; + #address-cells = <1>; + #size-cells = <0>; + + status = "disable"; + }; + + snfi: spi@1fa10000 { + compatible = "airoha,en7581-snand"; + reg = <0x0 0x1fa10000 0x0 0x140>, + <0x0 0x1fa11000 0x0 0x160>; + + clocks = <&scuclk EN7523_CLK_SPI>; + clock-names = "spi"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + spi_nand: nand@0 { + compatible = "spi-nand"; + reg = <0>; + spi-max-frequency = <50000000>; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <2>; + airoha,bmt; + }; + }; + + mmc0: mmc@1fa0e000 { + compatible = "mediatek,mt7622-mmc"; + reg = <0x0 0x1fa0e000 0x0 0x1000>, + <0x0 0x1fa0c000 0x0 0x60>; + interrupts = ; + bus-width = <4>; + max-frequency = <52000000>; + disable-wp; + cap-mmc-highspeed; + non-removable; + + status = "disabled"; + }; + + pciephy: phy@1fa5a000 { + compatible = "airoha,en7581-pcie-phy"; + reg = <0x0 0x1fa5a000 0x0 0xfff>, + <0x0 0x1fa5b000 0x0 0xfff>, + <0x0 0x1fa5c000 0x0 0xfff>, + <0x0 0x1fc10044 0x0 0x4>, + <0x0 0x1fc30044 0x0 0x4>, + <0x0 0x1fc15030 0x0 0x104>; + reg-names = "csr-2l", "pma0", "pma1", + "p0-xr-dtime", "p1-xr-dtime", + "rx-aeq"; + #phy-cells = <0>; + }; + + pcie0: pcie@1fc00000 { + compatible = "airoha,en7581-pcie"; + device_type = "pci"; + linux,pci-domain = <0>; + #address-cells = <3>; + #size-cells = <2>; + + reg = <0x0 0x1fc00000 0x0 0x1670>; + reg-names = "pcie-mac"; + + clocks = <&scuclk EN7523_CLK_PCIE>; + clock-names = "sys-ck"; + + phys = <&pciephy>; + phy-names = "pcie-phy"; + + ranges = <0x02000000 0 0x20000000 0x0 0x20000000 0 0x4000000>; + + resets = <&scuclk EN7581_PCIE0_RST>, + <&scuclk EN7581_PCIE1_RST>, + <&scuclk EN7581_PCIE2_RST>; + reset-names = "phy-lane0", "phy-lane1", "phy-lane2"; + + interrupts = ; + bus-range = <0x00 0xff>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc0 0>, + <0 0 0 2 &pcie_intc0 1>, + <0 0 0 3 &pcie_intc0 2>, + <0 0 0 4 &pcie_intc0 3>; + + status = "disabled"; + + pcie_intc0: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; + + pcie1: pcie@1fc20000 { + compatible = "airoha,en7581-pcie"; + device_type = "pci"; + linux,pci-domain = <1>; + #address-cells = <3>; + #size-cells = <2>; + + reg = <0x0 0x1fc20000 0x0 0x1670>; + reg-names = "pcie-mac"; + + clocks = <&scuclk EN7523_CLK_PCIE>; + clock-names = "sys-ck"; + + phys = <&pciephy>; + phy-names = "pcie-phy"; + + ranges = <0x02000000 0 0x24000000 0x0 0x24000000 0 0x4000000>; + + resets = <&scuclk EN7581_PCIE0_RST>, + <&scuclk EN7581_PCIE1_RST>, + <&scuclk EN7581_PCIE2_RST>; + reset-names = "phy-lane0", "phy-lane1", "phy-lane2"; + + interrupts = ; + bus-range = <0x00 0xff>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc1 0>, + <0 0 0 2 &pcie_intc1 1>, + <0 0 0 3 &pcie_intc1 2>, + <0 0 0 4 &pcie_intc1 3>; + + status = "disabled"; + + pcie_intc1: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; + + eth: ethernet@1fb50000 { + compatible = "airoha,en7581-eth"; + reg = <0 0x1fb50000 0 0x2600>, + <0 0x1fb54000 0 0x2000>, + <0 0x1fb56000 0 0x2000>; + reg-names = "fe", "qdma0", "qdma1"; + + resets = <&scuclk EN7581_FE_RST>, + <&scuclk EN7581_FE_PDMA_RST>, + <&scuclk EN7581_FE_QDMA_RST>, + <&scuclk EN7581_XSI_MAC_RST>, + <&scuclk EN7581_DUAL_HSI0_MAC_RST>, + <&scuclk EN7581_DUAL_HSI1_MAC_RST>, + <&scuclk EN7581_HSI_MAC_RST>, + <&scuclk EN7581_XFP_MAC_RST>; + reset-names = "fe", "pdma", "qdma", "xsi-mac", + "hsi0-mac", "hsi1-mac", "hsi-mac", + "xfp-mac"; + + interrupts = , + , + , + , + , + , + , + , + , + ; + + status = "disabled"; + + #address-cells = <1>; + #size-cells = <0>; + + gdm1: ethernet@1 { + compatible = "airoha,eth-mac"; + reg = <1>; + phy-mode = "internal"; + status = "disabled"; + + fixed-link { + speed = <1000>; + full-duplex; + pause; + }; + }; + }; + + switch: switch@1fb58000 { + compatible = "airoha,en7581-switch"; + reg = <0 0x1fb58000 0 0x8000>; + resets = <&scuclk EN7581_GSW_RST>; + + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic>; + interrupts = ; + + status = "disabled"; + + #address-cells = <1>; + #size-cells = <1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + gsw_port1: port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "internal"; + phy-handle = <&gsw_phy1>; + }; + + gsw_port2: port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "internal"; + phy-handle = <&gsw_phy2>; + }; + + gsw_port3: port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "internal"; + phy-handle = <&gsw_phy3>; + }; + + gsw_port4: port@4 { + reg = <4>; + label = "lan4"; + phy-mode = "internal"; + phy-handle = <&gsw_phy4>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gdm1>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + gsw_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <9>; + phy-mode = "internal"; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + gsw_phy1_led0: gsw-phy1-led0@0 { + reg = <0>; + function = "phy1_led0"; + status = "disabled"; + }; + + gsw_phy1_led1: gsw-phy1-led1@1 { + reg = <1>; + function = "phy1_led1"; + status = "disabled"; + }; + }; + }; + + gsw_phy2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <10>; + phy-mode = "internal"; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + gsw_phy2_led0: gsw-phy2-led0@0 { + reg = <0>; + function = "phy2_led0"; + status = "disabled"; + }; + + gsw_phy2_led1: gsw-phy2-led1@1 { + reg = <1>; + function = "phy1_led1"; + status = "disabled"; + }; + }; + }; + + gsw_phy3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <11>; + phy-mode = "internal"; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + gsw_phy3_led0: gsw-phy3-led0@0 { + reg = <0>; + function = LED_FUNCTION_LAN; + status = "disabled"; + }; + + gsw_phy3_led1: gsw-phy3-led1@1 { + reg = <1>; + function = LED_FUNCTION_LAN; + status = "disabled"; + }; + }; + }; + + gsw_phy4: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <12>; + phy-mode = "internal"; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + gsw_phy4_led0: gsw-phy4-led0@0 { + reg = <0>; + function = LED_FUNCTION_LAN; + status = "disabled"; + }; + + gsw_phy4_led1: gsw-phy4-led1@1 { + reg = <1>; + function = LED_FUNCTION_LAN; + status = "disabled"; + }; + }; + }; + }; + }; + }; +}; diff --git a/target/linux/airoha/en7581/config-6.6 b/target/linux/airoha/en7581/config-6.6 new file mode 100644 index 0000000000..5001de9b19 --- /dev/null +++ b/target/linux/airoha/en7581/config-6.6 @@ -0,0 +1,616 @@ +CONFIG_64BIT=y +CONFIG_AIROHA_THERMAL=y +CONFIG_AIROHA_WATCHDOG=y +CONFIG_AMPERE_ERRATUM_AC03_CPU_38=y +CONFIG_ARCH_AIROHA=y +CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y +CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y +CONFIG_ARCH_DEFAULT_KEXEC_IMAGE_VERIFY_SIG=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_FORCE_MAX_ORDER=10 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_USES_HIGH_VMA_FLAGS=y +CONFIG_ARCH_USES_PG_ARCH_X=y +CONFIG_ARCH_WANTS_NO_INSTR=y +CONFIG_ARCH_WANTS_THP_SWAP=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_AMU_EXTN=y +CONFIG_ARM64_BTI=y +CONFIG_ARM64_E0PD=y +CONFIG_ARM64_EPAN=y +CONFIG_ARM64_ERRATUM_1024718=y +CONFIG_ARM64_ERRATUM_1165522=y +CONFIG_ARM64_ERRATUM_1286807=y +CONFIG_ARM64_ERRATUM_1319367=y +CONFIG_ARM64_ERRATUM_1463225=y +CONFIG_ARM64_ERRATUM_1508412=y +CONFIG_ARM64_ERRATUM_1530923=y +CONFIG_ARM64_ERRATUM_1542419=y +CONFIG_ARM64_ERRATUM_2051678=y +CONFIG_ARM64_ERRATUM_2054223=y +CONFIG_ARM64_ERRATUM_2067961=y +CONFIG_ARM64_ERRATUM_2077057=y +CONFIG_ARM64_ERRATUM_2441007=y +CONFIG_ARM64_ERRATUM_2441009=y +CONFIG_ARM64_ERRATUM_2457168=y +CONFIG_ARM64_ERRATUM_2658417=y +CONFIG_ARM64_ERRATUM_819472=y +CONFIG_ARM64_ERRATUM_824069=y +CONFIG_ARM64_ERRATUM_826319=y +CONFIG_ARM64_ERRATUM_827319=y +CONFIG_ARM64_ERRATUM_832075=y +CONFIG_ARM64_ERRATUM_843419=y +CONFIG_ARM64_ERRATUM_858921=y +CONFIG_ARM64_HW_AFDBM=y +CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y +CONFIG_ARM64_MTE=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_PTR_AUTH_KERNEL=y +CONFIG_ARM64_RAS_EXTN=y +CONFIG_ARM64_SME=y +CONFIG_ARM64_SVE=y +# CONFIG_ARM64_SW_TTBR0_PAN is not set +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_TLB_RANGE=y +CONFIG_ARM64_VA_BITS=39 +CONFIG_ARM64_VA_BITS_39=y +CONFIG_ARM64_WORKAROUND_CLEAN_CACHE=y +CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y +CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y +CONFIG_ARM64_WORKAROUND_TSB_FLUSH_FAILURE=y +CONFIG_ARM_AIROHA_SOC_CPUFREQ=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V2M=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V3_ITS_PCI=y +CONFIG_ARM_PMU=y +CONFIG_ARM_PMUV3=y +CONFIG_ARM_PSCI_FW=y +CONFIG_ARM_SMCCC_SOC_ID=y +# CONFIG_ARM_SMMU is not set +# CONFIG_ARM_SMMU_V3 is not set +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BINFMT_MISC=y +# CONFIG_BLK_CGROUP is not set +CONFIG_BLK_DEBUG_FS=y +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BLOCK_LEGACY_AUTOLOAD=y +# CONFIG_BPF_JIT is not set +# CONFIG_BPF_SYSCALL is not set +# CONFIG_BRIDGE_VLAN_FILTERING is not set +CONFIG_BUFFER_HEAD=y +CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC=y +CONFIG_CAVIUM_ERRATUM_22375=y +CONFIG_CAVIUM_ERRATUM_23154=y +CONFIG_CAVIUM_ERRATUM_27456=y +CONFIG_CAVIUM_ERRATUM_30115=y +CONFIG_CAVIUM_TX2_ERRATUM_219=y +CONFIG_CC_HAVE_SHADOW_CALL_STACK=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +# CONFIG_CFS_BANDWIDTH is not set +CONFIG_CGROUPS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_NET_CLASSID is not set +# CONFIG_CGROUP_NET_PRIO is not set +# CONFIG_CGROUP_PERF is not set +# CONFIG_CGROUP_PIDS is not set +# CONFIG_CGROUP_RDMA is not set +CONFIG_CGROUP_SCHED=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_EN7523=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_COREDUMP=y +CONFIG_CPUSETS=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_ISOLATION=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MITIGATIONS=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CRC16=y +CONFIG_CRC_CCITT=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_CRYPTO_AUTHENC=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_DEV_EIP93=y +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_DRBG_HMAC=y +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_GENIV=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_DES=y +CONFIG_CRYPTO_LIB_GF128MUL=y +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=y +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_SEQIV=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA3=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set +CONFIG_DEBUG_INFO_NONE=y +CONFIG_DEBUG_MISC=y +CONFIG_DEVMEM=y +CONFIG_DMADEVICES=y +CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_OPS=y +CONFIG_DTC=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAT_DEFAULT_CODEPAGE=936 +CONFIG_FAT_DEFAULT_IOCHARSET="utf8" +CONFIG_FAT_FS=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +# CONFIG_FORTIFY_SOURCE is not set +CONFIG_FRAME_POINTER=y +CONFIG_FREEZER=y +CONFIG_FSL_ERRATUM_A008585=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FS_POSIX_ACL=y +CONFIG_FUJITSU_ERRATUM_010001=y +CONFIG_FUNCTION_ALIGNMENT=4 +CONFIG_FUNCTION_ALIGNMENT_4B=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_CACHE=y +# CONFIG_FW_LOADER_USER_HELPER is not set +CONFIG_GCC10_NO_ARRAY_BOUNDS=y +CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IOREMAP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_EN7523=y +CONFIG_GPIO_GENERIC=y +CONFIG_GRO_CELLS=y +# CONFIG_HARDENED_USERCOPY is not set +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HISILICON_ERRATUM_161010101=y +CONFIG_HISILICON_ERRATUM_161600802=y +CONFIG_HOTPLUG_CORE_SYNC=y +CONFIG_HOTPLUG_CORE_SYNC_DEAD=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_AIROHA=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +# CONFIG_INET_ESP_OFFLOAD is not set +CONFIG_INET_IPCOMP=y +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INITRAMFS_PRESERVE_MTIME=y +CONFIG_INPUT=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYBOARD=y +# CONFIG_INPUT_MISC is not set +CONFIG_INTERVAL_TREE=y +CONFIG_INTERVAL_TREE_SPAN_ITER=y +CONFIG_IOMMUFD=y +CONFIG_IOMMU_API=y +# CONFIG_IOMMU_DEBUGFS is not set +# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set +CONFIG_IOMMU_DEFAULT_DMA_STRICT=y +# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set +CONFIG_IOMMU_DMA=y +CONFIG_IOMMU_IOVA=y +# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set +# CONFIG_IOMMU_IO_PGTABLE_DART is not set +# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set +CONFIG_IOMMU_SUPPORT=y +CONFIG_IO_URING=y +CONFIG_IPC_NS=y +CONFIG_IPV6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_COMMON=y +# CONFIG_IP_MROUTE_MULTIPLE_TABLES is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MSI_IOMMU=y +CONFIG_IRQ_WORK=y +# CONFIG_ISDN is not set +CONFIG_JBD2=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_SUMMARY is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_KALLSYMS=y +CONFIG_LEGACY_DIRECT_IO=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=8 +CONFIG_LIBCRC32C=y +CONFIG_LIBFDT=y +CONFIG_LOCALVERSION_AUTO=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_LRU_GEN is not set +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +# CONFIG_MEDIATEK_GE_SOC_PHY is not set +# CONFIG_MEMCG is not set +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMU_LAZY_TLB_REFCOUNT=y +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MQ_IOSCHED_DEADLINE=y +CONFIG_MQ_IOSCHED_KYBER=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +# CONFIG_MTD_CFI_AMDSTD is not set +CONFIG_MTD_CFI_GEOMETRY=y +# CONFIG_MTD_CFI_INTELEXT is not set +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_MTK_BMT=y +CONFIG_MTD_OF_PARTS_AIROHA=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_FIRMWARE_NAME="tclinux" +CONFIG_MTD_SPLIT_FIT_FW=y +CONFIG_MTD_SPLIT_LZMA_FW=y +# CONFIG_MTD_SPLIT_SQUASHFS_ROOT is not set +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NAMESPACES=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_FLAGS=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NET_AIROHA=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_MT7530=y +CONFIG_NET_DSA_MT7530_MDIO=y +CONFIG_NET_DSA_MT7530_MMIO=y +CONFIG_NET_DSA_TAG_MTK=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_KEY=y +CONFIG_NET_KEY_MIGRATE=y +# CONFIG_NET_MEDIATEK_SOC is not set +CONFIG_NET_NS=y +# CONFIG_NET_SCHED is not set +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +# CONFIG_NET_VENDOR_3COM is not set +CONFIG_NET_VENDOR_MEDIATEK=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_NTFS_DEBUG=y +CONFIG_NTFS_FS=y +CONFIG_NTFS_RW=y +CONFIG_NVIDIA_CARMEL_CNP_ERRATUM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IOMMU=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +# CONFIG_OVERLAY_FS_XINO_AUTO is not set +CONFIG_PAGE_POOL=y +CONFIG_PAGE_SIZE_LESS_THAN_256KB=y +CONFIG_PAGE_SIZE_LESS_THAN_64KB=y +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +CONFIG_PARTITION_PERCPU=y +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEASPM=y +# CONFIG_PCIEASPM_DEFAULT is not set +CONFIG_PCIEASPM_PERFORMANCE=y +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCIEPORTBUS=y +# CONFIG_PCIE_MEDIATEK is not set +CONFIG_PCIE_PME=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCPU_DEV_REFCNT=y +CONFIG_PCS_MTK_LYNXI=y +CONFIG_PERF_EVENTS=y +CONFIG_PER_VMA_LOCK=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYLIB=y +CONFIG_PHYLIB_LEDS=y +CONFIG_PHYLINK=y +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PHY_AIROHA_PCIE=y +CONFIG_PID_NS=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_AIROHA=y +# CONFIG_PINCTRL_MT2712 is not set +# CONFIG_PINCTRL_MT6765 is not set +# CONFIG_PINCTRL_MT6795 is not set +# CONFIG_PINCTRL_MT6797 is not set +# CONFIG_PINCTRL_MT7622 is not set +# CONFIG_PINCTRL_MT7981 is not set +# CONFIG_PINCTRL_MT7986 is not set +# CONFIG_PINCTRL_MT8173 is not set +# CONFIG_PINCTRL_MT8183 is not set +# CONFIG_PINCTRL_MT8186 is not set +# CONFIG_PINCTRL_MT8188 is not set +# CONFIG_PINCTRL_MT8516 is not set +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_OPP=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QCOM_FALKOR_ERRATUM_1003=y +CONFIG_QCOM_FALKOR_ERRATUM_1009=y +CONFIG_QCOM_FALKOR_ERRATUM_E1041=y +CONFIG_QCOM_QDF2400_ERRATUM_0065=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RELOCATABLE=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPS=y +CONFIG_RSEQ=y +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_RWSEM_SPIN_ON_OWNER=y +# CONFIG_SCHED_CORE is not set +CONFIG_SCHED_DEBUG=y +CONFIG_SCHED_MM_CID=y +CONFIG_SCHED_SMT=y +# CONFIG_SCHED_STACK_END_CHECK is not set +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY_NETWORK is not set +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_NR_UARTS=5 +CONFIG_SERIAL_8250_RUNTIME_UARTS=5 +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIO=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SGETMASK_SYSCALL=y +CONFIG_SGL_ALLOC=y +CONFIG_SKB_EXTENSIONS=y +# CONFIG_SLAB_FREELIST_HARDENED is not set +# CONFIG_SLAB_FREELIST_RANDOM is not set +CONFIG_SLUB_DEBUG=y +CONFIG_SMP=y +CONFIG_SOCIONEXT_SYNQUACER_PREITS=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOC_BUS=y +CONFIG_SOFTIRQ_ON_OWN_STACK=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +# CONFIG_SPI_AIROHA_EN7523 is not set +CONFIG_SPI_AIROHA_SNFI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FILE_CACHE=y +# CONFIG_SQUASHFS_FILE_DIRECT is not set +CONFIG_SQUASHFS_ZLIB=y +CONFIG_STACKDEPOT=y +CONFIG_STACKPROTECTOR=y +CONFIG_STACKPROTECTOR_PER_TASK=y +CONFIG_STACKPROTECTOR_STRONG=y +CONFIG_STACKTRACE=y +# CONFIG_STAGING is not set +# CONFIG_STRIP_ASM_SYMS is not set +CONFIG_SURFACE_PLATFORMS=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +# CONFIG_SWAP is not set +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSFS_SYSCALL=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_MD5SIG=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_OF=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TIME_NS=y +# CONFIG_TMPFS_XATTR is not set +CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_UNMAP_KERNEL_AT_EL0=y +CONFIG_USELIB=y +CONFIG_USER_NS=y +CONFIG_UTS_NS=y +CONFIG_VFAT_FS=y +CONFIG_VMAP_STACK=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WLAN is not set +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_XFRM_AH=y +CONFIG_XFRM_ALGO=y +CONFIG_XFRM_ESP=y +CONFIG_XFRM_IPCOMP=y +CONFIG_XFRM_MIGRATE=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_X86=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA32=y +CONFIG_ZSTD_COMMON=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/airoha/en7581/target.mk b/target/linux/airoha/en7581/target.mk new file mode 100644 index 0000000000..d890d9ef58 --- /dev/null +++ b/target/linux/airoha/en7581/target.mk @@ -0,0 +1,11 @@ +ARCH:=aarch64 +SUBTARGET:=en7581 +BOARDNAME:=EN7581 +CPU_TYPE:=cortex-a53 +KERNELNAME:=Image dtbs +FEATURES+=pwm source-only + +define Target/Description + Build firmware images for Airoha en7581 ARM based boards. +endef + diff --git a/target/linux/airoha/image/en7581.mk b/target/linux/airoha/image/en7581.mk new file mode 100644 index 0000000000..7fc6b8ec0e --- /dev/null +++ b/target/linux/airoha/image/en7581.mk @@ -0,0 +1,27 @@ +define Device/FitImageLzma + KERNEL_SUFFIX := -uImage.itb + KERNEL = kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(DEVICE_DTS).dtb + KERNEL_NAME := Image +endef + +define Device/airoha_en7581-evb + $(call Device/FitImageLzma) + DEVICE_VENDOR := Airoha + DEVICE_MODEL := EN7581 Evaluation Board (SNAND) + DEVICE_PACKAGES := kmod-leds-pwm kmod-i2c-en7581 kmod-pwm-airoha kmod-input-gpio-keys-polled + DEVICE_DTS := en7581-evb + DEVICE_DTS_DIR := ../dts + DEVICE_DTS_CONFIG := config@1 + KERNEL_LOADADDR := 0x80088000 + IMAGE/sysupgrade.bin := append-kernel | pad-to 128k | append-rootfs | pad-rootfs | append-metadata +endef +TARGET_DEVICES += airoha_en7581-evb + +define Device/airoha_en7581-evb-emmc + DEVICE_VENDOR := Airoha + DEVICE_MODEL := EN7581 Evaluation Board (EMMC) + DEVICE_DTS := en7581-evb-emmc + DEVICE_DTS_DIR := ../dts + DEVICE_PACKAGES := kmod-i2c-en7581 +endef +TARGET_DEVICES += airoha_en7581-evb-emmc diff --git a/target/linux/airoha/modules.mk b/target/linux/airoha/modules.mk new file mode 100644 index 0000000000..38126d5bc4 --- /dev/null +++ b/target/linux/airoha/modules.mk @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0-only + +OTHER_MENU:=Other modules + +I2C_MT7621_MODULES:= \ + CONFIG_I2C_MT7621:drivers/i2c/busses/i2c-mt7621 + +define KernelPackage/i2c-en7581 + SUBMENU:=$(OTHER_MENU) + $(call i2c_defaults,$(I2C_MT7621_MODULES),79) + TITLE:=Airoha I2C Controller + DEPENDS:=+kmod-i2c-core \ + @(TARGET_airoha_en7581) +endef + +define KernelPackage/i2c-en7581/description + Kernel modules for enable mt7621 i2c controller. +endef + +$(eval $(call KernelPackage,i2c-en7581)) + + +define KernelPackage/pwm-en7581 + SUBMENU:=$(OTHER_MENU) + TITLE:=Airoha EN7581 PWM + DEPENDS:=@(TARGET_airoha_en7581) + KCONFIG:= \ + CONFIG_PWM=y \ + CONFIG_PWM_AIROHA=y \ + CONFIG_PWM_SYSFS=y + FILES:= \ + $(LINUX_DIR)/drivers/pwm/pwm-airoha.ko + AUTOLOAD:=$(call AutoProbe,pwm-airoha) +endef + +define KernelPackage/pwm-en7581/description + Kernel module to use the PWM channel on Airoha SoC +endef + +$(eval $(call KernelPackage,pwm-en7581)) + + diff --git a/target/linux/airoha/patches-6.6/001-v6.10-arm64-add-Airoha-EN7581-platform.patch b/target/linux/airoha/patches-6.6/001-v6.10-arm64-add-Airoha-EN7581-platform.patch new file mode 100644 index 0000000000..a77ed8c778 --- /dev/null +++ b/target/linux/airoha/patches-6.6/001-v6.10-arm64-add-Airoha-EN7581-platform.patch @@ -0,0 +1,34 @@ +From 428ae88ef519f2009fac37563de76ffa6f93046f Mon Sep 17 00:00:00 2001 +From: Daniel Danzberger +Date: Sat, 9 Mar 2024 10:32:16 +0100 +Subject: [PATCH] arm64: add Airoha EN7581 platform + +Introduce the Kconfig entry for the Airoha EN7581 multicore architecture +available in the Airoha EN7581 evaluation board. + +Signed-off-by: Daniel Danzberger +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/d52d95db313e6a58ba997ba2181faf78a1014bcc.1709975956.git.lorenzo@kernel.org +Signed-off-by: AngeloGioacchino Del Regno +Signed-off-by: Arnd Bergmann +--- + arch/arm64/Kconfig.platforms | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/arch/arm64/Kconfig.platforms ++++ b/arch/arm64/Kconfig.platforms +@@ -8,6 +8,13 @@ config ARCH_ACTIONS + help + This enables support for the Actions Semiconductor S900 SoC family. + ++config ARCH_AIROHA ++ bool "Airoha SoC Support" ++ select ARM_PSCI ++ select HAVE_ARM_ARCH_TIMER ++ help ++ This enables support for the ARM64 based Airoha SoCs. ++ + config ARCH_SUNXI + bool "Allwinner sunxi 64-bit SoC Family" + select ARCH_HAS_RESET_CONTROLLER diff --git a/target/linux/airoha/patches-6.6/002-v6.11-i2c-mt7621-Add-Airoha-EN7581-i2c-support.patch b/target/linux/airoha/patches-6.6/002-v6.11-i2c-mt7621-Add-Airoha-EN7581-i2c-support.patch new file mode 100644 index 0000000000..25eb475e21 --- /dev/null +++ b/target/linux/airoha/patches-6.6/002-v6.11-i2c-mt7621-Add-Airoha-EN7581-i2c-support.patch @@ -0,0 +1,27 @@ +From fd6acb0d21b8683fd8804129beeb4fe629488aff Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 9 Jul 2024 00:42:38 +0200 +Subject: [PATCH] i2c: mt7621: Add Airoha EN7581 i2c support + +Introduce i2c support to Airoha EN7581 SoC through the i2c-mt7621 +driver. + +Reviewed-by: AngeloGioacchino Del Regno +Tested-by: Ray Liu +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Andi Shyti +--- + drivers/i2c/busses/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -839,7 +839,7 @@ config I2C_MT65XX + + config I2C_MT7621 + tristate "MT7621/MT7628 I2C Controller" +- depends on (RALINK && (SOC_MT7620 || SOC_MT7621)) || COMPILE_TEST ++ depends on (RALINK && (SOC_MT7620 || SOC_MT7621)) || ARCH_AIROHA || COMPILE_TEST + help + Say Y here to include support for I2C controller in the + MediaTek MT7621/MT7628 SoCs. diff --git a/target/linux/airoha/patches-6.6/006-v6.11-net-airoha-Introduce-ethernet-support-for-EN7581-SoC.patch b/target/linux/airoha/patches-6.6/006-v6.11-net-airoha-Introduce-ethernet-support-for-EN7581-SoC.patch new file mode 100644 index 0000000000..253b6fd7ab --- /dev/null +++ b/target/linux/airoha/patches-6.6/006-v6.11-net-airoha-Introduce-ethernet-support-for-EN7581-SoC.patch @@ -0,0 +1,2835 @@ +From 23020f04932701d5c8363e60756f12b43b8ed752 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 12 Jul 2024 23:27:58 +0200 +Subject: [PATCH] net: airoha: Introduce ethernet support for EN7581 SoC +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add airoha_eth driver in order to introduce ethernet support for +Airoha EN7581 SoC available on EN7581 development board (en7581-evb). +EN7581 mac controller is mainly composed by the Frame Engine (PSE+PPE) +and QoS-DMA (QDMA) modules. FE is used for traffic offloading (just +basic functionalities are currently supported) while QDMA is used for +DMA operations and QOS functionalities between the mac layer and the +external modules conncted to the FE GDM ports (e.g MT7530 DSA switch +or external phys). +A general overview of airoha_eth architecture is reported below: + + ┌───────┐ ┌───────┐ + │ QDMA2 │ │ QDMA1 │ + └───┬───┘ └───┬───┘ + │ │ + ┌───────▼─────────────────────────────────────────────▼────────┐ + │ │ + │ P5 P0 │ + │ │ + │ │ + │ │ ┌──────┐ + │ P3 ├────► GDM3 │ + │ │ └──────┘ + │ │ + │ │ +┌─────┐ │ │ +│ PPE ◄────┤ P4 PSE │ +└─────┘ │ │ + │ │ + │ │ + │ │ ┌──────┐ + │ P9 ├────► GDM4 │ + │ │ └──────┘ + │ │ + │ │ + │ │ + │ P2 P1 │ + └─────────┬───────────────────────────────────────────┬────────┘ + │ │ + ┌───▼──┐ ┌──▼───┐ + │ GDM2 │ │ GDM1 │ + └──────┘ └──┬───┘ + │ + ┌────▼─────┐ + │ MT7530 │ + └──────────┘ + +Currently only hw LAN features (QDMA1+GDM1) are available while hw WAN +(QDMA2+GDM{2,3,4}) ones will be added with subsequent patches introducing +traffic offloading support. + +Tested-by: Benjamin Larsson +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/274945d2391c195098ab180a46d0617b18b9e42c.1720818878.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + MAINTAINERS | 9 + + drivers/net/ethernet/mediatek/Kconfig | 10 +- + drivers/net/ethernet/mediatek/Makefile | 1 + + drivers/net/ethernet/mediatek/airoha_eth.c | 2730 ++++++++++++++++++++ + 4 files changed, 2749 insertions(+), 1 deletion(-) + create mode 100644 drivers/net/ethernet/mediatek/airoha_eth.c + +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -1,12 +1,20 @@ + # SPDX-License-Identifier: GPL-2.0-only + config NET_VENDOR_MEDIATEK + bool "MediaTek devices" +- depends on ARCH_MEDIATEK || SOC_MT7621 || SOC_MT7620 || COMPILE_TEST ++ depends on ARCH_MEDIATEK || ARCH_AIROHA || SOC_MT7621 || SOC_MT7620 || COMPILE_TEST + help + If you have a Mediatek SoC with ethernet, say Y. + + if NET_VENDOR_MEDIATEK + ++config NET_AIROHA ++ tristate "Airoha SoC Gigabit Ethernet support" ++ depends on NET_DSA || !NET_DSA ++ select PAGE_POOL ++ help ++ This driver supports the gigabit ethernet MACs in the ++ Airoha SoC family. ++ + config NET_MEDIATEK_SOC_WED + depends on ARCH_MEDIATEK || COMPILE_TEST + def_bool NET_MEDIATEK_SOC != n +--- a/drivers/net/ethernet/mediatek/Makefile ++++ b/drivers/net/ethernet/mediatek/Makefile +@@ -11,3 +11,4 @@ mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) + + endif + obj-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_ops.o + obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o ++obj-$(CONFIG_NET_AIROHA) += airoha_eth.o +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -0,0 +1,2731 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AIROHA_MAX_NUM_GDM_PORTS 1 ++#define AIROHA_MAX_NUM_RSTS 3 ++#define AIROHA_MAX_NUM_XSI_RSTS 5 ++#define AIROHA_MAX_MTU 2000 ++#define AIROHA_MAX_PACKET_SIZE 2048 ++#define AIROHA_NUM_TX_RING 32 ++#define AIROHA_NUM_RX_RING 32 ++#define AIROHA_FE_MC_MAX_VLAN_TABLE 64 ++#define AIROHA_FE_MC_MAX_VLAN_PORT 16 ++#define AIROHA_NUM_TX_IRQ 2 ++#define HW_DSCP_NUM 2048 ++#define IRQ_QUEUE_LEN(_n) ((_n) ? 1024 : 2048) ++#define TX_DSCP_NUM 1024 ++#define RX_DSCP_NUM(_n) \ ++ ((_n) == 2 ? 128 : \ ++ (_n) == 11 ? 128 : \ ++ (_n) == 15 ? 128 : \ ++ (_n) == 0 ? 1024 : 16) ++ ++#define PSE_RSV_PAGES 128 ++#define PSE_QUEUE_RSV_PAGES 64 ++ ++/* FE */ ++#define PSE_BASE 0x0100 ++#define CSR_IFC_BASE 0x0200 ++#define CDM1_BASE 0x0400 ++#define GDM1_BASE 0x0500 ++#define PPE1_BASE 0x0c00 ++ ++#define CDM2_BASE 0x1400 ++#define GDM2_BASE 0x1500 ++ ++#define GDM3_BASE 0x1100 ++#define GDM4_BASE 0x2500 ++ ++#define GDM_BASE(_n) \ ++ ((_n) == 4 ? GDM4_BASE : \ ++ (_n) == 3 ? GDM3_BASE : \ ++ (_n) == 2 ? GDM2_BASE : GDM1_BASE) ++ ++#define REG_FE_DMA_GLO_CFG 0x0000 ++#define FE_DMA_GLO_L2_SPACE_MASK GENMASK(7, 4) ++#define FE_DMA_GLO_PG_SZ_MASK BIT(3) ++ ++#define REG_FE_RST_GLO_CFG 0x0004 ++#define FE_RST_GDM4_MBI_ARB_MASK BIT(3) ++#define FE_RST_GDM3_MBI_ARB_MASK BIT(2) ++#define FE_RST_CORE_MASK BIT(0) ++ ++#define REG_FE_LAN_MAC_H 0x0040 ++#define REG_FE_LAN_MAC_LMIN 0x0044 ++#define REG_FE_LAN_MAC_LMAX 0x0048 ++ ++#define REG_FE_CDM1_OQ_MAP0 0x0050 ++#define REG_FE_CDM1_OQ_MAP1 0x0054 ++#define REG_FE_CDM1_OQ_MAP2 0x0058 ++#define REG_FE_CDM1_OQ_MAP3 0x005c ++ ++#define REG_FE_PCE_CFG 0x0070 ++#define PCE_DPI_EN_MASK BIT(2) ++#define PCE_KA_EN_MASK BIT(1) ++#define PCE_MC_EN_MASK BIT(0) ++ ++#define REG_FE_PSE_QUEUE_CFG_WR 0x0080 ++#define PSE_CFG_PORT_ID_MASK GENMASK(27, 24) ++#define PSE_CFG_QUEUE_ID_MASK GENMASK(20, 16) ++#define PSE_CFG_WR_EN_MASK BIT(8) ++#define PSE_CFG_OQRSV_SEL_MASK BIT(0) ++ ++#define REG_FE_PSE_QUEUE_CFG_VAL 0x0084 ++#define PSE_CFG_OQ_RSV_MASK GENMASK(13, 0) ++ ++#define PSE_FQ_CFG 0x008c ++#define PSE_FQ_LIMIT_MASK GENMASK(14, 0) ++ ++#define REG_FE_PSE_BUF_SET 0x0090 ++#define PSE_SHARE_USED_LTHD_MASK GENMASK(31, 16) ++#define PSE_ALLRSV_MASK GENMASK(14, 0) ++ ++#define REG_PSE_SHARE_USED_THD 0x0094 ++#define PSE_SHARE_USED_MTHD_MASK GENMASK(31, 16) ++#define PSE_SHARE_USED_HTHD_MASK GENMASK(15, 0) ++ ++#define REG_GDM_MISC_CFG 0x0148 ++#define GDM2_RDM_ACK_WAIT_PREF_MASK BIT(9) ++#define GDM2_CHN_VLD_MODE_MASK BIT(5) ++ ++#define REG_FE_CSR_IFC_CFG CSR_IFC_BASE ++#define FE_IFC_EN_MASK BIT(0) ++ ++#define REG_FE_VIP_PORT_EN 0x01f0 ++#define REG_FE_IFC_PORT_EN 0x01f4 ++ ++#define REG_PSE_IQ_REV1 (PSE_BASE + 0x08) ++#define PSE_IQ_RES1_P2_MASK GENMASK(23, 16) ++ ++#define REG_PSE_IQ_REV2 (PSE_BASE + 0x0c) ++#define PSE_IQ_RES2_P5_MASK GENMASK(15, 8) ++#define PSE_IQ_RES2_P4_MASK GENMASK(7, 0) ++ ++#define REG_FE_VIP_EN(_n) (0x0300 + ((_n) << 3)) ++#define PATN_FCPU_EN_MASK BIT(7) ++#define PATN_SWP_EN_MASK BIT(6) ++#define PATN_DP_EN_MASK BIT(5) ++#define PATN_SP_EN_MASK BIT(4) ++#define PATN_TYPE_MASK GENMASK(3, 1) ++#define PATN_EN_MASK BIT(0) ++ ++#define REG_FE_VIP_PATN(_n) (0x0304 + ((_n) << 3)) ++#define PATN_DP_MASK GENMASK(31, 16) ++#define PATN_SP_MASK GENMASK(15, 0) ++ ++#define REG_CDM1_VLAN_CTRL CDM1_BASE ++#define CDM1_VLAN_MASK GENMASK(31, 16) ++ ++#define REG_CDM1_FWD_CFG (CDM1_BASE + 0x08) ++#define CDM1_VIP_QSEL_MASK GENMASK(24, 20) ++ ++#define REG_CDM1_CRSN_QSEL(_n) (CDM1_BASE + 0x10 + ((_n) << 2)) ++#define CDM1_CRSN_QSEL_REASON_MASK(_n) \ ++ GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) ++ ++#define REG_CDM2_FWD_CFG (CDM2_BASE + 0x08) ++#define CDM2_OAM_QSEL_MASK GENMASK(31, 27) ++#define CDM2_VIP_QSEL_MASK GENMASK(24, 20) ++ ++#define REG_CDM2_CRSN_QSEL(_n) (CDM2_BASE + 0x10 + ((_n) << 2)) ++#define CDM2_CRSN_QSEL_REASON_MASK(_n) \ ++ GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) ++ ++#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n) ++#define GDM_DROP_CRC_ERR BIT(23) ++#define GDM_IP4_CKSUM BIT(22) ++#define GDM_TCP_CKSUM BIT(21) ++#define GDM_UDP_CKSUM BIT(20) ++#define GDM_UCFQ_MASK GENMASK(15, 12) ++#define GDM_BCFQ_MASK GENMASK(11, 8) ++#define GDM_MCFQ_MASK GENMASK(7, 4) ++#define GDM_OCFQ_MASK GENMASK(3, 0) ++ ++#define REG_GDM_INGRESS_CFG(_n) (GDM_BASE(_n) + 0x10) ++#define GDM_INGRESS_FC_EN_MASK BIT(1) ++#define GDM_STAG_EN_MASK BIT(0) ++ ++#define REG_GDM_LEN_CFG(_n) (GDM_BASE(_n) + 0x14) ++#define GDM_SHORT_LEN_MASK GENMASK(13, 0) ++#define GDM_LONG_LEN_MASK GENMASK(29, 16) ++ ++#define REG_FE_CPORT_CFG (GDM1_BASE + 0x40) ++#define FE_CPORT_PAD BIT(26) ++#define FE_CPORT_PORT_XFC_MASK BIT(25) ++#define FE_CPORT_QUEUE_XFC_MASK BIT(24) ++ ++#define REG_FE_GDM_MIB_CLEAR(_n) (GDM_BASE(_n) + 0xf0) ++#define FE_GDM_MIB_RX_CLEAR_MASK BIT(1) ++#define FE_GDM_MIB_TX_CLEAR_MASK BIT(0) ++ ++#define REG_FE_GDM1_MIB_CFG (GDM1_BASE + 0xf4) ++#define FE_STRICT_RFC2819_MODE_MASK BIT(31) ++#define FE_GDM1_TX_MIB_SPLIT_EN_MASK BIT(17) ++#define FE_GDM1_RX_MIB_SPLIT_EN_MASK BIT(16) ++#define FE_TX_MIB_ID_MASK GENMASK(15, 8) ++#define FE_RX_MIB_ID_MASK GENMASK(7, 0) ++ ++#define REG_FE_GDM_TX_OK_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x104) ++#define REG_FE_GDM_TX_OK_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x10c) ++#define REG_FE_GDM_TX_ETH_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x110) ++#define REG_FE_GDM_TX_ETH_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x114) ++#define REG_FE_GDM_TX_ETH_DROP_CNT(_n) (GDM_BASE(_n) + 0x118) ++#define REG_FE_GDM_TX_ETH_BC_CNT(_n) (GDM_BASE(_n) + 0x11c) ++#define REG_FE_GDM_TX_ETH_MC_CNT(_n) (GDM_BASE(_n) + 0x120) ++#define REG_FE_GDM_TX_ETH_RUNT_CNT(_n) (GDM_BASE(_n) + 0x124) ++#define REG_FE_GDM_TX_ETH_LONG_CNT(_n) (GDM_BASE(_n) + 0x128) ++#define REG_FE_GDM_TX_ETH_E64_CNT_L(_n) (GDM_BASE(_n) + 0x12c) ++#define REG_FE_GDM_TX_ETH_L64_CNT_L(_n) (GDM_BASE(_n) + 0x130) ++#define REG_FE_GDM_TX_ETH_L127_CNT_L(_n) (GDM_BASE(_n) + 0x134) ++#define REG_FE_GDM_TX_ETH_L255_CNT_L(_n) (GDM_BASE(_n) + 0x138) ++#define REG_FE_GDM_TX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x13c) ++#define REG_FE_GDM_TX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x140) ++ ++#define REG_FE_GDM_RX_OK_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x148) ++#define REG_FE_GDM_RX_FC_DROP_CNT(_n) (GDM_BASE(_n) + 0x14c) ++#define REG_FE_GDM_RX_RC_DROP_CNT(_n) (GDM_BASE(_n) + 0x150) ++#define REG_FE_GDM_RX_OVERFLOW_DROP_CNT(_n) (GDM_BASE(_n) + 0x154) ++#define REG_FE_GDM_RX_ERROR_DROP_CNT(_n) (GDM_BASE(_n) + 0x158) ++#define REG_FE_GDM_RX_OK_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x15c) ++#define REG_FE_GDM_RX_ETH_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x160) ++#define REG_FE_GDM_RX_ETH_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x164) ++#define REG_FE_GDM_RX_ETH_DROP_CNT(_n) (GDM_BASE(_n) + 0x168) ++#define REG_FE_GDM_RX_ETH_BC_CNT(_n) (GDM_BASE(_n) + 0x16c) ++#define REG_FE_GDM_RX_ETH_MC_CNT(_n) (GDM_BASE(_n) + 0x170) ++#define REG_FE_GDM_RX_ETH_CRC_ERR_CNT(_n) (GDM_BASE(_n) + 0x174) ++#define REG_FE_GDM_RX_ETH_FRAG_CNT(_n) (GDM_BASE(_n) + 0x178) ++#define REG_FE_GDM_RX_ETH_JABBER_CNT(_n) (GDM_BASE(_n) + 0x17c) ++#define REG_FE_GDM_RX_ETH_RUNT_CNT(_n) (GDM_BASE(_n) + 0x180) ++#define REG_FE_GDM_RX_ETH_LONG_CNT(_n) (GDM_BASE(_n) + 0x184) ++#define REG_FE_GDM_RX_ETH_E64_CNT_L(_n) (GDM_BASE(_n) + 0x188) ++#define REG_FE_GDM_RX_ETH_L64_CNT_L(_n) (GDM_BASE(_n) + 0x18c) ++#define REG_FE_GDM_RX_ETH_L127_CNT_L(_n) (GDM_BASE(_n) + 0x190) ++#define REG_FE_GDM_RX_ETH_L255_CNT_L(_n) (GDM_BASE(_n) + 0x194) ++#define REG_FE_GDM_RX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x198) ++#define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x19c) ++ ++#define REG_PPE1_TB_HASH_CFG (PPE1_BASE + 0x250) ++#define PPE1_SRAM_TABLE_EN_MASK BIT(0) ++#define PPE1_SRAM_HASH1_EN_MASK BIT(8) ++#define PPE1_DRAM_TABLE_EN_MASK BIT(16) ++#define PPE1_DRAM_HASH1_EN_MASK BIT(24) ++ ++#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280) ++#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284) ++#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288) ++#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x28c) ++ ++#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x290) ++#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x294) ++#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x298) ++#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x29c) ++#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2b8) ++#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2bc) ++#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2c0) ++#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2c4) ++#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2c8) ++#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2cc) ++#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2e8) ++#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2ec) ++#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2f0) ++#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2f4) ++#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2f8) ++#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2fc) ++ ++#define REG_GDM2_CHN_RLS (GDM2_BASE + 0x20) ++#define MBI_RX_AGE_SEL_MASK GENMASK(18, 17) ++#define MBI_TX_AGE_SEL_MASK GENMASK(18, 17) ++ ++#define REG_GDM3_FWD_CFG GDM3_BASE ++#define GDM3_PAD_EN_MASK BIT(28) ++ ++#define REG_GDM4_FWD_CFG (GDM4_BASE + 0x100) ++#define GDM4_PAD_EN_MASK BIT(28) ++#define GDM4_SPORT_OFFSET0_MASK GENMASK(11, 8) ++ ++#define REG_GDM4_SRC_PORT_SET (GDM4_BASE + 0x33c) ++#define GDM4_SPORT_OFF2_MASK GENMASK(19, 16) ++#define GDM4_SPORT_OFF1_MASK GENMASK(15, 12) ++#define GDM4_SPORT_OFF0_MASK GENMASK(11, 8) ++ ++#define REG_IP_FRAG_FP 0x2010 ++#define IP_ASSEMBLE_PORT_MASK GENMASK(24, 21) ++#define IP_ASSEMBLE_NBQ_MASK GENMASK(20, 16) ++#define IP_FRAGMENT_PORT_MASK GENMASK(8, 5) ++#define IP_FRAGMENT_NBQ_MASK GENMASK(4, 0) ++ ++#define REG_MC_VLAN_EN 0x2100 ++#define MC_VLAN_EN_MASK BIT(0) ++ ++#define REG_MC_VLAN_CFG 0x2104 ++#define MC_VLAN_CFG_CMD_DONE_MASK BIT(31) ++#define MC_VLAN_CFG_TABLE_ID_MASK GENMASK(21, 16) ++#define MC_VLAN_CFG_PORT_ID_MASK GENMASK(11, 8) ++#define MC_VLAN_CFG_TABLE_SEL_MASK BIT(4) ++#define MC_VLAN_CFG_RW_MASK BIT(0) ++ ++#define REG_MC_VLAN_DATA 0x2108 ++ ++#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 ++ ++/* QDMA */ ++#define REG_QDMA_GLOBAL_CFG 0x0004 ++#define GLOBAL_CFG_RX_2B_OFFSET_MASK BIT(31) ++#define GLOBAL_CFG_DMA_PREFERENCE_MASK GENMASK(30, 29) ++#define GLOBAL_CFG_CPU_TXR_RR_MASK BIT(28) ++#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK BIT(27) ++#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK BIT(26) ++#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK BIT(25) ++#define GLOBAL_CFG_OAM_MODIFY_MASK BIT(24) ++#define GLOBAL_CFG_RESET_MASK BIT(23) ++#define GLOBAL_CFG_RESET_DONE_MASK BIT(22) ++#define GLOBAL_CFG_MULTICAST_EN_MASK BIT(21) ++#define GLOBAL_CFG_IRQ1_EN_MASK BIT(20) ++#define GLOBAL_CFG_IRQ0_EN_MASK BIT(19) ++#define GLOBAL_CFG_LOOPCNT_EN_MASK BIT(18) ++#define GLOBAL_CFG_RD_BYPASS_WR_MASK BIT(17) ++#define GLOBAL_CFG_QDMA_LOOPBACK_MASK BIT(16) ++#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK GENMASK(13, 8) ++#define GLOBAL_CFG_CHECK_DONE_MASK BIT(7) ++#define GLOBAL_CFG_TX_WB_DONE_MASK BIT(6) ++#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK GENMASK(5, 4) ++#define GLOBAL_CFG_RX_DMA_BUSY_MASK BIT(3) ++#define GLOBAL_CFG_RX_DMA_EN_MASK BIT(2) ++#define GLOBAL_CFG_TX_DMA_BUSY_MASK BIT(1) ++#define GLOBAL_CFG_TX_DMA_EN_MASK BIT(0) ++ ++#define REG_FWD_DSCP_BASE 0x0010 ++#define REG_FWD_BUF_BASE 0x0014 ++ ++#define REG_HW_FWD_DSCP_CFG 0x0018 ++#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK GENMASK(29, 28) ++#define HW_FWD_DSCP_SCATTER_LEN_MASK GENMASK(17, 16) ++#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK GENMASK(15, 0) ++ ++#define REG_INT_STATUS(_n) \ ++ (((_n) == 4) ? 0x0730 : \ ++ ((_n) == 3) ? 0x0724 : \ ++ ((_n) == 2) ? 0x0720 : \ ++ ((_n) == 1) ? 0x0024 : 0x0020) ++ ++#define REG_INT_ENABLE(_n) \ ++ (((_n) == 4) ? 0x0750 : \ ++ ((_n) == 3) ? 0x0744 : \ ++ ((_n) == 2) ? 0x0740 : \ ++ ((_n) == 1) ? 0x002c : 0x0028) ++ ++/* QDMA_CSR_INT_ENABLE1 */ ++#define RX15_COHERENT_INT_MASK BIT(31) ++#define RX14_COHERENT_INT_MASK BIT(30) ++#define RX13_COHERENT_INT_MASK BIT(29) ++#define RX12_COHERENT_INT_MASK BIT(28) ++#define RX11_COHERENT_INT_MASK BIT(27) ++#define RX10_COHERENT_INT_MASK BIT(26) ++#define RX9_COHERENT_INT_MASK BIT(25) ++#define RX8_COHERENT_INT_MASK BIT(24) ++#define RX7_COHERENT_INT_MASK BIT(23) ++#define RX6_COHERENT_INT_MASK BIT(22) ++#define RX5_COHERENT_INT_MASK BIT(21) ++#define RX4_COHERENT_INT_MASK BIT(20) ++#define RX3_COHERENT_INT_MASK BIT(19) ++#define RX2_COHERENT_INT_MASK BIT(18) ++#define RX1_COHERENT_INT_MASK BIT(17) ++#define RX0_COHERENT_INT_MASK BIT(16) ++#define TX7_COHERENT_INT_MASK BIT(15) ++#define TX6_COHERENT_INT_MASK BIT(14) ++#define TX5_COHERENT_INT_MASK BIT(13) ++#define TX4_COHERENT_INT_MASK BIT(12) ++#define TX3_COHERENT_INT_MASK BIT(11) ++#define TX2_COHERENT_INT_MASK BIT(10) ++#define TX1_COHERENT_INT_MASK BIT(9) ++#define TX0_COHERENT_INT_MASK BIT(8) ++#define CNT_OVER_FLOW_INT_MASK BIT(7) ++#define IRQ1_FULL_INT_MASK BIT(5) ++#define IRQ1_INT_MASK BIT(4) ++#define HWFWD_DSCP_LOW_INT_MASK BIT(3) ++#define HWFWD_DSCP_EMPTY_INT_MASK BIT(2) ++#define IRQ0_FULL_INT_MASK BIT(1) ++#define IRQ0_INT_MASK BIT(0) ++ ++#define TX_DONE_INT_MASK(_n) \ ++ ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK \ ++ : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) ++ ++#define INT_TX_MASK \ ++ (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK | \ ++ IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) ++ ++#define INT_IDX0_MASK \ ++ (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK | \ ++ TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK | \ ++ TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK | \ ++ TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK | \ ++ RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK | \ ++ RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK | \ ++ RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK | \ ++ RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK | \ ++ RX15_COHERENT_INT_MASK | INT_TX_MASK) ++ ++/* QDMA_CSR_INT_ENABLE2 */ ++#define RX15_NO_CPU_DSCP_INT_MASK BIT(31) ++#define RX14_NO_CPU_DSCP_INT_MASK BIT(30) ++#define RX13_NO_CPU_DSCP_INT_MASK BIT(29) ++#define RX12_NO_CPU_DSCP_INT_MASK BIT(28) ++#define RX11_NO_CPU_DSCP_INT_MASK BIT(27) ++#define RX10_NO_CPU_DSCP_INT_MASK BIT(26) ++#define RX9_NO_CPU_DSCP_INT_MASK BIT(25) ++#define RX8_NO_CPU_DSCP_INT_MASK BIT(24) ++#define RX7_NO_CPU_DSCP_INT_MASK BIT(23) ++#define RX6_NO_CPU_DSCP_INT_MASK BIT(22) ++#define RX5_NO_CPU_DSCP_INT_MASK BIT(21) ++#define RX4_NO_CPU_DSCP_INT_MASK BIT(20) ++#define RX3_NO_CPU_DSCP_INT_MASK BIT(19) ++#define RX2_NO_CPU_DSCP_INT_MASK BIT(18) ++#define RX1_NO_CPU_DSCP_INT_MASK BIT(17) ++#define RX0_NO_CPU_DSCP_INT_MASK BIT(16) ++#define RX15_DONE_INT_MASK BIT(15) ++#define RX14_DONE_INT_MASK BIT(14) ++#define RX13_DONE_INT_MASK BIT(13) ++#define RX12_DONE_INT_MASK BIT(12) ++#define RX11_DONE_INT_MASK BIT(11) ++#define RX10_DONE_INT_MASK BIT(10) ++#define RX9_DONE_INT_MASK BIT(9) ++#define RX8_DONE_INT_MASK BIT(8) ++#define RX7_DONE_INT_MASK BIT(7) ++#define RX6_DONE_INT_MASK BIT(6) ++#define RX5_DONE_INT_MASK BIT(5) ++#define RX4_DONE_INT_MASK BIT(4) ++#define RX3_DONE_INT_MASK BIT(3) ++#define RX2_DONE_INT_MASK BIT(2) ++#define RX1_DONE_INT_MASK BIT(1) ++#define RX0_DONE_INT_MASK BIT(0) ++ ++#define RX_DONE_INT_MASK \ ++ (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK | \ ++ RX2_DONE_INT_MASK | RX3_DONE_INT_MASK | \ ++ RX4_DONE_INT_MASK | RX7_DONE_INT_MASK | \ ++ RX8_DONE_INT_MASK | RX9_DONE_INT_MASK | \ ++ RX15_DONE_INT_MASK) ++#define INT_IDX1_MASK \ ++ (RX_DONE_INT_MASK | \ ++ RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK | \ ++ RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK | \ ++ RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK | \ ++ RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK | \ ++ RX15_NO_CPU_DSCP_INT_MASK) ++ ++/* QDMA_CSR_INT_ENABLE5 */ ++#define TX31_COHERENT_INT_MASK BIT(31) ++#define TX30_COHERENT_INT_MASK BIT(30) ++#define TX29_COHERENT_INT_MASK BIT(29) ++#define TX28_COHERENT_INT_MASK BIT(28) ++#define TX27_COHERENT_INT_MASK BIT(27) ++#define TX26_COHERENT_INT_MASK BIT(26) ++#define TX25_COHERENT_INT_MASK BIT(25) ++#define TX24_COHERENT_INT_MASK BIT(24) ++#define TX23_COHERENT_INT_MASK BIT(23) ++#define TX22_COHERENT_INT_MASK BIT(22) ++#define TX21_COHERENT_INT_MASK BIT(21) ++#define TX20_COHERENT_INT_MASK BIT(20) ++#define TX19_COHERENT_INT_MASK BIT(19) ++#define TX18_COHERENT_INT_MASK BIT(18) ++#define TX17_COHERENT_INT_MASK BIT(17) ++#define TX16_COHERENT_INT_MASK BIT(16) ++#define TX15_COHERENT_INT_MASK BIT(15) ++#define TX14_COHERENT_INT_MASK BIT(14) ++#define TX13_COHERENT_INT_MASK BIT(13) ++#define TX12_COHERENT_INT_MASK BIT(12) ++#define TX11_COHERENT_INT_MASK BIT(11) ++#define TX10_COHERENT_INT_MASK BIT(10) ++#define TX9_COHERENT_INT_MASK BIT(9) ++#define TX8_COHERENT_INT_MASK BIT(8) ++ ++#define INT_IDX4_MASK \ ++ (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK | \ ++ TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK | \ ++ TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK | \ ++ TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK | \ ++ TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK | \ ++ TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK | \ ++ TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK | \ ++ TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK | \ ++ TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK | \ ++ TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK | \ ++ TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK | \ ++ TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK) ++ ++#define REG_TX_IRQ_BASE(_n) ((_n) ? 0x0048 : 0x0050) ++ ++#define REG_TX_IRQ_CFG(_n) ((_n) ? 0x004c : 0x0054) ++#define TX_IRQ_THR_MASK GENMASK(27, 16) ++#define TX_IRQ_DEPTH_MASK GENMASK(11, 0) ++ ++#define REG_IRQ_CLEAR_LEN(_n) ((_n) ? 0x0064 : 0x0058) ++#define IRQ_CLEAR_LEN_MASK GENMASK(7, 0) ++ ++#define REG_IRQ_STATUS(_n) ((_n) ? 0x0068 : 0x005c) ++#define IRQ_ENTRY_LEN_MASK GENMASK(27, 16) ++#define IRQ_HEAD_IDX_MASK GENMASK(11, 0) ++ ++#define REG_TX_RING_BASE(_n) \ ++ (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5)) ++ ++#define REG_TX_RING_BLOCKING(_n) \ ++ (((_n) < 8) ? 0x0104 + ((_n) << 5) : 0x0b04 + (((_n) - 8) << 5)) ++ ++#define TX_RING_IRQ_BLOCKING_MAP_MASK BIT(6) ++#define TX_RING_IRQ_BLOCKING_CFG_MASK BIT(4) ++#define TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK BIT(2) ++#define TX_RING_IRQ_BLOCKING_MAX_TH_TXRING_EN_MASK BIT(1) ++#define TX_RING_IRQ_BLOCKING_MIN_TH_TXRING_EN_MASK BIT(0) ++ ++#define REG_TX_CPU_IDX(_n) \ ++ (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5)) ++ ++#define TX_RING_CPU_IDX_MASK GENMASK(15, 0) ++ ++#define REG_TX_DMA_IDX(_n) \ ++ (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5)) ++ ++#define TX_RING_DMA_IDX_MASK GENMASK(15, 0) ++ ++#define IRQ_RING_IDX_MASK GENMASK(20, 16) ++#define IRQ_DESC_IDX_MASK GENMASK(15, 0) ++ ++#define REG_RX_RING_BASE(_n) \ ++ (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5)) ++ ++#define REG_RX_RING_SIZE(_n) \ ++ (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5)) ++ ++#define RX_RING_THR_MASK GENMASK(31, 16) ++#define RX_RING_SIZE_MASK GENMASK(15, 0) ++ ++#define REG_RX_CPU_IDX(_n) \ ++ (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5)) ++ ++#define RX_RING_CPU_IDX_MASK GENMASK(15, 0) ++ ++#define REG_RX_DMA_IDX(_n) \ ++ (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5)) ++ ++#define REG_RX_DELAY_INT_IDX(_n) \ ++ (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5)) ++ ++#define RX_DELAY_INT_MASK GENMASK(15, 0) ++ ++#define RX_RING_DMA_IDX_MASK GENMASK(15, 0) ++ ++#define REG_INGRESS_TRTCM_CFG 0x0070 ++#define INGRESS_TRTCM_EN_MASK BIT(31) ++#define INGRESS_TRTCM_MODE_MASK BIT(30) ++#define INGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define INGRESS_FAST_TICK_MASK GENMASK(15, 0) ++ ++#define REG_TXQ_DIS_CFG_BASE(_n) ((_n) ? 0x20a0 : 0x00a0) ++#define REG_TXQ_DIS_CFG(_n, _m) (REG_TXQ_DIS_CFG_BASE((_n)) + (_m) << 2) ++ ++#define REG_LMGR_INIT_CFG 0x1000 ++#define LMGR_INIT_START BIT(31) ++#define LMGR_SRAM_MODE_MASK BIT(30) ++#define HW_FWD_PKTSIZE_OVERHEAD_MASK GENMASK(27, 20) ++#define HW_FWD_DESC_NUM_MASK GENMASK(16, 0) ++ ++#define REG_FWD_DSCP_LOW_THR 0x1004 ++#define FWD_DSCP_LOW_THR_MASK GENMASK(17, 0) ++ ++#define REG_EGRESS_RATE_METER_CFG 0x100c ++#define EGRESS_RATE_METER_EN_MASK BIT(29) ++#define EGRESS_RATE_METER_EQ_RATE_EN_MASK BIT(17) ++#define EGRESS_RATE_METER_WINDOW_SZ_MASK GENMASK(16, 12) ++#define EGRESS_RATE_METER_TIMESLICE_MASK GENMASK(10, 0) ++ ++#define REG_EGRESS_TRTCM_CFG 0x1010 ++#define EGRESS_TRTCM_EN_MASK BIT(31) ++#define EGRESS_TRTCM_MODE_MASK BIT(30) ++#define EGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define EGRESS_FAST_TICK_MASK GENMASK(15, 0) ++ ++#define REG_TXWRR_MODE_CFG 0x1020 ++#define TWRR_WEIGHT_SCALE_MASK BIT(31) ++#define TWRR_WEIGHT_BASE_MASK BIT(3) ++ ++#define REG_PSE_BUF_USAGE_CFG 0x1028 ++#define PSE_BUF_ESTIMATE_EN_MASK BIT(29) ++ ++#define REG_GLB_TRTCM_CFG 0x1080 ++#define GLB_TRTCM_EN_MASK BIT(31) ++#define GLB_TRTCM_MODE_MASK BIT(30) ++#define GLB_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define GLB_FAST_TICK_MASK GENMASK(15, 0) ++ ++#define REG_TXQ_CNGST_CFG 0x10a0 ++#define TXQ_CNGST_DROP_EN BIT(31) ++#define TXQ_CNGST_DEI_DROP_EN BIT(30) ++ ++#define REG_SLA_TRTCM_CFG 0x1150 ++#define SLA_TRTCM_EN_MASK BIT(31) ++#define SLA_TRTCM_MODE_MASK BIT(30) ++#define SLA_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define SLA_FAST_TICK_MASK GENMASK(15, 0) ++ ++/* CTRL */ ++#define QDMA_DESC_DONE_MASK BIT(31) ++#define QDMA_DESC_DROP_MASK BIT(30) /* tx: drop - rx: overflow */ ++#define QDMA_DESC_MORE_MASK BIT(29) /* more SG elements */ ++#define QDMA_DESC_DEI_MASK BIT(25) ++#define QDMA_DESC_NO_DROP_MASK BIT(24) ++#define QDMA_DESC_LEN_MASK GENMASK(15, 0) ++/* DATA */ ++#define QDMA_DESC_NEXT_ID_MASK GENMASK(15, 0) ++/* TX MSG0 */ ++#define QDMA_ETH_TXMSG_MIC_IDX_MASK BIT(30) ++#define QDMA_ETH_TXMSG_SP_TAG_MASK GENMASK(29, 14) ++#define QDMA_ETH_TXMSG_ICO_MASK BIT(13) ++#define QDMA_ETH_TXMSG_UCO_MASK BIT(12) ++#define QDMA_ETH_TXMSG_TCO_MASK BIT(11) ++#define QDMA_ETH_TXMSG_TSO_MASK BIT(10) ++#define QDMA_ETH_TXMSG_FAST_MASK BIT(9) ++#define QDMA_ETH_TXMSG_OAM_MASK BIT(8) ++#define QDMA_ETH_TXMSG_CHAN_MASK GENMASK(7, 3) ++#define QDMA_ETH_TXMSG_QUEUE_MASK GENMASK(2, 0) ++/* TX MSG1 */ ++#define QDMA_ETH_TXMSG_NO_DROP BIT(31) ++#define QDMA_ETH_TXMSG_METER_MASK GENMASK(30, 24) /* 0x7f no meters */ ++#define QDMA_ETH_TXMSG_FPORT_MASK GENMASK(23, 20) ++#define QDMA_ETH_TXMSG_NBOQ_MASK GENMASK(19, 15) ++#define QDMA_ETH_TXMSG_HWF_MASK BIT(14) ++#define QDMA_ETH_TXMSG_HOP_MASK BIT(13) ++#define QDMA_ETH_TXMSG_PTP_MASK BIT(12) ++#define QDMA_ETH_TXMSG_ACNT_G1_MASK GENMASK(10, 6) /* 0x1f do not count */ ++#define QDMA_ETH_TXMSG_ACNT_G0_MASK GENMASK(5, 0) /* 0x3f do not count */ ++ ++/* RX MSG1 */ ++#define QDMA_ETH_RXMSG_DEI_MASK BIT(31) ++#define QDMA_ETH_RXMSG_IP6_MASK BIT(30) ++#define QDMA_ETH_RXMSG_IP4_MASK BIT(29) ++#define QDMA_ETH_RXMSG_IP4F_MASK BIT(28) ++#define QDMA_ETH_RXMSG_L4_VALID_MASK BIT(27) ++#define QDMA_ETH_RXMSG_L4F_MASK BIT(26) ++#define QDMA_ETH_RXMSG_SPORT_MASK GENMASK(25, 21) ++#define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16) ++#define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0) ++ ++struct airoha_qdma_desc { ++ __le32 rsv; ++ __le32 ctrl; ++ __le32 addr; ++ __le32 data; ++ __le32 msg0; ++ __le32 msg1; ++ __le32 msg2; ++ __le32 msg3; ++}; ++ ++/* CTRL0 */ ++#define QDMA_FWD_DESC_CTX_MASK BIT(31) ++#define QDMA_FWD_DESC_RING_MASK GENMASK(30, 28) ++#define QDMA_FWD_DESC_IDX_MASK GENMASK(27, 16) ++#define QDMA_FWD_DESC_LEN_MASK GENMASK(15, 0) ++/* CTRL1 */ ++#define QDMA_FWD_DESC_FIRST_IDX_MASK GENMASK(15, 0) ++/* CTRL2 */ ++#define QDMA_FWD_DESC_MORE_PKT_NUM_MASK GENMASK(2, 0) ++ ++struct airoha_qdma_fwd_desc { ++ __le32 addr; ++ __le32 ctrl0; ++ __le32 ctrl1; ++ __le32 ctrl2; ++ __le32 msg0; ++ __le32 msg1; ++ __le32 rsv0; ++ __le32 rsv1; ++}; ++ ++enum { ++ QDMA_INT_REG_IDX0, ++ QDMA_INT_REG_IDX1, ++ QDMA_INT_REG_IDX2, ++ QDMA_INT_REG_IDX3, ++ QDMA_INT_REG_IDX4, ++ QDMA_INT_REG_MAX ++}; ++ ++enum { ++ XSI_PCIE0_PORT, ++ XSI_PCIE1_PORT, ++ XSI_USB_PORT, ++ XSI_AE_PORT, ++ XSI_ETH_PORT, ++}; ++ ++enum { ++ XSI_PCIE0_VIP_PORT_MASK = BIT(22), ++ XSI_PCIE1_VIP_PORT_MASK = BIT(23), ++ XSI_USB_VIP_PORT_MASK = BIT(25), ++ XSI_ETH_VIP_PORT_MASK = BIT(24), ++}; ++ ++enum { ++ DEV_STATE_INITIALIZED, ++}; ++ ++enum { ++ CDM_CRSN_QSEL_Q1 = 1, ++ CDM_CRSN_QSEL_Q5 = 5, ++ CDM_CRSN_QSEL_Q6 = 6, ++ CDM_CRSN_QSEL_Q15 = 15, ++}; ++ ++enum { ++ CRSN_08 = 0x8, ++ CRSN_21 = 0x15, /* KA */ ++ CRSN_22 = 0x16, /* hit bind and force route to CPU */ ++ CRSN_24 = 0x18, ++ CRSN_25 = 0x19, ++}; ++ ++enum { ++ FE_PSE_PORT_CDM1, ++ FE_PSE_PORT_GDM1, ++ FE_PSE_PORT_GDM2, ++ FE_PSE_PORT_GDM3, ++ FE_PSE_PORT_PPE1, ++ FE_PSE_PORT_CDM2, ++ FE_PSE_PORT_CDM3, ++ FE_PSE_PORT_CDM4, ++ FE_PSE_PORT_PPE2, ++ FE_PSE_PORT_GDM4, ++ FE_PSE_PORT_CDM5, ++ FE_PSE_PORT_DROP = 0xf, ++}; ++ ++struct airoha_queue_entry { ++ union { ++ void *buf; ++ struct sk_buff *skb; ++ }; ++ dma_addr_t dma_addr; ++ u16 dma_len; ++}; ++ ++struct airoha_queue { ++ struct airoha_eth *eth; ++ ++ /* protect concurrent queue accesses */ ++ spinlock_t lock; ++ struct airoha_queue_entry *entry; ++ struct airoha_qdma_desc *desc; ++ u16 head; ++ u16 tail; ++ ++ int queued; ++ int ndesc; ++ int free_thr; ++ int buf_size; ++ ++ struct napi_struct napi; ++ struct page_pool *page_pool; ++}; ++ ++struct airoha_tx_irq_queue { ++ struct airoha_eth *eth; ++ ++ struct napi_struct napi; ++ u32 *q; ++ ++ int size; ++ int queued; ++ u16 head; ++}; ++ ++struct airoha_hw_stats { ++ /* protect concurrent hw_stats accesses */ ++ spinlock_t lock; ++ struct u64_stats_sync syncp; ++ ++ /* get_stats64 */ ++ u64 rx_ok_pkts; ++ u64 tx_ok_pkts; ++ u64 rx_ok_bytes; ++ u64 tx_ok_bytes; ++ u64 rx_multicast; ++ u64 rx_errors; ++ u64 rx_drops; ++ u64 tx_drops; ++ u64 rx_crc_error; ++ u64 rx_over_errors; ++ /* ethtool stats */ ++ u64 tx_broadcast; ++ u64 tx_multicast; ++ u64 tx_len[7]; ++ u64 rx_broadcast; ++ u64 rx_fragment; ++ u64 rx_jabber; ++ u64 rx_len[7]; ++}; ++ ++struct airoha_gdm_port { ++ struct net_device *dev; ++ struct airoha_eth *eth; ++ int id; ++ ++ struct airoha_hw_stats stats; ++}; ++ ++struct airoha_eth { ++ struct device *dev; ++ ++ unsigned long state; ++ ++ void __iomem *qdma_regs; ++ void __iomem *fe_regs; ++ ++ /* protect concurrent irqmask accesses */ ++ spinlock_t irq_lock; ++ u32 irqmask[QDMA_INT_REG_MAX]; ++ int irq; ++ ++ struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; ++ struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; ++ ++ struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; ++ ++ struct net_device *napi_dev; ++ struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; ++ struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; ++ ++ struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; ++ ++ /* descriptor and packet buffers for qdma hw forward */ ++ struct { ++ void *desc; ++ void *q; ++ } hfwd; ++}; ++ ++static u32 airoha_rr(void __iomem *base, u32 offset) ++{ ++ return readl(base + offset); ++} ++ ++static void airoha_wr(void __iomem *base, u32 offset, u32 val) ++{ ++ writel(val, base + offset); ++} ++ ++static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) ++{ ++ val |= (airoha_rr(base, offset) & ~mask); ++ airoha_wr(base, offset, val); ++ ++ return val; ++} ++ ++#define airoha_fe_rr(eth, offset) \ ++ airoha_rr((eth)->fe_regs, (offset)) ++#define airoha_fe_wr(eth, offset, val) \ ++ airoha_wr((eth)->fe_regs, (offset), (val)) ++#define airoha_fe_rmw(eth, offset, mask, val) \ ++ airoha_rmw((eth)->fe_regs, (offset), (mask), (val)) ++#define airoha_fe_set(eth, offset, val) \ ++ airoha_rmw((eth)->fe_regs, (offset), 0, (val)) ++#define airoha_fe_clear(eth, offset, val) \ ++ airoha_rmw((eth)->fe_regs, (offset), (val), 0) ++ ++#define airoha_qdma_rr(eth, offset) \ ++ airoha_rr((eth)->qdma_regs, (offset)) ++#define airoha_qdma_wr(eth, offset, val) \ ++ airoha_wr((eth)->qdma_regs, (offset), (val)) ++#define airoha_qdma_rmw(eth, offset, mask, val) \ ++ airoha_rmw((eth)->qdma_regs, (offset), (mask), (val)) ++#define airoha_qdma_set(eth, offset, val) \ ++ airoha_rmw((eth)->qdma_regs, (offset), 0, (val)) ++#define airoha_qdma_clear(eth, offset, val) \ ++ airoha_rmw((eth)->qdma_regs, (offset), (val), 0) ++ ++static void airoha_qdma_set_irqmask(struct airoha_eth *eth, int index, ++ u32 clear, u32 set) ++{ ++ unsigned long flags; ++ ++ if (WARN_ON_ONCE(index >= ARRAY_SIZE(eth->irqmask))) ++ return; ++ ++ spin_lock_irqsave(ð->irq_lock, flags); ++ ++ eth->irqmask[index] &= ~clear; ++ eth->irqmask[index] |= set; ++ airoha_qdma_wr(eth, REG_INT_ENABLE(index), eth->irqmask[index]); ++ /* Read irq_enable register in order to guarantee the update above ++ * completes in the spinlock critical section. ++ */ ++ airoha_qdma_rr(eth, REG_INT_ENABLE(index)); ++ ++ spin_unlock_irqrestore(ð->irq_lock, flags); ++} ++ ++static void airoha_qdma_irq_enable(struct airoha_eth *eth, int index, ++ u32 mask) ++{ ++ airoha_qdma_set_irqmask(eth, index, 0, mask); ++} ++ ++static void airoha_qdma_irq_disable(struct airoha_eth *eth, int index, ++ u32 mask) ++{ ++ airoha_qdma_set_irqmask(eth, index, mask, 0); ++} ++ ++static void airoha_set_macaddr(struct airoha_eth *eth, const u8 *addr) ++{ ++ u32 val; ++ ++ val = (addr[0] << 16) | (addr[1] << 8) | addr[2]; ++ airoha_fe_wr(eth, REG_FE_LAN_MAC_H, val); ++ ++ val = (addr[3] << 16) | (addr[4] << 8) | addr[5]; ++ airoha_fe_wr(eth, REG_FE_LAN_MAC_LMIN, val); ++ airoha_fe_wr(eth, REG_FE_LAN_MAC_LMAX, val); ++} ++ ++static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, ++ u32 val) ++{ ++ airoha_fe_rmw(eth, addr, GDM_OCFQ_MASK, ++ FIELD_PREP(GDM_OCFQ_MASK, val)); ++ airoha_fe_rmw(eth, addr, GDM_MCFQ_MASK, ++ FIELD_PREP(GDM_MCFQ_MASK, val)); ++ airoha_fe_rmw(eth, addr, GDM_BCFQ_MASK, ++ FIELD_PREP(GDM_BCFQ_MASK, val)); ++ airoha_fe_rmw(eth, addr, GDM_UCFQ_MASK, ++ FIELD_PREP(GDM_UCFQ_MASK, val)); ++} ++ ++static int airoha_set_gdm_port(struct airoha_eth *eth, int port, bool enable) ++{ ++ u32 val = enable ? FE_PSE_PORT_PPE1 : FE_PSE_PORT_DROP; ++ u32 vip_port, cfg_addr; ++ ++ switch (port) { ++ case XSI_PCIE0_PORT: ++ vip_port = XSI_PCIE0_VIP_PORT_MASK; ++ cfg_addr = REG_GDM_FWD_CFG(3); ++ break; ++ case XSI_PCIE1_PORT: ++ vip_port = XSI_PCIE1_VIP_PORT_MASK; ++ cfg_addr = REG_GDM_FWD_CFG(3); ++ break; ++ case XSI_USB_PORT: ++ vip_port = XSI_USB_VIP_PORT_MASK; ++ cfg_addr = REG_GDM_FWD_CFG(4); ++ break; ++ case XSI_ETH_PORT: ++ vip_port = XSI_ETH_VIP_PORT_MASK; ++ cfg_addr = REG_GDM_FWD_CFG(4); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (enable) { ++ airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port); ++ airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port); ++ } else { ++ airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, vip_port); ++ airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, vip_port); ++ } ++ ++ airoha_set_gdm_port_fwd_cfg(eth, cfg_addr, val); ++ ++ return 0; ++} ++ ++static int airoha_set_gdm_ports(struct airoha_eth *eth, bool enable) ++{ ++ const int port_list[] = { ++ XSI_PCIE0_PORT, ++ XSI_PCIE1_PORT, ++ XSI_USB_PORT, ++ XSI_ETH_PORT ++ }; ++ int i, err; ++ ++ for (i = 0; i < ARRAY_SIZE(port_list); i++) { ++ err = airoha_set_gdm_port(eth, port_list[i], enable); ++ if (err) ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ for (i--; i >= 0; i++) ++ airoha_set_gdm_port(eth, port_list[i], false); ++ ++ return err; ++} ++ ++static void airoha_fe_maccr_init(struct airoha_eth *eth) ++{ ++ int p; ++ ++ for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) { ++ airoha_fe_set(eth, REG_GDM_FWD_CFG(p), ++ GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM | ++ GDM_DROP_CRC_ERR); ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(p), ++ FE_PSE_PORT_CDM1); ++ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p), ++ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, ++ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | ++ FIELD_PREP(GDM_LONG_LEN_MASK, 4004)); ++ } ++ ++ airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK, ++ FIELD_PREP(CDM1_VLAN_MASK, 0x8100)); ++ ++ airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PAD); ++} ++ ++static void airoha_fe_vip_setup(struct airoha_eth *eth) ++{ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(3), ETH_P_PPP_DISC); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(3), PATN_FCPU_EN_MASK | PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(4), PPP_LCP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(4), ++ PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | ++ PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(6), PPP_IPCP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(6), ++ PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | ++ PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(7), PPP_CHAP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(7), ++ PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | ++ PATN_EN_MASK); ++ ++ /* BOOTP (0x43) */ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(8), 0x43); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(8), ++ PATN_FCPU_EN_MASK | PATN_SP_EN_MASK | ++ FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); ++ ++ /* BOOTP (0x44) */ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(9), 0x44); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(9), ++ PATN_FCPU_EN_MASK | PATN_SP_EN_MASK | ++ FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); ++ ++ /* ISAKMP */ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(10), 0x1f401f4); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(10), ++ PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK | ++ FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(11), PPP_IPV6CP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(11), ++ PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | ++ PATN_EN_MASK); ++ ++ /* DHCPv6 */ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(12), 0x2220223); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(12), ++ PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK | ++ FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(19), PPP_PAP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(19), ++ PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | ++ PATN_EN_MASK); ++ ++ /* ETH->ETH_P_1905 (0x893a) */ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(20), 0x893a); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(20), ++ PATN_FCPU_EN_MASK | PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(21), ETH_P_LLDP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(21), ++ PATN_FCPU_EN_MASK | PATN_EN_MASK); ++} ++ ++static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth, ++ u32 port, u32 queue) ++{ ++ u32 val; ++ ++ airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR, ++ PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK, ++ FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) | ++ FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue)); ++ val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL); ++ ++ return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val); ++} ++ ++static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth, ++ u32 port, u32 queue, u32 val) ++{ ++ airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_VAL, PSE_CFG_OQ_RSV_MASK, ++ FIELD_PREP(PSE_CFG_OQ_RSV_MASK, val)); ++ airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR, ++ PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK | ++ PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK, ++ FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) | ++ FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue) | ++ PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK); ++} ++ ++static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth, ++ u32 port, u32 queue, u32 val) ++{ ++ u32 orig_val, tmp, all_rsv, fq_limit; ++ ++ airoha_fe_set_pse_queue_rsv_pages(eth, port, queue, val); ++ ++ /* modify all rsv */ ++ orig_val = airoha_fe_get_pse_queue_rsv_pages(eth, port, queue); ++ tmp = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET); ++ all_rsv = FIELD_GET(PSE_ALLRSV_MASK, tmp); ++ all_rsv += (val - orig_val); ++ airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK, ++ FIELD_PREP(PSE_ALLRSV_MASK, all_rsv)); ++ ++ /* modify hthd */ ++ tmp = airoha_fe_rr(eth, PSE_FQ_CFG); ++ fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp); ++ tmp = fq_limit - all_rsv - 0x20; ++ airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD, ++ PSE_SHARE_USED_HTHD_MASK, ++ FIELD_PREP(PSE_SHARE_USED_HTHD_MASK, tmp)); ++ ++ tmp = fq_limit - all_rsv - 0x100; ++ airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD, ++ PSE_SHARE_USED_MTHD_MASK, ++ FIELD_PREP(PSE_SHARE_USED_MTHD_MASK, tmp)); ++ tmp = (3 * tmp) >> 2; ++ airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, ++ PSE_SHARE_USED_LTHD_MASK, ++ FIELD_PREP(PSE_SHARE_USED_LTHD_MASK, tmp)); ++ ++ return 0; ++} ++ ++static void airoha_fe_pse_ports_init(struct airoha_eth *eth) ++{ ++ const u32 pse_port_num_queues[] = { ++ [FE_PSE_PORT_CDM1] = 6, ++ [FE_PSE_PORT_GDM1] = 6, ++ [FE_PSE_PORT_GDM2] = 32, ++ [FE_PSE_PORT_GDM3] = 6, ++ [FE_PSE_PORT_PPE1] = 4, ++ [FE_PSE_PORT_CDM2] = 6, ++ [FE_PSE_PORT_CDM3] = 8, ++ [FE_PSE_PORT_CDM4] = 10, ++ [FE_PSE_PORT_PPE2] = 4, ++ [FE_PSE_PORT_GDM4] = 2, ++ [FE_PSE_PORT_CDM5] = 2, ++ }; ++ int q; ++ ++ /* hw misses PPE2 oq rsv */ ++ airoha_fe_set(eth, REG_FE_PSE_BUF_SET, ++ PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2]); ++ ++ /* CMD1 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM1]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM1, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* GMD1 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM1]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM1, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* GMD2 */ ++ for (q = 6; q < pse_port_num_queues[FE_PSE_PORT_GDM2]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM2, q, 0); ++ /* GMD3 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM3]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM3, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* PPE1 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) { ++ if (q < pse_port_num_queues[FE_PSE_PORT_PPE1]) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, ++ PSE_QUEUE_RSV_PAGES); ++ else ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, 0); ++ } ++ /* CDM2 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM2]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM2, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* CDM3 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM3] - 1; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM3, q, 0); ++ /* CDM4 */ ++ for (q = 4; q < pse_port_num_queues[FE_PSE_PORT_CDM4]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM4, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* PPE2 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) { ++ if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, ++ PSE_QUEUE_RSV_PAGES); ++ else ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, 0); ++ } ++ /* GMD4 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM4, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* CDM5 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM5]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM5, q, ++ PSE_QUEUE_RSV_PAGES); ++} ++ ++static int airoha_fe_mc_vlan_clear(struct airoha_eth *eth) ++{ ++ int i; ++ ++ for (i = 0; i < AIROHA_FE_MC_MAX_VLAN_TABLE; i++) { ++ int err, j; ++ u32 val; ++ ++ airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0); ++ ++ val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) | ++ MC_VLAN_CFG_TABLE_SEL_MASK | MC_VLAN_CFG_RW_MASK; ++ airoha_fe_wr(eth, REG_MC_VLAN_CFG, val); ++ err = read_poll_timeout(airoha_fe_rr, val, ++ val & MC_VLAN_CFG_CMD_DONE_MASK, ++ USEC_PER_MSEC, 5 * USEC_PER_MSEC, ++ false, eth, REG_MC_VLAN_CFG); ++ if (err) ++ return err; ++ ++ for (j = 0; j < AIROHA_FE_MC_MAX_VLAN_PORT; j++) { ++ airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0); ++ ++ val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) | ++ FIELD_PREP(MC_VLAN_CFG_PORT_ID_MASK, j) | ++ MC_VLAN_CFG_RW_MASK; ++ airoha_fe_wr(eth, REG_MC_VLAN_CFG, val); ++ err = read_poll_timeout(airoha_fe_rr, val, ++ val & MC_VLAN_CFG_CMD_DONE_MASK, ++ USEC_PER_MSEC, ++ 5 * USEC_PER_MSEC, false, eth, ++ REG_MC_VLAN_CFG); ++ if (err) ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth) ++{ ++ /* CDM1_CRSN_QSEL */ ++ airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_22 >> 2), ++ CDM1_CRSN_QSEL_REASON_MASK(CRSN_22), ++ FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_22), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_08 >> 2), ++ CDM1_CRSN_QSEL_REASON_MASK(CRSN_08), ++ FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_08), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_21 >> 2), ++ CDM1_CRSN_QSEL_REASON_MASK(CRSN_21), ++ FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_21), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_24 >> 2), ++ CDM1_CRSN_QSEL_REASON_MASK(CRSN_24), ++ FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_24), ++ CDM_CRSN_QSEL_Q6)); ++ airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_25 >> 2), ++ CDM1_CRSN_QSEL_REASON_MASK(CRSN_25), ++ FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_25), ++ CDM_CRSN_QSEL_Q1)); ++ /* CDM2_CRSN_QSEL */ ++ airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_08 >> 2), ++ CDM2_CRSN_QSEL_REASON_MASK(CRSN_08), ++ FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_08), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_21 >> 2), ++ CDM2_CRSN_QSEL_REASON_MASK(CRSN_21), ++ FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_21), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_22 >> 2), ++ CDM2_CRSN_QSEL_REASON_MASK(CRSN_22), ++ FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_22), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_24 >> 2), ++ CDM2_CRSN_QSEL_REASON_MASK(CRSN_24), ++ FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_24), ++ CDM_CRSN_QSEL_Q6)); ++ airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_25 >> 2), ++ CDM2_CRSN_QSEL_REASON_MASK(CRSN_25), ++ FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_25), ++ CDM_CRSN_QSEL_Q1)); ++} ++ ++static int airoha_fe_init(struct airoha_eth *eth) ++{ ++ airoha_fe_maccr_init(eth); ++ ++ /* PSE IQ reserve */ ++ airoha_fe_rmw(eth, REG_PSE_IQ_REV1, PSE_IQ_RES1_P2_MASK, ++ FIELD_PREP(PSE_IQ_RES1_P2_MASK, 0x10)); ++ airoha_fe_rmw(eth, REG_PSE_IQ_REV2, ++ PSE_IQ_RES2_P5_MASK | PSE_IQ_RES2_P4_MASK, ++ FIELD_PREP(PSE_IQ_RES2_P5_MASK, 0x40) | ++ FIELD_PREP(PSE_IQ_RES2_P4_MASK, 0x34)); ++ ++ /* enable FE copy engine for MC/KA/DPI */ ++ airoha_fe_wr(eth, REG_FE_PCE_CFG, ++ PCE_DPI_EN_MASK | PCE_KA_EN_MASK | PCE_MC_EN_MASK); ++ /* set vip queue selection to ring 1 */ ++ airoha_fe_rmw(eth, REG_CDM1_FWD_CFG, CDM1_VIP_QSEL_MASK, ++ FIELD_PREP(CDM1_VIP_QSEL_MASK, 0x4)); ++ airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_VIP_QSEL_MASK, ++ FIELD_PREP(CDM2_VIP_QSEL_MASK, 0x4)); ++ /* set GDM4 source interface offset to 8 */ ++ airoha_fe_rmw(eth, REG_GDM4_SRC_PORT_SET, ++ GDM4_SPORT_OFF2_MASK | ++ GDM4_SPORT_OFF1_MASK | ++ GDM4_SPORT_OFF0_MASK, ++ FIELD_PREP(GDM4_SPORT_OFF2_MASK, 8) | ++ FIELD_PREP(GDM4_SPORT_OFF1_MASK, 8) | ++ FIELD_PREP(GDM4_SPORT_OFF0_MASK, 8)); ++ ++ /* set PSE Page as 128B */ ++ airoha_fe_rmw(eth, REG_FE_DMA_GLO_CFG, ++ FE_DMA_GLO_L2_SPACE_MASK | FE_DMA_GLO_PG_SZ_MASK, ++ FIELD_PREP(FE_DMA_GLO_L2_SPACE_MASK, 2) | ++ FE_DMA_GLO_PG_SZ_MASK); ++ airoha_fe_wr(eth, REG_FE_RST_GLO_CFG, ++ FE_RST_CORE_MASK | FE_RST_GDM3_MBI_ARB_MASK | ++ FE_RST_GDM4_MBI_ARB_MASK); ++ usleep_range(1000, 2000); ++ ++ /* connect RxRing1 and RxRing15 to PSE Port0 OQ-1 ++ * connect other rings to PSE Port0 OQ-0 ++ */ ++ airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP0, BIT(4)); ++ airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP1, BIT(28)); ++ airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP2, BIT(4)); ++ airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP3, BIT(28)); ++ ++ airoha_fe_vip_setup(eth); ++ airoha_fe_pse_ports_init(eth); ++ ++ airoha_fe_set(eth, REG_GDM_MISC_CFG, ++ GDM2_RDM_ACK_WAIT_PREF_MASK | ++ GDM2_CHN_VLD_MODE_MASK); ++ airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_OAM_QSEL_MASK, 15); ++ ++ /* init fragment and assemble Force Port */ ++ /* NPU Core-3, NPU Bridge Channel-3 */ ++ airoha_fe_rmw(eth, REG_IP_FRAG_FP, ++ IP_FRAGMENT_PORT_MASK | IP_FRAGMENT_NBQ_MASK, ++ FIELD_PREP(IP_FRAGMENT_PORT_MASK, 6) | ++ FIELD_PREP(IP_FRAGMENT_NBQ_MASK, 3)); ++ /* QDMA LAN, RX Ring-22 */ ++ airoha_fe_rmw(eth, REG_IP_FRAG_FP, ++ IP_ASSEMBLE_PORT_MASK | IP_ASSEMBLE_NBQ_MASK, ++ FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) | ++ FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22)); ++ ++ airoha_fe_set(eth, REG_GDM3_FWD_CFG, GDM3_PAD_EN_MASK); ++ airoha_fe_set(eth, REG_GDM4_FWD_CFG, GDM4_PAD_EN_MASK); ++ ++ airoha_fe_crsn_qsel_init(eth); ++ ++ airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK); ++ airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PORT_XFC_MASK); ++ ++ /* default aging mode for mbi unlock issue */ ++ airoha_fe_rmw(eth, REG_GDM2_CHN_RLS, ++ MBI_RX_AGE_SEL_MASK | MBI_TX_AGE_SEL_MASK, ++ FIELD_PREP(MBI_RX_AGE_SEL_MASK, 3) | ++ FIELD_PREP(MBI_TX_AGE_SEL_MASK, 3)); ++ ++ /* disable IFC by default */ ++ airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK); ++ ++ /* enable 1:N vlan action, init vlan table */ ++ airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK); ++ ++ return airoha_fe_mc_vlan_clear(eth); ++} ++ ++static int airoha_qdma_fill_rx_queue(struct airoha_queue *q) ++{ ++ enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); ++ struct airoha_eth *eth = q->eth; ++ int qid = q - ð->q_rx[0]; ++ int nframes = 0; ++ ++ while (q->queued < q->ndesc - 1) { ++ struct airoha_queue_entry *e = &q->entry[q->head]; ++ struct airoha_qdma_desc *desc = &q->desc[q->head]; ++ struct page *page; ++ int offset; ++ u32 val; ++ ++ page = page_pool_dev_alloc_frag(q->page_pool, &offset, ++ q->buf_size); ++ if (!page) ++ break; ++ ++ q->head = (q->head + 1) % q->ndesc; ++ q->queued++; ++ nframes++; ++ ++ e->buf = page_address(page) + offset; ++ e->dma_addr = page_pool_get_dma_addr(page) + offset; ++ e->dma_len = SKB_WITH_OVERHEAD(q->buf_size); ++ ++ dma_sync_single_for_device(eth->dev, e->dma_addr, e->dma_len, ++ dir); ++ ++ val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len); ++ WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); ++ WRITE_ONCE(desc->addr, cpu_to_le32(e->dma_addr)); ++ val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, q->head); ++ WRITE_ONCE(desc->data, cpu_to_le32(val)); ++ WRITE_ONCE(desc->msg0, 0); ++ WRITE_ONCE(desc->msg1, 0); ++ WRITE_ONCE(desc->msg2, 0); ++ WRITE_ONCE(desc->msg3, 0); ++ ++ airoha_qdma_rmw(eth, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK, ++ FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head)); ++ } ++ ++ return nframes; ++} ++ ++static int airoha_qdma_get_gdm_port(struct airoha_eth *eth, ++ struct airoha_qdma_desc *desc) ++{ ++ u32 port, sport, msg1 = le32_to_cpu(desc->msg1); ++ ++ sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); ++ switch (sport) { ++ case 0x10 ... 0x13: ++ port = 0; ++ break; ++ case 0x2 ... 0x4: ++ port = sport - 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port; ++} ++ ++static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) ++{ ++ enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); ++ struct airoha_eth *eth = q->eth; ++ int qid = q - ð->q_rx[0]; ++ int done = 0; ++ ++ while (done < budget) { ++ struct airoha_queue_entry *e = &q->entry[q->tail]; ++ struct airoha_qdma_desc *desc = &q->desc[q->tail]; ++ dma_addr_t dma_addr = le32_to_cpu(desc->addr); ++ u32 desc_ctrl = le32_to_cpu(desc->ctrl); ++ struct sk_buff *skb; ++ int len, p; ++ ++ if (!(desc_ctrl & QDMA_DESC_DONE_MASK)) ++ break; ++ ++ if (!dma_addr) ++ break; ++ ++ len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); ++ if (!len) ++ break; ++ ++ q->tail = (q->tail + 1) % q->ndesc; ++ q->queued--; ++ ++ dma_sync_single_for_cpu(eth->dev, dma_addr, ++ SKB_WITH_OVERHEAD(q->buf_size), dir); ++ ++ p = airoha_qdma_get_gdm_port(eth, desc); ++ if (p < 0 || !eth->ports[p]) { ++ page_pool_put_full_page(q->page_pool, ++ virt_to_head_page(e->buf), ++ true); ++ continue; ++ } ++ ++ skb = napi_build_skb(e->buf, q->buf_size); ++ if (!skb) { ++ page_pool_put_full_page(q->page_pool, ++ virt_to_head_page(e->buf), ++ true); ++ break; ++ } ++ ++ skb_reserve(skb, 2); ++ __skb_put(skb, len); ++ skb_mark_for_recycle(skb); ++ skb->dev = eth->ports[p]->dev; ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ skb_record_rx_queue(skb, qid); ++ napi_gro_receive(&q->napi, skb); ++ ++ done++; ++ } ++ airoha_qdma_fill_rx_queue(q); ++ ++ return done; ++} ++ ++static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget) ++{ ++ struct airoha_queue *q = container_of(napi, struct airoha_queue, napi); ++ struct airoha_eth *eth = q->eth; ++ int cur, done = 0; ++ ++ do { ++ cur = airoha_qdma_rx_process(q, budget - done); ++ done += cur; ++ } while (cur && done < budget); ++ ++ if (done < budget && napi_complete(napi)) ++ airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX1, ++ RX_DONE_INT_MASK); ++ ++ return done; ++} ++ ++static int airoha_qdma_init_rx_queue(struct airoha_eth *eth, ++ struct airoha_queue *q, int ndesc) ++{ ++ const struct page_pool_params pp_params = { ++ .order = 0, ++ .pool_size = 256, ++ .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV | ++ PP_FLAG_PAGE_FRAG, ++ .dma_dir = DMA_FROM_DEVICE, ++ .max_len = PAGE_SIZE, ++ .nid = NUMA_NO_NODE, ++ .dev = eth->dev, ++ .napi = &q->napi, ++ }; ++ int qid = q - ð->q_rx[0], thr; ++ dma_addr_t dma_addr; ++ ++ q->buf_size = PAGE_SIZE / 2; ++ q->ndesc = ndesc; ++ q->eth = eth; ++ ++ q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), ++ GFP_KERNEL); ++ if (!q->entry) ++ return -ENOMEM; ++ ++ q->page_pool = page_pool_create(&pp_params); ++ if (IS_ERR(q->page_pool)) { ++ int err = PTR_ERR(q->page_pool); ++ ++ q->page_pool = NULL; ++ return err; ++ } ++ ++ q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), ++ &dma_addr, GFP_KERNEL); ++ if (!q->desc) ++ return -ENOMEM; ++ ++ netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll); ++ ++ airoha_qdma_wr(eth, REG_RX_RING_BASE(qid), dma_addr); ++ airoha_qdma_rmw(eth, REG_RX_RING_SIZE(qid), RX_RING_SIZE_MASK, ++ FIELD_PREP(RX_RING_SIZE_MASK, ndesc)); ++ ++ thr = clamp(ndesc >> 3, 1, 32); ++ airoha_qdma_rmw(eth, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK, ++ FIELD_PREP(RX_RING_THR_MASK, thr)); ++ airoha_qdma_rmw(eth, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, ++ FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head)); ++ ++ airoha_qdma_fill_rx_queue(q); ++ ++ return 0; ++} ++ ++static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q) ++{ ++ enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); ++ struct airoha_eth *eth = q->eth; ++ ++ while (q->queued) { ++ struct airoha_queue_entry *e = &q->entry[q->tail]; ++ struct page *page = virt_to_head_page(e->buf); ++ ++ dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len, ++ dir); ++ page_pool_put_full_page(q->page_pool, page, false); ++ q->tail = (q->tail + 1) % q->ndesc; ++ q->queued--; ++ } ++} ++ ++static int airoha_qdma_init_rx(struct airoha_eth *eth) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { ++ int err; ++ ++ if (!(RX_DONE_INT_MASK & BIT(i))) { ++ /* rx-queue not binded to irq */ ++ continue; ++ } ++ ++ err = airoha_qdma_init_rx_queue(eth, ð->q_rx[i], ++ RX_DSCP_NUM(i)); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) ++{ ++ struct airoha_tx_irq_queue *irq_q; ++ struct airoha_eth *eth; ++ int id, done = 0; ++ ++ irq_q = container_of(napi, struct airoha_tx_irq_queue, napi); ++ eth = irq_q->eth; ++ id = irq_q - ð->q_tx_irq[0]; ++ ++ while (irq_q->queued > 0 && done < budget) { ++ u32 qid, last, val = irq_q->q[irq_q->head]; ++ struct airoha_queue *q; ++ ++ if (val == 0xff) ++ break; ++ ++ irq_q->q[irq_q->head] = 0xff; /* mark as done */ ++ irq_q->head = (irq_q->head + 1) % irq_q->size; ++ irq_q->queued--; ++ done++; ++ ++ last = FIELD_GET(IRQ_DESC_IDX_MASK, val); ++ qid = FIELD_GET(IRQ_RING_IDX_MASK, val); ++ ++ if (qid >= ARRAY_SIZE(eth->q_tx)) ++ continue; ++ ++ q = ð->q_tx[qid]; ++ if (!q->ndesc) ++ continue; ++ ++ spin_lock_bh(&q->lock); ++ ++ while (q->queued > 0) { ++ struct airoha_qdma_desc *desc = &q->desc[q->tail]; ++ struct airoha_queue_entry *e = &q->entry[q->tail]; ++ u32 desc_ctrl = le32_to_cpu(desc->ctrl); ++ struct sk_buff *skb = e->skb; ++ u16 index = q->tail; ++ ++ if (!(desc_ctrl & QDMA_DESC_DONE_MASK) && ++ !(desc_ctrl & QDMA_DESC_DROP_MASK)) ++ break; ++ ++ q->tail = (q->tail + 1) % q->ndesc; ++ q->queued--; ++ ++ dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, ++ DMA_TO_DEVICE); ++ ++ WRITE_ONCE(desc->msg0, 0); ++ WRITE_ONCE(desc->msg1, 0); ++ ++ if (skb) { ++ struct netdev_queue *txq; ++ ++ txq = netdev_get_tx_queue(skb->dev, qid); ++ if (netif_tx_queue_stopped(txq) && ++ q->ndesc - q->queued >= q->free_thr) ++ netif_tx_wake_queue(txq); ++ ++ dev_kfree_skb_any(skb); ++ e->skb = NULL; ++ } ++ ++ if (index == last) ++ break; ++ } ++ ++ spin_unlock_bh(&q->lock); ++ } ++ ++ if (done) { ++ int i, len = done >> 7; ++ ++ for (i = 0; i < len; i++) ++ airoha_qdma_rmw(eth, REG_IRQ_CLEAR_LEN(id), ++ IRQ_CLEAR_LEN_MASK, 0x80); ++ airoha_qdma_rmw(eth, REG_IRQ_CLEAR_LEN(id), ++ IRQ_CLEAR_LEN_MASK, (done & 0x7f)); ++ } ++ ++ if (done < budget && napi_complete(napi)) ++ airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX0, ++ TX_DONE_INT_MASK(id)); ++ ++ return done; ++} ++ ++static int airoha_qdma_init_tx_queue(struct airoha_eth *eth, ++ struct airoha_queue *q, int size) ++{ ++ int i, qid = q - ð->q_tx[0]; ++ dma_addr_t dma_addr; ++ ++ spin_lock_init(&q->lock); ++ q->ndesc = size; ++ q->eth = eth; ++ q->free_thr = 1 + MAX_SKB_FRAGS; ++ ++ q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), ++ GFP_KERNEL); ++ if (!q->entry) ++ return -ENOMEM; ++ ++ q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), ++ &dma_addr, GFP_KERNEL); ++ if (!q->desc) ++ return -ENOMEM; ++ ++ for (i = 0; i < q->ndesc; i++) { ++ u32 val; ++ ++ val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1); ++ WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val)); ++ } ++ ++ airoha_qdma_wr(eth, REG_TX_RING_BASE(qid), dma_addr); ++ airoha_qdma_rmw(eth, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, ++ FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); ++ airoha_qdma_rmw(eth, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, ++ FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head)); ++ ++ return 0; ++} ++ ++static int airoha_qdma_tx_irq_init(struct airoha_eth *eth, ++ struct airoha_tx_irq_queue *irq_q, ++ int size) ++{ ++ int id = irq_q - ð->q_tx_irq[0]; ++ dma_addr_t dma_addr; ++ ++ netif_napi_add_tx(eth->napi_dev, &irq_q->napi, ++ airoha_qdma_tx_napi_poll); ++ irq_q->q = dmam_alloc_coherent(eth->dev, size * sizeof(u32), ++ &dma_addr, GFP_KERNEL); ++ if (!irq_q->q) ++ return -ENOMEM; ++ ++ memset(irq_q->q, 0xff, size * sizeof(u32)); ++ irq_q->size = size; ++ irq_q->eth = eth; ++ ++ airoha_qdma_wr(eth, REG_TX_IRQ_BASE(id), dma_addr); ++ airoha_qdma_rmw(eth, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, ++ FIELD_PREP(TX_IRQ_DEPTH_MASK, size)); ++ airoha_qdma_rmw(eth, REG_TX_IRQ_CFG(id), TX_IRQ_THR_MASK, ++ FIELD_PREP(TX_IRQ_THR_MASK, 1)); ++ ++ return 0; ++} ++ ++static int airoha_qdma_init_tx(struct airoha_eth *eth) ++{ ++ int i, err; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->q_tx_irq); i++) { ++ err = airoha_qdma_tx_irq_init(eth, ð->q_tx_irq[i], ++ IRQ_QUEUE_LEN(i)); ++ if (err) ++ return err; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(eth->q_tx); i++) { ++ err = airoha_qdma_init_tx_queue(eth, ð->q_tx[i], ++ TX_DSCP_NUM); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) ++{ ++ struct airoha_eth *eth = q->eth; ++ ++ spin_lock_bh(&q->lock); ++ while (q->queued) { ++ struct airoha_queue_entry *e = &q->entry[q->tail]; ++ ++ dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, ++ DMA_TO_DEVICE); ++ dev_kfree_skb_any(e->skb); ++ e->skb = NULL; ++ ++ q->tail = (q->tail + 1) % q->ndesc; ++ q->queued--; ++ } ++ spin_unlock_bh(&q->lock); ++} ++ ++static int airoha_qdma_init_hfwd_queues(struct airoha_eth *eth) ++{ ++ dma_addr_t dma_addr; ++ u32 status; ++ int size; ++ ++ size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); ++ eth->hfwd.desc = dmam_alloc_coherent(eth->dev, size, &dma_addr, ++ GFP_KERNEL); ++ if (!eth->hfwd.desc) ++ return -ENOMEM; ++ ++ airoha_qdma_wr(eth, REG_FWD_DSCP_BASE, dma_addr); ++ ++ size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; ++ eth->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr, ++ GFP_KERNEL); ++ if (!eth->hfwd.q) ++ return -ENOMEM; ++ ++ airoha_qdma_wr(eth, REG_FWD_BUF_BASE, dma_addr); ++ ++ airoha_qdma_rmw(eth, REG_HW_FWD_DSCP_CFG, ++ HW_FWD_DSCP_PAYLOAD_SIZE_MASK, ++ FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0)); ++ airoha_qdma_rmw(eth, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK, ++ FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128)); ++ airoha_qdma_rmw(eth, REG_LMGR_INIT_CFG, ++ LMGR_INIT_START | LMGR_SRAM_MODE_MASK | ++ HW_FWD_DESC_NUM_MASK, ++ FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | ++ LMGR_INIT_START); ++ ++ return read_poll_timeout(airoha_qdma_rr, status, ++ !(status & LMGR_INIT_START), USEC_PER_MSEC, ++ 30 * USEC_PER_MSEC, true, eth, ++ REG_LMGR_INIT_CFG); ++} ++ ++static void airoha_qdma_init_qos(struct airoha_eth *eth) ++{ ++ airoha_qdma_clear(eth, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_SCALE_MASK); ++ airoha_qdma_set(eth, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_BASE_MASK); ++ ++ airoha_qdma_clear(eth, REG_PSE_BUF_USAGE_CFG, ++ PSE_BUF_ESTIMATE_EN_MASK); ++ ++ airoha_qdma_set(eth, REG_EGRESS_RATE_METER_CFG, ++ EGRESS_RATE_METER_EN_MASK | ++ EGRESS_RATE_METER_EQ_RATE_EN_MASK); ++ /* 2047us x 31 = 63.457ms */ ++ airoha_qdma_rmw(eth, REG_EGRESS_RATE_METER_CFG, ++ EGRESS_RATE_METER_WINDOW_SZ_MASK, ++ FIELD_PREP(EGRESS_RATE_METER_WINDOW_SZ_MASK, 0x1f)); ++ airoha_qdma_rmw(eth, REG_EGRESS_RATE_METER_CFG, ++ EGRESS_RATE_METER_TIMESLICE_MASK, ++ FIELD_PREP(EGRESS_RATE_METER_TIMESLICE_MASK, 0x7ff)); ++ ++ /* ratelimit init */ ++ airoha_qdma_set(eth, REG_GLB_TRTCM_CFG, GLB_TRTCM_EN_MASK); ++ /* fast-tick 25us */ ++ airoha_qdma_rmw(eth, REG_GLB_TRTCM_CFG, GLB_FAST_TICK_MASK, ++ FIELD_PREP(GLB_FAST_TICK_MASK, 25)); ++ airoha_qdma_rmw(eth, REG_GLB_TRTCM_CFG, GLB_SLOW_TICK_RATIO_MASK, ++ FIELD_PREP(GLB_SLOW_TICK_RATIO_MASK, 40)); ++ ++ airoha_qdma_set(eth, REG_EGRESS_TRTCM_CFG, EGRESS_TRTCM_EN_MASK); ++ airoha_qdma_rmw(eth, REG_EGRESS_TRTCM_CFG, EGRESS_FAST_TICK_MASK, ++ FIELD_PREP(EGRESS_FAST_TICK_MASK, 25)); ++ airoha_qdma_rmw(eth, REG_EGRESS_TRTCM_CFG, ++ EGRESS_SLOW_TICK_RATIO_MASK, ++ FIELD_PREP(EGRESS_SLOW_TICK_RATIO_MASK, 40)); ++ ++ airoha_qdma_set(eth, REG_INGRESS_TRTCM_CFG, INGRESS_TRTCM_EN_MASK); ++ airoha_qdma_clear(eth, REG_INGRESS_TRTCM_CFG, ++ INGRESS_TRTCM_MODE_MASK); ++ airoha_qdma_rmw(eth, REG_INGRESS_TRTCM_CFG, INGRESS_FAST_TICK_MASK, ++ FIELD_PREP(INGRESS_FAST_TICK_MASK, 125)); ++ airoha_qdma_rmw(eth, REG_INGRESS_TRTCM_CFG, ++ INGRESS_SLOW_TICK_RATIO_MASK, ++ FIELD_PREP(INGRESS_SLOW_TICK_RATIO_MASK, 8)); ++ ++ airoha_qdma_set(eth, REG_SLA_TRTCM_CFG, SLA_TRTCM_EN_MASK); ++ airoha_qdma_rmw(eth, REG_SLA_TRTCM_CFG, SLA_FAST_TICK_MASK, ++ FIELD_PREP(SLA_FAST_TICK_MASK, 25)); ++ airoha_qdma_rmw(eth, REG_SLA_TRTCM_CFG, SLA_SLOW_TICK_RATIO_MASK, ++ FIELD_PREP(SLA_SLOW_TICK_RATIO_MASK, 40)); ++} ++ ++static int airoha_qdma_hw_init(struct airoha_eth *eth) ++{ ++ int i; ++ ++ /* clear pending irqs */ ++ for (i = 0; i < ARRAY_SIZE(eth->irqmask); i++) ++ airoha_qdma_wr(eth, REG_INT_STATUS(i), 0xffffffff); ++ ++ /* setup irqs */ ++ airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX0, INT_IDX0_MASK); ++ airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX1, INT_IDX1_MASK); ++ airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX4, INT_IDX4_MASK); ++ ++ /* setup irq binding */ ++ for (i = 0; i < ARRAY_SIZE(eth->q_tx); i++) { ++ if (!eth->q_tx[i].ndesc) ++ continue; ++ ++ if (TX_RING_IRQ_BLOCKING_MAP_MASK & BIT(i)) ++ airoha_qdma_set(eth, REG_TX_RING_BLOCKING(i), ++ TX_RING_IRQ_BLOCKING_CFG_MASK); ++ else ++ airoha_qdma_clear(eth, REG_TX_RING_BLOCKING(i), ++ TX_RING_IRQ_BLOCKING_CFG_MASK); ++ } ++ ++ airoha_qdma_wr(eth, REG_QDMA_GLOBAL_CFG, ++ GLOBAL_CFG_RX_2B_OFFSET_MASK | ++ FIELD_PREP(GLOBAL_CFG_DMA_PREFERENCE_MASK, 3) | ++ GLOBAL_CFG_CPU_TXR_RR_MASK | ++ GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK | ++ GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK | ++ GLOBAL_CFG_MULTICAST_EN_MASK | ++ GLOBAL_CFG_IRQ0_EN_MASK | GLOBAL_CFG_IRQ1_EN_MASK | ++ GLOBAL_CFG_TX_WB_DONE_MASK | ++ FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 2)); ++ ++ airoha_qdma_init_qos(eth); ++ ++ /* disable qdma rx delay interrupt */ ++ for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { ++ if (!eth->q_rx[i].ndesc) ++ continue; ++ ++ airoha_qdma_clear(eth, REG_RX_DELAY_INT_IDX(i), ++ RX_DELAY_INT_MASK); ++ } ++ ++ airoha_qdma_set(eth, REG_TXQ_CNGST_CFG, ++ TXQ_CNGST_DROP_EN | TXQ_CNGST_DEI_DROP_EN); ++ ++ return 0; ++} ++ ++static irqreturn_t airoha_irq_handler(int irq, void *dev_instance) ++{ ++ struct airoha_eth *eth = dev_instance; ++ u32 intr[ARRAY_SIZE(eth->irqmask)]; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->irqmask); i++) { ++ intr[i] = airoha_qdma_rr(eth, REG_INT_STATUS(i)); ++ intr[i] &= eth->irqmask[i]; ++ airoha_qdma_wr(eth, REG_INT_STATUS(i), intr[i]); ++ } ++ ++ if (!test_bit(DEV_STATE_INITIALIZED, ð->state)) ++ return IRQ_NONE; ++ ++ if (intr[1] & RX_DONE_INT_MASK) { ++ airoha_qdma_irq_disable(eth, QDMA_INT_REG_IDX1, ++ RX_DONE_INT_MASK); ++ ++ for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { ++ if (!eth->q_rx[i].ndesc) ++ continue; ++ ++ if (intr[1] & BIT(i)) ++ napi_schedule(ð->q_rx[i].napi); ++ } ++ } ++ ++ if (intr[0] & INT_TX_MASK) { ++ for (i = 0; i < ARRAY_SIZE(eth->q_tx_irq); i++) { ++ struct airoha_tx_irq_queue *irq_q = ð->q_tx_irq[i]; ++ u32 status, head; ++ ++ if (!(intr[0] & TX_DONE_INT_MASK(i))) ++ continue; ++ ++ airoha_qdma_irq_disable(eth, QDMA_INT_REG_IDX0, ++ TX_DONE_INT_MASK(i)); ++ ++ status = airoha_qdma_rr(eth, REG_IRQ_STATUS(i)); ++ head = FIELD_GET(IRQ_HEAD_IDX_MASK, status); ++ irq_q->head = head % irq_q->size; ++ irq_q->queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status); ++ ++ napi_schedule(ð->q_tx_irq[i].napi); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int airoha_qdma_init(struct airoha_eth *eth) ++{ ++ int err; ++ ++ err = devm_request_irq(eth->dev, eth->irq, airoha_irq_handler, ++ IRQF_SHARED, KBUILD_MODNAME, eth); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_init_rx(eth); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_init_tx(eth); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_init_hfwd_queues(eth); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_hw_init(eth); ++ if (err) ++ return err; ++ ++ set_bit(DEV_STATE_INITIALIZED, ð->state); ++ ++ return 0; ++} ++ ++static int airoha_hw_init(struct airoha_eth *eth) ++{ ++ int err; ++ ++ /* disable xsi */ ++ reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts), eth->xsi_rsts); ++ ++ reset_control_bulk_assert(ARRAY_SIZE(eth->rsts), eth->rsts); ++ msleep(20); ++ reset_control_bulk_deassert(ARRAY_SIZE(eth->rsts), eth->rsts); ++ msleep(20); ++ ++ err = airoha_fe_init(eth); ++ if (err) ++ return err; ++ ++ return airoha_qdma_init(eth); ++} ++ ++static void airoha_hw_cleanup(struct airoha_eth *eth) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { ++ if (!eth->q_rx[i].ndesc) ++ continue; ++ ++ napi_disable(ð->q_rx[i].napi); ++ netif_napi_del(ð->q_rx[i].napi); ++ airoha_qdma_cleanup_rx_queue(ð->q_rx[i]); ++ if (eth->q_rx[i].page_pool) ++ page_pool_destroy(eth->q_rx[i].page_pool); ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(eth->q_tx_irq); i++) { ++ napi_disable(ð->q_tx_irq[i].napi); ++ netif_napi_del(ð->q_tx_irq[i].napi); ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(eth->q_tx); i++) { ++ if (!eth->q_tx[i].ndesc) ++ continue; ++ ++ airoha_qdma_cleanup_tx_queue(ð->q_tx[i]); ++ } ++} ++ ++static void airoha_qdma_start_napi(struct airoha_eth *eth) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->q_tx_irq); i++) ++ napi_enable(ð->q_tx_irq[i].napi); ++ ++ for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { ++ if (!eth->q_rx[i].ndesc) ++ continue; ++ ++ napi_enable(ð->q_rx[i].napi); ++ } ++} ++ ++static void airoha_update_hw_stats(struct airoha_gdm_port *port) ++{ ++ struct airoha_eth *eth = port->eth; ++ u32 val, i = 0; ++ ++ spin_lock(&port->stats.lock); ++ u64_stats_update_begin(&port->stats.syncp); ++ ++ /* TX */ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id)); ++ port->stats.tx_ok_pkts += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id)); ++ port->stats.tx_ok_pkts += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id)); ++ port->stats.tx_ok_bytes += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id)); ++ port->stats.tx_ok_bytes += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id)); ++ port->stats.tx_drops += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id)); ++ port->stats.tx_broadcast += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id)); ++ port->stats.tx_multicast += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id)); ++ port->stats.tx_len[i] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ /* RX */ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id)); ++ port->stats.rx_ok_pkts += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id)); ++ port->stats.rx_ok_pkts += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id)); ++ port->stats.rx_ok_bytes += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id)); ++ port->stats.rx_ok_bytes += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id)); ++ port->stats.rx_drops += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id)); ++ port->stats.rx_broadcast += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id)); ++ port->stats.rx_multicast += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id)); ++ port->stats.rx_errors += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id)); ++ port->stats.rx_crc_error += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id)); ++ port->stats.rx_over_errors += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id)); ++ port->stats.rx_fragment += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id)); ++ port->stats.rx_jabber += val; ++ ++ i = 0; ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id)); ++ port->stats.rx_len[i] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ /* reset mib counters */ ++ airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id), ++ FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK); ++ ++ u64_stats_update_end(&port->stats.syncp); ++ spin_unlock(&port->stats.lock); ++} ++ ++static int airoha_dev_open(struct net_device *dev) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_eth *eth = port->eth; ++ int err; ++ ++ netif_tx_start_all_queues(dev); ++ err = airoha_set_gdm_ports(eth, true); ++ if (err) ++ return err; ++ ++ if (netdev_uses_dsa(dev)) ++ airoha_fe_set(eth, REG_GDM_INGRESS_CFG(port->id), ++ GDM_STAG_EN_MASK); ++ else ++ airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id), ++ GDM_STAG_EN_MASK); ++ ++ airoha_qdma_set(eth, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_TX_DMA_EN_MASK); ++ airoha_qdma_set(eth, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_RX_DMA_EN_MASK); ++ ++ return 0; ++} ++ ++static int airoha_dev_stop(struct net_device *dev) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_eth *eth = port->eth; ++ int err; ++ ++ netif_tx_disable(dev); ++ err = airoha_set_gdm_ports(eth, false); ++ if (err) ++ return err; ++ ++ airoha_qdma_clear(eth, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_TX_DMA_EN_MASK); ++ airoha_qdma_clear(eth, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_RX_DMA_EN_MASK); ++ ++ return 0; ++} ++ ++static int airoha_dev_set_macaddr(struct net_device *dev, void *p) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ int err; ++ ++ err = eth_mac_addr(dev, p); ++ if (err) ++ return err; ++ ++ airoha_set_macaddr(port->eth, dev->dev_addr); ++ ++ return 0; ++} ++ ++static int airoha_dev_init(struct net_device *dev) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ ++ airoha_set_macaddr(port->eth, dev->dev_addr); ++ ++ return 0; ++} ++ ++static void airoha_dev_get_stats64(struct net_device *dev, ++ struct rtnl_link_stats64 *storage) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ unsigned int start; ++ ++ airoha_update_hw_stats(port); ++ do { ++ start = u64_stats_fetch_begin(&port->stats.syncp); ++ storage->rx_packets = port->stats.rx_ok_pkts; ++ storage->tx_packets = port->stats.tx_ok_pkts; ++ storage->rx_bytes = port->stats.rx_ok_bytes; ++ storage->tx_bytes = port->stats.tx_ok_bytes; ++ storage->multicast = port->stats.rx_multicast; ++ storage->rx_errors = port->stats.rx_errors; ++ storage->rx_dropped = port->stats.rx_drops; ++ storage->tx_dropped = port->stats.tx_drops; ++ storage->rx_crc_errors = port->stats.rx_crc_error; ++ storage->rx_over_errors = port->stats.rx_over_errors; ++ } while (u64_stats_fetch_retry(&port->stats.syncp, start)); ++} ++ ++static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct skb_shared_info *sinfo = skb_shinfo(skb); ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ u32 msg0 = 0, msg1, len = skb_headlen(skb); ++ int i, qid = skb_get_queue_mapping(skb); ++ struct airoha_eth *eth = port->eth; ++ u32 nr_frags = 1 + sinfo->nr_frags; ++ struct netdev_queue *txq; ++ struct airoha_queue *q; ++ void *data = skb->data; ++ u16 index; ++ u8 fport; ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) | ++ FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) | ++ FIELD_PREP(QDMA_ETH_TXMSG_ICO_MASK, 1); ++ ++ /* TSO: fill MSS info in tcp checksum field */ ++ if (skb_is_gso(skb)) { ++ if (skb_cow_head(skb, 0)) ++ goto error; ++ ++ if (sinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) { ++ __be16 csum = cpu_to_be16(sinfo->gso_size); ++ ++ tcp_hdr(skb)->check = (__force __sum16)csum; ++ msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TSO_MASK, 1); ++ } ++ } ++ ++ fport = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; ++ msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | ++ FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); ++ ++ q = ð->q_tx[qid]; ++ if (WARN_ON_ONCE(!q->ndesc)) ++ goto error; ++ ++ spin_lock_bh(&q->lock); ++ ++ txq = netdev_get_tx_queue(dev, qid); ++ if (q->queued + nr_frags > q->ndesc) { ++ /* not enough space in the queue */ ++ netif_tx_stop_queue(txq); ++ spin_unlock_bh(&q->lock); ++ return NETDEV_TX_BUSY; ++ } ++ ++ index = q->head; ++ for (i = 0; i < nr_frags; i++) { ++ struct airoha_qdma_desc *desc = &q->desc[index]; ++ struct airoha_queue_entry *e = &q->entry[index]; ++ skb_frag_t *frag = &sinfo->frags[i]; ++ dma_addr_t addr; ++ u32 val; ++ ++ addr = dma_map_single(dev->dev.parent, data, len, ++ DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(dev->dev.parent, addr))) ++ goto error_unmap; ++ ++ index = (index + 1) % q->ndesc; ++ ++ val = FIELD_PREP(QDMA_DESC_LEN_MASK, len); ++ if (i < nr_frags - 1) ++ val |= FIELD_PREP(QDMA_DESC_MORE_MASK, 1); ++ WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); ++ WRITE_ONCE(desc->addr, cpu_to_le32(addr)); ++ val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, index); ++ WRITE_ONCE(desc->data, cpu_to_le32(val)); ++ WRITE_ONCE(desc->msg0, cpu_to_le32(msg0)); ++ WRITE_ONCE(desc->msg1, cpu_to_le32(msg1)); ++ WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff)); ++ ++ e->skb = i ? NULL : skb; ++ e->dma_addr = addr; ++ e->dma_len = len; ++ ++ airoha_qdma_rmw(eth, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, ++ FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); ++ ++ data = skb_frag_address(frag); ++ len = skb_frag_size(frag); ++ } ++ ++ q->head = index; ++ q->queued += i; ++ ++ skb_tx_timestamp(skb); ++ if (q->ndesc - q->queued < q->free_thr) ++ netif_tx_stop_queue(txq); ++ ++ spin_unlock_bh(&q->lock); ++ ++ return NETDEV_TX_OK; ++ ++error_unmap: ++ for (i--; i >= 0; i++) ++ dma_unmap_single(dev->dev.parent, q->entry[i].dma_addr, ++ q->entry[i].dma_len, DMA_TO_DEVICE); ++ ++ spin_unlock_bh(&q->lock); ++error: ++ dev_kfree_skb_any(skb); ++ dev->stats.tx_dropped++; ++ ++ return NETDEV_TX_OK; ++} ++ ++static void airoha_ethtool_get_drvinfo(struct net_device *dev, ++ struct ethtool_drvinfo *info) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_eth *eth = port->eth; ++ ++ strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver)); ++ strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info)); ++} ++ ++static void airoha_ethtool_get_mac_stats(struct net_device *dev, ++ struct ethtool_eth_mac_stats *stats) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ unsigned int start; ++ ++ airoha_update_hw_stats(port); ++ do { ++ start = u64_stats_fetch_begin(&port->stats.syncp); ++ stats->MulticastFramesXmittedOK = port->stats.tx_multicast; ++ stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast; ++ stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast; ++ } while (u64_stats_fetch_retry(&port->stats.syncp, start)); ++} ++ ++static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = { ++ { 0, 64 }, ++ { 65, 127 }, ++ { 128, 255 }, ++ { 256, 511 }, ++ { 512, 1023 }, ++ { 1024, 1518 }, ++ { 1519, 10239 }, ++ {}, ++}; ++ ++static void ++airoha_ethtool_get_rmon_stats(struct net_device *dev, ++ struct ethtool_rmon_stats *stats, ++ const struct ethtool_rmon_hist_range **ranges) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_hw_stats *hw_stats = &port->stats; ++ unsigned int start; ++ ++ BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) != ++ ARRAY_SIZE(hw_stats->tx_len) + 1); ++ BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) != ++ ARRAY_SIZE(hw_stats->rx_len) + 1); ++ ++ *ranges = airoha_ethtool_rmon_ranges; ++ airoha_update_hw_stats(port); ++ do { ++ int i; ++ ++ start = u64_stats_fetch_begin(&port->stats.syncp); ++ stats->fragments = hw_stats->rx_fragment; ++ stats->jabbers = hw_stats->rx_jabber; ++ for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1; ++ i++) { ++ stats->hist[i] = hw_stats->rx_len[i]; ++ stats->hist_tx[i] = hw_stats->tx_len[i]; ++ } ++ } while (u64_stats_fetch_retry(&port->stats.syncp, start)); ++} ++ ++static const struct net_device_ops airoha_netdev_ops = { ++ .ndo_init = airoha_dev_init, ++ .ndo_open = airoha_dev_open, ++ .ndo_stop = airoha_dev_stop, ++ .ndo_start_xmit = airoha_dev_xmit, ++ .ndo_get_stats64 = airoha_dev_get_stats64, ++ .ndo_set_mac_address = airoha_dev_set_macaddr, ++}; ++ ++static const struct ethtool_ops airoha_ethtool_ops = { ++ .get_drvinfo = airoha_ethtool_get_drvinfo, ++ .get_eth_mac_stats = airoha_ethtool_get_mac_stats, ++ .get_rmon_stats = airoha_ethtool_get_rmon_stats, ++}; ++ ++static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np) ++{ ++ const __be32 *id_ptr = of_get_property(np, "reg", NULL); ++ struct airoha_gdm_port *port; ++ struct net_device *dev; ++ int err, index; ++ u32 id; ++ ++ if (!id_ptr) { ++ dev_err(eth->dev, "missing gdm port id\n"); ++ return -EINVAL; ++ } ++ ++ id = be32_to_cpup(id_ptr); ++ index = id - 1; ++ ++ if (!id || id > ARRAY_SIZE(eth->ports)) { ++ dev_err(eth->dev, "invalid gdm port id: %d\n", id); ++ return -EINVAL; ++ } ++ ++ if (eth->ports[index]) { ++ dev_err(eth->dev, "duplicate gdm port id: %d\n", id); ++ return -EINVAL; ++ } ++ ++ dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port), ++ AIROHA_NUM_TX_RING, AIROHA_NUM_RX_RING); ++ if (!dev) { ++ dev_err(eth->dev, "alloc_etherdev failed\n"); ++ return -ENOMEM; ++ } ++ ++ dev->netdev_ops = &airoha_netdev_ops; ++ dev->ethtool_ops = &airoha_ethtool_ops; ++ dev->max_mtu = AIROHA_MAX_MTU; ++ dev->watchdog_timeo = 5 * HZ; ++ dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | ++ NETIF_F_TSO6 | NETIF_F_IPV6_CSUM | ++ NETIF_F_SG | NETIF_F_TSO; ++ dev->features |= dev->hw_features; ++ dev->dev.of_node = np; ++ SET_NETDEV_DEV(dev, eth->dev); ++ ++ err = of_get_ethdev_address(np, dev); ++ if (err) { ++ if (err == -EPROBE_DEFER) ++ return err; ++ ++ eth_hw_addr_random(dev); ++ dev_info(eth->dev, "generated random MAC address %pM\n", ++ dev->dev_addr); ++ } ++ ++ port = netdev_priv(dev); ++ u64_stats_init(&port->stats.syncp); ++ spin_lock_init(&port->stats.lock); ++ port->dev = dev; ++ port->eth = eth; ++ port->id = id; ++ eth->ports[index] = port; ++ ++ return register_netdev(dev); ++} ++ ++static int airoha_probe(struct platform_device *pdev) ++{ ++ struct device_node *np; ++ struct airoha_eth *eth; ++ int i, err; ++ ++ eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL); ++ if (!eth) ++ return -ENOMEM; ++ ++ eth->dev = &pdev->dev; ++ ++ err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32)); ++ if (err) { ++ dev_err(eth->dev, "failed configuring DMA mask\n"); ++ return err; ++ } ++ ++ eth->fe_regs = devm_platform_ioremap_resource_byname(pdev, "fe"); ++ if (IS_ERR(eth->fe_regs)) ++ return dev_err_probe(eth->dev, PTR_ERR(eth->fe_regs), ++ "failed to iomap fe regs\n"); ++ ++ eth->qdma_regs = devm_platform_ioremap_resource_byname(pdev, "qdma0"); ++ if (IS_ERR(eth->qdma_regs)) ++ return dev_err_probe(eth->dev, PTR_ERR(eth->qdma_regs), ++ "failed to iomap qdma regs\n"); ++ ++ eth->rsts[0].id = "fe"; ++ eth->rsts[1].id = "pdma"; ++ eth->rsts[2].id = "qdma"; ++ err = devm_reset_control_bulk_get_exclusive(eth->dev, ++ ARRAY_SIZE(eth->rsts), ++ eth->rsts); ++ if (err) { ++ dev_err(eth->dev, "failed to get bulk reset lines\n"); ++ return err; ++ } ++ ++ eth->xsi_rsts[0].id = "xsi-mac"; ++ eth->xsi_rsts[1].id = "hsi0-mac"; ++ eth->xsi_rsts[2].id = "hsi1-mac"; ++ eth->xsi_rsts[3].id = "hsi-mac"; ++ eth->xsi_rsts[4].id = "xfp-mac"; ++ err = devm_reset_control_bulk_get_exclusive(eth->dev, ++ ARRAY_SIZE(eth->xsi_rsts), ++ eth->xsi_rsts); ++ if (err) { ++ dev_err(eth->dev, "failed to get bulk xsi reset lines\n"); ++ return err; ++ } ++ ++ spin_lock_init(ð->irq_lock); ++ eth->irq = platform_get_irq(pdev, 0); ++ if (eth->irq < 0) ++ return eth->irq; ++ ++ eth->napi_dev = alloc_netdev_dummy(0); ++ if (!eth->napi_dev) ++ return -ENOMEM; ++ ++ /* Enable threaded NAPI by default */ ++ eth->napi_dev->threaded = true; ++ strscpy(eth->napi_dev->name, "qdma_eth", sizeof(eth->napi_dev->name)); ++ platform_set_drvdata(pdev, eth); ++ ++ err = airoha_hw_init(eth); ++ if (err) ++ goto error; ++ ++ airoha_qdma_start_napi(eth); ++ for_each_child_of_node(pdev->dev.of_node, np) { ++ if (!of_device_is_compatible(np, "airoha,eth-mac")) ++ continue; ++ ++ if (!of_device_is_available(np)) ++ continue; ++ ++ err = airoha_alloc_gdm_port(eth, np); ++ if (err) { ++ of_node_put(np); ++ goto error; ++ } ++ } ++ ++ return 0; ++ ++error: ++ airoha_hw_cleanup(eth); ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ struct airoha_gdm_port *port = eth->ports[i]; ++ ++ if (port && port->dev->reg_state == NETREG_REGISTERED) ++ unregister_netdev(port->dev); ++ } ++ free_netdev(eth->napi_dev); ++ platform_set_drvdata(pdev, NULL); ++ ++ return err; ++} ++ ++static void airoha_remove(struct platform_device *pdev) ++{ ++ struct airoha_eth *eth = platform_get_drvdata(pdev); ++ int i; ++ ++ airoha_hw_cleanup(eth); ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ struct airoha_gdm_port *port = eth->ports[i]; ++ ++ if (!port) ++ continue; ++ ++ airoha_dev_stop(port->dev); ++ unregister_netdev(port->dev); ++ } ++ free_netdev(eth->napi_dev); ++ ++ platform_set_drvdata(pdev, NULL); ++} ++ ++static const struct of_device_id of_airoha_match[] = { ++ { .compatible = "airoha,en7581-eth" }, ++ { /* sentinel */ } ++}; ++ ++static struct platform_driver airoha_driver = { ++ .probe = airoha_probe, ++ .remove_new = airoha_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = of_airoha_match, ++ }, ++}; ++module_platform_driver(airoha_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Lorenzo Bianconi "); ++MODULE_DESCRIPTION("Ethernet driver for Airoha SoC"); diff --git a/target/linux/airoha/patches-6.6/007-v6.11-net-airoha-fix-error-branch-in-airoha_dev_xmit-and-a.patch b/target/linux/airoha/patches-6.6/007-v6.11-net-airoha-fix-error-branch-in-airoha_dev_xmit-and-a.patch new file mode 100644 index 0000000000..3f2d577403 --- /dev/null +++ b/target/linux/airoha/patches-6.6/007-v6.11-net-airoha-fix-error-branch-in-airoha_dev_xmit-and-a.patch @@ -0,0 +1,46 @@ +From 1f038d5897fe6b439039fc28420842abcc0d126b Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 17 Jul 2024 10:15:46 +0200 +Subject: [PATCH] net: airoha: fix error branch in airoha_dev_xmit and + airoha_set_gdm_ports + +Fix error case management in airoha_dev_xmit routine since we need to +DMA unmap pending buffers starting from q->head. +Moreover fix a typo in error case branch in airoha_set_gdm_ports +routine. + +Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/b628871bc8ae4861b5e2ab4db90aaf373cbb7cee.1721203880.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -977,7 +977,7 @@ static int airoha_set_gdm_ports(struct a + return 0; + + error: +- for (i--; i >= 0; i++) ++ for (i--; i >= 0; i--) + airoha_set_gdm_port(eth, port_list[i], false); + + return err; +@@ -2432,9 +2432,11 @@ static netdev_tx_t airoha_dev_xmit(struc + return NETDEV_TX_OK; + + error_unmap: +- for (i--; i >= 0; i++) +- dma_unmap_single(dev->dev.parent, q->entry[i].dma_addr, +- q->entry[i].dma_len, DMA_TO_DEVICE); ++ for (i--; i >= 0; i--) { ++ index = (q->head + i) % q->ndesc; ++ dma_unmap_single(dev->dev.parent, q->entry[index].dma_addr, ++ q->entry[index].dma_len, DMA_TO_DEVICE); ++ } + + spin_unlock_bh(&q->lock); + error: diff --git a/target/linux/airoha/patches-6.6/008-v6.11-net-airoha-Fix-NULL-pointer-dereference-in-airoha_qd.patch b/target/linux/airoha/patches-6.6/008-v6.11-net-airoha-Fix-NULL-pointer-dereference-in-airoha_qd.patch new file mode 100644 index 0000000000..4c8b361f3d --- /dev/null +++ b/target/linux/airoha/patches-6.6/008-v6.11-net-airoha-Fix-NULL-pointer-dereference-in-airoha_qd.patch @@ -0,0 +1,39 @@ +From 4e076ff6ad5302c015617da30d877b4cdcbdf613 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 17 Jul 2024 10:47:19 +0200 +Subject: [PATCH] net: airoha: Fix NULL pointer dereference in + airoha_qdma_cleanup_rx_queue() + +Move page_pool_get_dma_dir() inside the while loop of +airoha_qdma_cleanup_rx_queue routine in order to avoid possible NULL +pointer dereference if airoha_qdma_init_rx_queue() fails before +properly allocating the page_pool pointer. + +Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/7330a41bba720c33abc039955f6172457a3a34f0.1721205981.git.lorenzo@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -1586,7 +1586,6 @@ static int airoha_qdma_init_rx_queue(str + + static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q) + { +- enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); + struct airoha_eth *eth = q->eth; + + while (q->queued) { +@@ -1594,7 +1593,7 @@ static void airoha_qdma_cleanup_rx_queue + struct page *page = virt_to_head_page(e->buf); + + dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len, +- dir); ++ page_pool_get_dma_dir(q->page_pool)); + page_pool_put_full_page(q->page_pool, page, false); + q->tail = (q->tail + 1) % q->ndesc; + q->queued--; diff --git a/target/linux/airoha/patches-6.6/009-v6.11-net-airoha-Fix-MBI_RX_AGE_SEL_MASK-definition.patch b/target/linux/airoha/patches-6.6/009-v6.11-net-airoha-Fix-MBI_RX_AGE_SEL_MASK-definition.patch new file mode 100644 index 0000000000..15385beced --- /dev/null +++ b/target/linux/airoha/patches-6.6/009-v6.11-net-airoha-Fix-MBI_RX_AGE_SEL_MASK-definition.patch @@ -0,0 +1,27 @@ +From 39a9c25bcdfb5e88995841c47439b74cac74a527 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 19 Jul 2024 22:38:31 +0200 +Subject: [PATCH] net: airoha: Fix MBI_RX_AGE_SEL_MASK definition + +Fix copy-paste error in MBI_RX_AGE_SEL_MASK macro definition + +Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/d27d0465be1bff3369e886e5f10c4d37fefc4934.1721419930.git.lorenzo@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -249,7 +249,7 @@ + #define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2fc) + + #define REG_GDM2_CHN_RLS (GDM2_BASE + 0x20) +-#define MBI_RX_AGE_SEL_MASK GENMASK(18, 17) ++#define MBI_RX_AGE_SEL_MASK GENMASK(26, 25) + #define MBI_TX_AGE_SEL_MASK GENMASK(18, 17) + + #define REG_GDM3_FWD_CFG GDM3_BASE diff --git a/target/linux/airoha/patches-6.6/010-01-v6.12-net-airoha-Introduce-airoha_qdma-struct.patch b/target/linux/airoha/patches-6.6/010-01-v6.12-net-airoha-Introduce-airoha_qdma-struct.patch new file mode 100644 index 0000000000..3649e1c843 --- /dev/null +++ b/target/linux/airoha/patches-6.6/010-01-v6.12-net-airoha-Introduce-airoha_qdma-struct.patch @@ -0,0 +1,553 @@ +From 16874d1cf3818a5804cded8eaff634122b1d6c7c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 1 Aug 2024 16:35:03 +0200 +Subject: [PATCH 1/8] net: airoha: Introduce airoha_qdma struct + +Introduce airoha_qdma struct and move qdma IO register mapping in +airoha_qdma. This is a preliminary patch to enable both QDMA controllers +available on EN7581 SoC. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/7df163bdc72ee29c3d27a0cbf54522ffeeafe53c.1722522582.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 197 ++++++++++++--------- + 1 file changed, 112 insertions(+), 85 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -18,6 +18,7 @@ + #include + + #define AIROHA_MAX_NUM_GDM_PORTS 1 ++#define AIROHA_MAX_NUM_QDMA 1 + #define AIROHA_MAX_NUM_RSTS 3 + #define AIROHA_MAX_NUM_XSI_RSTS 5 + #define AIROHA_MAX_MTU 2000 +@@ -782,6 +783,10 @@ struct airoha_hw_stats { + u64 rx_len[7]; + }; + ++struct airoha_qdma { ++ void __iomem *regs; ++}; ++ + struct airoha_gdm_port { + struct net_device *dev; + struct airoha_eth *eth; +@@ -794,8 +799,6 @@ struct airoha_eth { + struct device *dev; + + unsigned long state; +- +- void __iomem *qdma_regs; + void __iomem *fe_regs; + + /* protect concurrent irqmask accesses */ +@@ -806,6 +809,7 @@ struct airoha_eth { + struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; + struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; + ++ struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA]; + struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; + + struct net_device *napi_dev; +@@ -850,16 +854,16 @@ static u32 airoha_rmw(void __iomem *base + #define airoha_fe_clear(eth, offset, val) \ + airoha_rmw((eth)->fe_regs, (offset), (val), 0) + +-#define airoha_qdma_rr(eth, offset) \ +- airoha_rr((eth)->qdma_regs, (offset)) +-#define airoha_qdma_wr(eth, offset, val) \ +- airoha_wr((eth)->qdma_regs, (offset), (val)) +-#define airoha_qdma_rmw(eth, offset, mask, val) \ +- airoha_rmw((eth)->qdma_regs, (offset), (mask), (val)) +-#define airoha_qdma_set(eth, offset, val) \ +- airoha_rmw((eth)->qdma_regs, (offset), 0, (val)) +-#define airoha_qdma_clear(eth, offset, val) \ +- airoha_rmw((eth)->qdma_regs, (offset), (val), 0) ++#define airoha_qdma_rr(qdma, offset) \ ++ airoha_rr((qdma)->regs, (offset)) ++#define airoha_qdma_wr(qdma, offset, val) \ ++ airoha_wr((qdma)->regs, (offset), (val)) ++#define airoha_qdma_rmw(qdma, offset, mask, val) \ ++ airoha_rmw((qdma)->regs, (offset), (mask), (val)) ++#define airoha_qdma_set(qdma, offset, val) \ ++ airoha_rmw((qdma)->regs, (offset), 0, (val)) ++#define airoha_qdma_clear(qdma, offset, val) \ ++ airoha_rmw((qdma)->regs, (offset), (val), 0) + + static void airoha_qdma_set_irqmask(struct airoha_eth *eth, int index, + u32 clear, u32 set) +@@ -873,11 +877,12 @@ static void airoha_qdma_set_irqmask(stru + + eth->irqmask[index] &= ~clear; + eth->irqmask[index] |= set; +- airoha_qdma_wr(eth, REG_INT_ENABLE(index), eth->irqmask[index]); ++ airoha_qdma_wr(ð->qdma[0], REG_INT_ENABLE(index), ++ eth->irqmask[index]); + /* Read irq_enable register in order to guarantee the update above + * completes in the spinlock critical section. + */ +- airoha_qdma_rr(eth, REG_INT_ENABLE(index)); ++ airoha_qdma_rr(ð->qdma[0], REG_INT_ENABLE(index)); + + spin_unlock_irqrestore(ð->irq_lock, flags); + } +@@ -1383,6 +1388,7 @@ static int airoha_fe_init(struct airoha_ + static int airoha_qdma_fill_rx_queue(struct airoha_queue *q) + { + enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); ++ struct airoha_qdma *qdma = &q->eth->qdma[0]; + struct airoha_eth *eth = q->eth; + int qid = q - ð->q_rx[0]; + int nframes = 0; +@@ -1420,7 +1426,8 @@ static int airoha_qdma_fill_rx_queue(str + WRITE_ONCE(desc->msg2, 0); + WRITE_ONCE(desc->msg3, 0); + +- airoha_qdma_rmw(eth, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK, ++ airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), ++ RX_RING_CPU_IDX_MASK, + FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head)); + } + +@@ -1529,7 +1536,8 @@ static int airoha_qdma_rx_napi_poll(stru + } + + static int airoha_qdma_init_rx_queue(struct airoha_eth *eth, +- struct airoha_queue *q, int ndesc) ++ struct airoha_queue *q, ++ struct airoha_qdma *qdma, int ndesc) + { + const struct page_pool_params pp_params = { + .order = 0, +@@ -1569,14 +1577,15 @@ static int airoha_qdma_init_rx_queue(str + + netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll); + +- airoha_qdma_wr(eth, REG_RX_RING_BASE(qid), dma_addr); +- airoha_qdma_rmw(eth, REG_RX_RING_SIZE(qid), RX_RING_SIZE_MASK, ++ airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr); ++ airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), ++ RX_RING_SIZE_MASK, + FIELD_PREP(RX_RING_SIZE_MASK, ndesc)); + + thr = clamp(ndesc >> 3, 1, 32); +- airoha_qdma_rmw(eth, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK, ++ airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK, + FIELD_PREP(RX_RING_THR_MASK, thr)); +- airoha_qdma_rmw(eth, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, ++ airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, + FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head)); + + airoha_qdma_fill_rx_queue(q); +@@ -1600,7 +1609,8 @@ static void airoha_qdma_cleanup_rx_queue + } + } + +-static int airoha_qdma_init_rx(struct airoha_eth *eth) ++static int airoha_qdma_init_rx(struct airoha_eth *eth, ++ struct airoha_qdma *qdma) + { + int i; + +@@ -1613,7 +1623,7 @@ static int airoha_qdma_init_rx(struct ai + } + + err = airoha_qdma_init_rx_queue(eth, ð->q_rx[i], +- RX_DSCP_NUM(i)); ++ qdma, RX_DSCP_NUM(i)); + if (err) + return err; + } +@@ -1624,11 +1634,13 @@ static int airoha_qdma_init_rx(struct ai + static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) + { + struct airoha_tx_irq_queue *irq_q; ++ struct airoha_qdma *qdma; + struct airoha_eth *eth; + int id, done = 0; + + irq_q = container_of(napi, struct airoha_tx_irq_queue, napi); + eth = irq_q->eth; ++ qdma = ð->qdma[0]; + id = irq_q - ð->q_tx_irq[0]; + + while (irq_q->queued > 0 && done < budget) { +@@ -1698,9 +1710,9 @@ static int airoha_qdma_tx_napi_poll(stru + int i, len = done >> 7; + + for (i = 0; i < len; i++) +- airoha_qdma_rmw(eth, REG_IRQ_CLEAR_LEN(id), ++ airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id), + IRQ_CLEAR_LEN_MASK, 0x80); +- airoha_qdma_rmw(eth, REG_IRQ_CLEAR_LEN(id), ++ airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id), + IRQ_CLEAR_LEN_MASK, (done & 0x7f)); + } + +@@ -1712,7 +1724,8 @@ static int airoha_qdma_tx_napi_poll(stru + } + + static int airoha_qdma_init_tx_queue(struct airoha_eth *eth, +- struct airoha_queue *q, int size) ++ struct airoha_queue *q, ++ struct airoha_qdma *qdma, int size) + { + int i, qid = q - ð->q_tx[0]; + dma_addr_t dma_addr; +@@ -1739,10 +1752,10 @@ static int airoha_qdma_init_tx_queue(str + WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val)); + } + +- airoha_qdma_wr(eth, REG_TX_RING_BASE(qid), dma_addr); +- airoha_qdma_rmw(eth, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, ++ airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr); ++ airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); +- airoha_qdma_rmw(eth, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, ++ airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, + FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head)); + + return 0; +@@ -1750,7 +1763,7 @@ static int airoha_qdma_init_tx_queue(str + + static int airoha_qdma_tx_irq_init(struct airoha_eth *eth, + struct airoha_tx_irq_queue *irq_q, +- int size) ++ struct airoha_qdma *qdma, int size) + { + int id = irq_q - ð->q_tx_irq[0]; + dma_addr_t dma_addr; +@@ -1766,29 +1779,30 @@ static int airoha_qdma_tx_irq_init(struc + irq_q->size = size; + irq_q->eth = eth; + +- airoha_qdma_wr(eth, REG_TX_IRQ_BASE(id), dma_addr); +- airoha_qdma_rmw(eth, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, ++ airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr); ++ airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, + FIELD_PREP(TX_IRQ_DEPTH_MASK, size)); +- airoha_qdma_rmw(eth, REG_TX_IRQ_CFG(id), TX_IRQ_THR_MASK, ++ airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_THR_MASK, + FIELD_PREP(TX_IRQ_THR_MASK, 1)); + + return 0; + } + +-static int airoha_qdma_init_tx(struct airoha_eth *eth) ++static int airoha_qdma_init_tx(struct airoha_eth *eth, ++ struct airoha_qdma *qdma) + { + int i, err; + + for (i = 0; i < ARRAY_SIZE(eth->q_tx_irq); i++) { + err = airoha_qdma_tx_irq_init(eth, ð->q_tx_irq[i], +- IRQ_QUEUE_LEN(i)); ++ qdma, IRQ_QUEUE_LEN(i)); + if (err) + return err; + } + + for (i = 0; i < ARRAY_SIZE(eth->q_tx); i++) { + err = airoha_qdma_init_tx_queue(eth, ð->q_tx[i], +- TX_DSCP_NUM); ++ qdma, TX_DSCP_NUM); + if (err) + return err; + } +@@ -1815,7 +1829,8 @@ static void airoha_qdma_cleanup_tx_queue + spin_unlock_bh(&q->lock); + } + +-static int airoha_qdma_init_hfwd_queues(struct airoha_eth *eth) ++static int airoha_qdma_init_hfwd_queues(struct airoha_eth *eth, ++ struct airoha_qdma *qdma) + { + dma_addr_t dma_addr; + u32 status; +@@ -1827,7 +1842,7 @@ static int airoha_qdma_init_hfwd_queues( + if (!eth->hfwd.desc) + return -ENOMEM; + +- airoha_qdma_wr(eth, REG_FWD_DSCP_BASE, dma_addr); ++ airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); + + size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; + eth->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr, +@@ -1835,14 +1850,14 @@ static int airoha_qdma_init_hfwd_queues( + if (!eth->hfwd.q) + return -ENOMEM; + +- airoha_qdma_wr(eth, REG_FWD_BUF_BASE, dma_addr); ++ airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); + +- airoha_qdma_rmw(eth, REG_HW_FWD_DSCP_CFG, ++ airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, + HW_FWD_DSCP_PAYLOAD_SIZE_MASK, + FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0)); +- airoha_qdma_rmw(eth, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK, ++ airoha_qdma_rmw(qdma, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK, + FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128)); +- airoha_qdma_rmw(eth, REG_LMGR_INIT_CFG, ++ airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, + LMGR_INIT_START | LMGR_SRAM_MODE_MASK | + HW_FWD_DESC_NUM_MASK, + FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | +@@ -1850,67 +1865,69 @@ static int airoha_qdma_init_hfwd_queues( + + return read_poll_timeout(airoha_qdma_rr, status, + !(status & LMGR_INIT_START), USEC_PER_MSEC, +- 30 * USEC_PER_MSEC, true, eth, ++ 30 * USEC_PER_MSEC, true, qdma, + REG_LMGR_INIT_CFG); + } + +-static void airoha_qdma_init_qos(struct airoha_eth *eth) ++static void airoha_qdma_init_qos(struct airoha_eth *eth, ++ struct airoha_qdma *qdma) + { +- airoha_qdma_clear(eth, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_SCALE_MASK); +- airoha_qdma_set(eth, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_BASE_MASK); ++ airoha_qdma_clear(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_SCALE_MASK); ++ airoha_qdma_set(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_BASE_MASK); + +- airoha_qdma_clear(eth, REG_PSE_BUF_USAGE_CFG, ++ airoha_qdma_clear(qdma, REG_PSE_BUF_USAGE_CFG, + PSE_BUF_ESTIMATE_EN_MASK); + +- airoha_qdma_set(eth, REG_EGRESS_RATE_METER_CFG, ++ airoha_qdma_set(qdma, REG_EGRESS_RATE_METER_CFG, + EGRESS_RATE_METER_EN_MASK | + EGRESS_RATE_METER_EQ_RATE_EN_MASK); + /* 2047us x 31 = 63.457ms */ +- airoha_qdma_rmw(eth, REG_EGRESS_RATE_METER_CFG, ++ airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG, + EGRESS_RATE_METER_WINDOW_SZ_MASK, + FIELD_PREP(EGRESS_RATE_METER_WINDOW_SZ_MASK, 0x1f)); +- airoha_qdma_rmw(eth, REG_EGRESS_RATE_METER_CFG, ++ airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG, + EGRESS_RATE_METER_TIMESLICE_MASK, + FIELD_PREP(EGRESS_RATE_METER_TIMESLICE_MASK, 0x7ff)); + + /* ratelimit init */ +- airoha_qdma_set(eth, REG_GLB_TRTCM_CFG, GLB_TRTCM_EN_MASK); ++ airoha_qdma_set(qdma, REG_GLB_TRTCM_CFG, GLB_TRTCM_EN_MASK); + /* fast-tick 25us */ +- airoha_qdma_rmw(eth, REG_GLB_TRTCM_CFG, GLB_FAST_TICK_MASK, ++ airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_FAST_TICK_MASK, + FIELD_PREP(GLB_FAST_TICK_MASK, 25)); +- airoha_qdma_rmw(eth, REG_GLB_TRTCM_CFG, GLB_SLOW_TICK_RATIO_MASK, ++ airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_SLOW_TICK_RATIO_MASK, + FIELD_PREP(GLB_SLOW_TICK_RATIO_MASK, 40)); + +- airoha_qdma_set(eth, REG_EGRESS_TRTCM_CFG, EGRESS_TRTCM_EN_MASK); +- airoha_qdma_rmw(eth, REG_EGRESS_TRTCM_CFG, EGRESS_FAST_TICK_MASK, ++ airoha_qdma_set(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_TRTCM_EN_MASK); ++ airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_FAST_TICK_MASK, + FIELD_PREP(EGRESS_FAST_TICK_MASK, 25)); +- airoha_qdma_rmw(eth, REG_EGRESS_TRTCM_CFG, ++ airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG, + EGRESS_SLOW_TICK_RATIO_MASK, + FIELD_PREP(EGRESS_SLOW_TICK_RATIO_MASK, 40)); + +- airoha_qdma_set(eth, REG_INGRESS_TRTCM_CFG, INGRESS_TRTCM_EN_MASK); +- airoha_qdma_clear(eth, REG_INGRESS_TRTCM_CFG, ++ airoha_qdma_set(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_TRTCM_EN_MASK); ++ airoha_qdma_clear(qdma, REG_INGRESS_TRTCM_CFG, + INGRESS_TRTCM_MODE_MASK); +- airoha_qdma_rmw(eth, REG_INGRESS_TRTCM_CFG, INGRESS_FAST_TICK_MASK, ++ airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_FAST_TICK_MASK, + FIELD_PREP(INGRESS_FAST_TICK_MASK, 125)); +- airoha_qdma_rmw(eth, REG_INGRESS_TRTCM_CFG, ++ airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG, + INGRESS_SLOW_TICK_RATIO_MASK, + FIELD_PREP(INGRESS_SLOW_TICK_RATIO_MASK, 8)); + +- airoha_qdma_set(eth, REG_SLA_TRTCM_CFG, SLA_TRTCM_EN_MASK); +- airoha_qdma_rmw(eth, REG_SLA_TRTCM_CFG, SLA_FAST_TICK_MASK, ++ airoha_qdma_set(qdma, REG_SLA_TRTCM_CFG, SLA_TRTCM_EN_MASK); ++ airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_FAST_TICK_MASK, + FIELD_PREP(SLA_FAST_TICK_MASK, 25)); +- airoha_qdma_rmw(eth, REG_SLA_TRTCM_CFG, SLA_SLOW_TICK_RATIO_MASK, ++ airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_SLOW_TICK_RATIO_MASK, + FIELD_PREP(SLA_SLOW_TICK_RATIO_MASK, 40)); + } + +-static int airoha_qdma_hw_init(struct airoha_eth *eth) ++static int airoha_qdma_hw_init(struct airoha_eth *eth, ++ struct airoha_qdma *qdma) + { + int i; + + /* clear pending irqs */ + for (i = 0; i < ARRAY_SIZE(eth->irqmask); i++) +- airoha_qdma_wr(eth, REG_INT_STATUS(i), 0xffffffff); ++ airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); + + /* setup irqs */ + airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX0, INT_IDX0_MASK); +@@ -1923,14 +1940,14 @@ static int airoha_qdma_hw_init(struct ai + continue; + + if (TX_RING_IRQ_BLOCKING_MAP_MASK & BIT(i)) +- airoha_qdma_set(eth, REG_TX_RING_BLOCKING(i), ++ airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(i), + TX_RING_IRQ_BLOCKING_CFG_MASK); + else +- airoha_qdma_clear(eth, REG_TX_RING_BLOCKING(i), ++ airoha_qdma_clear(qdma, REG_TX_RING_BLOCKING(i), + TX_RING_IRQ_BLOCKING_CFG_MASK); + } + +- airoha_qdma_wr(eth, REG_QDMA_GLOBAL_CFG, ++ airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_RX_2B_OFFSET_MASK | + FIELD_PREP(GLOBAL_CFG_DMA_PREFERENCE_MASK, 3) | + GLOBAL_CFG_CPU_TXR_RR_MASK | +@@ -1941,18 +1958,18 @@ static int airoha_qdma_hw_init(struct ai + GLOBAL_CFG_TX_WB_DONE_MASK | + FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 2)); + +- airoha_qdma_init_qos(eth); ++ airoha_qdma_init_qos(eth, qdma); + + /* disable qdma rx delay interrupt */ + for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { + if (!eth->q_rx[i].ndesc) + continue; + +- airoha_qdma_clear(eth, REG_RX_DELAY_INT_IDX(i), ++ airoha_qdma_clear(qdma, REG_RX_DELAY_INT_IDX(i), + RX_DELAY_INT_MASK); + } + +- airoha_qdma_set(eth, REG_TXQ_CNGST_CFG, ++ airoha_qdma_set(qdma, REG_TXQ_CNGST_CFG, + TXQ_CNGST_DROP_EN | TXQ_CNGST_DEI_DROP_EN); + + return 0; +@@ -1962,12 +1979,14 @@ static irqreturn_t airoha_irq_handler(in + { + struct airoha_eth *eth = dev_instance; + u32 intr[ARRAY_SIZE(eth->irqmask)]; ++ struct airoha_qdma *qdma; + int i; + ++ qdma = ð->qdma[0]; + for (i = 0; i < ARRAY_SIZE(eth->irqmask); i++) { +- intr[i] = airoha_qdma_rr(eth, REG_INT_STATUS(i)); ++ intr[i] = airoha_qdma_rr(qdma, REG_INT_STATUS(i)); + intr[i] &= eth->irqmask[i]; +- airoha_qdma_wr(eth, REG_INT_STATUS(i), intr[i]); ++ airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]); + } + + if (!test_bit(DEV_STATE_INITIALIZED, ð->state)) +@@ -1997,7 +2016,7 @@ static irqreturn_t airoha_irq_handler(in + airoha_qdma_irq_disable(eth, QDMA_INT_REG_IDX0, + TX_DONE_INT_MASK(i)); + +- status = airoha_qdma_rr(eth, REG_IRQ_STATUS(i)); ++ status = airoha_qdma_rr(qdma, REG_IRQ_STATUS(i)); + head = FIELD_GET(IRQ_HEAD_IDX_MASK, status); + irq_q->head = head % irq_q->size; + irq_q->queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status); +@@ -2011,6 +2030,7 @@ static irqreturn_t airoha_irq_handler(in + + static int airoha_qdma_init(struct airoha_eth *eth) + { ++ struct airoha_qdma *qdma = ð->qdma[0]; + int err; + + err = devm_request_irq(eth->dev, eth->irq, airoha_irq_handler, +@@ -2018,19 +2038,19 @@ static int airoha_qdma_init(struct airoh + if (err) + return err; + +- err = airoha_qdma_init_rx(eth); ++ err = airoha_qdma_init_rx(eth, qdma); + if (err) + return err; + +- err = airoha_qdma_init_tx(eth); ++ err = airoha_qdma_init_tx(eth, qdma); + if (err) + return err; + +- err = airoha_qdma_init_hfwd_queues(eth); ++ err = airoha_qdma_init_hfwd_queues(eth, qdma); + if (err) + return err; + +- err = airoha_qdma_hw_init(eth); ++ err = airoha_qdma_hw_init(eth, qdma); + if (err) + return err; + +@@ -2263,8 +2283,9 @@ static int airoha_dev_open(struct net_de + airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id), + GDM_STAG_EN_MASK); + +- airoha_qdma_set(eth, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_TX_DMA_EN_MASK); +- airoha_qdma_set(eth, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_RX_DMA_EN_MASK); ++ airoha_qdma_set(ð->qdma[0], REG_QDMA_GLOBAL_CFG, ++ GLOBAL_CFG_TX_DMA_EN_MASK | ++ GLOBAL_CFG_RX_DMA_EN_MASK); + + return 0; + } +@@ -2280,8 +2301,9 @@ static int airoha_dev_stop(struct net_de + if (err) + return err; + +- airoha_qdma_clear(eth, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_TX_DMA_EN_MASK); +- airoha_qdma_clear(eth, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_RX_DMA_EN_MASK); ++ airoha_qdma_clear(ð->qdma[0], REG_QDMA_GLOBAL_CFG, ++ GLOBAL_CFG_TX_DMA_EN_MASK | ++ GLOBAL_CFG_RX_DMA_EN_MASK); + + return 0; + } +@@ -2341,6 +2363,7 @@ static netdev_tx_t airoha_dev_xmit(struc + struct airoha_eth *eth = port->eth; + u32 nr_frags = 1 + sinfo->nr_frags; + struct netdev_queue *txq; ++ struct airoha_qdma *qdma; + struct airoha_queue *q; + void *data = skb->data; + u16 index; +@@ -2368,6 +2391,7 @@ static netdev_tx_t airoha_dev_xmit(struc + msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | + FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); + ++ qdma = ð->qdma[0]; + q = ð->q_tx[qid]; + if (WARN_ON_ONCE(!q->ndesc)) + goto error; +@@ -2412,7 +2436,8 @@ static netdev_tx_t airoha_dev_xmit(struc + e->dma_addr = addr; + e->dma_len = len; + +- airoha_qdma_rmw(eth, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, ++ airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), ++ TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); + + data = skb_frag_address(frag); +@@ -2614,9 +2639,11 @@ static int airoha_probe(struct platform_ + return dev_err_probe(eth->dev, PTR_ERR(eth->fe_regs), + "failed to iomap fe regs\n"); + +- eth->qdma_regs = devm_platform_ioremap_resource_byname(pdev, "qdma0"); +- if (IS_ERR(eth->qdma_regs)) +- return dev_err_probe(eth->dev, PTR_ERR(eth->qdma_regs), ++ eth->qdma[0].regs = devm_platform_ioremap_resource_byname(pdev, ++ "qdma0"); ++ if (IS_ERR(eth->qdma[0].regs)) ++ return dev_err_probe(eth->dev, ++ PTR_ERR(eth->qdma[0].regs), + "failed to iomap qdma regs\n"); + + eth->rsts[0].id = "fe"; diff --git a/target/linux/airoha/patches-6.6/010-02-v6.12-net-airoha-Move-airoha_queues-in-airoha_qdma.patch b/target/linux/airoha/patches-6.6/010-02-v6.12-net-airoha-Move-airoha_queues-in-airoha_qdma.patch new file mode 100644 index 0000000000..853a785180 --- /dev/null +++ b/target/linux/airoha/patches-6.6/010-02-v6.12-net-airoha-Move-airoha_queues-in-airoha_qdma.patch @@ -0,0 +1,318 @@ +From 245c7bc86b198e5ec227eba6b582da73cb0721c8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 1 Aug 2024 16:35:04 +0200 +Subject: [PATCH 2/8] net: airoha: Move airoha_queues in airoha_qdma + +QDMA controllers available in EN7581 SoC have independent tx/rx hw queues +so move them in airoha_queues structure. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/795fc4797bffbf7f0a1351308aa9bf0e65b5126e.1722522582.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 126 +++++++++++---------- + 1 file changed, 65 insertions(+), 61 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -785,6 +785,17 @@ struct airoha_hw_stats { + + struct airoha_qdma { + void __iomem *regs; ++ ++ struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; ++ ++ struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; ++ struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; ++ ++ /* descriptor and packet buffers for qdma hw forward */ ++ struct { ++ void *desc; ++ void *q; ++ } hfwd; + }; + + struct airoha_gdm_port { +@@ -809,20 +820,10 @@ struct airoha_eth { + struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; + struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; + +- struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA]; +- struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; +- + struct net_device *napi_dev; +- struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; +- struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; +- +- struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; + +- /* descriptor and packet buffers for qdma hw forward */ +- struct { +- void *desc; +- void *q; +- } hfwd; ++ struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA]; ++ struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; + }; + + static u32 airoha_rr(void __iomem *base, u32 offset) +@@ -1390,7 +1391,7 @@ static int airoha_qdma_fill_rx_queue(str + enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); + struct airoha_qdma *qdma = &q->eth->qdma[0]; + struct airoha_eth *eth = q->eth; +- int qid = q - ð->q_rx[0]; ++ int qid = q - &qdma->q_rx[0]; + int nframes = 0; + + while (q->queued < q->ndesc - 1) { +@@ -1457,8 +1458,9 @@ static int airoha_qdma_get_gdm_port(stru + static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) + { + enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); ++ struct airoha_qdma *qdma = &q->eth->qdma[0]; + struct airoha_eth *eth = q->eth; +- int qid = q - ð->q_rx[0]; ++ int qid = q - &qdma->q_rx[0]; + int done = 0; + + while (done < budget) { +@@ -1550,7 +1552,7 @@ static int airoha_qdma_init_rx_queue(str + .dev = eth->dev, + .napi = &q->napi, + }; +- int qid = q - ð->q_rx[0], thr; ++ int qid = q - &qdma->q_rx[0], thr; + dma_addr_t dma_addr; + + q->buf_size = PAGE_SIZE / 2; +@@ -1614,7 +1616,7 @@ static int airoha_qdma_init_rx(struct ai + { + int i; + +- for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { + int err; + + if (!(RX_DONE_INT_MASK & BIT(i))) { +@@ -1622,7 +1624,7 @@ static int airoha_qdma_init_rx(struct ai + continue; + } + +- err = airoha_qdma_init_rx_queue(eth, ð->q_rx[i], ++ err = airoha_qdma_init_rx_queue(eth, &qdma->q_rx[i], + qdma, RX_DSCP_NUM(i)); + if (err) + return err; +@@ -1641,7 +1643,7 @@ static int airoha_qdma_tx_napi_poll(stru + irq_q = container_of(napi, struct airoha_tx_irq_queue, napi); + eth = irq_q->eth; + qdma = ð->qdma[0]; +- id = irq_q - ð->q_tx_irq[0]; ++ id = irq_q - &qdma->q_tx_irq[0]; + + while (irq_q->queued > 0 && done < budget) { + u32 qid, last, val = irq_q->q[irq_q->head]; +@@ -1658,10 +1660,10 @@ static int airoha_qdma_tx_napi_poll(stru + last = FIELD_GET(IRQ_DESC_IDX_MASK, val); + qid = FIELD_GET(IRQ_RING_IDX_MASK, val); + +- if (qid >= ARRAY_SIZE(eth->q_tx)) ++ if (qid >= ARRAY_SIZE(qdma->q_tx)) + continue; + +- q = ð->q_tx[qid]; ++ q = &qdma->q_tx[qid]; + if (!q->ndesc) + continue; + +@@ -1727,7 +1729,7 @@ static int airoha_qdma_init_tx_queue(str + struct airoha_queue *q, + struct airoha_qdma *qdma, int size) + { +- int i, qid = q - ð->q_tx[0]; ++ int i, qid = q - &qdma->q_tx[0]; + dma_addr_t dma_addr; + + spin_lock_init(&q->lock); +@@ -1765,7 +1767,7 @@ static int airoha_qdma_tx_irq_init(struc + struct airoha_tx_irq_queue *irq_q, + struct airoha_qdma *qdma, int size) + { +- int id = irq_q - ð->q_tx_irq[0]; ++ int id = irq_q - &qdma->q_tx_irq[0]; + dma_addr_t dma_addr; + + netif_napi_add_tx(eth->napi_dev, &irq_q->napi, +@@ -1793,15 +1795,15 @@ static int airoha_qdma_init_tx(struct ai + { + int i, err; + +- for (i = 0; i < ARRAY_SIZE(eth->q_tx_irq); i++) { +- err = airoha_qdma_tx_irq_init(eth, ð->q_tx_irq[i], ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { ++ err = airoha_qdma_tx_irq_init(eth, &qdma->q_tx_irq[i], + qdma, IRQ_QUEUE_LEN(i)); + if (err) + return err; + } + +- for (i = 0; i < ARRAY_SIZE(eth->q_tx); i++) { +- err = airoha_qdma_init_tx_queue(eth, ð->q_tx[i], ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { ++ err = airoha_qdma_init_tx_queue(eth, &qdma->q_tx[i], + qdma, TX_DSCP_NUM); + if (err) + return err; +@@ -1837,17 +1839,17 @@ static int airoha_qdma_init_hfwd_queues( + int size; + + size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); +- eth->hfwd.desc = dmam_alloc_coherent(eth->dev, size, &dma_addr, +- GFP_KERNEL); +- if (!eth->hfwd.desc) ++ qdma->hfwd.desc = dmam_alloc_coherent(eth->dev, size, &dma_addr, ++ GFP_KERNEL); ++ if (!qdma->hfwd.desc) + return -ENOMEM; + + airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); + + size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; +- eth->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr, +- GFP_KERNEL); +- if (!eth->hfwd.q) ++ qdma->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr, ++ GFP_KERNEL); ++ if (!qdma->hfwd.q) + return -ENOMEM; + + airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); +@@ -1935,8 +1937,8 @@ static int airoha_qdma_hw_init(struct ai + airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX4, INT_IDX4_MASK); + + /* setup irq binding */ +- for (i = 0; i < ARRAY_SIZE(eth->q_tx); i++) { +- if (!eth->q_tx[i].ndesc) ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { ++ if (!qdma->q_tx[i].ndesc) + continue; + + if (TX_RING_IRQ_BLOCKING_MAP_MASK & BIT(i)) +@@ -1961,8 +1963,8 @@ static int airoha_qdma_hw_init(struct ai + airoha_qdma_init_qos(eth, qdma); + + /* disable qdma rx delay interrupt */ +- for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { +- if (!eth->q_rx[i].ndesc) ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) + continue; + + airoha_qdma_clear(qdma, REG_RX_DELAY_INT_IDX(i), +@@ -1996,18 +1998,18 @@ static irqreturn_t airoha_irq_handler(in + airoha_qdma_irq_disable(eth, QDMA_INT_REG_IDX1, + RX_DONE_INT_MASK); + +- for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { +- if (!eth->q_rx[i].ndesc) ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) + continue; + + if (intr[1] & BIT(i)) +- napi_schedule(ð->q_rx[i].napi); ++ napi_schedule(&qdma->q_rx[i].napi); + } + } + + if (intr[0] & INT_TX_MASK) { +- for (i = 0; i < ARRAY_SIZE(eth->q_tx_irq); i++) { +- struct airoha_tx_irq_queue *irq_q = ð->q_tx_irq[i]; ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { ++ struct airoha_tx_irq_queue *irq_q = &qdma->q_tx_irq[i]; + u32 status, head; + + if (!(intr[0] & TX_DONE_INT_MASK(i))) +@@ -2021,7 +2023,7 @@ static irqreturn_t airoha_irq_handler(in + irq_q->head = head % irq_q->size; + irq_q->queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status); + +- napi_schedule(ð->q_tx_irq[i].napi); ++ napi_schedule(&qdma->q_tx_irq[i].napi); + } + } + +@@ -2080,44 +2082,46 @@ static int airoha_hw_init(struct airoha_ + + static void airoha_hw_cleanup(struct airoha_eth *eth) + { ++ struct airoha_qdma *qdma = ð->qdma[0]; + int i; + +- for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { +- if (!eth->q_rx[i].ndesc) ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) + continue; + +- napi_disable(ð->q_rx[i].napi); +- netif_napi_del(ð->q_rx[i].napi); +- airoha_qdma_cleanup_rx_queue(ð->q_rx[i]); +- if (eth->q_rx[i].page_pool) +- page_pool_destroy(eth->q_rx[i].page_pool); ++ napi_disable(&qdma->q_rx[i].napi); ++ netif_napi_del(&qdma->q_rx[i].napi); ++ airoha_qdma_cleanup_rx_queue(&qdma->q_rx[i]); ++ if (qdma->q_rx[i].page_pool) ++ page_pool_destroy(qdma->q_rx[i].page_pool); + } + +- for (i = 0; i < ARRAY_SIZE(eth->q_tx_irq); i++) { +- napi_disable(ð->q_tx_irq[i].napi); +- netif_napi_del(ð->q_tx_irq[i].napi); ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { ++ napi_disable(&qdma->q_tx_irq[i].napi); ++ netif_napi_del(&qdma->q_tx_irq[i].napi); + } + +- for (i = 0; i < ARRAY_SIZE(eth->q_tx); i++) { +- if (!eth->q_tx[i].ndesc) ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { ++ if (!qdma->q_tx[i].ndesc) + continue; + +- airoha_qdma_cleanup_tx_queue(ð->q_tx[i]); ++ airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); + } + } + + static void airoha_qdma_start_napi(struct airoha_eth *eth) + { ++ struct airoha_qdma *qdma = ð->qdma[0]; + int i; + +- for (i = 0; i < ARRAY_SIZE(eth->q_tx_irq); i++) +- napi_enable(ð->q_tx_irq[i].napi); ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) ++ napi_enable(&qdma->q_tx_irq[i].napi); + +- for (i = 0; i < ARRAY_SIZE(eth->q_rx); i++) { +- if (!eth->q_rx[i].ndesc) ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) + continue; + +- napi_enable(ð->q_rx[i].napi); ++ napi_enable(&qdma->q_rx[i].napi); + } + } + +@@ -2392,7 +2396,7 @@ static netdev_tx_t airoha_dev_xmit(struc + FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); + + qdma = ð->qdma[0]; +- q = ð->q_tx[qid]; ++ q = &qdma->q_tx[qid]; + if (WARN_ON_ONCE(!q->ndesc)) + goto error; + diff --git a/target/linux/airoha/patches-6.6/010-03-v6.12-net-airoha-Move-irq_mask-in-airoha_qdma-structure.patch b/target/linux/airoha/patches-6.6/010-03-v6.12-net-airoha-Move-irq_mask-in-airoha_qdma-structure.patch new file mode 100644 index 0000000000..9f05ad4057 --- /dev/null +++ b/target/linux/airoha/patches-6.6/010-03-v6.12-net-airoha-Move-irq_mask-in-airoha_qdma-structure.patch @@ -0,0 +1,236 @@ +From 19e47fc2aeda3a657c4f64144ffd6e65f7a66601 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 1 Aug 2024 16:35:05 +0200 +Subject: [PATCH 3/8] net: airoha: Move irq_mask in airoha_qdma structure + +QDMA controllers have independent irq lines, so move irqmask in +airoha_qdma structure. This is a preliminary patch to support multiple +QDMA controllers. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/1c8a06e8be605278a7b2f3cd8ac06e74bf5ebf2b.1722522582.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 84 +++++++++++----------- + 1 file changed, 42 insertions(+), 42 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -786,6 +786,11 @@ struct airoha_hw_stats { + struct airoha_qdma { + void __iomem *regs; + ++ /* protect concurrent irqmask accesses */ ++ spinlock_t irq_lock; ++ u32 irqmask[QDMA_INT_REG_MAX]; ++ int irq; ++ + struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; + + struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; +@@ -812,11 +817,6 @@ struct airoha_eth { + unsigned long state; + void __iomem *fe_regs; + +- /* protect concurrent irqmask accesses */ +- spinlock_t irq_lock; +- u32 irqmask[QDMA_INT_REG_MAX]; +- int irq; +- + struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; + struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; + +@@ -866,38 +866,37 @@ static u32 airoha_rmw(void __iomem *base + #define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) + +-static void airoha_qdma_set_irqmask(struct airoha_eth *eth, int index, ++static void airoha_qdma_set_irqmask(struct airoha_qdma *qdma, int index, + u32 clear, u32 set) + { + unsigned long flags; + +- if (WARN_ON_ONCE(index >= ARRAY_SIZE(eth->irqmask))) ++ if (WARN_ON_ONCE(index >= ARRAY_SIZE(qdma->irqmask))) + return; + +- spin_lock_irqsave(ð->irq_lock, flags); ++ spin_lock_irqsave(&qdma->irq_lock, flags); + +- eth->irqmask[index] &= ~clear; +- eth->irqmask[index] |= set; +- airoha_qdma_wr(ð->qdma[0], REG_INT_ENABLE(index), +- eth->irqmask[index]); ++ qdma->irqmask[index] &= ~clear; ++ qdma->irqmask[index] |= set; ++ airoha_qdma_wr(qdma, REG_INT_ENABLE(index), qdma->irqmask[index]); + /* Read irq_enable register in order to guarantee the update above + * completes in the spinlock critical section. + */ +- airoha_qdma_rr(ð->qdma[0], REG_INT_ENABLE(index)); ++ airoha_qdma_rr(qdma, REG_INT_ENABLE(index)); + +- spin_unlock_irqrestore(ð->irq_lock, flags); ++ spin_unlock_irqrestore(&qdma->irq_lock, flags); + } + +-static void airoha_qdma_irq_enable(struct airoha_eth *eth, int index, ++static void airoha_qdma_irq_enable(struct airoha_qdma *qdma, int index, + u32 mask) + { +- airoha_qdma_set_irqmask(eth, index, 0, mask); ++ airoha_qdma_set_irqmask(qdma, index, 0, mask); + } + +-static void airoha_qdma_irq_disable(struct airoha_eth *eth, int index, ++static void airoha_qdma_irq_disable(struct airoha_qdma *qdma, int index, + u32 mask) + { +- airoha_qdma_set_irqmask(eth, index, mask, 0); ++ airoha_qdma_set_irqmask(qdma, index, mask, 0); + } + + static void airoha_set_macaddr(struct airoha_eth *eth, const u8 *addr) +@@ -1522,7 +1521,7 @@ static int airoha_qdma_rx_process(struct + static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget) + { + struct airoha_queue *q = container_of(napi, struct airoha_queue, napi); +- struct airoha_eth *eth = q->eth; ++ struct airoha_qdma *qdma = &q->eth->qdma[0]; + int cur, done = 0; + + do { +@@ -1531,7 +1530,7 @@ static int airoha_qdma_rx_napi_poll(stru + } while (cur && done < budget); + + if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX1, ++ airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX1, + RX_DONE_INT_MASK); + + return done; +@@ -1719,7 +1718,7 @@ static int airoha_qdma_tx_napi_poll(stru + } + + if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX0, ++ airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, + TX_DONE_INT_MASK(id)); + + return done; +@@ -1928,13 +1927,13 @@ static int airoha_qdma_hw_init(struct ai + int i; + + /* clear pending irqs */ +- for (i = 0; i < ARRAY_SIZE(eth->irqmask); i++) ++ for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) + airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); + + /* setup irqs */ +- airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX0, INT_IDX0_MASK); +- airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX1, INT_IDX1_MASK); +- airoha_qdma_irq_enable(eth, QDMA_INT_REG_IDX4, INT_IDX4_MASK); ++ airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, INT_IDX0_MASK); ++ airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX1, INT_IDX1_MASK); ++ airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX4, INT_IDX4_MASK); + + /* setup irq binding */ + for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +@@ -1980,14 +1979,13 @@ static int airoha_qdma_hw_init(struct ai + static irqreturn_t airoha_irq_handler(int irq, void *dev_instance) + { + struct airoha_eth *eth = dev_instance; +- u32 intr[ARRAY_SIZE(eth->irqmask)]; +- struct airoha_qdma *qdma; ++ struct airoha_qdma *qdma = ð->qdma[0]; ++ u32 intr[ARRAY_SIZE(qdma->irqmask)]; + int i; + +- qdma = ð->qdma[0]; +- for (i = 0; i < ARRAY_SIZE(eth->irqmask); i++) { ++ for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) { + intr[i] = airoha_qdma_rr(qdma, REG_INT_STATUS(i)); +- intr[i] &= eth->irqmask[i]; ++ intr[i] &= qdma->irqmask[i]; + airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]); + } + +@@ -1995,7 +1993,7 @@ static irqreturn_t airoha_irq_handler(in + return IRQ_NONE; + + if (intr[1] & RX_DONE_INT_MASK) { +- airoha_qdma_irq_disable(eth, QDMA_INT_REG_IDX1, ++ airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX1, + RX_DONE_INT_MASK); + + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +@@ -2015,7 +2013,7 @@ static irqreturn_t airoha_irq_handler(in + if (!(intr[0] & TX_DONE_INT_MASK(i))) + continue; + +- airoha_qdma_irq_disable(eth, QDMA_INT_REG_IDX0, ++ airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX0, + TX_DONE_INT_MASK(i)); + + status = airoha_qdma_rr(qdma, REG_IRQ_STATUS(i)); +@@ -2030,12 +2028,18 @@ static irqreturn_t airoha_irq_handler(in + return IRQ_HANDLED; + } + +-static int airoha_qdma_init(struct airoha_eth *eth) ++static int airoha_qdma_init(struct platform_device *pdev, ++ struct airoha_eth *eth) + { + struct airoha_qdma *qdma = ð->qdma[0]; + int err; + +- err = devm_request_irq(eth->dev, eth->irq, airoha_irq_handler, ++ spin_lock_init(&qdma->irq_lock); ++ qdma->irq = platform_get_irq(pdev, 0); ++ if (qdma->irq < 0) ++ return qdma->irq; ++ ++ err = devm_request_irq(eth->dev, qdma->irq, airoha_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, eth); + if (err) + return err; +@@ -2061,7 +2065,8 @@ static int airoha_qdma_init(struct airoh + return 0; + } + +-static int airoha_hw_init(struct airoha_eth *eth) ++static int airoha_hw_init(struct platform_device *pdev, ++ struct airoha_eth *eth) + { + int err; + +@@ -2077,7 +2082,7 @@ static int airoha_hw_init(struct airoha_ + if (err) + return err; + +- return airoha_qdma_init(eth); ++ return airoha_qdma_init(pdev, eth); + } + + static void airoha_hw_cleanup(struct airoha_eth *eth) +@@ -2674,11 +2679,6 @@ static int airoha_probe(struct platform_ + return err; + } + +- spin_lock_init(ð->irq_lock); +- eth->irq = platform_get_irq(pdev, 0); +- if (eth->irq < 0) +- return eth->irq; +- + eth->napi_dev = alloc_netdev_dummy(0); + if (!eth->napi_dev) + return -ENOMEM; +@@ -2688,7 +2688,7 @@ static int airoha_probe(struct platform_ + strscpy(eth->napi_dev->name, "qdma_eth", sizeof(eth->napi_dev->name)); + platform_set_drvdata(pdev, eth); + +- err = airoha_hw_init(eth); ++ err = airoha_hw_init(pdev, eth); + if (err) + goto error; + diff --git a/target/linux/airoha/patches-6.6/010-04-v6.12-net-airoha-Add-airoha_qdma-pointer-in-airoha_tx_irq_.patch b/target/linux/airoha/patches-6.6/010-04-v6.12-net-airoha-Add-airoha_qdma-pointer-in-airoha_tx_irq_.patch new file mode 100644 index 0000000000..b73fc34ef1 --- /dev/null +++ b/target/linux/airoha/patches-6.6/010-04-v6.12-net-airoha-Add-airoha_qdma-pointer-in-airoha_tx_irq_.patch @@ -0,0 +1,306 @@ +From 9a2500ab22f059e596942172a8e4a60ae8243ce4 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 1 Aug 2024 16:35:06 +0200 +Subject: [PATCH 4/8] net: airoha: Add airoha_qdma pointer in + airoha_tx_irq_queue/airoha_queue structures + +Move airoha_eth pointer in airoha_qdma structure from +airoha_tx_irq_queue/airoha_queue ones. This is a preliminary patch to +introduce support for multi-QDMA controllers available on EN7581. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/074565b82fd0ceefe66e186f21133d825dbd48eb.1722522582.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 84 +++++++++++----------- + 1 file changed, 41 insertions(+), 43 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -728,7 +728,7 @@ struct airoha_queue_entry { + }; + + struct airoha_queue { +- struct airoha_eth *eth; ++ struct airoha_qdma *qdma; + + /* protect concurrent queue accesses */ + spinlock_t lock; +@@ -747,7 +747,7 @@ struct airoha_queue { + }; + + struct airoha_tx_irq_queue { +- struct airoha_eth *eth; ++ struct airoha_qdma *qdma; + + struct napi_struct napi; + u32 *q; +@@ -784,6 +784,7 @@ struct airoha_hw_stats { + }; + + struct airoha_qdma { ++ struct airoha_eth *eth; + void __iomem *regs; + + /* protect concurrent irqmask accesses */ +@@ -1388,8 +1389,8 @@ static int airoha_fe_init(struct airoha_ + static int airoha_qdma_fill_rx_queue(struct airoha_queue *q) + { + enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); +- struct airoha_qdma *qdma = &q->eth->qdma[0]; +- struct airoha_eth *eth = q->eth; ++ struct airoha_qdma *qdma = q->qdma; ++ struct airoha_eth *eth = qdma->eth; + int qid = q - &qdma->q_rx[0]; + int nframes = 0; + +@@ -1457,8 +1458,8 @@ static int airoha_qdma_get_gdm_port(stru + static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) + { + enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); +- struct airoha_qdma *qdma = &q->eth->qdma[0]; +- struct airoha_eth *eth = q->eth; ++ struct airoha_qdma *qdma = q->qdma; ++ struct airoha_eth *eth = qdma->eth; + int qid = q - &qdma->q_rx[0]; + int done = 0; + +@@ -1521,7 +1522,6 @@ static int airoha_qdma_rx_process(struct + static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget) + { + struct airoha_queue *q = container_of(napi, struct airoha_queue, napi); +- struct airoha_qdma *qdma = &q->eth->qdma[0]; + int cur, done = 0; + + do { +@@ -1530,14 +1530,13 @@ static int airoha_qdma_rx_napi_poll(stru + } while (cur && done < budget); + + if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX1, ++ airoha_qdma_irq_enable(q->qdma, QDMA_INT_REG_IDX1, + RX_DONE_INT_MASK); + + return done; + } + +-static int airoha_qdma_init_rx_queue(struct airoha_eth *eth, +- struct airoha_queue *q, ++static int airoha_qdma_init_rx_queue(struct airoha_queue *q, + struct airoha_qdma *qdma, int ndesc) + { + const struct page_pool_params pp_params = { +@@ -1548,15 +1547,16 @@ static int airoha_qdma_init_rx_queue(str + .dma_dir = DMA_FROM_DEVICE, + .max_len = PAGE_SIZE, + .nid = NUMA_NO_NODE, +- .dev = eth->dev, ++ .dev = qdma->eth->dev, + .napi = &q->napi, + }; ++ struct airoha_eth *eth = qdma->eth; + int qid = q - &qdma->q_rx[0], thr; + dma_addr_t dma_addr; + + q->buf_size = PAGE_SIZE / 2; + q->ndesc = ndesc; +- q->eth = eth; ++ q->qdma = qdma; + + q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), + GFP_KERNEL); +@@ -1596,7 +1596,7 @@ static int airoha_qdma_init_rx_queue(str + + static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q) + { +- struct airoha_eth *eth = q->eth; ++ struct airoha_eth *eth = q->qdma->eth; + + while (q->queued) { + struct airoha_queue_entry *e = &q->entry[q->tail]; +@@ -1610,8 +1610,7 @@ static void airoha_qdma_cleanup_rx_queue + } + } + +-static int airoha_qdma_init_rx(struct airoha_eth *eth, +- struct airoha_qdma *qdma) ++static int airoha_qdma_init_rx(struct airoha_qdma *qdma) + { + int i; + +@@ -1623,8 +1622,8 @@ static int airoha_qdma_init_rx(struct ai + continue; + } + +- err = airoha_qdma_init_rx_queue(eth, &qdma->q_rx[i], +- qdma, RX_DSCP_NUM(i)); ++ err = airoha_qdma_init_rx_queue(&qdma->q_rx[i], qdma, ++ RX_DSCP_NUM(i)); + if (err) + return err; + } +@@ -1640,9 +1639,9 @@ static int airoha_qdma_tx_napi_poll(stru + int id, done = 0; + + irq_q = container_of(napi, struct airoha_tx_irq_queue, napi); +- eth = irq_q->eth; +- qdma = ð->qdma[0]; ++ qdma = irq_q->qdma; + id = irq_q - &qdma->q_tx_irq[0]; ++ eth = qdma->eth; + + while (irq_q->queued > 0 && done < budget) { + u32 qid, last, val = irq_q->q[irq_q->head]; +@@ -1724,16 +1723,16 @@ static int airoha_qdma_tx_napi_poll(stru + return done; + } + +-static int airoha_qdma_init_tx_queue(struct airoha_eth *eth, +- struct airoha_queue *q, ++static int airoha_qdma_init_tx_queue(struct airoha_queue *q, + struct airoha_qdma *qdma, int size) + { ++ struct airoha_eth *eth = qdma->eth; + int i, qid = q - &qdma->q_tx[0]; + dma_addr_t dma_addr; + + spin_lock_init(&q->lock); + q->ndesc = size; +- q->eth = eth; ++ q->qdma = qdma; + q->free_thr = 1 + MAX_SKB_FRAGS; + + q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), +@@ -1762,11 +1761,11 @@ static int airoha_qdma_init_tx_queue(str + return 0; + } + +-static int airoha_qdma_tx_irq_init(struct airoha_eth *eth, +- struct airoha_tx_irq_queue *irq_q, ++static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, + struct airoha_qdma *qdma, int size) + { + int id = irq_q - &qdma->q_tx_irq[0]; ++ struct airoha_eth *eth = qdma->eth; + dma_addr_t dma_addr; + + netif_napi_add_tx(eth->napi_dev, &irq_q->napi, +@@ -1778,7 +1777,7 @@ static int airoha_qdma_tx_irq_init(struc + + memset(irq_q->q, 0xff, size * sizeof(u32)); + irq_q->size = size; +- irq_q->eth = eth; ++ irq_q->qdma = qdma; + + airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr); + airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, +@@ -1789,21 +1788,20 @@ static int airoha_qdma_tx_irq_init(struc + return 0; + } + +-static int airoha_qdma_init_tx(struct airoha_eth *eth, +- struct airoha_qdma *qdma) ++static int airoha_qdma_init_tx(struct airoha_qdma *qdma) + { + int i, err; + + for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { +- err = airoha_qdma_tx_irq_init(eth, &qdma->q_tx_irq[i], +- qdma, IRQ_QUEUE_LEN(i)); ++ err = airoha_qdma_tx_irq_init(&qdma->q_tx_irq[i], qdma, ++ IRQ_QUEUE_LEN(i)); + if (err) + return err; + } + + for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +- err = airoha_qdma_init_tx_queue(eth, &qdma->q_tx[i], +- qdma, TX_DSCP_NUM); ++ err = airoha_qdma_init_tx_queue(&qdma->q_tx[i], qdma, ++ TX_DSCP_NUM); + if (err) + return err; + } +@@ -1813,7 +1811,7 @@ static int airoha_qdma_init_tx(struct ai + + static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) + { +- struct airoha_eth *eth = q->eth; ++ struct airoha_eth *eth = q->qdma->eth; + + spin_lock_bh(&q->lock); + while (q->queued) { +@@ -1830,9 +1828,9 @@ static void airoha_qdma_cleanup_tx_queue + spin_unlock_bh(&q->lock); + } + +-static int airoha_qdma_init_hfwd_queues(struct airoha_eth *eth, +- struct airoha_qdma *qdma) ++static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) + { ++ struct airoha_eth *eth = qdma->eth; + dma_addr_t dma_addr; + u32 status; + int size; +@@ -1870,8 +1868,7 @@ static int airoha_qdma_init_hfwd_queues( + REG_LMGR_INIT_CFG); + } + +-static void airoha_qdma_init_qos(struct airoha_eth *eth, +- struct airoha_qdma *qdma) ++static void airoha_qdma_init_qos(struct airoha_qdma *qdma) + { + airoha_qdma_clear(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_SCALE_MASK); + airoha_qdma_set(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_BASE_MASK); +@@ -1921,8 +1918,7 @@ static void airoha_qdma_init_qos(struct + FIELD_PREP(SLA_SLOW_TICK_RATIO_MASK, 40)); + } + +-static int airoha_qdma_hw_init(struct airoha_eth *eth, +- struct airoha_qdma *qdma) ++static int airoha_qdma_hw_init(struct airoha_qdma *qdma) + { + int i; + +@@ -1959,7 +1955,7 @@ static int airoha_qdma_hw_init(struct ai + GLOBAL_CFG_TX_WB_DONE_MASK | + FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 2)); + +- airoha_qdma_init_qos(eth, qdma); ++ airoha_qdma_init_qos(qdma); + + /* disable qdma rx delay interrupt */ + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +@@ -2035,6 +2031,8 @@ static int airoha_qdma_init(struct platf + int err; + + spin_lock_init(&qdma->irq_lock); ++ qdma->eth = eth; ++ + qdma->irq = platform_get_irq(pdev, 0); + if (qdma->irq < 0) + return qdma->irq; +@@ -2044,19 +2042,19 @@ static int airoha_qdma_init(struct platf + if (err) + return err; + +- err = airoha_qdma_init_rx(eth, qdma); ++ err = airoha_qdma_init_rx(qdma); + if (err) + return err; + +- err = airoha_qdma_init_tx(eth, qdma); ++ err = airoha_qdma_init_tx(qdma); + if (err) + return err; + +- err = airoha_qdma_init_hfwd_queues(eth, qdma); ++ err = airoha_qdma_init_hfwd_queues(qdma); + if (err) + return err; + +- err = airoha_qdma_hw_init(eth, qdma); ++ err = airoha_qdma_hw_init(qdma); + if (err) + return err; + diff --git a/target/linux/airoha/patches-6.6/010-05-v6.12-net-airoha-Use-qdma-pointer-as-private-structure-in-.patch b/target/linux/airoha/patches-6.6/010-05-v6.12-net-airoha-Use-qdma-pointer-as-private-structure-in-.patch new file mode 100644 index 0000000000..9cabd10b58 --- /dev/null +++ b/target/linux/airoha/patches-6.6/010-05-v6.12-net-airoha-Use-qdma-pointer-as-private-structure-in-.patch @@ -0,0 +1,45 @@ +From e3d6bfdfc0aeb8c1d7965413b1050ec07f9761e5 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 1 Aug 2024 16:35:07 +0200 +Subject: [PATCH 5/8] net: airoha: Use qdma pointer as private structure in + airoha_irq_handler routine + +This is a preliminary patch to support multi-QDMA controllers. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/1e40c3cb973881c0eb3c3c247c78550da62054ab.1722522582.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -1974,8 +1974,7 @@ static int airoha_qdma_hw_init(struct ai + + static irqreturn_t airoha_irq_handler(int irq, void *dev_instance) + { +- struct airoha_eth *eth = dev_instance; +- struct airoha_qdma *qdma = ð->qdma[0]; ++ struct airoha_qdma *qdma = dev_instance; + u32 intr[ARRAY_SIZE(qdma->irqmask)]; + int i; + +@@ -1985,7 +1984,7 @@ static irqreturn_t airoha_irq_handler(in + airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]); + } + +- if (!test_bit(DEV_STATE_INITIALIZED, ð->state)) ++ if (!test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state)) + return IRQ_NONE; + + if (intr[1] & RX_DONE_INT_MASK) { +@@ -2038,7 +2037,7 @@ static int airoha_qdma_init(struct platf + return qdma->irq; + + err = devm_request_irq(eth->dev, qdma->irq, airoha_irq_handler, +- IRQF_SHARED, KBUILD_MODNAME, eth); ++ IRQF_SHARED, KBUILD_MODNAME, qdma); + if (err) + return err; + diff --git a/target/linux/airoha/patches-6.6/010-06-v6.12-net-airoha-Allow-mapping-IO-region-for-multiple-qdma.patch b/target/linux/airoha/patches-6.6/010-06-v6.12-net-airoha-Allow-mapping-IO-region-for-multiple-qdma.patch new file mode 100644 index 0000000000..ebc7318578 --- /dev/null +++ b/target/linux/airoha/patches-6.6/010-06-v6.12-net-airoha-Allow-mapping-IO-region-for-multiple-qdma.patch @@ -0,0 +1,131 @@ +From e618447cf492d04415007336eec025fae6e9a2ea Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 1 Aug 2024 16:35:08 +0200 +Subject: [PATCH 6/8] net: airoha: Allow mapping IO region for multiple qdma + controllers + +Map MMIO regions of both qdma controllers available on EN7581 SoC. +Run airoha_hw_cleanup routine for both QDMA controllers available on +EN7581 SoC removing airoha_eth module or in airoha_probe error path. +This is a preliminary patch to support multi-QDMA controllers. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/a734ae608da14b67ae749b375d880dbbc70868ea.1722522582.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 56 ++++++++++++---------- + 1 file changed, 32 insertions(+), 24 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -2024,15 +2024,25 @@ static irqreturn_t airoha_irq_handler(in + } + + static int airoha_qdma_init(struct platform_device *pdev, +- struct airoha_eth *eth) ++ struct airoha_eth *eth, ++ struct airoha_qdma *qdma) + { +- struct airoha_qdma *qdma = ð->qdma[0]; +- int err; ++ int err, id = qdma - ð->qdma[0]; ++ const char *res; + + spin_lock_init(&qdma->irq_lock); + qdma->eth = eth; + +- qdma->irq = platform_get_irq(pdev, 0); ++ res = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d", id); ++ if (!res) ++ return -ENOMEM; ++ ++ qdma->regs = devm_platform_ioremap_resource_byname(pdev, res); ++ if (IS_ERR(qdma->regs)) ++ return dev_err_probe(eth->dev, PTR_ERR(qdma->regs), ++ "failed to iomap qdma%d regs\n", id); ++ ++ qdma->irq = platform_get_irq(pdev, 4 * id); + if (qdma->irq < 0) + return qdma->irq; + +@@ -2053,19 +2063,13 @@ static int airoha_qdma_init(struct platf + if (err) + return err; + +- err = airoha_qdma_hw_init(qdma); +- if (err) +- return err; +- +- set_bit(DEV_STATE_INITIALIZED, ð->state); +- +- return 0; ++ return airoha_qdma_hw_init(qdma); + } + + static int airoha_hw_init(struct platform_device *pdev, + struct airoha_eth *eth) + { +- int err; ++ int err, i; + + /* disable xsi */ + reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts), eth->xsi_rsts); +@@ -2079,12 +2083,19 @@ static int airoha_hw_init(struct platfor + if (err) + return err; + +- return airoha_qdma_init(pdev, eth); ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { ++ err = airoha_qdma_init(pdev, eth, ð->qdma[i]); ++ if (err) ++ return err; ++ } ++ ++ set_bit(DEV_STATE_INITIALIZED, ð->state); ++ ++ return 0; + } + +-static void airoha_hw_cleanup(struct airoha_eth *eth) ++static void airoha_hw_cleanup(struct airoha_qdma *qdma) + { +- struct airoha_qdma *qdma = ð->qdma[0]; + int i; + + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +@@ -2645,13 +2656,6 @@ static int airoha_probe(struct platform_ + return dev_err_probe(eth->dev, PTR_ERR(eth->fe_regs), + "failed to iomap fe regs\n"); + +- eth->qdma[0].regs = devm_platform_ioremap_resource_byname(pdev, +- "qdma0"); +- if (IS_ERR(eth->qdma[0].regs)) +- return dev_err_probe(eth->dev, +- PTR_ERR(eth->qdma[0].regs), +- "failed to iomap qdma regs\n"); +- + eth->rsts[0].id = "fe"; + eth->rsts[1].id = "pdma"; + eth->rsts[2].id = "qdma"; +@@ -2707,7 +2711,9 @@ static int airoha_probe(struct platform_ + return 0; + + error: +- airoha_hw_cleanup(eth); ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) ++ airoha_hw_cleanup(ð->qdma[i]); ++ + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + +@@ -2725,7 +2731,9 @@ static void airoha_remove(struct platfor + struct airoha_eth *eth = platform_get_drvdata(pdev); + int i; + +- airoha_hw_cleanup(eth); ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) ++ airoha_hw_cleanup(ð->qdma[i]); ++ + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + diff --git a/target/linux/airoha/patches-6.6/010-07-v6.12-net-airoha-Start-all-qdma-NAPIs-in-airoha_probe.patch b/target/linux/airoha/patches-6.6/010-07-v6.12-net-airoha-Start-all-qdma-NAPIs-in-airoha_probe.patch new file mode 100644 index 0000000000..c9a99f1e79 --- /dev/null +++ b/target/linux/airoha/patches-6.6/010-07-v6.12-net-airoha-Start-all-qdma-NAPIs-in-airoha_probe.patch @@ -0,0 +1,38 @@ +From 160231e34b8e9512ba20530f3e68fb0ac499af87 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 1 Aug 2024 16:35:09 +0200 +Subject: [PATCH 7/8] net: airoha: Start all qdma NAPIs in airoha_probe() + +This is a preliminary patch to support multi-QDMA controllers. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/b51cf69c94d8cbc81e0a0b35587f024d01e6d9c0.1722522582.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -2122,9 +2122,8 @@ static void airoha_hw_cleanup(struct air + } + } + +-static void airoha_qdma_start_napi(struct airoha_eth *eth) ++static void airoha_qdma_start_napi(struct airoha_qdma *qdma) + { +- struct airoha_qdma *qdma = ð->qdma[0]; + int i; + + for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) +@@ -2693,7 +2692,9 @@ static int airoha_probe(struct platform_ + if (err) + goto error; + +- airoha_qdma_start_napi(eth); ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) ++ airoha_qdma_start_napi(ð->qdma[i]); ++ + for_each_child_of_node(pdev->dev.of_node, np) { + if (!of_device_is_compatible(np, "airoha,eth-mac")) + continue; diff --git a/target/linux/airoha/patches-6.6/010-08-v6.12-net-airoha-Link-the-gdm-port-to-the-selected-qdma-co.patch b/target/linux/airoha/patches-6.6/010-08-v6.12-net-airoha-Link-the-gdm-port-to-the-selected-qdma-co.patch new file mode 100644 index 0000000000..1e89cf15aa --- /dev/null +++ b/target/linux/airoha/patches-6.6/010-08-v6.12-net-airoha-Link-the-gdm-port-to-the-selected-qdma-co.patch @@ -0,0 +1,174 @@ +From 9304640f2f78147dddf97a5ea01502ae175e41d9 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 1 Aug 2024 16:35:10 +0200 +Subject: [PATCH 8/8] net: airoha: Link the gdm port to the selected qdma + controller + +Link the running gdm port to the qdma controller used to connect with +the CPU. Moreover, load all QDMA controllers available on EN7581 SoC. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/95b515df34ba4727f7ae5b14a1d0462cceec84ff.1722522582.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 37 +++++++++++----------- + 1 file changed, 19 insertions(+), 18 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -18,7 +18,7 @@ + #include + + #define AIROHA_MAX_NUM_GDM_PORTS 1 +-#define AIROHA_MAX_NUM_QDMA 1 ++#define AIROHA_MAX_NUM_QDMA 2 + #define AIROHA_MAX_NUM_RSTS 3 + #define AIROHA_MAX_NUM_XSI_RSTS 5 + #define AIROHA_MAX_MTU 2000 +@@ -805,8 +805,8 @@ struct airoha_qdma { + }; + + struct airoha_gdm_port { ++ struct airoha_qdma *qdma; + struct net_device *dev; +- struct airoha_eth *eth; + int id; + + struct airoha_hw_stats stats; +@@ -2139,7 +2139,7 @@ static void airoha_qdma_start_napi(struc + + static void airoha_update_hw_stats(struct airoha_gdm_port *port) + { +- struct airoha_eth *eth = port->eth; ++ struct airoha_eth *eth = port->qdma->eth; + u32 val, i = 0; + + spin_lock(&port->stats.lock); +@@ -2284,22 +2284,22 @@ static void airoha_update_hw_stats(struc + static int airoha_dev_open(struct net_device *dev) + { + struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_eth *eth = port->eth; ++ struct airoha_qdma *qdma = port->qdma; + int err; + + netif_tx_start_all_queues(dev); +- err = airoha_set_gdm_ports(eth, true); ++ err = airoha_set_gdm_ports(qdma->eth, true); + if (err) + return err; + + if (netdev_uses_dsa(dev)) +- airoha_fe_set(eth, REG_GDM_INGRESS_CFG(port->id), ++ airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id), + GDM_STAG_EN_MASK); + else +- airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id), ++ airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), + GDM_STAG_EN_MASK); + +- airoha_qdma_set(ð->qdma[0], REG_QDMA_GLOBAL_CFG, ++ airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); + +@@ -2309,15 +2309,15 @@ static int airoha_dev_open(struct net_de + static int airoha_dev_stop(struct net_device *dev) + { + struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_eth *eth = port->eth; ++ struct airoha_qdma *qdma = port->qdma; + int err; + + netif_tx_disable(dev); +- err = airoha_set_gdm_ports(eth, false); ++ err = airoha_set_gdm_ports(qdma->eth, false); + if (err) + return err; + +- airoha_qdma_clear(ð->qdma[0], REG_QDMA_GLOBAL_CFG, ++ airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); + +@@ -2333,7 +2333,7 @@ static int airoha_dev_set_macaddr(struct + if (err) + return err; + +- airoha_set_macaddr(port->eth, dev->dev_addr); ++ airoha_set_macaddr(port->qdma->eth, dev->dev_addr); + + return 0; + } +@@ -2342,7 +2342,7 @@ static int airoha_dev_init(struct net_de + { + struct airoha_gdm_port *port = netdev_priv(dev); + +- airoha_set_macaddr(port->eth, dev->dev_addr); ++ airoha_set_macaddr(port->qdma->eth, dev->dev_addr); + + return 0; + } +@@ -2376,10 +2376,9 @@ static netdev_tx_t airoha_dev_xmit(struc + struct airoha_gdm_port *port = netdev_priv(dev); + u32 msg0 = 0, msg1, len = skb_headlen(skb); + int i, qid = skb_get_queue_mapping(skb); +- struct airoha_eth *eth = port->eth; ++ struct airoha_qdma *qdma = port->qdma; + u32 nr_frags = 1 + sinfo->nr_frags; + struct netdev_queue *txq; +- struct airoha_qdma *qdma; + struct airoha_queue *q; + void *data = skb->data; + u16 index; +@@ -2407,7 +2406,6 @@ static netdev_tx_t airoha_dev_xmit(struc + msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | + FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); + +- qdma = ð->qdma[0]; + q = &qdma->q_tx[qid]; + if (WARN_ON_ONCE(!q->ndesc)) + goto error; +@@ -2490,7 +2488,7 @@ static void airoha_ethtool_get_drvinfo(s + struct ethtool_drvinfo *info) + { + struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_eth *eth = port->eth; ++ struct airoha_eth *eth = port->qdma->eth; + + strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info)); +@@ -2571,6 +2569,7 @@ static int airoha_alloc_gdm_port(struct + { + const __be32 *id_ptr = of_get_property(np, "reg", NULL); + struct airoha_gdm_port *port; ++ struct airoha_qdma *qdma; + struct net_device *dev; + int err, index; + u32 id; +@@ -2600,6 +2599,7 @@ static int airoha_alloc_gdm_port(struct + return -ENOMEM; + } + ++ qdma = ð->qdma[index % AIROHA_MAX_NUM_QDMA]; + dev->netdev_ops = &airoha_netdev_ops; + dev->ethtool_ops = &airoha_ethtool_ops; + dev->max_mtu = AIROHA_MAX_MTU; +@@ -2609,6 +2609,7 @@ static int airoha_alloc_gdm_port(struct + NETIF_F_SG | NETIF_F_TSO; + dev->features |= dev->hw_features; + dev->dev.of_node = np; ++ dev->irq = qdma->irq; + SET_NETDEV_DEV(dev, eth->dev); + + err = of_get_ethdev_address(np, dev); +@@ -2624,8 +2625,8 @@ static int airoha_alloc_gdm_port(struct + port = netdev_priv(dev); + u64_stats_init(&port->stats.syncp); + spin_lock_init(&port->stats.lock); ++ port->qdma = qdma; + port->dev = dev; +- port->eth = eth; + port->id = id; + eth->ports[index] = port; + diff --git a/target/linux/airoha/patches-6.6/011-v6.12-net-airoha-honor-reset-return-value-in-airoha_hw_ini.patch b/target/linux/airoha/patches-6.6/011-v6.12-net-airoha-honor-reset-return-value-in-airoha_hw_ini.patch new file mode 100644 index 0000000000..ed25ccb89d --- /dev/null +++ b/target/linux/airoha/patches-6.6/011-v6.12-net-airoha-honor-reset-return-value-in-airoha_hw_ini.patch @@ -0,0 +1,44 @@ +From 63a796b4988c3dca83176a534890b510d44f105a Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 3 Aug 2024 17:50:50 +0200 +Subject: [PATCH] net: airoha: honor reset return value in airoha_hw_init() + +Take into account return value from reset_control_bulk_assert and +reset_control_bulk_deassert routines in airoha_hw_init(). + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/f49dc04a87653e0155f4fab3e3eb584785c8ad6a.1722699555.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -2072,13 +2072,21 @@ static int airoha_hw_init(struct platfor + int err, i; + + /* disable xsi */ +- reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts), eth->xsi_rsts); ++ err = reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts), ++ eth->xsi_rsts); ++ if (err) ++ return err; ++ ++ err = reset_control_bulk_assert(ARRAY_SIZE(eth->rsts), eth->rsts); ++ if (err) ++ return err; + +- reset_control_bulk_assert(ARRAY_SIZE(eth->rsts), eth->rsts); +- msleep(20); +- reset_control_bulk_deassert(ARRAY_SIZE(eth->rsts), eth->rsts); + msleep(20); ++ err = reset_control_bulk_deassert(ARRAY_SIZE(eth->rsts), eth->rsts); ++ if (err) ++ return err; + ++ msleep(20); + err = airoha_fe_init(eth); + if (err) + return err; diff --git a/target/linux/airoha/patches-6.6/012-v6.12-net-airoha-configure-hw-mac-address-according-to-the.patch b/target/linux/airoha/patches-6.6/012-v6.12-net-airoha-configure-hw-mac-address-according-to-the.patch new file mode 100644 index 0000000000..da23955501 --- /dev/null +++ b/target/linux/airoha/patches-6.6/012-v6.12-net-airoha-configure-hw-mac-address-according-to-the.patch @@ -0,0 +1,85 @@ +From 812a2751e827fa1eb01f3bd268b4d74c23f4226a Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 21 Aug 2024 09:30:14 +0200 +Subject: [PATCH] net: airoha: configure hw mac address according to the port + id + +GDM1 port on EN7581 SoC is connected to the lan dsa switch. +GDM{2,3,4} can be used as wan port connected to an external +phy module. Configure hw mac address registers according to the port id. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20240821-airoha-eth-wan-mac-addr-v2-1-8706d0cd6cd5@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 32 ++++++++++++++++------ + 1 file changed, 23 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -67,9 +67,11 @@ + #define FE_RST_GDM3_MBI_ARB_MASK BIT(2) + #define FE_RST_CORE_MASK BIT(0) + ++#define REG_FE_WAN_MAC_H 0x0030 + #define REG_FE_LAN_MAC_H 0x0040 +-#define REG_FE_LAN_MAC_LMIN 0x0044 +-#define REG_FE_LAN_MAC_LMAX 0x0048 ++ ++#define REG_FE_MAC_LMIN(_n) ((_n) + 0x04) ++#define REG_FE_MAC_LMAX(_n) ((_n) + 0x08) + + #define REG_FE_CDM1_OQ_MAP0 0x0050 + #define REG_FE_CDM1_OQ_MAP1 0x0054 +@@ -900,16 +902,28 @@ static void airoha_qdma_irq_disable(stru + airoha_qdma_set_irqmask(qdma, index, mask, 0); + } + +-static void airoha_set_macaddr(struct airoha_eth *eth, const u8 *addr) ++static bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) + { +- u32 val; ++ /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. ++ * GDM{2,3,4} can be used as wan port connected to an external ++ * phy module. ++ */ ++ return port->id == 1; ++} ++ ++static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) ++{ ++ struct airoha_eth *eth = port->qdma->eth; ++ u32 val, reg; + ++ reg = airhoa_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H ++ : REG_FE_WAN_MAC_H; + val = (addr[0] << 16) | (addr[1] << 8) | addr[2]; +- airoha_fe_wr(eth, REG_FE_LAN_MAC_H, val); ++ airoha_fe_wr(eth, reg, val); + + val = (addr[3] << 16) | (addr[4] << 8) | addr[5]; +- airoha_fe_wr(eth, REG_FE_LAN_MAC_LMIN, val); +- airoha_fe_wr(eth, REG_FE_LAN_MAC_LMAX, val); ++ airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val); ++ airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val); + } + + static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, +@@ -2341,7 +2355,7 @@ static int airoha_dev_set_macaddr(struct + if (err) + return err; + +- airoha_set_macaddr(port->qdma->eth, dev->dev_addr); ++ airoha_set_macaddr(port, dev->dev_addr); + + return 0; + } +@@ -2350,7 +2364,7 @@ static int airoha_dev_init(struct net_de + { + struct airoha_gdm_port *port = netdev_priv(dev); + +- airoha_set_macaddr(port->qdma->eth, dev->dev_addr); ++ airoha_set_macaddr(port, dev->dev_addr); + + return 0; + } diff --git a/target/linux/airoha/patches-6.6/013-v6.12-net-airoha-fix-module-autoloading.patch b/target/linux/airoha/patches-6.6/013-v6.12-net-airoha-fix-module-autoloading.patch new file mode 100644 index 0000000000..63c6162770 --- /dev/null +++ b/target/linux/airoha/patches-6.6/013-v6.12-net-airoha-fix-module-autoloading.patch @@ -0,0 +1,26 @@ +From 7d2bd8ac9d2494cf9b16c4b00df9424ad24ed18c Mon Sep 17 00:00:00 2001 +From: Liao Chen +Date: Mon, 26 Aug 2024 09:18:58 +0000 +Subject: [PATCH] net: airoha: fix module autoloading + +Add MODULE_DEVICE_TABLE(), so modules could be properly autoloaded +based on the alias from of_device_id table. + +Signed-off-by: Liao Chen +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20240826091858.369910-4-liaochen4@huawei.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -2776,6 +2776,7 @@ static const struct of_device_id of_airo + { .compatible = "airoha,en7581-eth" }, + { /* sentinel */ } + }; ++MODULE_DEVICE_TABLE(of, of_airoha_match); + + static struct platform_driver airoha_driver = { + .probe = airoha_probe, diff --git a/target/linux/airoha/patches-6.6/014-01-v6.13-net-airoha-fix-PSE-memory-configuration-in-airoha_fe.patch b/target/linux/airoha/patches-6.6/014-01-v6.13-net-airoha-fix-PSE-memory-configuration-in-airoha_fe.patch new file mode 100644 index 0000000000..fb86423733 --- /dev/null +++ b/target/linux/airoha/patches-6.6/014-01-v6.13-net-airoha-fix-PSE-memory-configuration-in-airoha_fe.patch @@ -0,0 +1,40 @@ +From 8e38e08f2c560328a873c35aff1a0dbea6a7d084 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 1 Oct 2024 12:10:25 +0200 +Subject: [PATCH 2/2] net: airoha: fix PSE memory configuration in + airoha_fe_pse_ports_init() + +Align PSE memory configuration to vendor SDK. In particular, increase +initial value of PSE reserved memory in airoha_fe_pse_ports_init() +routine by the value used for the second Packet Processor Engine (PPE2) +and do not overwrite the default value. + +Introduced by commit 23020f049327 ("net: airoha: Introduce ethernet support +for EN7581 SoC") + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20241001-airoha-eth-pse-fix-v2-2-9a56cdffd074@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -1166,11 +1166,13 @@ static void airoha_fe_pse_ports_init(str + [FE_PSE_PORT_GDM4] = 2, + [FE_PSE_PORT_CDM5] = 2, + }; ++ u32 all_rsv; + int q; + ++ all_rsv = airoha_fe_get_pse_all_rsv(eth); + /* hw misses PPE2 oq rsv */ +- airoha_fe_set(eth, REG_FE_PSE_BUF_SET, +- PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2]); ++ all_rsv += PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2]; ++ airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv); + + /* CMD1 */ + for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM1]; q++) diff --git a/target/linux/airoha/patches-6.6/014-02-v6.13-net-airoha-read-default-PSE-reserved-pages-value-bef.patch b/target/linux/airoha/patches-6.6/014-02-v6.13-net-airoha-read-default-PSE-reserved-pages-value-bef.patch new file mode 100644 index 0000000000..a2e5c4fdd9 --- /dev/null +++ b/target/linux/airoha/patches-6.6/014-02-v6.13-net-airoha-read-default-PSE-reserved-pages-value-bef.patch @@ -0,0 +1,52 @@ +From 1f3e7ff4f296af1f4350f457d5bd82bc825e645a Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 1 Oct 2024 12:10:24 +0200 +Subject: [PATCH 1/2] net: airoha: read default PSE reserved pages value before + updating + +Store the default value for the number of PSE reserved pages in orig_val +at the beginning of airoha_fe_set_pse_oq_rsv routine, before updating it +with airoha_fe_set_pse_queue_rsv_pages(). +Introduce airoha_fe_get_pse_all_rsv utility routine. + +Introduced by commit 23020f049327 ("net: airoha: Introduce ethernet support +for EN7581 SoC") + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20241001-airoha-eth-pse-fix-v2-1-9a56cdffd074@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -1116,17 +1116,23 @@ static void airoha_fe_set_pse_queue_rsv_ + PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK); + } + ++static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth) ++{ ++ u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET); ++ ++ return FIELD_GET(PSE_ALLRSV_MASK, val); ++} ++ + static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth, + u32 port, u32 queue, u32 val) + { +- u32 orig_val, tmp, all_rsv, fq_limit; ++ u32 orig_val = airoha_fe_get_pse_queue_rsv_pages(eth, port, queue); ++ u32 tmp, all_rsv, fq_limit; + + airoha_fe_set_pse_queue_rsv_pages(eth, port, queue, val); + + /* modify all rsv */ +- orig_val = airoha_fe_get_pse_queue_rsv_pages(eth, port, queue); +- tmp = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET); +- all_rsv = FIELD_GET(PSE_ALLRSV_MASK, tmp); ++ all_rsv = airoha_fe_get_pse_all_rsv(eth); + all_rsv += (val - orig_val); + airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK, + FIELD_PREP(PSE_ALLRSV_MASK, all_rsv)); diff --git a/target/linux/airoha/patches-6.6/015-v6.12-net-airoha-Update-tx-cpu-dma-ring-idx-at-the-end-of-.patch b/target/linux/airoha/patches-6.6/015-v6.12-net-airoha-Update-tx-cpu-dma-ring-idx-at-the-end-of-.patch new file mode 100644 index 0000000000..db6cc9caee --- /dev/null +++ b/target/linux/airoha/patches-6.6/015-v6.12-net-airoha-Update-tx-cpu-dma-ring-idx-at-the-end-of-.patch @@ -0,0 +1,45 @@ +From 3dc6e998d18bfba6e0dc979d3cc68eba98dfeef7 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 4 Oct 2024 15:51:26 +0200 +Subject: [PATCH] net: airoha: Update tx cpu dma ring idx at the end of xmit + loop + +Move the tx cpu dma ring index update out of transmit loop of +airoha_dev_xmit routine in order to not start transmitting the packet +before it is fully DMA mapped (e.g. fragmented skbs). + +Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Reported-by: Felix Fietkau +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20241004-airoha-eth-7581-mapping-fix-v1-1-8e4279ab1812@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -2480,10 +2480,6 @@ static netdev_tx_t airoha_dev_xmit(struc + e->dma_addr = addr; + e->dma_len = len; + +- airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), +- TX_RING_CPU_IDX_MASK, +- FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); +- + data = skb_frag_address(frag); + len = skb_frag_size(frag); + } +@@ -2492,6 +2488,11 @@ static netdev_tx_t airoha_dev_xmit(struc + q->queued += i; + + skb_tx_timestamp(skb); ++ if (!netdev_xmit_more()) ++ airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), ++ TX_RING_CPU_IDX_MASK, ++ FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); ++ + if (q->ndesc - q->queued < q->free_thr) + netif_tx_stop_queue(txq); + diff --git a/target/linux/airoha/patches-6.6/016-v6.13-net-airoha-Fix-EGRESS_RATE_METER_EN_MASK-definition.patch b/target/linux/airoha/patches-6.6/016-v6.13-net-airoha-Fix-EGRESS_RATE_METER_EN_MASK-definition.patch new file mode 100644 index 0000000000..d70cadf9d9 --- /dev/null +++ b/target/linux/airoha/patches-6.6/016-v6.13-net-airoha-Fix-EGRESS_RATE_METER_EN_MASK-definition.patch @@ -0,0 +1,33 @@ +From 2518b119639162251b6cc7195aec394930c1d867 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 9 Oct 2024 00:21:47 +0200 +Subject: [PATCH] net: airoha: Fix EGRESS_RATE_METER_EN_MASK definition + +Fix typo in EGRESS_RATE_METER_EN_MASK mask definition. This bus in not +introducing any user visible problem since, even if we are setting +EGRESS_RATE_METER_EN_MASK bit in REG_EGRESS_RATE_METER_CFG register, +egress QoS metering is not supported yet since we are missing some other +hw configurations (e.g token bucket rate, token bucket size). + +Introduced by commit 23020f049327 ("net: airoha: Introduce ethernet support +for EN7581 SoC") + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20241009-airoha-fixes-v2-1-18af63ec19bf@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -554,7 +554,7 @@ + #define FWD_DSCP_LOW_THR_MASK GENMASK(17, 0) + + #define REG_EGRESS_RATE_METER_CFG 0x100c +-#define EGRESS_RATE_METER_EN_MASK BIT(29) ++#define EGRESS_RATE_METER_EN_MASK BIT(31) + #define EGRESS_RATE_METER_EQ_RATE_EN_MASK BIT(17) + #define EGRESS_RATE_METER_WINDOW_SZ_MASK GENMASK(16, 12) + #define EGRESS_RATE_METER_TIMESLICE_MASK GENMASK(10, 0) diff --git a/target/linux/airoha/patches-6.6/017-v6.13-net-airoha-Implement-BQL-support.patch b/target/linux/airoha/patches-6.6/017-v6.13-net-airoha-Implement-BQL-support.patch new file mode 100644 index 0000000000..b6bb9f647d --- /dev/null +++ b/target/linux/airoha/patches-6.6/017-v6.13-net-airoha-Implement-BQL-support.patch @@ -0,0 +1,42 @@ +From 1d304174106c93ce05f6088813ad7203b3eb381a Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 12 Oct 2024 11:01:11 +0200 +Subject: [PATCH] net: airoha: Implement BQL support + +Introduce BQL support in the airoha_eth driver reporting to the kernel +info about tx hw DMA queues in order to avoid bufferbloat and keep the +latency small. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20241012-en7581-bql-v2-1-4deb4efdb60b@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -1710,9 +1710,11 @@ static int airoha_qdma_tx_napi_poll(stru + WRITE_ONCE(desc->msg1, 0); + + if (skb) { ++ u16 queue = skb_get_queue_mapping(skb); + struct netdev_queue *txq; + +- txq = netdev_get_tx_queue(skb->dev, qid); ++ txq = netdev_get_tx_queue(skb->dev, queue); ++ netdev_tx_completed_queue(txq, 1, skb->len); + if (netif_tx_queue_stopped(txq) && + q->ndesc - q->queued >= q->free_thr) + netif_tx_wake_queue(txq); +@@ -2488,7 +2490,9 @@ static netdev_tx_t airoha_dev_xmit(struc + q->queued += i; + + skb_tx_timestamp(skb); +- if (!netdev_xmit_more()) ++ netdev_tx_sent_queue(txq, skb->len); ++ ++ if (netif_xmit_stopped(txq) || !netdev_xmit_more()) + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), + TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); diff --git a/target/linux/airoha/patches-6.6/018-01-v6.10-clk-en7523-Add-en_clk_soc_data-data-structure.patch b/target/linux/airoha/patches-6.6/018-01-v6.10-clk-en7523-Add-en_clk_soc_data-data-structure.patch new file mode 100644 index 0000000000..1e19356762 --- /dev/null +++ b/target/linux/airoha/patches-6.6/018-01-v6.10-clk-en7523-Add-en_clk_soc_data-data-structure.patch @@ -0,0 +1,98 @@ +From 457e74667f452d7f071ad2b2d9313ec62ebc4b02 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 6 Apr 2024 12:43:43 +0200 +Subject: [PATCH 1/2] clk: en7523: Add en_clk_soc_data data structure + +Introduce en_clk_soc_data data structure in order to define multiple +clk_ops for each supported SoC. This is a preliminary patch to +introduce EN7581 clock support. + +Tested-by: Zhengping Zhang +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/562a0da8d7874a02a324687c152c87a1549924bd.1712399981.git.lorenzo@kernel.org +Signed-off-by: Stephen Boyd +--- + drivers/clk/clk-en7523.c | 34 +++++++++++++++++++++------------- + 1 file changed, 21 insertions(+), 13 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -3,8 +3,8 @@ + #include + #include + #include +-#include + #include ++#include + #include + + #define REG_PCI_CONTROL 0x88 +@@ -48,6 +48,10 @@ struct en_clk_gate { + struct clk_hw hw; + }; + ++struct en_clk_soc_data { ++ const struct clk_ops pcie_ops; ++}; ++ + static const u32 gsw_base[] = { 400000000, 500000000 }; + static const u32 emi_base[] = { 333000000, 400000000 }; + static const u32 bus_base[] = { 500000000, 540000000 }; +@@ -150,11 +154,6 @@ static const struct en_clk_desc en7523_b + } + }; + +-static const struct of_device_id of_match_clk_en7523[] = { +- { .compatible = "airoha,en7523-scu", }, +- { /* sentinel */ } +-}; +- + static unsigned int en7523_get_base_rate(void __iomem *base, unsigned int i) + { + const struct en_clk_desc *desc = &en7523_base_clks[i]; +@@ -252,14 +251,10 @@ static void en7523_pci_unprepare(struct + static struct clk_hw *en7523_register_pcie_clk(struct device *dev, + void __iomem *np_base) + { +- static const struct clk_ops pcie_gate_ops = { +- .is_enabled = en7523_pci_is_enabled, +- .prepare = en7523_pci_prepare, +- .unprepare = en7523_pci_unprepare, +- }; ++ const struct en_clk_soc_data *soc_data = device_get_match_data(dev); + struct clk_init_data init = { + .name = "pcie", +- .ops = &pcie_gate_ops, ++ .ops = &soc_data->pcie_ops, + }; + struct en_clk_gate *cg; + +@@ -269,7 +264,7 @@ static struct clk_hw *en7523_register_pc + + cg->base = np_base; + cg->hw.init = &init; +- en7523_pci_unprepare(&cg->hw); ++ init.ops->unprepare(&cg->hw); + + if (clk_hw_register(dev, &cg->hw)) + return NULL; +@@ -338,6 +333,19 @@ static int en7523_clk_probe(struct platf + return r; + } + ++static const struct en_clk_soc_data en7523_data = { ++ .pcie_ops = { ++ .is_enabled = en7523_pci_is_enabled, ++ .prepare = en7523_pci_prepare, ++ .unprepare = en7523_pci_unprepare, ++ }, ++}; ++ ++static const struct of_device_id of_match_clk_en7523[] = { ++ { .compatible = "airoha,en7523-scu", .data = &en7523_data }, ++ { /* sentinel */ } ++}; ++ + static struct platform_driver clk_en7523_drv = { + .probe = en7523_clk_probe, + .driver = { diff --git a/target/linux/airoha/patches-6.6/018-02-v6.10-clk-en7523-Add-EN7581-support.patch b/target/linux/airoha/patches-6.6/018-02-v6.10-clk-en7523-Add-EN7581-support.patch new file mode 100644 index 0000000000..c27b79cf55 --- /dev/null +++ b/target/linux/airoha/patches-6.6/018-02-v6.10-clk-en7523-Add-EN7581-support.patch @@ -0,0 +1,248 @@ +From 66bc47326ce2a319add7e933d9340215711236ac Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 6 Apr 2024 12:43:44 +0200 +Subject: [PATCH 2/2] clk: en7523: Add EN7581 support + +Introduce EN7581 clock support to clk-en7523 driver. +Add hw_init callback to en_clk_soc_data data structure. + +Tested-by: Zhengping Zhang +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/57b6e53ed4d2b2e38abff6a3ea56841bad6be8a9.1712399981.git.lorenzo@kernel.org +Signed-off-by: Stephen Boyd +--- + drivers/clk/clk-en7523.c | 157 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 152 insertions(+), 5 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -10,7 +10,9 @@ + #define REG_PCI_CONTROL 0x88 + #define REG_PCI_CONTROL_PERSTOUT BIT(29) + #define REG_PCI_CONTROL_PERSTOUT1 BIT(26) ++#define REG_PCI_CONTROL_REFCLK_EN0 BIT(23) + #define REG_PCI_CONTROL_REFCLK_EN1 BIT(22) ++#define REG_PCI_CONTROL_PERSTOUT2 BIT(16) + #define REG_GSW_CLK_DIV_SEL 0x1b4 + #define REG_EMI_CLK_DIV_SEL 0x1b8 + #define REG_BUS_CLK_DIV_SEL 0x1bc +@@ -18,10 +20,25 @@ + #define REG_SPI_CLK_FREQ_SEL 0x1c8 + #define REG_NPU_CLK_DIV_SEL 0x1fc + #define REG_CRYPTO_CLKSRC 0x200 +-#define REG_RESET_CONTROL 0x834 ++#define REG_RESET_CONTROL2 0x830 ++#define REG_RESET2_CONTROL_PCIE2 BIT(27) ++#define REG_RESET_CONTROL1 0x834 + #define REG_RESET_CONTROL_PCIEHB BIT(29) + #define REG_RESET_CONTROL_PCIE1 BIT(27) + #define REG_RESET_CONTROL_PCIE2 BIT(26) ++/* EN7581 */ ++#define REG_PCIE0_MEM 0x00 ++#define REG_PCIE0_MEM_MASK 0x04 ++#define REG_PCIE1_MEM 0x08 ++#define REG_PCIE1_MEM_MASK 0x0c ++#define REG_PCIE2_MEM 0x10 ++#define REG_PCIE2_MEM_MASK 0x14 ++#define REG_PCIE_RESET_OPEN_DRAIN 0x018c ++#define REG_PCIE_RESET_OPEN_DRAIN_MASK GENMASK(2, 0) ++#define REG_NP_SCU_PCIC 0x88 ++#define REG_NP_SCU_SSTR 0x9c ++#define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13) ++#define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11) + + struct en_clk_desc { + int id; +@@ -50,6 +67,8 @@ struct en_clk_gate { + + struct en_clk_soc_data { + const struct clk_ops pcie_ops; ++ int (*hw_init)(struct platform_device *pdev, void __iomem *base, ++ void __iomem *np_base); + }; + + static const u32 gsw_base[] = { 400000000, 500000000 }; +@@ -216,14 +235,14 @@ static int en7523_pci_prepare(struct clk + usleep_range(1000, 2000); + + /* Reset to default */ +- val = readl(np_base + REG_RESET_CONTROL); ++ val = readl(np_base + REG_RESET_CONTROL1); + mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 | + REG_RESET_CONTROL_PCIEHB; +- writel(val & ~mask, np_base + REG_RESET_CONTROL); ++ writel(val & ~mask, np_base + REG_RESET_CONTROL1); + usleep_range(1000, 2000); +- writel(val | mask, np_base + REG_RESET_CONTROL); ++ writel(val | mask, np_base + REG_RESET_CONTROL1); + msleep(100); +- writel(val & ~mask, np_base + REG_RESET_CONTROL); ++ writel(val & ~mask, np_base + REG_RESET_CONTROL1); + usleep_range(5000, 10000); + + /* Release device */ +@@ -264,6 +283,9 @@ static struct clk_hw *en7523_register_pc + + cg->base = np_base; + cg->hw.init = &init; ++ ++ if (init.ops->disable) ++ init.ops->disable(&cg->hw); + init.ops->unprepare(&cg->hw); + + if (clk_hw_register(dev, &cg->hw)) +@@ -272,6 +294,111 @@ static struct clk_hw *en7523_register_pc + return &cg->hw; + } + ++static int en7581_pci_is_enabled(struct clk_hw *hw) ++{ ++ struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); ++ u32 val, mask; ++ ++ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1; ++ val = readl(cg->base + REG_PCI_CONTROL); ++ return (val & mask) == mask; ++} ++ ++static int en7581_pci_prepare(struct clk_hw *hw) ++{ ++ struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); ++ void __iomem *np_base = cg->base; ++ u32 val, mask; ++ ++ mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 | ++ REG_RESET_CONTROL_PCIEHB; ++ val = readl(np_base + REG_RESET_CONTROL1); ++ writel(val & ~mask, np_base + REG_RESET_CONTROL1); ++ val = readl(np_base + REG_RESET_CONTROL2); ++ writel(val & ~REG_RESET2_CONTROL_PCIE2, np_base + REG_RESET_CONTROL2); ++ usleep_range(5000, 10000); ++ ++ return 0; ++} ++ ++static int en7581_pci_enable(struct clk_hw *hw) ++{ ++ struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); ++ void __iomem *np_base = cg->base; ++ u32 val, mask; ++ ++ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | ++ REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | ++ REG_PCI_CONTROL_PERSTOUT; ++ val = readl(np_base + REG_PCI_CONTROL); ++ writel(val | mask, np_base + REG_PCI_CONTROL); ++ msleep(250); ++ ++ return 0; ++} ++ ++static void en7581_pci_unprepare(struct clk_hw *hw) ++{ ++ struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); ++ void __iomem *np_base = cg->base; ++ u32 val, mask; ++ ++ mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 | ++ REG_RESET_CONTROL_PCIEHB; ++ val = readl(np_base + REG_RESET_CONTROL1); ++ writel(val | mask, np_base + REG_RESET_CONTROL1); ++ mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2; ++ writel(val | mask, np_base + REG_RESET_CONTROL1); ++ val = readl(np_base + REG_RESET_CONTROL2); ++ writel(val | REG_RESET_CONTROL_PCIE2, np_base + REG_RESET_CONTROL2); ++ msleep(100); ++} ++ ++static void en7581_pci_disable(struct clk_hw *hw) ++{ ++ struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); ++ void __iomem *np_base = cg->base; ++ u32 val, mask; ++ ++ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | ++ REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | ++ REG_PCI_CONTROL_PERSTOUT; ++ val = readl(np_base + REG_PCI_CONTROL); ++ writel(val & ~mask, np_base + REG_PCI_CONTROL); ++ usleep_range(1000, 2000); ++} ++ ++static int en7581_clk_hw_init(struct platform_device *pdev, ++ void __iomem *base, ++ void __iomem *np_base) ++{ ++ void __iomem *pb_base; ++ u32 val; ++ ++ pb_base = devm_platform_ioremap_resource(pdev, 2); ++ if (IS_ERR(pb_base)) ++ return PTR_ERR(pb_base); ++ ++ val = readl(np_base + REG_NP_SCU_SSTR); ++ val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); ++ writel(val, np_base + REG_NP_SCU_SSTR); ++ val = readl(np_base + REG_NP_SCU_PCIC); ++ writel(val | 3, np_base + REG_NP_SCU_PCIC); ++ ++ writel(0x20000000, pb_base + REG_PCIE0_MEM); ++ writel(0xfc000000, pb_base + REG_PCIE0_MEM_MASK); ++ writel(0x24000000, pb_base + REG_PCIE1_MEM); ++ writel(0xfc000000, pb_base + REG_PCIE1_MEM_MASK); ++ writel(0x28000000, pb_base + REG_PCIE2_MEM); ++ writel(0xfc000000, pb_base + REG_PCIE2_MEM_MASK); ++ ++ val = readl(base + REG_PCIE_RESET_OPEN_DRAIN); ++ writel(val | REG_PCIE_RESET_OPEN_DRAIN_MASK, ++ base + REG_PCIE_RESET_OPEN_DRAIN); ++ ++ return 0; ++} ++ + static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, + void __iomem *base, void __iomem *np_base) + { +@@ -304,6 +431,7 @@ static void en7523_register_clocks(struc + static int en7523_clk_probe(struct platform_device *pdev) + { + struct device_node *node = pdev->dev.of_node; ++ const struct en_clk_soc_data *soc_data; + struct clk_hw_onecell_data *clk_data; + void __iomem *base, *np_base; + int r; +@@ -316,6 +444,13 @@ static int en7523_clk_probe(struct platf + if (IS_ERR(np_base)) + return PTR_ERR(np_base); + ++ soc_data = device_get_match_data(&pdev->dev); ++ if (soc_data->hw_init) { ++ r = soc_data->hw_init(pdev, base, np_base); ++ if (r) ++ return r; ++ } ++ + clk_data = devm_kzalloc(&pdev->dev, + struct_size(clk_data, hws, EN7523_NUM_CLOCKS), + GFP_KERNEL); +@@ -341,8 +476,20 @@ static const struct en_clk_soc_data en75 + }, + }; + ++static const struct en_clk_soc_data en7581_data = { ++ .pcie_ops = { ++ .is_enabled = en7581_pci_is_enabled, ++ .prepare = en7581_pci_prepare, ++ .enable = en7581_pci_enable, ++ .unprepare = en7581_pci_unprepare, ++ .disable = en7581_pci_disable, ++ }, ++ .hw_init = en7581_clk_hw_init, ++}; ++ + static const struct of_device_id of_match_clk_en7523[] = { + { .compatible = "airoha,en7523-scu", .data = &en7523_data }, ++ { .compatible = "airoha,en7581-scu", .data = &en7581_data }, + { /* sentinel */ } + }; + diff --git a/target/linux/airoha/patches-6.6/019-01-v6.11-clk-en7523-Add-reset-controller-support-for-EN7581-S.patch b/target/linux/airoha/patches-6.6/019-01-v6.11-clk-en7523-Add-reset-controller-support-for-EN7581-S.patch new file mode 100644 index 0000000000..f7cc5159ea --- /dev/null +++ b/target/linux/airoha/patches-6.6/019-01-v6.11-clk-en7523-Add-reset-controller-support-for-EN7581-S.patch @@ -0,0 +1,270 @@ +From e0d8ea4ed5fa70fd085a54d0b574a044b9407c39 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 27 Jun 2024 13:04:23 +0200 +Subject: [PATCH 1/4] clk: en7523: Add reset-controller support for EN7581 SoC + +Introduce reset API support to EN7581 clock driver. + +Reviewed-by: AngeloGioacchino Del Regno +Tested-by: Zhengping Zhang +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/4f735d17e549ea53769bf5a3f50406debb879a44.1719485847.git.lorenzo@kernel.org +Signed-off-by: Stephen Boyd +--- + drivers/clk/clk-en7523.c | 192 ++++++++++++++++++++++++++++++++++++++- + 1 file changed, 187 insertions(+), 5 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -5,7 +5,11 @@ + #include + #include + #include ++#include + #include ++#include ++ ++#define RST_NR_PER_BANK 32 + + #define REG_PCI_CONTROL 0x88 + #define REG_PCI_CONTROL_PERSTOUT BIT(29) +@@ -40,6 +44,9 @@ + #define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13) + #define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11) + ++#define REG_RST_CTRL2 0x00 ++#define REG_RST_CTRL1 0x04 ++ + struct en_clk_desc { + int id; + const char *name; +@@ -65,8 +72,20 @@ struct en_clk_gate { + struct clk_hw hw; + }; + ++struct en_rst_data { ++ const u16 *bank_ofs; ++ const u16 *idx_map; ++ void __iomem *base; ++ struct reset_controller_dev rcdev; ++}; ++ + struct en_clk_soc_data { + const struct clk_ops pcie_ops; ++ struct { ++ const u16 *bank_ofs; ++ const u16 *idx_map; ++ u16 idx_map_nr; ++ } reset; + int (*hw_init)(struct platform_device *pdev, void __iomem *base, + void __iomem *np_base); + }; +@@ -173,6 +192,69 @@ static const struct en_clk_desc en7523_b + } + }; + ++static const u16 en7581_rst_ofs[] = { ++ REG_RST_CTRL2, ++ REG_RST_CTRL1, ++}; ++ ++static const u16 en7581_rst_map[] = { ++ /* RST_CTRL2 */ ++ [EN7581_XPON_PHY_RST] = 0, ++ [EN7581_CPU_TIMER2_RST] = 2, ++ [EN7581_HSUART_RST] = 3, ++ [EN7581_UART4_RST] = 4, ++ [EN7581_UART5_RST] = 5, ++ [EN7581_I2C2_RST] = 6, ++ [EN7581_XSI_MAC_RST] = 7, ++ [EN7581_XSI_PHY_RST] = 8, ++ [EN7581_NPU_RST] = 9, ++ [EN7581_I2S_RST] = 10, ++ [EN7581_TRNG_RST] = 11, ++ [EN7581_TRNG_MSTART_RST] = 12, ++ [EN7581_DUAL_HSI0_RST] = 13, ++ [EN7581_DUAL_HSI1_RST] = 14, ++ [EN7581_HSI_RST] = 15, ++ [EN7581_DUAL_HSI0_MAC_RST] = 16, ++ [EN7581_DUAL_HSI1_MAC_RST] = 17, ++ [EN7581_HSI_MAC_RST] = 18, ++ [EN7581_WDMA_RST] = 19, ++ [EN7581_WOE0_RST] = 20, ++ [EN7581_WOE1_RST] = 21, ++ [EN7581_HSDMA_RST] = 22, ++ [EN7581_TDMA_RST] = 24, ++ [EN7581_EMMC_RST] = 25, ++ [EN7581_SOE_RST] = 26, ++ [EN7581_PCIE2_RST] = 27, ++ [EN7581_XFP_MAC_RST] = 28, ++ [EN7581_USB_HOST_P1_RST] = 29, ++ [EN7581_USB_HOST_P1_U3_PHY_RST] = 30, ++ /* RST_CTRL1 */ ++ [EN7581_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0, ++ [EN7581_FE_PDMA_RST] = RST_NR_PER_BANK + 1, ++ [EN7581_FE_QDMA_RST] = RST_NR_PER_BANK + 2, ++ [EN7581_PCM_SPIWP_RST] = RST_NR_PER_BANK + 4, ++ [EN7581_CRYPTO_RST] = RST_NR_PER_BANK + 6, ++ [EN7581_TIMER_RST] = RST_NR_PER_BANK + 8, ++ [EN7581_PCM1_RST] = RST_NR_PER_BANK + 11, ++ [EN7581_UART_RST] = RST_NR_PER_BANK + 12, ++ [EN7581_GPIO_RST] = RST_NR_PER_BANK + 13, ++ [EN7581_GDMA_RST] = RST_NR_PER_BANK + 14, ++ [EN7581_I2C_MASTER_RST] = RST_NR_PER_BANK + 16, ++ [EN7581_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17, ++ [EN7581_SFC_RST] = RST_NR_PER_BANK + 18, ++ [EN7581_UART2_RST] = RST_NR_PER_BANK + 19, ++ [EN7581_GDMP_RST] = RST_NR_PER_BANK + 20, ++ [EN7581_FE_RST] = RST_NR_PER_BANK + 21, ++ [EN7581_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22, ++ [EN7581_GSW_RST] = RST_NR_PER_BANK + 23, ++ [EN7581_SFC2_PCM_RST] = RST_NR_PER_BANK + 25, ++ [EN7581_PCIE0_RST] = RST_NR_PER_BANK + 26, ++ [EN7581_PCIE1_RST] = RST_NR_PER_BANK + 27, ++ [EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28, ++ [EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29, ++ [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31, ++}; ++ + static unsigned int en7523_get_base_rate(void __iomem *base, unsigned int i) + { + const struct en_clk_desc *desc = &en7523_base_clks[i]; +@@ -375,7 +457,7 @@ static int en7581_clk_hw_init(struct pla + void __iomem *pb_base; + u32 val; + +- pb_base = devm_platform_ioremap_resource(pdev, 2); ++ pb_base = devm_platform_ioremap_resource(pdev, 3); + if (IS_ERR(pb_base)) + return PTR_ERR(pb_base); + +@@ -428,6 +510,95 @@ static void en7523_register_clocks(struc + clk_data->num = EN7523_NUM_CLOCKS; + } + ++static int en7523_reset_update(struct reset_controller_dev *rcdev, ++ unsigned long id, bool assert) ++{ ++ struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev); ++ void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK]; ++ u32 val; ++ ++ val = readl(addr); ++ if (assert) ++ val |= BIT(id % RST_NR_PER_BANK); ++ else ++ val &= ~BIT(id % RST_NR_PER_BANK); ++ writel(val, addr); ++ ++ return 0; ++} ++ ++static int en7523_reset_assert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ return en7523_reset_update(rcdev, id, true); ++} ++ ++static int en7523_reset_deassert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ return en7523_reset_update(rcdev, id, false); ++} ++ ++static int en7523_reset_status(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev); ++ void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK]; ++ ++ return !!(readl(addr) & BIT(id % RST_NR_PER_BANK)); ++} ++ ++static int en7523_reset_xlate(struct reset_controller_dev *rcdev, ++ const struct of_phandle_args *reset_spec) ++{ ++ struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev); ++ ++ if (reset_spec->args[0] >= rcdev->nr_resets) ++ return -EINVAL; ++ ++ return rst_data->idx_map[reset_spec->args[0]]; ++} ++ ++static const struct reset_control_ops en7523_reset_ops = { ++ .assert = en7523_reset_assert, ++ .deassert = en7523_reset_deassert, ++ .status = en7523_reset_status, ++}; ++ ++static int en7523_reset_register(struct platform_device *pdev, ++ const struct en_clk_soc_data *soc_data) ++{ ++ struct device *dev = &pdev->dev; ++ struct en_rst_data *rst_data; ++ void __iomem *base; ++ ++ /* no reset lines available */ ++ if (!soc_data->reset.idx_map_nr) ++ return 0; ++ ++ base = devm_platform_ioremap_resource(pdev, 2); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ rst_data = devm_kzalloc(dev, sizeof(*rst_data), GFP_KERNEL); ++ if (!rst_data) ++ return -ENOMEM; ++ ++ rst_data->bank_ofs = soc_data->reset.bank_ofs; ++ rst_data->idx_map = soc_data->reset.idx_map; ++ rst_data->base = base; ++ ++ rst_data->rcdev.nr_resets = soc_data->reset.idx_map_nr; ++ rst_data->rcdev.of_xlate = en7523_reset_xlate; ++ rst_data->rcdev.ops = &en7523_reset_ops; ++ rst_data->rcdev.of_node = dev->of_node; ++ rst_data->rcdev.of_reset_n_cells = 1; ++ rst_data->rcdev.owner = THIS_MODULE; ++ rst_data->rcdev.dev = dev; ++ ++ return devm_reset_controller_register(dev, &rst_data->rcdev); ++} ++ + static int en7523_clk_probe(struct platform_device *pdev) + { + struct device_node *node = pdev->dev.of_node; +@@ -461,11 +632,17 @@ static int en7523_clk_probe(struct platf + + r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); + if (r) +- dev_err(&pdev->dev, +- "could not register clock provider: %s: %d\n", +- pdev->name, r); ++ return dev_err_probe(&pdev->dev, r, "Could not register clock provider: %s\n", ++ pdev->name); ++ ++ r = en7523_reset_register(pdev, soc_data); ++ if (r) { ++ of_clk_del_provider(node); ++ return dev_err_probe(&pdev->dev, r, "Could not register reset controller: %s\n", ++ pdev->name); ++ } + +- return r; ++ return 0; + } + + static const struct en_clk_soc_data en7523_data = { +@@ -484,6 +661,11 @@ static const struct en_clk_soc_data en75 + .unprepare = en7581_pci_unprepare, + .disable = en7581_pci_disable, + }, ++ .reset = { ++ .bank_ofs = en7581_rst_ofs, ++ .idx_map = en7581_rst_map, ++ .idx_map_nr = ARRAY_SIZE(en7581_rst_map), ++ }, + .hw_init = en7581_clk_hw_init, + }; + diff --git a/target/linux/airoha/patches-6.6/019-02-v6.11-clk-en7523-Remove-pcie-prepare-unpreare-callbacks-fo.patch b/target/linux/airoha/patches-6.6/019-02-v6.11-clk-en7523-Remove-pcie-prepare-unpreare-callbacks-fo.patch new file mode 100644 index 0000000000..2a32ad0cff --- /dev/null +++ b/target/linux/airoha/patches-6.6/019-02-v6.11-clk-en7523-Remove-pcie-prepare-unpreare-callbacks-fo.patch @@ -0,0 +1,91 @@ +From db7a4a11e8be375b0a9c159f688e0cea49eacc5d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 27 Jun 2024 13:04:24 +0200 +Subject: [PATCH 2/4] clk: en7523: Remove pcie prepare/unpreare callbacks for + EN7581 SoC + +Get rid of prepare and unpreare callbacks for PCIe clock since they can +be modeled as a reset line cosumed by the PCIe driver +(pcie-mediatek-gen3) + +Reviewed-by: AngeloGioacchino Del Regno +Tested-by: Zhengping Zhang +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/16df149975514d3030499c48fc1c64f090093595.1719485847.git.lorenzo@kernel.org +Signed-off-by: Stephen Boyd +--- + drivers/clk/clk-en7523.c | 41 ++-------------------------------------- + 1 file changed, 2 insertions(+), 39 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -366,9 +366,8 @@ static struct clk_hw *en7523_register_pc + cg->base = np_base; + cg->hw.init = &init; + +- if (init.ops->disable) +- init.ops->disable(&cg->hw); +- init.ops->unprepare(&cg->hw); ++ if (init.ops->unprepare) ++ init.ops->unprepare(&cg->hw); + + if (clk_hw_register(dev, &cg->hw)) + return NULL; +@@ -386,23 +385,6 @@ static int en7581_pci_is_enabled(struct + return (val & mask) == mask; + } + +-static int en7581_pci_prepare(struct clk_hw *hw) +-{ +- struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); +- void __iomem *np_base = cg->base; +- u32 val, mask; +- +- mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 | +- REG_RESET_CONTROL_PCIEHB; +- val = readl(np_base + REG_RESET_CONTROL1); +- writel(val & ~mask, np_base + REG_RESET_CONTROL1); +- val = readl(np_base + REG_RESET_CONTROL2); +- writel(val & ~REG_RESET2_CONTROL_PCIE2, np_base + REG_RESET_CONTROL2); +- usleep_range(5000, 10000); +- +- return 0; +-} +- + static int en7581_pci_enable(struct clk_hw *hw) + { + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); +@@ -419,23 +401,6 @@ static int en7581_pci_enable(struct clk_ + return 0; + } + +-static void en7581_pci_unprepare(struct clk_hw *hw) +-{ +- struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); +- void __iomem *np_base = cg->base; +- u32 val, mask; +- +- mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 | +- REG_RESET_CONTROL_PCIEHB; +- val = readl(np_base + REG_RESET_CONTROL1); +- writel(val | mask, np_base + REG_RESET_CONTROL1); +- mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2; +- writel(val | mask, np_base + REG_RESET_CONTROL1); +- val = readl(np_base + REG_RESET_CONTROL2); +- writel(val | REG_RESET_CONTROL_PCIE2, np_base + REG_RESET_CONTROL2); +- msleep(100); +-} +- + static void en7581_pci_disable(struct clk_hw *hw) + { + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); +@@ -656,9 +621,7 @@ static const struct en_clk_soc_data en75 + static const struct en_clk_soc_data en7581_data = { + .pcie_ops = { + .is_enabled = en7581_pci_is_enabled, +- .prepare = en7581_pci_prepare, + .enable = en7581_pci_enable, +- .unprepare = en7581_pci_unprepare, + .disable = en7581_pci_disable, + }, + .reset = { diff --git a/target/linux/airoha/patches-6.6/019-03-v6.11-clk-en7523-Remove-PCIe-reset-open-drain-configuratio.patch b/target/linux/airoha/patches-6.6/019-03-v6.11-clk-en7523-Remove-PCIe-reset-open-drain-configuratio.patch new file mode 100644 index 0000000000..8a4b9c7340 --- /dev/null +++ b/target/linux/airoha/patches-6.6/019-03-v6.11-clk-en7523-Remove-PCIe-reset-open-drain-configuratio.patch @@ -0,0 +1,65 @@ +From bf288bd25d6232310abb81db417376ce460eb032 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 27 Jun 2024 13:04:25 +0200 +Subject: [PATCH 3/4] clk: en7523: Remove PCIe reset open drain configuration + for EN7581 + +PCIe reset open drain configuration will be managed by pinctrl driver. + +Reviewed-by: AngeloGioacchino Del Regno +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/43276af5f08a554b4ab2e52e8d437fff5c06a732.1719485847.git.lorenzo@kernel.org +Signed-off-by: Stephen Boyd +--- + drivers/clk/clk-en7523.c | 12 ++---------- + 1 file changed, 2 insertions(+), 10 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -37,8 +37,6 @@ + #define REG_PCIE1_MEM_MASK 0x0c + #define REG_PCIE2_MEM 0x10 + #define REG_PCIE2_MEM_MASK 0x14 +-#define REG_PCIE_RESET_OPEN_DRAIN 0x018c +-#define REG_PCIE_RESET_OPEN_DRAIN_MASK GENMASK(2, 0) + #define REG_NP_SCU_PCIC 0x88 + #define REG_NP_SCU_SSTR 0x9c + #define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13) +@@ -86,8 +84,7 @@ struct en_clk_soc_data { + const u16 *idx_map; + u16 idx_map_nr; + } reset; +- int (*hw_init)(struct platform_device *pdev, void __iomem *base, +- void __iomem *np_base); ++ int (*hw_init)(struct platform_device *pdev, void __iomem *np_base); + }; + + static const u32 gsw_base[] = { 400000000, 500000000 }; +@@ -416,7 +413,6 @@ static void en7581_pci_disable(struct cl + } + + static int en7581_clk_hw_init(struct platform_device *pdev, +- void __iomem *base, + void __iomem *np_base) + { + void __iomem *pb_base; +@@ -439,10 +435,6 @@ static int en7581_clk_hw_init(struct pla + writel(0x28000000, pb_base + REG_PCIE2_MEM); + writel(0xfc000000, pb_base + REG_PCIE2_MEM_MASK); + +- val = readl(base + REG_PCIE_RESET_OPEN_DRAIN); +- writel(val | REG_PCIE_RESET_OPEN_DRAIN_MASK, +- base + REG_PCIE_RESET_OPEN_DRAIN); +- + return 0; + } + +@@ -582,7 +574,7 @@ static int en7523_clk_probe(struct platf + + soc_data = device_get_match_data(&pdev->dev); + if (soc_data->hw_init) { +- r = soc_data->hw_init(pdev, base, np_base); ++ r = soc_data->hw_init(pdev, np_base); + if (r) + return r; + } diff --git a/target/linux/airoha/patches-6.6/020-v6.11-dt-bindings-clock-airoha-Add-reset-support-to-EN7581.patch b/target/linux/airoha/patches-6.6/020-v6.11-dt-bindings-clock-airoha-Add-reset-support-to-EN7581.patch new file mode 100644 index 0000000000..49ab4e9786 --- /dev/null +++ b/target/linux/airoha/patches-6.6/020-v6.11-dt-bindings-clock-airoha-Add-reset-support-to-EN7581.patch @@ -0,0 +1,93 @@ +From 7aa291962f4c3b7afb9a12fa60b406b95e5eacb4 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 27 Jun 2024 13:04:22 +0200 +Subject: [PATCH] dt-bindings: clock: airoha: Add reset support to EN7581 clock + binding + +Introduce reset capability to EN7581 device-tree clock binding +documentation. Add reset register mapping between misc scu and pb scu +ones in order to follow the memory order. This change is not +introducing any backward compatibility issue since the EN7581 dts is not +upstream yet. + +Fixes: 0a382be005cf ("dt-bindings: clock: airoha: add EN7581 binding") +Reviewed-by: AngeloGioacchino Del Regno +Reviewed-by: Rob Herring (Arm) +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/28fef3e83062d5d71e7b4be4b47583f851a15bf8.1719485847.git.lorenzo@kernel.org +Signed-off-by: Stephen Boyd +--- + .../bindings/clock/airoha,en7523-scu.yaml | 25 ++++++- + .../dt-bindings/reset/airoha,en7581-reset.h | 66 +++++++++++++++++++ + 2 files changed, 90 insertions(+), 1 deletion(-) + create mode 100644 include/dt-bindings/reset/airoha,en7581-reset.h + +--- /dev/null ++++ b/include/dt-bindings/reset/airoha,en7581-reset.h +@@ -0,0 +1,66 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#ifndef __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7581_H_ ++#define __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7581_H_ ++ ++/* RST_CTRL2 */ ++#define EN7581_XPON_PHY_RST 0 ++#define EN7581_CPU_TIMER2_RST 1 ++#define EN7581_HSUART_RST 2 ++#define EN7581_UART4_RST 3 ++#define EN7581_UART5_RST 4 ++#define EN7581_I2C2_RST 5 ++#define EN7581_XSI_MAC_RST 6 ++#define EN7581_XSI_PHY_RST 7 ++#define EN7581_NPU_RST 8 ++#define EN7581_I2S_RST 9 ++#define EN7581_TRNG_RST 10 ++#define EN7581_TRNG_MSTART_RST 11 ++#define EN7581_DUAL_HSI0_RST 12 ++#define EN7581_DUAL_HSI1_RST 13 ++#define EN7581_HSI_RST 14 ++#define EN7581_DUAL_HSI0_MAC_RST 15 ++#define EN7581_DUAL_HSI1_MAC_RST 16 ++#define EN7581_HSI_MAC_RST 17 ++#define EN7581_WDMA_RST 18 ++#define EN7581_WOE0_RST 19 ++#define EN7581_WOE1_RST 20 ++#define EN7581_HSDMA_RST 21 ++#define EN7581_TDMA_RST 22 ++#define EN7581_EMMC_RST 23 ++#define EN7581_SOE_RST 24 ++#define EN7581_PCIE2_RST 25 ++#define EN7581_XFP_MAC_RST 26 ++#define EN7581_USB_HOST_P1_RST 27 ++#define EN7581_USB_HOST_P1_U3_PHY_RST 28 ++/* RST_CTRL1 */ ++#define EN7581_PCM1_ZSI_ISI_RST 29 ++#define EN7581_FE_PDMA_RST 30 ++#define EN7581_FE_QDMA_RST 31 ++#define EN7581_PCM_SPIWP_RST 32 ++#define EN7581_CRYPTO_RST 33 ++#define EN7581_TIMER_RST 34 ++#define EN7581_PCM1_RST 35 ++#define EN7581_UART_RST 36 ++#define EN7581_GPIO_RST 37 ++#define EN7581_GDMA_RST 38 ++#define EN7581_I2C_MASTER_RST 39 ++#define EN7581_PCM2_ZSI_ISI_RST 40 ++#define EN7581_SFC_RST 41 ++#define EN7581_UART2_RST 42 ++#define EN7581_GDMP_RST 43 ++#define EN7581_FE_RST 44 ++#define EN7581_USB_HOST_P0_RST 45 ++#define EN7581_GSW_RST 46 ++#define EN7581_SFC2_PCM_RST 47 ++#define EN7581_PCIE0_RST 48 ++#define EN7581_PCIE1_RST 49 ++#define EN7581_CPU_TIMER_RST 50 ++#define EN7581_PCIE_HB_RST 51 ++#define EN7581_XPON_MAC_RST 52 ++ ++#endif /* __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7581_H_ */ diff --git a/target/linux/airoha/patches-6.6/021-01-v6.12-PCI-mediatek-gen3-Add-mtk_gen3_pcie_pdata-data-struc.patch b/target/linux/airoha/patches-6.6/021-01-v6.12-PCI-mediatek-gen3-Add-mtk_gen3_pcie_pdata-data-struc.patch new file mode 100644 index 0000000000..f09e69dc9a --- /dev/null +++ b/target/linux/airoha/patches-6.6/021-01-v6.12-PCI-mediatek-gen3-Add-mtk_gen3_pcie_pdata-data-struc.patch @@ -0,0 +1,100 @@ +From dc869a40d73ee6e9f47d683690ae507e30e56044 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 3 Jul 2024 18:12:42 +0200 +Subject: [PATCH 1/3] PCI: mediatek-gen3: Add mtk_gen3_pcie_pdata data + structure +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce mtk_gen3_pcie_pdata data structure in order to define +multiple callbacks for each supported SoC. + +This is a preliminary patch to introduce EN7581 PCIe support. + +Link: https://lore.kernel.org/linux-pci/c193d1a87505d045e2e0ef33317bce17012ee095.1720022580.git.lorenzo@kernel.org +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Krzysztof Wilczyński +Tested-by: Zhengping Zhang +Reviewed-by: AngeloGioacchino Del Regno +Acked-by: Jianjun Wang +--- + drivers/pci/controller/pcie-mediatek-gen3.c | 24 ++++++++++++++++++--- + 1 file changed, 21 insertions(+), 3 deletions(-) + +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -100,6 +100,16 @@ + #define PCIE_ATR_TLP_TYPE_MEM PCIE_ATR_TLP_TYPE(0) + #define PCIE_ATR_TLP_TYPE_IO PCIE_ATR_TLP_TYPE(2) + ++struct mtk_gen3_pcie; ++ ++/** ++ * struct mtk_gen3_pcie_pdata - differentiate between host generations ++ * @power_up: pcie power_up callback ++ */ ++struct mtk_gen3_pcie_pdata { ++ int (*power_up)(struct mtk_gen3_pcie *pcie); ++}; ++ + /** + * struct mtk_msi_set - MSI information for each set + * @base: IO mapped register base +@@ -131,6 +141,7 @@ struct mtk_msi_set { + * @msi_sets: MSI sets information + * @lock: lock protecting IRQ bit map + * @msi_irq_in_use: bit map for assigned MSI IRQ ++ * @soc: pointer to SoC-dependent operations + */ + struct mtk_gen3_pcie { + struct device *dev; +@@ -151,6 +162,8 @@ struct mtk_gen3_pcie { + struct mtk_msi_set msi_sets[PCIE_MSI_SET_NUM]; + struct mutex lock; + DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_IRQS_NUM); ++ ++ const struct mtk_gen3_pcie_pdata *soc; + }; + + /* LTSSM state in PCIE_LTSSM_STATUS_REG bit[28:24] */ +@@ -904,7 +917,7 @@ static int mtk_pcie_setup(struct mtk_gen + usleep_range(10, 20); + + /* Don't touch the hardware registers before power up */ +- err = mtk_pcie_power_up(pcie); ++ err = pcie->soc->power_up(pcie); + if (err) + return err; + +@@ -939,6 +952,7 @@ static int mtk_pcie_probe(struct platfor + pcie = pci_host_bridge_priv(host); + + pcie->dev = dev; ++ pcie->soc = device_get_match_data(dev); + platform_set_drvdata(pdev, pcie); + + err = mtk_pcie_setup(pcie); +@@ -1054,7 +1068,7 @@ static int mtk_pcie_resume_noirq(struct + struct mtk_gen3_pcie *pcie = dev_get_drvdata(dev); + int err; + +- err = mtk_pcie_power_up(pcie); ++ err = pcie->soc->power_up(pcie); + if (err) + return err; + +@@ -1074,8 +1088,12 @@ static const struct dev_pm_ops mtk_pcie_ + mtk_pcie_resume_noirq) + }; + ++static const struct mtk_gen3_pcie_pdata mtk_pcie_soc_mt8192 = { ++ .power_up = mtk_pcie_power_up, ++}; ++ + static const struct of_device_id mtk_pcie_of_match[] = { +- { .compatible = "mediatek,mt8192-pcie" }, ++ { .compatible = "mediatek,mt8192-pcie", .data = &mtk_pcie_soc_mt8192 }, + {}, + }; + MODULE_DEVICE_TABLE(of, mtk_pcie_of_match); diff --git a/target/linux/airoha/patches-6.6/021-02-v6.12-PCI-mediatek-gen3-Rely-on-reset_bulk-APIs-for-PHY-re.patch b/target/linux/airoha/patches-6.6/021-02-v6.12-PCI-mediatek-gen3-Rely-on-reset_bulk-APIs-for-PHY-re.patch new file mode 100644 index 0000000000..5fbbc832d4 --- /dev/null +++ b/target/linux/airoha/patches-6.6/021-02-v6.12-PCI-mediatek-gen3-Rely-on-reset_bulk-APIs-for-PHY-re.patch @@ -0,0 +1,155 @@ +From ee9eabbe3f0f0c7458d89840add97e54d4e0bccf Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 3 Jul 2024 18:12:43 +0200 +Subject: [PATCH 2/3] PCI: mediatek-gen3: Rely on reset_bulk APIs for PHY reset + lines +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use reset_bulk APIs to manage PHY reset lines. + +This is a preliminary patch in order to add Airoha EN7581 PCIe support. + +Link: https://lore.kernel.org/linux-pci/3ceb83bc0defbcf868521f8df4b9100e55ec2614.1720022580.git.lorenzo@kernel.org +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Krzysztof Wilczyński +Tested-by: Zhengping Zhang +Reviewed-by: AngeloGioacchino Del Regno +Acked-by: Jianjun Wang +--- + drivers/pci/controller/pcie-mediatek-gen3.c | 45 +++++++++++++++------ + 1 file changed, 33 insertions(+), 12 deletions(-) + +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -100,14 +100,21 @@ + #define PCIE_ATR_TLP_TYPE_MEM PCIE_ATR_TLP_TYPE(0) + #define PCIE_ATR_TLP_TYPE_IO PCIE_ATR_TLP_TYPE(2) + ++#define MAX_NUM_PHY_RESETS 1 ++ + struct mtk_gen3_pcie; + + /** + * struct mtk_gen3_pcie_pdata - differentiate between host generations + * @power_up: pcie power_up callback ++ * @phy_resets: phy reset lines SoC data. + */ + struct mtk_gen3_pcie_pdata { + int (*power_up)(struct mtk_gen3_pcie *pcie); ++ struct { ++ const char *id[MAX_NUM_PHY_RESETS]; ++ int num_resets; ++ } phy_resets; + }; + + /** +@@ -128,7 +135,7 @@ struct mtk_msi_set { + * @base: IO mapped register base + * @reg_base: physical register base + * @mac_reset: MAC reset control +- * @phy_reset: PHY reset control ++ * @phy_resets: PHY reset controllers + * @phy: PHY controller block + * @clks: PCIe clocks + * @num_clks: PCIe clocks count for this port +@@ -148,7 +155,7 @@ struct mtk_gen3_pcie { + void __iomem *base; + phys_addr_t reg_base; + struct reset_control *mac_reset; +- struct reset_control *phy_reset; ++ struct reset_control_bulk_data phy_resets[MAX_NUM_PHY_RESETS]; + struct phy *phy; + struct clk_bulk_data *clks; + int num_clks; +@@ -788,10 +795,10 @@ static int mtk_pcie_setup_irq(struct mtk + + static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie) + { ++ int i, ret, num_resets = pcie->soc->phy_resets.num_resets; + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); + struct resource *regs; +- int ret; + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcie-mac"); + if (!regs) +@@ -804,12 +811,12 @@ static int mtk_pcie_parse_port(struct mt + + pcie->reg_base = regs->start; + +- pcie->phy_reset = devm_reset_control_get_optional_exclusive(dev, "phy"); +- if (IS_ERR(pcie->phy_reset)) { +- ret = PTR_ERR(pcie->phy_reset); +- if (ret != -EPROBE_DEFER) +- dev_err(dev, "failed to get PHY reset\n"); ++ for (i = 0; i < num_resets; i++) ++ pcie->phy_resets[i].id = pcie->soc->phy_resets.id[i]; + ++ ret = devm_reset_control_bulk_get_optional_shared(dev, num_resets, pcie->phy_resets); ++ if (ret) { ++ dev_err(dev, "failed to get PHY bulk reset\n"); + return ret; + } + +@@ -846,7 +853,11 @@ static int mtk_pcie_power_up(struct mtk_ + int err; + + /* PHY power on and enable pipe clock */ +- reset_control_deassert(pcie->phy_reset); ++ err = reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); ++ if (err) { ++ dev_err(dev, "failed to deassert PHYs\n"); ++ return err; ++ } + + err = phy_init(pcie->phy); + if (err) { +@@ -882,7 +893,7 @@ err_clk_init: + err_phy_on: + phy_exit(pcie->phy); + err_phy_init: +- reset_control_assert(pcie->phy_reset); ++ reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); + + return err; + } +@@ -897,7 +908,7 @@ static void mtk_pcie_power_down(struct m + + phy_power_off(pcie->phy); + phy_exit(pcie->phy); +- reset_control_assert(pcie->phy_reset); ++ reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); + } + + static int mtk_pcie_setup(struct mtk_gen3_pcie *pcie) +@@ -909,10 +920,16 @@ static int mtk_pcie_setup(struct mtk_gen + return err; + + /* ++ * Deassert the line in order to avoid unbalance in deassert_count ++ * counter since the bulk is shared. ++ */ ++ reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); ++ /* + * The controller may have been left out of reset by the bootloader + * so make sure that we get a clean start by asserting resets here. + */ +- reset_control_assert(pcie->phy_reset); ++ reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); ++ + reset_control_assert(pcie->mac_reset); + usleep_range(10, 20); + +@@ -1090,6 +1107,10 @@ static const struct dev_pm_ops mtk_pcie_ + + static const struct mtk_gen3_pcie_pdata mtk_pcie_soc_mt8192 = { + .power_up = mtk_pcie_power_up, ++ .phy_resets = { ++ .id[0] = "phy", ++ .num_resets = 1, ++ }, + }; + + static const struct of_device_id mtk_pcie_of_match[] = { diff --git a/target/linux/airoha/patches-6.6/021-03-v6.12-PCI-mediatek-gen3-Add-Airoha-EN7581-support.patch b/target/linux/airoha/patches-6.6/021-03-v6.12-PCI-mediatek-gen3-Add-Airoha-EN7581-support.patch new file mode 100644 index 0000000000..19b003d892 --- /dev/null +++ b/target/linux/airoha/patches-6.6/021-03-v6.12-PCI-mediatek-gen3-Add-Airoha-EN7581-support.patch @@ -0,0 +1,199 @@ +From f6ab898356dd70f267c49045a79d28ea5cf5e43e Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 3 Jul 2024 18:12:44 +0200 +Subject: [PATCH 3/3] PCI: mediatek-gen3: Add Airoha EN7581 support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce support for Airoha EN7581 PCIe controller to mediatek-gen3 +PCIe controller driver. + +Link: https://lore.kernel.org/linux-pci/aca00bd672ee576ad96d279414fc0835ff31f637.1720022580.git.lorenzo@kernel.org +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Krzysztof Wilczyński +Tested-by: Zhengping Zhang +Reviewed-by: AngeloGioacchino Del Regno +Acked-by: Jianjun Wang +--- + drivers/pci/controller/Kconfig | 2 +- + drivers/pci/controller/pcie-mediatek-gen3.c | 113 +++++++++++++++++++- + 2 files changed, 113 insertions(+), 2 deletions(-) + +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -196,7 +196,7 @@ config PCIE_MEDIATEK + + config PCIE_MEDIATEK_GEN3 + tristate "MediaTek Gen3 PCIe controller" +- depends on ARCH_MEDIATEK || COMPILE_TEST ++ depends on ARCH_AIROHA || ARCH_MEDIATEK || COMPILE_TEST + depends on PCI_MSI + help + Adds support for PCIe Gen3 MAC controller for MediaTek SoCs. +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -6,7 +6,9 @@ + * Author: Jianjun Wang + */ + ++#include + #include ++#include + #include + #include + #include +@@ -15,6 +17,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -29,6 +33,12 @@ + #define PCI_CLASS(class) (class << 8) + #define PCIE_RC_MODE BIT(0) + ++#define PCIE_EQ_PRESET_01_REG 0x100 ++#define PCIE_VAL_LN0_DOWNSTREAM GENMASK(6, 0) ++#define PCIE_VAL_LN0_UPSTREAM GENMASK(14, 8) ++#define PCIE_VAL_LN1_DOWNSTREAM GENMASK(22, 16) ++#define PCIE_VAL_LN1_UPSTREAM GENMASK(30, 24) ++ + #define PCIE_CFGNUM_REG 0x140 + #define PCIE_CFG_DEVFN(devfn) ((devfn) & GENMASK(7, 0)) + #define PCIE_CFG_BUS(bus) (((bus) << 8) & GENMASK(15, 8)) +@@ -68,6 +78,14 @@ + #define PCIE_MSI_SET_ENABLE_REG 0x190 + #define PCIE_MSI_SET_ENABLE GENMASK(PCIE_MSI_SET_NUM - 1, 0) + ++#define PCIE_PIPE4_PIE8_REG 0x338 ++#define PCIE_K_FINETUNE_MAX GENMASK(5, 0) ++#define PCIE_K_FINETUNE_ERR GENMASK(7, 6) ++#define PCIE_K_PRESET_TO_USE GENMASK(18, 8) ++#define PCIE_K_PHYPARAM_QUERY BIT(19) ++#define PCIE_K_QUERY_TIMEOUT BIT(20) ++#define PCIE_K_PRESET_TO_USE_16G GENMASK(31, 21) ++ + #define PCIE_MSI_SET_BASE_REG 0xc00 + #define PCIE_MSI_SET_OFFSET 0x10 + #define PCIE_MSI_SET_STATUS_OFFSET 0x04 +@@ -100,7 +118,10 @@ + #define PCIE_ATR_TLP_TYPE_MEM PCIE_ATR_TLP_TYPE(0) + #define PCIE_ATR_TLP_TYPE_IO PCIE_ATR_TLP_TYPE(2) + +-#define MAX_NUM_PHY_RESETS 1 ++#define MAX_NUM_PHY_RESETS 3 ++ ++/* Time in ms needed to complete PCIe reset on EN7581 SoC */ ++#define PCIE_EN7581_RESET_TIME_MS 100 + + struct mtk_gen3_pcie; + +@@ -847,6 +868,85 @@ static int mtk_pcie_parse_port(struct mt + return 0; + } + ++static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) ++{ ++ struct device *dev = pcie->dev; ++ int err; ++ u32 val; ++ ++ /* ++ * Wait for the time needed to complete the bulk assert in ++ * mtk_pcie_setup for EN7581 SoC. ++ */ ++ mdelay(PCIE_EN7581_RESET_TIME_MS); ++ ++ err = phy_init(pcie->phy); ++ if (err) { ++ dev_err(dev, "failed to initialize PHY\n"); ++ return err; ++ } ++ ++ err = phy_power_on(pcie->phy); ++ if (err) { ++ dev_err(dev, "failed to power on PHY\n"); ++ goto err_phy_on; ++ } ++ ++ err = reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); ++ if (err) { ++ dev_err(dev, "failed to deassert PHYs\n"); ++ goto err_phy_deassert; ++ } ++ ++ /* ++ * Wait for the time needed to complete the bulk de-assert above. ++ * This time is specific for EN7581 SoC. ++ */ ++ mdelay(PCIE_EN7581_RESET_TIME_MS); ++ ++ pm_runtime_enable(dev); ++ pm_runtime_get_sync(dev); ++ ++ err = clk_bulk_prepare(pcie->num_clks, pcie->clks); ++ if (err) { ++ dev_err(dev, "failed to prepare clock\n"); ++ goto err_clk_prepare; ++ } ++ ++ val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) | ++ FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) | ++ FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) | ++ FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41); ++ writel_relaxed(val, pcie->base + PCIE_EQ_PRESET_01_REG); ++ ++ val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT | ++ FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) | ++ FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) | ++ FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf); ++ writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG); ++ ++ err = clk_bulk_enable(pcie->num_clks, pcie->clks); ++ if (err) { ++ dev_err(dev, "failed to prepare clock\n"); ++ goto err_clk_enable; ++ } ++ ++ return 0; ++ ++err_clk_enable: ++ clk_bulk_unprepare(pcie->num_clks, pcie->clks); ++err_clk_prepare: ++ pm_runtime_put_sync(dev); ++ pm_runtime_disable(dev); ++ reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); ++err_phy_deassert: ++ phy_power_off(pcie->phy); ++err_phy_on: ++ phy_exit(pcie->phy); ++ ++ return err; ++} ++ + static int mtk_pcie_power_up(struct mtk_gen3_pcie *pcie) + { + struct device *dev = pcie->dev; +@@ -1113,7 +1213,18 @@ static const struct mtk_gen3_pcie_pdata + }, + }; + ++static const struct mtk_gen3_pcie_pdata mtk_pcie_soc_en7581 = { ++ .power_up = mtk_pcie_en7581_power_up, ++ .phy_resets = { ++ .id[0] = "phy-lane0", ++ .id[1] = "phy-lane1", ++ .id[2] = "phy-lane2", ++ .num_resets = 3, ++ }, ++}; ++ + static const struct of_device_id mtk_pcie_of_match[] = { ++ { .compatible = "airoha,en7581-pcie", .data = &mtk_pcie_soc_en7581 }, + { .compatible = "mediatek,mt8192-pcie", .data = &mtk_pcie_soc_mt8192 }, + {}, + }; diff --git a/target/linux/airoha/patches-6.6/022-v6.11-phy-airoha-Add-PCIe-PHY-driver-for-EN7581-SoC.patch b/target/linux/airoha/patches-6.6/022-v6.11-phy-airoha-Add-PCIe-PHY-driver-for-EN7581-SoC.patch new file mode 100644 index 0000000000..3f9443e4d0 --- /dev/null +++ b/target/linux/airoha/patches-6.6/022-v6.11-phy-airoha-Add-PCIe-PHY-driver-for-EN7581-SoC.patch @@ -0,0 +1,1783 @@ +From d7d2818b93837def4a33f92da2e64c3a2752c47e Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 15 Jun 2024 23:15:42 +0200 +Subject: [PATCH] phy: airoha: Add PCIe PHY driver for EN7581 SoC. + +Introduce support for Airoha PCIe PHY controller available in EN7581 +SoC. + +Reviewed-by: AngeloGioacchino Del Regno +Tested-by: Zhengping Zhang +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/20ac99aa8628d97778594f606681db7f868f24fe.1718485860.git.lorenzo@kernel.org +Signed-off-by: Vinod Koul +--- + MAINTAINERS | 8 + + drivers/phy/Kconfig | 10 + + drivers/phy/Makefile | 1 + + drivers/phy/phy-airoha-pcie-regs.h | 477 +++++++++++ + drivers/phy/phy-airoha-pcie.c | 1248 ++++++++++++++++++++++++++++ + 5 files changed, 1744 insertions(+) + create mode 100644 drivers/phy/phy-airoha-pcie-regs.h + create mode 100644 drivers/phy/phy-airoha-pcie.c + +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -72,6 +72,16 @@ config PHY_CAN_TRANSCEIVER + functional modes using gpios and sets the attribute max link + rate, for CAN drivers. + ++config PHY_AIROHA_PCIE ++ tristate "Airoha PCIe-PHY Driver" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ depends on OF ++ select GENERIC_PHY ++ help ++ Say Y here to add support for Airoha PCIe PHY driver. ++ This driver create the basic PHY instance and provides initialize ++ callback for PCIe GEN3 port. ++ + source "drivers/phy/allwinner/Kconfig" + source "drivers/phy/amlogic/Kconfig" + source "drivers/phy/broadcom/Kconfig" +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -10,6 +10,7 @@ obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy + obj-$(CONFIG_PHY_XGENE) += phy-xgene.o + obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o + obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o ++obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o + obj-y += allwinner/ \ + amlogic/ \ + broadcom/ \ +--- /dev/null ++++ b/drivers/phy/phy-airoha-pcie-regs.h +@@ -0,0 +1,477 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#ifndef _PHY_AIROHA_PCIE_H ++#define _PHY_AIROHA_PCIE_H ++ ++/* CSR_2L */ ++#define REG_CSR_2L_CMN 0x0000 ++#define CSR_2L_PXP_CMN_LANE_EN BIT(0) ++#define CSR_2L_PXP_CMN_TRIM_MASK GENMASK(28, 24) ++ ++#define REG_CSR_2L_JCPLL_IB_EXT 0x0004 ++#define REG_CSR_2L_JCPLL_LPF_SHCK_EN BIT(8) ++#define CSR_2L_PXP_JCPLL_CHP_IBIAS GENMASK(21, 16) ++#define CSR_2L_PXP_JCPLL_CHP_IOFST GENMASK(29, 24) ++ ++#define REG_CSR_2L_JCPLL_LPF_BR 0x0008 ++#define CSR_2L_PXP_JCPLL_LPF_BR GENMASK(4, 0) ++#define CSR_2L_PXP_JCPLL_LPF_BC GENMASK(12, 8) ++#define CSR_2L_PXP_JCPLL_LPF_BP GENMASK(20, 16) ++#define CSR_2L_PXP_JCPLL_LPF_BWR GENMASK(28, 24) ++ ++#define REG_CSR_2L_JCPLL_LPF_BWC 0x000c ++#define CSR_2L_PXP_JCPLL_LPF_BWC GENMASK(4, 0) ++#define CSR_2L_PXP_JCPLL_KBAND_CODE GENMASK(23, 16) ++#define CSR_2L_PXP_JCPLL_KBAND_DIV GENMASK(26, 24) ++ ++#define REG_CSR_2L_JCPLL_KBAND_KFC 0x0010 ++#define CSR_2L_PXP_JCPLL_KBAND_KFC GENMASK(1, 0) ++#define CSR_2L_PXP_JCPLL_KBAND_KF GENMASK(9, 8) ++#define CSR_2L_PXP_JCPLL_KBAND_KS GENMASK(17, 16) ++#define CSR_2L_PXP_JCPLL_POSTDIV_EN BIT(24) ++ ++#define REG_CSR_2L_JCPLL_MMD_PREDIV_MODE 0x0014 ++#define CSR_2L_PXP_JCPLL_MMD_PREDIV_MODE GENMASK(1, 0) ++#define CSR_2L_PXP_JCPLL_POSTDIV_D2 BIT(16) ++#define CSR_2L_PXP_JCPLL_POSTDIV_D5 BIT(24) ++ ++#define CSR_2L_PXP_JCPLL_MONCK 0x0018 ++#define CSR_2L_PXP_JCPLL_REFIN_DIV GENMASK(25, 24) ++ ++#define REG_CSR_2L_JCPLL_RST_DLY 0x001c ++#define CSR_2L_PXP_JCPLL_RST_DLY GENMASK(2, 0) ++#define CSR_2L_PXP_JCPLL_RST BIT(8) ++#define CSR_2L_PXP_JCPLL_SDM_DI_EN BIT(16) ++#define CSR_2L_PXP_JCPLL_SDM_DI_LS GENMASK(25, 24) ++ ++#define REG_CSR_2L_JCPLL_SDM_IFM 0x0020 ++#define CSR_2L_PXP_JCPLL_SDM_IFM BIT(0) ++ ++#define REG_CSR_2L_JCPLL_SDM_HREN 0x0024 ++#define CSR_2L_PXP_JCPLL_SDM_HREN BIT(0) ++#define CSR_2L_PXP_JCPLL_TCL_AMP_EN BIT(8) ++#define CSR_2L_PXP_JCPLL_TCL_AMP_GAIN GENMASK(18, 16) ++#define CSR_2L_PXP_JCPLL_TCL_AMP_VREF GENMASK(28, 24) ++ ++#define REG_CSR_2L_JCPLL_TCL_CMP 0x0028 ++#define CSR_2L_PXP_JCPLL_TCL_LPF_EN BIT(16) ++#define CSR_2L_PXP_JCPLL_TCL_LPF_BW GENMASK(26, 24) ++ ++#define REG_CSR_2L_JCPLL_VCODIV 0x002c ++#define CSR_2L_PXP_JCPLL_VCO_CFIX GENMASK(9, 8) ++#define CSR_2L_PXP_JCPLL_VCO_HALFLSB_EN BIT(16) ++#define CSR_2L_PXP_JCPLL_VCO_SCAPWR GENMASK(26, 24) ++ ++#define REG_CSR_2L_JCPLL_VCO_TCLVAR 0x0030 ++#define CSR_2L_PXP_JCPLL_VCO_TCLVAR GENMASK(2, 0) ++ ++#define REG_CSR_2L_JCPLL_SSC 0x0038 ++#define CSR_2L_PXP_JCPLL_SSC_EN BIT(0) ++#define CSR_2L_PXP_JCPLL_SSC_PHASE_INI BIT(8) ++#define CSR_2L_PXP_JCPLL_SSC_TRI_EN BIT(16) ++ ++#define REG_CSR_2L_JCPLL_SSC_DELTA1 0x003c ++#define CSR_2L_PXP_JCPLL_SSC_DELTA1 GENMASK(15, 0) ++#define CSR_2L_PXP_JCPLL_SSC_DELTA GENMASK(31, 16) ++ ++#define REG_CSR_2L_JCPLL_SSC_PERIOD 0x0040 ++#define CSR_2L_PXP_JCPLL_SSC_PERIOD GENMASK(15, 0) ++ ++#define REG_CSR_2L_JCPLL_TCL_VTP_EN 0x004c ++#define CSR_2L_PXP_JCPLL_SPARE_LOW GENMASK(31, 24) ++ ++#define REG_CSR_2L_JCPLL_TCL_KBAND_VREF 0x0050 ++#define CSR_2L_PXP_JCPLL_TCL_KBAND_VREF GENMASK(4, 0) ++#define CSR_2L_PXP_JCPLL_VCO_KBAND_MEAS_EN BIT(24) ++ ++#define REG_CSR_2L_750M_SYS_CK 0x0054 ++#define CSR_2L_PXP_TXPLL_LPF_SHCK_EN BIT(16) ++#define CSR_2L_PXP_TXPLL_CHP_IBIAS GENMASK(29, 24) ++ ++#define REG_CSR_2L_TXPLL_CHP_IOFST 0x0058 ++#define CSR_2L_PXP_TXPLL_CHP_IOFST GENMASK(5, 0) ++#define CSR_2L_PXP_TXPLL_LPF_BR GENMASK(12, 8) ++#define CSR_2L_PXP_TXPLL_LPF_BC GENMASK(20, 16) ++#define CSR_2L_PXP_TXPLL_LPF_BP GENMASK(28, 24) ++ ++#define REG_CSR_2L_TXPLL_LPF_BWR 0x005c ++#define CSR_2L_PXP_TXPLL_LPF_BWR GENMASK(4, 0) ++#define CSR_2L_PXP_TXPLL_LPF_BWC GENMASK(12, 8) ++#define CSR_2L_PXP_TXPLL_KBAND_CODE GENMASK(31, 24) ++ ++#define REG_CSR_2L_TXPLL_KBAND_DIV 0x0060 ++#define CSR_2L_PXP_TXPLL_KBAND_DIV GENMASK(2, 0) ++#define CSR_2L_PXP_TXPLL_KBAND_KFC GENMASK(9, 8) ++#define CSR_2L_PXP_TXPLL_KBAND_KF GENMASK(17, 16) ++#define CSR_2L_PXP_txpll_KBAND_KS GENMASK(25, 24) ++ ++#define REG_CSR_2L_TXPLL_POSTDIV 0x0064 ++#define CSR_2L_PXP_TXPLL_POSTDIV_EN BIT(0) ++#define CSR_2L_PXP_TXPLL_MMD_PREDIV_MODE GENMASK(9, 8) ++#define CSR_2L_PXP_TXPLL_PHY_CK1_EN BIT(24) ++ ++#define REG_CSR_2L_TXPLL_PHY_CK2 0x0068 ++#define CSR_2L_PXP_TXPLL_REFIN_INTERNAL BIT(24) ++ ++#define REG_CSR_2L_TXPLL_REFIN_DIV 0x006c ++#define CSR_2L_PXP_TXPLL_REFIN_DIV GENMASK(1, 0) ++#define CSR_2L_PXP_TXPLL_RST_DLY GENMASK(10, 8) ++#define CSR_2L_PXP_TXPLL_PLL_RSTB BIT(16) ++ ++#define REG_CSR_2L_TXPLL_SDM_DI_LS 0x0070 ++#define CSR_2L_PXP_TXPLL_SDM_DI_LS GENMASK(1, 0) ++#define CSR_2L_PXP_TXPLL_SDM_IFM BIT(8) ++#define CSR_2L_PXP_TXPLL_SDM_ORD GENMASK(25, 24) ++ ++#define REG_CSR_2L_TXPLL_SDM_OUT 0x0074 ++#define CSR_2L_PXP_TXPLL_TCL_AMP_EN BIT(16) ++#define CSR_2L_PXP_TXPLL_TCL_AMP_GAIN GENMASK(26, 24) ++ ++#define REG_CSR_2L_TXPLL_TCL_AMP_VREF 0x0078 ++#define CSR_2L_PXP_TXPLL_TCL_AMP_VREF GENMASK(4, 0) ++#define CSR_2L_PXP_TXPLL_TCL_LPF_EN BIT(24) ++ ++#define REG_CSR_2L_TXPLL_TCL_LPF_BW 0x007c ++#define CSR_2L_PXP_TXPLL_TCL_LPF_BW GENMASK(2, 0) ++#define CSR_2L_PXP_TXPLL_VCO_CFIX GENMASK(17, 16) ++#define CSR_2L_PXP_TXPLL_VCO_HALFLSB_EN BIT(24) ++ ++#define REG_CSR_2L_TXPLL_VCO_SCAPWR 0x0080 ++#define CSR_2L_PXP_TXPLL_VCO_SCAPWR GENMASK(2, 0) ++ ++#define REG_CSR_2L_TXPLL_SSC 0x0084 ++#define CSR_2L_PXP_TXPLL_SSC_EN BIT(0) ++#define CSR_2L_PXP_TXPLL_SSC_PHASE_INI BIT(8) ++ ++#define REG_CSR_2L_TXPLL_SSC_DELTA1 0x0088 ++#define CSR_2L_PXP_TXPLL_SSC_DELTA1 GENMASK(15, 0) ++#define CSR_2L_PXP_TXPLL_SSC_DELTA GENMASK(31, 16) ++ ++#define REG_CSR_2L_TXPLL_SSC_PERIOD 0x008c ++#define CSR_2L_PXP_txpll_SSC_PERIOD GENMASK(15, 0) ++ ++#define REG_CSR_2L_TXPLL_VTP 0x0090 ++#define CSR_2L_PXP_TXPLL_VTP_EN BIT(0) ++ ++#define REG_CSR_2L_TXPLL_TCL_VTP 0x0098 ++#define CSR_2L_PXP_TXPLL_SPARE_L GENMASK(31, 24) ++ ++#define REG_CSR_2L_TXPLL_TCL_KBAND_VREF 0x009c ++#define CSR_2L_PXP_TXPLL_TCL_KBAND_VREF GENMASK(4, 0) ++#define CSR_2L_PXP_TXPLL_VCO_KBAND_MEAS_EN BIT(24) ++ ++#define REG_CSR_2L_TXPLL_POSTDIV_D256 0x00a0 ++#define CSR_2L_PXP_CLKTX0_AMP GENMASK(10, 8) ++#define CSR_2L_PXP_CLKTX0_OFFSET GENMASK(17, 16) ++#define CSR_2L_PXP_CLKTX0_SR GENMASK(25, 24) ++ ++#define REG_CSR_2L_CLKTX0_FORCE_OUT1 0x00a4 ++#define CSR_2L_PXP_CLKTX0_HZ BIT(8) ++#define CSR_2L_PXP_CLKTX0_IMP_SEL GENMASK(20, 16) ++#define CSR_2L_PXP_CLKTX1_AMP GENMASK(26, 24) ++ ++#define REG_CSR_2L_CLKTX1_OFFSET 0x00a8 ++#define CSR_2L_PXP_CLKTX1_OFFSET GENMASK(1, 0) ++#define CSR_2L_PXP_CLKTX1_SR GENMASK(9, 8) ++#define CSR_2L_PXP_CLKTX1_HZ BIT(24) ++ ++#define REG_CSR_2L_CLKTX1_IMP_SEL 0x00ac ++#define CSR_2L_PXP_CLKTX1_IMP_SEL GENMASK(4, 0) ++ ++#define REG_CSR_2L_PLL_CMN_RESERVE0 0x00b0 ++#define CSR_2L_PXP_PLL_RESERVE_MASK GENMASK(15, 0) ++ ++#define REG_CSR_2L_TX0_CKLDO 0x00cc ++#define CSR_2L_PXP_TX0_CKLDO_EN BIT(0) ++#define CSR_2L_PXP_TX0_DMEDGEGEN_EN BIT(24) ++ ++#define REG_CSR_2L_TX1_CKLDO 0x00e8 ++#define CSR_2L_PXP_TX1_CKLDO_EN BIT(0) ++#define CSR_2L_PXP_TX1_DMEDGEGEN_EN BIT(24) ++ ++#define REG_CSR_2L_TX1_MULTLANE 0x00ec ++#define CSR_2L_PXP_TX1_MULTLANE_EN BIT(0) ++ ++#define REG_CSR_2L_RX0_REV0 0x00fc ++#define CSR_2L_PXP_VOS_PNINV GENMASK(3, 2) ++#define CSR_2L_PXP_FE_GAIN_NORMAL_MODE GENMASK(6, 4) ++#define CSR_2L_PXP_FE_GAIN_TRAIN_MODE GENMASK(10, 8) ++ ++#define REG_CSR_2L_RX0_PHYCK_DIV 0x0100 ++#define CSR_2L_PXP_RX0_PHYCK_SEL GENMASK(9, 8) ++#define CSR_2L_PXP_RX0_PHYCK_RSTB BIT(16) ++#define CSR_2L_PXP_RX0_TDC_CK_SEL BIT(24) ++ ++#define REG_CSR_2L_CDR0_PD_PICAL_CKD8_INV 0x0104 ++#define CSR_2L_PXP_CDR0_PD_EDGE_DISABLE BIT(8) ++ ++#define REG_CSR_2L_CDR0_LPF_RATIO 0x0110 ++#define CSR_2L_PXP_CDR0_LPF_TOP_LIM GENMASK(26, 8) ++ ++#define REG_CSR_2L_CDR0_PR_INJ_MODE 0x011c ++#define CSR_2L_PXP_CDR0_INJ_FORCE_OFF BIT(24) ++ ++#define REG_CSR_2L_CDR0_PR_BETA_DAC 0x0120 ++#define CSR_2L_PXP_CDR0_PR_BETA_SEL GENMASK(19, 16) ++#define CSR_2L_PXP_CDR0_PR_KBAND_DIV GENMASK(26, 24) ++ ++#define REG_CSR_2L_CDR0_PR_VREG_IBAND 0x0124 ++#define CSR_2L_PXP_CDR0_PR_VREG_IBAND GENMASK(2, 0) ++#define CSR_2L_PXP_CDR0_PR_VREG_CKBUF GENMASK(10, 8) ++ ++#define REG_CSR_2L_CDR0_PR_CKREF_DIV 0x0128 ++#define CSR_2L_PXP_CDR0_PR_CKREF_DIV GENMASK(1, 0) ++ ++#define REG_CSR_2L_CDR0_PR_MONCK 0x012c ++#define CSR_2L_PXP_CDR0_PR_MONCK_ENABLE BIT(0) ++#define CSR_2L_PXP_CDR0_PR_RESERVE0 GENMASK(19, 16) ++ ++#define REG_CSR_2L_CDR0_PR_COR_HBW 0x0130 ++#define CSR_2L_PXP_CDR0_PR_LDO_FORCE_ON BIT(8) ++#define CSR_2L_PXP_CDR0_PR_CKREF_DIV1 GENMASK(17, 16) ++ ++#define REG_CSR_2L_CDR0_PR_MONPI 0x0134 ++#define CSR_2L_PXP_CDR0_PR_XFICK_EN BIT(8) ++ ++#define REG_CSR_2L_RX0_SIGDET_DCTEST 0x0140 ++#define CSR_2L_PXP_RX0_SIGDET_LPF_CTRL GENMASK(9, 8) ++#define CSR_2L_PXP_RX0_SIGDET_PEAK GENMASK(25, 24) ++ ++#define REG_CSR_2L_RX0_SIGDET_VTH_SEL 0x0144 ++#define CSR_2L_PXP_RX0_SIGDET_VTH_SEL GENMASK(4, 0) ++#define CSR_2L_PXP_RX0_FE_VB_EQ1_EN BIT(24) ++ ++#define REG_CSR_2L_PXP_RX0_FE_VB_EQ2 0x0148 ++#define CSR_2L_PXP_RX0_FE_VB_EQ2_EN BIT(0) ++#define CSR_2L_PXP_RX0_FE_VB_EQ3_EN BIT(8) ++#define CSR_2L_PXP_RX0_FE_VCM_GEN_PWDB BIT(16) ++ ++#define REG_CSR_2L_PXP_RX0_OSCAL_CTLE1IOS 0x0158 ++#define CSR_2L_PXP_RX0_PR_OSCAL_VGA1IOS GENMASK(29, 24) ++ ++#define REG_CSR_2L_PXP_RX0_OSCA_VGA1VOS 0x015c ++#define CSR_2L_PXP_RX0_PR_OSCAL_VGA1VOS GENMASK(5, 0) ++#define CSR_2L_PXP_RX0_PR_OSCAL_VGA2IOS GENMASK(13, 8) ++ ++#define REG_CSR_2L_RX1_REV0 0x01b4 ++ ++#define REG_CSR_2L_RX1_PHYCK_DIV 0x01b8 ++#define CSR_2L_PXP_RX1_PHYCK_SEL GENMASK(9, 8) ++#define CSR_2L_PXP_RX1_PHYCK_RSTB BIT(16) ++#define CSR_2L_PXP_RX1_TDC_CK_SEL BIT(24) ++ ++#define REG_CSR_2L_CDR1_PD_PICAL_CKD8_INV 0x01bc ++#define CSR_2L_PXP_CDR1_PD_EDGE_DISABLE BIT(8) ++ ++#define REG_CSR_2L_CDR1_PR_BETA_DAC 0x01d8 ++#define CSR_2L_PXP_CDR1_PR_BETA_SEL GENMASK(19, 16) ++#define CSR_2L_PXP_CDR1_PR_KBAND_DIV GENMASK(26, 24) ++ ++#define REG_CSR_2L_CDR1_PR_MONCK 0x01e4 ++#define CSR_2L_PXP_CDR1_PR_MONCK_ENABLE BIT(0) ++#define CSR_2L_PXP_CDR1_PR_RESERVE0 GENMASK(19, 16) ++ ++#define REG_CSR_2L_CDR1_LPF_RATIO 0x01c8 ++#define CSR_2L_PXP_CDR1_LPF_TOP_LIM GENMASK(26, 8) ++ ++#define REG_CSR_2L_CDR1_PR_INJ_MODE 0x01d4 ++#define CSR_2L_PXP_CDR1_INJ_FORCE_OFF BIT(24) ++ ++#define REG_CSR_2L_CDR1_PR_VREG_IBAND_VAL 0x01dc ++#define CSR_2L_PXP_CDR1_PR_VREG_IBAND GENMASK(2, 0) ++#define CSR_2L_PXP_CDR1_PR_VREG_CKBUF GENMASK(10, 8) ++ ++#define REG_CSR_2L_CDR1_PR_CKREF_DIV 0x01e0 ++#define CSR_2L_PXP_CDR1_PR_CKREF_DIV GENMASK(1, 0) ++ ++#define REG_CSR_2L_CDR1_PR_COR_HBW 0x01e8 ++#define CSR_2L_PXP_CDR1_PR_LDO_FORCE_ON BIT(8) ++#define CSR_2L_PXP_CDR1_PR_CKREF_DIV1 GENMASK(17, 16) ++ ++#define REG_CSR_2L_CDR1_PR_MONPI 0x01ec ++#define CSR_2L_PXP_CDR1_PR_XFICK_EN BIT(8) ++ ++#define REG_CSR_2L_RX1_DAC_RANGE_EYE 0x01f4 ++#define CSR_2L_PXP_RX1_SIGDET_LPF_CTRL GENMASK(25, 24) ++ ++#define REG_CSR_2L_RX1_SIGDET_NOVTH 0x01f8 ++#define CSR_2L_PXP_RX1_SIGDET_PEAK GENMASK(9, 8) ++#define CSR_2L_PXP_RX1_SIGDET_VTH_SEL GENMASK(20, 16) ++ ++#define REG_CSR_2L_RX1_FE_VB_EQ1 0x0200 ++#define CSR_2L_PXP_RX1_FE_VB_EQ1_EN BIT(0) ++#define CSR_2L_PXP_RX1_FE_VB_EQ2_EN BIT(8) ++#define CSR_2L_PXP_RX1_FE_VB_EQ3_EN BIT(16) ++#define CSR_2L_PXP_RX1_FE_VCM_GEN_PWDB BIT(24) ++ ++#define REG_CSR_2L_RX1_OSCAL_VGA1IOS 0x0214 ++#define CSR_2L_PXP_RX1_PR_OSCAL_VGA1IOS GENMASK(5, 0) ++#define CSR_2L_PXP_RX1_PR_OSCAL_VGA1VOS GENMASK(13, 8) ++#define CSR_2L_PXP_RX1_PR_OSCAL_VGA2IOS GENMASK(21, 16) ++ ++/* PMA */ ++#define REG_PCIE_PMA_SS_LCPLL_PWCTL_SETTING_1 0x0004 ++#define PCIE_LCPLL_MAN_PWDB BIT(0) ++ ++#define REG_PCIE_PMA_SEQUENCE_DISB_CTRL1 0x010c ++#define PCIE_DISB_RX_SDCAL_EN BIT(0) ++ ++#define REG_PCIE_PMA_CTRL_SEQUENCE_FORCE_CTRL1 0x0114 ++#define PCIE_FORCE_RX_SDCAL_EN BIT(0) ++ ++#define REG_PCIE_PMA_SS_RX_FREQ_DET1 0x014c ++#define PCIE_PLL_FT_LOCK_CYCLECNT GENMASK(15, 0) ++#define PCIE_PLL_FT_UNLOCK_CYCLECNT GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_SS_RX_FREQ_DET2 0x0150 ++#define PCIE_LOCK_TARGET_BEG GENMASK(15, 0) ++#define PCIE_LOCK_TARGET_END GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_SS_RX_FREQ_DET3 0x0154 ++#define PCIE_UNLOCK_TARGET_BEG GENMASK(15, 0) ++#define PCIE_UNLOCK_TARGET_END GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_SS_RX_FREQ_DET4 0x0158 ++#define PCIE_FREQLOCK_DET_EN GENMASK(2, 0) ++#define PCIE_LOCK_LOCKTH GENMASK(11, 8) ++#define PCIE_UNLOCK_LOCKTH GENMASK(15, 12) ++ ++#define REG_PCIE_PMA_SS_RX_CAL1 0x0160 ++#define REG_PCIE_PMA_SS_RX_CAL2 0x0164 ++#define PCIE_CAL_OUT_OS GENMASK(11, 8) ++ ++#define REG_PCIE_PMA_SS_RX_SIGDET0 0x0168 ++#define PCIE_SIGDET_WIN_NONVLD_TIMES GENMASK(28, 24) ++ ++#define REG_PCIE_PMA_TX_RESET 0x0260 ++#define PCIE_TX_TOP_RST BIT(0) ++#define PCIE_TX_CAL_RST BIT(8) ++ ++#define REG_PCIE_PMA_RX_FORCE_MODE0 0x0294 ++#define PCIE_FORCE_DA_XPON_RX_FE_GAIN_CTRL GENMASK(1, 0) ++ ++#define REG_PCIE_PMA_SS_DA_XPON_PWDB0 0x034c ++#define PCIE_DA_XPON_CDR_PR_PWDB BIT(8) ++ ++#define REG_PCIE_PMA_SW_RESET 0x0460 ++#define PCIE_SW_RX_FIFO_RST BIT(0) ++#define PCIE_SW_RX_RST BIT(1) ++#define PCIE_SW_TX_RST BIT(2) ++#define PCIE_SW_PMA_RST BIT(3) ++#define PCIE_SW_ALLPCS_RST BIT(4) ++#define PCIE_SW_REF_RST BIT(5) ++#define PCIE_SW_TX_FIFO_RST BIT(6) ++#define PCIE_SW_XFI_TXPCS_RST BIT(7) ++#define PCIE_SW_XFI_RXPCS_RST BIT(8) ++#define PCIE_SW_XFI_RXPCS_BIST_RST BIT(9) ++#define PCIE_SW_HSG_TXPCS_RST BIT(10) ++#define PCIE_SW_HSG_RXPCS_RST BIT(11) ++#define PCIE_PMA_SW_RST (PCIE_SW_RX_FIFO_RST | \ ++ PCIE_SW_RX_RST | \ ++ PCIE_SW_TX_RST | \ ++ PCIE_SW_PMA_RST | \ ++ PCIE_SW_ALLPCS_RST | \ ++ PCIE_SW_REF_RST | \ ++ PCIE_SW_TX_FIFO_RST | \ ++ PCIE_SW_XFI_TXPCS_RST | \ ++ PCIE_SW_XFI_RXPCS_RST | \ ++ PCIE_SW_XFI_RXPCS_BIST_RST | \ ++ PCIE_SW_HSG_TXPCS_RST | \ ++ PCIE_SW_HSG_RXPCS_RST) ++ ++#define REG_PCIE_PMA_RO_RX_FREQDET 0x0530 ++#define PCIE_RO_FBCK_LOCK BIT(0) ++#define PCIE_RO_FL_OUT GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC 0x0794 ++#define PCIE_FORCE_DA_PXP_CDR_PR_IDAC GENMASK(10, 0) ++#define PCIE_FORCE_SEL_DA_PXP_CDR_PR_IDAC BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_TXPLL_SDM_PCW BIT(24) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_SDM_PCW 0x0798 ++#define PCIE_FORCE_DA_PXP_TXPLL_SDM_PCW GENMASK(30, 0) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_VOS 0x079c ++#define PCIE_FORCE_SEL_DA_PXP_JCPLL_SDM_PCW BIT(16) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_JCPLL_SDM_PCW 0x0800 ++#define PCIE_FORCE_DA_PXP_JCPLL_SDM_PCW GENMASK(30, 0) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_CDR_PD_PWDB 0x081c ++#define PCIE_FORCE_DA_PXP_CDR_PD_PWDB BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_CDR_PD_PWDB BIT(8) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C 0x0820 ++#define PCIE_FORCE_DA_PXP_CDR_PR_LPF_C_EN BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_C_EN BIT(8) ++#define PCIE_FORCE_DA_PXP_CDR_PR_LPF_R_EN BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_R_EN BIT(24) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB 0x0824 ++#define PCIE_FORCE_DA_PXP_CDR_PR_PWDB BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_CDR_PR_PWDB BIT(24) ++ ++#define REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT 0x0828 ++#define PCIE_FORCE_DA_PXP_JCPLL_CKOUT_EN BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_JCPLL_CKOUT_EN BIT(8) ++#define PCIE_FORCE_DA_PXP_JCPLL_EN BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_JCPLL_EN BIT(24) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_RX_SCAN_RST 0x0084c ++#define PCIE_FORCE_DA_PXP_RX_SIGDET_PWDB BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_RX_SIGDET_PWDB BIT(24) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT 0x0854 ++#define PCIE_FORCE_DA_PXP_TXPLL_CKOUT_EN BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_TXPLL_CKOUT_EN BIT(8) ++#define PCIE_FORCE_DA_PXP_TXPLL_EN BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_TXPLL_EN BIT(24) ++ ++#define REG_PCIE_PMA_SCAN_MODE 0x0884 ++#define PCIE_FORCE_DA_PXP_JCPLL_KBAND_LOAD_EN BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_JCPLL_KBAND_LOAD_EN BIT(8) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_13 0x08bc ++#define PCIE_FLL_IDAC_PCIEG1 GENMASK(10, 0) ++#define PCIE_FLL_IDAC_PCIEG2 GENMASK(26, 16) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_14 0x08c0 ++#define PCIE_FLL_IDAC_PCIEG3 GENMASK(10, 0) ++#define PCIE_FLL_LOAD_EN BIT(16) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_GAIN_CTRL 0x088c ++#define PCIE_FORCE_DA_PXP_RX_FE_GAIN_CTRL GENMASK(1, 0) ++#define PCIE_FORCE_SEL_DA_PXP_RX_FE_GAIN_CTRL BIT(8) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_PWDB 0x0894 ++#define PCIE_FORCE_DA_PXP_RX_FE_PWDB BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_RX_FE_PWDB BIT(8) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_12 0x08b8 ++#define PCIE_FORCE_PMA_RX_SPEED GENMASK(7, 4) ++#define PCIE_FORCE_SEL_PMA_RX_SPEED BIT(7) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_17 0x08e0 ++ ++#define REG_PCIE_PMA_DIG_RESERVE_18 0x08e4 ++#define PCIE_PXP_RX_VTH_SEL_PCIE_G1 GENMASK(4, 0) ++#define PCIE_PXP_RX_VTH_SEL_PCIE_G2 GENMASK(12, 8) ++#define PCIE_PXP_RX_VTH_SEL_PCIE_G3 GENMASK(20, 16) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_19 0x08e8 ++#define PCIE_PCP_RX_REV0_PCIE_GEN1 GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_20 0x08ec ++#define PCIE_PCP_RX_REV0_PCIE_GEN2 GENMASK(15, 0) ++#define PCIE_PCP_RX_REV0_PCIE_GEN3 GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_21 0x08f0 ++#define REG_PCIE_PMA_DIG_RESERVE_22 0x08f4 ++#define REG_PCIE_PMA_DIG_RESERVE_27 0x0908 ++#define REG_PCIE_PMA_DIG_RESERVE_30 0x0914 ++ ++#endif /* _PHY_AIROHA_PCIE_H */ +--- /dev/null ++++ b/drivers/phy/phy-airoha-pcie.c +@@ -0,0 +1,1248 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "phy-airoha-pcie-regs.h" ++ ++#define LEQ_LEN_CTRL_MAX_VAL 7 ++#define FREQ_LOCK_MAX_ATTEMPT 10 ++ ++enum airoha_pcie_port_gen { ++ PCIE_PORT_GEN1 = 1, ++ PCIE_PORT_GEN2, ++ PCIE_PORT_GEN3, ++}; ++ ++/** ++ * struct airoha_pcie_phy - PCIe phy driver main structure ++ * @dev: pointer to device ++ * @phy: pointer to generic phy ++ * @csr_2l: Analogic lane IO mapped register base address ++ * @pma0: IO mapped register base address of PMA0-PCIe ++ * @pma1: IO mapped register base address of PMA1-PCIe ++ */ ++struct airoha_pcie_phy { ++ struct device *dev; ++ struct phy *phy; ++ void __iomem *csr_2l; ++ void __iomem *pma0; ++ void __iomem *pma1; ++}; ++ ++static void airoha_phy_clear_bits(void __iomem *reg, u32 mask) ++{ ++ u32 val = readl(reg) & ~mask; ++ ++ writel(val, reg); ++} ++ ++static void airoha_phy_set_bits(void __iomem *reg, u32 mask) ++{ ++ u32 val = readl(reg) | mask; ++ ++ writel(val, reg); ++} ++ ++static void airoha_phy_update_bits(void __iomem *reg, u32 mask, u32 val) ++{ ++ u32 tmp = readl(reg); ++ ++ tmp &= ~mask; ++ tmp |= val & mask; ++ writel(tmp, reg); ++} ++ ++#define airoha_phy_update_field(reg, mask, val) \ ++ do { \ ++ BUILD_BUG_ON_MSG(!__builtin_constant_p((mask)), \ ++ "mask is not constant"); \ ++ airoha_phy_update_bits((reg), (mask), \ ++ FIELD_PREP((mask), (val))); \ ++ } while (0) ++ ++#define airoha_phy_csr_2l_clear_bits(pcie_phy, reg, mask) \ ++ airoha_phy_clear_bits((pcie_phy)->csr_2l + (reg), (mask)) ++#define airoha_phy_csr_2l_set_bits(pcie_phy, reg, mask) \ ++ airoha_phy_set_bits((pcie_phy)->csr_2l + (reg), (mask)) ++#define airoha_phy_csr_2l_update_field(pcie_phy, reg, mask, val) \ ++ airoha_phy_update_field((pcie_phy)->csr_2l + (reg), (mask), (val)) ++#define airoha_phy_pma0_clear_bits(pcie_phy, reg, mask) \ ++ airoha_phy_clear_bits((pcie_phy)->pma0 + (reg), (mask)) ++#define airoha_phy_pma1_clear_bits(pcie_phy, reg, mask) \ ++ airoha_phy_clear_bits((pcie_phy)->pma1 + (reg), (mask)) ++#define airoha_phy_pma0_set_bits(pcie_phy, reg, mask) \ ++ airoha_phy_set_bits((pcie_phy)->pma0 + (reg), (mask)) ++#define airoha_phy_pma1_set_bits(pcie_phy, reg, mask) \ ++ airoha_phy_set_bits((pcie_phy)->pma1 + (reg), (mask)) ++#define airoha_phy_pma0_update_field(pcie_phy, reg, mask, val) \ ++ airoha_phy_update_field((pcie_phy)->pma0 + (reg), (mask), (val)) ++#define airoha_phy_pma1_update_field(pcie_phy, reg, mask, val) \ ++ airoha_phy_update_field((pcie_phy)->pma1 + (reg), (mask), (val)) ++ ++static void ++airoha_phy_init_lane0_rx_fw_pre_calib(struct airoha_pcie_phy *pcie_phy, ++ enum airoha_pcie_port_gen gen) ++{ ++ u32 fl_out_target = gen == PCIE_PORT_GEN3 ? 41600 : 41941; ++ u32 lock_cyclecnt = gen == PCIE_PORT_GEN3 ? 26000 : 32767; ++ u32 pr_idac, val, cdr_pr_idac_tmp = 0; ++ int i; ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_SS_LCPLL_PWCTL_SETTING_1, ++ PCIE_LCPLL_MAN_PWDB); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET2, ++ PCIE_LOCK_TARGET_BEG, ++ fl_out_target - 100); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET2, ++ PCIE_LOCK_TARGET_END, ++ fl_out_target + 100); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET1, ++ PCIE_PLL_FT_LOCK_CYCLECNT, lock_cyclecnt); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_LOCK_LOCKTH, 0x3); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET3, ++ PCIE_UNLOCK_TARGET_BEG, ++ fl_out_target - 100); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET3, ++ PCIE_UNLOCK_TARGET_END, ++ fl_out_target + 100); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET1, ++ PCIE_PLL_FT_UNLOCK_CYCLECNT, ++ lock_cyclecnt); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_UNLOCK_LOCKTH, 0x3); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR0_PR_INJ_MODE, ++ CSR_2L_PXP_CDR0_INJ_FORCE_OFF); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_IDAC); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PR_PWDB); ++ ++ for (i = 0; i < LEQ_LEN_CTRL_MAX_VAL; i++) { ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, i << 8); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = FIELD_GET(PCIE_RO_FL_OUT, ++ readl(pcie_phy->pma0 + ++ REG_PCIE_PMA_RO_RX_FREQDET)); ++ if (val > fl_out_target) ++ cdr_pr_idac_tmp = i << 8; ++ } ++ ++ for (i = LEQ_LEN_CTRL_MAX_VAL; i >= 0; i--) { ++ pr_idac = cdr_pr_idac_tmp | (0x1 << i); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, pr_idac); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = FIELD_GET(PCIE_RO_FL_OUT, ++ readl(pcie_phy->pma0 + ++ REG_PCIE_PMA_RO_RX_FREQDET)); ++ if (val < fl_out_target) ++ pr_idac &= ~(0x1 << i); ++ ++ cdr_pr_idac_tmp = pr_idac; ++ } ++ ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, ++ cdr_pr_idac_tmp); ++ ++ for (i = 0; i < FREQ_LOCK_MAX_ATTEMPT; i++) { ++ u32 val; ++ ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = readl(pcie_phy->pma0 + REG_PCIE_PMA_RO_RX_FREQDET); ++ if (val & PCIE_RO_FBCK_LOCK) ++ break; ++ } ++ ++ /* turn off force mode and update band values */ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR0_PR_INJ_MODE, ++ CSR_2L_PXP_CDR0_INJ_FORCE_OFF); ++ ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_IDAC); ++ if (gen == PCIE_PORT_GEN3) { ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_14, ++ PCIE_FLL_IDAC_PCIEG3, ++ cdr_pr_idac_tmp); ++ } else { ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_13, ++ PCIE_FLL_IDAC_PCIEG1, ++ cdr_pr_idac_tmp); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_13, ++ PCIE_FLL_IDAC_PCIEG2, ++ cdr_pr_idac_tmp); ++ } ++} ++ ++static void ++airoha_phy_init_lane1_rx_fw_pre_calib(struct airoha_pcie_phy *pcie_phy, ++ enum airoha_pcie_port_gen gen) ++{ ++ u32 fl_out_target = gen == PCIE_PORT_GEN3 ? 41600 : 41941; ++ u32 lock_cyclecnt = gen == PCIE_PORT_GEN3 ? 26000 : 32767; ++ u32 pr_idac, val, cdr_pr_idac_tmp = 0; ++ int i; ++ ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_SS_LCPLL_PWCTL_SETTING_1, ++ PCIE_LCPLL_MAN_PWDB); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET2, ++ PCIE_LOCK_TARGET_BEG, ++ fl_out_target - 100); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET2, ++ PCIE_LOCK_TARGET_END, ++ fl_out_target + 100); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET1, ++ PCIE_PLL_FT_LOCK_CYCLECNT, lock_cyclecnt); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_LOCK_LOCKTH, 0x3); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET3, ++ PCIE_UNLOCK_TARGET_BEG, ++ fl_out_target - 100); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET3, ++ PCIE_UNLOCK_TARGET_END, ++ fl_out_target + 100); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET1, ++ PCIE_PLL_FT_UNLOCK_CYCLECNT, ++ lock_cyclecnt); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_UNLOCK_LOCKTH, 0x3); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR1_PR_INJ_MODE, ++ CSR_2L_PXP_CDR1_INJ_FORCE_OFF); ++ ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_IDAC); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PR_PWDB); ++ ++ for (i = 0; i < LEQ_LEN_CTRL_MAX_VAL; i++) { ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, i << 8); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = FIELD_GET(PCIE_RO_FL_OUT, ++ readl(pcie_phy->pma1 + ++ REG_PCIE_PMA_RO_RX_FREQDET)); ++ if (val > fl_out_target) ++ cdr_pr_idac_tmp = i << 8; ++ } ++ ++ for (i = LEQ_LEN_CTRL_MAX_VAL; i >= 0; i--) { ++ pr_idac = cdr_pr_idac_tmp | (0x1 << i); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, pr_idac); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = FIELD_GET(PCIE_RO_FL_OUT, ++ readl(pcie_phy->pma1 + ++ REG_PCIE_PMA_RO_RX_FREQDET)); ++ if (val < fl_out_target) ++ pr_idac &= ~(0x1 << i); ++ ++ cdr_pr_idac_tmp = pr_idac; ++ } ++ ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, ++ cdr_pr_idac_tmp); ++ ++ for (i = 0; i < FREQ_LOCK_MAX_ATTEMPT; i++) { ++ u32 val; ++ ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = readl(pcie_phy->pma1 + REG_PCIE_PMA_RO_RX_FREQDET); ++ if (val & PCIE_RO_FBCK_LOCK) ++ break; ++ } ++ ++ /* turn off force mode and update band values */ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR1_PR_INJ_MODE, ++ CSR_2L_PXP_CDR1_INJ_FORCE_OFF); ++ ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_IDAC); ++ if (gen == PCIE_PORT_GEN3) { ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_14, ++ PCIE_FLL_IDAC_PCIEG3, ++ cdr_pr_idac_tmp); ++ } else { ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_13, ++ PCIE_FLL_IDAC_PCIEG1, ++ cdr_pr_idac_tmp); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_13, ++ PCIE_FLL_IDAC_PCIEG2, ++ cdr_pr_idac_tmp); ++ } ++} ++ ++static void airoha_pcie_phy_init_default(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CMN, ++ CSR_2L_PXP_CMN_TRIM_MASK, 0x10); ++ writel(0xcccbcccb, pcie_phy->pma0 + REG_PCIE_PMA_DIG_RESERVE_21); ++ writel(0xcccb, pcie_phy->pma0 + REG_PCIE_PMA_DIG_RESERVE_22); ++ writel(0xcccbcccb, pcie_phy->pma1 + REG_PCIE_PMA_DIG_RESERVE_21); ++ writel(0xcccb, pcie_phy->pma1 + REG_PCIE_PMA_DIG_RESERVE_22); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CMN, ++ CSR_2L_PXP_CMN_LANE_EN); ++} ++ ++static void airoha_pcie_phy_init_clk_out(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_TXPLL_POSTDIV_D256, ++ CSR_2L_PXP_CLKTX0_AMP, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_CLKTX0_FORCE_OUT1, ++ CSR_2L_PXP_CLKTX1_AMP, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_TXPLL_POSTDIV_D256, ++ CSR_2L_PXP_CLKTX0_OFFSET, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CLKTX1_OFFSET, ++ CSR_2L_PXP_CLKTX1_OFFSET, 0x2); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CLKTX0_FORCE_OUT1, ++ CSR_2L_PXP_CLKTX0_HZ); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CLKTX1_OFFSET, ++ CSR_2L_PXP_CLKTX1_HZ); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_CLKTX0_FORCE_OUT1, ++ CSR_2L_PXP_CLKTX0_IMP_SEL, 0x12); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CLKTX1_IMP_SEL, ++ CSR_2L_PXP_CLKTX1_IMP_SEL, 0x12); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_POSTDIV_D256, ++ CSR_2L_PXP_CLKTX0_SR); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CLKTX1_OFFSET, ++ CSR_2L_PXP_CLKTX1_SR); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_PLL_CMN_RESERVE0, ++ CSR_2L_PXP_PLL_RESERVE_MASK, 0xdd); ++} ++ ++static void airoha_pcie_phy_init_csr_2l(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_SW_XFI_RXPCS_RST | PCIE_SW_REF_RST | ++ PCIE_SW_RX_RST); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_SW_XFI_RXPCS_RST | PCIE_SW_REF_RST | ++ PCIE_SW_RX_RST); ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_TX_RESET, ++ PCIE_TX_TOP_RST | REG_PCIE_PMA_TX_RESET); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_TX_RESET, ++ PCIE_TX_TOP_RST | REG_PCIE_PMA_TX_RESET); ++} ++ ++static void airoha_pcie_phy_init_rx(struct airoha_pcie_phy *pcie_phy) ++{ ++ writel(0x2a00090b, pcie_phy->pma0 + REG_PCIE_PMA_DIG_RESERVE_17); ++ writel(0x2a00090b, pcie_phy->pma1 + REG_PCIE_PMA_DIG_RESERVE_17); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR0_PR_MONPI, ++ CSR_2L_PXP_CDR0_PR_XFICK_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR1_PR_MONPI, ++ CSR_2L_PXP_CDR1_PR_XFICK_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, ++ REG_CSR_2L_CDR0_PD_PICAL_CKD8_INV, ++ CSR_2L_PXP_CDR0_PD_EDGE_DISABLE); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, ++ REG_CSR_2L_CDR1_PD_PICAL_CKD8_INV, ++ CSR_2L_PXP_CDR1_PD_EDGE_DISABLE); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_PHYCK_DIV, ++ CSR_2L_PXP_RX0_PHYCK_SEL, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_PHYCK_DIV, ++ CSR_2L_PXP_RX1_PHYCK_SEL, 0x1); ++} ++ ++static void airoha_pcie_phy_init_jcpll(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_EN); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_TCL_VTP_EN, ++ CSR_2L_PXP_JCPLL_SPARE_LOW, 0x20); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_RST_DLY, ++ CSR_2L_PXP_JCPLL_RST); ++ writel(0x0, pcie_phy->csr_2l + REG_CSR_2L_JCPLL_SSC_DELTA1); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC_PERIOD, ++ CSR_2L_PXP_JCPLL_SSC_PERIOD); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_PHASE_INI); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_TRI_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BR, ++ CSR_2L_PXP_JCPLL_LPF_BR, 0xa); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BR, ++ CSR_2L_PXP_JCPLL_LPF_BP, 0xc); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BR, ++ CSR_2L_PXP_JCPLL_LPF_BC, 0x1f); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BWC, ++ CSR_2L_PXP_JCPLL_LPF_BWC, 0x1e); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BR, ++ CSR_2L_PXP_JCPLL_LPF_BWR, 0xa); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_JCPLL_MMD_PREDIV_MODE, ++ CSR_2L_PXP_JCPLL_MMD_PREDIV_MODE, ++ 0x1); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, CSR_2L_PXP_JCPLL_MONCK, ++ CSR_2L_PXP_JCPLL_REFIN_DIV); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_VOS, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_SDM_PCW); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_VOS, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_SDM_PCW); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_JCPLL_SDM_PCW, ++ PCIE_FORCE_DA_PXP_JCPLL_SDM_PCW, ++ 0x50000000); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_JCPLL_SDM_PCW, ++ PCIE_FORCE_DA_PXP_JCPLL_SDM_PCW, ++ 0x50000000); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, ++ REG_CSR_2L_JCPLL_MMD_PREDIV_MODE, ++ CSR_2L_PXP_JCPLL_POSTDIV_D5); ++ airoha_phy_csr_2l_set_bits(pcie_phy, ++ REG_CSR_2L_JCPLL_MMD_PREDIV_MODE, ++ CSR_2L_PXP_JCPLL_POSTDIV_D2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_RST_DLY, ++ CSR_2L_PXP_JCPLL_RST_DLY, 0x4); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_RST_DLY, ++ CSR_2L_PXP_JCPLL_SDM_DI_LS); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_TCL_KBAND_VREF, ++ CSR_2L_PXP_JCPLL_VCO_KBAND_MEAS_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_IB_EXT, ++ CSR_2L_PXP_JCPLL_CHP_IOFST); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_IB_EXT, ++ CSR_2L_PXP_JCPLL_CHP_IBIAS, 0xc); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_JCPLL_MMD_PREDIV_MODE, ++ CSR_2L_PXP_JCPLL_MMD_PREDIV_MODE, ++ 0x1); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_VCODIV, ++ CSR_2L_PXP_JCPLL_VCO_HALFLSB_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_VCODIV, ++ CSR_2L_PXP_JCPLL_VCO_CFIX, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_VCODIV, ++ CSR_2L_PXP_JCPLL_VCO_SCAPWR, 0x4); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_IB_EXT, ++ REG_CSR_2L_JCPLL_LPF_SHCK_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_KBAND_KFC, ++ CSR_2L_PXP_JCPLL_POSTDIV_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_KBAND_KFC, ++ CSR_2L_PXP_JCPLL_KBAND_KFC); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_KBAND_KFC, ++ CSR_2L_PXP_JCPLL_KBAND_KF, 0x3); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_KBAND_KFC, ++ CSR_2L_PXP_JCPLL_KBAND_KS); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BWC, ++ CSR_2L_PXP_JCPLL_KBAND_DIV, 0x1); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_SCAN_MODE, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_KBAND_LOAD_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, REG_PCIE_PMA_SCAN_MODE, ++ PCIE_FORCE_DA_PXP_JCPLL_KBAND_LOAD_EN); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BWC, ++ CSR_2L_PXP_JCPLL_KBAND_CODE, 0xe4); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SDM_HREN, ++ CSR_2L_PXP_JCPLL_TCL_AMP_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_TCL_CMP, ++ CSR_2L_PXP_JCPLL_TCL_LPF_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_JCPLL_TCL_KBAND_VREF, ++ CSR_2L_PXP_JCPLL_TCL_KBAND_VREF, 0xf); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_SDM_HREN, ++ CSR_2L_PXP_JCPLL_TCL_AMP_GAIN, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_SDM_HREN, ++ CSR_2L_PXP_JCPLL_TCL_AMP_VREF, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_TCL_CMP, ++ CSR_2L_PXP_JCPLL_TCL_LPF_BW, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_VCO_TCLVAR, ++ CSR_2L_PXP_JCPLL_VCO_TCLVAR, 0x3); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_CKOUT_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_CKOUT_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_CKOUT_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_CKOUT_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_EN); ++} ++ ++static void airoha_pcie_phy_txpll(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_EN); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_EN); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_REFIN_DIV, ++ CSR_2L_PXP_TXPLL_PLL_RSTB); ++ writel(0x0, pcie_phy->csr_2l + REG_CSR_2L_TXPLL_SSC_DELTA1); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SSC_PERIOD, ++ CSR_2L_PXP_txpll_SSC_PERIOD); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_CHP_IOFST, ++ CSR_2L_PXP_TXPLL_CHP_IOFST, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_750M_SYS_CK, ++ CSR_2L_PXP_TXPLL_CHP_IBIAS, 0x2d); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_REFIN_DIV, ++ CSR_2L_PXP_TXPLL_REFIN_DIV); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_TCL_LPF_BW, ++ CSR_2L_PXP_TXPLL_VCO_CFIX, 0x3); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_SDM_PCW); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_SDM_PCW); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_SDM_PCW, ++ PCIE_FORCE_DA_PXP_TXPLL_SDM_PCW, ++ 0xc800000); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_SDM_PCW, ++ PCIE_FORCE_DA_PXP_TXPLL_SDM_PCW, ++ 0xc800000); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SDM_DI_LS, ++ CSR_2L_PXP_TXPLL_SDM_IFM); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SSC, ++ CSR_2L_PXP_TXPLL_SSC_PHASE_INI); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_REFIN_DIV, ++ CSR_2L_PXP_TXPLL_RST_DLY, 0x4); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SDM_DI_LS, ++ CSR_2L_PXP_TXPLL_SDM_DI_LS); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_SDM_DI_LS, ++ CSR_2L_PXP_TXPLL_SDM_ORD, 0x3); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_TCL_KBAND_VREF, ++ CSR_2L_PXP_TXPLL_VCO_KBAND_MEAS_EN); ++ writel(0x0, pcie_phy->csr_2l + REG_CSR_2L_TXPLL_SSC_DELTA1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_CHP_IOFST, ++ CSR_2L_PXP_TXPLL_LPF_BP, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_CHP_IOFST, ++ CSR_2L_PXP_TXPLL_LPF_BC, 0x18); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_CHP_IOFST, ++ CSR_2L_PXP_TXPLL_LPF_BR, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_CHP_IOFST, ++ CSR_2L_PXP_TXPLL_CHP_IOFST, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_750M_SYS_CK, ++ CSR_2L_PXP_TXPLL_CHP_IBIAS, 0x2d); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_TCL_VTP, ++ CSR_2L_PXP_TXPLL_SPARE_L, 0x1); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_LPF_BWR, ++ CSR_2L_PXP_TXPLL_LPF_BWC); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_POSTDIV, ++ CSR_2L_PXP_TXPLL_MMD_PREDIV_MODE); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_REFIN_DIV, ++ CSR_2L_PXP_TXPLL_REFIN_DIV); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_TCL_LPF_BW, ++ CSR_2L_PXP_TXPLL_VCO_HALFLSB_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_VCO_SCAPWR, ++ CSR_2L_PXP_TXPLL_VCO_SCAPWR, 0x7); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_TCL_LPF_BW, ++ CSR_2L_PXP_TXPLL_VCO_CFIX, 0x3); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_SDM_PCW); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_SDM_PCW); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SSC, ++ CSR_2L_PXP_TXPLL_SSC_PHASE_INI); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_LPF_BWR, ++ CSR_2L_PXP_TXPLL_LPF_BWR); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_PHY_CK2, ++ CSR_2L_PXP_TXPLL_REFIN_INTERNAL); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_TCL_KBAND_VREF, ++ CSR_2L_PXP_TXPLL_VCO_KBAND_MEAS_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_VTP, ++ CSR_2L_PXP_TXPLL_VTP_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_POSTDIV, ++ CSR_2L_PXP_TXPLL_PHY_CK1_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_PHY_CK2, ++ CSR_2L_PXP_TXPLL_REFIN_INTERNAL); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SSC, ++ CSR_2L_PXP_TXPLL_SSC_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_750M_SYS_CK, ++ CSR_2L_PXP_TXPLL_LPF_SHCK_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_POSTDIV, ++ CSR_2L_PXP_TXPLL_POSTDIV_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_KBAND_DIV, ++ CSR_2L_PXP_TXPLL_KBAND_KFC); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_KBAND_DIV, ++ CSR_2L_PXP_TXPLL_KBAND_KF, 0x3); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_KBAND_DIV, ++ CSR_2L_PXP_txpll_KBAND_KS, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_KBAND_DIV, ++ CSR_2L_PXP_TXPLL_KBAND_DIV, 0x4); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_LPF_BWR, ++ CSR_2L_PXP_TXPLL_KBAND_CODE, 0xe4); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_SDM_OUT, ++ CSR_2L_PXP_TXPLL_TCL_AMP_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_TCL_AMP_VREF, ++ CSR_2L_PXP_TXPLL_TCL_LPF_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_TXPLL_TCL_KBAND_VREF, ++ CSR_2L_PXP_TXPLL_TCL_KBAND_VREF, 0xf); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_SDM_OUT, ++ CSR_2L_PXP_TXPLL_TCL_AMP_GAIN, 0x3); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_TXPLL_TCL_AMP_VREF, ++ CSR_2L_PXP_TXPLL_TCL_AMP_VREF, 0xb); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_TCL_LPF_BW, ++ CSR_2L_PXP_TXPLL_TCL_LPF_BW, 0x3); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_CKOUT_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_CKOUT_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_CKOUT_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_CKOUT_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_EN); ++} ++ ++static void airoha_pcie_phy_init_ssc_jcpll(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_SSC_DELTA1, ++ CSR_2L_PXP_JCPLL_SSC_DELTA1, 0x106); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_SSC_DELTA1, ++ CSR_2L_PXP_JCPLL_SSC_DELTA, 0x106); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_SSC_PERIOD, ++ CSR_2L_PXP_JCPLL_SSC_PERIOD, 0x31b); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_PHASE_INI); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SDM_IFM, ++ CSR_2L_PXP_JCPLL_SDM_IFM); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SDM_HREN, ++ REG_CSR_2L_JCPLL_SDM_HREN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_RST_DLY, ++ CSR_2L_PXP_JCPLL_SDM_DI_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_TRI_EN); ++} ++ ++static void ++airoha_pcie_phy_set_rxlan0_signal_detect(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR0_PR_COR_HBW, ++ CSR_2L_PXP_CDR0_PR_LDO_FORCE_ON); ++ ++ usleep_range(100, 200); ++ ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_19, ++ PCIE_PCP_RX_REV0_PCIE_GEN1, 0x18b0); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_20, ++ PCIE_PCP_RX_REV0_PCIE_GEN2, 0x18b0); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_20, ++ PCIE_PCP_RX_REV0_PCIE_GEN3, 0x1030); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_SIGDET_DCTEST, ++ CSR_2L_PXP_RX0_SIGDET_PEAK, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_SIGDET_VTH_SEL, ++ CSR_2L_PXP_RX0_SIGDET_VTH_SEL, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_REV0, ++ CSR_2L_PXP_VOS_PNINV, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_SIGDET_DCTEST, ++ CSR_2L_PXP_RX0_SIGDET_LPF_CTRL, 0x1); ++ ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_CAL2, ++ PCIE_CAL_OUT_OS, 0x0); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_PXP_RX0_FE_VB_EQ2, ++ CSR_2L_PXP_RX0_FE_VCM_GEN_PWDB); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_GAIN_CTRL, ++ PCIE_FORCE_SEL_DA_PXP_RX_FE_PWDB); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_GAIN_CTRL, ++ PCIE_FORCE_DA_PXP_RX_FE_GAIN_CTRL, 0x3); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_RX_FORCE_MODE0, ++ PCIE_FORCE_DA_XPON_RX_FE_GAIN_CTRL, 0x1); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_SIGDET0, ++ PCIE_SIGDET_WIN_NONVLD_TIMES, 0x3); ++ airoha_phy_pma0_clear_bits(pcie_phy, REG_PCIE_PMA_SEQUENCE_DISB_CTRL1, ++ PCIE_DISB_RX_SDCAL_EN); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_CTRL_SEQUENCE_FORCE_CTRL1, ++ PCIE_FORCE_RX_SDCAL_EN); ++ usleep_range(150, 200); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_CTRL_SEQUENCE_FORCE_CTRL1, ++ PCIE_FORCE_RX_SDCAL_EN); ++} ++ ++static void ++airoha_pcie_phy_set_rxlan1_signal_detect(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR1_PR_COR_HBW, ++ CSR_2L_PXP_CDR1_PR_LDO_FORCE_ON); ++ ++ usleep_range(100, 200); ++ ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_19, ++ PCIE_PCP_RX_REV0_PCIE_GEN1, 0x18b0); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_20, ++ PCIE_PCP_RX_REV0_PCIE_GEN2, 0x18b0); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_20, ++ PCIE_PCP_RX_REV0_PCIE_GEN3, 0x1030); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_SIGDET_NOVTH, ++ CSR_2L_PXP_RX1_SIGDET_PEAK, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_SIGDET_NOVTH, ++ CSR_2L_PXP_RX1_SIGDET_VTH_SEL, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_REV0, ++ CSR_2L_PXP_VOS_PNINV, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_DAC_RANGE_EYE, ++ CSR_2L_PXP_RX1_SIGDET_LPF_CTRL, 0x1); ++ ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_CAL2, ++ PCIE_CAL_OUT_OS, 0x0); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_RX1_FE_VB_EQ1, ++ CSR_2L_PXP_RX1_FE_VCM_GEN_PWDB); ++ ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_GAIN_CTRL, ++ PCIE_FORCE_SEL_DA_PXP_RX_FE_PWDB); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_GAIN_CTRL, ++ PCIE_FORCE_DA_PXP_RX_FE_GAIN_CTRL, 0x3); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_RX_FORCE_MODE0, ++ PCIE_FORCE_DA_XPON_RX_FE_GAIN_CTRL, 0x1); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_SIGDET0, ++ PCIE_SIGDET_WIN_NONVLD_TIMES, 0x3); ++ airoha_phy_pma1_clear_bits(pcie_phy, REG_PCIE_PMA_SEQUENCE_DISB_CTRL1, ++ PCIE_DISB_RX_SDCAL_EN); ++ ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_CTRL_SEQUENCE_FORCE_CTRL1, ++ PCIE_FORCE_RX_SDCAL_EN); ++ usleep_range(150, 200); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_CTRL_SEQUENCE_FORCE_CTRL1, ++ PCIE_FORCE_RX_SDCAL_EN); ++} ++ ++static void airoha_pcie_phy_set_rxflow(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_SCAN_RST, ++ PCIE_FORCE_DA_PXP_RX_SIGDET_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_RX_SIGDET_PWDB); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_SCAN_RST, ++ PCIE_FORCE_DA_PXP_RX_SIGDET_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_RX_SIGDET_PWDB); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PD_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PD_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_CDR_PD_PWDB); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_PWDB, ++ PCIE_FORCE_DA_PXP_RX_FE_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_RX_FE_PWDB); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PD_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PD_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_CDR_PD_PWDB); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_PWDB, ++ PCIE_FORCE_DA_PXP_RX_FE_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_RX_FE_PWDB); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_RX0_PHYCK_DIV, ++ CSR_2L_PXP_RX0_PHYCK_RSTB | ++ CSR_2L_PXP_RX0_TDC_CK_SEL); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_RX1_PHYCK_DIV, ++ CSR_2L_PXP_RX1_PHYCK_RSTB | ++ CSR_2L_PXP_RX1_TDC_CK_SEL); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_SW_RX_FIFO_RST | PCIE_SW_TX_RST | ++ PCIE_SW_PMA_RST | PCIE_SW_ALLPCS_RST | ++ PCIE_SW_TX_FIFO_RST); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_SW_RX_FIFO_RST | PCIE_SW_TX_RST | ++ PCIE_SW_PMA_RST | PCIE_SW_ALLPCS_RST | ++ PCIE_SW_TX_FIFO_RST); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_PXP_RX0_FE_VB_EQ2, ++ CSR_2L_PXP_RX0_FE_VB_EQ2_EN | ++ CSR_2L_PXP_RX0_FE_VB_EQ3_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_RX0_SIGDET_VTH_SEL, ++ CSR_2L_PXP_RX0_FE_VB_EQ1_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_RX1_FE_VB_EQ1, ++ CSR_2L_PXP_RX1_FE_VB_EQ1_EN | ++ CSR_2L_PXP_RX1_FE_VB_EQ2_EN | ++ CSR_2L_PXP_RX1_FE_VB_EQ3_EN); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_REV0, ++ CSR_2L_PXP_FE_GAIN_NORMAL_MODE, 0x4); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_REV0, ++ CSR_2L_PXP_FE_GAIN_TRAIN_MODE, 0x4); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_REV0, ++ CSR_2L_PXP_FE_GAIN_NORMAL_MODE, 0x4); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_REV0, ++ CSR_2L_PXP_FE_GAIN_TRAIN_MODE, 0x4); ++} ++ ++static void airoha_pcie_phy_set_pr(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_PR_VREG_IBAND, ++ CSR_2L_PXP_CDR0_PR_VREG_IBAND, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_PR_VREG_IBAND, ++ CSR_2L_PXP_CDR0_PR_VREG_CKBUF, 0x5); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR0_PR_CKREF_DIV, ++ CSR_2L_PXP_CDR0_PR_CKREF_DIV); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR0_PR_COR_HBW, ++ CSR_2L_PXP_CDR0_PR_CKREF_DIV1); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_CDR1_PR_VREG_IBAND_VAL, ++ CSR_2L_PXP_CDR1_PR_VREG_IBAND, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_CDR1_PR_VREG_IBAND_VAL, ++ CSR_2L_PXP_CDR1_PR_VREG_CKBUF, 0x5); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR1_PR_CKREF_DIV, ++ CSR_2L_PXP_CDR1_PR_CKREF_DIV); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR1_PR_COR_HBW, ++ CSR_2L_PXP_CDR1_PR_CKREF_DIV1); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_LPF_RATIO, ++ CSR_2L_PXP_CDR0_LPF_TOP_LIM, 0x20000); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR1_LPF_RATIO, ++ CSR_2L_PXP_CDR1_LPF_TOP_LIM, 0x20000); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_PR_BETA_DAC, ++ CSR_2L_PXP_CDR0_PR_BETA_SEL, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR1_PR_BETA_DAC, ++ CSR_2L_PXP_CDR1_PR_BETA_SEL, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_PR_BETA_DAC, ++ CSR_2L_PXP_CDR0_PR_KBAND_DIV, 0x4); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR1_PR_BETA_DAC, ++ CSR_2L_PXP_CDR1_PR_KBAND_DIV, 0x4); ++} ++ ++static void airoha_pcie_phy_set_txflow(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TX0_CKLDO, ++ CSR_2L_PXP_TX0_CKLDO_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TX1_CKLDO, ++ CSR_2L_PXP_TX1_CKLDO_EN); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TX0_CKLDO, ++ CSR_2L_PXP_TX0_DMEDGEGEN_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TX1_CKLDO, ++ CSR_2L_PXP_TX1_DMEDGEGEN_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TX1_MULTLANE, ++ CSR_2L_PXP_TX1_MULTLANE_EN); ++} ++ ++static void airoha_pcie_phy_set_rx_mode(struct airoha_pcie_phy *pcie_phy) ++{ ++ writel(0x804000, pcie_phy->pma0 + REG_PCIE_PMA_DIG_RESERVE_27); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G1, 0x5); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G2, 0x5); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G3, 0x5); ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_30, ++ 0x77700); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR0_PR_MONCK, ++ CSR_2L_PXP_CDR0_PR_MONCK_ENABLE); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_PR_MONCK, ++ CSR_2L_PXP_CDR0_PR_RESERVE0, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_PXP_RX0_OSCAL_CTLE1IOS, ++ CSR_2L_PXP_RX0_PR_OSCAL_VGA1IOS, 0x19); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_PXP_RX0_OSCA_VGA1VOS, ++ CSR_2L_PXP_RX0_PR_OSCAL_VGA1VOS, 0x19); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_PXP_RX0_OSCA_VGA1VOS, ++ CSR_2L_PXP_RX0_PR_OSCAL_VGA2IOS, 0x14); ++ ++ writel(0x804000, pcie_phy->pma1 + REG_PCIE_PMA_DIG_RESERVE_27); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G1, 0x5); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G2, 0x5); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G3, 0x5); ++ ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_30, ++ 0x77700); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR1_PR_MONCK, ++ CSR_2L_PXP_CDR1_PR_MONCK_ENABLE); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR1_PR_MONCK, ++ CSR_2L_PXP_CDR1_PR_RESERVE0, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_OSCAL_VGA1IOS, ++ CSR_2L_PXP_RX1_PR_OSCAL_VGA1IOS, 0x19); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_OSCAL_VGA1IOS, ++ CSR_2L_PXP_RX1_PR_OSCAL_VGA1VOS, 0x19); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_OSCAL_VGA1IOS, ++ CSR_2L_PXP_RX1_PR_OSCAL_VGA2IOS, 0x14); ++} ++ ++static void airoha_pcie_phy_load_kflow(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_12, ++ PCIE_FORCE_PMA_RX_SPEED, 0xa); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_12, ++ PCIE_FORCE_PMA_RX_SPEED, 0xa); ++ airoha_phy_init_lane0_rx_fw_pre_calib(pcie_phy, PCIE_PORT_GEN3); ++ airoha_phy_init_lane1_rx_fw_pre_calib(pcie_phy, PCIE_PORT_GEN3); ++ ++ airoha_phy_pma0_clear_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_12, ++ PCIE_FORCE_PMA_RX_SPEED); ++ airoha_phy_pma1_clear_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_12, ++ PCIE_FORCE_PMA_RX_SPEED); ++ usleep_range(100, 200); ++ ++ airoha_phy_init_lane0_rx_fw_pre_calib(pcie_phy, PCIE_PORT_GEN2); ++ airoha_phy_init_lane1_rx_fw_pre_calib(pcie_phy, PCIE_PORT_GEN2); ++} ++ ++/** ++ * airoha_pcie_phy_init() - Initialize the phy ++ * @phy: the phy to be initialized ++ * ++ * Initialize the phy registers. ++ * The hardware settings will be reset during suspend, it should be ++ * reinitialized when the consumer calls phy_init() again on resume. ++ */ ++static int airoha_pcie_phy_init(struct phy *phy) ++{ ++ struct airoha_pcie_phy *pcie_phy = phy_get_drvdata(phy); ++ ++ /* enable load FLL-K flow */ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_14, ++ PCIE_FLL_LOAD_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_14, ++ PCIE_FLL_LOAD_EN); ++ ++ airoha_pcie_phy_init_default(pcie_phy); ++ airoha_pcie_phy_init_clk_out(pcie_phy); ++ airoha_pcie_phy_init_csr_2l(pcie_phy); ++ ++ usleep_range(100, 200); ++ ++ airoha_pcie_phy_init_rx(pcie_phy); ++ /* phase 1, no ssc for K TXPLL */ ++ airoha_pcie_phy_init_jcpll(pcie_phy); ++ ++ usleep_range(500, 600); ++ ++ /* TX PLL settings */ ++ airoha_pcie_phy_txpll(pcie_phy); ++ ++ usleep_range(200, 300); ++ ++ /* SSC JCPLL setting */ ++ airoha_pcie_phy_init_ssc_jcpll(pcie_phy); ++ ++ usleep_range(100, 200); ++ ++ /* Rx lan0 signal detect */ ++ airoha_pcie_phy_set_rxlan0_signal_detect(pcie_phy); ++ /* Rx lan1 signal detect */ ++ airoha_pcie_phy_set_rxlan1_signal_detect(pcie_phy); ++ /* RX FLOW */ ++ airoha_pcie_phy_set_rxflow(pcie_phy); ++ ++ usleep_range(100, 200); ++ ++ airoha_pcie_phy_set_pr(pcie_phy); ++ /* TX FLOW */ ++ airoha_pcie_phy_set_txflow(pcie_phy); ++ ++ usleep_range(100, 200); ++ /* RX mode setting */ ++ airoha_pcie_phy_set_rx_mode(pcie_phy); ++ /* Load K-Flow */ ++ airoha_pcie_phy_load_kflow(pcie_phy); ++ airoha_phy_pma0_clear_bits(pcie_phy, REG_PCIE_PMA_SS_DA_XPON_PWDB0, ++ PCIE_DA_XPON_CDR_PR_PWDB); ++ airoha_phy_pma1_clear_bits(pcie_phy, REG_PCIE_PMA_SS_DA_XPON_PWDB0, ++ PCIE_DA_XPON_CDR_PR_PWDB); ++ ++ usleep_range(100, 200); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_SS_DA_XPON_PWDB0, ++ PCIE_DA_XPON_CDR_PR_PWDB); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_SS_DA_XPON_PWDB0, ++ PCIE_DA_XPON_CDR_PR_PWDB); ++ ++ usleep_range(100, 200); ++ ++ return 0; ++} ++ ++static int airoha_pcie_phy_exit(struct phy *phy) ++{ ++ struct airoha_pcie_phy *pcie_phy = phy_get_drvdata(phy); ++ ++ airoha_phy_pma0_clear_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_PMA_SW_RST); ++ airoha_phy_pma1_clear_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_PMA_SW_RST); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_PHASE_INI | ++ CSR_2L_PXP_JCPLL_SSC_TRI_EN | ++ CSR_2L_PXP_JCPLL_SSC_EN); ++ ++ return 0; ++} ++ ++static const struct phy_ops airoha_pcie_phy_ops = { ++ .init = airoha_pcie_phy_init, ++ .exit = airoha_pcie_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int airoha_pcie_phy_probe(struct platform_device *pdev) ++{ ++ struct airoha_pcie_phy *pcie_phy; ++ struct device *dev = &pdev->dev; ++ struct phy_provider *provider; ++ ++ pcie_phy = devm_kzalloc(dev, sizeof(*pcie_phy), GFP_KERNEL); ++ if (!pcie_phy) ++ return -ENOMEM; ++ ++ pcie_phy->csr_2l = devm_platform_ioremap_resource_byname(pdev, "csr-2l"); ++ if (IS_ERR(pcie_phy->csr_2l)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->csr_2l), ++ "Failed to map phy-csr-2l base\n"); ++ ++ pcie_phy->pma0 = devm_platform_ioremap_resource_byname(pdev, "pma0"); ++ if (IS_ERR(pcie_phy->pma0)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->pma0), ++ "Failed to map phy-pma0 base\n"); ++ ++ pcie_phy->pma1 = devm_platform_ioremap_resource_byname(pdev, "pma1"); ++ if (IS_ERR(pcie_phy->pma1)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->pma1), ++ "Failed to map phy-pma1 base\n"); ++ ++ pcie_phy->phy = devm_phy_create(dev, dev->of_node, &airoha_pcie_phy_ops); ++ if (IS_ERR(pcie_phy->phy)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->phy), ++ "Failed to create PCIe phy\n"); ++ ++ pcie_phy->dev = dev; ++ phy_set_drvdata(pcie_phy->phy, pcie_phy); ++ ++ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(provider)) ++ return dev_err_probe(dev, PTR_ERR(provider), ++ "PCIe phy probe failed\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id airoha_pcie_phy_of_match[] = { ++ { .compatible = "airoha,en7581-pcie-phy" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, airoha_pcie_phy_of_match); ++ ++static struct platform_driver airoha_pcie_phy_driver = { ++ .probe = airoha_pcie_phy_probe, ++ .driver = { ++ .name = "airoha-pcie-phy", ++ .of_match_table = airoha_pcie_phy_of_match, ++ }, ++}; ++module_platform_driver(airoha_pcie_phy_driver); ++ ++MODULE_DESCRIPTION("Airoha PCIe PHY driver"); ++MODULE_AUTHOR("Lorenzo Bianconi "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.6/023-v6.11-phy-airoha-Add-dtime-and-Rx-AEQ-IO-registers.patch b/target/linux/airoha/patches-6.6/023-v6.11-phy-airoha-Add-dtime-and-Rx-AEQ-IO-registers.patch new file mode 100644 index 0000000000..51be7664b4 --- /dev/null +++ b/target/linux/airoha/patches-6.6/023-v6.11-phy-airoha-Add-dtime-and-Rx-AEQ-IO-registers.patch @@ -0,0 +1,112 @@ +From 2a011c3c12e8de461fb1fdce85fa38d308c4eb8b Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 29 Jun 2024 19:51:49 +0200 +Subject: [PATCH] phy: airoha: Add dtime and Rx AEQ IO registers + +Introduce Tx-Rx detection Time and Rx AEQ training mappings to +phy-airoha-pcie driver. This is a preliminary patch to introduce PCIe +support to En7581 SoC through the mediatek-gen3 PCIe driver. +This change is not introducing any backward compatibility issue since +the EN7581 dts is not upstream yet. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: AngeloGioacchino Del Regno +Link: https://lore.kernel.org/r/edf3b28926177166c65256604d69f2f576cb6fb3.1719682943.git.lorenzo@kernel.org +Signed-off-by: Vinod Koul +--- + drivers/phy/phy-airoha-pcie-regs.h | 17 +++++++++++++ + drivers/phy/phy-airoha-pcie.c | 38 ++++++++++++++++++++++++++++++ + 2 files changed, 55 insertions(+) + +--- a/drivers/phy/phy-airoha-pcie-regs.h ++++ b/drivers/phy/phy-airoha-pcie-regs.h +@@ -474,4 +474,21 @@ + #define REG_PCIE_PMA_DIG_RESERVE_27 0x0908 + #define REG_PCIE_PMA_DIG_RESERVE_30 0x0914 + ++/* DTIME */ ++#define REG_PCIE_PEXTP_DIG_GLB44 0x00 ++#define PCIE_XTP_RXDET_VCM_OFF_STB_T_SEL GENMASK(7, 0) ++#define PCIE_XTP_RXDET_EN_STB_T_SEL GENMASK(15, 8) ++#define PCIE_XTP_RXDET_FINISH_STB_T_SEL GENMASK(23, 16) ++#define PCIE_XTP_TXPD_TX_DATA_EN_DLY GENMASK(27, 24) ++#define PCIE_XTP_TXPD_RXDET_DONE_CDT BIT(28) ++#define PCIE_XTP_RXDET_LATCH_STB_T_SEL GENMASK(31, 29) ++ ++/* RX AEQ */ ++#define REG_PCIE_PEXTP_DIG_LN_RX30_P0 0x0000 ++#define PCIE_XTP_LN_RX_PDOWN_L1P2_EXIT_WAIT GENMASK(7, 0) ++#define PCIE_XTP_LN_RX_PDOWN_T2RLB_DIG_EN BIT(8) ++#define PCIE_XTP_LN_RX_PDOWN_E0_AEQEN_WAIT GENMASK(31, 16) ++ ++#define REG_PCIE_PEXTP_DIG_LN_RX30_P1 0x0100 ++ + #endif /* _PHY_AIROHA_PCIE_H */ +--- a/drivers/phy/phy-airoha-pcie.c ++++ b/drivers/phy/phy-airoha-pcie.c +@@ -31,6 +31,9 @@ enum airoha_pcie_port_gen { + * @csr_2l: Analogic lane IO mapped register base address + * @pma0: IO mapped register base address of PMA0-PCIe + * @pma1: IO mapped register base address of PMA1-PCIe ++ * @p0_xr_dtime: IO mapped register base address of port0 Tx-Rx detection time ++ * @p1_xr_dtime: IO mapped register base address of port1 Tx-Rx detection time ++ * @rx_aeq: IO mapped register base address of Rx AEQ training + */ + struct airoha_pcie_phy { + struct device *dev; +@@ -38,6 +41,9 @@ struct airoha_pcie_phy { + void __iomem *csr_2l; + void __iomem *pma0; + void __iomem *pma1; ++ void __iomem *p0_xr_dtime; ++ void __iomem *p1_xr_dtime; ++ void __iomem *rx_aeq; + }; + + static void airoha_phy_clear_bits(void __iomem *reg, u32 mask) +@@ -1101,6 +1107,21 @@ static void airoha_pcie_phy_load_kflow(s + static int airoha_pcie_phy_init(struct phy *phy) + { + struct airoha_pcie_phy *pcie_phy = phy_get_drvdata(phy); ++ u32 val; ++ ++ /* Setup Tx-Rx detection time */ ++ val = FIELD_PREP(PCIE_XTP_RXDET_VCM_OFF_STB_T_SEL, 0x33) | ++ FIELD_PREP(PCIE_XTP_RXDET_EN_STB_T_SEL, 0x1) | ++ FIELD_PREP(PCIE_XTP_RXDET_FINISH_STB_T_SEL, 0x2) | ++ FIELD_PREP(PCIE_XTP_TXPD_TX_DATA_EN_DLY, 0x3) | ++ FIELD_PREP(PCIE_XTP_RXDET_LATCH_STB_T_SEL, 0x1); ++ writel(val, pcie_phy->p0_xr_dtime + REG_PCIE_PEXTP_DIG_GLB44); ++ writel(val, pcie_phy->p1_xr_dtime + REG_PCIE_PEXTP_DIG_GLB44); ++ /* Setup Rx AEQ training time */ ++ val = FIELD_PREP(PCIE_XTP_LN_RX_PDOWN_L1P2_EXIT_WAIT, 0x32) | ++ FIELD_PREP(PCIE_XTP_LN_RX_PDOWN_E0_AEQEN_WAIT, 0x5050); ++ writel(val, pcie_phy->rx_aeq + REG_PCIE_PEXTP_DIG_LN_RX30_P0); ++ writel(val, pcie_phy->rx_aeq + REG_PCIE_PEXTP_DIG_LN_RX30_P1); + + /* enable load FLL-K flow */ + airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_14, +@@ -1217,6 +1238,23 @@ static int airoha_pcie_phy_probe(struct + return dev_err_probe(dev, PTR_ERR(pcie_phy->phy), + "Failed to create PCIe phy\n"); + ++ pcie_phy->p0_xr_dtime = ++ devm_platform_ioremap_resource_byname(pdev, "p0-xr-dtime"); ++ if (IS_ERR(pcie_phy->p0_xr_dtime)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->p0_xr_dtime), ++ "Failed to map P0 Tx-Rx dtime base\n"); ++ ++ pcie_phy->p1_xr_dtime = ++ devm_platform_ioremap_resource_byname(pdev, "p1-xr-dtime"); ++ if (IS_ERR(pcie_phy->p1_xr_dtime)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->p1_xr_dtime), ++ "Failed to map P1 Tx-Rx dtime base\n"); ++ ++ pcie_phy->rx_aeq = devm_platform_ioremap_resource_byname(pdev, "rx-aeq"); ++ if (IS_ERR(pcie_phy->rx_aeq)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->rx_aeq), ++ "Failed to map Rx AEQ base\n"); ++ + pcie_phy->dev = dev; + phy_set_drvdata(pcie_phy->phy, pcie_phy); + diff --git a/target/linux/airoha/patches-6.6/024-v6.12-phy-airoha-adjust-initialization-delay-in-airoha_pci.patch b/target/linux/airoha/patches-6.6/024-v6.12-phy-airoha-adjust-initialization-delay-in-airoha_pci.patch new file mode 100644 index 0000000000..ff31b23800 --- /dev/null +++ b/target/linux/airoha/patches-6.6/024-v6.12-phy-airoha-adjust-initialization-delay-in-airoha_pci.patch @@ -0,0 +1,40 @@ +From 7f7315db3d262298ab33d198d3f0b09cabfa7b6b Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 6 Aug 2024 17:55:48 +0200 +Subject: [PATCH] phy: airoha: adjust initialization delay in + airoha_pcie_phy_init() + +Align phy-pcie initialization delay to the vendor sdk in +airoha_pcie_phy_init routine and allow the hw to complete required +configuration before proceeding + +Reviewed-by: AngeloGioacchino Del Regno +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/8af6f27857619f1e0dd227f08b8584ae8fb22fb2.1722959625.git.lorenzo@kernel.org +Signed-off-by: Vinod Koul +--- + drivers/phy/phy-airoha-pcie.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/phy/phy-airoha-pcie.c ++++ b/drivers/phy/phy-airoha-pcie.c +@@ -18,6 +18,9 @@ + #define LEQ_LEN_CTRL_MAX_VAL 7 + #define FREQ_LOCK_MAX_ATTEMPT 10 + ++/* PCIe-PHY initialization time in ms needed by the hw to complete */ ++#define PHY_HW_INIT_TIME_MS 30 ++ + enum airoha_pcie_port_gen { + PCIE_PORT_GEN1 = 1, + PCIE_PORT_GEN2, +@@ -1181,7 +1184,8 @@ static int airoha_pcie_phy_init(struct p + airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_SS_DA_XPON_PWDB0, + PCIE_DA_XPON_CDR_PR_PWDB); + +- usleep_range(100, 200); ++ /* Wait for the PCIe PHY to complete initialization before returning */ ++ msleep(PHY_HW_INIT_TIME_MS); + + return 0; + } diff --git a/target/linux/airoha/patches-6.6/025-01-v6.13-phy-airoha-Fix-REG_CSR_2L_PLL_CMN_RESERVE0-config-in.patch b/target/linux/airoha/patches-6.6/025-01-v6.13-phy-airoha-Fix-REG_CSR_2L_PLL_CMN_RESERVE0-config-in.patch new file mode 100644 index 0000000000..271ef01ed3 --- /dev/null +++ b/target/linux/airoha/patches-6.6/025-01-v6.13-phy-airoha-Fix-REG_CSR_2L_PLL_CMN_RESERVE0-config-in.patch @@ -0,0 +1,26 @@ +From ca9afde0563a80200eab856a53d7eab28c8fdd90 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 18 Sep 2024 15:32:52 +0200 +Subject: [PATCH 1/4] phy: airoha: Fix REG_CSR_2L_PLL_CMN_RESERVE0 config in + airoha_pcie_phy_init_clk_out() + +Fix typo configuring REG_CSR_2L_PLL_CMN_RESERVE0 register in +airoha_pcie_phy_init_clk_out routine. + +Fixes: d7d2818b9383 ("phy: airoha: Add PCIe PHY driver for EN7581 SoC.") +Signed-off-by: Lorenzo Bianconi +--- + drivers/phy/phy-airoha-pcie.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/phy/phy-airoha-pcie.c ++++ b/drivers/phy/phy-airoha-pcie.c +@@ -459,7 +459,7 @@ static void airoha_pcie_phy_init_clk_out + airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CLKTX1_OFFSET, + CSR_2L_PXP_CLKTX1_SR); + airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_PLL_CMN_RESERVE0, +- CSR_2L_PXP_PLL_RESERVE_MASK, 0xdd); ++ CSR_2L_PXP_PLL_RESERVE_MASK, 0xd0d); + } + + static void airoha_pcie_phy_init_csr_2l(struct airoha_pcie_phy *pcie_phy) diff --git a/target/linux/airoha/patches-6.6/025-02-v6.13-phy-airoha-Fix-REG_PCIE_PMA_TX_RESET-config-in-airoh.patch b/target/linux/airoha/patches-6.6/025-02-v6.13-phy-airoha-Fix-REG_PCIE_PMA_TX_RESET-config-in-airoh.patch new file mode 100644 index 0000000000..5c909596f1 --- /dev/null +++ b/target/linux/airoha/patches-6.6/025-02-v6.13-phy-airoha-Fix-REG_PCIE_PMA_TX_RESET-config-in-airoh.patch @@ -0,0 +1,29 @@ +From 2c2313c84ad7c0e5e39fbd98559d40f6b9ec1f83 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 18 Sep 2024 15:32:53 +0200 +Subject: [PATCH 2/4] phy: airoha: Fix REG_PCIE_PMA_TX_RESET config in + airoha_pcie_phy_init_csr_2l() + +Fix typos configuring REG_PCIE_PMA_TX_RESET register in +airoha_pcie_phy_init_csr_2l routine for lane0 and lane1 + +Fixes: d7d2818b9383 ("phy: airoha: Add PCIe PHY driver for EN7581 SoC.") +Signed-off-by: Lorenzo Bianconi +--- + drivers/phy/phy-airoha-pcie.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/phy/phy-airoha-pcie.c ++++ b/drivers/phy/phy-airoha-pcie.c +@@ -471,9 +471,9 @@ static void airoha_pcie_phy_init_csr_2l( + PCIE_SW_XFI_RXPCS_RST | PCIE_SW_REF_RST | + PCIE_SW_RX_RST); + airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_TX_RESET, +- PCIE_TX_TOP_RST | REG_PCIE_PMA_TX_RESET); ++ PCIE_TX_TOP_RST | PCIE_TX_CAL_RST); + airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_TX_RESET, +- PCIE_TX_TOP_RST | REG_PCIE_PMA_TX_RESET); ++ PCIE_TX_TOP_RST | PCIE_TX_CAL_RST); + } + + static void airoha_pcie_phy_init_rx(struct airoha_pcie_phy *pcie_phy) diff --git a/target/linux/airoha/patches-6.6/025-03-v6.13-phy-airoha-Fix-REG_CSR_2L_JCPLL_SDM_HREN-config-in-a.patch b/target/linux/airoha/patches-6.6/025-03-v6.13-phy-airoha-Fix-REG_CSR_2L_JCPLL_SDM_HREN-config-in-a.patch new file mode 100644 index 0000000000..8cde5f1cf7 --- /dev/null +++ b/target/linux/airoha/patches-6.6/025-03-v6.13-phy-airoha-Fix-REG_CSR_2L_JCPLL_SDM_HREN-config-in-a.patch @@ -0,0 +1,26 @@ +From 6e0c349a8a59959c3d3571b5f6776bc2d2ca62bc Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 18 Sep 2024 15:32:54 +0200 +Subject: [PATCH 3/4] phy: airoha: Fix REG_CSR_2L_JCPLL_SDM_HREN config in + airoha_pcie_phy_init_ssc_jcpll() + +Fix typo configuring REG_CSR_2L_JCPLL_SDM_HREN register in +airoha_pcie_phy_init_ssc_jcpll routine. + +Fixes: d7d2818b9383 ("phy: airoha: Add PCIe PHY driver for EN7581 SoC.") +Signed-off-by: Lorenzo Bianconi +--- + drivers/phy/phy-airoha-pcie.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/phy/phy-airoha-pcie.c ++++ b/drivers/phy/phy-airoha-pcie.c +@@ -802,7 +802,7 @@ static void airoha_pcie_phy_init_ssc_jcp + airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SDM_IFM, + CSR_2L_PXP_JCPLL_SDM_IFM); + airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SDM_HREN, +- REG_CSR_2L_JCPLL_SDM_HREN); ++ CSR_2L_PXP_JCPLL_SDM_HREN); + airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_RST_DLY, + CSR_2L_PXP_JCPLL_SDM_DI_EN); + airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, diff --git a/target/linux/airoha/patches-6.6/025-04-v6.13-phy-airoha-Fix-REG_CSR_2L_RX-0-1-_REV0-definitions.patch b/target/linux/airoha/patches-6.6/025-04-v6.13-phy-airoha-Fix-REG_CSR_2L_RX-0-1-_REV0-definitions.patch new file mode 100644 index 0000000000..163aebcbdb --- /dev/null +++ b/target/linux/airoha/patches-6.6/025-04-v6.13-phy-airoha-Fix-REG_CSR_2L_RX-0-1-_REV0-definitions.patch @@ -0,0 +1,32 @@ +From bc1bb265f504ea19ce611a1aec1a40dec409cd15 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 18 Sep 2024 15:32:55 +0200 +Subject: [PATCH 4/4] phy: airoha: Fix REG_CSR_2L_RX{0,1}_REV0 definitions + +Fix the following register definitions for REG_CSR_2L_RX{0,1}_REV0 +registers: +- CSR_2L_PXP_VOS_PNINV +- CSR_2L_PXP_FE_GAIN_NORMAL_MODE +- CSR_2L_PXP_FE_GAIN_TRAIN_MODE + +Fixes: d7d2818b9383 ("phy: airoha: Add PCIe PHY driver for EN7581 SoC.") +Signed-off-by: Lorenzo Bianconi +--- + drivers/phy/phy-airoha-pcie-regs.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/phy/phy-airoha-pcie-regs.h ++++ b/drivers/phy/phy-airoha-pcie-regs.h +@@ -197,9 +197,9 @@ + #define CSR_2L_PXP_TX1_MULTLANE_EN BIT(0) + + #define REG_CSR_2L_RX0_REV0 0x00fc +-#define CSR_2L_PXP_VOS_PNINV GENMASK(3, 2) +-#define CSR_2L_PXP_FE_GAIN_NORMAL_MODE GENMASK(6, 4) +-#define CSR_2L_PXP_FE_GAIN_TRAIN_MODE GENMASK(10, 8) ++#define CSR_2L_PXP_VOS_PNINV GENMASK(19, 18) ++#define CSR_2L_PXP_FE_GAIN_NORMAL_MODE GENMASK(22, 20) ++#define CSR_2L_PXP_FE_GAIN_TRAIN_MODE GENMASK(26, 24) + + #define REG_CSR_2L_RX0_PHYCK_DIV 0x0100 + #define CSR_2L_PXP_RX0_PHYCK_SEL GENMASK(9, 8) diff --git a/target/linux/airoha/patches-6.6/025-v6.10-spi-airoha-add-SPI-NAND-Flash-controller-driver.patch b/target/linux/airoha/patches-6.6/025-v6.10-spi-airoha-add-SPI-NAND-Flash-controller-driver.patch new file mode 100644 index 0000000000..417dcc06d8 --- /dev/null +++ b/target/linux/airoha/patches-6.6/025-v6.10-spi-airoha-add-SPI-NAND-Flash-controller-driver.patch @@ -0,0 +1,1203 @@ +From a403997c12019d0f82a9480207bf85985b8de5e7 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 29 Apr 2024 10:13:10 +0200 +Subject: [PATCH] spi: airoha: add SPI-NAND Flash controller driver + +Introduce support for SPI-NAND driver of the Airoha NAND Flash Interface +found on Airoha ARM SoCs. + +Tested-by: Rajeev Kumar +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Andy Shevchenko +Reviewed-by: AngeloGioacchino Del Regno +Link: https://lore.kernel.org/r/6c9db20505b01a66807995374f2af475a23ce5b2.1714377864.git.lorenzo@kernel.org +Signed-off-by: Mark Brown +--- + MAINTAINERS | 9 + + drivers/spi/Kconfig | 10 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-airoha-snfi.c | 1129 +++++++++++++++++++++++++++++++++ + 4 files changed, 1149 insertions(+) + create mode 100644 drivers/spi/spi-airoha-snfi.c + +# diff --git a/MAINTAINERS b/MAINTAINERS +# index 2b63ed114532..dde7dd956156 100644 +# --- a/MAINTAINERS +# +++ b/MAINTAINERS +# @@ -653,6 +653,15 @@ S: Supported +# F: fs/aio.c +# F: include/linux/*aio*.h + +# +AIROHA SPI SNFI DRIVER +# +M: Lorenzo Bianconi +# +M: Ray Liu +# +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +# +L: linux-spi@vger.kernel.org +# +S: Maintained +# +F: Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml +# +F: drivers/spi/spi-airoha.c +# + +# AIRSPY MEDIA DRIVER +# L: linux-media@vger.kernel.org +# S: Orphan +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -57,6 +57,16 @@ config SPI_MEM + + comment "SPI Master Controller Drivers" + ++config SPI_AIROHA_SNFI ++ tristate "Airoha SPI NAND Flash Interface" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ depends on SPI_MASTER ++ select REGMAP_MMIO ++ help ++ This enables support for SPI-NAND mode on the Airoha NAND ++ Flash Interface found on Airoha ARM SoCs. This controller ++ is implemented as a SPI-MEM controller. ++ + config SPI_ALTERA + tristate "Altera SPI Controller platform driver" + select SPI_ALTERA_CORE +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_SPIDEV) += spidev.o + obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o + + # SPI master controller drivers (bus) ++obj-$(CONFIG_SPI_AIROHA_SNFI) += spi-airoha-snfi.o + obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o + obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o + obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o +--- /dev/null ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -0,0 +1,1129 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ * Author: Ray Liu ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* SPI */ ++#define REG_SPI_CTRL_BASE 0x1FA10000 ++ ++#define REG_SPI_CTRL_READ_MODE 0x0000 ++#define REG_SPI_CTRL_READ_IDLE_EN 0x0004 ++#define REG_SPI_CTRL_SIDLY 0x0008 ++#define REG_SPI_CTRL_CSHEXT 0x000c ++#define REG_SPI_CTRL_CSLEXT 0x0010 ++ ++#define REG_SPI_CTRL_MTX_MODE_TOG 0x0014 ++#define SPI_CTRL_MTX_MODE_TOG GENMASK(3, 0) ++ ++#define REG_SPI_CTRL_RDCTL_FSM 0x0018 ++#define SPI_CTRL_RDCTL_FSM GENMASK(3, 0) ++ ++#define REG_SPI_CTRL_MACMUX_SEL 0x001c ++ ++#define REG_SPI_CTRL_MANUAL_EN 0x0020 ++#define SPI_CTRL_MANUAL_EN BIT(0) ++ ++#define REG_SPI_CTRL_OPFIFO_EMPTY 0x0024 ++#define SPI_CTRL_OPFIFO_EMPTY BIT(0) ++ ++#define REG_SPI_CTRL_OPFIFO_WDATA 0x0028 ++#define SPI_CTRL_OPFIFO_LEN GENMASK(8, 0) ++#define SPI_CTRL_OPFIFO_OP GENMASK(13, 9) ++ ++#define REG_SPI_CTRL_OPFIFO_FULL 0x002c ++#define SPI_CTRL_OPFIFO_FULL BIT(0) ++ ++#define REG_SPI_CTRL_OPFIFO_WR 0x0030 ++#define SPI_CTRL_OPFIFO_WR BIT(0) ++ ++#define REG_SPI_CTRL_DFIFO_FULL 0x0034 ++#define SPI_CTRL_DFIFO_FULL BIT(0) ++ ++#define REG_SPI_CTRL_DFIFO_WDATA 0x0038 ++#define SPI_CTRL_DFIFO_WDATA GENMASK(7, 0) ++ ++#define REG_SPI_CTRL_DFIFO_EMPTY 0x003c ++#define SPI_CTRL_DFIFO_EMPTY BIT(0) ++ ++#define REG_SPI_CTRL_DFIFO_RD 0x0040 ++#define SPI_CTRL_DFIFO_RD BIT(0) ++ ++#define REG_SPI_CTRL_DFIFO_RDATA 0x0044 ++#define SPI_CTRL_DFIFO_RDATA GENMASK(7, 0) ++ ++#define REG_SPI_CTRL_DUMMY 0x0080 ++#define SPI_CTRL_CTRL_DUMMY GENMASK(3, 0) ++ ++#define REG_SPI_CTRL_PROBE_SEL 0x0088 ++#define REG_SPI_CTRL_INTERRUPT 0x0090 ++#define REG_SPI_CTRL_INTERRUPT_EN 0x0094 ++#define REG_SPI_CTRL_SI_CK_SEL 0x009c ++#define REG_SPI_CTRL_SW_CFGNANDADDR_VAL 0x010c ++#define REG_SPI_CTRL_SW_CFGNANDADDR_EN 0x0110 ++#define REG_SPI_CTRL_SFC_STRAP 0x0114 ++ ++#define REG_SPI_CTRL_NFI2SPI_EN 0x0130 ++#define SPI_CTRL_NFI2SPI_EN BIT(0) ++ ++/* NFI2SPI */ ++#define REG_SPI_NFI_CNFG 0x0000 ++#define SPI_NFI_DMA_MODE BIT(0) ++#define SPI_NFI_READ_MODE BIT(1) ++#define SPI_NFI_DMA_BURST_EN BIT(2) ++#define SPI_NFI_HW_ECC_EN BIT(8) ++#define SPI_NFI_AUTO_FDM_EN BIT(9) ++#define SPI_NFI_OPMODE GENMASK(14, 12) ++ ++#define REG_SPI_NFI_PAGEFMT 0x0004 ++#define SPI_NFI_PAGE_SIZE GENMASK(1, 0) ++#define SPI_NFI_SPARE_SIZE GENMASK(5, 4) ++ ++#define REG_SPI_NFI_CON 0x0008 ++#define SPI_NFI_FIFO_FLUSH BIT(0) ++#define SPI_NFI_RST BIT(1) ++#define SPI_NFI_RD_TRIG BIT(8) ++#define SPI_NFI_WR_TRIG BIT(9) ++#define SPI_NFI_SEC_NUM GENMASK(15, 12) ++ ++#define REG_SPI_NFI_INTR_EN 0x0010 ++#define SPI_NFI_RD_DONE_EN BIT(0) ++#define SPI_NFI_WR_DONE_EN BIT(1) ++#define SPI_NFI_RST_DONE_EN BIT(2) ++#define SPI_NFI_ERASE_DONE_EN BIT(3) ++#define SPI_NFI_BUSY_RETURN_EN BIT(4) ++#define SPI_NFI_ACCESS_LOCK_EN BIT(5) ++#define SPI_NFI_AHB_DONE_EN BIT(6) ++#define SPI_NFI_ALL_IRQ_EN \ ++ (SPI_NFI_RD_DONE_EN | SPI_NFI_WR_DONE_EN | \ ++ SPI_NFI_RST_DONE_EN | SPI_NFI_ERASE_DONE_EN | \ ++ SPI_NFI_BUSY_RETURN_EN | SPI_NFI_ACCESS_LOCK_EN | \ ++ SPI_NFI_AHB_DONE_EN) ++ ++#define REG_SPI_NFI_INTR 0x0014 ++#define SPI_NFI_AHB_DONE BIT(6) ++ ++#define REG_SPI_NFI_CMD 0x0020 ++ ++#define REG_SPI_NFI_ADDR_NOB 0x0030 ++#define SPI_NFI_ROW_ADDR_NOB GENMASK(6, 4) ++ ++#define REG_SPI_NFI_STA 0x0060 ++#define REG_SPI_NFI_FIFOSTA 0x0064 ++#define REG_SPI_NFI_STRADDR 0x0080 ++#define REG_SPI_NFI_FDM0L 0x00a0 ++#define REG_SPI_NFI_FDM0M 0x00a4 ++#define REG_SPI_NFI_FDM7L 0x00d8 ++#define REG_SPI_NFI_FDM7M 0x00dc ++#define REG_SPI_NFI_FIFODATA0 0x0190 ++#define REG_SPI_NFI_FIFODATA1 0x0194 ++#define REG_SPI_NFI_FIFODATA2 0x0198 ++#define REG_SPI_NFI_FIFODATA3 0x019c ++#define REG_SPI_NFI_MASTERSTA 0x0224 ++ ++#define REG_SPI_NFI_SECCUS_SIZE 0x022c ++#define SPI_NFI_CUS_SEC_SIZE GENMASK(12, 0) ++#define SPI_NFI_CUS_SEC_SIZE_EN BIT(16) ++ ++#define REG_SPI_NFI_RD_CTL2 0x0510 ++#define REG_SPI_NFI_RD_CTL3 0x0514 ++ ++#define REG_SPI_NFI_PG_CTL1 0x0524 ++#define SPI_NFI_PG_LOAD_CMD GENMASK(15, 8) ++ ++#define REG_SPI_NFI_PG_CTL2 0x0528 ++#define REG_SPI_NFI_NOR_PROG_ADDR 0x052c ++#define REG_SPI_NFI_NOR_RD_ADDR 0x0534 ++ ++#define REG_SPI_NFI_SNF_MISC_CTL 0x0538 ++#define SPI_NFI_DATA_READ_WR_MODE GENMASK(18, 16) ++ ++#define REG_SPI_NFI_SNF_MISC_CTL2 0x053c ++#define SPI_NFI_READ_DATA_BYTE_NUM GENMASK(12, 0) ++#define SPI_NFI_PROG_LOAD_BYTE_NUM GENMASK(28, 16) ++ ++#define REG_SPI_NFI_SNF_STA_CTL1 0x0550 ++#define SPI_NFI_READ_FROM_CACHE_DONE BIT(25) ++#define SPI_NFI_LOAD_TO_CACHE_DONE BIT(26) ++ ++#define REG_SPI_NFI_SNF_STA_CTL2 0x0554 ++ ++#define REG_SPI_NFI_SNF_NFI_CNFG 0x055c ++#define SPI_NFI_SPI_MODE BIT(0) ++ ++/* SPI NAND Protocol OP */ ++#define SPI_NAND_OP_GET_FEATURE 0x0f ++#define SPI_NAND_OP_SET_FEATURE 0x1f ++#define SPI_NAND_OP_PAGE_READ 0x13 ++#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03 ++#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST 0x0b ++#define SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3b ++#define SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6b ++#define SPI_NAND_OP_WRITE_ENABLE 0x06 ++#define SPI_NAND_OP_WRITE_DISABLE 0x04 ++#define SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02 ++#define SPI_NAND_OP_PROGRAM_LOAD_QUAD 0x32 ++#define SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE 0x84 ++#define SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD 0x34 ++#define SPI_NAND_OP_PROGRAM_EXECUTE 0x10 ++#define SPI_NAND_OP_READ_ID 0x9f ++#define SPI_NAND_OP_BLOCK_ERASE 0xd8 ++#define SPI_NAND_OP_RESET 0xff ++#define SPI_NAND_OP_DIE_SELECT 0xc2 ++ ++#define SPI_NAND_CACHE_SIZE (SZ_4K + SZ_256) ++#define SPI_MAX_TRANSFER_SIZE 511 ++ ++enum airoha_snand_mode { ++ SPI_MODE_AUTO, ++ SPI_MODE_MANUAL, ++ SPI_MODE_DMA, ++}; ++ ++enum airoha_snand_cs { ++ SPI_CHIP_SEL_HIGH, ++ SPI_CHIP_SEL_LOW, ++}; ++ ++struct airoha_snand_dev { ++ size_t buf_len; ++ ++ u8 *txrx_buf; ++ dma_addr_t dma_addr; ++ ++ u64 cur_page_num; ++ bool data_need_update; ++}; ++ ++struct airoha_snand_ctrl { ++ struct device *dev; ++ struct regmap *regmap_ctrl; ++ struct regmap *regmap_nfi; ++ struct clk *spi_clk; ++ ++ struct { ++ size_t page_size; ++ size_t sec_size; ++ u8 sec_num; ++ u8 spare_size; ++ } nfi_cfg; ++}; ++ ++static int airoha_snand_set_fifo_op(struct airoha_snand_ctrl *as_ctrl, ++ u8 op_cmd, int op_len) ++{ ++ int err; ++ u32 val; ++ ++ err = regmap_write(as_ctrl->regmap_ctrl, REG_SPI_CTRL_OPFIFO_WDATA, ++ FIELD_PREP(SPI_CTRL_OPFIFO_LEN, op_len) | ++ FIELD_PREP(SPI_CTRL_OPFIFO_OP, op_cmd)); ++ if (err) ++ return err; ++ ++ err = regmap_read_poll_timeout(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_OPFIFO_FULL, ++ val, !(val & SPI_CTRL_OPFIFO_FULL), ++ 0, 250 * USEC_PER_MSEC); ++ if (err) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_ctrl, REG_SPI_CTRL_OPFIFO_WR, ++ SPI_CTRL_OPFIFO_WR); ++ if (err) ++ return err; ++ ++ return regmap_read_poll_timeout(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_OPFIFO_EMPTY, ++ val, (val & SPI_CTRL_OPFIFO_EMPTY), ++ 0, 250 * USEC_PER_MSEC); ++} ++ ++static int airoha_snand_set_cs(struct airoha_snand_ctrl *as_ctrl, u8 cs) ++{ ++ return airoha_snand_set_fifo_op(as_ctrl, cs, sizeof(cs)); ++} ++ ++static int airoha_snand_write_data_to_fifo(struct airoha_snand_ctrl *as_ctrl, ++ const u8 *data, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) { ++ int err; ++ u32 val; ++ ++ /* 1. Wait until dfifo is not full */ ++ err = regmap_read_poll_timeout(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_DFIFO_FULL, val, ++ !(val & SPI_CTRL_DFIFO_FULL), ++ 0, 250 * USEC_PER_MSEC); ++ if (err) ++ return err; ++ ++ /* 2. Write data to register DFIFO_WDATA */ ++ err = regmap_write(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_DFIFO_WDATA, ++ FIELD_PREP(SPI_CTRL_DFIFO_WDATA, data[i])); ++ if (err) ++ return err; ++ ++ /* 3. Wait until dfifo is not full */ ++ err = regmap_read_poll_timeout(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_DFIFO_FULL, val, ++ !(val & SPI_CTRL_DFIFO_FULL), ++ 0, 250 * USEC_PER_MSEC); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int airoha_snand_read_data_from_fifo(struct airoha_snand_ctrl *as_ctrl, ++ u8 *ptr, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) { ++ int err; ++ u32 val; ++ ++ /* 1. wait until dfifo is not empty */ ++ err = regmap_read_poll_timeout(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_DFIFO_EMPTY, val, ++ !(val & SPI_CTRL_DFIFO_EMPTY), ++ 0, 250 * USEC_PER_MSEC); ++ if (err) ++ return err; ++ ++ /* 2. read from dfifo to register DFIFO_RDATA */ ++ err = regmap_read(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_DFIFO_RDATA, &val); ++ if (err) ++ return err; ++ ++ ptr[i] = FIELD_GET(SPI_CTRL_DFIFO_RDATA, val); ++ /* 3. enable register DFIFO_RD to read next byte */ ++ err = regmap_write(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_DFIFO_RD, SPI_CTRL_DFIFO_RD); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int airoha_snand_set_mode(struct airoha_snand_ctrl *as_ctrl, ++ enum airoha_snand_mode mode) ++{ ++ int err; ++ ++ switch (mode) { ++ case SPI_MODE_MANUAL: { ++ u32 val; ++ ++ err = regmap_write(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_NFI2SPI_EN, 0); ++ if (err) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_READ_IDLE_EN, 0); ++ if (err) ++ return err; ++ ++ err = regmap_read_poll_timeout(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_RDCTL_FSM, val, ++ !(val & SPI_CTRL_RDCTL_FSM), ++ 0, 250 * USEC_PER_MSEC); ++ if (err) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_MTX_MODE_TOG, 9); ++ if (err) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_MANUAL_EN, SPI_CTRL_MANUAL_EN); ++ if (err) ++ return err; ++ break; ++ } ++ case SPI_MODE_DMA: ++ err = regmap_write(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_NFI2SPI_EN, ++ SPI_CTRL_MANUAL_EN); ++ if (err < 0) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_MTX_MODE_TOG, 0x0); ++ if (err < 0) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_ctrl, ++ REG_SPI_CTRL_MANUAL_EN, 0x0); ++ if (err < 0) ++ return err; ++ break; ++ case SPI_MODE_AUTO: ++ default: ++ break; ++ } ++ ++ return regmap_write(as_ctrl->regmap_ctrl, REG_SPI_CTRL_DUMMY, 0); ++} ++ ++static int airoha_snand_write_data(struct airoha_snand_ctrl *as_ctrl, u8 cmd, ++ const u8 *data, int len) ++{ ++ int i, data_len; ++ ++ for (i = 0; i < len; i += data_len) { ++ int err; ++ ++ data_len = min(len, SPI_MAX_TRANSFER_SIZE); ++ err = airoha_snand_set_fifo_op(as_ctrl, cmd, data_len); ++ if (err) ++ return err; ++ ++ err = airoha_snand_write_data_to_fifo(as_ctrl, &data[i], ++ data_len); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int airoha_snand_read_data(struct airoha_snand_ctrl *as_ctrl, u8 *data, ++ int len) ++{ ++ int i, data_len; ++ ++ for (i = 0; i < len; i += data_len) { ++ int err; ++ ++ data_len = min(len, SPI_MAX_TRANSFER_SIZE); ++ err = airoha_snand_set_fifo_op(as_ctrl, 0xc, data_len); ++ if (err) ++ return err; ++ ++ err = airoha_snand_read_data_from_fifo(as_ctrl, &data[i], ++ data_len); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int airoha_snand_nfi_init(struct airoha_snand_ctrl *as_ctrl) ++{ ++ int err; ++ ++ /* switch to SNFI mode */ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_NFI_CNFG, ++ SPI_NFI_SPI_MODE); ++ if (err) ++ return err; ++ ++ /* Enable DMA */ ++ return regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_INTR_EN, ++ SPI_NFI_ALL_IRQ_EN, SPI_NFI_AHB_DONE_EN); ++} ++ ++static int airoha_snand_nfi_config(struct airoha_snand_ctrl *as_ctrl) ++{ ++ int err; ++ u32 val; ++ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, ++ SPI_NFI_FIFO_FLUSH | SPI_NFI_RST); ++ if (err) ++ return err; ++ ++ /* auto FDM */ ++ err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, ++ SPI_NFI_AUTO_FDM_EN); ++ if (err) ++ return err; ++ ++ /* HW ECC */ ++ err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, ++ SPI_NFI_HW_ECC_EN); ++ if (err) ++ return err; ++ ++ /* DMA Burst */ ++ err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, ++ SPI_NFI_DMA_BURST_EN); ++ if (err) ++ return err; ++ ++ /* page format */ ++ switch (as_ctrl->nfi_cfg.spare_size) { ++ case 26: ++ val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x1); ++ break; ++ case 27: ++ val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x2); ++ break; ++ case 28: ++ val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x3); ++ break; ++ default: ++ val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x0); ++ break; ++ } ++ ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT, ++ SPI_NFI_SPARE_SIZE, val); ++ if (err) ++ return err; ++ ++ switch (as_ctrl->nfi_cfg.page_size) { ++ case 2048: ++ val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x1); ++ break; ++ case 4096: ++ val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x2); ++ break; ++ default: ++ val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x0); ++ break; ++ } ++ ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT, ++ SPI_NFI_PAGE_SIZE, val); ++ if (err) ++ return err; ++ ++ /* sec num */ ++ val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num); ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, ++ SPI_NFI_SEC_NUM, val); ++ if (err) ++ return err; ++ ++ /* enable cust sec size */ ++ err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, ++ SPI_NFI_CUS_SEC_SIZE_EN); ++ if (err) ++ return err; ++ ++ /* set cust sec size */ ++ val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, as_ctrl->nfi_cfg.sec_size); ++ return regmap_update_bits(as_ctrl->regmap_nfi, ++ REG_SPI_NFI_SECCUS_SIZE, ++ SPI_NFI_CUS_SEC_SIZE, val); ++} ++ ++static bool airoha_snand_is_page_ops(const struct spi_mem_op *op) ++{ ++ if (op->addr.nbytes != 2) ++ return false; ++ ++ if (op->addr.buswidth != 1 && op->addr.buswidth != 2 && ++ op->addr.buswidth != 4) ++ return false; ++ ++ switch (op->data.dir) { ++ case SPI_MEM_DATA_IN: ++ if (op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth > 0xf) ++ return false; ++ ++ /* quad in / quad out */ ++ if (op->addr.buswidth == 4) ++ return op->data.buswidth == 4; ++ ++ if (op->addr.buswidth == 2) ++ return op->data.buswidth == 2; ++ ++ /* standard spi */ ++ return op->data.buswidth == 4 || op->data.buswidth == 2 || ++ op->data.buswidth == 1; ++ case SPI_MEM_DATA_OUT: ++ return !op->dummy.nbytes && op->addr.buswidth == 1 && ++ (op->data.buswidth == 4 || op->data.buswidth == 1); ++ default: ++ return false; ++ } ++} ++ ++static int airoha_snand_adjust_op_size(struct spi_mem *mem, ++ struct spi_mem_op *op) ++{ ++ size_t max_len; ++ ++ if (airoha_snand_is_page_ops(op)) { ++ struct airoha_snand_ctrl *as_ctrl; ++ ++ as_ctrl = spi_controller_get_devdata(mem->spi->controller); ++ max_len = as_ctrl->nfi_cfg.sec_size; ++ max_len += as_ctrl->nfi_cfg.spare_size; ++ max_len *= as_ctrl->nfi_cfg.sec_num; ++ ++ if (op->data.nbytes > max_len) ++ op->data.nbytes = max_len; ++ } else { ++ max_len = 1 + op->addr.nbytes + op->dummy.nbytes; ++ if (max_len >= 160) ++ return -EOPNOTSUPP; ++ ++ if (op->data.nbytes > 160 - max_len) ++ op->data.nbytes = 160 - max_len; ++ } ++ ++ return 0; ++} ++ ++static bool airoha_snand_supports_op(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ if (!spi_mem_default_supports_op(mem, op)) ++ return false; ++ ++ if (op->cmd.buswidth != 1) ++ return false; ++ ++ if (airoha_snand_is_page_ops(op)) ++ return true; ++ ++ return (!op->addr.nbytes || op->addr.buswidth == 1) && ++ (!op->dummy.nbytes || op->dummy.buswidth == 1) && ++ (!op->data.nbytes || op->data.buswidth == 1); ++} ++ ++static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc) ++{ ++ struct airoha_snand_dev *as_dev = spi_get_ctldata(desc->mem->spi); ++ ++ if (!as_dev->txrx_buf) ++ return -EINVAL; ++ ++ if (desc->info.offset + desc->info.length > U32_MAX) ++ return -EINVAL; ++ ++ if (!airoha_snand_supports_op(desc->mem, &desc->info.op_tmpl)) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++ ++static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, ++ u64 offs, size_t len, void *buf) ++{ ++ struct spi_device *spi = desc->mem->spi; ++ struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); ++ struct spi_mem_op *op = &desc->info.op_tmpl; ++ struct airoha_snand_ctrl *as_ctrl; ++ u32 val, rd_mode; ++ int err; ++ ++ if (!as_dev->data_need_update) ++ return len; ++ ++ as_dev->data_need_update = false; ++ ++ switch (op->cmd.opcode) { ++ case SPI_NAND_OP_READ_FROM_CACHE_DUAL: ++ rd_mode = 1; ++ break; ++ case SPI_NAND_OP_READ_FROM_CACHE_QUAD: ++ rd_mode = 2; ++ break; ++ default: ++ rd_mode = 0; ++ break; ++ } ++ ++ as_ctrl = spi_controller_get_devdata(spi->controller); ++ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); ++ if (err < 0) ++ return err; ++ ++ err = airoha_snand_nfi_config(as_ctrl); ++ if (err) ++ return err; ++ ++ dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr, ++ as_dev->buf_len, DMA_BIDIRECTIONAL); ++ ++ /* set dma addr */ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, ++ as_dev->dma_addr); ++ if (err) ++ return err; ++ ++ /* set cust sec size */ ++ val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; ++ val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val); ++ err = regmap_update_bits(as_ctrl->regmap_nfi, ++ REG_SPI_NFI_SNF_MISC_CTL2, ++ SPI_NFI_READ_DATA_BYTE_NUM, val); ++ if (err) ++ return err; ++ ++ /* set read command */ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2, ++ op->cmd.opcode); ++ if (err) ++ return err; ++ ++ /* set read mode */ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL, ++ FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, rd_mode)); ++ if (err) ++ return err; ++ ++ /* set read addr */ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL3, 0x0); ++ if (err) ++ return err; ++ ++ /* set nfi read */ ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, ++ SPI_NFI_OPMODE, ++ FIELD_PREP(SPI_NFI_OPMODE, 6)); ++ if (err) ++ return err; ++ ++ err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, ++ SPI_NFI_READ_MODE | SPI_NFI_DMA_MODE); ++ if (err) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x0); ++ if (err) ++ return err; ++ ++ /* trigger dma start read */ ++ err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, ++ SPI_NFI_RD_TRIG); ++ if (err) ++ return err; ++ ++ err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, ++ SPI_NFI_RD_TRIG); ++ if (err) ++ return err; ++ ++ err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, ++ REG_SPI_NFI_SNF_STA_CTL1, val, ++ (val & SPI_NFI_READ_FROM_CACHE_DONE), ++ 0, 1 * USEC_PER_SEC); ++ if (err) ++ return err; ++ ++ err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, ++ SPI_NFI_READ_FROM_CACHE_DONE); ++ if (err) ++ return err; ++ ++ err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, REG_SPI_NFI_INTR, ++ val, (val & SPI_NFI_AHB_DONE), 0, ++ 1 * USEC_PER_SEC); ++ if (err) ++ return err; ++ ++ /* DMA read need delay for data ready from controller to DRAM */ ++ udelay(1); ++ ++ dma_sync_single_for_cpu(as_ctrl->dev, as_dev->dma_addr, ++ as_dev->buf_len, DMA_BIDIRECTIONAL); ++ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); ++ if (err < 0) ++ return err; ++ ++ memcpy(buf, as_dev->txrx_buf + offs, len); ++ ++ return len; ++} ++ ++static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, ++ u64 offs, size_t len, const void *buf) ++{ ++ struct spi_device *spi = desc->mem->spi; ++ struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); ++ struct spi_mem_op *op = &desc->info.op_tmpl; ++ struct airoha_snand_ctrl *as_ctrl; ++ u32 wr_mode, val; ++ int err; ++ ++ as_ctrl = spi_controller_get_devdata(spi->controller); ++ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); ++ if (err < 0) ++ return err; ++ ++ dma_sync_single_for_cpu(as_ctrl->dev, as_dev->dma_addr, ++ as_dev->buf_len, DMA_BIDIRECTIONAL); ++ memcpy(as_dev->txrx_buf + offs, buf, len); ++ dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr, ++ as_dev->buf_len, DMA_BIDIRECTIONAL); ++ ++ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); ++ if (err < 0) ++ return err; ++ ++ err = airoha_snand_nfi_config(as_ctrl); ++ if (err) ++ return err; ++ ++ if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD || ++ op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD) ++ wr_mode = BIT(1); ++ else ++ wr_mode = 0; ++ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, ++ as_dev->dma_addr); ++ if (err) ++ return err; ++ ++ val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, ++ as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num); ++ err = regmap_update_bits(as_ctrl->regmap_nfi, ++ REG_SPI_NFI_SNF_MISC_CTL2, ++ SPI_NFI_PROG_LOAD_BYTE_NUM, val); ++ if (err) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1, ++ FIELD_PREP(SPI_NFI_PG_LOAD_CMD, ++ op->cmd.opcode)); ++ if (err) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL, ++ FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode)); ++ if (err) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL2, 0x0); ++ if (err) ++ return err; ++ ++ err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, ++ SPI_NFI_READ_MODE); ++ if (err) ++ return err; ++ ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, ++ SPI_NFI_OPMODE, ++ FIELD_PREP(SPI_NFI_OPMODE, 3)); ++ if (err) ++ return err; ++ ++ err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, ++ SPI_NFI_DMA_MODE); ++ if (err) ++ return err; ++ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x80); ++ if (err) ++ return err; ++ ++ err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, ++ SPI_NFI_WR_TRIG); ++ if (err) ++ return err; ++ ++ err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, ++ SPI_NFI_WR_TRIG); ++ if (err) ++ return err; ++ ++ err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, REG_SPI_NFI_INTR, ++ val, (val & SPI_NFI_AHB_DONE), 0, ++ 1 * USEC_PER_SEC); ++ if (err) ++ return err; ++ ++ err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, ++ REG_SPI_NFI_SNF_STA_CTL1, val, ++ (val & SPI_NFI_LOAD_TO_CACHE_DONE), ++ 0, 1 * USEC_PER_SEC); ++ if (err) ++ return err; ++ ++ err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, ++ SPI_NFI_LOAD_TO_CACHE_DONE); ++ if (err) ++ return err; ++ ++ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); ++ if (err < 0) ++ return err; ++ ++ return len; ++} ++ ++static int airoha_snand_exec_op(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ struct airoha_snand_dev *as_dev = spi_get_ctldata(mem->spi); ++ u8 data[8], cmd, opcode = op->cmd.opcode; ++ struct airoha_snand_ctrl *as_ctrl; ++ int i, err; ++ ++ as_ctrl = spi_controller_get_devdata(mem->spi->controller); ++ if (opcode == SPI_NAND_OP_PROGRAM_EXECUTE && ++ op->addr.val == as_dev->cur_page_num) { ++ as_dev->data_need_update = true; ++ } else if (opcode == SPI_NAND_OP_PAGE_READ) { ++ if (!as_dev->data_need_update && ++ op->addr.val == as_dev->cur_page_num) ++ return 0; ++ ++ as_dev->data_need_update = true; ++ as_dev->cur_page_num = op->addr.val; ++ } ++ ++ /* switch to manual mode */ ++ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); ++ if (err < 0) ++ return err; ++ ++ err = airoha_snand_set_cs(as_ctrl, SPI_CHIP_SEL_LOW); ++ if (err < 0) ++ return err; ++ ++ /* opcode */ ++ err = airoha_snand_write_data(as_ctrl, 0x8, &opcode, sizeof(opcode)); ++ if (err) ++ return err; ++ ++ /* addr part */ ++ cmd = opcode == SPI_NAND_OP_GET_FEATURE ? 0x11 : 0x8; ++ put_unaligned_be64(op->addr.val, data); ++ ++ for (i = ARRAY_SIZE(data) - op->addr.nbytes; ++ i < ARRAY_SIZE(data); i++) { ++ err = airoha_snand_write_data(as_ctrl, cmd, &data[i], ++ sizeof(data[0])); ++ if (err) ++ return err; ++ } ++ ++ /* dummy */ ++ data[0] = 0xff; ++ for (i = 0; i < op->dummy.nbytes; i++) { ++ err = airoha_snand_write_data(as_ctrl, 0x8, &data[0], ++ sizeof(data[0])); ++ if (err) ++ return err; ++ } ++ ++ /* data */ ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ err = airoha_snand_read_data(as_ctrl, op->data.buf.in, ++ op->data.nbytes); ++ if (err) ++ return err; ++ } else { ++ err = airoha_snand_write_data(as_ctrl, 0x8, op->data.buf.out, ++ op->data.nbytes); ++ if (err) ++ return err; ++ } ++ ++ return airoha_snand_set_cs(as_ctrl, SPI_CHIP_SEL_HIGH); ++} ++ ++static const struct spi_controller_mem_ops airoha_snand_mem_ops = { ++ .adjust_op_size = airoha_snand_adjust_op_size, ++ .supports_op = airoha_snand_supports_op, ++ .exec_op = airoha_snand_exec_op, ++ .dirmap_create = airoha_snand_dirmap_create, ++ .dirmap_read = airoha_snand_dirmap_read, ++ .dirmap_write = airoha_snand_dirmap_write, ++}; ++ ++static int airoha_snand_setup(struct spi_device *spi) ++{ ++ struct airoha_snand_ctrl *as_ctrl; ++ struct airoha_snand_dev *as_dev; ++ ++ as_ctrl = spi_controller_get_devdata(spi->controller); ++ ++ as_dev = devm_kzalloc(as_ctrl->dev, sizeof(*as_dev), GFP_KERNEL); ++ if (!as_dev) ++ return -ENOMEM; ++ ++ /* prepare device buffer */ ++ as_dev->buf_len = SPI_NAND_CACHE_SIZE; ++ as_dev->txrx_buf = devm_kzalloc(as_ctrl->dev, as_dev->buf_len, ++ GFP_KERNEL); ++ if (!as_dev->txrx_buf) ++ return -ENOMEM; ++ ++ as_dev->dma_addr = dma_map_single(as_ctrl->dev, as_dev->txrx_buf, ++ as_dev->buf_len, DMA_BIDIRECTIONAL); ++ if (dma_mapping_error(as_ctrl->dev, as_dev->dma_addr)) ++ return -ENOMEM; ++ ++ as_dev->data_need_update = true; ++ spi_set_ctldata(spi, as_dev); ++ ++ return 0; ++} ++ ++static void airoha_snand_cleanup(struct spi_device *spi) ++{ ++ struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); ++ struct airoha_snand_ctrl *as_ctrl; ++ ++ as_ctrl = spi_controller_get_devdata(spi->controller); ++ dma_unmap_single(as_ctrl->dev, as_dev->dma_addr, ++ as_dev->buf_len, DMA_BIDIRECTIONAL); ++ spi_set_ctldata(spi, NULL); ++} ++ ++static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl) ++{ ++ u32 val, sec_size, sec_num; ++ int err; ++ ++ err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, &val); ++ if (err) ++ return err; ++ ++ sec_num = FIELD_GET(SPI_NFI_SEC_NUM, val); ++ ++ err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, &val); ++ if (err) ++ return err; ++ ++ sec_size = FIELD_GET(SPI_NFI_CUS_SEC_SIZE, val); ++ ++ /* init default value */ ++ as_ctrl->nfi_cfg.sec_size = sec_size; ++ as_ctrl->nfi_cfg.sec_num = sec_num; ++ as_ctrl->nfi_cfg.page_size = round_down(sec_size * sec_num, 1024); ++ as_ctrl->nfi_cfg.spare_size = 16; ++ ++ err = airoha_snand_nfi_init(as_ctrl); ++ if (err) ++ return err; ++ ++ return airoha_snand_nfi_config(as_ctrl); ++} ++ ++static const struct regmap_config spi_ctrl_regmap_config = { ++ .name = "ctrl", ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = REG_SPI_CTRL_NFI2SPI_EN, ++}; ++ ++static const struct regmap_config spi_nfi_regmap_config = { ++ .name = "nfi", ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = REG_SPI_NFI_SNF_NFI_CNFG, ++}; ++ ++static const struct of_device_id airoha_snand_ids[] = { ++ { .compatible = "airoha,en7581-snand" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, airoha_snand_ids); ++ ++static int airoha_snand_probe(struct platform_device *pdev) ++{ ++ struct airoha_snand_ctrl *as_ctrl; ++ struct device *dev = &pdev->dev; ++ struct spi_controller *ctrl; ++ void __iomem *base; ++ int err; ++ ++ ctrl = devm_spi_alloc_host(dev, sizeof(*as_ctrl)); ++ if (!ctrl) ++ return -ENOMEM; ++ ++ as_ctrl = spi_controller_get_devdata(ctrl); ++ as_ctrl->dev = dev; ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ as_ctrl->regmap_ctrl = devm_regmap_init_mmio(dev, base, ++ &spi_ctrl_regmap_config); ++ if (IS_ERR(as_ctrl->regmap_ctrl)) ++ return dev_err_probe(dev, PTR_ERR(as_ctrl->regmap_ctrl), ++ "failed to init spi ctrl regmap\n"); ++ ++ base = devm_platform_ioremap_resource(pdev, 1); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ as_ctrl->regmap_nfi = devm_regmap_init_mmio(dev, base, ++ &spi_nfi_regmap_config); ++ if (IS_ERR(as_ctrl->regmap_nfi)) ++ return dev_err_probe(dev, PTR_ERR(as_ctrl->regmap_nfi), ++ "failed to init spi nfi regmap\n"); ++ ++ as_ctrl->spi_clk = devm_clk_get_enabled(dev, "spi"); ++ if (IS_ERR(as_ctrl->spi_clk)) ++ return dev_err_probe(dev, PTR_ERR(as_ctrl->spi_clk), ++ "unable to get spi clk\n"); ++ ++ err = dma_set_mask(as_ctrl->dev, DMA_BIT_MASK(32)); ++ if (err) ++ return err; ++ ++ ctrl->num_chipselect = 2; ++ ctrl->mem_ops = &airoha_snand_mem_ops; ++ ctrl->bits_per_word_mask = SPI_BPW_MASK(8); ++ ctrl->mode_bits = SPI_RX_DUAL; ++ ctrl->setup = airoha_snand_setup; ++ ctrl->cleanup = airoha_snand_cleanup; ++ device_set_node(&ctrl->dev, dev_fwnode(dev)); ++ ++ err = airoha_snand_nfi_setup(as_ctrl); ++ if (err) ++ return err; ++ ++ return devm_spi_register_controller(dev, ctrl); ++} ++ ++static struct platform_driver airoha_snand_driver = { ++ .driver = { ++ .name = "airoha-spi", ++ .of_match_table = airoha_snand_ids, ++ }, ++ .probe = airoha_snand_probe, ++}; ++module_platform_driver(airoha_snand_driver); ++ ++MODULE_DESCRIPTION("Airoha SPI-NAND Flash Controller Driver"); ++MODULE_AUTHOR("Lorenzo Bianconi "); ++MODULE_AUTHOR("Ray Liu "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.6/026-01-v6.12-spi-airoha-fix-dirmap_-read-write-operations.patch b/target/linux/airoha/patches-6.6/026-01-v6.12-spi-airoha-fix-dirmap_-read-write-operations.patch new file mode 100644 index 0000000000..dce013acd3 --- /dev/null +++ b/target/linux/airoha/patches-6.6/026-01-v6.12-spi-airoha-fix-dirmap_-read-write-operations.patch @@ -0,0 +1,55 @@ +From 2e6bbfe7b0c0607001b784082c2685b134174fac Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 13 Sep 2024 23:07:13 +0200 +Subject: [PATCH 1/2] spi: airoha: fix dirmap_{read,write} operations + +SPI_NFI_READ_FROM_CACHE_DONE bit must be written at the end of +dirmap_read operation even if it is already set. +In the same way, SPI_NFI_LOAD_TO_CACHE_DONE bit must be written at the +end of dirmap_write operation even if it is already set. +For this reason use regmap_write_bits() instead of regmap_set_bits(). +This patch fixes mtd_pagetest kernel module test. + +Fixes: a403997c1201 ("spi: airoha: add SPI-NAND Flash controller driver") +Tested-by: Christian Marangi +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20240913-airoha-spi-fixes-v1-1-de2e74ed4664@kernel.org +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -739,8 +739,13 @@ static ssize_t airoha_snand_dirmap_read( + if (err) + return err; + +- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, +- SPI_NFI_READ_FROM_CACHE_DONE); ++ /* ++ * SPI_NFI_READ_FROM_CACHE_DONE bit must be written at the end ++ * of dirmap_read operation even if it is already set. ++ */ ++ err = regmap_write_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, ++ SPI_NFI_READ_FROM_CACHE_DONE, ++ SPI_NFI_READ_FROM_CACHE_DONE); + if (err) + return err; + +@@ -870,8 +875,13 @@ static ssize_t airoha_snand_dirmap_write + if (err) + return err; + +- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, +- SPI_NFI_LOAD_TO_CACHE_DONE); ++ /* ++ * SPI_NFI_LOAD_TO_CACHE_DONE bit must be written at the end ++ * of dirmap_write operation even if it is already set. ++ */ ++ err = regmap_write_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, ++ SPI_NFI_LOAD_TO_CACHE_DONE, ++ SPI_NFI_LOAD_TO_CACHE_DONE); + if (err) + return err; + diff --git a/target/linux/airoha/patches-6.6/026-02-v6.12-spi-airoha-fix-airoha_snand_-write-read-_data-data_l.patch b/target/linux/airoha/patches-6.6/026-02-v6.12-spi-airoha-fix-airoha_snand_-write-read-_data-data_l.patch new file mode 100644 index 0000000000..738cb0c9cb --- /dev/null +++ b/target/linux/airoha/patches-6.6/026-02-v6.12-spi-airoha-fix-airoha_snand_-write-read-_data-data_l.patch @@ -0,0 +1,39 @@ +From 0e58637eb968c636725dcd6c7055249b4e5326fb Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 13 Sep 2024 23:07:14 +0200 +Subject: [PATCH 2/2] spi: airoha: fix airoha_snand_{write,read}_data data_len + estimation + +Fix data length written and read in airoha_snand_write_data and +airoha_snand_read_data routines respectively if it is bigger than +SPI_MAX_TRANSFER_SIZE. + +Fixes: a403997c1201 ("spi: airoha: add SPI-NAND Flash controller driver") +Tested-by: Christian Marangi +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20240913-airoha-spi-fixes-v1-2-de2e74ed4664@kernel.org +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -405,7 +405,7 @@ static int airoha_snand_write_data(struc + for (i = 0; i < len; i += data_len) { + int err; + +- data_len = min(len, SPI_MAX_TRANSFER_SIZE); ++ data_len = min(len - i, SPI_MAX_TRANSFER_SIZE); + err = airoha_snand_set_fifo_op(as_ctrl, cmd, data_len); + if (err) + return err; +@@ -427,7 +427,7 @@ static int airoha_snand_read_data(struct + for (i = 0; i < len; i += data_len) { + int err; + +- data_len = min(len, SPI_MAX_TRANSFER_SIZE); ++ data_len = min(len - i, SPI_MAX_TRANSFER_SIZE); + err = airoha_snand_set_fifo_op(as_ctrl, 0xc, data_len); + if (err) + return err; diff --git a/target/linux/airoha/patches-6.6/027-v6.12-spi-airoha-remove-read-cache-in-airoha_snand_dirmap_.patch b/target/linux/airoha/patches-6.6/027-v6.12-spi-airoha-remove-read-cache-in-airoha_snand_dirmap_.patch new file mode 100644 index 0000000000..d2d2b54d30 --- /dev/null +++ b/target/linux/airoha/patches-6.6/027-v6.12-spi-airoha-remove-read-cache-in-airoha_snand_dirmap_.patch @@ -0,0 +1,116 @@ +From fffca269e4f31c3633c6d810833ba1b184407915 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 19 Sep 2024 18:57:16 +0200 +Subject: [PATCH] spi: airoha: remove read cache in airoha_snand_dirmap_read() + +Current upstream driver reports errors running mtd_oobtest kernel module +test: + +root@OpenWrt:/# insmod mtd_test.ko +root@OpenWrt:/# insmod mtd_oobtest.ko dev=5 +[ 7023.730584] ================================================= +[ 7023.736399] mtd_oobtest: MTD device: 5 +[ 7023.740160] mtd_oobtest: MTD device size 3670016, eraseblock size 131072, page size 2048, count of eraseblocks 28, pages per eraseblock 64, OOB size 128 +[ 7023.753837] mtd_test: scanning for bad eraseblocks +[ 7023.758636] mtd_test: scanned 28 eraseblocks, 0 are bad +[ 7023.763861] mtd_oobtest: test 1 of 5 +[ 7024.042076] mtd_oobtest: writing OOBs of whole device +[ 7024.682069] mtd_oobtest: written up to eraseblock 0 +[ 7041.962077] mtd_oobtest: written 28 eraseblocks +[ 7041.966626] mtd_oobtest: verifying all eraseblocks +[ 7041.972276] mtd_oobtest: error @addr[0x0:0x0] 0xff -> 0xe diff 0xf1 +[ 7041.978550] mtd_oobtest: error @addr[0x0:0x1] 0xff -> 0x10 diff 0xef +[ 7041.984932] mtd_oobtest: error @addr[0x0:0x2] 0xff -> 0x82 diff 0x7d +[ 7041.991293] mtd_oobtest: error @addr[0x0:0x3] 0xff -> 0x10 diff 0xef +[ 7041.997659] mtd_oobtest: error @addr[0x0:0x4] 0xff -> 0x0 diff 0xff +[ 7042.003942] mtd_oobtest: error @addr[0x0:0x5] 0xff -> 0x8a diff 0x75 +[ 7042.010294] mtd_oobtest: error @addr[0x0:0x6] 0xff -> 0x20 diff 0xdf +[ 7042.016659] mtd_oobtest: error @addr[0x0:0x7] 0xff -> 0x1 diff 0xfe +[ 7042.022935] mtd_oobtest: error @addr[0x0:0x8] 0xff -> 0x2e diff 0xd1 +[ 7042.029295] mtd_oobtest: error @addr[0x0:0x9] 0xff -> 0x40 diff 0xbf +[ 7042.035661] mtd_oobtest: error @addr[0x0:0xa] 0xff -> 0x0 diff 0xff +[ 7042.041935] mtd_oobtest: error @addr[0x0:0xb] 0xff -> 0x89 diff 0x76 +[ 7042.048300] mtd_oobtest: error @addr[0x0:0xc] 0xff -> 0x82 diff 0x7d +[ 7042.054662] mtd_oobtest: error @addr[0x0:0xd] 0xff -> 0x15 diff 0xea +[ 7042.061014] mtd_oobtest: error @addr[0x0:0xe] 0xff -> 0x90 diff 0x6f +[ 7042.067380] mtd_oobtest: error @addr[0x0:0xf] 0xff -> 0x0 diff 0xff +.... +[ 7432.421369] mtd_oobtest: error @addr[0x237800:0x36] 0xff -> 0x5f diff 0xa0 +[ 7432.428242] mtd_oobtest: error @addr[0x237800:0x37] 0xff -> 0x21 diff 0xde +[ 7432.435118] mtd_oobtest: error: verify failed at 0x237800 +[ 7432.440510] mtd_oobtest: error: too many errors +[ 7432.445053] mtd_oobtest: error -1 occurred + +The above errors are due to the buggy logic in the 'read cache' available +in airoha_snand_dirmap_read() routine since there are some corner cases +where we are missing data updates. Since we do not get any read/write speed +improvement using the cache (according to the mtd_speedtest kernel +module test), in order to fix the mtd_oobtest test, remove the 'read cache' +in airoha_snand_dirmap_read routine. Now the driver is passing all the +tests available in mtd_test suite. + +Fixes: a403997c1201 ("spi: airoha: add SPI-NAND Flash controller driver") +Tested-by: Christian Marangi +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20240919-airoha-spi-fixes-v2-1-cb0f0ed9920a@kernel.org +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 21 --------------------- + 1 file changed, 21 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -211,9 +211,6 @@ struct airoha_snand_dev { + + u8 *txrx_buf; + dma_addr_t dma_addr; +- +- u64 cur_page_num; +- bool data_need_update; + }; + + struct airoha_snand_ctrl { +@@ -644,11 +641,6 @@ static ssize_t airoha_snand_dirmap_read( + u32 val, rd_mode; + int err; + +- if (!as_dev->data_need_update) +- return len; +- +- as_dev->data_need_update = false; +- + switch (op->cmd.opcode) { + case SPI_NAND_OP_READ_FROM_CACHE_DUAL: + rd_mode = 1; +@@ -895,23 +887,11 @@ static ssize_t airoha_snand_dirmap_write + static int airoha_snand_exec_op(struct spi_mem *mem, + const struct spi_mem_op *op) + { +- struct airoha_snand_dev *as_dev = spi_get_ctldata(mem->spi); + u8 data[8], cmd, opcode = op->cmd.opcode; + struct airoha_snand_ctrl *as_ctrl; + int i, err; + + as_ctrl = spi_controller_get_devdata(mem->spi->controller); +- if (opcode == SPI_NAND_OP_PROGRAM_EXECUTE && +- op->addr.val == as_dev->cur_page_num) { +- as_dev->data_need_update = true; +- } else if (opcode == SPI_NAND_OP_PAGE_READ) { +- if (!as_dev->data_need_update && +- op->addr.val == as_dev->cur_page_num) +- return 0; +- +- as_dev->data_need_update = true; +- as_dev->cur_page_num = op->addr.val; +- } + + /* switch to manual mode */ + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); +@@ -996,7 +976,6 @@ static int airoha_snand_setup(struct spi + if (dma_mapping_error(as_ctrl->dev, as_dev->dma_addr)) + return -ENOMEM; + +- as_dev->data_need_update = true; + spi_set_ctldata(spi, as_dev); + + return 0; diff --git a/target/linux/airoha/patches-6.6/028-v6.13-spi-airoha-do-not-keep-tx-rx-dma-buffer-always-mappe.patch b/target/linux/airoha/patches-6.6/028-v6.13-spi-airoha-do-not-keep-tx-rx-dma-buffer-always-mappe.patch new file mode 100644 index 0000000000..71e920cd0f --- /dev/null +++ b/target/linux/airoha/patches-6.6/028-v6.13-spi-airoha-do-not-keep-tx-rx-dma-buffer-always-mappe.patch @@ -0,0 +1,435 @@ +From 7a4b3ebf1d60349587fee21872536e7bd6a4cf39 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 22 Sep 2024 19:38:30 +0200 +Subject: [PATCH] spi: airoha: do not keep {tx,rx} dma buffer always mapped + +DMA map txrx_buf on demand in airoha_snand_dirmap_read and +airoha_snand_dirmap_write routines and do not keep it always mapped. +This patch is not fixing any bug or introducing any functional change +to the driver, it just simplifies the code and improve code readability +without introducing any performance degradation according to the results +obtained from the mtd_speedtest kernel module test. + +root@OpenWrt:# insmod mtd_test.ko +root@OpenWrt:# insmod mtd_speedtest.ko dev=5 +[ 49.849869] ================================================= +[ 49.855659] mtd_speedtest: MTD device: 5 +[ 49.859583] mtd_speedtest: MTD device size 8388608, eraseblock size 131072, page size 2048, count of eraseblocks 64, pages per eraseblock 64, OOB size 128 +[ 49.874622] mtd_test: scanning for bad eraseblocks +[ 49.879433] mtd_test: scanned 64 eraseblocks, 0 are bad +[ 50.106372] mtd_speedtest: testing eraseblock write speed +[ 53.083380] mtd_speedtest: eraseblock write speed is 2756 KiB/s +[ 53.089322] mtd_speedtest: testing eraseblock read speed +[ 54.143360] mtd_speedtest: eraseblock read speed is 7811 KiB/s +[ 54.370365] mtd_speedtest: testing page write speed +[ 57.349480] mtd_speedtest: page write speed is 2754 KiB/s +[ 57.354895] mtd_speedtest: testing page read speed +[ 58.410431] mtd_speedtest: page read speed is 7796 KiB/s +[ 58.636805] mtd_speedtest: testing 2 page write speed +[ 61.612427] mtd_speedtest: 2 page write speed is 2757 KiB/s +[ 61.618021] mtd_speedtest: testing 2 page read speed +[ 62.672653] mtd_speedtest: 2 page read speed is 7804 KiB/s +[ 62.678159] mtd_speedtest: Testing erase speed +[ 62.903617] mtd_speedtest: erase speed is 37063 KiB/s +[ 62.908678] mtd_speedtest: Testing 2x multi-block erase speed +[ 63.134083] mtd_speedtest: 2x multi-block erase speed is 37292 KiB/s +[ 63.140442] mtd_speedtest: Testing 4x multi-block erase speed +[ 63.364262] mtd_speedtest: 4x multi-block erase speed is 37566 KiB/s +[ 63.370632] mtd_speedtest: Testing 8x multi-block erase speed +[ 63.595740] mtd_speedtest: 8x multi-block erase speed is 37344 KiB/s +[ 63.602089] mtd_speedtest: Testing 16x multi-block erase speed +[ 63.827426] mtd_speedtest: 16x multi-block erase speed is 37320 KiB/s +[ 63.833860] mtd_speedtest: Testing 32x multi-block erase speed +[ 64.059389] mtd_speedtest: 32x multi-block erase speed is 37288 KiB/s +[ 64.065833] mtd_speedtest: Testing 64x multi-block erase speed +[ 64.290609] mtd_speedtest: 64x multi-block erase speed is 37415 KiB/s +[ 64.297063] mtd_speedtest: finished +[ 64.300555] ================================================= + +Tested-by: Christian Marangi +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20240922-airoha-spi-fixes-v3-1-f958802b3d68@kernel.org +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 154 ++++++++++++++++------------------ + 1 file changed, 71 insertions(+), 83 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -206,13 +206,6 @@ enum airoha_snand_cs { + SPI_CHIP_SEL_LOW, + }; + +-struct airoha_snand_dev { +- size_t buf_len; +- +- u8 *txrx_buf; +- dma_addr_t dma_addr; +-}; +- + struct airoha_snand_ctrl { + struct device *dev; + struct regmap *regmap_ctrl; +@@ -617,9 +610,9 @@ static bool airoha_snand_supports_op(str + + static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc) + { +- struct airoha_snand_dev *as_dev = spi_get_ctldata(desc->mem->spi); ++ u8 *txrx_buf = spi_get_ctldata(desc->mem->spi); + +- if (!as_dev->txrx_buf) ++ if (!txrx_buf) + return -EINVAL; + + if (desc->info.offset + desc->info.length > U32_MAX) +@@ -634,10 +627,11 @@ static int airoha_snand_dirmap_create(st + static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf) + { +- struct spi_device *spi = desc->mem->spi; +- struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); + struct spi_mem_op *op = &desc->info.op_tmpl; ++ struct spi_device *spi = desc->mem->spi; + struct airoha_snand_ctrl *as_ctrl; ++ u8 *txrx_buf = spi_get_ctldata(spi); ++ dma_addr_t dma_addr; + u32 val, rd_mode; + int err; + +@@ -662,14 +656,17 @@ static ssize_t airoha_snand_dirmap_read( + if (err) + return err; + +- dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr, +- as_dev->buf_len, DMA_BIDIRECTIONAL); ++ dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE, ++ DMA_FROM_DEVICE); ++ err = dma_mapping_error(as_ctrl->dev, dma_addr); ++ if (err) ++ return err; + + /* set dma addr */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, +- as_dev->dma_addr); ++ dma_addr); + if (err) +- return err; ++ goto error_dma_unmap; + + /* set cust sec size */ + val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; +@@ -678,58 +675,58 @@ static ssize_t airoha_snand_dirmap_read( + REG_SPI_NFI_SNF_MISC_CTL2, + SPI_NFI_READ_DATA_BYTE_NUM, val); + if (err) +- return err; ++ goto error_dma_unmap; + + /* set read command */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2, + op->cmd.opcode); + if (err) +- return err; ++ goto error_dma_unmap; + + /* set read mode */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL, + FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, rd_mode)); + if (err) +- return err; ++ goto error_dma_unmap; + + /* set read addr */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL3, 0x0); + if (err) +- return err; ++ goto error_dma_unmap; + + /* set nfi read */ + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_OPMODE, + FIELD_PREP(SPI_NFI_OPMODE, 6)); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_READ_MODE | SPI_NFI_DMA_MODE); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x0); + if (err) +- return err; ++ goto error_dma_unmap; + + /* trigger dma start read */ + err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_RD_TRIG); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_RD_TRIG); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, + REG_SPI_NFI_SNF_STA_CTL1, val, + (val & SPI_NFI_READ_FROM_CACHE_DONE), + 0, 1 * USEC_PER_SEC); + if (err) +- return err; ++ goto error_dma_unmap; + + /* + * SPI_NFI_READ_FROM_CACHE_DONE bit must be written at the end +@@ -739,35 +736,41 @@ static ssize_t airoha_snand_dirmap_read( + SPI_NFI_READ_FROM_CACHE_DONE, + SPI_NFI_READ_FROM_CACHE_DONE); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, REG_SPI_NFI_INTR, + val, (val & SPI_NFI_AHB_DONE), 0, + 1 * USEC_PER_SEC); + if (err) +- return err; ++ goto error_dma_unmap; + + /* DMA read need delay for data ready from controller to DRAM */ + udelay(1); + +- dma_sync_single_for_cpu(as_ctrl->dev, as_dev->dma_addr, +- as_dev->buf_len, DMA_BIDIRECTIONAL); ++ dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE, ++ DMA_FROM_DEVICE); + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); + if (err < 0) + return err; + +- memcpy(buf, as_dev->txrx_buf + offs, len); ++ memcpy(buf, txrx_buf + offs, len); + + return len; ++ ++error_dma_unmap: ++ dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE, ++ DMA_FROM_DEVICE); ++ return err; + } + + static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf) + { +- struct spi_device *spi = desc->mem->spi; +- struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); + struct spi_mem_op *op = &desc->info.op_tmpl; ++ struct spi_device *spi = desc->mem->spi; ++ u8 *txrx_buf = spi_get_ctldata(spi); + struct airoha_snand_ctrl *as_ctrl; ++ dma_addr_t dma_addr; + u32 wr_mode, val; + int err; + +@@ -776,19 +779,20 @@ static ssize_t airoha_snand_dirmap_write + if (err < 0) + return err; + +- dma_sync_single_for_cpu(as_ctrl->dev, as_dev->dma_addr, +- as_dev->buf_len, DMA_BIDIRECTIONAL); +- memcpy(as_dev->txrx_buf + offs, buf, len); +- dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr, +- as_dev->buf_len, DMA_BIDIRECTIONAL); ++ memcpy(txrx_buf + offs, buf, len); ++ dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE, ++ DMA_TO_DEVICE); ++ err = dma_mapping_error(as_ctrl->dev, dma_addr); ++ if (err) ++ return err; + + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); + if (err < 0) +- return err; ++ goto error_dma_unmap; + + err = airoha_snand_nfi_config(as_ctrl); + if (err) +- return err; ++ goto error_dma_unmap; + + if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD || + op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD) +@@ -797,9 +801,9 @@ static ssize_t airoha_snand_dirmap_write + wr_mode = 0; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, +- as_dev->dma_addr); ++ dma_addr); + if (err) +- return err; ++ goto error_dma_unmap; + + val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, + as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num); +@@ -807,65 +811,65 @@ static ssize_t airoha_snand_dirmap_write + REG_SPI_NFI_SNF_MISC_CTL2, + SPI_NFI_PROG_LOAD_BYTE_NUM, val); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1, + FIELD_PREP(SPI_NFI_PG_LOAD_CMD, + op->cmd.opcode)); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL, + FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode)); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL2, 0x0); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_READ_MODE); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_OPMODE, + FIELD_PREP(SPI_NFI_OPMODE, 3)); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_DMA_MODE); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x80); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_WR_TRIG); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_WR_TRIG); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, REG_SPI_NFI_INTR, + val, (val & SPI_NFI_AHB_DONE), 0, + 1 * USEC_PER_SEC); + if (err) +- return err; ++ goto error_dma_unmap; + + err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, + REG_SPI_NFI_SNF_STA_CTL1, val, + (val & SPI_NFI_LOAD_TO_CACHE_DONE), + 0, 1 * USEC_PER_SEC); + if (err) +- return err; ++ goto error_dma_unmap; + + /* + * SPI_NFI_LOAD_TO_CACHE_DONE bit must be written at the end +@@ -875,13 +879,20 @@ static ssize_t airoha_snand_dirmap_write + SPI_NFI_LOAD_TO_CACHE_DONE, + SPI_NFI_LOAD_TO_CACHE_DONE); + if (err) +- return err; ++ goto error_dma_unmap; + ++ dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE, ++ DMA_TO_DEVICE); + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); + if (err < 0) + return err; + + return len; ++ ++error_dma_unmap: ++ dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE, ++ DMA_TO_DEVICE); ++ return err; + } + + static int airoha_snand_exec_op(struct spi_mem *mem, +@@ -956,42 +967,20 @@ static const struct spi_controller_mem_o + static int airoha_snand_setup(struct spi_device *spi) + { + struct airoha_snand_ctrl *as_ctrl; +- struct airoha_snand_dev *as_dev; +- +- as_ctrl = spi_controller_get_devdata(spi->controller); +- +- as_dev = devm_kzalloc(as_ctrl->dev, sizeof(*as_dev), GFP_KERNEL); +- if (!as_dev) +- return -ENOMEM; ++ u8 *txrx_buf; + + /* prepare device buffer */ +- as_dev->buf_len = SPI_NAND_CACHE_SIZE; +- as_dev->txrx_buf = devm_kzalloc(as_ctrl->dev, as_dev->buf_len, +- GFP_KERNEL); +- if (!as_dev->txrx_buf) +- return -ENOMEM; +- +- as_dev->dma_addr = dma_map_single(as_ctrl->dev, as_dev->txrx_buf, +- as_dev->buf_len, DMA_BIDIRECTIONAL); +- if (dma_mapping_error(as_ctrl->dev, as_dev->dma_addr)) ++ as_ctrl = spi_controller_get_devdata(spi->controller); ++ txrx_buf = devm_kzalloc(as_ctrl->dev, SPI_NAND_CACHE_SIZE, ++ GFP_KERNEL); ++ if (!txrx_buf) + return -ENOMEM; + +- spi_set_ctldata(spi, as_dev); ++ spi_set_ctldata(spi, txrx_buf); + + return 0; + } + +-static void airoha_snand_cleanup(struct spi_device *spi) +-{ +- struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); +- struct airoha_snand_ctrl *as_ctrl; +- +- as_ctrl = spi_controller_get_devdata(spi->controller); +- dma_unmap_single(as_ctrl->dev, as_dev->dma_addr, +- as_dev->buf_len, DMA_BIDIRECTIONAL); +- spi_set_ctldata(spi, NULL); +-} +- + static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl) + { + u32 val, sec_size, sec_num; +@@ -1093,7 +1082,6 @@ static int airoha_snand_probe(struct pla + ctrl->bits_per_word_mask = SPI_BPW_MASK(8); + ctrl->mode_bits = SPI_RX_DUAL; + ctrl->setup = airoha_snand_setup; +- ctrl->cleanup = airoha_snand_cleanup; + device_set_node(&ctrl->dev, dev_fwnode(dev)); + + err = airoha_snand_nfi_setup(as_ctrl); diff --git a/target/linux/airoha/patches-6.6/029-v6.12-net-dsa-mt7530-Add-EN7581-support.patch b/target/linux/airoha/patches-6.6/029-v6.12-net-dsa-mt7530-Add-EN7581-support.patch new file mode 100644 index 0000000000..55e4ae9d40 --- /dev/null +++ b/target/linux/airoha/patches-6.6/029-v6.12-net-dsa-mt7530-Add-EN7581-support.patch @@ -0,0 +1,184 @@ +From 2b0229f67932e4b9e2f458bf286903582bd30740 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 1 Aug 2024 09:35:12 +0200 +Subject: [PATCH] net: dsa: mt7530: Add EN7581 support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce support for the DSA built-in switch available on the EN7581 +development board. EN7581 support is similar to MT7988 one except +it requires to set MT7530_FORCE_MODE bit in MT753X_PMCR_P register +for on cpu port. + +Tested-by: Benjamin Larsson +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Arınç ÜNAL +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530-mmio.c | 1 + + drivers/net/dsa/mt7530.c | 49 ++++++++++++++++++++++++++++++----- + drivers/net/dsa/mt7530.h | 20 ++++++++++---- + 3 files changed, 59 insertions(+), 11 deletions(-) + +--- a/drivers/net/dsa/mt7530-mmio.c ++++ b/drivers/net/dsa/mt7530-mmio.c +@@ -11,6 +11,7 @@ + #include "mt7530.h" + + static const struct of_device_id mt7988_of_match[] = { ++ { .compatible = "airoha,en7581-switch", .data = &mt753x_table[ID_EN7581], }, + { .compatible = "mediatek,mt7988-switch", .data = &mt753x_table[ID_MT7988], }, + { /* sentinel */ }, + }; +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1152,7 +1152,8 @@ mt753x_cpu_port_enable(struct dsa_switch + * the MT7988 SoC. Trapped frames will be forwarded to the CPU port that + * is affine to the inbound user port. + */ +- if (priv->id == ID_MT7531 || priv->id == ID_MT7988) ++ if (priv->id == ID_MT7531 || priv->id == ID_MT7988 || ++ priv->id == ID_EN7581) + mt7530_set(priv, MT7531_CFC, MT7531_CPU_PMAP(BIT(port))); + + /* CPU port gets connected to all user ports of +@@ -2207,7 +2208,7 @@ mt7530_setup_irq(struct mt7530_priv *pri + return priv->irq ? : -EINVAL; + } + +- if (priv->id == ID_MT7988) ++ if (priv->id == ID_MT7988 || priv->id == ID_EN7581) + priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, + &mt7988_irq_domain_ops, + priv); +@@ -2438,8 +2439,10 @@ mt7530_setup(struct dsa_switch *ds) + /* Clear link settings and enable force mode to force link down + * on all ports until they're enabled later. + */ +- mt7530_rmw(priv, MT753X_PMCR_P(i), PMCR_LINK_SETTINGS_MASK | +- MT7530_FORCE_MODE, MT7530_FORCE_MODE); ++ mt7530_rmw(priv, MT753X_PMCR_P(i), ++ PMCR_LINK_SETTINGS_MASK | ++ MT753X_FORCE_MODE(priv->id), ++ MT753X_FORCE_MODE(priv->id)); + + /* Disable forwarding by default on all ports */ + mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, +@@ -2550,8 +2553,10 @@ mt7531_setup_common(struct dsa_switch *d + /* Clear link settings and enable force mode to force link down + * on all ports until they're enabled later. + */ +- mt7530_rmw(priv, MT753X_PMCR_P(i), PMCR_LINK_SETTINGS_MASK | +- MT7531_FORCE_MODE_MASK, MT7531_FORCE_MODE_MASK); ++ mt7530_rmw(priv, MT753X_PMCR_P(i), ++ PMCR_LINK_SETTINGS_MASK | ++ MT753X_FORCE_MODE(priv->id), ++ MT753X_FORCE_MODE(priv->id)); + + /* Disable forwarding by default on all ports */ + mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, +@@ -2783,6 +2788,28 @@ static void mt7988_mac_port_get_caps(str + } + } + ++static void en7581_mac_port_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) ++{ ++ switch (port) { ++ /* Ports which are connected to switch PHYs. There is no MII pinout. */ ++ case 0 ... 4: ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); ++ ++ config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD; ++ break; ++ ++ /* Port 6 is connected to SoC's XGMII MAC. There is no MII pinout. */ ++ case 6: ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); ++ ++ config->mac_capabilities |= MAC_10000FD; ++ break; ++ } ++} ++ + static void + mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface) +@@ -3220,6 +3247,16 @@ const struct mt753x_info mt753x_table[] + .phy_write_c45 = mt7531_ind_c45_phy_write, + .mac_port_get_caps = mt7988_mac_port_get_caps, + }, ++ [ID_EN7581] = { ++ .id = ID_EN7581, ++ .pcs_ops = &mt7530_pcs_ops, ++ .sw_setup = mt7988_setup, ++ .phy_read_c22 = mt7531_ind_c22_phy_read, ++ .phy_write_c22 = mt7531_ind_c22_phy_write, ++ .phy_read_c45 = mt7531_ind_c45_phy_read, ++ .phy_write_c45 = mt7531_ind_c45_phy_write, ++ .mac_port_get_caps = en7581_mac_port_get_caps, ++ }, + }; + EXPORT_SYMBOL_GPL(mt753x_table); + +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -19,6 +19,7 @@ enum mt753x_id { + ID_MT7621 = 1, + ID_MT7531 = 2, + ID_MT7988 = 3, ++ ID_EN7581 = 4, + }; + + #define NUM_TRGMII_CTRL 5 +@@ -64,25 +65,30 @@ enum mt753x_id { + #define MT7531_CPU_PMAP(x) FIELD_PREP(MT7531_CPU_PMAP_MASK, x) + + #define MT753X_MIRROR_REG(id) ((id == ID_MT7531 || \ +- id == ID_MT7988) ? \ ++ id == ID_MT7988 || \ ++ id == ID_EN7581) ? \ + MT7531_CFC : MT753X_MFC) + + #define MT753X_MIRROR_EN(id) ((id == ID_MT7531 || \ +- id == ID_MT7988) ? \ ++ id == ID_MT7988 || \ ++ id == ID_EN7581) ? \ + MT7531_MIRROR_EN : MT7530_MIRROR_EN) + + #define MT753X_MIRROR_PORT_MASK(id) ((id == ID_MT7531 || \ +- id == ID_MT7988) ? \ ++ id == ID_MT7988 || \ ++ id == ID_EN7581) ? \ + MT7531_MIRROR_PORT_MASK : \ + MT7530_MIRROR_PORT_MASK) + + #define MT753X_MIRROR_PORT_GET(id, val) ((id == ID_MT7531 || \ +- id == ID_MT7988) ? \ ++ id == ID_MT7988 || \ ++ id == ID_EN7581) ? \ + MT7531_MIRROR_PORT_GET(val) : \ + MT7530_MIRROR_PORT_GET(val)) + + #define MT753X_MIRROR_PORT_SET(id, val) ((id == ID_MT7531 || \ +- id == ID_MT7988) ? \ ++ id == ID_MT7988 || \ ++ id == ID_EN7581) ? \ + MT7531_MIRROR_PORT_SET(val) : \ + MT7530_MIRROR_PORT_SET(val)) + +@@ -355,6 +361,10 @@ enum mt7530_vlan_port_acc_frm { + MT7531_FORCE_MODE_TX_FC | \ + MT7531_FORCE_MODE_EEE100 | \ + MT7531_FORCE_MODE_EEE1G) ++#define MT753X_FORCE_MODE(id) ((id == ID_MT7531 || \ ++ id == ID_MT7988) ? \ ++ MT7531_FORCE_MODE_MASK : \ ++ MT7530_FORCE_MODE) + #define PMCR_LINK_SETTINGS_MASK (PMCR_MAC_TX_EN | PMCR_MAC_RX_EN | \ + PMCR_FORCE_EEE1G | \ + PMCR_FORCE_EEE100 | \ diff --git a/target/linux/airoha/patches-6.6/100-cpufreq-airoha-Add-EN7581-Cpufreq-SMC-driver.patch b/target/linux/airoha/patches-6.6/100-cpufreq-airoha-Add-EN7581-Cpufreq-SMC-driver.patch new file mode 100644 index 0000000000..0276a4ee81 --- /dev/null +++ b/target/linux/airoha/patches-6.6/100-cpufreq-airoha-Add-EN7581-Cpufreq-SMC-driver.patch @@ -0,0 +1,247 @@ +From 5296da64f77ef6c809b715cdecf308977a08acb9 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 16 Oct 2024 18:00:57 +0200 +Subject: [PATCH] cpufreq: airoha: Add EN7581 Cpufreq SMC driver + +Add simple Cpufreq driver for Airoha EN7581 SoC that control CPU +frequency scaling with SMC APIs. + +All CPU share the same frequency and can't be controlled independently. +Current shared CPU frequency is returned by the related SMC command. + +Add SoC compatible to cpufreq-dt-plat block list as a dedicated cpufreq +driver is needed with OPP v2 nodes declared in DTS. + +Signed-off-by: Christian Marangi +--- + drivers/cpufreq/Kconfig.arm | 8 ++ + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/airoha-cpufreq.c | 183 +++++++++++++++++++++++++++ + drivers/cpufreq/cpufreq-dt-platdev.c | 2 + + 4 files changed, 194 insertions(+) + create mode 100644 drivers/cpufreq/airoha-cpufreq.c + +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -41,6 +41,14 @@ config ARM_ALLWINNER_SUN50I_CPUFREQ_NVME + To compile this driver as a module, choose M here: the + module will be called sun50i-cpufreq-nvmem. + ++config ARM_AIROHA_SOC_CPUFREQ ++ tristate "Airoha EN7581 SoC CPUFreq support" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ select PM_OPP ++ default ARCH_AIROHA ++ help ++ This adds the CPUFreq driver for Airoha EN7581 SoCs. ++ + config ARM_APPLE_SOC_CPUFREQ + tristate "Apple Silicon SoC CPUFreq support" + depends on ARCH_APPLE || (COMPILE_TEST && 64BIT) +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -52,6 +52,7 @@ obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) + + + ################################################################################## + # ARM SoC drivers ++obj-$(CONFIG_ARM_AIROHA_SOC_CPUFREQ) += airoha-cpufreq.o + obj-$(CONFIG_ARM_APPLE_SOC_CPUFREQ) += apple-soc-cpufreq.o + obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o + obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ) += armada-8k-cpufreq.o +--- /dev/null ++++ b/drivers/cpufreq/airoha-cpufreq.c +@@ -0,0 +1,183 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++#include ++ ++#define AIROHA_SIP_AVS_HANDLE 0x82000301 ++#define AIROHA_AVS_OP_BASE 0xddddddd0 ++#define AIROHA_AVS_OP_MASK GENMASK(1, 0) ++#define AIROHA_AVS_OP_FREQ_DYN_ADJ (AIROHA_AVS_OP_BASE | \ ++ FIELD_PREP(AIROHA_AVS_OP_MASK, 0x1)) ++#define AIROHA_AVS_OP_GET_FREQ (AIROHA_AVS_OP_BASE | \ ++ FIELD_PREP(AIROHA_AVS_OP_MASK, 0x2)) ++ ++struct airoha_cpufreq_priv { ++ struct list_head list; ++ ++ cpumask_var_t cpus; ++ struct device *cpu_dev; ++ struct cpufreq_frequency_table *freq_table; ++}; ++ ++static LIST_HEAD(priv_list); ++ ++static unsigned int airoha_cpufreq_get(unsigned int cpu) ++{ ++ const struct arm_smccc_1_2_regs args = { ++ .a0 = AIROHA_SIP_AVS_HANDLE, ++ .a1 = AIROHA_AVS_OP_GET_FREQ, ++ }; ++ struct arm_smccc_1_2_regs res; ++ ++ arm_smccc_1_2_smc(&args, &res); ++ ++ return (int)(res.a0 * 1000); ++} ++ ++static int airoha_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index) ++{ ++ const struct arm_smccc_1_2_regs args = { ++ .a0 = AIROHA_SIP_AVS_HANDLE, ++ .a1 = AIROHA_AVS_OP_FREQ_DYN_ADJ, ++ .a3 = index, ++ }; ++ struct arm_smccc_1_2_regs res; ++ ++ arm_smccc_1_2_smc(&args, &res); ++ ++ /* SMC signal correct apply by unsetting BIT 0 */ ++ return res.a0 & BIT(0) ? -EINVAL : 0; ++} ++ ++static struct airoha_cpufreq_priv *airoha_cpufreq_find_data(int cpu) ++{ ++ struct airoha_cpufreq_priv *priv; ++ ++ list_for_each_entry(priv, &priv_list, list) { ++ if (cpumask_test_cpu(cpu, priv->cpus)) ++ return priv; ++ } ++ ++ return NULL; ++} ++ ++static int airoha_cpufreq_init(struct cpufreq_policy *policy) ++{ ++ struct airoha_cpufreq_priv *priv; ++ struct device *cpu_dev; ++ ++ priv = airoha_cpufreq_find_data(policy->cpu); ++ if (!priv) ++ return -ENODEV; ++ ++ cpu_dev = priv->cpu_dev; ++ cpumask_copy(policy->cpus, priv->cpus); ++ policy->driver_data = priv; ++ policy->freq_table = priv->freq_table; ++ ++ return 0; ++} ++ ++static struct cpufreq_driver airoha_cpufreq_driver = { ++ .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK | ++ CPUFREQ_IS_COOLING_DEV, ++ .verify = cpufreq_generic_frequency_table_verify, ++ .target_index = airoha_cpufreq_set_target, ++ .get = airoha_cpufreq_get, ++ .init = airoha_cpufreq_init, ++ .attr = cpufreq_generic_attr, ++ .name = "airoha-cpufreq", ++}; ++ ++static int airoha_cpufreq_driver_init_cpu(int cpu) ++{ ++ struct airoha_cpufreq_priv *priv; ++ struct device *cpu_dev; ++ int ret; ++ ++ cpu_dev = get_cpu_device(cpu); ++ if (!cpu_dev) ++ return -EPROBE_DEFER; ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ if (!zalloc_cpumask_var(&priv->cpus, GFP_KERNEL)) ++ return -ENOMEM; ++ ++ cpumask_set_cpu(cpu, priv->cpus); ++ priv->cpu_dev = cpu_dev; ++ ++ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus); ++ if (ret) ++ goto err; ++ ++ ret = dev_pm_opp_of_cpumask_add_table(priv->cpus); ++ if (ret) ++ goto err; ++ ++ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &priv->freq_table); ++ if (ret) ++ goto err; ++ ++ list_add(&priv->list, &priv_list); ++ ++ return 0; ++ ++err: ++ dev_pm_opp_of_cpumask_remove_table(priv->cpus); ++ free_cpumask_var(priv->cpus); ++ ++ return ret; ++} ++ ++static void airoha_cpufreq_release(void) ++{ ++ struct airoha_cpufreq_priv *priv, *tmp; ++ ++ list_for_each_entry_safe(priv, tmp, &priv_list, list) { ++ dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &priv->freq_table); ++ dev_pm_opp_of_cpumask_remove_table(priv->cpus); ++ free_cpumask_var(priv->cpus); ++ list_del(&priv->list); ++ kfree(priv); ++ } ++} ++ ++static int __init airoha_cpufreq_driver_probe(void) ++{ ++ int cpu, ret; ++ ++ if (!of_machine_is_compatible("airoha,en7581")) ++ return -ENODEV; ++ ++ for_each_possible_cpu(cpu) { ++ ret = airoha_cpufreq_driver_init_cpu(cpu); ++ if (ret) ++ goto err; ++ } ++ ++ ret = cpufreq_register_driver(&airoha_cpufreq_driver); ++ if (ret) ++ goto err; ++ ++ return 0; ++ ++err: ++ airoha_cpufreq_release(); ++ return ret; ++} ++module_init(airoha_cpufreq_driver_probe); ++ ++static void __exit airoha_cpufreq_driver_remove(void) ++{ ++ cpufreq_unregister_driver(&airoha_cpufreq_driver); ++ airoha_cpufreq_release(); ++} ++module_exit(airoha_cpufreq_driver_remove); ++ ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("CPUfreq driver for Airoha SoCs"); ++MODULE_LICENSE("GPL"); +--- a/drivers/cpufreq/cpufreq-dt-platdev.c ++++ b/drivers/cpufreq/cpufreq-dt-platdev.c +@@ -103,6 +103,8 @@ static const struct of_device_id allowli + * platforms using "operating-points-v2" property. + */ + static const struct of_device_id blocklist[] __initconst = { ++ { .compatible = "airoha,en7581", }, ++ + { .compatible = "allwinner,sun50i-h6", }, + + { .compatible = "apple,arm-platform", }, diff --git a/target/linux/airoha/patches-6.6/101-01-thermal-of-Add-devm_thermal_of_zone_register_with_pa.patch b/target/linux/airoha/patches-6.6/101-01-thermal-of-Add-devm_thermal_of_zone_register_with_pa.patch new file mode 100644 index 0000000000..e69f57d273 --- /dev/null +++ b/target/linux/airoha/patches-6.6/101-01-thermal-of-Add-devm_thermal_of_zone_register_with_pa.patch @@ -0,0 +1,210 @@ +From 1f194995c3648e20da53137d4c9110b39e779f41 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 18 Oct 2024 11:34:35 +0200 +Subject: [PATCH 2/3] thermal: of: Add + devm_thermal_of_zone_register_with_params() variant + +Commit b1ae92dcfa8e ("thermal: core: Make struct thermal_zone_device +definition internal") moved the thermal_zone_device struct from global +thermal.h to internal thermal_core.h making the internal variables of +the struct not accessible from the user drivers (without inclusing +thermal_core.h). + +One case where the internal variables might be needed is for the +thermal_zone_params in the context of OF probe. + +In such case a thermal driver might have default params that can only be +parsed at runtime (example present in EFUSE or derived from other values) +and wants to update the values in the thermal_zone_params for the +thermal device. (to use the helper like get_slope() or get_offset()) + +To account for this scenario, introduce a variant of +devm_thermal_of_zone_register(), +devm_thermal_of_zone_register_with_params(), that takes and additional +variable and permits to register the thermal device with default +thermal_zone_params. + +To follow OF implementation, these params are only treated as default +params and are ignored if a related one is defined in DT. (example a +slope or offset value defined in DT have priority to the default one +passed in a thermal_device_params struct) + +This permits to support both implementation, use the helpers and expose +these values in sysfs. + +Signed-off-by: Christian Marangi +--- + drivers/thermal/thermal_of.c | 67 +++++++++++++++++++++++++++++------- + include/linux/thermal.h | 13 +++++++ + 2 files changed, 68 insertions(+), 12 deletions(-) + +--- a/drivers/thermal/thermal_of.c ++++ b/drivers/thermal/thermal_of.c +@@ -249,7 +249,7 @@ static void thermal_of_parameters_init(s + { + int coef[2]; + int ncoef = ARRAY_SIZE(coef); +- int prop, ret; ++ int prop; + + tzp->no_hwmon = true; + +@@ -261,14 +261,11 @@ static void thermal_of_parameters_init(s + * thermal zone. Thus, we are considering only the first two + * values as slope and offset. + */ +- ret = of_property_read_u32_array(np, "coefficients", coef, ncoef); +- if (ret) { +- coef[0] = 1; +- coef[1] = 0; ++ if (!of_property_read_u32_array(np, "coefficients", coef, ncoef)) { ++ tzp->slope = coef[0]; ++ tzp->offset = coef[1]; + } + +- tzp->slope = coef[0]; +- tzp->offset = coef[1]; + } + + static struct device_node *thermal_of_zone_get_by_name(struct thermal_zone_device *tz) +@@ -462,10 +459,15 @@ static void thermal_of_zone_unregister(s + * zone properties and registers new thermal zone with those + * properties. + * ++ * The passed thermal zone params are treated as default values and ignored if ++ * the related property is found in DT. (DT params have priority to ++ * default values) ++ * + * @sensor: A device node pointer corresponding to the sensor in the device tree + * @id: An integer as sensor identifier + * @data: A private data to be stored in the thermal zone dedicated private area + * @ops: A set of thermal sensor ops ++ * @tzp: a pointer to the default thermal zone params structure associated with the sensor + * + * Return: a valid thermal zone structure pointer on success. + * - EINVAL: if the device tree thermal description is malformed +@@ -473,11 +475,11 @@ static void thermal_of_zone_unregister(s + * - Other negative errors are returned by the underlying called functions + */ + static struct thermal_zone_device *thermal_of_zone_register(struct device_node *sensor, int id, void *data, +- const struct thermal_zone_device_ops *ops) ++ const struct thermal_zone_device_ops *ops, ++ struct thermal_zone_params *tzp) + { + struct thermal_zone_device *tz; + struct thermal_trip *trips; +- struct thermal_zone_params tzp = {}; + struct thermal_zone_device_ops *of_ops; + struct device_node *np; + int delay, pdelay; +@@ -509,7 +511,7 @@ static struct thermal_zone_device *therm + goto out_kfree_trips; + } + +- thermal_of_parameters_init(np, &tzp); ++ thermal_of_parameters_init(np, tzp); + + of_ops->bind = thermal_of_bind; + of_ops->unbind = thermal_of_unbind; +@@ -517,7 +519,7 @@ static struct thermal_zone_device *therm + mask = GENMASK_ULL((ntrips) - 1, 0); + + tz = thermal_zone_device_register_with_trips(np->name, trips, ntrips, +- mask, data, of_ops, &tzp, ++ mask, data, of_ops, tzp, + pdelay, delay); + if (IS_ERR(tz)) { + ret = PTR_ERR(tz); +@@ -572,6 +574,7 @@ static int devm_thermal_of_zone_match(st + struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, int sensor_id, void *data, + const struct thermal_zone_device_ops *ops) + { ++ struct thermal_zone_params tzp = { .slope = 1 }; + struct thermal_zone_device **ptr, *tzd; + + ptr = devres_alloc(devm_thermal_of_zone_release, sizeof(*ptr), +@@ -579,7 +582,7 @@ struct thermal_zone_device *devm_thermal + if (!ptr) + return ERR_PTR(-ENOMEM); + +- tzd = thermal_of_zone_register(dev->of_node, sensor_id, data, ops); ++ tzd = thermal_of_zone_register(dev->of_node, sensor_id, data, ops, &tzp); + if (IS_ERR(tzd)) { + devres_free(ptr); + return tzd; +@@ -593,6 +596,46 @@ struct thermal_zone_device *devm_thermal + EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register); + + /** ++ * devm_thermal_of_zone_register_with_params - register a thermal tied with the sensor life cycle ++ * with default params ++ * ++ * This function is the device version of the thermal_of_zone_register() function. ++ * ++ * @dev: a device structure pointer to sensor to be tied with the thermal zone OF life cycle ++ * @sensor_id: the sensor identifier ++ * @data: a pointer to a private data to be stored in the thermal zone 'devdata' field ++ * @ops: a pointer to the ops structure associated with the sensor ++ * @tzp: a pointer to the default thermal zone params structure associated with the sensor ++ * ++ * The thermal zone params are treated as default values and ignored if the related property is ++ * found in DT. (DT params have priority to default values) ++ */ ++struct thermal_zone_device *devm_thermal_of_zone_register_with_params(struct device *dev, int sensor_id, ++ void *data, ++ const struct thermal_zone_device_ops *ops, ++ struct thermal_zone_params *tzp) ++{ ++ struct thermal_zone_device **ptr, *tzd; ++ ++ ptr = devres_alloc(devm_thermal_of_zone_release, sizeof(*ptr), ++ GFP_KERNEL); ++ if (!ptr) ++ return ERR_PTR(-ENOMEM); ++ ++ tzd = thermal_of_zone_register(dev->of_node, sensor_id, data, ops, tzp); ++ if (IS_ERR(tzd)) { ++ devres_free(ptr); ++ return tzd; ++ } ++ ++ *ptr = tzd; ++ devres_add(dev, ptr); ++ ++ return tzd; ++} ++EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register_with_params); ++ ++/** + * devm_thermal_of_zone_unregister - Resource managed version of + * thermal_of_zone_unregister(). + * @dev: Device for which which resource was allocated. +--- a/include/linux/thermal.h ++++ b/include/linux/thermal.h +@@ -261,6 +261,10 @@ struct thermal_zone_params { + #ifdef CONFIG_THERMAL_OF + struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, int id, void *data, + const struct thermal_zone_device_ops *ops); ++struct thermal_zone_device *devm_thermal_of_zone_register_with_params(struct device *dev, int id, ++ void *data, ++ const struct thermal_zone_device_ops *ops, ++ struct thermal_zone_params *tzp); + + void devm_thermal_of_zone_unregister(struct device *dev, struct thermal_zone_device *tz); + +@@ -272,6 +276,15 @@ struct thermal_zone_device *devm_thermal + { + return ERR_PTR(-ENOTSUPP); + } ++ ++static inline ++struct thermal_zone_device *devm_thermal_of_zone_register_with_params(struct device *dev, int id, ++ void *data, ++ const struct thermal_zone_device_ops *ops, ++ struct thermal_zone_params *tzp) ++{ ++ return ERR_PTR(-ENOTSUPP); ++} + + static inline void devm_thermal_of_zone_unregister(struct device *dev, + struct thermal_zone_device *tz) diff --git a/target/linux/airoha/patches-6.6/101-02-thermal-Add-support-for-Airoha-EN7581-thermal-sensor.patch b/target/linux/airoha/patches-6.6/101-02-thermal-Add-support-for-Airoha-EN7581-thermal-sensor.patch new file mode 100644 index 0000000000..ffacbba693 --- /dev/null +++ b/target/linux/airoha/patches-6.6/101-02-thermal-Add-support-for-Airoha-EN7581-thermal-sensor.patch @@ -0,0 +1,535 @@ +From bc6a6a4ec6c28467683121cc165e5681b4acdf8d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 27 Aug 2024 23:04:53 +0200 +Subject: [PATCH 3/3] thermal: Add support for Airoha EN7581 thermal sensor + +Add support for Airoha EN7581 thermal sensor. This provide support for +reading the CPU or SoC Package sensor and to setup trip points for hot +and critical condition. An interrupt is fired to react on this and +doesn't require passive poll to read the temperature. + +The thermal regs provide a way to read the ADC value from an external +register placed in the Chip SCU regs. Monitor will read this value and +fire an interrupt if the trip condition configured is reached. + +Signed-off-by: Christian Marangi +--- + drivers/thermal/Kconfig | 9 + + drivers/thermal/Makefile | 1 + + drivers/thermal/airoha_thermal.c | 482 +++++++++++++++++++++++++++++++ + 3 files changed, 492 insertions(+) + create mode 100644 drivers/thermal/airoha_thermal.c + +--- a/drivers/thermal/Kconfig ++++ b/drivers/thermal/Kconfig +@@ -317,6 +317,15 @@ config QORIQ_THERMAL + cpufreq is used as the cooling device to throttle CPUs when the + passive trip is crossed. + ++config AIROHA_THERMAL ++ tristate "Airoha thermal sensor driver" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ depends on MFD_SYSCON ++ depends on OF ++ help ++ Enable this to plug the Airoha thermal sensor driver into the Linux ++ thermal framework. ++ + config SPEAR_THERMAL + tristate "SPEAr thermal sensor driver" + depends on PLAT_SPEAR || COMPILE_TEST +--- a/drivers/thermal/Makefile ++++ b/drivers/thermal/Makefile +@@ -34,6 +34,7 @@ obj-$(CONFIG_K3_THERMAL) += k3_bandgap.o + # platform thermal drivers + obj-y += broadcom/ + obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o ++obj-$(CONFIG_AIROHA_THERMAL) += airoha_thermal.o + obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o + obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o + obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o +--- /dev/null ++++ b/drivers/thermal/airoha_thermal.c +@@ -0,0 +1,482 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* SCU regs */ ++#define EN7581_PLLRG_PROTECT 0x268 ++#define EN7581_PWD_TADC 0x2ec ++#define EN7581_MUX_TADC GENMASK(3, 1) ++#define EN7581_DOUT_TADC 0x2f8 ++#define EN7581_DOUT_TADC_MASK GENMASK(15, 0) ++ ++/* PTP_THERMAL regs */ ++#define EN7581_TEMPMONCTL0 0x800 ++#define EN7581_SENSE3_EN BIT(3) ++#define EN7581_SENSE2_EN BIT(2) ++#define EN7581_SENSE1_EN BIT(1) ++#define EN7581_SENSE0_EN BIT(0) ++#define EN7581_TEMPMONCTL1 0x804 ++/* period unit calculated in BUS clock * 256 scaling-up */ ++#define EN7581_PERIOD_UNIT GENMASK(9, 0) ++#define EN7581_TEMPMONCTL2 0x808 ++#define EN7581_FILT_INTERVAL GENMASK(25, 16) ++#define EN7581_SEN_INTERVAL GENMASK(9, 0) ++#define EN7581_TEMPMONINT 0x80C ++#define EN7581_STAGE3_INT_EN BIT(31) ++#define EN7581_STAGE2_INT_EN BIT(30) ++#define EN7581_STAGE1_INT_EN BIT(29) ++#define EN7581_FILTER_INT_EN_3 BIT(28) ++#define EN7581_IMMD_INT_EN3 BIT(27) ++#define EN7581_NOHOTINTEN3 BIT(26) ++#define EN7581_HOFSINTEN3 BIT(25) ++#define EN7581_LOFSINTEN3 BIT(24) ++#define EN7581_HINTEN3 BIT(23) ++#define EN7581_CINTEN3 BIT(22) ++#define EN7581_FILTER_INT_EN_2 BIT(21) ++#define EN7581_FILTER_INT_EN_1 BIT(20) ++#define EN7581_FILTER_INT_EN_0 BIT(19) ++#define EN7581_IMMD_INT_EN2 BIT(18) ++#define EN7581_IMMD_INT_EN1 BIT(17) ++#define EN7581_IMMD_INT_EN0 BIT(16) ++#define EN7581_TIME_OUT_INT_EN BIT(15) ++#define EN7581_NOHOTINTEN2 BIT(14) ++#define EN7581_HOFSINTEN2 BIT(13) ++#define EN7581_LOFSINTEN2 BIT(12) ++#define EN7581_HINTEN2 BIT(11) ++#define EN7581_CINTEN2 BIT(10) ++#define EN7581_NOHOTINTEN1 BIT(9) ++#define EN7581_HOFSINTEN1 BIT(8) ++#define EN7581_LOFSINTEN1 BIT(7) ++#define EN7581_HINTEN1 BIT(6) ++#define EN7581_CINTEN1 BIT(5) ++#define EN7581_NOHOTINTEN0 BIT(4) ++/* Similar to COLD and HOT also these seems to be swapped in documentation */ ++#define EN7581_LOFSINTEN0 BIT(3) /* In documentation: BIT(2) */ ++#define EN7581_HOFSINTEN0 BIT(2) /* In documentation: BIT(3) */ ++/* It seems documentation have these swapped as the HW ++ * - Fire BIT(1) when lower than EN7581_COLD_THRE ++ * - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or ++ * EN7581_HOT_THRE ++ */ ++#define EN7581_CINTEN0 BIT(1) /* In documentation: BIT(0) */ ++#define EN7581_HINTEN0 BIT(0) /* In documentation: BIT(1) */ ++#define EN7581_TEMPMONINTSTS 0x810 ++#define EN7581_STAGE3_INT_STAT BIT(31) ++#define EN7581_STAGE2_INT_STAT BIT(30) ++#define EN7581_STAGE1_INT_STAT BIT(29) ++#define EN7581_FILTER_INT_STAT_3 BIT(28) ++#define EN7581_IMMD_INT_STS3 BIT(27) ++#define EN7581_NOHOTINTSTS3 BIT(26) ++#define EN7581_HOFSINTSTS3 BIT(25) ++#define EN7581_LOFSINTSTS3 BIT(24) ++#define EN7581_HINTSTS3 BIT(23) ++#define EN7581_CINTSTS3 BIT(22) ++#define EN7581_FILTER_INT_STAT_2 BIT(21) ++#define EN7581_FILTER_INT_STAT_1 BIT(20) ++#define EN7581_FILTER_INT_STAT_0 BIT(19) ++#define EN7581_IMMD_INT_STS2 BIT(18) ++#define EN7581_IMMD_INT_STS1 BIT(17) ++#define EN7581_IMMD_INT_STS0 BIT(16) ++#define EN7581_TIME_OUT_INT_STAT BIT(15) ++#define EN7581_NOHOTINTSTS2 BIT(14) ++#define EN7581_HOFSINTSTS2 BIT(13) ++#define EN7581_LOFSINTSTS2 BIT(12) ++#define EN7581_HINTSTS2 BIT(11) ++#define EN7581_CINTSTS2 BIT(10) ++#define EN7581_NOHOTINTSTS1 BIT(9) ++#define EN7581_HOFSINTSTS1 BIT(8) ++#define EN7581_LOFSINTSTS1 BIT(7) ++#define EN7581_HINTSTS1 BIT(6) ++#define EN7581_CINTSTS1 BIT(5) ++#define EN7581_NOHOTINTSTS0 BIT(4) ++/* Similar to COLD and HOT also these seems to be swapped in documentation */ ++#define EN7581_LOFSINTSTS0 BIT(3) /* In documentation: BIT(2) */ ++#define EN7581_HOFSINTSTS0 BIT(2) /* In documentation: BIT(3) */ ++/* It seems documentation have these swapped as the HW ++ * - Fire BIT(1) when lower than EN7581_COLD_THRE ++ * - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or ++ * EN7581_HOT_THRE ++ * ++ * To clear things, we swap the define but we keep them documented here. ++ */ ++#define EN7581_CINTSTS0 BIT(1) /* In documentation: BIT(0) */ ++#define EN7581_HINTSTS0 BIT(0) /* In documentation: BIT(1)*/ ++/* Monitor will take the bigger threshold between HOT2NORMAL and HOT ++ * and will fire both HOT2NORMAL and HOT interrupt when higher than the 2 ++ * ++ * It has also been observed that not setting HOT2NORMAL makes the monitor ++ * treat COLD threshold as HOT2NORMAL. ++ */ ++#define EN7581_TEMPH2NTHRE 0x824 ++/* It seems HOT2NORMAL is actually NORMAL2HOT */ ++#define EN7581_HOT2NORMAL_THRE GENMASK(11, 0) ++#define EN7581_TEMPHTHRE 0x828 ++#define EN7581_HOT_THRE GENMASK(11, 0) ++/* Monitor will use this as HOT2NORMAL (fire interrupt when lower than...)*/ ++#define EN7581_TEMPCTHRE 0x82c ++#define EN7581_COLD_THRE GENMASK(11, 0) ++/* Also LOW and HIGH offset register are swapped */ ++#define EN7581_TEMPOFFSETL 0x830 /* In documentation: 0x834 */ ++#define EN7581_LOW_OFFSET GENMASK(11, 0) ++#define EN7581_TEMPOFFSETH 0x834 /* In documentation: 0x830 */ ++#define EN7581_HIGH_OFFSET GENMASK(11, 0) ++#define EN7581_TEMPMSRCTL0 0x838 ++#define EN7581_MSRCTL3 GENMASK(11, 9) ++#define EN7581_MSRCTL2 GENMASK(8, 6) ++#define EN7581_MSRCTL1 GENMASK(5, 3) ++#define EN7581_MSRCTL0 GENMASK(2, 0) ++#define EN7581_TEMPADCVALIDADDR 0x878 ++#define EN7581_ADC_VALID_ADDR GENMASK(31, 0) ++#define EN7581_TEMPADCVOLTADDR 0x87c ++#define EN7581_ADC_VOLT_ADDR GENMASK(31, 0) ++#define EN7581_TEMPRDCTRL 0x880 ++/* ++ * NOTICE: AHB have this set to 0 by default. Means that ++ * the same addr is used for ADC volt and valid reading. ++ * In such case, VALID ADDR is used and volt addr is ignored. ++ */ ++#define EN7581_RD_CTRL_DIFF BIT(0) ++#define EN7581_TEMPADCVALIDMASK 0x884 ++#define EN7581_ADV_RD_VALID_POLARITY BIT(5) ++#define EN7581_ADV_RD_VALID_POS GENMASK(4, 0) ++#define EN7581_TEMPADCVOLTAGESHIFT 0x888 ++#define EN7581_ADC_VOLTAGE_SHIFT GENMASK(4, 0) ++/* ++ * Same values for each CTL. ++ * Can operate in: ++ * - 1 sample ++ * - 2 sample and make average of them ++ * - 4,6,10,16 sample, drop max and min and make avgerage of them ++ */ ++#define EN7581_MSRCTL_1SAMPLE 0x0 ++#define EN7581_MSRCTL_AVG2SAMPLE 0x1 ++#define EN7581_MSRCTL_4SAMPLE_MAX_MIX_AVG2 0x2 ++#define EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4 0x3 ++#define EN7581_MSRCTL_10SAMPLE_MAX_MIX_AVG8 0x4 ++#define EN7581_MSRCTL_18SAMPLE_MAX_MIX_AVG16 0x5 ++#define EN7581_TEMPAHBPOLL 0x840 ++#define EN7581_ADC_POLL_INTVL GENMASK(31, 0) ++/* PTPSPARE0,2 reg are used to store efuse info for calibrated temp offset */ ++#define EN7581_EFUSE_TEMP_OFFSET_REG 0xf20 /* PTPSPARE0 */ ++#define EN7581_EFUSE_TEMP_OFFSET GENMASK(31, 16) ++#define EN7581_PTPSPARE1 0xf24 /* PTPSPARE1 */ ++#define EN7581_EFUSE_TEMP_CPU_SENSOR_REG 0xf28 /* PTPSPARE2 */ ++ ++#define EN7581_SLOPE_X100_DIO_DEFAULT 5645 ++#define EN7581_SLOPE_X100_DIO_AVS 5645 ++ ++#define EN7581_INIT_TEMP_CPK_X10 300 ++#define EN7581_INIT_TEMP_FTK_X10 620 ++#define EN7581_INIT_TEMP_NONK_X10 550 ++ ++#define EN7581_SCU_THERMAL_PROTECT_KEY 0x12 ++#define EN7581_SCU_THERMAL_MUX_DIODE1 0x7 ++ ++/* Convert temp to raw value as read from ADC ((((temp / 100) - init) * slope) / 1000) + offset */ ++#define TEMP_TO_RAW(priv, tz, temp) ((((((temp) / 100) - (priv)->init_temp) * \ ++ thermal_zone_get_slope(tz)) / 1000) + \ ++ thermal_zone_get_offset(tz)) ++ ++/* Convert raw to temp ((((temp - offset) * 1000) / slope + init) * 100) */ ++#define RAW_TO_TEMP(priv, tz, raw) (((((raw) - thermal_zone_get_offset(tz)) * 1000) / \ ++ thermal_zone_get_slope(tz) + \ ++ (priv)->init_temp) * 100) ++ ++struct airoha_thermal_priv { ++ void __iomem *base; ++ struct regmap *chip_scu; ++ struct resource scu_adc_res; ++ ++ struct thermal_zone_device *tz; ++ int init_temp; ++}; ++ ++static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv) ++{ ++ u32 val; ++ ++ regmap_read(priv->chip_scu, EN7581_DOUT_TADC, &val); ++ return FIELD_GET(EN7581_DOUT_TADC_MASK, val); ++} ++ ++static void airoha_init_thermal_ADC_mode(struct airoha_thermal_priv *priv) ++{ ++ u32 adc_mux, pllrg; ++ ++ /* Save PLLRG current value */ ++ regmap_read(priv->chip_scu, EN7581_PLLRG_PROTECT, &pllrg); ++ ++ /* Give access to thermal regs */ ++ regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, EN7581_SCU_THERMAL_PROTECT_KEY); ++ adc_mux = FIELD_PREP(EN7581_MUX_TADC, EN7581_SCU_THERMAL_MUX_DIODE1); ++ regmap_write(priv->chip_scu, EN7581_PWD_TADC, adc_mux); ++ ++ /* Restore PLLRG value on exit */ ++ regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg); ++} ++ ++static int airoha_thermal_get_temp(struct thermal_zone_device *tz, int *temp) ++{ ++ struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz); ++ int min, max, avg_temp, temp_adc; ++ int i; ++ ++ /* Get the starting temp */ ++ temp_adc = airoha_get_thermal_ADC(priv); ++ min = temp_adc; ++ max = temp_adc; ++ avg_temp = temp_adc; ++ ++ /* Make 5 more measurement and average the temp ADC difference */ ++ for (i = 0; i < 5; i++) { ++ temp_adc = airoha_get_thermal_ADC(priv); ++ avg_temp += temp_adc; ++ if (temp_adc > max) ++ max = temp_adc; ++ if (temp_adc < min) ++ min = temp_adc; ++ } ++ avg_temp = avg_temp - max - min; ++ avg_temp /= 4; ++ ++ *temp = RAW_TO_TEMP(priv, tz, avg_temp); ++ return 0; ++} ++ ++static int airoha_thermal_set_trips(struct thermal_zone_device *tz, int low, ++ int high) ++{ ++ struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz); ++ ++ if (high != INT_MAX) { ++ /* Validate high and clamp them a sane value */ ++ if (high > RAW_TO_TEMP(priv, tz, FIELD_MAX(EN7581_DOUT_TADC_MASK))) ++ high = 110000; ++ ++ /* We offset the high temp of 1°C to trigger correct event */ ++ writel(TEMP_TO_RAW(priv, tz, high) >> 4, ++ priv->base + EN7581_TEMPOFFSETH); ++ } ++ ++ if (low != -INT_MAX) { ++ /* Validate low and clamp them to a sane value */ ++ if (low < RAW_TO_TEMP(priv, tz, 0)) ++ low = -33000; ++ ++ /* We offset the low temp of 1°C to trigger correct event */ ++ writel(TEMP_TO_RAW(priv, tz, low) >> 4, ++ priv->base + EN7581_TEMPOFFSETL); ++ } ++ ++ /* Enable sensor 0 monitor */ ++ writel(EN7581_SENSE0_EN, priv->base + EN7581_TEMPMONCTL0); ++ ++ return 0; ++} ++ ++static const struct thermal_zone_device_ops thdev_ops = { ++ .get_temp = airoha_thermal_get_temp, ++ .set_trips = airoha_thermal_set_trips, ++}; ++ ++static irqreturn_t airoha_thermal_irq(int irq, void *data) ++{ ++ struct airoha_thermal_priv *priv = data; ++ enum thermal_notify_event event; ++ u32 status; ++ ++ status = readl(priv->base + EN7581_TEMPMONINTSTS); ++ switch (status & (EN7581_HOFSINTSTS0 | EN7581_LOFSINTSTS0)) { ++ case EN7581_HOFSINTSTS0: ++ event = THERMAL_TRIP_VIOLATED; ++ break; ++ case EN7581_LOFSINTSTS0: ++ event = THERMAL_EVENT_UNSPECIFIED; ++ break; ++ default: ++ goto exit; ++ } ++ ++ thermal_zone_device_update(priv->tz, event); ++ ++exit: ++ /* reset interrupt */ ++ writel(status, priv->base + EN7581_TEMPMONINTSTS); ++ ++ return IRQ_HANDLED; ++} ++ ++static void airoha_thermal_setup_adc_val(struct device *dev, ++ struct airoha_thermal_priv *priv, ++ struct thermal_zone_params *tzp) ++{ ++ u32 efuse_calib_info, cpu_sensor; ++ ++ /* Setup thermal sensor to ADC mode and setup the mux to DIODE1 */ ++ airoha_init_thermal_ADC_mode(priv); ++ /* sleep 10 ms for ADC to enable */ ++ usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC); ++ ++ efuse_calib_info = readl(priv->base + EN7581_EFUSE_TEMP_OFFSET_REG); ++ if (efuse_calib_info) { ++ tzp->offset = FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse_calib_info); ++ /* Different slope are applied if the sensor is used for CPU or for package */ ++ cpu_sensor = readl(priv->base + EN7581_EFUSE_TEMP_CPU_SENSOR_REG); ++ if (cpu_sensor) { ++ tzp->slope = EN7581_SLOPE_X100_DIO_DEFAULT; ++ priv->init_temp = EN7581_INIT_TEMP_FTK_X10; ++ } else { ++ tzp->slope = EN7581_SLOPE_X100_DIO_AVS; ++ priv->init_temp = EN7581_INIT_TEMP_CPK_X10; ++ } ++ } else { ++ tzp->offset = airoha_get_thermal_ADC(priv); ++ tzp->slope = EN7581_SLOPE_X100_DIO_DEFAULT; ++ priv->init_temp = EN7581_INIT_TEMP_NONK_X10; ++ dev_info(dev, "missing thermal calibrarion EFUSE, using non calibrated value\n"); ++ } ++} ++ ++static void airoha_thermal_setup_monitor(struct airoha_thermal_priv *priv) ++{ ++ /* Set measure mode */ ++ writel(FIELD_PREP(EN7581_MSRCTL0, EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4), ++ priv->base + EN7581_TEMPMSRCTL0); ++ ++ /* ++ * Configure ADC valid reading addr ++ * The AHB temp monitor system doesn't have direct access to the ++ * thermal sensor. It does instead work by providing all kind of ++ * address to configure how to access and setup an ADC for the ++ * sensor. EN7581 supports only one sensor hence the ++ * implementation is greatly simplified but the AHB supports ++ * up to 4 different sensor from the same ADC that can be ++ * switched by tuning the ADC mux or wiriting address. ++ * ++ * We set valid instead of volt as we don't enable valid/volt ++ * split reading and AHB read valid addr in such case. ++ */ ++ writel(priv->scu_adc_res.start + EN7581_DOUT_TADC, ++ priv->base + EN7581_TEMPADCVALIDADDR); ++ ++ /* ++ * Configure valid bit on a fake value of bit 16. The ADC outputs ++ * max of 2 bytes for voltage. ++ */ ++ writel(FIELD_PREP(EN7581_ADV_RD_VALID_POS, 16), ++ priv->base + EN7581_TEMPADCVALIDMASK); ++ ++ /* ++ * AHB supports max 12 bytes for ADC voltage. Shift the read ++ * value 4 bit to the right. Precision lost by this is minimal ++ * in the order of half a °C and is acceptable in the context ++ * of triggering interrupt in critical condition. ++ */ ++ writel(FIELD_PREP(EN7581_ADC_VOLTAGE_SHIFT, 4), ++ priv->base + EN7581_TEMPADCVOLTAGESHIFT); ++ ++ /* BUS clock is 300MHz counting unit is 3 * 68.64 * 256 = 52.715us */ ++ writel(FIELD_PREP(EN7581_PERIOD_UNIT, 3), ++ priv->base + EN7581_TEMPMONCTL1); ++ ++ /* ++ * filt interval is 1 * 52.715us = 52.715us, ++ * sen interval is 379 * 52.715us = 19.97ms ++ */ ++ writel(FIELD_PREP(EN7581_FILT_INTERVAL, 1) | ++ FIELD_PREP(EN7581_FILT_INTERVAL, 379), ++ priv->base + EN7581_TEMPMONCTL2); ++ ++ /* AHB poll is set to 146 * 68.64 = 10.02us */ ++ writel(FIELD_PREP(EN7581_ADC_POLL_INTVL, 146), ++ priv->base + EN7581_TEMPAHBPOLL); ++} ++ ++static int airoha_thermal_probe(struct platform_device *pdev) ++{ ++ struct thermal_zone_params tzp = { }; ++ struct airoha_thermal_priv *priv; ++ struct device_node *chip_scu_np; ++ struct device *dev = &pdev->dev; ++ int irq, ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ chip_scu_np = of_parse_phandle(dev->of_node, "airoha,chip-scu", 0); ++ if (!chip_scu_np) ++ return -EINVAL; ++ ++ priv->chip_scu = syscon_node_to_regmap(chip_scu_np); ++ if (IS_ERR(priv->chip_scu)) ++ return PTR_ERR(priv->chip_scu); ++ ++ of_address_to_resource(chip_scu_np, 0, &priv->scu_adc_res); ++ of_node_put(chip_scu_np); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, ++ airoha_thermal_irq, IRQF_ONESHOT, ++ pdev->name, (void *)priv); ++ if (ret) { ++ dev_err(dev, "Can't get interrupt working.\n"); ++ return ret; ++ } ++ ++ airoha_thermal_setup_monitor(priv); ++ airoha_thermal_setup_adc_val(dev, priv, &tzp); ++ ++ /* register of thermal sensor and get info from DT */ ++ priv->tz = devm_thermal_of_zone_register_with_params(dev, 0, priv, ++ &thdev_ops, ++ &tzp); ++ if (IS_ERR(priv->tz)) { ++ dev_err(dev, "register thermal zone sensor failed\n"); ++ return PTR_ERR(priv->tz); ++ } ++ ++ platform_set_drvdata(pdev, priv); ++ ++ /* Enable LOW and HIGH interrupt */ ++ writel(EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0, ++ priv->base + EN7581_TEMPMONINT); ++ ++ return 0; ++} ++ ++static const struct of_device_id airoha_thermal_match[] = { ++ { .compatible = "airoha,en7581-thermal" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, airoha_thermal_match); ++ ++static struct platform_driver airoha_thermal_driver = { ++ .driver = { ++ .name = "airoha-thermal", ++ .of_match_table = airoha_thermal_match, ++ }, ++ .probe = airoha_thermal_probe, ++}; ++ ++module_platform_driver(airoha_thermal_driver); ++ ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Airoha thermal driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.6/102-hwrng-add-support-for-Airoha-EN7581-TRNG.patch b/target/linux/airoha/patches-6.6/102-hwrng-add-support-for-Airoha-EN7581-TRNG.patch new file mode 100644 index 0000000000..1c99369fcb --- /dev/null +++ b/target/linux/airoha/patches-6.6/102-hwrng-add-support-for-Airoha-EN7581-TRNG.patch @@ -0,0 +1,304 @@ +From 9dbd16ac89e00bd8640ecac3971b0943410b5cec Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 6 Jul 2024 01:15:24 +0200 +Subject: [PATCH 2/2] hwrng: add support for Airoha EN7581 TRNG + +Add support for Airoha TRNG. The Airoha SoC provide a True RNG module +that can output 4 bytes of raw data at times. + +The module makes use of various noise source to provide True Random +Number Generation. + +On probe the module is reset to operate Health Test and verify correct +execution of it. + +The module can also provide DRBG function but the execution mode is +mutually exclusive, running as TRNG doesn't permit to also run it as +DRBG. + +Signed-off-by: Christian Marangi +--- + drivers/char/hw_random/Kconfig | 13 ++ + drivers/char/hw_random/Makefile | 1 + + drivers/char/hw_random/airoha-trng.c | 243 +++++++++++++++++++++++++++ + 3 files changed, 257 insertions(+) + create mode 100644 drivers/char/hw_random/airoha-trng.c + +--- a/drivers/char/hw_random/Kconfig ++++ b/drivers/char/hw_random/Kconfig +@@ -62,6 +62,19 @@ config HW_RANDOM_AMD + + If unsure, say Y. + ++config HW_RANDOM_AIROHA ++ tristate "Airoha True HW Random Number Generator support" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ default HW_RANDOM ++ help ++ This driver provides kernel-side support for the True Random Number ++ Generator hardware found on Airoha SoC. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called airoha-rng. ++ ++ If unsure, say Y. ++ + config HW_RANDOM_ATMEL + tristate "Atmel Random Number Generator support" + depends on (ARCH_AT91 || COMPILE_TEST) +--- a/drivers/char/hw_random/Makefile ++++ b/drivers/char/hw_random/Makefile +@@ -8,6 +8,7 @@ rng-core-y := core.o + obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o + obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o + obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o ++obj-$(CONFIG_HW_RANDOM_AIROHA) += airoha-trng.o + obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o + obj-$(CONFIG_HW_RANDOM_BA431) += ba431-rng.o + obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o +--- /dev/null ++++ b/drivers/char/hw_random/airoha-trng.c +@@ -0,0 +1,243 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2024 Christian Marangi */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TRNG_IP_RDY 0x800 ++#define CNT_TRANS GENMASK(15, 8) ++#define SAMPLE_RDY BIT(0) ++#define TRNG_NS_SEK_AND_DAT_EN 0x804 ++#define RNG_EN BIT(31) /* referenced as ring_en */ ++#define RAW_DATA_EN BIT(16) ++#define TRNG_HEALTH_TEST_SW_RST 0x808 ++#define SW_RST BIT(0) /* Active High */ ++#define TRNG_INTR_EN 0x818 ++#define INTR_MASK BIT(16) ++#define CONTINUOUS_HEALTH_INITR_EN BIT(2) ++#define SW_STARTUP_INITR_EN BIT(1) ++#define RST_STARTUP_INITR_EN BIT(0) ++/* Notice that Health Test are done only out of Reset and with RNG_EN */ ++#define TRNG_HEALTH_TEST_STATUS 0x824 ++#define CONTINUOUS_HEALTH_AP_TEST_FAIL BIT(23) ++#define CONTINUOUS_HEALTH_RC_TEST_FAIL BIT(22) ++#define SW_STARTUP_TEST_DONE BIT(21) ++#define SW_STARTUP_AP_TEST_FAIL BIT(20) ++#define SW_STARTUP_RC_TEST_FAIL BIT(19) ++#define RST_STARTUP_TEST_DONE BIT(18) ++#define RST_STARTUP_AP_TEST_FAIL BIT(17) ++#define RST_STARTUP_RC_TEST_FAIL BIT(16) ++#define RAW_DATA_VALID BIT(7) ++ ++#define TRNG_RAW_DATA_OUT 0x828 ++ ++#define TRNG_CNT_TRANS_VALID 0x80 ++#define BUSY_LOOP_SLEEP 10 ++#define BUSY_LOOP_TIMEOUT (BUSY_LOOP_SLEEP * 10000) ++ ++struct airoha_trng { ++ void __iomem *base; ++ struct hwrng rng; ++ struct device *dev; ++ ++ struct completion rng_op_done; ++}; ++ ++static int airoha_trng_irq_mask(struct airoha_trng *trng) ++{ ++ u32 val; ++ ++ val = readl(trng->base + TRNG_INTR_EN); ++ val |= INTR_MASK; ++ writel(val, trng->base + TRNG_INTR_EN); ++ ++ return 0; ++} ++ ++static int airoha_trng_irq_unmask(struct airoha_trng *trng) ++{ ++ u32 val; ++ ++ val = readl(trng->base + TRNG_INTR_EN); ++ val &= ~INTR_MASK; ++ writel(val, trng->base + TRNG_INTR_EN); ++ ++ return 0; ++} ++ ++static int airoha_trng_init(struct hwrng *rng) ++{ ++ struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng); ++ int ret; ++ u32 val; ++ ++ val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ val |= RNG_EN; ++ writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ ++ /* Set out of SW Reset */ ++ airoha_trng_irq_unmask(trng); ++ writel(0, trng->base + TRNG_HEALTH_TEST_SW_RST); ++ ++ ret = wait_for_completion_timeout(&trng->rng_op_done, BUSY_LOOP_TIMEOUT); ++ if (ret <= 0) { ++ dev_err(trng->dev, "Timeout waiting for Health Check\n"); ++ airoha_trng_irq_mask(trng); ++ return -ENODEV; ++ } ++ ++ /* Check if Health Test Failed */ ++ val = readl(trng->base + TRNG_HEALTH_TEST_STATUS); ++ if (val & (RST_STARTUP_AP_TEST_FAIL | RST_STARTUP_RC_TEST_FAIL)) { ++ dev_err(trng->dev, "Health Check fail: %s test fail\n", ++ val & RST_STARTUP_AP_TEST_FAIL ? "AP" : "RC"); ++ return -ENODEV; ++ } ++ ++ /* Check if IP is ready */ ++ ret = readl_poll_timeout(trng->base + TRNG_IP_RDY, val, ++ val & SAMPLE_RDY, 10, 1000); ++ if (ret < 0) { ++ dev_err(trng->dev, "Timeout waiting for IP ready"); ++ return -ENODEV; ++ } ++ ++ /* CNT_TRANS must be 0x80 for IP to be considered ready */ ++ ret = readl_poll_timeout(trng->base + TRNG_IP_RDY, val, ++ FIELD_GET(CNT_TRANS, val) == TRNG_CNT_TRANS_VALID, ++ 10, 1000); ++ if (ret < 0) { ++ dev_err(trng->dev, "Timeout waiting for IP ready"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static void airoha_trng_cleanup(struct hwrng *rng) ++{ ++ struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng); ++ u32 val; ++ ++ val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ val &= ~RNG_EN; ++ writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ ++ /* Put it in SW Reset */ ++ writel(SW_RST, trng->base + TRNG_HEALTH_TEST_SW_RST); ++} ++ ++static int airoha_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) ++{ ++ struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng); ++ u32 *data = buf; ++ u32 status; ++ int ret; ++ ++ ret = readl_poll_timeout(trng->base + TRNG_HEALTH_TEST_STATUS, status, ++ status & RAW_DATA_VALID, 10, 1000); ++ if (ret < 0) { ++ dev_err(trng->dev, "Timeout waiting for TRNG RAW Data valid\n"); ++ return ret; ++ } ++ ++ *data = readl(trng->base + TRNG_RAW_DATA_OUT); ++ ++ return 4; ++} ++ ++static irqreturn_t airoha_trng_irq(int irq, void *priv) ++{ ++ struct airoha_trng *trng = (struct airoha_trng *)priv; ++ ++ airoha_trng_irq_mask(trng); ++ /* Just complete the task, we will read the value later */ ++ complete(&trng->rng_op_done); ++ ++ return IRQ_HANDLED; ++} ++ ++static int airoha_trng_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct airoha_trng *trng; ++ int irq, ret; ++ u32 val; ++ ++ trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL); ++ if (!trng) ++ return -ENOMEM; ++ ++ trng->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(trng->base)) ++ return PTR_ERR(trng->base); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ airoha_trng_irq_mask(trng); ++ ret = devm_request_irq(&pdev->dev, irq, airoha_trng_irq, 0, ++ pdev->name, (void *)trng); ++ if (ret) { ++ dev_err(dev, "Can't get interrupt working.\n"); ++ return ret; ++ } ++ ++ init_completion(&trng->rng_op_done); ++ ++ /* Enable interrupt for SW reset Health Check */ ++ val = readl(trng->base + TRNG_INTR_EN); ++ val |= RST_STARTUP_INITR_EN; ++ writel(val, trng->base + TRNG_INTR_EN); ++ ++ /* Set output to raw data */ ++ val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ val |= RAW_DATA_EN; ++ writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ ++ /* Put it in SW Reset */ ++ writel(SW_RST, trng->base + TRNG_HEALTH_TEST_SW_RST); ++ ++ trng->dev = dev; ++ trng->rng.name = pdev->name; ++ trng->rng.init = airoha_trng_init; ++ trng->rng.cleanup = airoha_trng_cleanup; ++ trng->rng.read = airoha_trng_read; ++ ++ ret = devm_hwrng_register(dev, &trng->rng); ++ if (ret) { ++ dev_err(dev, "failed to register rng device: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id airoha_trng_of_match[] = { ++ { .compatible = "airoha,en7581-trng", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, airoha_trng_of_match); ++ ++static struct platform_driver airoha_trng_driver = { ++ .driver = { ++ .name = "airoha-trng", ++ .of_match_table = airoha_trng_of_match, ++ }, ++ .probe = airoha_trng_probe, ++}; ++ ++module_platform_driver(airoha_trng_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Airoha True Random Number Generator driver"); diff --git a/target/linux/airoha/patches-6.6/103-watchdog-Add-support-for-Airoha-EN7851-watchdog.patch b/target/linux/airoha/patches-6.6/103-watchdog-Add-support-for-Airoha-EN7851-watchdog.patch new file mode 100644 index 0000000000..e58ee54609 --- /dev/null +++ b/target/linux/airoha/patches-6.6/103-watchdog-Add-support-for-Airoha-EN7851-watchdog.patch @@ -0,0 +1,268 @@ +From 4019d58ca5b249e4cf79169cc0c6a4ff5275c155 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 5 Jul 2024 19:12:12 +0200 +Subject: [PATCH v2 2/2] watchdog: Add support for Airoha EN7851 watchdog + +Add support for Airoha EN7851 watchdog. This is a very basic watchdog +with no pretimeout support, max timeout is 28 seconds and it ticks based +on half the SoC BUS clock. + +Signed-off-by: Christian Marangi +--- +Changes v2: +- Drop clock-frequency implementation +- Add missing bitfield.h header +- Attach BUS clock + + drivers/watchdog/Kconfig | 8 ++ + drivers/watchdog/Makefile | 1 + + drivers/watchdog/airoha_wdt.c | 216 ++++++++++++++++++++++++++++++++++ + 3 files changed, 225 insertions(+) + create mode 100644 drivers/watchdog/airoha_wdt.c + +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -372,6 +372,14 @@ config SL28CPLD_WATCHDOG + + # ARM Architecture + ++config AIROHA_WATCHDOG ++ tristate "Airoha EN7581 Watchdog" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ select WATCHDOG_CORE ++ help ++ Watchdog timer embedded into Airoha SoC. This will reboot your ++ system when the timeout is reached. ++ + config ARM_SP805_WATCHDOG + tristate "ARM SP805 Watchdog" + depends on (ARM || ARM64 || COMPILE_TEST) && ARM_AMBA +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -40,6 +40,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb. + obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o + obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o + obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o ++obj-$(CONFIG_AIROHA_WATCHDOG) += airoha_wdt.o + obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o + obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o + obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o +--- /dev/null ++++ b/drivers/watchdog/airoha_wdt.c +@@ -0,0 +1,216 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Airoha Watchdog Driver ++ * ++ * Copyright (c) 2024, AIROHA All rights reserved. ++ * ++ * Mayur Kumar ++ * Christian Marangi ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Base address of timer and watchdog registers */ ++#define TIMER_CTRL 0x0 ++#define WDT_ENABLE BIT(25) ++#define WDT_TIMER_INTERRUPT BIT(21) ++/* Timer3 is used as Watchdog Timer */ ++#define WDT_TIMER_ENABLE BIT(5) ++#define WDT_TIMER_LOAD_VALUE 0x2c ++#define WDT_TIMER_CUR_VALUE 0x30 ++#define WDT_TIMER_VAL GENMASK(31, 0) ++#define WDT_RELOAD 0x38 ++#define WDT_RLD BIT(0) ++ ++/* Airoha watchdog structure description */ ++struct airoha_wdt_desc { ++ struct watchdog_device wdog_dev; ++ unsigned int wdt_freq; ++ void __iomem *base; ++}; ++ ++#define WDT_HEARTBEAT 24 ++static int heartbeat = WDT_HEARTBEAT; ++module_param(heartbeat, int, 0); ++MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. (default=" ++ __MODULE_STRING(WDT_HEARTBEAT) ")"); ++ ++static bool nowayout = WATCHDOG_NOWAYOUT; ++module_param(nowayout, bool, 0); ++MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" ++ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); ++ ++static int airoha_wdt_start(struct watchdog_device *wdog_dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); ++ u32 val; ++ ++ val = readl(airoha_wdt->base + TIMER_CTRL); ++ val |= (WDT_TIMER_ENABLE | WDT_ENABLE | WDT_TIMER_INTERRUPT); ++ writel(val, airoha_wdt->base + TIMER_CTRL); ++ val = wdog_dev->timeout * airoha_wdt->wdt_freq; ++ writel(val, airoha_wdt->base + WDT_TIMER_LOAD_VALUE); ++ ++ return 0; ++} ++ ++static int airoha_wdt_stop(struct watchdog_device *wdog_dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); ++ u32 val; ++ ++ val = readl(airoha_wdt->base + TIMER_CTRL); ++ val &= (~WDT_ENABLE & ~WDT_TIMER_ENABLE); ++ writel(val, airoha_wdt->base + TIMER_CTRL); ++ ++ return 0; ++} ++ ++static int airoha_wdt_ping(struct watchdog_device *wdog_dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); ++ u32 val; ++ ++ val = readl(airoha_wdt->base + WDT_RELOAD); ++ val |= WDT_RLD; ++ writel(val, airoha_wdt->base + WDT_RELOAD); ++ ++ return 0; ++} ++ ++static int airoha_wdt_set_timeout(struct watchdog_device *wdog_dev, unsigned int timeout) ++{ ++ wdog_dev->timeout = timeout; ++ ++ if (watchdog_active(wdog_dev)) { ++ airoha_wdt_stop(wdog_dev); ++ return airoha_wdt_start(wdog_dev); ++ } ++ ++ return 0; ++} ++ ++static unsigned int airoha_wdt_get_timeleft(struct watchdog_device *wdog_dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); ++ u32 val; ++ ++ val = readl(airoha_wdt->base + WDT_TIMER_CUR_VALUE); ++ return DIV_ROUND_UP(val, airoha_wdt->wdt_freq); ++} ++ ++static const struct watchdog_info airoha_wdt_info = { ++ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, ++ .identity = "Airoha Watchdog", ++}; ++ ++static const struct watchdog_ops airoha_wdt_ops = { ++ .owner = THIS_MODULE, ++ .start = airoha_wdt_start, ++ .stop = airoha_wdt_stop, ++ .ping = airoha_wdt_ping, ++ .set_timeout = airoha_wdt_set_timeout, ++ .get_timeleft = airoha_wdt_get_timeleft, ++}; ++ ++static int airoha_wdt_probe(struct platform_device *pdev) ++{ ++ struct airoha_wdt_desc *airoha_wdt; ++ struct watchdog_device *wdog_dev; ++ struct device *dev = &pdev->dev; ++ struct clk *bus_clk; ++ int ret; ++ ++ airoha_wdt = devm_kzalloc(dev, sizeof(*airoha_wdt), GFP_KERNEL); ++ if (!airoha_wdt) ++ return -ENOMEM; ++ ++ airoha_wdt->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(airoha_wdt->base)) ++ return PTR_ERR(airoha_wdt->base); ++ ++ bus_clk = devm_clk_get_enabled(dev, "bus"); ++ if (IS_ERR(bus_clk)) ++ return dev_err_probe(dev, PTR_ERR(bus_clk), ++ "failed to enable bus clock\n"); ++ ++ /* Watchdog ticks at half the bus rate */ ++ airoha_wdt->wdt_freq = clk_get_rate(bus_clk) / 2; ++ ++ /* Initialize struct watchdog device */ ++ wdog_dev = &airoha_wdt->wdog_dev; ++ wdog_dev->timeout = heartbeat; ++ wdog_dev->info = &airoha_wdt_info; ++ wdog_dev->ops = &airoha_wdt_ops; ++ /* Bus 300MHz, watchdog 150MHz, 28 seconds */ ++ wdog_dev->max_timeout = FIELD_MAX(WDT_TIMER_VAL) / airoha_wdt->wdt_freq; ++ wdog_dev->parent = dev; ++ ++ watchdog_set_drvdata(wdog_dev, airoha_wdt); ++ watchdog_set_nowayout(wdog_dev, nowayout); ++ watchdog_stop_on_unregister(wdog_dev); ++ ++ ret = devm_watchdog_register_device(dev, wdog_dev); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, airoha_wdt); ++ return 0; ++} ++ ++static int airoha_wdt_suspend(struct device *dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev); ++ ++ if (watchdog_active(&airoha_wdt->wdog_dev)) ++ airoha_wdt_stop(&airoha_wdt->wdog_dev); ++ ++ return 0; ++} ++ ++static int airoha_wdt_resume(struct device *dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev); ++ ++ if (watchdog_active(&airoha_wdt->wdog_dev)) { ++ airoha_wdt_start(&airoha_wdt->wdog_dev); ++ airoha_wdt_ping(&airoha_wdt->wdog_dev); ++ } ++ return 0; ++} ++ ++static const struct of_device_id airoha_wdt_of_match[] = { ++ { .compatible = "airoha,en7581-wdt", }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, airoha_wdt_of_match); ++ ++static DEFINE_SIMPLE_DEV_PM_OPS(airoha_wdt_pm_ops, airoha_wdt_suspend, airoha_wdt_resume); ++ ++static struct platform_driver airoha_wdt_driver = { ++ .probe = airoha_wdt_probe, ++ .driver = { ++ .name = "airoha-wdt", ++ .pm = pm_sleep_ptr(&airoha_wdt_pm_ops), ++ .of_match_table = airoha_wdt_of_match, ++ }, ++}; ++ ++module_platform_driver(airoha_wdt_driver); ++ ++MODULE_AUTHOR("Mayur Kumar "); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Airoha EN7581 Watchdog Driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.6/104-i2c-mt7621-optional-reset.patch b/target/linux/airoha/patches-6.6/104-i2c-mt7621-optional-reset.patch new file mode 100644 index 0000000000..1fad1bdd01 --- /dev/null +++ b/target/linux/airoha/patches-6.6/104-i2c-mt7621-optional-reset.patch @@ -0,0 +1,11 @@ +--- a/drivers/i2c/busses/i2c-mt7621.c ++++ b/drivers/i2c/busses/i2c-mt7621.c +@@ -85,7 +85,7 @@ static void mtk_i2c_reset(struct mtk_i2c + { + int ret; + +- ret = device_reset(i2c->adap.dev.parent); ++ ret = device_reset_optional(i2c->adap.dev.parent); + if (ret) + dev_err(i2c->dev, "I2C reset failed!\n"); + diff --git a/target/linux/airoha/patches-6.6/105-uart-add-en7523-support.patch b/target/linux/airoha/patches-6.6/105-uart-add-en7523-support.patch new file mode 100644 index 0000000000..38611b907c --- /dev/null +++ b/target/linux/airoha/patches-6.6/105-uart-add-en7523-support.patch @@ -0,0 +1,187 @@ +--- /dev/null ++++ b/drivers/tty/serial/8250/8250_en7523.c +@@ -0,0 +1,94 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Airoha EN7523 driver. ++ * ++ * Copyright (c) 2022 Genexis Sweden AB ++ * Author: Benjamin Larsson ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "8250.h" ++ ++ ++/* The Airoha UART is 16550-compatible except for the baud rate calculation. ++ * ++ * crystal_clock = 20 MHz ++ * xindiv_clock = crystal_clock / clock_div ++ * (x/y) = XYD, 32 bit register with 16 bits of x and and then 16 bits of y ++ * clock_div = XINCLK_DIVCNT (default set to 10 (0x4)), ++ * - 3 bit register [ 1, 2, 4, 8, 10, 12, 16, 20 ] ++ * ++ * baud_rate = ((xindiv_clock) * (x/y)) / ([BRDH,BRDL] * 16) ++ * ++ * XYD_y seems to need to be larger then XYD_x for things to work. ++ * Setting [BRDH,BRDL] to [0,1] and XYD_y to 65000 give even values ++ * for usual baud rates. ++ * ++ * Selecting divider needs to fulfill ++ * 1.8432 MHz <= xindiv_clk <= APB clock / 2 ++ * The clocks are unknown but a divider of value 1 did not work. ++ * ++ * Optimally the XYD, BRD and XINCLK_DIVCNT registers could be searched to ++ * find values that gives the least error for every baud rate. But searching ++ * the space takes time and in practise only a few rates are of interest. ++ * With some value combinations not working a tested subset is used giving ++ * a usable range from 110 to 460800 baud. ++ */ ++ ++#define CLOCK_DIV_TAB_ELEMS 3 ++#define XYD_Y 65000 ++#define XINDIV_CLOCK 20000000 ++#define UART_BRDL_20M 0x01 ++#define UART_BRDH_20M 0x00 ++ ++static int clock_div_tab[] = { 10, 4, 2}; ++static int clock_div_reg[] = { 4, 2, 1}; ++ ++ ++int en7523_set_uart_baud_rate (struct uart_port *port, unsigned int baud) ++{ ++ struct uart_8250_port *up = up_to_u8250p(port); ++ unsigned int xyd_x, nom, denom; ++ int i; ++ ++ /* set DLAB to access the baud rate divider registers (BRDH, BRDL) */ ++ serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB); ++ ++ /* set baud rate calculation defaults */ ++ ++ /* set BRDIV ([BRDH,BRDL]) to 1 */ ++ serial_port_out(port, UART_BRDL, UART_BRDL_20M); ++ serial_port_out(port, UART_BRDH, UART_BRDH_20M); ++ ++ /* calculate XYD_x and XINCLKDR register */ ++ ++ for (i = 0 ; i < CLOCK_DIV_TAB_ELEMS ; i++) { ++ denom = (XINDIV_CLOCK/40) / clock_div_tab[i]; ++ nom = (baud * (XYD_Y/40)); ++ xyd_x = ((nom/denom) << 4); ++ if (xyd_x < XYD_Y) break; ++ } ++ ++ serial_port_out(port, UART_XINCLKDR, clock_div_reg[i]); ++ serial_port_out(port, UART_XYD, (xyd_x<<16) | XYD_Y); ++ ++ /* unset DLAB */ ++ serial_port_out(port, UART_LCR, up->lcr); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(en7523_set_uart_baud_rate); +--- a/drivers/tty/serial/8250/8250_of.c ++++ b/drivers/tty/serial/8250/8250_of.c +@@ -338,6 +338,7 @@ static const struct of_device_id of_plat + { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, + { .compatible = "nuvoton,wpcm450-uart", .data = (void *)PORT_NPCM, }, + { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, ++ { .compatible = "airoha,en7523-uart", .data = (void *)PORT_AIROHA, }, + { /* end of list */ }, + }; + MODULE_DEVICE_TABLE(of, of_platform_serial_table); +--- a/drivers/tty/serial/8250/8250_port.c ++++ b/drivers/tty/serial/8250/8250_port.c +@@ -330,6 +330,14 @@ static const struct serial8250_config ua + .rxtrig_bytes = {1, 8, 16, 30}, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, ++ [PORT_AIROHA] = { ++ .name = "Airoha 16550", ++ .fifo_size = 8, ++ .tx_loadsz = 1, ++ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01, ++ .rxtrig_bytes = {1, 4}, ++ .flags = UART_CAP_FIFO, ++ }, + }; + + /* Uart divisor latch read */ +@@ -2880,6 +2888,12 @@ serial8250_do_set_termios(struct uart_po + + serial8250_set_divisor(port, baud, quot, frac); + ++#ifdef CONFIG_SERIAL_8250_AIROHA ++ /* Airoha SoCs have custom registers for baud rate settings */ ++ if (port->type == PORT_AIROHA) ++ en7523_set_uart_baud_rate(port, baud); ++#endif ++ + /* + * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR + * is written without DLAB set, this mode will be disabled. +--- a/drivers/tty/serial/8250/Makefile ++++ b/drivers/tty/serial/8250/Makefile +@@ -46,6 +46,7 @@ obj-$(CONFIG_SERIAL_8250_PERICOM) += 825 + obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o + obj-$(CONFIG_SERIAL_8250_TEGRA) += 8250_tegra.o + obj-$(CONFIG_SERIAL_8250_BCM7271) += 8250_bcm7271.o ++obj-$(CONFIG_SERIAL_8250_AIROHA) += 8250_en7523.o + obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o + + CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt +--- a/include/uapi/linux/serial_reg.h ++++ b/include/uapi/linux/serial_reg.h +@@ -382,5 +382,17 @@ + #define UART_ALTR_EN_TXFIFO_LW 0x01 /* Enable the TX FIFO Low Watermark */ + #define UART_ALTR_TX_LOW 0x41 /* Tx FIFO Low Watermark */ + ++/* ++ * These are definitions for the Airoha EN75XX uart registers ++ * Normalized because of 32 bits registers. ++ */ ++#define UART_BRDL 0 ++#define UART_BRDH 1 ++#define UART_XINCLKDR 10 ++#define UART_XYD 11 ++#define UART_TXLVLCNT 12 ++#define UART_RXLVLCNT 13 ++#define UART_FINTLVL 14 ++ + #endif /* _LINUX_SERIAL_REG_H */ + +--- a/include/uapi/linux/serial_core.h ++++ b/include/uapi/linux/serial_core.h +@@ -45,6 +45,7 @@ + #define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */ + #define PORT_RT2880 29 /* Ralink RT2880 internal UART */ + #define PORT_16550A_FSL64 30 /* Freescale 16550 UART with 64 FIFOs */ ++#define PORT_AIROHA 31 /* Airoha 16550 UART */ + + /* + * ARM specific type numbers. These are not currently guaranteed +--- a/include/linux/serial_8250.h ++++ b/include/linux/serial_8250.h +@@ -195,6 +195,7 @@ void serial8250_do_set_mctrl(struct uart + void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int quot_frac); + int fsl8250_handle_irq(struct uart_port *port); ++int en7523_set_uart_baud_rate(struct uart_port *port, unsigned int baud); + int serial8250_handle_irq(struct uart_port *port, unsigned int iir); + u16 serial8250_rx_chars(struct uart_8250_port *up, u16 lsr); + void serial8250_read_char(struct uart_8250_port *up, u16 lsr); diff --git a/target/linux/airoha/patches-6.6/106-01-clk-en7523-remove-REG_PCIE-_-MEM-MEM_MASK-configurat.patch b/target/linux/airoha/patches-6.6/106-01-clk-en7523-remove-REG_PCIE-_-MEM-MEM_MASK-configurat.patch new file mode 100644 index 0000000000..d2e0c6d541 --- /dev/null +++ b/target/linux/airoha/patches-6.6/106-01-clk-en7523-remove-REG_PCIE-_-MEM-MEM_MASK-configurat.patch @@ -0,0 +1,60 @@ +From 64e497f372dfca3e6be9fe05a0f9b874ea8604d2 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 3 Sep 2024 23:39:46 +0200 +Subject: [PATCH 1/6] clk: en7523: remove REG_PCIE*_{MEM,MEM_MASK} + configuration + +REG_PCIE*_MEM and REG_PCIE*_MEM_MASK regs (PBUS_CSR memory region) are not +part of the scu block on the EN7581 SoC and they are used to select the +PCIE ports on the PBUS, so remove this configuration from the clock driver +and set these registers in the PCIE host driver instead. +This patch does not introduce any backward incompatibility since the dts +for EN7581 SoC is not upstream yet. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/clk/clk-en7523.c | 18 ------------------ + 1 file changed, 18 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -31,12 +31,6 @@ + #define REG_RESET_CONTROL_PCIE1 BIT(27) + #define REG_RESET_CONTROL_PCIE2 BIT(26) + /* EN7581 */ +-#define REG_PCIE0_MEM 0x00 +-#define REG_PCIE0_MEM_MASK 0x04 +-#define REG_PCIE1_MEM 0x08 +-#define REG_PCIE1_MEM_MASK 0x0c +-#define REG_PCIE2_MEM 0x10 +-#define REG_PCIE2_MEM_MASK 0x14 + #define REG_NP_SCU_PCIC 0x88 + #define REG_NP_SCU_SSTR 0x9c + #define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13) +@@ -415,26 +409,14 @@ static void en7581_pci_disable(struct cl + static int en7581_clk_hw_init(struct platform_device *pdev, + void __iomem *np_base) + { +- void __iomem *pb_base; + u32 val; + +- pb_base = devm_platform_ioremap_resource(pdev, 3); +- if (IS_ERR(pb_base)) +- return PTR_ERR(pb_base); +- + val = readl(np_base + REG_NP_SCU_SSTR); + val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); + writel(val, np_base + REG_NP_SCU_SSTR); + val = readl(np_base + REG_NP_SCU_PCIC); + writel(val | 3, np_base + REG_NP_SCU_PCIC); + +- writel(0x20000000, pb_base + REG_PCIE0_MEM); +- writel(0xfc000000, pb_base + REG_PCIE0_MEM_MASK); +- writel(0x24000000, pb_base + REG_PCIE1_MEM); +- writel(0xfc000000, pb_base + REG_PCIE1_MEM_MASK); +- writel(0x28000000, pb_base + REG_PCIE2_MEM); +- writel(0xfc000000, pb_base + REG_PCIE2_MEM_MASK); +- + return 0; + } + diff --git a/target/linux/airoha/patches-6.6/106-02-clk-en7523-move-clock_register-in-hw_init-callback.patch b/target/linux/airoha/patches-6.6/106-02-clk-en7523-move-clock_register-in-hw_init-callback.patch new file mode 100644 index 0000000000..44bfc4404b --- /dev/null +++ b/target/linux/airoha/patches-6.6/106-02-clk-en7523-move-clock_register-in-hw_init-callback.patch @@ -0,0 +1,144 @@ +From 0dd8a6df58a4a8cf1f341249e7358b3bb51f52ad Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 3 Sep 2024 23:39:47 +0200 +Subject: [PATCH 2/6] clk: en7523: move clock_register in hw_init callback + +Move en7523_register_clocks routine in hw_init callback. +Introduce en7523_clk_hw_init callback for EN7523 SoC. +This is a preliminary patch to differentiate IO mapped region between +EN7523 and EN7581 SoCs in order to access chip-scu IO region +<0x1fa20000 0x384> on EN7581 SoC as syscon device since it contains +miscellaneous registers needed by multiple devices (clock, pinctrl ..). + +Signed-off-by: Lorenzo Bianconi +--- + drivers/clk/clk-en7523.c | 82 ++++++++++++++++++++++++---------------- + 1 file changed, 50 insertions(+), 32 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -78,7 +78,8 @@ struct en_clk_soc_data { + const u16 *idx_map; + u16 idx_map_nr; + } reset; +- int (*hw_init)(struct platform_device *pdev, void __iomem *np_base); ++ int (*hw_init)(struct platform_device *pdev, ++ struct clk_hw_onecell_data *clk_data); + }; + + static const u32 gsw_base[] = { 400000000, 500000000 }; +@@ -406,20 +407,6 @@ static void en7581_pci_disable(struct cl + usleep_range(1000, 2000); + } + +-static int en7581_clk_hw_init(struct platform_device *pdev, +- void __iomem *np_base) +-{ +- u32 val; +- +- val = readl(np_base + REG_NP_SCU_SSTR); +- val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); +- writel(val, np_base + REG_NP_SCU_SSTR); +- val = readl(np_base + REG_NP_SCU_PCIC); +- writel(val | 3, np_base + REG_NP_SCU_PCIC); +- +- return 0; +-} +- + static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, + void __iomem *base, void __iomem *np_base) + { +@@ -449,6 +436,49 @@ static void en7523_register_clocks(struc + clk_data->num = EN7523_NUM_CLOCKS; + } + ++static int en7523_clk_hw_init(struct platform_device *pdev, ++ struct clk_hw_onecell_data *clk_data) ++{ ++ void __iomem *base, *np_base; ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ np_base = devm_platform_ioremap_resource(pdev, 1); ++ if (IS_ERR(np_base)) ++ return PTR_ERR(np_base); ++ ++ en7523_register_clocks(&pdev->dev, clk_data, base, np_base); ++ ++ return 0; ++} ++ ++static int en7581_clk_hw_init(struct platform_device *pdev, ++ struct clk_hw_onecell_data *clk_data) ++{ ++ void __iomem *base, *np_base; ++ u32 val; ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ np_base = devm_platform_ioremap_resource(pdev, 1); ++ if (IS_ERR(np_base)) ++ return PTR_ERR(np_base); ++ ++ en7523_register_clocks(&pdev->dev, clk_data, base, np_base); ++ ++ val = readl(np_base + REG_NP_SCU_SSTR); ++ val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); ++ writel(val, np_base + REG_NP_SCU_SSTR); ++ val = readl(np_base + REG_NP_SCU_PCIC); ++ writel(val | 3, np_base + REG_NP_SCU_PCIC); ++ ++ return 0; ++} ++ + static int en7523_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) + { +@@ -543,31 +573,18 @@ static int en7523_clk_probe(struct platf + struct device_node *node = pdev->dev.of_node; + const struct en_clk_soc_data *soc_data; + struct clk_hw_onecell_data *clk_data; +- void __iomem *base, *np_base; + int r; + +- base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(base)) +- return PTR_ERR(base); +- +- np_base = devm_platform_ioremap_resource(pdev, 1); +- if (IS_ERR(np_base)) +- return PTR_ERR(np_base); +- +- soc_data = device_get_match_data(&pdev->dev); +- if (soc_data->hw_init) { +- r = soc_data->hw_init(pdev, np_base); +- if (r) +- return r; +- } +- + clk_data = devm_kzalloc(&pdev->dev, + struct_size(clk_data, hws, EN7523_NUM_CLOCKS), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + +- en7523_register_clocks(&pdev->dev, clk_data, base, np_base); ++ soc_data = device_get_match_data(&pdev->dev); ++ r = soc_data->hw_init(pdev, clk_data); ++ if (r) ++ return r; + + r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); + if (r) +@@ -590,6 +607,7 @@ static const struct en_clk_soc_data en75 + .prepare = en7523_pci_prepare, + .unprepare = en7523_pci_unprepare, + }, ++ .hw_init = en7523_clk_hw_init, + }; + + static const struct en_clk_soc_data en7581_data = { diff --git a/target/linux/airoha/patches-6.6/106-03-clk-en7523-introduce-chip_scu-regmap.patch b/target/linux/airoha/patches-6.6/106-03-clk-en7523-introduce-chip_scu-regmap.patch new file mode 100644 index 0000000000..4f8211c564 --- /dev/null +++ b/target/linux/airoha/patches-6.6/106-03-clk-en7523-introduce-chip_scu-regmap.patch @@ -0,0 +1,160 @@ +From f849bcb746abeaafa63b4f02f1d8bb22703fc645 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 3 Sep 2024 23:39:48 +0200 +Subject: [PATCH 3/6] clk: en7523: introduce chip_scu regmap + +Introduce chip_scu regmap pointer since EN7581 SoC will access chip-scu +memory area via a syscon node. Remove first memory region mapping +for EN7581 SoC. This patch does not introduce any backward incompatibility +since the dts for EN7581 SoC is not upstream yet. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/clk/clk-en7523.c | 81 ++++++++++++++++++++++++++++++---------- + 1 file changed, 61 insertions(+), 20 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -3,8 +3,10 @@ + #include + #include + #include ++#include + #include + #include ++#include + #include + #include + #include +@@ -247,15 +249,11 @@ static const u16 en7581_rst_map[] = { + [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31, + }; + +-static unsigned int en7523_get_base_rate(void __iomem *base, unsigned int i) ++static u32 en7523_get_base_rate(const struct en_clk_desc *desc, u32 val) + { +- const struct en_clk_desc *desc = &en7523_base_clks[i]; +- u32 val; +- + if (!desc->base_bits) + return desc->base_value; + +- val = readl(base + desc->base_reg); + val >>= desc->base_shift; + val &= (1 << desc->base_bits) - 1; + +@@ -265,16 +263,11 @@ static unsigned int en7523_get_base_rate + return desc->base_values[val]; + } + +-static u32 en7523_get_div(void __iomem *base, int i) ++static u32 en7523_get_div(const struct en_clk_desc *desc, u32 val) + { +- const struct en_clk_desc *desc = &en7523_base_clks[i]; +- u32 reg, val; +- + if (!desc->div_bits) + return 1; + +- reg = desc->div_reg ? desc->div_reg : desc->base_reg; +- val = readl(base + reg); + val >>= desc->div_shift; + val &= (1 << desc->div_bits) - 1; + +@@ -416,9 +409,12 @@ static void en7523_register_clocks(struc + + for (i = 0; i < ARRAY_SIZE(en7523_base_clks); i++) { + const struct en_clk_desc *desc = &en7523_base_clks[i]; ++ u32 reg = desc->div_reg ? desc->div_reg : desc->base_reg; ++ u32 val = readl(base + desc->base_reg); + +- rate = en7523_get_base_rate(base, i); +- rate /= en7523_get_div(base, i); ++ rate = en7523_get_base_rate(desc, val); ++ val = readl(base + reg); ++ rate /= en7523_get_div(desc, val); + + hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate); + if (IS_ERR(hw)) { +@@ -454,21 +450,66 @@ static int en7523_clk_hw_init(struct pla + return 0; + } + ++static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, ++ struct regmap *map, void __iomem *base) ++{ ++ struct clk_hw *hw; ++ u32 rate; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(en7523_base_clks); i++) { ++ const struct en_clk_desc *desc = &en7523_base_clks[i]; ++ u32 val, reg = desc->div_reg ? desc->div_reg : desc->base_reg; ++ int err; ++ ++ err = regmap_read(map, desc->base_reg, &val); ++ if (err) { ++ pr_err("Failed reading fixed clk rate %s: %d\n", ++ desc->name, err); ++ continue; ++ } ++ rate = en7523_get_base_rate(desc, val); ++ ++ err = regmap_read(map, reg, &val); ++ if (err) { ++ pr_err("Failed reading fixed clk div %s: %d\n", ++ desc->name, err); ++ continue; ++ } ++ rate /= en7523_get_div(desc, val); ++ ++ hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate); ++ if (IS_ERR(hw)) { ++ pr_err("Failed to register clk %s: %ld\n", ++ desc->name, PTR_ERR(hw)); ++ continue; ++ } ++ ++ clk_data->hws[desc->id] = hw; ++ } ++ ++ hw = en7523_register_pcie_clk(dev, base); ++ clk_data->hws[EN7523_CLK_PCIE] = hw; ++ ++ clk_data->num = EN7523_NUM_CLOCKS; ++} ++ + static int en7581_clk_hw_init(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data) + { +- void __iomem *base, *np_base; ++ void __iomem *np_base; ++ struct regmap *map; + u32 val; + +- base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(base)) +- return PTR_ERR(base); ++ map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); ++ if (IS_ERR(map)) ++ return PTR_ERR(map); + +- np_base = devm_platform_ioremap_resource(pdev, 1); ++ np_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(np_base)) + return PTR_ERR(np_base); + +- en7523_register_clocks(&pdev->dev, clk_data, base, np_base); ++ en7581_register_clocks(&pdev->dev, clk_data, map, np_base); + + val = readl(np_base + REG_NP_SCU_SSTR); + val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); +@@ -545,7 +586,7 @@ static int en7523_reset_register(struct + if (!soc_data->reset.idx_map_nr) + return 0; + +- base = devm_platform_ioremap_resource(pdev, 2); ++ base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(base)) + return PTR_ERR(base); + diff --git a/target/linux/airoha/patches-6.6/106-04-clk-en7523-fix-estimation-of-fixed-rate-for-EN7581.patch b/target/linux/airoha/patches-6.6/106-04-clk-en7523-fix-estimation-of-fixed-rate-for-EN7581.patch new file mode 100644 index 0000000000..c311375a8b --- /dev/null +++ b/target/linux/airoha/patches-6.6/106-04-clk-en7523-fix-estimation-of-fixed-rate-for-EN7581.patch @@ -0,0 +1,150 @@ +From b9ea4918216ca0c2511446c531d3f8163ac1466d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 3 Sep 2024 23:39:49 +0200 +Subject: [PATCH 4/6] clk: en7523: fix estimation of fixed rate for EN7581 + +Introduce en7581_base_clks array in order to define per-SoC fixed-rate +clock parameters and fix wrong parameters for emi, npu and crypto EN7581 +clocks + +Fixes: 66bc47326ce2 ("clk: en7523: Add EN7581 support") +Signed-off-by: Lorenzo Bianconi +--- + drivers/clk/clk-en7523.c | 105 ++++++++++++++++++++++++++++++++++++++- + 1 file changed, 103 insertions(+), 2 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -37,6 +37,7 @@ + #define REG_NP_SCU_SSTR 0x9c + #define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13) + #define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11) ++#define REG_CRYPTO_CLKSRC2 0x20c + + #define REG_RST_CTRL2 0x00 + #define REG_RST_CTRL1 0x04 +@@ -89,6 +90,10 @@ static const u32 emi_base[] = { 33300000 + static const u32 bus_base[] = { 500000000, 540000000 }; + static const u32 slic_base[] = { 100000000, 3125000 }; + static const u32 npu_base[] = { 333000000, 400000000, 500000000 }; ++/* EN7581 */ ++static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 }; ++static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 }; ++static const u32 crypto_base[] = { 540000000, 480000000 }; + + static const struct en_clk_desc en7523_base_clks[] = { + { +@@ -186,6 +191,102 @@ static const struct en_clk_desc en7523_b + } + }; + ++static const struct en_clk_desc en7581_base_clks[] = { ++ { ++ .id = EN7523_CLK_GSW, ++ .name = "gsw", ++ ++ .base_reg = REG_GSW_CLK_DIV_SEL, ++ .base_bits = 1, ++ .base_shift = 8, ++ .base_values = gsw_base, ++ .n_base_values = ARRAY_SIZE(gsw_base), ++ ++ .div_bits = 3, ++ .div_shift = 0, ++ .div_step = 1, ++ .div_offset = 1, ++ }, { ++ .id = EN7523_CLK_EMI, ++ .name = "emi", ++ ++ .base_reg = REG_EMI_CLK_DIV_SEL, ++ .base_bits = 2, ++ .base_shift = 8, ++ .base_values = emi7581_base, ++ .n_base_values = ARRAY_SIZE(emi7581_base), ++ ++ .div_bits = 3, ++ .div_shift = 0, ++ .div_step = 1, ++ .div_offset = 1, ++ }, { ++ .id = EN7523_CLK_BUS, ++ .name = "bus", ++ ++ .base_reg = REG_BUS_CLK_DIV_SEL, ++ .base_bits = 1, ++ .base_shift = 8, ++ .base_values = bus_base, ++ .n_base_values = ARRAY_SIZE(bus_base), ++ ++ .div_bits = 3, ++ .div_shift = 0, ++ .div_step = 1, ++ .div_offset = 1, ++ }, { ++ .id = EN7523_CLK_SLIC, ++ .name = "slic", ++ ++ .base_reg = REG_SPI_CLK_FREQ_SEL, ++ .base_bits = 1, ++ .base_shift = 0, ++ .base_values = slic_base, ++ .n_base_values = ARRAY_SIZE(slic_base), ++ ++ .div_reg = REG_SPI_CLK_DIV_SEL, ++ .div_bits = 5, ++ .div_shift = 24, ++ .div_val0 = 20, ++ .div_step = 2, ++ }, { ++ .id = EN7523_CLK_SPI, ++ .name = "spi", ++ ++ .base_reg = REG_SPI_CLK_DIV_SEL, ++ ++ .base_value = 400000000, ++ ++ .div_bits = 5, ++ .div_shift = 8, ++ .div_val0 = 40, ++ .div_step = 2, ++ }, { ++ .id = EN7523_CLK_NPU, ++ .name = "npu", ++ ++ .base_reg = REG_NPU_CLK_DIV_SEL, ++ .base_bits = 2, ++ .base_shift = 8, ++ .base_values = npu7581_base, ++ .n_base_values = ARRAY_SIZE(npu7581_base), ++ ++ .div_bits = 3, ++ .div_shift = 0, ++ .div_step = 1, ++ .div_offset = 1, ++ }, { ++ .id = EN7523_CLK_CRYPTO, ++ .name = "crypto", ++ ++ .base_reg = REG_CRYPTO_CLKSRC2, ++ .base_bits = 1, ++ .base_shift = 0, ++ .base_values = crypto_base, ++ .n_base_values = ARRAY_SIZE(crypto_base), ++ } ++}; ++ + static const u16 en7581_rst_ofs[] = { + REG_RST_CTRL2, + REG_RST_CTRL1, +@@ -457,8 +558,8 @@ static void en7581_register_clocks(struc + u32 rate; + int i; + +- for (i = 0; i < ARRAY_SIZE(en7523_base_clks); i++) { +- const struct en_clk_desc *desc = &en7523_base_clks[i]; ++ for (i = 0; i < ARRAY_SIZE(en7581_base_clks); i++) { ++ const struct en_clk_desc *desc = &en7581_base_clks[i]; + u32 val, reg = desc->div_reg ? desc->div_reg : desc->base_reg; + int err; + diff --git a/target/linux/airoha/patches-6.6/106-05-clk-en7523-move-en7581_reset_register-in-en7581_clk_.patch b/target/linux/airoha/patches-6.6/106-05-clk-en7523-move-en7581_reset_register-in-en7581_clk_.patch new file mode 100644 index 0000000000..3869a58a83 --- /dev/null +++ b/target/linux/airoha/patches-6.6/106-05-clk-en7523-move-en7581_reset_register-in-en7581_clk_.patch @@ -0,0 +1,172 @@ +From 2c5b1a5b68973947a6919d9c951f9b3e0d84f347 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 3 Sep 2024 23:39:50 +0200 +Subject: [PATCH 5/6] clk: en7523: move en7581_reset_register() in + en7581_clk_hw_init() + +Move en7581_reset_register routine in en7581_clk_hw_init() since reset +feature is supported just by EN7581 SoC. +Get rid of reset struct in en_clk_soc_data data struct. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/clk/clk-en7523.c | 93 ++++++++++++++-------------------------- + 1 file changed, 33 insertions(+), 60 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -76,11 +76,6 @@ struct en_rst_data { + + struct en_clk_soc_data { + const struct clk_ops pcie_ops; +- struct { +- const u16 *bank_ofs; +- const u16 *idx_map; +- u16 idx_map_nr; +- } reset; + int (*hw_init)(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data); + }; +@@ -595,32 +590,6 @@ static void en7581_register_clocks(struc + clk_data->num = EN7523_NUM_CLOCKS; + } + +-static int en7581_clk_hw_init(struct platform_device *pdev, +- struct clk_hw_onecell_data *clk_data) +-{ +- void __iomem *np_base; +- struct regmap *map; +- u32 val; +- +- map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); +- if (IS_ERR(map)) +- return PTR_ERR(map); +- +- np_base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(np_base)) +- return PTR_ERR(np_base); +- +- en7581_register_clocks(&pdev->dev, clk_data, map, np_base); +- +- val = readl(np_base + REG_NP_SCU_SSTR); +- val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); +- writel(val, np_base + REG_NP_SCU_SSTR); +- val = readl(np_base + REG_NP_SCU_PCIC); +- writel(val | 3, np_base + REG_NP_SCU_PCIC); +- +- return 0; +-} +- + static int en7523_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) + { +@@ -670,23 +639,18 @@ static int en7523_reset_xlate(struct res + return rst_data->idx_map[reset_spec->args[0]]; + } + +-static const struct reset_control_ops en7523_reset_ops = { ++static const struct reset_control_ops en7581_reset_ops = { + .assert = en7523_reset_assert, + .deassert = en7523_reset_deassert, + .status = en7523_reset_status, + }; + +-static int en7523_reset_register(struct platform_device *pdev, +- const struct en_clk_soc_data *soc_data) ++static int en7581_reset_register(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct en_rst_data *rst_data; + void __iomem *base; + +- /* no reset lines available */ +- if (!soc_data->reset.idx_map_nr) +- return 0; +- + base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(base)) + return PTR_ERR(base); +@@ -695,13 +659,13 @@ static int en7523_reset_register(struct + if (!rst_data) + return -ENOMEM; + +- rst_data->bank_ofs = soc_data->reset.bank_ofs; +- rst_data->idx_map = soc_data->reset.idx_map; ++ rst_data->bank_ofs = en7581_rst_ofs; ++ rst_data->idx_map = en7581_rst_map; + rst_data->base = base; + +- rst_data->rcdev.nr_resets = soc_data->reset.idx_map_nr; ++ rst_data->rcdev.nr_resets = ARRAY_SIZE(en7581_rst_map); + rst_data->rcdev.of_xlate = en7523_reset_xlate; +- rst_data->rcdev.ops = &en7523_reset_ops; ++ rst_data->rcdev.ops = &en7581_reset_ops; + rst_data->rcdev.of_node = dev->of_node; + rst_data->rcdev.of_reset_n_cells = 1; + rst_data->rcdev.owner = THIS_MODULE; +@@ -710,6 +674,32 @@ static int en7523_reset_register(struct + return devm_reset_controller_register(dev, &rst_data->rcdev); + } + ++static int en7581_clk_hw_init(struct platform_device *pdev, ++ struct clk_hw_onecell_data *clk_data) ++{ ++ void __iomem *np_base; ++ struct regmap *map; ++ u32 val; ++ ++ map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); ++ if (IS_ERR(map)) ++ return PTR_ERR(map); ++ ++ np_base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(np_base)) ++ return PTR_ERR(np_base); ++ ++ en7581_register_clocks(&pdev->dev, clk_data, map, np_base); ++ ++ val = readl(np_base + REG_NP_SCU_SSTR); ++ val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); ++ writel(val, np_base + REG_NP_SCU_SSTR); ++ val = readl(np_base + REG_NP_SCU_PCIC); ++ writel(val | 3, np_base + REG_NP_SCU_PCIC); ++ ++ return en7581_reset_register(pdev); ++} ++ + static int en7523_clk_probe(struct platform_device *pdev) + { + struct device_node *node = pdev->dev.of_node; +@@ -728,19 +718,7 @@ static int en7523_clk_probe(struct platf + if (r) + return r; + +- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); +- if (r) +- return dev_err_probe(&pdev->dev, r, "Could not register clock provider: %s\n", +- pdev->name); +- +- r = en7523_reset_register(pdev, soc_data); +- if (r) { +- of_clk_del_provider(node); +- return dev_err_probe(&pdev->dev, r, "Could not register reset controller: %s\n", +- pdev->name); +- } +- +- return 0; ++ return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); + } + + static const struct en_clk_soc_data en7523_data = { +@@ -758,11 +736,6 @@ static const struct en_clk_soc_data en75 + .enable = en7581_pci_enable, + .disable = en7581_pci_disable, + }, +- .reset = { +- .bank_ofs = en7581_rst_ofs, +- .idx_map = en7581_rst_map, +- .idx_map_nr = ARRAY_SIZE(en7581_rst_map), +- }, + .hw_init = en7581_clk_hw_init, + }; + diff --git a/target/linux/airoha/patches-6.6/106-06-clk-en7523-map-io-region-in-a-single-block.patch b/target/linux/airoha/patches-6.6/106-06-clk-en7523-map-io-region-in-a-single-block.patch new file mode 100644 index 0000000000..db04d602f3 --- /dev/null +++ b/target/linux/airoha/patches-6.6/106-06-clk-en7523-map-io-region-in-a-single-block.patch @@ -0,0 +1,82 @@ +From 665a59f4836c3d7813a9d8bfb9680d93adb4626e Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 3 Sep 2024 23:39:51 +0200 +Subject: [PATCH 6/6] clk: en7523: map io region in a single block + +Map all clock-controller memory region in a single block. +This patch does not introduce any backward incompatibility since the dts +for EN7581 SoC is not upstream yet. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/clk/clk-en7523.c | 32 +++++++++++++------------------- + 1 file changed, 13 insertions(+), 19 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -39,8 +39,8 @@ + #define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11) + #define REG_CRYPTO_CLKSRC2 0x20c + +-#define REG_RST_CTRL2 0x00 +-#define REG_RST_CTRL1 0x04 ++#define REG_RST_CTRL2 0x830 ++#define REG_RST_CTRL1 0x834 + + struct en_clk_desc { + int id; +@@ -645,15 +645,9 @@ static const struct reset_control_ops en + .status = en7523_reset_status, + }; + +-static int en7581_reset_register(struct platform_device *pdev) ++static int en7581_reset_register(struct device *dev, void __iomem *base) + { +- struct device *dev = &pdev->dev; + struct en_rst_data *rst_data; +- void __iomem *base; +- +- base = devm_platform_ioremap_resource(pdev, 1); +- if (IS_ERR(base)) +- return PTR_ERR(base); + + rst_data = devm_kzalloc(dev, sizeof(*rst_data), GFP_KERNEL); + if (!rst_data) +@@ -677,27 +671,27 @@ static int en7581_reset_register(struct + static int en7581_clk_hw_init(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data) + { +- void __iomem *np_base; + struct regmap *map; ++ void __iomem *base; + u32 val; + + map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); + if (IS_ERR(map)) + return PTR_ERR(map); + +- np_base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(np_base)) +- return PTR_ERR(np_base); ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); + +- en7581_register_clocks(&pdev->dev, clk_data, map, np_base); ++ en7581_register_clocks(&pdev->dev, clk_data, map, base); + +- val = readl(np_base + REG_NP_SCU_SSTR); ++ val = readl(base + REG_NP_SCU_SSTR); + val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); +- writel(val, np_base + REG_NP_SCU_SSTR); +- val = readl(np_base + REG_NP_SCU_PCIC); +- writel(val | 3, np_base + REG_NP_SCU_PCIC); ++ writel(val, base + REG_NP_SCU_SSTR); ++ val = readl(base + REG_NP_SCU_PCIC); ++ writel(val | 3, base + REG_NP_SCU_PCIC); + +- return en7581_reset_register(pdev); ++ return en7581_reset_register(&pdev->dev, base); + } + + static int en7523_clk_probe(struct platform_device *pdev) diff --git a/target/linux/airoha/patches-6.6/107-pinctrl-airoha-Add-support-for-EN7581-SoC.patch b/target/linux/airoha/patches-6.6/107-pinctrl-airoha-Add-support-for-EN7581-SoC.patch new file mode 100644 index 0000000000..8e299512c3 --- /dev/null +++ b/target/linux/airoha/patches-6.6/107-pinctrl-airoha-Add-support-for-EN7581-SoC.patch @@ -0,0 +1,3060 @@ +From 21cb14f3e6d12d666a9ec0fd7cc01d722b04e514 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 16 Oct 2024 12:07:33 +0200 +Subject: [PATCH 1/2] pinctrl: airoha: Add support for EN7581 SoC + +Introduce pinctrl driver for EN7581 SoC. Current EN7581 pinctrl driver +supports the following functionalities: +- pin multiplexing +- pin pull-up, pull-down, open-drain, current strength, + {input,output}_enable, output_{low,high} +- gpio controller +- irq controller + +Tested-by: Benjamin Larsson +Co-developed-by: Benjamin Larsson +Signed-off-by: Benjamin Larsson +Reviewed-by: Linus Walleij +Signed-off-by: Lorenzo Bianconi +Reviewed-by: AngeloGioacchino Del Regno +--- + MAINTAINERS | 7 + + drivers/pinctrl/mediatek/Kconfig | 17 +- + drivers/pinctrl/mediatek/Makefile | 1 + + drivers/pinctrl/mediatek/pinctrl-airoha.c | 2970 +++++++++++++++++++++ + 4 files changed, 2994 insertions(+), 1 deletion(-) + create mode 100644 drivers/pinctrl/mediatek/pinctrl-airoha.c + +# diff --git a/MAINTAINERS b/MAINTAINERS +# index 8a6ea49e1a9d..ca4a78737dc6 100644 +# --- a/MAINTAINERS +# +++ b/MAINTAINERS +# @@ -18331,6 +18331,13 @@ F: drivers/pinctrl/ +# F: include/dt-bindings/pinctrl/ +# F: include/linux/pinctrl/ + +# +PIN CONTROLLER - AIROHA +# +M: Lorenzo Bianconi +# +L: linux-mediatek@lists.infradead.org (moderated for non-subscribers) +# +S: Maintained +# +F: Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml +# +F: drivers/pinctrl/mediatek/pinctrl-airoha.c +# + +# PIN CONTROLLER - AMD +# M: Basavaraj Natikar +# M: Shyam Sundar S K +--- a/drivers/pinctrl/mediatek/Kconfig ++++ b/drivers/pinctrl/mediatek/Kconfig +@@ -1,6 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + menu "MediaTek pinctrl drivers" +- depends on ARCH_MEDIATEK || RALINK || COMPILE_TEST ++ depends on ARCH_MEDIATEK || ARCH_AIROHA || RALINK || COMPILE_TEST + + config EINT_MTK + tristate "MediaTek External Interrupt Support" +@@ -126,6 +126,21 @@ config PINCTRL_MT8127 + select PINCTRL_MTK + + # For ARMv8 SoCs ++config PINCTRL_AIROHA ++ tristate "Airoha EN7581 pin control" ++ depends on OF ++ depends on ARM64 || COMPILE_TEST ++ select PINMUX ++ select GENERIC_PINCONF ++ select GENERIC_PINCTRL_GROUPS ++ select GENERIC_PINMUX_FUNCTIONS ++ select GPIOLIB ++ select GPIOLIB_IRQCHIP ++ select REGMAP_MMIO ++ help ++ Say yes here to support pin controller and gpio driver ++ on Airoha EN7581 SoC. ++ + config PINCTRL_MT2712 + bool "MediaTek MT2712 pin control" + depends on OF +--- a/drivers/pinctrl/mediatek/Makefile ++++ b/drivers/pinctrl/mediatek/Makefile +@@ -8,6 +8,7 @@ obj-$(CONFIG_PINCTRL_MTK_MOORE) += pinc + obj-$(CONFIG_PINCTRL_MTK_PARIS) += pinctrl-paris.o + + # SoC Drivers ++obj-$(CONFIG_PINCTRL_AIROHA) += pinctrl-airoha.o + obj-$(CONFIG_PINCTRL_MT7620) += pinctrl-mt7620.o + obj-$(CONFIG_PINCTRL_MT7621) += pinctrl-mt7621.o + obj-$(CONFIG_PINCTRL_MT76X8) += pinctrl-mt76x8.o +--- /dev/null ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -0,0 +1,2970 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Author: Lorenzo Bianconi ++ * Author: Benjamin Larsson ++ * Author: Markus Gothe ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../core.h" ++#include "../pinconf.h" ++#include "../pinmux.h" ++ ++#define PINCTRL_PIN_GROUP(id) \ ++ PINCTRL_PINGROUP(#id, id##_pins, ARRAY_SIZE(id##_pins)) ++ ++#define PINCTRL_FUNC_DESC(id) \ ++ { \ ++ .desc = { #id, id##_groups, ARRAY_SIZE(id##_groups) }, \ ++ .groups = id##_func_group, \ ++ .group_size = ARRAY_SIZE(id##_func_group), \ ++ } ++ ++#define PINCTRL_CONF_DESC(p, offset, mask) \ ++ { \ ++ .pin = p, \ ++ .reg = { offset, mask }, \ ++ } ++ ++/* MUX */ ++#define REG_GPIO_2ND_I2C_MODE 0x0214 ++#define GPIO_MDC_IO_MASTER_MODE_MODE BIT(14) ++#define GPIO_I2C_MASTER_MODE_MODE BIT(13) ++#define GPIO_I2S_MODE_MASK BIT(12) ++#define GPIO_I2C_SLAVE_MODE_MODE BIT(11) ++#define GPIO_LAN3_LED1_MODE_MASK BIT(10) ++#define GPIO_LAN3_LED0_MODE_MASK BIT(9) ++#define GPIO_LAN2_LED1_MODE_MASK BIT(8) ++#define GPIO_LAN2_LED0_MODE_MASK BIT(7) ++#define GPIO_LAN1_LED1_MODE_MASK BIT(6) ++#define GPIO_LAN1_LED0_MODE_MASK BIT(5) ++#define GPIO_LAN0_LED1_MODE_MASK BIT(4) ++#define GPIO_LAN0_LED0_MODE_MASK BIT(3) ++#define PON_TOD_1PPS_MODE_MASK BIT(2) ++#define GSW_TOD_1PPS_MODE_MASK BIT(1) ++#define GPIO_2ND_I2C_MODE_MASK BIT(0) ++ ++#define REG_GPIO_SPI_CS1_MODE 0x0218 ++#define GPIO_PCM_SPI_CS4_MODE_MASK BIT(21) ++#define GPIO_PCM_SPI_CS3_MODE_MASK BIT(20) ++#define GPIO_PCM_SPI_CS2_MODE_P156_MASK BIT(19) ++#define GPIO_PCM_SPI_CS2_MODE_P128_MASK BIT(18) ++#define GPIO_PCM_SPI_CS1_MODE_MASK BIT(17) ++#define GPIO_PCM_SPI_MODE_MASK BIT(16) ++#define GPIO_PCM2_MODE_MASK BIT(13) ++#define GPIO_PCM1_MODE_MASK BIT(12) ++#define GPIO_PCM_INT_MODE_MASK BIT(9) ++#define GPIO_PCM_RESET_MODE_MASK BIT(8) ++#define GPIO_SPI_QUAD_MODE_MASK BIT(4) ++#define GPIO_SPI_CS4_MODE_MASK BIT(3) ++#define GPIO_SPI_CS3_MODE_MASK BIT(2) ++#define GPIO_SPI_CS2_MODE_MASK BIT(1) ++#define GPIO_SPI_CS1_MODE_MASK BIT(0) ++ ++#define REG_GPIO_PON_MODE 0x021c ++#define GPIO_PARALLEL_NAND_MODE_MASK BIT(14) ++#define GPIO_SGMII_MDIO_MODE_MASK BIT(13) ++#define GPIO_PCIE_RESET2_MASK BIT(12) ++#define SIPO_RCLK_MODE_MASK BIT(11) ++#define GPIO_PCIE_RESET1_MASK BIT(10) ++#define GPIO_PCIE_RESET0_MASK BIT(9) ++#define GPIO_UART5_MODE_MASK BIT(8) ++#define GPIO_UART4_MODE_MASK BIT(7) ++#define GPIO_HSUART_CTS_RTS_MODE_MASK BIT(6) ++#define GPIO_HSUART_MODE_MASK BIT(5) ++#define GPIO_UART2_CTS_RTS_MODE_MASK BIT(4) ++#define GPIO_UART2_MODE_MASK BIT(3) ++#define GPIO_SIPO_MODE_MASK BIT(2) ++#define GPIO_EMMC_MODE_MASK BIT(1) ++#define GPIO_PON_MODE_MASK BIT(0) ++ ++#define REG_NPU_UART_EN 0x0224 ++#define JTAG_UDI_EN_MASK BIT(4) ++#define JTAG_DFD_EN_MASK BIT(3) ++ ++/* LED MAP */ ++#define REG_LAN_LED0_MAPPING 0x027c ++#define REG_LAN_LED1_MAPPING 0x0280 ++ ++#define LAN4_LED_MAPPING_MASK GENMASK(18, 16) ++#define LAN4_PHY4_LED_MAP BIT(18) ++#define LAN4_PHY2_LED_MAP BIT(17) ++#define LAN4_PHY1_LED_MAP BIT(16) ++#define LAN4_PHY0_LED_MAP 0 ++#define LAN4_PHY3_LED_MAP GENMASK(17, 16) ++ ++#define LAN3_LED_MAPPING_MASK GENMASK(14, 12) ++#define LAN3_PHY4_LED_MAP BIT(14) ++#define LAN3_PHY2_LED_MAP BIT(13) ++#define LAN3_PHY1_LED_MAP BIT(12) ++#define LAN3_PHY0_LED_MAP 0 ++#define LAN3_PHY3_LED_MAP GENMASK(13, 12) ++ ++#define LAN2_LED_MAPPING_MASK GENMASK(10, 8) ++#define LAN2_PHY4_LED_MAP BIT(12) ++#define LAN2_PHY2_LED_MAP BIT(11) ++#define LAN2_PHY1_LED_MAP BIT(10) ++#define LAN2_PHY0_LED_MAP 0 ++#define LAN2_PHY3_LED_MAP GENMASK(11, 10) ++ ++#define LAN1_LED_MAPPING_MASK GENMASK(6, 4) ++#define LAN1_PHY4_LED_MAP BIT(6) ++#define LAN1_PHY2_LED_MAP BIT(5) ++#define LAN1_PHY1_LED_MAP BIT(4) ++#define LAN1_PHY0_LED_MAP 0 ++#define LAN1_PHY3_LED_MAP GENMASK(5, 4) ++ ++#define LAN0_LED_MAPPING_MASK GENMASK(2, 0) ++#define LAN0_PHY4_LED_MAP BIT(3) ++#define LAN0_PHY2_LED_MAP BIT(2) ++#define LAN0_PHY1_LED_MAP BIT(1) ++#define LAN0_PHY0_LED_MAP 0 ++#define LAN0_PHY3_LED_MAP GENMASK(2, 1) ++ ++/* CONF */ ++#define REG_I2C_SDA_E2 0x001c ++#define SPI_MISO_E2_MASK BIT(14) ++#define SPI_MOSI_E2_MASK BIT(13) ++#define SPI_CLK_E2_MASK BIT(12) ++#define SPI_CS0_E2_MASK BIT(11) ++#define PCIE2_RESET_E2_MASK BIT(10) ++#define PCIE1_RESET_E2_MASK BIT(9) ++#define PCIE0_RESET_E2_MASK BIT(8) ++#define UART1_RXD_E2_MASK BIT(3) ++#define UART1_TXD_E2_MASK BIT(2) ++#define I2C_SCL_E2_MASK BIT(1) ++#define I2C_SDA_E2_MASK BIT(0) ++ ++#define REG_I2C_SDA_E4 0x0020 ++#define SPI_MISO_E4_MASK BIT(14) ++#define SPI_MOSI_E4_MASK BIT(13) ++#define SPI_CLK_E4_MASK BIT(12) ++#define SPI_CS0_E4_MASK BIT(11) ++#define PCIE2_RESET_E4_MASK BIT(10) ++#define PCIE1_RESET_E4_MASK BIT(9) ++#define PCIE0_RESET_E4_MASK BIT(8) ++#define UART1_RXD_E4_MASK BIT(3) ++#define UART1_TXD_E4_MASK BIT(2) ++#define I2C_SCL_E4_MASK BIT(1) ++#define I2C_SDA_E4_MASK BIT(0) ++ ++#define REG_GPIO_L_E2 0x0024 ++#define REG_GPIO_L_E4 0x0028 ++#define REG_GPIO_H_E2 0x002c ++#define REG_GPIO_H_E4 0x0030 ++ ++#define REG_I2C_SDA_PU 0x0044 ++#define SPI_MISO_PU_MASK BIT(14) ++#define SPI_MOSI_PU_MASK BIT(13) ++#define SPI_CLK_PU_MASK BIT(12) ++#define SPI_CS0_PU_MASK BIT(11) ++#define PCIE2_RESET_PU_MASK BIT(10) ++#define PCIE1_RESET_PU_MASK BIT(9) ++#define PCIE0_RESET_PU_MASK BIT(8) ++#define UART1_RXD_PU_MASK BIT(3) ++#define UART1_TXD_PU_MASK BIT(2) ++#define I2C_SCL_PU_MASK BIT(1) ++#define I2C_SDA_PU_MASK BIT(0) ++ ++#define REG_I2C_SDA_PD 0x0048 ++#define SPI_MISO_PD_MASK BIT(14) ++#define SPI_MOSI_PD_MASK BIT(13) ++#define SPI_CLK_PD_MASK BIT(12) ++#define SPI_CS0_PD_MASK BIT(11) ++#define PCIE2_RESET_PD_MASK BIT(10) ++#define PCIE1_RESET_PD_MASK BIT(9) ++#define PCIE0_RESET_PD_MASK BIT(8) ++#define UART1_RXD_PD_MASK BIT(3) ++#define UART1_TXD_PD_MASK BIT(2) ++#define I2C_SCL_PD_MASK BIT(1) ++#define I2C_SDA_PD_MASK BIT(0) ++ ++#define REG_GPIO_L_PU 0x004c ++#define REG_GPIO_L_PD 0x0050 ++#define REG_GPIO_H_PU 0x0054 ++#define REG_GPIO_H_PD 0x0058 ++ ++#define REG_PCIE_RESET_OD 0x018c ++#define PCIE2_RESET_OD_MASK BIT(2) ++#define PCIE1_RESET_OD_MASK BIT(1) ++#define PCIE0_RESET_OD_MASK BIT(0) ++ ++/* GPIOs */ ++#define REG_GPIO_CTRL 0x0000 ++#define REG_GPIO_DATA 0x0004 ++#define REG_GPIO_INT 0x0008 ++#define REG_GPIO_INT_EDGE 0x000c ++#define REG_GPIO_INT_LEVEL 0x0010 ++#define REG_GPIO_OE 0x0014 ++#define REG_GPIO_CTRL1 0x0020 ++ ++/* PWM MODE CONF */ ++#define REG_GPIO_FLASH_MODE_CFG 0x0034 ++#define GPIO15_FLASH_MODE_CFG BIT(15) ++#define GPIO14_FLASH_MODE_CFG BIT(14) ++#define GPIO13_FLASH_MODE_CFG BIT(13) ++#define GPIO12_FLASH_MODE_CFG BIT(12) ++#define GPIO11_FLASH_MODE_CFG BIT(11) ++#define GPIO10_FLASH_MODE_CFG BIT(10) ++#define GPIO9_FLASH_MODE_CFG BIT(9) ++#define GPIO8_FLASH_MODE_CFG BIT(8) ++#define GPIO7_FLASH_MODE_CFG BIT(7) ++#define GPIO6_FLASH_MODE_CFG BIT(6) ++#define GPIO5_FLASH_MODE_CFG BIT(5) ++#define GPIO4_FLASH_MODE_CFG BIT(4) ++#define GPIO3_FLASH_MODE_CFG BIT(3) ++#define GPIO2_FLASH_MODE_CFG BIT(2) ++#define GPIO1_FLASH_MODE_CFG BIT(1) ++#define GPIO0_FLASH_MODE_CFG BIT(0) ++ ++#define REG_GPIO_CTRL2 0x0060 ++#define REG_GPIO_CTRL3 0x0064 ++ ++/* PWM MODE CONF EXT */ ++#define REG_GPIO_FLASH_MODE_CFG_EXT 0x0068 ++#define GPIO51_FLASH_MODE_CFG BIT(31) ++#define GPIO50_FLASH_MODE_CFG BIT(30) ++#define GPIO49_FLASH_MODE_CFG BIT(29) ++#define GPIO48_FLASH_MODE_CFG BIT(28) ++#define GPIO47_FLASH_MODE_CFG BIT(27) ++#define GPIO46_FLASH_MODE_CFG BIT(26) ++#define GPIO45_FLASH_MODE_CFG BIT(25) ++#define GPIO44_FLASH_MODE_CFG BIT(24) ++#define GPIO43_FLASH_MODE_CFG BIT(23) ++#define GPIO42_FLASH_MODE_CFG BIT(22) ++#define GPIO41_FLASH_MODE_CFG BIT(21) ++#define GPIO40_FLASH_MODE_CFG BIT(20) ++#define GPIO39_FLASH_MODE_CFG BIT(19) ++#define GPIO38_FLASH_MODE_CFG BIT(18) ++#define GPIO37_FLASH_MODE_CFG BIT(17) ++#define GPIO36_FLASH_MODE_CFG BIT(16) ++#define GPIO31_FLASH_MODE_CFG BIT(15) ++#define GPIO30_FLASH_MODE_CFG BIT(14) ++#define GPIO29_FLASH_MODE_CFG BIT(13) ++#define GPIO28_FLASH_MODE_CFG BIT(12) ++#define GPIO27_FLASH_MODE_CFG BIT(11) ++#define GPIO26_FLASH_MODE_CFG BIT(10) ++#define GPIO25_FLASH_MODE_CFG BIT(9) ++#define GPIO24_FLASH_MODE_CFG BIT(8) ++#define GPIO23_FLASH_MODE_CFG BIT(7) ++#define GPIO22_FLASH_MODE_CFG BIT(6) ++#define GPIO21_FLASH_MODE_CFG BIT(5) ++#define GPIO20_FLASH_MODE_CFG BIT(4) ++#define GPIO19_FLASH_MODE_CFG BIT(3) ++#define GPIO18_FLASH_MODE_CFG BIT(2) ++#define GPIO17_FLASH_MODE_CFG BIT(1) ++#define GPIO16_FLASH_MODE_CFG BIT(0) ++ ++#define REG_GPIO_DATA1 0x0070 ++#define REG_GPIO_OE1 0x0078 ++#define REG_GPIO_INT1 0x007c ++#define REG_GPIO_INT_EDGE1 0x0080 ++#define REG_GPIO_INT_EDGE2 0x0084 ++#define REG_GPIO_INT_EDGE3 0x0088 ++#define REG_GPIO_INT_LEVEL1 0x008c ++#define REG_GPIO_INT_LEVEL2 0x0090 ++#define REG_GPIO_INT_LEVEL3 0x0094 ++ ++#define AIROHA_NUM_PINS 64 ++#define AIROHA_PIN_BANK_SIZE (AIROHA_NUM_PINS / 2) ++#define AIROHA_REG_GPIOCTRL_NUM_PIN (AIROHA_NUM_PINS / 4) ++ ++static const u32 gpio_data_regs[] = { ++ REG_GPIO_DATA, ++ REG_GPIO_DATA1 ++}; ++ ++static const u32 gpio_out_regs[] = { ++ REG_GPIO_OE, ++ REG_GPIO_OE1 ++}; ++ ++static const u32 gpio_dir_regs[] = { ++ REG_GPIO_CTRL, ++ REG_GPIO_CTRL1, ++ REG_GPIO_CTRL2, ++ REG_GPIO_CTRL3 ++}; ++ ++static const u32 irq_status_regs[] = { ++ REG_GPIO_INT, ++ REG_GPIO_INT1 ++}; ++ ++static const u32 irq_level_regs[] = { ++ REG_GPIO_INT_LEVEL, ++ REG_GPIO_INT_LEVEL1, ++ REG_GPIO_INT_LEVEL2, ++ REG_GPIO_INT_LEVEL3 ++}; ++ ++static const u32 irq_edge_regs[] = { ++ REG_GPIO_INT_EDGE, ++ REG_GPIO_INT_EDGE1, ++ REG_GPIO_INT_EDGE2, ++ REG_GPIO_INT_EDGE3 ++}; ++ ++struct airoha_pinctrl_reg { ++ u32 offset; ++ u32 mask; ++}; ++ ++enum airoha_pinctrl_mux_func { ++ AIROHA_FUNC_MUX, ++ AIROHA_FUNC_PWM_MUX, ++ AIROHA_FUNC_PWM_EXT_MUX, ++}; ++ ++struct airoha_pinctrl_func_group { ++ const char *name; ++ struct { ++ enum airoha_pinctrl_mux_func mux; ++ u32 offset; ++ u32 mask; ++ u32 val; ++ } regmap[2]; ++ int regmap_size; ++}; ++ ++struct airoha_pinctrl_func { ++ const struct function_desc desc; ++ const struct airoha_pinctrl_func_group *groups; ++ u8 group_size; ++}; ++ ++struct airoha_pinctrl_conf { ++ u32 pin; ++ struct airoha_pinctrl_reg reg; ++}; ++ ++struct airoha_pinctrl_gpiochip { ++ struct gpio_chip chip; ++ ++ /* gpio */ ++ const u32 *data; ++ const u32 *dir; ++ const u32 *out; ++ /* irq */ ++ const u32 *status; ++ const u32 *level; ++ const u32 *edge; ++ ++ u32 irq_type[AIROHA_NUM_PINS]; ++}; ++ ++struct airoha_pinctrl { ++ struct pinctrl_dev *ctrl; ++ ++ struct regmap *chip_scu; ++ struct regmap *regmap; ++ ++ struct airoha_pinctrl_gpiochip gpiochip; ++}; ++ ++static struct pinctrl_pin_desc airoha_pinctrl_pins[] = { ++ PINCTRL_PIN(0, "uart1_txd"), ++ PINCTRL_PIN(1, "uart1_rxd"), ++ PINCTRL_PIN(2, "i2c_scl"), ++ PINCTRL_PIN(3, "i2c_sda"), ++ PINCTRL_PIN(4, "spi_cs0"), ++ PINCTRL_PIN(5, "spi_clk"), ++ PINCTRL_PIN(6, "spi_mosi"), ++ PINCTRL_PIN(7, "spi_miso"), ++ PINCTRL_PIN(13, "gpio0"), ++ PINCTRL_PIN(14, "gpio1"), ++ PINCTRL_PIN(15, "gpio2"), ++ PINCTRL_PIN(16, "gpio3"), ++ PINCTRL_PIN(17, "gpio4"), ++ PINCTRL_PIN(18, "gpio5"), ++ PINCTRL_PIN(19, "gpio6"), ++ PINCTRL_PIN(20, "gpio7"), ++ PINCTRL_PIN(21, "gpio8"), ++ PINCTRL_PIN(22, "gpio9"), ++ PINCTRL_PIN(23, "gpio10"), ++ PINCTRL_PIN(24, "gpio11"), ++ PINCTRL_PIN(25, "gpio12"), ++ PINCTRL_PIN(26, "gpio13"), ++ PINCTRL_PIN(27, "gpio14"), ++ PINCTRL_PIN(28, "gpio15"), ++ PINCTRL_PIN(29, "gpio16"), ++ PINCTRL_PIN(30, "gpio17"), ++ PINCTRL_PIN(31, "gpio18"), ++ PINCTRL_PIN(32, "gpio19"), ++ PINCTRL_PIN(33, "gpio20"), ++ PINCTRL_PIN(34, "gpio21"), ++ PINCTRL_PIN(35, "gpio22"), ++ PINCTRL_PIN(36, "gpio23"), ++ PINCTRL_PIN(37, "gpio24"), ++ PINCTRL_PIN(38, "gpio25"), ++ PINCTRL_PIN(39, "gpio26"), ++ PINCTRL_PIN(40, "gpio27"), ++ PINCTRL_PIN(41, "gpio28"), ++ PINCTRL_PIN(42, "gpio29"), ++ PINCTRL_PIN(43, "gpio30"), ++ PINCTRL_PIN(44, "gpio31"), ++ PINCTRL_PIN(45, "gpio32"), ++ PINCTRL_PIN(46, "gpio33"), ++ PINCTRL_PIN(47, "gpio34"), ++ PINCTRL_PIN(48, "gpio35"), ++ PINCTRL_PIN(49, "gpio36"), ++ PINCTRL_PIN(50, "gpio37"), ++ PINCTRL_PIN(51, "gpio38"), ++ PINCTRL_PIN(52, "gpio39"), ++ PINCTRL_PIN(53, "gpio40"), ++ PINCTRL_PIN(54, "gpio41"), ++ PINCTRL_PIN(55, "gpio42"), ++ PINCTRL_PIN(56, "gpio43"), ++ PINCTRL_PIN(57, "gpio44"), ++ PINCTRL_PIN(58, "gpio45"), ++ PINCTRL_PIN(59, "gpio46"), ++ PINCTRL_PIN(61, "pcie_reset0"), ++ PINCTRL_PIN(62, "pcie_reset1"), ++ PINCTRL_PIN(63, "pcie_reset2"), ++}; ++ ++static const int pon_pins[] = { 49, 50, 51, 52, 53, 54 }; ++static const int pon_tod_1pps_pins[] = { 46 }; ++static const int gsw_tod_1pps_pins[] = { 46 }; ++static const int sipo_pins[] = { 16, 17 }; ++static const int sipo_rclk_pins[] = { 16, 17, 43 }; ++static const int mdio_pins[] = { 14, 15 }; ++static const int uart2_pins[] = { 48, 55 }; ++static const int uart2_cts_rts_pins[] = { 46, 47 }; ++static const int hsuart_pins[] = { 28, 29 }; ++static const int hsuart_cts_rts_pins[] = { 26, 27 }; ++static const int uart4_pins[] = { 38, 39 }; ++static const int uart5_pins[] = { 18, 19 }; ++static const int i2c0_pins[] = { 2, 3 }; ++static const int i2c1_pins[] = { 14, 15 }; ++static const int jtag_udi_pins[] = { 16, 17, 18, 19, 20 }; ++static const int jtag_dfd_pins[] = { 16, 17, 18, 19, 20 }; ++static const int i2s_pins[] = { 26, 27, 28, 29 }; ++static const int pcm1_pins[] = { 22, 23, 24, 25 }; ++static const int pcm2_pins[] = { 18, 19, 20, 21 }; ++static const int spi_quad_pins[] = { 32, 33 }; ++static const int spi_pins[] = { 4, 5, 6, 7 }; ++static const int spi_cs1_pins[] = { 34 }; ++static const int pcm_spi_pins[] = { 18, 19, 20, 21, 22, 23, 24, 25 }; ++static const int pcm_spi_int_pins[] = { 14 }; ++static const int pcm_spi_rst_pins[] = { 15 }; ++static const int pcm_spi_cs1_pins[] = { 43 }; ++static const int pcm_spi_cs2_pins[] = { 40 }; ++static const int pcm_spi_cs2_p128_pins[] = { 40 }; ++static const int pcm_spi_cs2_p156_pins[] = { 40 }; ++static const int pcm_spi_cs3_pins[] = { 41 }; ++static const int pcm_spi_cs4_pins[] = { 42 }; ++static const int emmc_pins[] = { 4, 5, 6, 30, 31, 32, 33, 34, 35, 36, 37 }; ++static const int pnand_pins[] = { 4, 5, 6, 7, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; ++static const int gpio0_pins[] = { 13 }; ++static const int gpio1_pins[] = { 14 }; ++static const int gpio2_pins[] = { 15 }; ++static const int gpio3_pins[] = { 16 }; ++static const int gpio4_pins[] = { 17 }; ++static const int gpio5_pins[] = { 18 }; ++static const int gpio6_pins[] = { 19 }; ++static const int gpio7_pins[] = { 20 }; ++static const int gpio8_pins[] = { 21 }; ++static const int gpio9_pins[] = { 22 }; ++static const int gpio10_pins[] = { 23 }; ++static const int gpio11_pins[] = { 24 }; ++static const int gpio12_pins[] = { 25 }; ++static const int gpio13_pins[] = { 26 }; ++static const int gpio14_pins[] = { 27 }; ++static const int gpio15_pins[] = { 28 }; ++static const int gpio16_pins[] = { 29 }; ++static const int gpio17_pins[] = { 30 }; ++static const int gpio18_pins[] = { 31 }; ++static const int gpio19_pins[] = { 32 }; ++static const int gpio20_pins[] = { 33 }; ++static const int gpio21_pins[] = { 34 }; ++static const int gpio22_pins[] = { 35 }; ++static const int gpio23_pins[] = { 36 }; ++static const int gpio24_pins[] = { 37 }; ++static const int gpio25_pins[] = { 38 }; ++static const int gpio26_pins[] = { 39 }; ++static const int gpio27_pins[] = { 40 }; ++static const int gpio28_pins[] = { 41 }; ++static const int gpio29_pins[] = { 42 }; ++static const int gpio30_pins[] = { 43 }; ++static const int gpio31_pins[] = { 44 }; ++static const int gpio33_pins[] = { 46 }; ++static const int gpio34_pins[] = { 47 }; ++static const int gpio35_pins[] = { 48 }; ++static const int gpio36_pins[] = { 49 }; ++static const int gpio37_pins[] = { 50 }; ++static const int gpio38_pins[] = { 51 }; ++static const int gpio39_pins[] = { 52 }; ++static const int gpio40_pins[] = { 53 }; ++static const int gpio41_pins[] = { 54 }; ++static const int gpio42_pins[] = { 55 }; ++static const int gpio43_pins[] = { 56 }; ++static const int gpio44_pins[] = { 57 }; ++static const int gpio45_pins[] = { 58 }; ++static const int gpio46_pins[] = { 59 }; ++static const int pcie_reset0_pins[] = { 61 }; ++static const int pcie_reset1_pins[] = { 62 }; ++static const int pcie_reset2_pins[] = { 63 }; ++ ++static const struct pingroup airoha_pinctrl_groups[] = { ++ PINCTRL_PIN_GROUP(pon), ++ PINCTRL_PIN_GROUP(pon_tod_1pps), ++ PINCTRL_PIN_GROUP(gsw_tod_1pps), ++ PINCTRL_PIN_GROUP(sipo), ++ PINCTRL_PIN_GROUP(sipo_rclk), ++ PINCTRL_PIN_GROUP(mdio), ++ PINCTRL_PIN_GROUP(uart2), ++ PINCTRL_PIN_GROUP(uart2_cts_rts), ++ PINCTRL_PIN_GROUP(hsuart), ++ PINCTRL_PIN_GROUP(hsuart_cts_rts), ++ PINCTRL_PIN_GROUP(uart4), ++ PINCTRL_PIN_GROUP(uart5), ++ PINCTRL_PIN_GROUP(i2c0), ++ PINCTRL_PIN_GROUP(i2c1), ++ PINCTRL_PIN_GROUP(jtag_udi), ++ PINCTRL_PIN_GROUP(jtag_dfd), ++ PINCTRL_PIN_GROUP(i2s), ++ PINCTRL_PIN_GROUP(pcm1), ++ PINCTRL_PIN_GROUP(pcm2), ++ PINCTRL_PIN_GROUP(spi), ++ PINCTRL_PIN_GROUP(spi_quad), ++ PINCTRL_PIN_GROUP(spi_cs1), ++ PINCTRL_PIN_GROUP(pcm_spi), ++ PINCTRL_PIN_GROUP(pcm_spi_int), ++ PINCTRL_PIN_GROUP(pcm_spi_rst), ++ PINCTRL_PIN_GROUP(pcm_spi_cs1), ++ PINCTRL_PIN_GROUP(pcm_spi_cs2_p128), ++ PINCTRL_PIN_GROUP(pcm_spi_cs2_p156), ++ PINCTRL_PIN_GROUP(pcm_spi_cs2), ++ PINCTRL_PIN_GROUP(pcm_spi_cs3), ++ PINCTRL_PIN_GROUP(pcm_spi_cs4), ++ PINCTRL_PIN_GROUP(emmc), ++ PINCTRL_PIN_GROUP(pnand), ++ PINCTRL_PIN_GROUP(gpio0), ++ PINCTRL_PIN_GROUP(gpio1), ++ PINCTRL_PIN_GROUP(gpio2), ++ PINCTRL_PIN_GROUP(gpio3), ++ PINCTRL_PIN_GROUP(gpio4), ++ PINCTRL_PIN_GROUP(gpio5), ++ PINCTRL_PIN_GROUP(gpio6), ++ PINCTRL_PIN_GROUP(gpio7), ++ PINCTRL_PIN_GROUP(gpio8), ++ PINCTRL_PIN_GROUP(gpio9), ++ PINCTRL_PIN_GROUP(gpio10), ++ PINCTRL_PIN_GROUP(gpio11), ++ PINCTRL_PIN_GROUP(gpio12), ++ PINCTRL_PIN_GROUP(gpio13), ++ PINCTRL_PIN_GROUP(gpio14), ++ PINCTRL_PIN_GROUP(gpio15), ++ PINCTRL_PIN_GROUP(gpio16), ++ PINCTRL_PIN_GROUP(gpio17), ++ PINCTRL_PIN_GROUP(gpio18), ++ PINCTRL_PIN_GROUP(gpio19), ++ PINCTRL_PIN_GROUP(gpio20), ++ PINCTRL_PIN_GROUP(gpio21), ++ PINCTRL_PIN_GROUP(gpio22), ++ PINCTRL_PIN_GROUP(gpio23), ++ PINCTRL_PIN_GROUP(gpio24), ++ PINCTRL_PIN_GROUP(gpio25), ++ PINCTRL_PIN_GROUP(gpio26), ++ PINCTRL_PIN_GROUP(gpio27), ++ PINCTRL_PIN_GROUP(gpio28), ++ PINCTRL_PIN_GROUP(gpio29), ++ PINCTRL_PIN_GROUP(gpio30), ++ PINCTRL_PIN_GROUP(gpio31), ++ PINCTRL_PIN_GROUP(gpio33), ++ PINCTRL_PIN_GROUP(gpio34), ++ PINCTRL_PIN_GROUP(gpio35), ++ PINCTRL_PIN_GROUP(gpio36), ++ PINCTRL_PIN_GROUP(gpio37), ++ PINCTRL_PIN_GROUP(gpio38), ++ PINCTRL_PIN_GROUP(gpio39), ++ PINCTRL_PIN_GROUP(gpio40), ++ PINCTRL_PIN_GROUP(gpio41), ++ PINCTRL_PIN_GROUP(gpio42), ++ PINCTRL_PIN_GROUP(gpio43), ++ PINCTRL_PIN_GROUP(gpio44), ++ PINCTRL_PIN_GROUP(gpio45), ++ PINCTRL_PIN_GROUP(gpio46), ++ PINCTRL_PIN_GROUP(pcie_reset0), ++ PINCTRL_PIN_GROUP(pcie_reset1), ++ PINCTRL_PIN_GROUP(pcie_reset2), ++}; ++ ++static const char *const pon_groups[] = { "pon" }; ++static const char *const tod_1pps_groups[] = { "pon_tod_1pps", "gsw_tod_1pps" }; ++static const char *const sipo_groups[] = { "sipo", "sipo_rclk" }; ++static const char *const mdio_groups[] = { "mdio" }; ++static const char *const uart_groups[] = { "uart2", "uart2_cts_rts", "hsuart", ++ "hsuart_cts_rts", "uart4", ++ "uart5" }; ++static const char *const i2c_groups[] = { "i2c1" }; ++static const char *const jtag_groups[] = { "jtag_udi", "jtag_dfd" }; ++static const char *const pcm_groups[] = { "pcm1", "pcm2" }; ++static const char *const spi_groups[] = { "spi_quad", "spi_cs1" }; ++static const char *const pcm_spi_groups[] = { "pcm_spi", "pcm_spi_int", ++ "pcm_spi_rst", "pcm_spi_cs1", ++ "pcm_spi_cs2_p156", ++ "pcm_spi_cs2_p128", ++ "pcm_spi_cs3", "pcm_spi_cs4" }; ++static const char *const i2s_groups[] = { "i2s" }; ++static const char *const emmc_groups[] = { "emmc" }; ++static const char *const pnand_groups[] = { "pnand" }; ++static const char *const pcie_reset_groups[] = { "pcie_reset0", "pcie_reset1", ++ "pcie_reset2" }; ++static const char *const pwm_groups[] = { "gpio0", "gpio1", ++ "gpio2", "gpio3", ++ "gpio4", "gpio5", ++ "gpio6", "gpio7", ++ "gpio8", "gpio9", ++ "gpio10", "gpio11", ++ "gpio12", "gpio13", ++ "gpio14", "gpio15", ++ "gpio16", "gpio17", ++ "gpio18", "gpio19", ++ "gpio20", "gpio21", ++ "gpio22", "gpio23", ++ "gpio24", "gpio25", ++ "gpio26", "gpio27", ++ "gpio28", "gpio29", ++ "gpio30", "gpio31", ++ "gpio36", "gpio37", ++ "gpio38", "gpio39", ++ "gpio40", "gpio41", ++ "gpio42", "gpio43", ++ "gpio44", "gpio45", ++ "gpio46", "gpio47" }; ++static const char *const phy1_led0_groups[] = { "gpio33", "gpio34", ++ "gpio35", "gpio42" }; ++static const char *const phy2_led0_groups[] = { "gpio33", "gpio34", ++ "gpio35", "gpio42" }; ++static const char *const phy3_led0_groups[] = { "gpio33", "gpio34", ++ "gpio35", "gpio42" }; ++static const char *const phy4_led0_groups[] = { "gpio33", "gpio34", ++ "gpio35", "gpio42" }; ++static const char *const phy1_led1_groups[] = { "gpio43", "gpio44", ++ "gpio45", "gpio46" }; ++static const char *const phy2_led1_groups[] = { "gpio43", "gpio44", ++ "gpio45", "gpio46" }; ++static const char *const phy3_led1_groups[] = { "gpio43", "gpio44", ++ "gpio45", "gpio46" }; ++static const char *const phy4_led1_groups[] = { "gpio43", "gpio44", ++ "gpio45", "gpio46" }; ++ ++static const struct airoha_pinctrl_func_group pon_func_group[] = { ++ { ++ .name = "pon", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PON_MODE_MASK, ++ GPIO_PON_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group tod_1pps_func_group[] = { ++ { ++ .name = "pon_tod_1pps", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ PON_TOD_1PPS_MODE_MASK, ++ PON_TOD_1PPS_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gsw_tod_1pps", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GSW_TOD_1PPS_MODE_MASK, ++ GSW_TOD_1PPS_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group sipo_func_group[] = { ++ { ++ .name = "sipo", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK, ++ GPIO_SIPO_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "sipo_rclk", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK, ++ GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group mdio_func_group[] = { ++ { ++ .name = "mdio", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_SGMII_MDIO_MODE_MASK, ++ GPIO_SGMII_MDIO_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_MDC_IO_MASTER_MODE_MODE, ++ GPIO_MDC_IO_MASTER_MODE_MODE ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group uart_func_group[] = { ++ { ++ .name = "uart2", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_UART2_MODE_MASK, ++ GPIO_UART2_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "uart2_cts_rts", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_UART2_MODE_MASK | GPIO_UART2_CTS_RTS_MODE_MASK, ++ GPIO_UART2_MODE_MASK | GPIO_UART2_CTS_RTS_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "hsuart", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK, ++ GPIO_HSUART_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++ { ++ .name = "hsuart_cts_rts", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK, ++ GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "uart4", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_UART4_MODE_MASK, ++ GPIO_UART4_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "uart5", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_UART5_MODE_MASK, ++ GPIO_UART5_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group i2c_func_group[] = { ++ { ++ .name = "i2c1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_2ND_I2C_MODE_MASK, ++ GPIO_2ND_I2C_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group jtag_func_group[] = { ++ { ++ .name = "jtag_udi", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_NPU_UART_EN, ++ JTAG_UDI_EN_MASK, ++ JTAG_UDI_EN_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "jtag_dfd", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_NPU_UART_EN, ++ JTAG_DFD_EN_MASK, ++ JTAG_DFD_EN_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group pcm_func_group[] = { ++ { ++ .name = "pcm1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM1_MODE_MASK, ++ GPIO_PCM1_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm2", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM2_MODE_MASK, ++ GPIO_PCM2_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group spi_func_group[] = { ++ { ++ .name = "spi_quad", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_SPI_QUAD_MODE_MASK, ++ GPIO_SPI_QUAD_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "spi_cs1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_SPI_CS1_MODE_MASK, ++ GPIO_SPI_CS1_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "spi_cs2", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_SPI_CS2_MODE_MASK, ++ GPIO_SPI_CS2_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "spi_cs3", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_SPI_CS3_MODE_MASK, ++ GPIO_SPI_CS3_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "spi_cs4", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_SPI_CS4_MODE_MASK, ++ GPIO_SPI_CS4_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group pcm_spi_func_group[] = { ++ { ++ .name = "pcm_spi", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_MODE_MASK, ++ GPIO_PCM_SPI_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_int", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_INT_MODE_MASK, ++ GPIO_PCM_INT_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_rst", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_RESET_MODE_MASK, ++ GPIO_PCM_RESET_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS1_MODE_MASK, ++ GPIO_PCM_SPI_CS1_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs2_p128", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS2_MODE_P128_MASK, ++ GPIO_PCM_SPI_CS2_MODE_P128_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs2_p156", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS2_MODE_P156_MASK, ++ GPIO_PCM_SPI_CS2_MODE_P156_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs3", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS3_MODE_MASK, ++ GPIO_PCM_SPI_CS3_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs4", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS4_MODE_MASK, ++ GPIO_PCM_SPI_CS4_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group i2s_func_group[] = { ++ { ++ .name = "i2s", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_I2S_MODE_MASK, ++ GPIO_I2S_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group emmc_func_group[] = { ++ { ++ .name = "emmc", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_EMMC_MODE_MASK, ++ GPIO_EMMC_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group pnand_func_group[] = { ++ { ++ .name = "pnand", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PARALLEL_NAND_MODE_MASK, ++ GPIO_PARALLEL_NAND_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group pcie_reset_func_group[] = { ++ { ++ .name = "pcie_reset0", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PCIE_RESET0_MASK, ++ GPIO_PCIE_RESET0_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcie_reset1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PCIE_RESET1_MASK, ++ GPIO_PCIE_RESET1_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcie_reset2", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PCIE_RESET2_MASK, ++ GPIO_PCIE_RESET2_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++/* PWM */ ++static const struct airoha_pinctrl_func_group pwm_func_group[] = { ++ { ++ .name = "gpio0", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO0_FLASH_MODE_CFG, ++ GPIO0_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio1", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO1_FLASH_MODE_CFG, ++ GPIO1_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio2", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO2_FLASH_MODE_CFG, ++ GPIO2_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio3", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO3_FLASH_MODE_CFG, ++ GPIO3_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio4", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO4_FLASH_MODE_CFG, ++ GPIO4_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio5", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO5_FLASH_MODE_CFG, ++ GPIO5_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio6", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO6_FLASH_MODE_CFG, ++ GPIO6_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio7", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO7_FLASH_MODE_CFG, ++ GPIO7_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio8", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO8_FLASH_MODE_CFG, ++ GPIO8_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio9", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO9_FLASH_MODE_CFG, ++ GPIO9_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio10", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO10_FLASH_MODE_CFG, ++ GPIO10_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio11", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO11_FLASH_MODE_CFG, ++ GPIO11_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio12", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO12_FLASH_MODE_CFG, ++ GPIO12_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio13", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO13_FLASH_MODE_CFG, ++ GPIO13_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio14", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO14_FLASH_MODE_CFG, ++ GPIO14_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio15", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO15_FLASH_MODE_CFG, ++ GPIO15_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio16", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO16_FLASH_MODE_CFG, ++ GPIO16_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio17", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO17_FLASH_MODE_CFG, ++ GPIO17_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio18", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO18_FLASH_MODE_CFG, ++ GPIO18_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio19", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO19_FLASH_MODE_CFG, ++ GPIO19_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio20", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO20_FLASH_MODE_CFG, ++ GPIO20_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio21", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO21_FLASH_MODE_CFG, ++ GPIO21_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio22", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO22_FLASH_MODE_CFG, ++ GPIO22_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio23", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO23_FLASH_MODE_CFG, ++ GPIO23_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio24", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO24_FLASH_MODE_CFG, ++ GPIO24_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio25", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO25_FLASH_MODE_CFG, ++ GPIO25_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio26", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO26_FLASH_MODE_CFG, ++ GPIO26_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio27", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO27_FLASH_MODE_CFG, ++ GPIO27_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio28", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO28_FLASH_MODE_CFG, ++ GPIO28_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio29", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO29_FLASH_MODE_CFG, ++ GPIO29_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio30", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO30_FLASH_MODE_CFG, ++ GPIO30_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio31", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO31_FLASH_MODE_CFG, ++ GPIO31_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio36", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO36_FLASH_MODE_CFG, ++ GPIO36_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio37", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO37_FLASH_MODE_CFG, ++ GPIO37_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio38", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO38_FLASH_MODE_CFG, ++ GPIO38_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio39", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO39_FLASH_MODE_CFG, ++ GPIO39_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio40", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO40_FLASH_MODE_CFG, ++ GPIO40_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio41", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO41_FLASH_MODE_CFG, ++ GPIO41_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio42", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO42_FLASH_MODE_CFG, ++ GPIO42_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio43", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO43_FLASH_MODE_CFG, ++ GPIO43_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio44", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO44_FLASH_MODE_CFG, ++ GPIO44_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio45", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO45_FLASH_MODE_CFG, ++ GPIO45_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio46", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO46_FLASH_MODE_CFG, ++ GPIO46_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio47", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO47_FLASH_MODE_CFG, ++ GPIO47_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy1_led0_func_group[] = { ++ { ++ .name = "gpio33", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED0_MODE_MASK, ++ GPIO_LAN0_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio34", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED0_MODE_MASK, ++ GPIO_LAN1_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio35", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED0_MODE_MASK, ++ GPIO_LAN2_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio42", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy2_led0_func_group[] = { ++ { ++ .name = "gpio33", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED0_MODE_MASK, ++ GPIO_LAN0_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio34", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED0_MODE_MASK, ++ GPIO_LAN1_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio35", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED0_MODE_MASK, ++ GPIO_LAN2_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio42", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy3_led0_func_group[] = { ++ { ++ .name = "gpio33", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED0_MODE_MASK, ++ GPIO_LAN0_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio34", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED0_MODE_MASK, ++ GPIO_LAN1_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio35", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED0_MODE_MASK, ++ GPIO_LAN2_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio42", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy4_led0_func_group[] = { ++ { ++ .name = "gpio33", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED0_MODE_MASK, ++ GPIO_LAN0_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio34", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED0_MODE_MASK, ++ GPIO_LAN1_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio35", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED0_MODE_MASK, ++ GPIO_LAN2_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio42", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy1_led1_func_group[] = { ++ { ++ .name = "gpio43", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED1_MODE_MASK, ++ GPIO_LAN0_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio44", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED1_MODE_MASK, ++ GPIO_LAN1_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio45", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED1_MODE_MASK, ++ GPIO_LAN2_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio46", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy2_led1_func_group[] = { ++ { ++ .name = "gpio43", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED1_MODE_MASK, ++ GPIO_LAN0_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio44", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED1_MODE_MASK, ++ GPIO_LAN1_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio45", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED1_MODE_MASK, ++ GPIO_LAN2_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio46", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy3_led1_func_group[] = { ++ { ++ .name = "gpio43", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED1_MODE_MASK, ++ GPIO_LAN0_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio44", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED1_MODE_MASK, ++ GPIO_LAN1_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio45", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED1_MODE_MASK, ++ GPIO_LAN2_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio46", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy4_led1_func_group[] = { ++ { ++ .name = "gpio43", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED1_MODE_MASK, ++ GPIO_LAN0_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio44", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED1_MODE_MASK, ++ GPIO_LAN1_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio45", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED1_MODE_MASK, ++ GPIO_LAN2_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio46", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func airoha_pinctrl_funcs[] = { ++ PINCTRL_FUNC_DESC(pon), ++ PINCTRL_FUNC_DESC(tod_1pps), ++ PINCTRL_FUNC_DESC(sipo), ++ PINCTRL_FUNC_DESC(mdio), ++ PINCTRL_FUNC_DESC(uart), ++ PINCTRL_FUNC_DESC(i2c), ++ PINCTRL_FUNC_DESC(jtag), ++ PINCTRL_FUNC_DESC(pcm), ++ PINCTRL_FUNC_DESC(spi), ++ PINCTRL_FUNC_DESC(pcm_spi), ++ PINCTRL_FUNC_DESC(i2s), ++ PINCTRL_FUNC_DESC(emmc), ++ PINCTRL_FUNC_DESC(pnand), ++ PINCTRL_FUNC_DESC(pcie_reset), ++ PINCTRL_FUNC_DESC(pwm), ++ PINCTRL_FUNC_DESC(phy1_led0), ++ PINCTRL_FUNC_DESC(phy2_led0), ++ PINCTRL_FUNC_DESC(phy3_led0), ++ PINCTRL_FUNC_DESC(phy4_led0), ++ PINCTRL_FUNC_DESC(phy1_led1), ++ PINCTRL_FUNC_DESC(phy2_led1), ++ PINCTRL_FUNC_DESC(phy3_led1), ++ PINCTRL_FUNC_DESC(phy4_led1), ++}; ++ ++static const struct airoha_pinctrl_conf airoha_pinctrl_pullup_conf[] = { ++ PINCTRL_CONF_DESC(0, REG_I2C_SDA_PU, UART1_TXD_PU_MASK), ++ PINCTRL_CONF_DESC(1, REG_I2C_SDA_PU, UART1_RXD_PU_MASK), ++ PINCTRL_CONF_DESC(2, REG_I2C_SDA_PU, I2C_SDA_PU_MASK), ++ PINCTRL_CONF_DESC(3, REG_I2C_SDA_PU, I2C_SCL_PU_MASK), ++ PINCTRL_CONF_DESC(4, REG_I2C_SDA_PU, SPI_CS0_PU_MASK), ++ PINCTRL_CONF_DESC(5, REG_I2C_SDA_PU, SPI_CLK_PU_MASK), ++ PINCTRL_CONF_DESC(6, REG_I2C_SDA_PU, SPI_MOSI_PU_MASK), ++ PINCTRL_CONF_DESC(7, REG_I2C_SDA_PU, SPI_MISO_PU_MASK), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_PU, BIT(0)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_PU, BIT(1)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_PU, BIT(2)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_PU, BIT(3)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_PU, BIT(4)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_PU, BIT(5)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_PU, BIT(6)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_PU, BIT(7)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_PU, BIT(8)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_PU, BIT(9)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_PU, BIT(10)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_PU, BIT(11)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_PU, BIT(12)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_PU, BIT(13)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_PU, BIT(14)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_PU, BIT(15)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_PU, BIT(16)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_PU, BIT(17)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_PU, BIT(18)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(18)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_PU, BIT(20)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_L_PU, BIT(21)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_L_PU, BIT(22)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_L_PU, BIT(23)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_L_PU, BIT(24)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_L_PU, BIT(25)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_L_PU, BIT(26)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_L_PU, BIT(27)), ++ PINCTRL_CONF_DESC(41, REG_GPIO_L_PU, BIT(28)), ++ PINCTRL_CONF_DESC(42, REG_GPIO_L_PU, BIT(29)), ++ PINCTRL_CONF_DESC(43, REG_GPIO_L_PU, BIT(30)), ++ PINCTRL_CONF_DESC(44, REG_GPIO_L_PU, BIT(31)), ++ PINCTRL_CONF_DESC(45, REG_GPIO_H_PU, BIT(0)), ++ PINCTRL_CONF_DESC(46, REG_GPIO_H_PU, BIT(1)), ++ PINCTRL_CONF_DESC(47, REG_GPIO_H_PU, BIT(2)), ++ PINCTRL_CONF_DESC(48, REG_GPIO_H_PU, BIT(3)), ++ PINCTRL_CONF_DESC(49, REG_GPIO_H_PU, BIT(4)), ++ PINCTRL_CONF_DESC(50, REG_GPIO_H_PU, BIT(5)), ++ PINCTRL_CONF_DESC(51, REG_GPIO_H_PU, BIT(6)), ++ PINCTRL_CONF_DESC(52, REG_GPIO_H_PU, BIT(7)), ++ PINCTRL_CONF_DESC(53, REG_GPIO_H_PU, BIT(8)), ++ PINCTRL_CONF_DESC(54, REG_GPIO_H_PU, BIT(9)), ++ PINCTRL_CONF_DESC(55, REG_GPIO_H_PU, BIT(10)), ++ PINCTRL_CONF_DESC(56, REG_GPIO_H_PU, BIT(11)), ++ PINCTRL_CONF_DESC(57, REG_GPIO_H_PU, BIT(12)), ++ PINCTRL_CONF_DESC(58, REG_GPIO_H_PU, BIT(13)), ++ PINCTRL_CONF_DESC(59, REG_GPIO_H_PU, BIT(14)), ++ PINCTRL_CONF_DESC(61, REG_I2C_SDA_PU, PCIE0_RESET_PU_MASK), ++ PINCTRL_CONF_DESC(62, REG_I2C_SDA_PU, PCIE1_RESET_PU_MASK), ++ PINCTRL_CONF_DESC(63, REG_I2C_SDA_PU, PCIE2_RESET_PU_MASK), ++}; ++ ++static const struct airoha_pinctrl_conf airoha_pinctrl_pulldown_conf[] = { ++ PINCTRL_CONF_DESC(0, REG_I2C_SDA_PD, UART1_TXD_PD_MASK), ++ PINCTRL_CONF_DESC(1, REG_I2C_SDA_PD, UART1_RXD_PD_MASK), ++ PINCTRL_CONF_DESC(2, REG_I2C_SDA_PD, I2C_SDA_PD_MASK), ++ PINCTRL_CONF_DESC(3, REG_I2C_SDA_PD, I2C_SCL_PD_MASK), ++ PINCTRL_CONF_DESC(4, REG_I2C_SDA_PD, SPI_CS0_PD_MASK), ++ PINCTRL_CONF_DESC(5, REG_I2C_SDA_PD, SPI_CLK_PD_MASK), ++ PINCTRL_CONF_DESC(6, REG_I2C_SDA_PD, SPI_MOSI_PD_MASK), ++ PINCTRL_CONF_DESC(7, REG_I2C_SDA_PD, SPI_MISO_PD_MASK), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_PD, BIT(0)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_PD, BIT(1)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_PD, BIT(2)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_PD, BIT(3)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_PD, BIT(4)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_PD, BIT(5)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_PD, BIT(6)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_PD, BIT(7)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_PD, BIT(8)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_PD, BIT(9)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_PD, BIT(10)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_PD, BIT(11)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_PD, BIT(12)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_PD, BIT(13)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_PD, BIT(14)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_PD, BIT(15)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_PD, BIT(16)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_PD, BIT(17)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_PD, BIT(18)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(18)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_PD, BIT(20)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_L_PD, BIT(21)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_L_PD, BIT(22)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_L_PD, BIT(23)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_L_PD, BIT(24)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_L_PD, BIT(25)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_L_PD, BIT(26)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_L_PD, BIT(27)), ++ PINCTRL_CONF_DESC(41, REG_GPIO_L_PD, BIT(28)), ++ PINCTRL_CONF_DESC(42, REG_GPIO_L_PD, BIT(29)), ++ PINCTRL_CONF_DESC(43, REG_GPIO_L_PD, BIT(30)), ++ PINCTRL_CONF_DESC(44, REG_GPIO_L_PD, BIT(31)), ++ PINCTRL_CONF_DESC(45, REG_GPIO_H_PD, BIT(0)), ++ PINCTRL_CONF_DESC(46, REG_GPIO_H_PD, BIT(1)), ++ PINCTRL_CONF_DESC(47, REG_GPIO_H_PD, BIT(2)), ++ PINCTRL_CONF_DESC(48, REG_GPIO_H_PD, BIT(3)), ++ PINCTRL_CONF_DESC(49, REG_GPIO_H_PD, BIT(4)), ++ PINCTRL_CONF_DESC(50, REG_GPIO_H_PD, BIT(5)), ++ PINCTRL_CONF_DESC(51, REG_GPIO_H_PD, BIT(6)), ++ PINCTRL_CONF_DESC(52, REG_GPIO_H_PD, BIT(7)), ++ PINCTRL_CONF_DESC(53, REG_GPIO_H_PD, BIT(8)), ++ PINCTRL_CONF_DESC(54, REG_GPIO_H_PD, BIT(9)), ++ PINCTRL_CONF_DESC(55, REG_GPIO_H_PD, BIT(10)), ++ PINCTRL_CONF_DESC(56, REG_GPIO_H_PD, BIT(11)), ++ PINCTRL_CONF_DESC(57, REG_GPIO_H_PD, BIT(12)), ++ PINCTRL_CONF_DESC(58, REG_GPIO_H_PD, BIT(13)), ++ PINCTRL_CONF_DESC(59, REG_GPIO_H_PD, BIT(14)), ++ PINCTRL_CONF_DESC(61, REG_I2C_SDA_PD, PCIE0_RESET_PD_MASK), ++ PINCTRL_CONF_DESC(62, REG_I2C_SDA_PD, PCIE1_RESET_PD_MASK), ++ PINCTRL_CONF_DESC(63, REG_I2C_SDA_PD, PCIE2_RESET_PD_MASK), ++}; ++ ++static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e2_conf[] = { ++ PINCTRL_CONF_DESC(0, REG_I2C_SDA_E2, UART1_TXD_E2_MASK), ++ PINCTRL_CONF_DESC(1, REG_I2C_SDA_E2, UART1_RXD_E2_MASK), ++ PINCTRL_CONF_DESC(2, REG_I2C_SDA_E2, I2C_SDA_E2_MASK), ++ PINCTRL_CONF_DESC(3, REG_I2C_SDA_E2, I2C_SCL_E2_MASK), ++ PINCTRL_CONF_DESC(4, REG_I2C_SDA_E2, SPI_CS0_E2_MASK), ++ PINCTRL_CONF_DESC(5, REG_I2C_SDA_E2, SPI_CLK_E2_MASK), ++ PINCTRL_CONF_DESC(6, REG_I2C_SDA_E2, SPI_MOSI_E2_MASK), ++ PINCTRL_CONF_DESC(7, REG_I2C_SDA_E2, SPI_MISO_E2_MASK), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_E2, BIT(0)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_E2, BIT(1)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_E2, BIT(2)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_E2, BIT(3)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_E2, BIT(4)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_E2, BIT(5)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_E2, BIT(6)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_E2, BIT(7)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_E2, BIT(8)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_E2, BIT(9)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_E2, BIT(10)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_E2, BIT(11)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_E2, BIT(12)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_E2, BIT(13)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_E2, BIT(14)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_E2, BIT(15)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_E2, BIT(16)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_E2, BIT(17)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_E2, BIT(18)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(18)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_E2, BIT(20)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_L_E2, BIT(21)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_L_E2, BIT(22)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_L_E2, BIT(23)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_L_E2, BIT(24)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_L_E2, BIT(25)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_L_E2, BIT(26)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_L_E2, BIT(27)), ++ PINCTRL_CONF_DESC(41, REG_GPIO_L_E2, BIT(28)), ++ PINCTRL_CONF_DESC(42, REG_GPIO_L_E2, BIT(29)), ++ PINCTRL_CONF_DESC(43, REG_GPIO_L_E2, BIT(30)), ++ PINCTRL_CONF_DESC(44, REG_GPIO_L_E2, BIT(31)), ++ PINCTRL_CONF_DESC(45, REG_GPIO_H_E2, BIT(0)), ++ PINCTRL_CONF_DESC(46, REG_GPIO_H_E2, BIT(1)), ++ PINCTRL_CONF_DESC(47, REG_GPIO_H_E2, BIT(2)), ++ PINCTRL_CONF_DESC(48, REG_GPIO_H_E2, BIT(3)), ++ PINCTRL_CONF_DESC(49, REG_GPIO_H_E2, BIT(4)), ++ PINCTRL_CONF_DESC(50, REG_GPIO_H_E2, BIT(5)), ++ PINCTRL_CONF_DESC(51, REG_GPIO_H_E2, BIT(6)), ++ PINCTRL_CONF_DESC(52, REG_GPIO_H_E2, BIT(7)), ++ PINCTRL_CONF_DESC(53, REG_GPIO_H_E2, BIT(8)), ++ PINCTRL_CONF_DESC(54, REG_GPIO_H_E2, BIT(9)), ++ PINCTRL_CONF_DESC(55, REG_GPIO_H_E2, BIT(10)), ++ PINCTRL_CONF_DESC(56, REG_GPIO_H_E2, BIT(11)), ++ PINCTRL_CONF_DESC(57, REG_GPIO_H_E2, BIT(12)), ++ PINCTRL_CONF_DESC(58, REG_GPIO_H_E2, BIT(13)), ++ PINCTRL_CONF_DESC(59, REG_GPIO_H_E2, BIT(14)), ++ PINCTRL_CONF_DESC(61, REG_I2C_SDA_E2, PCIE0_RESET_E2_MASK), ++ PINCTRL_CONF_DESC(62, REG_I2C_SDA_E2, PCIE1_RESET_E2_MASK), ++ PINCTRL_CONF_DESC(63, REG_I2C_SDA_E2, PCIE2_RESET_E2_MASK), ++}; ++ ++static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e4_conf[] = { ++ PINCTRL_CONF_DESC(0, REG_I2C_SDA_E4, UART1_TXD_E4_MASK), ++ PINCTRL_CONF_DESC(1, REG_I2C_SDA_E4, UART1_RXD_E4_MASK), ++ PINCTRL_CONF_DESC(2, REG_I2C_SDA_E4, I2C_SDA_E4_MASK), ++ PINCTRL_CONF_DESC(3, REG_I2C_SDA_E4, I2C_SCL_E4_MASK), ++ PINCTRL_CONF_DESC(4, REG_I2C_SDA_E4, SPI_CS0_E4_MASK), ++ PINCTRL_CONF_DESC(5, REG_I2C_SDA_E4, SPI_CLK_E4_MASK), ++ PINCTRL_CONF_DESC(6, REG_I2C_SDA_E4, SPI_MOSI_E4_MASK), ++ PINCTRL_CONF_DESC(7, REG_I2C_SDA_E4, SPI_MISO_E4_MASK), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_E4, BIT(0)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_E4, BIT(1)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_E4, BIT(2)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_E4, BIT(3)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_E4, BIT(4)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_E4, BIT(5)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_E4, BIT(6)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_E4, BIT(7)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_E4, BIT(8)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_E4, BIT(9)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_E4, BIT(10)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_E4, BIT(11)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_E4, BIT(12)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_E4, BIT(13)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_E4, BIT(14)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_E4, BIT(15)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_E4, BIT(16)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_E4, BIT(17)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_E4, BIT(18)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(18)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_E4, BIT(20)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_L_E4, BIT(21)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_L_E4, BIT(22)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_L_E4, BIT(23)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_L_E4, BIT(24)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_L_E4, BIT(25)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_L_E4, BIT(26)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_L_E4, BIT(27)), ++ PINCTRL_CONF_DESC(41, REG_GPIO_L_E4, BIT(28)), ++ PINCTRL_CONF_DESC(42, REG_GPIO_L_E4, BIT(29)), ++ PINCTRL_CONF_DESC(43, REG_GPIO_L_E4, BIT(30)), ++ PINCTRL_CONF_DESC(44, REG_GPIO_L_E4, BIT(31)), ++ PINCTRL_CONF_DESC(45, REG_GPIO_H_E4, BIT(0)), ++ PINCTRL_CONF_DESC(46, REG_GPIO_H_E4, BIT(1)), ++ PINCTRL_CONF_DESC(47, REG_GPIO_H_E4, BIT(2)), ++ PINCTRL_CONF_DESC(48, REG_GPIO_H_E4, BIT(3)), ++ PINCTRL_CONF_DESC(49, REG_GPIO_H_E4, BIT(4)), ++ PINCTRL_CONF_DESC(50, REG_GPIO_H_E4, BIT(5)), ++ PINCTRL_CONF_DESC(51, REG_GPIO_H_E4, BIT(6)), ++ PINCTRL_CONF_DESC(52, REG_GPIO_H_E4, BIT(7)), ++ PINCTRL_CONF_DESC(53, REG_GPIO_H_E4, BIT(8)), ++ PINCTRL_CONF_DESC(54, REG_GPIO_H_E4, BIT(9)), ++ PINCTRL_CONF_DESC(55, REG_GPIO_H_E4, BIT(10)), ++ PINCTRL_CONF_DESC(56, REG_GPIO_H_E4, BIT(11)), ++ PINCTRL_CONF_DESC(57, REG_GPIO_H_E4, BIT(12)), ++ PINCTRL_CONF_DESC(58, REG_GPIO_H_E4, BIT(13)), ++ PINCTRL_CONF_DESC(59, REG_GPIO_H_E4, BIT(14)), ++ PINCTRL_CONF_DESC(61, REG_I2C_SDA_E4, PCIE0_RESET_E4_MASK), ++ PINCTRL_CONF_DESC(62, REG_I2C_SDA_E4, PCIE1_RESET_E4_MASK), ++ PINCTRL_CONF_DESC(63, REG_I2C_SDA_E4, PCIE2_RESET_E4_MASK), ++}; ++ ++static const struct airoha_pinctrl_conf airoha_pinctrl_pcie_rst_od_conf[] = { ++ PINCTRL_CONF_DESC(61, REG_PCIE_RESET_OD, PCIE0_RESET_OD_MASK), ++ PINCTRL_CONF_DESC(62, REG_PCIE_RESET_OD, PCIE1_RESET_OD_MASK), ++ PINCTRL_CONF_DESC(63, REG_PCIE_RESET_OD, PCIE2_RESET_OD_MASK), ++}; ++ ++static int airoha_convert_pin_to_reg_offset(struct pinctrl_dev *pctrl_dev, ++ struct pinctrl_gpio_range *range, ++ int pin) ++{ ++ if (!range) ++ range = pinctrl_find_gpio_range_from_pin_nolock(pctrl_dev, ++ pin); ++ if (!range) ++ return -EINVAL; ++ ++ return pin - range->pin_base; ++} ++ ++/* gpio callbacks */ ++static void airoha_gpio_set(struct gpio_chip *chip, unsigned int gpio, ++ int value) ++{ ++ struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip); ++ u32 offset = gpio % AIROHA_PIN_BANK_SIZE; ++ u8 index = gpio / AIROHA_PIN_BANK_SIZE; ++ ++ regmap_update_bits(pinctrl->regmap, pinctrl->gpiochip.data[index], ++ BIT(offset), value ? BIT(offset) : 0); ++} ++ ++static int airoha_gpio_get(struct gpio_chip *chip, unsigned int gpio) ++{ ++ struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip); ++ u32 val, pin = gpio % AIROHA_PIN_BANK_SIZE; ++ u8 index = gpio / AIROHA_PIN_BANK_SIZE; ++ int err; ++ ++ err = regmap_read(pinctrl->regmap, ++ pinctrl->gpiochip.data[index], &val); ++ ++ return err ? err : !!(val & BIT(pin)); ++} ++ ++static int airoha_gpio_direction_output(struct gpio_chip *chip, ++ unsigned int gpio, int value) ++{ ++ int err; ++ ++ err = pinctrl_gpio_direction_output(chip->base + gpio); ++ if (err) ++ return err; ++ ++ airoha_gpio_set(chip, gpio, value); ++ ++ return 0; ++} ++ ++/* irq callbacks */ ++static void airoha_irq_unmask(struct irq_data *data) ++{ ++ u8 offset = data->hwirq % AIROHA_REG_GPIOCTRL_NUM_PIN; ++ u8 index = data->hwirq / AIROHA_REG_GPIOCTRL_NUM_PIN; ++ u32 mask = GENMASK(2 * offset + 1, 2 * offset); ++ struct airoha_pinctrl_gpiochip *gpiochip; ++ struct airoha_pinctrl *pinctrl; ++ u32 val = BIT(2 * offset); ++ ++ gpiochip = irq_data_get_irq_chip_data(data); ++ if (WARN_ON_ONCE(data->hwirq >= ARRAY_SIZE(gpiochip->irq_type))) ++ return; ++ ++ pinctrl = container_of(gpiochip, struct airoha_pinctrl, gpiochip); ++ switch (gpiochip->irq_type[data->hwirq]) { ++ case IRQ_TYPE_LEVEL_LOW: ++ val = val << 1; ++ fallthrough; ++ case IRQ_TYPE_LEVEL_HIGH: ++ regmap_update_bits(pinctrl->regmap, gpiochip->level[index], ++ mask, val); ++ break; ++ case IRQ_TYPE_EDGE_FALLING: ++ val = val << 1; ++ fallthrough; ++ case IRQ_TYPE_EDGE_RISING: ++ regmap_update_bits(pinctrl->regmap, gpiochip->edge[index], ++ mask, val); ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ regmap_set_bits(pinctrl->regmap, gpiochip->edge[index], mask); ++ break; ++ default: ++ break; ++ } ++} ++ ++static void airoha_irq_mask(struct irq_data *data) ++{ ++ u8 offset = data->hwirq % AIROHA_REG_GPIOCTRL_NUM_PIN; ++ u8 index = data->hwirq / AIROHA_REG_GPIOCTRL_NUM_PIN; ++ u32 mask = GENMASK(2 * offset + 1, 2 * offset); ++ struct airoha_pinctrl_gpiochip *gpiochip; ++ struct airoha_pinctrl *pinctrl; ++ ++ gpiochip = irq_data_get_irq_chip_data(data); ++ pinctrl = container_of(gpiochip, struct airoha_pinctrl, gpiochip); ++ ++ regmap_clear_bits(pinctrl->regmap, gpiochip->level[index], mask); ++ regmap_clear_bits(pinctrl->regmap, gpiochip->edge[index], mask); ++} ++ ++static int airoha_irq_type(struct irq_data *data, unsigned int type) ++{ ++ struct airoha_pinctrl_gpiochip *gpiochip; ++ ++ gpiochip = irq_data_get_irq_chip_data(data); ++ if (data->hwirq >= ARRAY_SIZE(gpiochip->irq_type)) ++ return -EINVAL; ++ ++ if (type == IRQ_TYPE_PROBE) { ++ if (gpiochip->irq_type[data->hwirq]) ++ return 0; ++ ++ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; ++ } ++ gpiochip->irq_type[data->hwirq] = type & IRQ_TYPE_SENSE_MASK; ++ ++ return 0; ++} ++ ++static irqreturn_t airoha_irq_handler(int irq, void *data) ++{ ++ struct airoha_pinctrl *pinctrl = data; ++ bool handled = false; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(irq_status_regs); i++) { ++ struct gpio_irq_chip *girq = &pinctrl->gpiochip.chip.irq; ++ u32 status; ++ int irq; ++ ++ if (regmap_read(pinctrl->regmap, pinctrl->gpiochip.status[i], ++ &status)) ++ continue; ++ ++ for_each_set_bit(irq, (unsigned long *)&status, ++ AIROHA_PIN_BANK_SIZE) { ++ u32 offset = irq + i * AIROHA_PIN_BANK_SIZE; ++ ++ generic_handle_irq(irq_find_mapping(girq->domain, ++ offset)); ++ regmap_write(pinctrl->regmap, ++ pinctrl->gpiochip.status[i], BIT(irq)); ++ } ++ handled |= !!status; ++ } ++ ++ return handled ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++static const struct irq_chip airoha_gpio_irq_chip = { ++ .name = "airoha-gpio-irq", ++ .irq_unmask = airoha_irq_unmask, ++ .irq_mask = airoha_irq_mask, ++ .irq_mask_ack = airoha_irq_mask, ++ .irq_set_type = airoha_irq_type, ++ .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_IMMUTABLE, ++}; ++ ++static int airoha_pinctrl_gpio_direction_input(struct gpio_chip *chip, ++ unsigned int gpio) ++{ ++ return pinctrl_gpio_direction_input(chip->base + gpio); ++} ++ ++static int airoha_pinctrl_add_gpiochip(struct airoha_pinctrl *pinctrl, ++ struct platform_device *pdev) ++{ ++ struct airoha_pinctrl_gpiochip *chip = &pinctrl->gpiochip; ++ struct gpio_chip *gc = &chip->chip; ++ struct gpio_irq_chip *girq = &gc->irq; ++ struct device *dev = &pdev->dev; ++ int irq, err; ++ ++ chip->data = gpio_data_regs; ++ chip->dir = gpio_dir_regs; ++ chip->out = gpio_out_regs; ++ chip->status = irq_status_regs; ++ chip->level = irq_level_regs; ++ chip->edge = irq_edge_regs; ++ ++ gc->parent = dev; ++ gc->label = dev_name(dev); ++ gc->request = gpiochip_generic_request; ++ gc->free = gpiochip_generic_free; ++ gc->direction_input = airoha_pinctrl_gpio_direction_input; ++ gc->direction_output = airoha_gpio_direction_output; ++ gc->set = airoha_gpio_set; ++ gc->get = airoha_gpio_get; ++ gc->base = -1; ++ gc->ngpio = AIROHA_NUM_PINS; ++ ++ girq->default_type = IRQ_TYPE_NONE; ++ girq->handler = handle_simple_irq; ++ gpio_irq_chip_set_chip(girq, &airoha_gpio_irq_chip); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ err = devm_request_irq(dev, irq, airoha_irq_handler, IRQF_SHARED, ++ dev_name(dev), pinctrl); ++ if (err) { ++ dev_err(dev, "error requesting irq %d: %d\n", irq, err); ++ return err; ++ } ++ ++ return devm_gpiochip_add_data(dev, gc, pinctrl); ++} ++ ++/* pinmux callbacks */ ++static int airoha_pinmux_set_mux(struct pinctrl_dev *pctrl_dev, ++ unsigned int selector, ++ unsigned int group) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ const struct airoha_pinctrl_func *func; ++ struct function_desc *desc; ++ struct group_desc *grp; ++ int i; ++ ++ desc = pinmux_generic_get_function(pctrl_dev, selector); ++ if (!desc) ++ return -EINVAL; ++ ++ grp = pinctrl_generic_get_group(pctrl_dev, group); ++ if (!grp) ++ return -EINVAL; ++ ++ dev_dbg(pctrl_dev->dev, "enable function %s group %s\n", ++ desc->name, grp->name); ++ ++ func = desc->data; ++ for (i = 0; i < func->group_size; i++) { ++ const struct airoha_pinctrl_func_group *group; ++ int j; ++ ++ group = &func->groups[i]; ++ if (strcmp(group->name, grp->name)) ++ continue; ++ ++ for (j = 0; j < group->regmap_size; j++) { ++ switch (group->regmap[j].mux) { ++ case AIROHA_FUNC_PWM_EXT_MUX: ++ case AIROHA_FUNC_PWM_MUX: ++ regmap_update_bits(pinctrl->regmap, ++ group->regmap[j].offset, ++ group->regmap[j].mask, ++ group->regmap[j].val); ++ break; ++ default: ++ regmap_update_bits(pinctrl->chip_scu, ++ group->regmap[j].offset, ++ group->regmap[j].mask, ++ group->regmap[j].val); ++ break; ++ } ++ } ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int airoha_pinmux_set_direction(struct pinctrl_dev *pctrl_dev, ++ struct pinctrl_gpio_range *range, ++ unsigned int p, bool input) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ u32 mask, index; ++ int err, pin; ++ ++ pin = airoha_convert_pin_to_reg_offset(pctrl_dev, range, p); ++ if (pin < 0) ++ return pin; ++ ++ /* set output enable */ ++ mask = BIT(pin % AIROHA_PIN_BANK_SIZE); ++ index = pin / AIROHA_PIN_BANK_SIZE; ++ err = regmap_update_bits(pinctrl->regmap, pinctrl->gpiochip.out[index], ++ mask, !input ? mask : 0); ++ if (err) ++ return err; ++ ++ /* set direction */ ++ mask = BIT(2 * (pin % AIROHA_REG_GPIOCTRL_NUM_PIN)); ++ index = pin / AIROHA_REG_GPIOCTRL_NUM_PIN; ++ return regmap_update_bits(pinctrl->regmap, ++ pinctrl->gpiochip.dir[index], mask, ++ !input ? mask : 0); ++} ++ ++static const struct pinmux_ops airoha_pmxops = { ++ .get_functions_count = pinmux_generic_get_function_count, ++ .get_function_name = pinmux_generic_get_function_name, ++ .get_function_groups = pinmux_generic_get_function_groups, ++ .gpio_set_direction = airoha_pinmux_set_direction, ++ .set_mux = airoha_pinmux_set_mux, ++ .strict = true, ++}; ++ ++/* pinconf callbacks */ ++static const struct airoha_pinctrl_reg * ++airoha_pinctrl_get_conf_reg(const struct airoha_pinctrl_conf *conf, ++ int conf_size, int pin) ++{ ++ int i; ++ ++ for (i = 0; i < conf_size; i++) { ++ if (conf[i].pin == pin) ++ return &conf[i].reg; ++ } ++ ++ return NULL; ++} ++ ++static int airoha_pinctrl_get_conf(struct airoha_pinctrl *pinctrl, ++ const struct airoha_pinctrl_conf *conf, ++ int conf_size, int pin, u32 *val) ++{ ++ const struct airoha_pinctrl_reg *reg; ++ ++ reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin); ++ if (!reg) ++ return -EINVAL; ++ ++ if (regmap_read(pinctrl->chip_scu, reg->offset, val)) ++ return -EINVAL; ++ ++ *val = (*val & reg->mask) >> __ffs(reg->mask); ++ ++ return 0; ++} ++ ++static int airoha_pinctrl_set_conf(struct airoha_pinctrl *pinctrl, ++ const struct airoha_pinctrl_conf *conf, ++ int conf_size, int pin, u32 val) ++{ ++ const struct airoha_pinctrl_reg *reg = NULL; ++ ++ reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin); ++ if (!reg) ++ return -EINVAL; ++ ++ ++ if (regmap_update_bits(pinctrl->chip_scu, reg->offset, reg->mask, ++ val << __ffs(reg->mask))) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++#define airoha_pinctrl_get_pullup_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pullup_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pullup_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_get_pulldown_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pulldown_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pulldown_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e2_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_drive_e2_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e4_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_drive_e4_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_set_pullup_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pullup_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pullup_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_set_pulldown_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pulldown_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pulldown_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e2_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_drive_e2_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e4_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_drive_e4_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf), \ ++ (pin), (val)) ++ ++static int airoha_pinconf_get_direction(struct pinctrl_dev *pctrl_dev, u32 p) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ u32 val, mask; ++ int err, pin; ++ u8 index; ++ ++ pin = airoha_convert_pin_to_reg_offset(pctrl_dev, NULL, p); ++ if (pin < 0) ++ return pin; ++ ++ index = pin / AIROHA_REG_GPIOCTRL_NUM_PIN; ++ err = regmap_read(pinctrl->regmap, pinctrl->gpiochip.dir[index], &val); ++ if (err) ++ return err; ++ ++ mask = BIT(2 * (pin % AIROHA_REG_GPIOCTRL_NUM_PIN)); ++ return val & mask ? PIN_CONFIG_OUTPUT_ENABLE : PIN_CONFIG_INPUT_ENABLE; ++} ++ ++static int airoha_pinconf_get(struct pinctrl_dev *pctrl_dev, ++ unsigned int pin, unsigned long *config) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ enum pin_config_param param = pinconf_to_config_param(*config); ++ u32 arg; ++ ++ switch (param) { ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ case PIN_CONFIG_BIAS_DISABLE: ++ case PIN_CONFIG_BIAS_PULL_UP: { ++ u32 pull_up, pull_down; ++ ++ if (airoha_pinctrl_get_pullup_conf(pinctrl, pin, &pull_up) || ++ airoha_pinctrl_get_pulldown_conf(pinctrl, pin, &pull_down)) ++ return -EINVAL; ++ ++ if (param == PIN_CONFIG_BIAS_PULL_UP && ++ !(pull_up && !pull_down)) ++ return -EINVAL; ++ else if (param == PIN_CONFIG_BIAS_PULL_DOWN && ++ !(pull_down && !pull_up)) ++ return -EINVAL; ++ else if (pull_up || pull_down) ++ return -EINVAL; ++ ++ arg = 1; ++ break; ++ } ++ case PIN_CONFIG_DRIVE_STRENGTH: { ++ u32 e2, e4; ++ ++ if (airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, &e2) || ++ airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, &e4)) ++ return -EINVAL; ++ ++ arg = e4 << 1 | e2; ++ break; ++ } ++ case PIN_CONFIG_DRIVE_OPEN_DRAIN: ++ if (airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, &arg)) ++ return -EINVAL; ++ break; ++ case PIN_CONFIG_OUTPUT_ENABLE: ++ case PIN_CONFIG_INPUT_ENABLE: ++ arg = airoha_pinconf_get_direction(pctrl_dev, pin); ++ if (arg != param) ++ return -EINVAL; ++ ++ arg = 1; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ *config = pinconf_to_config_packed(param, arg); ++ ++ return 0; ++} ++ ++static int airoha_pinconf_set_pin_value(struct pinctrl_dev *pctrl_dev, ++ unsigned int p, bool value) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ int pin; ++ ++ pin = airoha_convert_pin_to_reg_offset(pctrl_dev, NULL, p); ++ if (pin < 0) ++ return pin; ++ ++ airoha_gpio_set(&pinctrl->gpiochip.chip, pin, value); ++ ++ return 0; ++} ++ ++static int airoha_pinconf_set(struct pinctrl_dev *pctrl_dev, ++ unsigned int pin, unsigned long *configs, ++ unsigned int num_configs) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ int i; ++ ++ for (i = 0; i < num_configs; i++) { ++ u32 param = pinconf_to_config_param(configs[i]); ++ u32 arg = pinconf_to_config_argument(configs[i]); ++ ++ switch (param) { ++ case PIN_CONFIG_BIAS_DISABLE: ++ airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 0); ++ airoha_pinctrl_set_pullup_conf(pinctrl, pin, 0); ++ break; ++ case PIN_CONFIG_BIAS_PULL_UP: ++ airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 0); ++ airoha_pinctrl_set_pullup_conf(pinctrl, pin, 1); ++ break; ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 1); ++ airoha_pinctrl_set_pullup_conf(pinctrl, pin, 0); ++ break; ++ case PIN_CONFIG_DRIVE_STRENGTH: { ++ u32 e2 = 0, e4 = 0; ++ ++ switch (arg) { ++ case MTK_DRIVE_2mA: ++ break; ++ case MTK_DRIVE_4mA: ++ e2 = 1; ++ break; ++ case MTK_DRIVE_6mA: ++ e4 = 1; ++ break; ++ case MTK_DRIVE_8mA: ++ e2 = 1; ++ e4 = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, e2); ++ airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, e4); ++ break; ++ } ++ case PIN_CONFIG_DRIVE_OPEN_DRAIN: ++ airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, !!arg); ++ break; ++ case PIN_CONFIG_OUTPUT_ENABLE: ++ case PIN_CONFIG_INPUT_ENABLE: ++ case PIN_CONFIG_OUTPUT: { ++ bool input = param == PIN_CONFIG_INPUT_ENABLE; ++ int err; ++ ++ err = airoha_pinmux_set_direction(pctrl_dev, NULL, pin, ++ input); ++ if (err) ++ return err; ++ ++ if (param == PIN_CONFIG_OUTPUT) { ++ err = airoha_pinconf_set_pin_value(pctrl_dev, ++ pin, !!arg); ++ if (err) ++ return err; ++ } ++ break; ++ } ++ default: ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ return 0; ++} ++ ++static int airoha_pinconf_group_get(struct pinctrl_dev *pctrl_dev, ++ unsigned int group, unsigned long *config) ++{ ++ u32 cur_config = 0; ++ int i; ++ ++ for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) { ++ if (airoha_pinconf_get(pctrl_dev, ++ airoha_pinctrl_groups[group].pins[i], ++ config)) ++ return -EOPNOTSUPP; ++ ++ if (i && cur_config != *config) ++ return -EOPNOTSUPP; ++ ++ cur_config = *config; ++ } ++ ++ return 0; ++} ++ ++static int airoha_pinconf_group_set(struct pinctrl_dev *pctrl_dev, ++ unsigned int group, unsigned long *configs, ++ unsigned int num_configs) ++{ ++ int i; ++ ++ for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) { ++ int err; ++ ++ err = airoha_pinconf_set(pctrl_dev, ++ airoha_pinctrl_groups[group].pins[i], ++ configs, num_configs); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static const struct pinconf_ops airoha_confops = { ++ .is_generic = true, ++ .pin_config_get = airoha_pinconf_get, ++ .pin_config_set = airoha_pinconf_set, ++ .pin_config_group_get = airoha_pinconf_group_get, ++ .pin_config_group_set = airoha_pinconf_group_set, ++ .pin_config_config_dbg_show = pinconf_generic_dump_config, ++}; ++ ++static const struct pinctrl_ops airoha_pctlops = { ++ .get_groups_count = pinctrl_generic_get_group_count, ++ .get_group_name = pinctrl_generic_get_group_name, ++ .get_group_pins = pinctrl_generic_get_group_pins, ++ .dt_node_to_map = pinconf_generic_dt_node_to_map_all, ++ .dt_free_map = pinconf_generic_dt_free_map, ++}; ++ ++static struct pinctrl_desc airoha_pinctrl_desc = { ++ .name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .pctlops = &airoha_pctlops, ++ .pmxops = &airoha_pmxops, ++ .confops = &airoha_confops, ++ .pins = airoha_pinctrl_pins, ++ .npins = ARRAY_SIZE(airoha_pinctrl_pins), ++}; ++ ++static int airoha_pinctrl_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct airoha_pinctrl *pinctrl; ++ struct regmap *map; ++ int err, i; ++ ++ pinctrl = devm_kzalloc(dev, sizeof(*pinctrl), GFP_KERNEL); ++ if (!pinctrl) ++ return -ENOMEM; ++ ++ pinctrl->regmap = device_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(pinctrl->regmap)) ++ return PTR_ERR(pinctrl->regmap); ++ ++ map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); ++ if (IS_ERR(map)) ++ return PTR_ERR(map); ++ ++ pinctrl->chip_scu = map; ++ ++ err = devm_pinctrl_register_and_init(dev, &airoha_pinctrl_desc, ++ pinctrl, &pinctrl->ctrl); ++ if (err) ++ return err; ++ ++ /* build pin groups */ ++ for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_groups); i++) { ++ const struct pingroup *grp = &airoha_pinctrl_groups[i]; ++ ++ err = pinctrl_generic_add_group(pinctrl->ctrl, grp->name, ++ (int *)grp->pins, grp->npins, ++ (void *)grp); ++ if (err < 0) { ++ dev_err(&pdev->dev, "Failed to register group %s\n", ++ grp->name); ++ return err; ++ } ++ } ++ ++ /* build functions */ ++ for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_funcs); i++) { ++ const struct airoha_pinctrl_func *func; ++ ++ func = &airoha_pinctrl_funcs[i]; ++ err = pinmux_generic_add_function(pinctrl->ctrl, ++ func->desc.name, ++ func->desc.group_names, ++ func->desc.num_group_names, ++ (void *)func); ++ if (err < 0) { ++ dev_err(dev, "Failed to register function %s\n", ++ func->desc.name); ++ return err; ++ } ++ } ++ ++ err = pinctrl_enable(pinctrl->ctrl); ++ if (err) ++ return err; ++ ++ /* build gpio-chip */ ++ return airoha_pinctrl_add_gpiochip(pinctrl, pdev); ++} ++ ++static const struct of_device_id airoha_pinctrl_of_match[] = { ++ { .compatible = "airoha,en7581-pinctrl" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, airoha_pinctrl_of_match); ++ ++static struct platform_driver airoha_pinctrl_driver = { ++ .probe = airoha_pinctrl_probe, ++ .driver = { ++ .name = "pinctrl-airoha", ++ .of_match_table = airoha_pinctrl_of_match, ++ }, ++}; ++module_platform_driver(airoha_pinctrl_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Lorenzo Bianconi "); ++MODULE_AUTHOR("Benjamin Larsson "); ++MODULE_AUTHOR("Markus Gothe "); ++MODULE_DESCRIPTION("Pinctrl driver for Airoha SoC"); diff --git a/target/linux/airoha/patches-6.6/108-pwm-airoha-Add-support-for-EN7581-SoC.patch b/target/linux/airoha/patches-6.6/108-pwm-airoha-Add-support-for-EN7581-SoC.patch new file mode 100644 index 0000000000..5eb4696b39 --- /dev/null +++ b/target/linux/airoha/patches-6.6/108-pwm-airoha-Add-support-for-EN7581-SoC.patch @@ -0,0 +1,474 @@ +From b235c45e83c8c2a24746652982d569896b142de9 Mon Sep 17 00:00:00 2001 +From: Benjamin Larsson +Date: Wed, 16 Oct 2024 12:07:34 +0200 +Subject: [PATCH 2/2] pwm: airoha: Add support for EN7581 SoC + +Introduce driver for PWM module available on EN7581 SoC. + +Signed-off-by: Benjamin Larsson +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +--- + drivers/pwm/Kconfig | 11 + + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-airoha.c | 424 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 436 insertions(+) + create mode 100644 drivers/pwm/pwm-airoha.c + +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -51,6 +51,17 @@ config PWM_AB8500 + To compile this driver as a module, choose M here: the module + will be called pwm-ab8500. + ++config PWM_AIROHA ++ tristate "Airoha PWM support" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ depends on OF ++ select REGMAP_MMIO ++ help ++ Generic PWM framework driver for Airoha SoC. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-airoha. ++ + config PWM_APPLE + tristate "Apple SoC PWM support" + depends on ARCH_APPLE || COMPILE_TEST +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -2,6 +2,7 @@ + obj-$(CONFIG_PWM) += core.o + obj-$(CONFIG_PWM_SYSFS) += sysfs.o + obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o ++obj-$(CONFIG_PWM_AIROHA) += pwm-airoha.o + obj-$(CONFIG_PWM_APPLE) += pwm-apple.o + obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o + obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o +--- /dev/null ++++ b/drivers/pwm/pwm-airoha.c +@@ -0,0 +1,424 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2022 Markus Gothe ++ * ++ * Limitations: ++ * - No disable bit, so a disabled PWM is simulated by setting duty_cycle to 0 ++ * - Only 8 concurrent waveform generators are available for 8 combinations of ++ * duty_cycle and period. Waveform generators are shared between 16 GPIO ++ * pins and 17 SIPO GPIO pins. ++ * - Supports only normal polarity. ++ * - On configuration the currently running period is completed. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define REG_SGPIO_LED_DATA 0x0024 ++#define SGPIO_LED_DATA_SHIFT_FLAG BIT(31) ++#define SGPIO_LED_DATA_DATA GENMASK(16, 0) ++ ++#define REG_SGPIO_CLK_DIVR 0x0028 ++#define REG_SGPIO_CLK_DLY 0x002c ++ ++#define REG_SIPO_FLASH_MODE_CFG 0x0030 ++#define SERIAL_GPIO_FLASH_MODE BIT(1) ++#define SERIAL_GPIO_MODE BIT(0) ++ ++#define REG_GPIO_FLASH_PRD_SET(_n) (0x003c + ((_n) << 2)) ++#define GPIO_FLASH_PRD_MASK(_n) GENMASK(15 + ((_n) << 4), ((_n) << 4)) ++ ++#define REG_GPIO_FLASH_MAP(_n) (0x004c + ((_n) << 2)) ++#define GPIO_FLASH_SETID_MASK(_n) GENMASK(2 + ((_n) << 2), ((_n) << 2)) ++#define GPIO_FLASH_EN(_n) BIT(3 + ((_n) << 2)) ++ ++#define REG_SIPO_FLASH_MAP(_n) (0x0054 + ((_n) << 2)) ++ ++#define REG_CYCLE_CFG_VALUE(_n) (0x0098 + ((_n) << 2)) ++#define WAVE_GEN_CYCLE_MASK(_n) GENMASK(7 + ((_n) << 3), ((_n) << 3)) ++ ++#define EN7581_NUM_BUCKETS 8 ++ ++struct airoha_pwm_bucket { ++ /* Bitmask of PWM channels using this bucket */ ++ u64 used; ++ u64 period_ns; ++ u64 duty_ns; ++}; ++ ++struct airoha_pwm { ++ struct pwm_chip chip; ++ ++ struct regmap *regmap; ++ ++ struct device_node *np; ++ u64 initialized; ++ ++ struct airoha_pwm_bucket bucket[EN7581_NUM_BUCKETS]; ++}; ++ ++/* ++ * The first 16 GPIO pins, GPIO0-GPIO15, are mapped into 16 PWM channels, 0-15. ++ * The SIPO GPIO pins are 17 pins which are mapped into 17 PWM channels, 16-32. ++ * However, we've only got 8 concurrent waveform generators and can therefore ++ * only use up to 8 different combinations of duty cycle and period at a time. ++ */ ++#define PWM_NUM_GPIO 16 ++#define PWM_NUM_SIPO 17 ++ ++/* The PWM hardware supports periods between 4 ms and 1 s */ ++#define PERIOD_MIN_NS (4 * NSEC_PER_MSEC) ++#define PERIOD_MAX_NS (1 * NSEC_PER_SEC) ++/* It is represented internally as 1/250 s between 1 and 250 */ ++#define PERIOD_MIN 1 ++#define PERIOD_MAX 250 ++/* Duty cycle is relative with 255 corresponding to 100% */ ++#define DUTY_FULL 255 ++ ++static int airoha_pwm_get_generator(struct airoha_pwm *pc, u64 duty_ns, ++ u64 period_ns) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) { ++ if (!pc->bucket[i].used) ++ continue; ++ ++ if (duty_ns == pc->bucket[i].duty_ns && ++ period_ns == pc->bucket[i].period_ns) ++ return i; ++ ++ /* ++ * Unlike duty cycle zero, which can be handled by ++ * disabling PWM, a generator is needed for full duty ++ * cycle but it can be reused regardless of period ++ */ ++ if (duty_ns == DUTY_FULL && pc->bucket[i].duty_ns == DUTY_FULL) ++ return i; ++ } ++ ++ return -1; ++} ++ ++static void airoha_pwm_release_bucket_config(struct airoha_pwm *pc, ++ unsigned int hwpwm) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) ++ pc->bucket[i].used &= ~BIT_ULL(hwpwm); ++} ++ ++static int airoha_pwm_consume_generator(struct airoha_pwm *pc, ++ u64 duty_ns, u64 period_ns, ++ unsigned int hwpwm) ++{ ++ int id = airoha_pwm_get_generator(pc, duty_ns, period_ns); ++ ++ if (id < 0) { ++ int i; ++ ++ /* find an unused waveform generator */ ++ for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) { ++ if (!(pc->bucket[i].used & ~BIT_ULL(hwpwm))) { ++ id = i; ++ break; ++ } ++ } ++ } ++ ++ if (id >= 0) { ++ airoha_pwm_release_bucket_config(pc, hwpwm); ++ pc->bucket[id].used |= BIT_ULL(hwpwm); ++ pc->bucket[id].period_ns = period_ns; ++ pc->bucket[id].duty_ns = duty_ns; ++ } ++ ++ return id; ++} ++ ++static int airoha_pwm_sipo_init(struct airoha_pwm *pc) ++{ ++ u32 clk_divr_val, sipo_clock_delay, sipo_clock_divisor; ++ u32 val; ++ ++ if (!(pc->initialized >> PWM_NUM_GPIO)) ++ return 0; ++ ++ /* ++ * Select the right shift register chip. ++ * By default 74HC164 is assumed. With this enabled ++ * 74HC595 chip is used that requires the latch pin ++ * to be triggered to apply the configuration. ++ */ ++ if (of_property_read_bool(pc->np, "airoha,74hc595-mode")) ++ regmap_set_bits(pc->regmap, REG_SIPO_FLASH_MODE_CFG, ++ SERIAL_GPIO_MODE); ++ else ++ regmap_clear_bits(pc->regmap, REG_SIPO_FLASH_MODE_CFG, ++ SERIAL_GPIO_MODE); ++ ++ if (of_property_read_u32(pc->np, "airoha,sipo-clock-divisor", ++ &sipo_clock_divisor)) ++ sipo_clock_divisor = 32; ++ ++ switch (sipo_clock_divisor) { ++ case 4: ++ clk_divr_val = 0; ++ break; ++ case 8: ++ clk_divr_val = 1; ++ break; ++ case 16: ++ clk_divr_val = 2; ++ break; ++ case 32: ++ clk_divr_val = 3; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Configure shift register timings */ ++ regmap_write(pc->regmap, REG_SGPIO_CLK_DIVR, clk_divr_val); ++ ++ if (of_property_read_u32(pc->np, "airoha,sipo-clock-delay", ++ &sipo_clock_delay)) ++ sipo_clock_delay = 1; ++ ++ if (sipo_clock_delay < 1 || sipo_clock_delay > sipo_clock_divisor / 2) ++ return -EINVAL; ++ ++ /* ++ * The actual delay is sclkdly + 1 so subtract 1 from ++ * sipo-clock-delay to calculate the register value ++ */ ++ sipo_clock_delay--; ++ regmap_write(pc->regmap, REG_SGPIO_CLK_DLY, sipo_clock_delay); ++ ++ /* ++ * It it necessary to after muxing explicitly shift out all ++ * zeroes to initialize the shift register before enabling PWM ++ * mode because in PWM mode SIPO will not start shifting until ++ * it needs to output a non-zero value (bit 31 of led_data ++ * indicates shifting in progress and it must return to zero ++ * before led_data can be written or PWM mode can be set) ++ */ ++ if (regmap_read_poll_timeout(pc->regmap, REG_SGPIO_LED_DATA, val, ++ !(val & SGPIO_LED_DATA_SHIFT_FLAG), 10, ++ 200 * USEC_PER_MSEC)) ++ return -ETIMEDOUT; ++ ++ regmap_clear_bits(pc->regmap, REG_SGPIO_LED_DATA, SGPIO_LED_DATA_DATA); ++ if (regmap_read_poll_timeout(pc->regmap, REG_SGPIO_LED_DATA, val, ++ !(val & SGPIO_LED_DATA_SHIFT_FLAG), 10, ++ 200 * USEC_PER_MSEC)) ++ return -ETIMEDOUT; ++ ++ /* Set SIPO in PWM mode */ ++ regmap_set_bits(pc->regmap, REG_SIPO_FLASH_MODE_CFG, ++ SERIAL_GPIO_FLASH_MODE); ++ ++ return 0; ++} ++ ++static void airoha_pwm_calc_bucket_config(struct airoha_pwm *pc, int index, ++ u64 duty_ns, u64 period_ns) ++{ ++ u32 period, duty, mask, val; ++ u64 tmp; ++ ++ tmp = duty_ns * DUTY_FULL; ++ duty = clamp_val(div64_u64(tmp, period_ns), 0, DUTY_FULL); ++ tmp = period_ns * 25; ++ period = clamp_val(div64_u64(tmp, 100000000), PERIOD_MIN, PERIOD_MAX); ++ ++ /* Configure frequency divisor */ ++ mask = WAVE_GEN_CYCLE_MASK(index % 4); ++ val = (period << __ffs(mask)) & mask; ++ regmap_update_bits(pc->regmap, REG_CYCLE_CFG_VALUE(index / 4), ++ mask, val); ++ ++ /* Configure duty cycle */ ++ duty = ((DUTY_FULL - duty) << 8) | duty; ++ mask = GPIO_FLASH_PRD_MASK(index % 2); ++ val = (duty << __ffs(mask)) & mask; ++ regmap_update_bits(pc->regmap, REG_GPIO_FLASH_PRD_SET(index / 2), ++ mask, val); ++} ++ ++static void airoha_pwm_config_flash_map(struct airoha_pwm *pc, ++ unsigned int hwpwm, int index) ++{ ++ u32 addr, mask, val; ++ ++ if (hwpwm < PWM_NUM_GPIO) { ++ addr = REG_GPIO_FLASH_MAP(hwpwm / 8); ++ } else { ++ addr = REG_SIPO_FLASH_MAP(hwpwm / 8); ++ hwpwm -= PWM_NUM_GPIO; ++ } ++ ++ if (index < 0) { ++ /* ++ * Change of waveform takes effect immediately but ++ * disabling has some delay so to prevent glitching ++ * only the enable bit is touched when disabling ++ */ ++ regmap_clear_bits(pc->regmap, addr, GPIO_FLASH_EN(hwpwm % 8)); ++ return; ++ } ++ ++ mask = GPIO_FLASH_SETID_MASK(hwpwm % 8); ++ val = ((index & 7) << __ffs(mask)) & mask; ++ regmap_update_bits(pc->regmap, addr, mask, val); ++ regmap_set_bits(pc->regmap, addr, GPIO_FLASH_EN(hwpwm % 8)); ++} ++ ++static int airoha_pwm_config(struct airoha_pwm *pc, struct pwm_device *pwm, ++ u64 duty_ns, u64 period_ns) ++{ ++ int index = -1; ++ ++ index = airoha_pwm_consume_generator(pc, duty_ns, period_ns, ++ pwm->hwpwm); ++ if (index < 0) ++ return -EBUSY; ++ ++ if (!(pc->initialized & BIT_ULL(pwm->hwpwm)) && ++ pwm->hwpwm >= PWM_NUM_GPIO) ++ airoha_pwm_sipo_init(pc); ++ ++ if (index >= 0) { ++ airoha_pwm_calc_bucket_config(pc, index, duty_ns, period_ns); ++ airoha_pwm_config_flash_map(pc, pwm->hwpwm, index); ++ } else { ++ airoha_pwm_config_flash_map(pc, pwm->hwpwm, index); ++ airoha_pwm_release_bucket_config(pc, pwm->hwpwm); ++ } ++ ++ pc->initialized |= BIT_ULL(pwm->hwpwm); ++ ++ return 0; ++} ++ ++static void airoha_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct airoha_pwm *pc = pwmchip_get_drvdata(chip); ++ ++ /* Disable PWM and release the waveform */ ++ airoha_pwm_config_flash_map(pc, pwm->hwpwm, -1); ++ airoha_pwm_release_bucket_config(pc, pwm->hwpwm); ++ ++ pc->initialized &= ~BIT_ULL(pwm->hwpwm); ++ if (!(pc->initialized >> PWM_NUM_GPIO)) ++ regmap_clear_bits(pc->regmap, REG_SIPO_FLASH_MODE_CFG, ++ SERIAL_GPIO_FLASH_MODE); ++} ++ ++static int airoha_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ const struct pwm_state *state) ++{ ++ struct airoha_pwm *pc = pwmchip_get_drvdata(chip); ++ u64 duty = state->enabled ? state->duty_cycle : 0; ++ u64 period = state->period; ++ ++ /* Only normal polarity is supported */ ++ if (state->polarity == PWM_POLARITY_INVERSED) ++ return -EINVAL; ++ ++ if (!state->enabled) { ++ airoha_pwm_disable(chip, pwm); ++ return 0; ++ } ++ ++ if (period < PERIOD_MIN_NS) ++ return -EINVAL; ++ ++ if (period > PERIOD_MAX_NS) ++ period = PERIOD_MAX_NS; ++ ++ return airoha_pwm_config(pc, pwm, duty, period); ++} ++ ++static int airoha_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, ++ struct pwm_state *state) ++{ ++ struct airoha_pwm *pc = pwmchip_get_drvdata(chip); ++ int i; ++ ++ /* find hwpwm in waveform generator bucket */ ++ for (i = 0; i < ARRAY_SIZE(pc->bucket); i++) { ++ if (pc->bucket[i].used & BIT_ULL(pwm->hwpwm)) { ++ state->enabled = pc->initialized & BIT_ULL(pwm->hwpwm); ++ state->polarity = PWM_POLARITY_NORMAL; ++ state->period = pc->bucket[i].period_ns; ++ state->duty_cycle = pc->bucket[i].duty_ns; ++ break; ++ } ++ } ++ ++ if (i == ARRAY_SIZE(pc->bucket)) ++ state->enabled = false; ++ ++ return 0; ++} ++ ++static const struct pwm_ops airoha_pwm_ops = { ++ .get_state = airoha_pwm_get_state, ++ .apply = airoha_pwm_apply, ++}; ++ ++static int airoha_pwm_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct airoha_pwm *pc; ++ struct pwm_chip *chip; ++ ++ chip = devm_pwmchip_alloc(dev, PWM_NUM_GPIO + PWM_NUM_SIPO, ++ sizeof(*pc)); ++ if (IS_ERR(chip)) ++ return PTR_ERR(chip); ++ ++ chip->ops = &airoha_pwm_ops; ++ pc = pwmchip_get_drvdata(chip); ++ pc->np = dev->of_node; ++ ++ pc->regmap = device_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(pc->regmap)) ++ return PTR_ERR(pc->regmap); ++ ++ return devm_pwmchip_add(&pdev->dev, chip); ++} ++ ++static const struct of_device_id airoha_pwm_of_match[] = { ++ { .compatible = "airoha,en7581-pwm" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, airoha_pwm_of_match); ++ ++static struct platform_driver airoha_pwm_driver = { ++ .driver = { ++ .name = "pwm-airoha", ++ .of_match_table = airoha_pwm_of_match, ++ }, ++ .probe = airoha_pwm_probe, ++}; ++module_platform_driver(airoha_pwm_driver); ++ ++MODULE_AUTHOR("Lorenzo Bianconi "); ++MODULE_AUTHOR("Markus Gothe "); ++MODULE_AUTHOR("Benjamin Larsson "); ++MODULE_DESCRIPTION("Airoha EN7581 PWM driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.6/109-clk-en7523-Fix-wrong-BUS-clock-for-EN7581.patch b/target/linux/airoha/patches-6.6/109-clk-en7523-Fix-wrong-BUS-clock-for-EN7581.patch new file mode 100644 index 0000000000..01a5fd0c87 --- /dev/null +++ b/target/linux/airoha/patches-6.6/109-clk-en7523-Fix-wrong-BUS-clock-for-EN7581.patch @@ -0,0 +1,45 @@ +From 6d74b9e6d3bb07f50b22b9ea047b84a83aba185c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 17 Oct 2024 19:26:24 +0200 +Subject: [PATCH] clk: en7523: Fix wrong BUS clock for EN7581 + +The Documentation for EN7581 had a typo and still referenced the EN7523 +BUS base source frequency. This was in conflict with a different page in +the Documentration that state that the BUS runs at 300MHz (600MHz source with +divisor set to 2) and the actual watchdog that tick at half the BUS +clock (150MHz). This was verified with the watchdog by timing the +seconds that the system takes to reboot (due too watchdog) and by +operating on different values of the BUS divisor. + +The correct values for source of BUS clock are 600MHz and 540MHz. + +This was also confirmed by Airoha. + +Cc: stable@vger.kernel.org +Fixes: 66bc47326ce2 ("clk: en7523: Add EN7581 support") +Signed-off-by: Christian Marangi +--- + drivers/clk/clk-en7523.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -87,6 +87,7 @@ static const u32 slic_base[] = { 1000000 + static const u32 npu_base[] = { 333000000, 400000000, 500000000 }; + /* EN7581 */ + static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 }; ++static const u32 bus7581_base[] = { 600000000, 540000000 }; + static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 }; + static const u32 crypto_base[] = { 540000000, 480000000 }; + +@@ -222,8 +223,8 @@ static const struct en_clk_desc en7581_b + .base_reg = REG_BUS_CLK_DIV_SEL, + .base_bits = 1, + .base_shift = 8, +- .base_values = bus_base, +- .n_base_values = ARRAY_SIZE(bus_base), ++ .base_values = bus7581_base, ++ .n_base_values = ARRAY_SIZE(bus7581_base), + + .div_bits = 3, + .div_shift = 0, diff --git a/target/linux/airoha/patches-6.6/200-spinlock-extend-guard-with-spinlock_bh-variants.patch b/target/linux/airoha/patches-6.6/200-spinlock-extend-guard-with-spinlock_bh-variants.patch new file mode 100644 index 0000000000..e613b69f81 --- /dev/null +++ b/target/linux/airoha/patches-6.6/200-spinlock-extend-guard-with-spinlock_bh-variants.patch @@ -0,0 +1,36 @@ +From 38d2c6aafc5bbcad3ec36f6d3356b3debd40f6fd Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 16 Oct 2024 20:26:05 +0200 +Subject: [RFC PATCH v2 1/3] spinlock: extend guard with spinlock_bh variants + +Extend guard APIs with missing raw/spinlock_bh variants. + +Signed-off-by: Christian Marangi +--- + include/linux/spinlock.h | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/include/linux/spinlock.h ++++ b/include/linux/spinlock.h +@@ -515,6 +515,10 @@ DEFINE_LOCK_GUARD_1(raw_spinlock_irq, ra + raw_spin_lock_irq(_T->lock), + raw_spin_unlock_irq(_T->lock)) + ++DEFINE_LOCK_GUARD_1(raw_spinlock_bh, raw_spinlock_t, ++ raw_spin_lock_bh(_T->lock), ++ raw_spin_unlock_bh(_T->lock)) ++ + DEFINE_LOCK_GUARD_1(raw_spinlock_irqsave, raw_spinlock_t, + raw_spin_lock_irqsave(_T->lock, _T->flags), + raw_spin_unlock_irqrestore(_T->lock, _T->flags), +@@ -528,6 +532,10 @@ DEFINE_LOCK_GUARD_1(spinlock_irq, spinlo + spin_lock_irq(_T->lock), + spin_unlock_irq(_T->lock)) + ++DEFINE_LOCK_GUARD_1(spinlock_bh, spinlock_t, ++ spin_lock_bh(_T->lock), ++ spin_unlock_bh(_T->lock)) ++ + DEFINE_LOCK_GUARD_1(spinlock_irqsave, spinlock_t, + spin_lock_irqsave(_T->lock, _T->flags), + spin_unlock_irqrestore(_T->lock, _T->flags), diff --git a/target/linux/airoha/patches-6.6/201-crypto-Add-Mediatek-EIP-93-crypto-engine-support.patch b/target/linux/airoha/patches-6.6/201-crypto-Add-Mediatek-EIP-93-crypto-engine-support.patch new file mode 100644 index 0000000000..1cb5812af3 --- /dev/null +++ b/target/linux/airoha/patches-6.6/201-crypto-Add-Mediatek-EIP-93-crypto-engine-support.patch @@ -0,0 +1,4206 @@ +From 45260ebcfb17a47bbad37055024dad50f2fcc5d0 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Oct 2021 17:13:29 +0800 +Subject: [RFC PATCH v2 3/3] crypto: Add Mediatek EIP-93 crypto engine support + +Add support for the Mediatek EIP-93 crypto engine used on MT7621 and new +Airoha SoC. + +EIP-93 IP supports AES/DES/3DES ciphers in ECB/CBC and CTR modes as well as +authenc(HMAC(x), cipher(y)) using HMAC MD5, SHA1, SHA224 and SHA256. + +EIP-93 provide regs to signal support for specific chipers and the +driver dynamically register only the supported one by the chip. + +Signed-off-by: Richard van Schagen +Co-developed-by: Christian Marangi +Signed-off-by: Christian Marangi +--- +Changes v2: +- Rename all variables from mtk to eip93 +- Move to inside-secure directory +- Check DMA map errors +- Use guard API for spinlock +- Minor improvements to code + + drivers/crypto/Kconfig | 1 + + drivers/crypto/Makefile | 1 + + drivers/crypto/inside-secure/eip93/Kconfig | 20 + + drivers/crypto/inside-secure/eip93/Makefile | 5 + + .../crypto/inside-secure/eip93/eip93-aead.c | 702 ++++++++++++++ + .../crypto/inside-secure/eip93/eip93-aead.h | 38 + + .../crypto/inside-secure/eip93/eip93-aes.h | 16 + + .../crypto/inside-secure/eip93/eip93-cipher.c | 407 ++++++++ + .../crypto/inside-secure/eip93/eip93-cipher.h | 60 ++ + .../crypto/inside-secure/eip93/eip93-common.c | 824 ++++++++++++++++ + .../crypto/inside-secure/eip93/eip93-common.h | 25 + + .../crypto/inside-secure/eip93/eip93-des.h | 16 + + .../crypto/inside-secure/eip93/eip93-hash.c | 909 ++++++++++++++++++ + .../crypto/inside-secure/eip93/eip93-hash.h | 72 ++ + .../crypto/inside-secure/eip93/eip93-main.c | 502 ++++++++++ + .../crypto/inside-secure/eip93/eip93-main.h | 155 +++ + .../crypto/inside-secure/eip93/eip93-regs.h | 335 +++++++ + 17 files changed, 4088 insertions(+) + create mode 100644 drivers/crypto/inside-secure/eip93/Kconfig + create mode 100644 drivers/crypto/inside-secure/eip93/Makefile + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-aead.c + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-aead.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-aes.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-cipher.c + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-cipher.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-common.c + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-common.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-des.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-hash.c + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-hash.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-main.c + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-main.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-regs.h + +--- a/drivers/crypto/Kconfig ++++ b/drivers/crypto/Kconfig +@@ -796,5 +796,6 @@ config CRYPTO_DEV_SA2UL + + source "drivers/crypto/aspeed/Kconfig" + source "drivers/crypto/starfive/Kconfig" ++source "drivers/crypto/inside-secure/eip93/Kconfig" + + endif # CRYPTO_HW +--- a/drivers/crypto/Makefile ++++ b/drivers/crypto/Makefile +@@ -51,3 +51,4 @@ obj-y += hisilicon/ + obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ + obj-y += intel/ + obj-y += starfive/ ++obj-y += inside-secure/eip93/ +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/Kconfig +@@ -0,0 +1,20 @@ ++# SPDX-License-Identifier: GPL-2.0 ++config CRYPTO_DEV_EIP93 ++ tristate "Support for EIP93 crypto HW accelerators" ++ depends on SOC_MT7621 || ARCH_AIROHA ||COMPILE_TEST ++ select CRYPTO_LIB_AES ++ select CRYPTO_LIB_DES ++ select CRYPTO_SKCIPHER ++ select CRYPTO_AEAD ++ select CRYPTO_AUTHENC ++ select CRYPTO_MD5 ++ select CRYPTO_SHA1 ++ select CRYPTO_SHA256 ++ help ++ EIP93 have various crypto HW accelerators. Select this if ++ you want to use the EIP93 modules for any of the crypto algorithms. ++ ++ If the IP supports it, this provide offload for AES - ECB, CBC and ++ CTR crypto. Also provide DES and 3DES ECB and CBC. ++ ++ Also provide AEAD authenc(hmac(x), cipher(y)) for supported algo. +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/Makefile +@@ -0,0 +1,5 @@ ++obj-$(CONFIG_CRYPTO_DEV_EIP93) += crypto-hw-eip93.o ++ ++crypto-hw-eip93-y += eip93-main.o eip93-common.o ++crypto-hw-eip93-y += eip93-cipher.o eip93-aead.o ++crypto-hw-eip93-y += eip93-hash.o +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-aead.c +@@ -0,0 +1,702 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include "eip93-aead.h" ++#include "eip93-cipher.h" ++#include "eip93-common.h" ++#include "eip93-regs.h" ++ ++void eip93_aead_handle_result(struct crypto_async_request *async, int err) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(async->tfm); ++ struct eip93_device *mtk = ctx->mtk; ++ struct aead_request *req = aead_request_cast(async); ++ struct eip93_cipher_reqctx *rctx = aead_request_ctx(req); ++ ++ eip93_unmap_dma(mtk, rctx, req->src, req->dst); ++ eip93_handle_result(mtk, rctx, req->iv); ++ ++ aead_request_complete(req, err); ++} ++ ++static int eip93_aead_send_req(struct crypto_async_request *async) ++{ ++ struct aead_request *req = aead_request_cast(async); ++ struct eip93_cipher_reqctx *rctx = aead_request_ctx(req); ++ int err; ++ ++ err = check_valid_request(rctx); ++ if (err) { ++ aead_request_complete(req, err); ++ return err; ++ } ++ ++ return eip93_send_req(async, req->iv, rctx); ++} ++ ++/* Crypto aead API functions */ ++static int eip93_aead_cra_init(struct crypto_tfm *tfm) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct eip93_alg_template *tmpl = container_of(tfm->__crt_alg, ++ struct eip93_alg_template, alg.aead.base); ++ ++ crypto_aead_set_reqsize(__crypto_aead_cast(tfm), ++ sizeof(struct eip93_cipher_reqctx)); ++ ++ ctx->mtk = tmpl->mtk; ++ ctx->flags = tmpl->flags; ++ ctx->type = tmpl->type; ++ ctx->set_assoc = true; ++ ++ ctx->sa_record = kzalloc(sizeof(*ctx->sa_record), GFP_KERNEL); ++ if (!ctx->sa_record) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void eip93_aead_cra_exit(struct crypto_tfm *tfm) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ dma_unmap_single(ctx->mtk->dev, ctx->sa_record_base, ++ sizeof(*ctx->sa_record), DMA_TO_DEVICE); ++ kfree(ctx->sa_record); ++} ++ ++static int eip93_aead_setkey(struct crypto_aead *ctfm, const u8 *key, ++ unsigned int len) ++{ ++ struct crypto_tfm *tfm = crypto_aead_tfm(ctfm); ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct crypto_authenc_keys keys; ++ struct crypto_aes_ctx aes; ++ struct sa_record *sa_record = ctx->sa_record; ++ u32 nonce = 0; ++ int ret; ++ ++ if (crypto_authenc_extractkeys(&keys, key, len)) ++ return -EINVAL; ++ ++ if (IS_RFC3686(ctx->flags)) { ++ if (keys.enckeylen < CTR_RFC3686_NONCE_SIZE) ++ return -EINVAL; ++ ++ keys.enckeylen -= CTR_RFC3686_NONCE_SIZE; ++ memcpy(&nonce, keys.enckey + keys.enckeylen, ++ CTR_RFC3686_NONCE_SIZE); ++ } ++ ++ switch ((ctx->flags & EIP93_ALG_MASK)) { ++ case EIP93_ALG_DES: ++ ret = verify_aead_des_key(ctfm, keys.enckey, keys.enckeylen); ++ break; ++ case EIP93_ALG_3DES: ++ if (keys.enckeylen != DES3_EDE_KEY_SIZE) ++ return -EINVAL; ++ ++ ret = verify_aead_des3_key(ctfm, keys.enckey, keys.enckeylen); ++ break; ++ case EIP93_ALG_AES: ++ ret = aes_expandkey(&aes, keys.enckey, keys.enckeylen); ++ } ++ if (ret) ++ return ret; ++ ++ ctx->blksize = crypto_aead_blocksize(ctfm); ++ /* Encryption key */ ++ eip93_set_sa_record(sa_record, keys.enckeylen, ctx->flags); ++ sa_record->sa_cmd0_word &= ~EIP93_SA_CMD_OPCODE; ++ sa_record->sa_cmd0_word |= FIELD_PREP(EIP93_SA_CMD_OPCODE, ++ EIP93_SA_CMD_OPCODE_BASIC_OUT_ENC_HASH); ++ sa_record->sa_cmd0_word &= ~EIP93_SA_CMD_DIGEST_LENGTH; ++ sa_record->sa_cmd0_word |= FIELD_PREP(EIP93_SA_CMD_DIGEST_LENGTH, ++ ctx->authsize / sizeof(u32)); ++ ++ memcpy(sa_record->sa_key, keys.enckey, keys.enckeylen); ++ ctx->sa_nonce = nonce; ++ sa_record->sa_nonce = nonce; ++ ++ /* authentication key */ ++ ret = eip93_authenc_setkey(ctfm, sa_record, keys.authkey, ++ keys.authkeylen); ++ ++ ctx->set_assoc = true; ++ ++ return ret; ++} ++ ++static int eip93_aead_setauthsize(struct crypto_aead *ctfm, ++ unsigned int authsize) ++{ ++ struct crypto_tfm *tfm = crypto_aead_tfm(ctfm); ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ ctx->authsize = authsize; ++ ctx->sa_record->sa_cmd0_word &= ~EIP93_SA_CMD_DIGEST_LENGTH; ++ ctx->sa_record->sa_cmd0_word |= FIELD_PREP(EIP93_SA_CMD_DIGEST_LENGTH, ++ ctx->authsize / sizeof(u32)); ++ ++ return 0; ++} ++ ++static void eip93_aead_setassoc(struct eip93_crypto_ctx *ctx, ++ struct aead_request *req) ++{ ++ struct sa_record *sa_record = ctx->sa_record; ++ ++ sa_record->sa_cmd1_word &= ~EIP93_SA_CMD_HASH_CRYPT_OFFSET; ++ sa_record->sa_cmd1_word |= FIELD_PREP(EIP93_SA_CMD_HASH_CRYPT_OFFSET, ++ req->assoclen / sizeof(u32)); ++ ++ ctx->assoclen = req->assoclen; ++} ++ ++static int eip93_aead_crypt(struct aead_request *req) ++{ ++ struct eip93_cipher_reqctx *rctx = aead_request_ctx(req); ++ struct crypto_async_request *async = &req->base; ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(req->base.tfm); ++ struct crypto_aead *aead = crypto_aead_reqtfm(req); ++ int ret; ++ ++ ctx->sa_record_base = dma_map_single(ctx->mtk->dev, ctx->sa_record, ++ sizeof(*ctx->sa_record), DMA_TO_DEVICE); ++ ret = dma_mapping_error(ctx->mtk->dev, ctx->sa_record_base); ++ if (ret) ++ return ret; ++ ++ rctx->textsize = req->cryptlen; ++ rctx->blksize = ctx->blksize; ++ rctx->assoclen = req->assoclen; ++ rctx->authsize = ctx->authsize; ++ rctx->sg_src = req->src; ++ rctx->sg_dst = req->dst; ++ rctx->ivsize = crypto_aead_ivsize(aead); ++ rctx->desc_flags = EIP93_DESC_AEAD; ++ rctx->sa_record_base = ctx->sa_record_base; ++ ++ if (IS_DECRYPT(rctx->flags)) ++ rctx->textsize -= rctx->authsize; ++ ++ return eip93_aead_send_req(async); ++} ++ ++static int eip93_aead_encrypt(struct aead_request *req) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(req->base.tfm); ++ struct eip93_cipher_reqctx *rctx = aead_request_ctx(req); ++ ++ rctx->flags = ctx->flags; ++ rctx->flags |= EIP93_ENCRYPT; ++ if (ctx->set_assoc) { ++ eip93_aead_setassoc(ctx, req); ++ ctx->set_assoc = false; ++ } ++ ++ if (req->assoclen != ctx->assoclen) { ++ dev_err(ctx->mtk->dev, "Request AAD length error\n"); ++ return -EINVAL; ++ } ++ ++ return eip93_aead_crypt(req); ++} ++ ++static int eip93_aead_decrypt(struct aead_request *req) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(req->base.tfm); ++ struct eip93_cipher_reqctx *rctx = aead_request_ctx(req); ++ ++ ctx->sa_record->sa_cmd0_word |= EIP93_SA_CMD_DIRECTION_IN; ++ ctx->sa_record->sa_cmd1_word &= ~(EIP93_SA_CMD_COPY_PAD | ++ EIP93_SA_CMD_COPY_DIGEST); ++ ++ rctx->flags = ctx->flags; ++ rctx->flags |= EIP93_DECRYPT; ++ if (ctx->set_assoc) { ++ eip93_aead_setassoc(ctx, req); ++ ctx->set_assoc = false; ++ } ++ ++ if (req->assoclen != ctx->assoclen) { ++ dev_err(ctx->mtk->dev, "Request AAD length error\n"); ++ return -EINVAL; ++ } ++ ++ return eip93_aead_crypt(req); ++} ++ ++/* Available authenc algorithms in this module */ ++struct eip93_alg_template eip93_alg_authenc_hmac_md5_cbc_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_MD5 | EIP93_MODE_CBC | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = AES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),cbc(aes))", ++ .cra_driver_name = ++ "authenc(hmac(md5-eip93), cbc(aes-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha1_cbc_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA1 | EIP93_MODE_CBC | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = AES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA1_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha1),cbc(aes))", ++ .cra_driver_name = ++ "authenc(hmac(sha1-eip93),cbc(aes-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha224_cbc_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA224 | EIP93_MODE_CBC | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = AES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA224_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha224),cbc(aes))", ++ .cra_driver_name = ++ "authenc(hmac(sha224-eip93),cbc(aes-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha256_cbc_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA256 | EIP93_MODE_CBC | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = AES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA256_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha256),cbc(aes))", ++ .cra_driver_name = ++ "authenc(hmac(sha256-eip93),cbc(aes-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_md5_rfc3686_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_MD5 | ++ EIP93_MODE_CTR | EIP93_MODE_RFC3686 | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),rfc3686(ctr(aes)))", ++ .cra_driver_name = ++ "authenc(hmac(md5-eip93),rfc3686(ctr(aes-eip93)))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha1_rfc3686_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA1 | ++ EIP93_MODE_CTR | EIP93_MODE_RFC3686 | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA1_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha1),rfc3686(ctr(aes)))", ++ .cra_driver_name = ++ "authenc(hmac(sha1-eip93),rfc3686(ctr(aes-eip93)))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha224_rfc3686_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA224 | ++ EIP93_MODE_CTR | EIP93_MODE_RFC3686 | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA224_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha224),rfc3686(ctr(aes)))", ++ .cra_driver_name = ++ "authenc(hmac(sha224-eip93),rfc3686(ctr(aes-eip93)))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha256_rfc3686_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA256 | ++ EIP93_MODE_CTR | EIP93_MODE_RFC3686 | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA256_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha256),rfc3686(ctr(aes)))", ++ .cra_driver_name = ++ "authenc(hmac(sha256-eip93),rfc3686(ctr(aes-eip93)))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_md5_cbc_des = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_MD5 | EIP93_MODE_CBC | EIP93_ALG_DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),cbc(des))", ++ .cra_driver_name = ++ "authenc(hmac(md5-eip93),cbc(des-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha1_cbc_des = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA1 | EIP93_MODE_CBC | EIP93_ALG_DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA1_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha1),cbc(des))", ++ .cra_driver_name = ++ "authenc(hmac(sha1-eip93),cbc(des-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha224_cbc_des = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA224 | EIP93_MODE_CBC | EIP93_ALG_DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA224_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha224),cbc(des))", ++ .cra_driver_name = ++ "authenc(hmac(sha224-eip93),cbc(des-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha256_cbc_des = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA256 | EIP93_MODE_CBC | EIP93_ALG_DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA256_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha256),cbc(des))", ++ .cra_driver_name = ++ "authenc(hmac(sha256-eip93),cbc(des-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_md5_cbc_des3_ede = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_MD5 | EIP93_MODE_CBC | EIP93_ALG_3DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),cbc(des3_ede))", ++ .cra_driver_name = ++ "authenc(hmac(md5-eip93),cbc(des3_ede-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0x0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha1_cbc_des3_ede = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA1 | EIP93_MODE_CBC | EIP93_ALG_3DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA1_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha1),cbc(des3_ede))", ++ .cra_driver_name = ++ "authenc(hmac(sha1-eip93),cbc(des3_ede-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0x0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha224_cbc_des3_ede = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA224 | EIP93_MODE_CBC | EIP93_ALG_3DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA224_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha224),cbc(des3_ede))", ++ .cra_driver_name = ++ "authenc(hmac(sha224-eip93),cbc(des3_ede-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0x0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha256_cbc_des3_ede = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA256 | EIP93_MODE_CBC | EIP93_ALG_3DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA256_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha256),cbc(des3_ede))", ++ .cra_driver_name = ++ "authenc(hmac(sha256-eip93),cbc(des3_ede-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0x0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-aead.h +@@ -0,0 +1,38 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++ * Christian Marangi ++ * Christian Marangi ++#include ++#include ++#include ++ ++#include "eip93-cipher.h" ++#include "eip93-common.h" ++#include "eip93-regs.h" ++ ++void eip93_skcipher_handle_result(struct crypto_async_request *async, int err) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(async->tfm); ++ struct eip93_device *mtk = ctx->mtk; ++ struct skcipher_request *req = skcipher_request_cast(async); ++ struct eip93_cipher_reqctx *rctx = skcipher_request_ctx(req); ++ ++ eip93_unmap_dma(mtk, rctx, req->src, req->dst); ++ eip93_handle_result(mtk, rctx, req->iv); ++ ++ skcipher_request_complete(req, err); ++} ++ ++static int eip93_skcipher_send_req(struct crypto_async_request *async) ++{ ++ struct skcipher_request *req = skcipher_request_cast(async); ++ struct eip93_cipher_reqctx *rctx = skcipher_request_ctx(req); ++ int err; ++ ++ err = check_valid_request(rctx); ++ ++ if (err) { ++ skcipher_request_complete(req, err); ++ return err; ++ } ++ ++ return eip93_send_req(async, req->iv, rctx); ++} ++ ++/* Crypto skcipher API functions */ ++static int eip93_skcipher_cra_init(struct crypto_tfm *tfm) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct eip93_alg_template *tmpl = container_of(tfm->__crt_alg, ++ struct eip93_alg_template, alg.skcipher.base); ++ ++ crypto_skcipher_set_reqsize(__crypto_skcipher_cast(tfm), ++ sizeof(struct eip93_cipher_reqctx)); ++ ++ memset(ctx, 0, sizeof(*ctx)); ++ ++ ctx->mtk = tmpl->mtk; ++ ctx->type = tmpl->type; ++ ++ ctx->sa_record = kzalloc(sizeof(*ctx->sa_record), GFP_KERNEL); ++ if (!ctx->sa_record) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void eip93_skcipher_cra_exit(struct crypto_tfm *tfm) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ dma_unmap_single(ctx->mtk->dev, ctx->sa_record_base, ++ sizeof(*ctx->sa_record), DMA_TO_DEVICE); ++ kfree(ctx->sa_record); ++} ++ ++static int eip93_skcipher_setkey(struct crypto_skcipher *ctfm, const u8 *key, ++ unsigned int len) ++{ ++ struct crypto_tfm *tfm = crypto_skcipher_tfm(ctfm); ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct eip93_alg_template *tmpl = container_of(tfm->__crt_alg, ++ struct eip93_alg_template, ++ alg.skcipher.base); ++ struct sa_record *sa_record = ctx->sa_record; ++ unsigned int keylen = len; ++ u32 flags = tmpl->flags; ++ u32 nonce = 0; ++ int ret; ++ ++ if (!key || !keylen) ++ return -EINVAL; ++ ++ if (IS_RFC3686(flags)) { ++ if (len < CTR_RFC3686_NONCE_SIZE) ++ return -EINVAL; ++ ++ keylen = len - CTR_RFC3686_NONCE_SIZE; ++ memcpy(&nonce, key + keylen, CTR_RFC3686_NONCE_SIZE); ++ } ++ ++ if (flags & EIP93_ALG_DES) { ++ ctx->blksize = DES_BLOCK_SIZE; ++ ret = verify_skcipher_des_key(ctfm, key); ++ } ++ if (flags & EIP93_ALG_3DES) { ++ ctx->blksize = DES3_EDE_BLOCK_SIZE; ++ ret = verify_skcipher_des3_key(ctfm, key); ++ } ++ ++ if (flags & EIP93_ALG_AES) { ++ struct crypto_aes_ctx aes; ++ ++ ctx->blksize = AES_BLOCK_SIZE; ++ ret = aes_expandkey(&aes, key, keylen); ++ } ++ if (ret) ++ return ret; ++ ++ eip93_set_sa_record(sa_record, keylen, flags); ++ ++ memcpy(sa_record->sa_key, key, keylen); ++ ctx->sa_nonce = nonce; ++ sa_record->sa_nonce = nonce; ++ ++ return 0; ++} ++ ++static int eip93_skcipher_crypt(struct skcipher_request *req) ++{ ++ struct eip93_cipher_reqctx *rctx = skcipher_request_ctx(req); ++ struct crypto_async_request *async = &req->base; ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(req->base.tfm); ++ struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req); ++ int ret; ++ ++ if (!req->cryptlen) ++ return 0; ++ ++ /* ++ * ECB and CBC algorithms require message lengths to be ++ * multiples of block size. ++ */ ++ if (IS_ECB(rctx->flags) || IS_CBC(rctx->flags)) ++ if (!IS_ALIGNED(req->cryptlen, ++ crypto_skcipher_blocksize(skcipher))) ++ return -EINVAL; ++ ++ ctx->sa_record_base = dma_map_single(ctx->mtk->dev, ctx->sa_record, ++ sizeof(*ctx->sa_record), DMA_TO_DEVICE); ++ ret = dma_mapping_error(ctx->mtk->dev, ctx->sa_record_base); ++ if (ret) ++ return ret; ++ ++ rctx->assoclen = 0; ++ rctx->textsize = req->cryptlen; ++ rctx->authsize = 0; ++ rctx->sg_src = req->src; ++ rctx->sg_dst = req->dst; ++ rctx->ivsize = crypto_skcipher_ivsize(skcipher); ++ rctx->blksize = ctx->blksize; ++ rctx->desc_flags = EIP93_DESC_SKCIPHER; ++ rctx->sa_record_base = ctx->sa_record_base; ++ ++ return eip93_skcipher_send_req(async); ++} ++ ++static int eip93_skcipher_encrypt(struct skcipher_request *req) ++{ ++ struct eip93_cipher_reqctx *rctx = skcipher_request_ctx(req); ++ struct eip93_alg_template *tmpl = container_of(req->base.tfm->__crt_alg, ++ struct eip93_alg_template, alg.skcipher.base); ++ ++ rctx->flags = tmpl->flags; ++ rctx->flags |= EIP93_ENCRYPT; ++ ++ return eip93_skcipher_crypt(req); ++} ++ ++static int eip93_skcipher_decrypt(struct skcipher_request *req) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(req->base.tfm); ++ struct eip93_cipher_reqctx *rctx = skcipher_request_ctx(req); ++ struct eip93_alg_template *tmpl = container_of(req->base.tfm->__crt_alg, ++ struct eip93_alg_template, alg.skcipher.base); ++ ++ ctx->sa_record->sa_cmd0_word |= EIP93_SA_CMD_DIRECTION_IN; ++ ++ rctx->flags = tmpl->flags; ++ rctx->flags |= EIP93_DECRYPT; ++ ++ return eip93_skcipher_crypt(req); ++} ++ ++/* Available algorithms in this module */ ++struct eip93_alg_template eip93_alg_ecb_aes = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_ECB | EIP93_ALG_AES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = 0, ++ .base = { ++ .cra_name = "ecb(aes)", ++ .cra_driver_name = "ecb(aes-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0xf, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_cbc_aes = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_CBC | EIP93_ALG_AES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .base = { ++ .cra_name = "cbc(aes)", ++ .cra_driver_name = "cbc(aes-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0xf, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_ctr_aes = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_CTR | EIP93_ALG_AES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .base = { ++ .cra_name = "ctr(aes)", ++ .cra_driver_name = "ctr(aes-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0xf, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_rfc3686_aes = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_CTR | EIP93_MODE_RFC3686 | EIP93_ALG_AES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = AES_MIN_KEY_SIZE + CTR_RFC3686_NONCE_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE + CTR_RFC3686_NONCE_SIZE, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .base = { ++ .cra_name = "rfc3686(ctr(aes))", ++ .cra_driver_name = "rfc3686(ctr(aes-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0xf, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_ecb_des = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_ECB | EIP93_ALG_DES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = DES_KEY_SIZE, ++ .max_keysize = DES_KEY_SIZE, ++ .ivsize = 0, ++ .base = { ++ .cra_name = "ecb(des)", ++ .cra_driver_name = "ebc(des-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_cbc_des = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_CBC | EIP93_ALG_DES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = DES_KEY_SIZE, ++ .max_keysize = DES_KEY_SIZE, ++ .ivsize = DES_BLOCK_SIZE, ++ .base = { ++ .cra_name = "cbc(des)", ++ .cra_driver_name = "cbc(des-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_ecb_des3_ede = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_ECB | EIP93_ALG_3DES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = DES3_EDE_KEY_SIZE, ++ .max_keysize = DES3_EDE_KEY_SIZE, ++ .ivsize = 0, ++ .base = { ++ .cra_name = "ecb(des3_ede)", ++ .cra_driver_name = "ecb(des3_ede-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_cbc_des3_ede = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_CBC | EIP93_ALG_3DES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = DES3_EDE_KEY_SIZE, ++ .max_keysize = DES3_EDE_KEY_SIZE, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .base = { ++ .cra_name = "cbc(des3_ede)", ++ .cra_driver_name = "cbc(des3_ede-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-cipher.h +@@ -0,0 +1,60 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "eip93-cipher.h" ++#include "eip93-hash.h" ++#include "eip93-common.h" ++#include "eip93-main.h" ++#include "eip93-regs.h" ++ ++int eip93_parse_ctrl_stat_err(struct eip93_device *mtk, int err) ++{ ++ u32 ext_err; ++ ++ if (!err) ++ return 0; ++ ++ switch (err & ~EIP93_PE_CTRL_PE_EXT_ERR_CODE) { ++ case EIP93_PE_CTRL_PE_AUTH_ERR: ++ case EIP93_PE_CTRL_PE_PAD_ERR: ++ return -EBADMSG; ++ /* let software handle anti-replay errors */ ++ case EIP93_PE_CTRL_PE_SEQNUM_ERR: ++ return 0; ++ case EIP93_PE_CTRL_PE_EXT_ERR: ++ break; ++ default: ++ dev_err(mtk->dev, "Unhandled error 0x%08x\n", err); ++ return -EINVAL; ++ } ++ ++ /* Parse additional ext errors */ ++ ext_err = FIELD_GET(EIP93_PE_CTRL_PE_EXT_ERR_CODE, err); ++ switch (ext_err) { ++ case EIP93_PE_CTRL_PE_EXT_ERR_BUS: ++ case EIP93_PE_CTRL_PE_EXT_ERR_PROCESSING: ++ return -EIO; ++ case EIP93_PE_CTRL_PE_EXT_ERR_DESC_OWNER: ++ return -EACCES; ++ case EIP93_PE_CTRL_PE_EXT_ERR_INVALID_CRYPTO_OP: ++ case EIP93_PE_CTRL_PE_EXT_ERR_INVALID_CRYPTO_ALGO: ++ case EIP93_PE_CTRL_PE_EXT_ERR_SPI: ++ return -EINVAL; ++ case EIP93_PE_CTRL_PE_EXT_ERR_ZERO_LENGTH: ++ case EIP93_PE_CTRL_PE_EXT_ERR_INVALID_PK_LENGTH: ++ case EIP93_PE_CTRL_PE_EXT_ERR_BLOCK_SIZE_ERR: ++ return -EBADMSG; ++ default: ++ dev_err(mtk->dev, "Unhandled ext error 0x%08x\n", ext_err); ++ return -EINVAL; ++ } ++} ++ ++static void *eip93_ring_next_wptr(struct eip93_device *mtk, ++ struct eip93_desc_ring *ring) ++{ ++ void *ptr = ring->write; ++ ++ if ((ring->write == ring->read - ring->offset) || ++ (ring->read == ring->base && ring->write == ring->base_end)) ++ return ERR_PTR(-ENOMEM); ++ ++ if (ring->write == ring->base_end) ++ ring->write = ring->base; ++ else ++ ring->write += ring->offset; ++ ++ return ptr; ++} ++ ++static void *eip93_ring_next_rptr(struct eip93_device *mtk, ++ struct eip93_desc_ring *ring) ++{ ++ void *ptr = ring->read; ++ ++ if (ring->write == ring->read) ++ return ERR_PTR(-ENOENT); ++ ++ if (ring->read == ring->base_end) ++ ring->read = ring->base; ++ else ++ ring->read += ring->offset; ++ ++ return ptr; ++} ++ ++int eip93_put_descriptor(struct eip93_device *mtk, ++ struct eip93_descriptor *desc) ++{ ++ struct eip93_descriptor *cdesc; ++ struct eip93_descriptor *rdesc; ++ ++ guard(spinlock_irqsave)(&mtk->ring->write_lock); ++ ++ rdesc = eip93_ring_next_wptr(mtk, &mtk->ring->rdr); ++ ++ if (IS_ERR(rdesc)) ++ return -ENOENT; ++ ++ cdesc = eip93_ring_next_wptr(mtk, &mtk->ring->cdr); ++ if (IS_ERR(cdesc)) ++ return -ENOENT; ++ ++ memset(rdesc, 0, sizeof(struct eip93_descriptor)); ++ ++ memcpy(cdesc, desc, sizeof(struct eip93_descriptor)); ++ ++ atomic_dec(&mtk->ring->free); ++ ++ return 0; ++} ++ ++void *eip93_get_descriptor(struct eip93_device *mtk) ++{ ++ struct eip93_descriptor *cdesc; ++ void *ptr; ++ ++ guard(spinlock_irqsave)(&mtk->ring->read_lock); ++ ++ cdesc = eip93_ring_next_rptr(mtk, &mtk->ring->cdr); ++ if (IS_ERR(cdesc)) ++ return ERR_PTR(-ENOENT); ++ ++ memset(cdesc, 0, sizeof(struct eip93_descriptor)); ++ ++ ptr = eip93_ring_next_rptr(mtk, &mtk->ring->rdr); ++ if (IS_ERR(ptr)) ++ return ERR_PTR(-ENOENT); ++ ++ atomic_inc(&mtk->ring->free); ++ ++ return ptr; ++} ++ ++static void eip93_free_sg_copy(const int len, struct scatterlist **sg) ++{ ++ if (!*sg || !len) ++ return; ++ ++ free_pages((unsigned long)sg_virt(*sg), get_order(len)); ++ kfree(*sg); ++ *sg = NULL; ++} ++ ++static int eip93_make_sg_copy(struct scatterlist *src, struct scatterlist **dst, ++ const u32 len, const bool copy) ++{ ++ void *pages; ++ ++ *dst = kmalloc(sizeof(**dst), GFP_KERNEL); ++ if (!*dst) ++ return -ENOMEM; ++ ++ pages = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, ++ get_order(len)); ++ if (!pages) { ++ kfree(*dst); ++ *dst = NULL; ++ return -ENOMEM; ++ } ++ ++ sg_init_table(*dst, 1); ++ sg_set_buf(*dst, pages, len); ++ ++ /* copy only as requested */ ++ if (copy) ++ sg_copy_to_buffer(src, sg_nents(src), pages, len); ++ ++ return 0; ++} ++ ++static bool eip93_is_sg_aligned(struct scatterlist *sg, u32 len, ++ const int blksize) ++{ ++ int nents; ++ ++ for (nents = 0; sg; sg = sg_next(sg), ++nents) { ++ if (!IS_ALIGNED(sg->offset, 4)) ++ return false; ++ ++ if (len <= sg->length) { ++ if (!IS_ALIGNED(len, blksize)) ++ return false; ++ ++ return true; ++ } ++ ++ if (!IS_ALIGNED(sg->length, blksize)) ++ return false; ++ ++ len -= sg->length; ++ } ++ return false; ++} ++ ++int check_valid_request(struct eip93_cipher_reqctx *rctx) ++{ ++ struct scatterlist *src = rctx->sg_src; ++ struct scatterlist *dst = rctx->sg_dst; ++ u32 src_nents, dst_nents; ++ u32 textsize = rctx->textsize; ++ u32 authsize = rctx->authsize; ++ u32 blksize = rctx->blksize; ++ u32 totlen_src = rctx->assoclen + rctx->textsize; ++ u32 totlen_dst = rctx->assoclen + rctx->textsize; ++ u32 copy_len; ++ bool src_align, dst_align; ++ int err = -EINVAL; ++ ++ if (!IS_CTR(rctx->flags)) { ++ if (!IS_ALIGNED(textsize, blksize)) ++ return err; ++ } ++ ++ if (authsize) { ++ if (IS_ENCRYPT(rctx->flags)) ++ totlen_dst += authsize; ++ else ++ totlen_src += authsize; ++ } ++ ++ src_nents = sg_nents_for_len(src, totlen_src); ++ dst_nents = sg_nents_for_len(dst, totlen_dst); ++ ++ if (src == dst) { ++ src_nents = max(src_nents, dst_nents); ++ dst_nents = src_nents; ++ if (unlikely((totlen_src || totlen_dst) && src_nents <= 0)) ++ return err; ++ ++ } else { ++ if (unlikely(totlen_src && src_nents <= 0)) ++ return err; ++ ++ if (unlikely(totlen_dst && dst_nents <= 0)) ++ return err; ++ } ++ ++ if (authsize) { ++ if (dst_nents == 1 && src_nents == 1) { ++ src_align = eip93_is_sg_aligned(src, totlen_src, blksize); ++ if (src == dst) ++ dst_align = src_align; ++ else ++ dst_align = eip93_is_sg_aligned(dst, totlen_dst, blksize); ++ } else { ++ src_align = false; ++ dst_align = false; ++ } ++ } else { ++ src_align = eip93_is_sg_aligned(src, totlen_src, blksize); ++ if (src == dst) ++ dst_align = src_align; ++ else ++ dst_align = eip93_is_sg_aligned(dst, totlen_dst, blksize); ++ } ++ ++ copy_len = max(totlen_src, totlen_dst); ++ if (!src_align) { ++ err = eip93_make_sg_copy(src, &rctx->sg_src, copy_len, true); ++ if (err) ++ return err; ++ } ++ ++ if (!dst_align) { ++ err = eip93_make_sg_copy(dst, &rctx->sg_dst, copy_len, false); ++ if (err) ++ return err; ++ } ++ ++ rctx->src_nents = sg_nents_for_len(rctx->sg_src, totlen_src); ++ rctx->dst_nents = sg_nents_for_len(rctx->sg_dst, totlen_dst); ++ ++ return 0; ++} ++ ++/* ++ * Set sa_record function: ++ * Even sa_record is set to "0", keep " = 0" for readability. ++ */ ++void eip93_set_sa_record(struct sa_record *sa_record, const unsigned int keylen, ++ const u32 flags) ++{ ++ /* Reset cmd word */ ++ sa_record->sa_cmd0_word = 0; ++ sa_record->sa_cmd1_word = 0; ++ ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_IV_FROM_STATE; ++ if (!IS_ECB(flags)) ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_SAVE_IV; ++ ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_OP_BASIC; ++ ++ switch ((flags & EIP93_ALG_MASK)) { ++ case EIP93_ALG_AES: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_AES; ++ sa_record->sa_cmd1_word |= FIELD_PREP(EIP93_SA_CMD_AES_KEY_LENGTH, ++ keylen >> 3); ++ break; ++ case EIP93_ALG_3DES: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_3DES; ++ break; ++ case EIP93_ALG_DES: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_DES; ++ break; ++ default: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_NULL; ++ } ++ ++ switch ((flags & EIP93_HASH_MASK)) { ++ case EIP93_HASH_SHA256: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_SHA256; ++ break; ++ case EIP93_HASH_SHA224: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_SHA224; ++ break; ++ case EIP93_HASH_SHA1: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_SHA1; ++ break; ++ case EIP93_HASH_MD5: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_MD5; ++ break; ++ default: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_NULL; ++ } ++ ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_PAD_ZERO; ++ ++ switch ((flags & EIP93_MODE_MASK)) { ++ case EIP93_MODE_CBC: ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_CHIPER_MODE_CBC; ++ break; ++ case EIP93_MODE_CTR: ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_CHIPER_MODE_CTR; ++ break; ++ case EIP93_MODE_ECB: ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_CHIPER_MODE_ECB; ++ break; ++ } ++ ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_DIGEST_3WORD; ++ if (IS_HASH(flags)) { ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_COPY_PAD; ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_COPY_DIGEST; ++ } ++ ++ if (IS_HMAC(flags)) { ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_HMAC; ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_COPY_HEADER; ++ } ++ ++ sa_record->sa_spi = 0x0; ++ sa_record->sa_seqmum_mask[0] = 0xFFFFFFFF; ++ sa_record->sa_seqmum_mask[1] = 0x0; ++} ++ ++/* ++ * Poor mans Scatter/gather function: ++ * Create a Descriptor for every segment to avoid copying buffers. ++ * For performance better to wait for hardware to perform multiple DMA ++ */ ++static int eip93_scatter_combine(struct eip93_device *mtk, ++ struct eip93_cipher_reqctx *rctx, ++ u32 datalen, u32 split, int offsetin) ++{ ++ struct eip93_descriptor *cdesc = rctx->cdesc; ++ struct scatterlist *sgsrc = rctx->sg_src; ++ struct scatterlist *sgdst = rctx->sg_dst; ++ unsigned int remainin = sg_dma_len(sgsrc); ++ unsigned int remainout = sg_dma_len(sgdst); ++ dma_addr_t saddr = sg_dma_address(sgsrc); ++ dma_addr_t daddr = sg_dma_address(sgdst); ++ dma_addr_t state_addr; ++ u32 src_addr, dst_addr, len, n; ++ bool nextin = false; ++ bool nextout = false; ++ int offsetout = 0; ++ int ndesc_cdr = 0, err; ++ ++ if (IS_ECB(rctx->flags)) ++ rctx->sa_state_base = 0; ++ ++ if (split < datalen) { ++ state_addr = rctx->sa_state_ctr_base; ++ n = split; ++ } else { ++ state_addr = rctx->sa_state_base; ++ n = datalen; ++ } ++ ++ do { ++ if (nextin) { ++ sgsrc = sg_next(sgsrc); ++ remainin = sg_dma_len(sgsrc); ++ if (remainin == 0) ++ continue; ++ ++ saddr = sg_dma_address(sgsrc); ++ offsetin = 0; ++ nextin = false; ++ } ++ ++ if (nextout) { ++ sgdst = sg_next(sgdst); ++ remainout = sg_dma_len(sgdst); ++ if (remainout == 0) ++ continue; ++ ++ daddr = sg_dma_address(sgdst); ++ offsetout = 0; ++ nextout = false; ++ } ++ src_addr = saddr + offsetin; ++ dst_addr = daddr + offsetout; ++ ++ if (remainin == remainout) { ++ len = remainin; ++ if (len > n) { ++ len = n; ++ remainin -= n; ++ remainout -= n; ++ offsetin += n; ++ offsetout += n; ++ } else { ++ nextin = true; ++ nextout = true; ++ } ++ } else if (remainin < remainout) { ++ len = remainin; ++ if (len > n) { ++ len = n; ++ remainin -= n; ++ remainout -= n; ++ offsetin += n; ++ offsetout += n; ++ } else { ++ offsetout += len; ++ remainout -= len; ++ nextin = true; ++ } ++ } else { ++ len = remainout; ++ if (len > n) { ++ len = n; ++ remainin -= n; ++ remainout -= n; ++ offsetin += n; ++ offsetout += n; ++ } else { ++ offsetin += len; ++ remainin -= len; ++ nextout = true; ++ } ++ } ++ n -= len; ++ ++ cdesc->src_addr = src_addr; ++ cdesc->dst_addr = dst_addr; ++ cdesc->state_addr = state_addr; ++ cdesc->pe_length_word = FIELD_PREP(EIP93_PE_LENGTH_HOST_PE_READY, ++ EIP93_PE_LENGTH_HOST_READY); ++ cdesc->pe_length_word |= FIELD_PREP(EIP93_PE_LENGTH_LENGTH, len); ++ ++ if (n == 0) { ++ n = datalen - split; ++ split = datalen; ++ state_addr = rctx->sa_state_base; ++ } ++ ++ if (n == 0) ++ cdesc->user_id |= FIELD_PREP(EIP93_PE_USER_ID_DESC_FLAGS, ++ EIP93_DESC_LAST); ++ ++ /* ++ * Loop - Delay - No need to rollback ++ * Maybe refine by slowing down at EIP93_RING_BUSY ++ */ ++again: ++ err = eip93_put_descriptor(mtk, cdesc); ++ if (err) { ++ usleep_range(EIP93_RING_BUSY_DELAY, ++ EIP93_RING_BUSY_DELAY * 2); ++ goto again; ++ } ++ /* Writing new descriptor count starts DMA action */ ++ writel(1, mtk->base + EIP93_REG_PE_CD_COUNT); ++ ++ ndesc_cdr++; ++ } while (n); ++ ++ return -EINPROGRESS; ++} ++ ++int eip93_send_req(struct crypto_async_request *async, ++ const u8 *reqiv, struct eip93_cipher_reqctx *rctx) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(async->tfm); ++ struct eip93_device *mtk = ctx->mtk; ++ struct scatterlist *src = rctx->sg_src; ++ struct scatterlist *dst = rctx->sg_dst; ++ struct sa_state *sa_state; ++ struct eip93_descriptor cdesc; ++ u32 flags = rctx->flags; ++ int offsetin = 0, err; ++ u32 datalen = rctx->assoclen + rctx->textsize; ++ u32 split = datalen; ++ u32 start, end, ctr, blocks; ++ u32 iv[AES_BLOCK_SIZE / sizeof(u32)]; ++ int crypto_async_idr; ++ ++ rctx->sa_state_ctr = NULL; ++ rctx->sa_state = NULL; ++ ++ if (IS_ECB(flags)) ++ goto skip_iv; ++ ++ memcpy(iv, reqiv, rctx->ivsize); ++ ++ rctx->sa_state = kzalloc(sizeof(*rctx->sa_state), GFP_KERNEL); ++ if (!rctx->sa_state) ++ return -ENOMEM; ++ ++ sa_state = rctx->sa_state; ++ ++ memcpy(sa_state->state_iv, iv, rctx->ivsize); ++ if (IS_RFC3686(flags)) { ++ sa_state->state_iv[0] = ctx->sa_nonce; ++ sa_state->state_iv[1] = iv[0]; ++ sa_state->state_iv[2] = iv[1]; ++ sa_state->state_iv[3] = cpu_to_be32(1); ++ } else if (!IS_HMAC(flags) && IS_CTR(flags)) { ++ /* Compute data length. */ ++ blocks = DIV_ROUND_UP(rctx->textsize, AES_BLOCK_SIZE); ++ ctr = be32_to_cpu(iv[3]); ++ /* Check 32bit counter overflow. */ ++ start = ctr; ++ end = start + blocks - 1; ++ if (end < start) { ++ split = AES_BLOCK_SIZE * -start; ++ /* ++ * Increment the counter manually to cope with ++ * the hardware counter overflow. ++ */ ++ iv[3] = 0xffffffff; ++ crypto_inc((u8 *)iv, AES_BLOCK_SIZE); ++ ++ rctx->sa_state_ctr = kzalloc(sizeof(*rctx->sa_state_ctr), ++ GFP_KERNEL); ++ if (!rctx->sa_state_ctr) ++ goto free_sa_state; ++ ++ memcpy(rctx->sa_state_ctr->state_iv, reqiv, rctx->ivsize); ++ memcpy(sa_state->state_iv, iv, rctx->ivsize); ++ ++ rctx->sa_state_ctr_base = dma_map_single(mtk->dev, rctx->sa_state_ctr, ++ sizeof(*rctx->sa_state_ctr), ++ DMA_TO_DEVICE); ++ err = dma_mapping_error(mtk->dev, rctx->sa_state_ctr_base); ++ if (err) ++ goto free_sa_state_ctr; ++ } ++ } ++ ++ rctx->sa_state_base = dma_map_single(mtk->dev, rctx->sa_state, ++ sizeof(*rctx->sa_state), DMA_TO_DEVICE); ++ err = dma_mapping_error(mtk->dev, rctx->sa_state_base); ++ if (err) ++ goto free_sa_state_ctr_dma; ++ ++skip_iv: ++ ++ cdesc.pe_ctrl_stat_word = FIELD_PREP(EIP93_PE_CTRL_PE_READY_DES_TRING_OWN, ++ EIP93_PE_CTRL_HOST_READY); ++ cdesc.sa_addr = rctx->sa_record_base; ++ cdesc.arc4_addr = 0; ++ ++ scoped_guard(spinlock_bh, &mtk->ring->idr_lock) ++ crypto_async_idr = idr_alloc(&mtk->ring->crypto_async_idr, async, 0, ++ EIP93_RING_NUM - 1, GFP_ATOMIC); ++ ++ cdesc.user_id = FIELD_PREP(EIP93_PE_USER_ID_CRYPTO_IDR, (u16)crypto_async_idr) | ++ FIELD_PREP(EIP93_PE_USER_ID_DESC_FLAGS, rctx->desc_flags); ++ ++ rctx->cdesc = &cdesc; ++ ++ /* map DMA_BIDIRECTIONAL to invalidate cache on destination ++ * implies __dma_cache_wback_inv ++ */ ++ if (!dma_map_sg(mtk->dev, dst, rctx->dst_nents, DMA_BIDIRECTIONAL)) { ++ err = -ENOMEM; ++ goto free_sa_state_ctr_dma; ++ } ++ ++ if (src != dst && ++ !dma_map_sg(mtk->dev, src, rctx->src_nents, DMA_TO_DEVICE)) { ++ err = -ENOMEM; ++ goto free_sg_dma; ++ } ++ ++ return eip93_scatter_combine(mtk, rctx, datalen, split, offsetin); ++ ++free_sg_dma: ++ dma_unmap_sg(mtk->dev, dst, rctx->dst_nents, DMA_BIDIRECTIONAL); ++free_sa_state_ctr_dma: ++ if (rctx->sa_state_ctr) ++ dma_unmap_single(mtk->dev, rctx->sa_state_ctr_base, ++ sizeof(*rctx->sa_state_ctr), ++ DMA_TO_DEVICE); ++free_sa_state_ctr: ++ kfree(rctx->sa_state_ctr); ++ if (rctx->sa_state) ++ dma_unmap_single(mtk->dev, rctx->sa_state_base, ++ sizeof(*rctx->sa_state), ++ DMA_TO_DEVICE); ++free_sa_state: ++ kfree(rctx->sa_state); ++ ++ return err; ++} ++ ++void eip93_unmap_dma(struct eip93_device *mtk, struct eip93_cipher_reqctx *rctx, ++ struct scatterlist *reqsrc, struct scatterlist *reqdst) ++{ ++ u32 len = rctx->assoclen + rctx->textsize; ++ u32 authsize = rctx->authsize; ++ u32 flags = rctx->flags; ++ u32 *otag; ++ int i; ++ ++ if (rctx->sg_src == rctx->sg_dst) { ++ dma_unmap_sg(mtk->dev, rctx->sg_dst, rctx->dst_nents, ++ DMA_BIDIRECTIONAL); ++ goto process_tag; ++ } ++ ++ dma_unmap_sg(mtk->dev, rctx->sg_src, rctx->src_nents, ++ DMA_TO_DEVICE); ++ ++ if (rctx->sg_src != reqsrc) ++ eip93_free_sg_copy(len + rctx->authsize, &rctx->sg_src); ++ ++ dma_unmap_sg(mtk->dev, rctx->sg_dst, rctx->dst_nents, ++ DMA_BIDIRECTIONAL); ++ ++ /* SHA tags need conversion from net-to-host */ ++process_tag: ++ if (IS_DECRYPT(flags)) ++ authsize = 0; ++ ++ if (authsize) { ++ if (!IS_HASH_MD5(flags)) { ++ otag = sg_virt(rctx->sg_dst) + len; ++ for (i = 0; i < (authsize / 4); i++) ++ otag[i] = be32_to_cpu(otag[i]); ++ } ++ } ++ ++ if (rctx->sg_dst != reqdst) { ++ sg_copy_from_buffer(reqdst, sg_nents(reqdst), ++ sg_virt(rctx->sg_dst), len + authsize); ++ eip93_free_sg_copy(len + rctx->authsize, &rctx->sg_dst); ++ } ++} ++ ++void eip93_handle_result(struct eip93_device *mtk, struct eip93_cipher_reqctx *rctx, ++ u8 *reqiv) ++{ ++ if (rctx->sa_state_ctr) ++ dma_unmap_single(mtk->dev, rctx->sa_state_ctr_base, ++ sizeof(*rctx->sa_state_ctr), ++ DMA_FROM_DEVICE); ++ ++ if (rctx->sa_state) ++ dma_unmap_single(mtk->dev, rctx->sa_state_base, ++ sizeof(*rctx->sa_state), ++ DMA_FROM_DEVICE); ++ ++ if (!IS_ECB(rctx->flags)) ++ memcpy(reqiv, rctx->sa_state->state_iv, rctx->ivsize); ++ ++ kfree(rctx->sa_state_ctr); ++ kfree(rctx->sa_state); ++} ++ ++/* basically this is set hmac - key */ ++int eip93_authenc_setkey(struct crypto_aead *aead, struct sa_record *sa, ++ const u8 *authkey, unsigned int authkeylen) ++{ ++ struct crypto_tfm *tfm = crypto_aead_tfm(aead); ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct crypto_ahash *ahash_tfm; ++ struct eip93_hash_reqctx *rctx; ++ struct scatterlist sg[1]; ++ struct ahash_request *req; ++ DECLARE_CRYPTO_WAIT(wait); ++ const char *alg_name; ++ u8 *ipad, *opad; ++ int i, ret; ++ ++ switch ((ctx->flags & EIP93_HASH_MASK)) { ++ case EIP93_HASH_SHA256: ++ alg_name = "sha256-eip93"; ++ break; ++ case EIP93_HASH_SHA224: ++ alg_name = "sha224-eip93"; ++ break; ++ case EIP93_HASH_SHA1: ++ alg_name = "sha1-eip93"; ++ break; ++ case EIP93_HASH_MD5: ++ alg_name = "md5-eip93"; ++ break; ++ default: /* Impossible */ ++ return -EINVAL; ++ } ++ ++ ahash_tfm = crypto_alloc_ahash(alg_name, 0, 0); ++ if (IS_ERR(ahash_tfm)) ++ return PTR_ERR(ahash_tfm); ++ ++ req = ahash_request_alloc(ahash_tfm, GFP_KERNEL); ++ if (!req) { ++ ret = -ENOMEM; ++ goto err_ahash; ++ } ++ ++ ipad = kcalloc(2, SHA256_BLOCK_SIZE, GFP_KERNEL); ++ if (!ipad) { ++ ret = -ENOMEM; ++ goto err_req; ++ } ++ opad = ipad + SHA256_BLOCK_SIZE; ++ ++ rctx = ahash_request_ctx(req); ++ crypto_init_wait(&wait); ++ ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, ++ crypto_req_done, &wait); ++ ++ /* Hash the key if > SHA256_BLOCK_SIZE */ ++ if (authkeylen > SHA256_BLOCK_SIZE) { ++ sg_init_one(&sg[0], authkey, authkeylen); ++ ++ ahash_request_set_crypt(req, sg, ipad, authkeylen); ++ ret = crypto_wait_req(crypto_ahash_digest(req), &wait); ++ ++ authkeylen = ctx->authsize; ++ } else { ++ memcpy(ipad, authkey, authkeylen); ++ } ++ ++ /* Copy to opad */ ++ memset(ipad + authkeylen, 0, SHA256_BLOCK_SIZE - authkeylen); ++ memcpy(opad, ipad, SHA256_BLOCK_SIZE); ++ ++ /* Pad with HMAC constants */ ++ for (i = 0; i < SHA256_BLOCK_SIZE; i++) { ++ ipad[i] ^= HMAC_IPAD_VALUE; ++ opad[i] ^= HMAC_OPAD_VALUE; ++ } ++ ++ /* Disable HASH_FINALIZE for ipad and opad hash */ ++ rctx->no_finalize = true; ++ ++ /* Hash ipad */ ++ sg_init_one(&sg[0], ipad, SHA256_BLOCK_SIZE); ++ ahash_request_set_crypt(req, sg, sa->sa_i_digest, SHA256_BLOCK_SIZE); ++ ret = crypto_ahash_init(req); ++ if (ret) ++ goto exit; ++ ++ /* Disable HASH_FINALIZE for ipad hash */ ++ rctx->no_finalize = true; ++ ++ ret = crypto_wait_req(crypto_ahash_finup(req), &wait); ++ if (ret) ++ goto exit; ++ ++ /* Hash opad */ ++ sg_init_one(&sg[0], opad, SHA256_BLOCK_SIZE); ++ ahash_request_set_crypt(req, sg, sa->sa_o_digest, SHA256_BLOCK_SIZE); ++ ret = crypto_ahash_init(req); ++ if (ret) ++ goto exit; ++ ++ /* Disable HASH_FINALIZE for opad hash */ ++ rctx->no_finalize = true; ++ ++ ret = crypto_wait_req(crypto_ahash_finup(req), &wait); ++ if (ret) ++ goto exit; ++ ++ if (!IS_HASH_MD5(ctx->flags)) { ++ for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(u32); i++) { ++ u32 *ipad_hash = (u32 *)sa->sa_i_digest; ++ u32 *opad_hash = (u32 *)sa->sa_o_digest; ++ ++ ipad_hash[i] = cpu_to_be32(ipad_hash[i]); ++ opad_hash[i] = cpu_to_be32(opad_hash[i]); ++ } ++ } ++ ++exit: ++ kfree(ipad); ++err_req: ++ ahash_request_free(req); ++err_ahash: ++ crypto_free_ahash(ahash_tfm); ++ ++ return ret; ++} +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-common.h +@@ -0,0 +1,25 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include ++ ++#include "eip93-cipher.h" ++#include "eip93-hash.h" ++#include "eip93-main.h" ++#include "eip93-common.h" ++#include "eip93-regs.h" ++ ++static void eip93_hash_free_data_blocks(struct ahash_request *req) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct mkt_hash_block *block; ++ ++ list_for_each_entry(block, &rctx->blocks, list) { ++ dma_unmap_single(rctx->mtk->dev, block->data_dma, ++ SHA256_BLOCK_SIZE, DMA_TO_DEVICE); ++ kfree(block); ++ } ++} ++ ++static void eip93_hash_free_sa_record(struct ahash_request *req) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ ++ if (IS_HMAC(ctx->flags)) { ++ dma_unmap_single(rctx->mtk->dev, rctx->sa_record_hmac_base, ++ sizeof(*rctx->sa_record_hmac), DMA_TO_DEVICE); ++ kfree(rctx->sa_record_hmac); ++ } ++ ++ dma_unmap_single(rctx->mtk->dev, rctx->sa_record_base, ++ sizeof(*rctx->sa_record), DMA_TO_DEVICE); ++ kfree(rctx->sa_record); ++} ++ ++static void eip93_hash_free_sa_state(struct ahash_request *req) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ ++ dma_unmap_single(rctx->mtk->dev, rctx->sa_state_base, ++ sizeof(*rctx->sa_state), DMA_TO_DEVICE); ++ kfree(rctx->sa_state); ++} ++ ++static struct sa_state *eip93_hash_get_sa_state(struct ahash_request *req, ++ dma_addr_t *sa_state_base) ++{ ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ struct eip93_device *mtk = ctx->mtk; ++ struct sa_state *sa_state; ++ int ret; ++ ++ sa_state = kzalloc(sizeof(*sa_state), GFP_KERNEL); ++ if (!sa_state) ++ return ERR_PTR(-ENOMEM); ++ ++ /* Init HASH constant */ ++ switch ((ctx->flags & EIP93_HASH_MASK)) { ++ case EIP93_HASH_SHA256: ++ u32 sha256_init[] = { SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3, ++ SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7 }; ++ ++ memcpy(sa_state->state_i_digest, sha256_init, sizeof(sha256_init)); ++ break; ++ case EIP93_HASH_SHA224: ++ u32 sha224_init[] = { SHA224_H0, SHA224_H1, SHA224_H2, SHA224_H3, ++ SHA224_H4, SHA224_H5, SHA224_H6, SHA224_H7 }; ++ ++ memcpy(sa_state->state_i_digest, sha224_init, sizeof(sha224_init)); ++ break; ++ case EIP93_HASH_SHA1: ++ u32 sha1_init[] = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 }; ++ ++ memcpy(sa_state->state_i_digest, sha1_init, sizeof(sha1_init)); ++ break; ++ case EIP93_HASH_MD5: ++ u32 md5_init[] = { MD5_H0, MD5_H1, MD5_H2, MD5_H3 }; ++ ++ memcpy(sa_state->state_i_digest, md5_init, sizeof(md5_init)); ++ break; ++ default: /* Impossible */ ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ *sa_state_base = dma_map_single(mtk->dev, sa_state, ++ sizeof(*sa_state), DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, *sa_state_base); ++ if (ret) { ++ kfree(sa_state); ++ return ERR_PTR(ret); ++ } ++ ++ return sa_state; ++} ++ ++static int _eip93_hash_init(struct ahash_request *req, struct sa_state *sa_state, ++ dma_addr_t sa_state_base) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ struct sa_record *sa_record, *sa_record_hmac; ++ struct eip93_device *mtk = rctx->mtk; ++ int digestsize; ++ int ret; ++ ++ sa_record = kzalloc(sizeof(*sa_record), GFP_KERNEL); ++ if (!sa_record) ++ return -ENOMEM; ++ ++ if (IS_HMAC(ctx->flags)) { ++ sa_record_hmac = kzalloc(sizeof(*sa_record_hmac), GFP_KERNEL); ++ if (!sa_record_hmac) { ++ ret = -ENOMEM; ++ goto free_sa_record; ++ } ++ } ++ ++ digestsize = crypto_ahash_digestsize(ahash); ++ ++ eip93_set_sa_record(sa_record, 0, ctx->flags); ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_FROM_STATE; ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_SAVE_HASH; ++ sa_record->sa_cmd0_word &= ~EIP93_SA_CMD_OPCODE; ++ sa_record->sa_cmd0_word |= FIELD_PREP(EIP93_SA_CMD_OPCODE, ++ EIP93_SA_CMD_OPCODE_BASIC_OUT_HASH); ++ sa_record->sa_cmd0_word &= ~EIP93_SA_CMD_DIGEST_LENGTH; ++ sa_record->sa_cmd0_word |= FIELD_PREP(EIP93_SA_CMD_DIGEST_LENGTH, ++ digestsize / sizeof(u32)); ++ ++ /* ++ * HMAC special handling ++ * Enabling CMD_HMAC force the inner hash to be always finalized. ++ * This cause problems on handling message > 64 byte as we ++ * need to produce intermediate inner hash on sending intermediate ++ * 64 bytes blocks. ++ * ++ * To handle this, enable CMD_HMAC only on the last block. ++ * We make a duplicate of sa_record and on the last descriptor, ++ * we pass a dedicated sa_record with CMD_HMAC enabled to make ++ * EIP93 apply the outer hash. ++ */ ++ if (IS_HMAC(ctx->flags)) { ++ memcpy(sa_record_hmac, sa_record, sizeof(*sa_record)); ++ /* Copy pre-hashed opad for HMAC */ ++ memcpy(sa_record_hmac->sa_o_digest, ctx->opad, SHA256_DIGEST_SIZE); ++ ++ /* Disable HMAC for hash normal sa_record */ ++ sa_record->sa_cmd1_word &= ~EIP93_SA_CMD_HMAC; ++ } ++ ++ rctx->mtk = ctx->mtk; ++ rctx->sa_record = sa_record; ++ rctx->sa_record_base = dma_map_single(mtk->dev, rctx->sa_record, ++ sizeof(*rctx->sa_record), ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, rctx->sa_record_base); ++ if (ret) ++ goto free_sa_record; ++ ++ if (IS_HMAC(ctx->flags)) { ++ rctx->sa_record_hmac = sa_record_hmac; ++ rctx->sa_record_hmac_base = dma_map_single(mtk->dev, ++ rctx->sa_record_hmac, ++ sizeof(*rctx->sa_record_hmac), ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, rctx->sa_record_hmac_base); ++ if (ret) ++ goto free_sa_record_base; ++ } ++ ++ rctx->sa_state = sa_state; ++ rctx->sa_state_base = sa_state_base; ++ ++ rctx->len = 0; ++ rctx->left_last = 0; ++ rctx->no_finalize = false; ++ INIT_LIST_HEAD(&rctx->blocks); ++ ++ return 0; ++ ++free_sa_record_base: ++ dma_unmap_single(mtk->dev, rctx->sa_record_base, sizeof(*rctx->sa_record), ++ DMA_TO_DEVICE); ++free_sa_record: ++ kfree(sa_record); ++ return ret; ++} ++ ++static int eip93_hash_init(struct ahash_request *req) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ struct sa_state *sa_state; ++ dma_addr_t sa_state_base; ++ int ret; ++ ++ sa_state = eip93_hash_get_sa_state(req, &sa_state_base); ++ if (IS_ERR(sa_state)) ++ return PTR_ERR(sa_state); ++ ++ ret = _eip93_hash_init(req, sa_state, sa_state_base); ++ if (ret) ++ eip93_hash_free_sa_state(req); ++ ++ /* For HMAC setup the initial block for ipad */ ++ if (IS_HMAC(ctx->flags)) { ++ struct mkt_hash_block *block; ++ ++ block = kzalloc(sizeof(*block), GFP_KERNEL); ++ if (!block) { ++ eip93_hash_free_sa_record(req); ++ eip93_hash_free_sa_state(req); ++ return -ENOMEM; ++ } ++ ++ memcpy(block->data, ctx->ipad, SHA256_BLOCK_SIZE); ++ ++ list_add(&block->list, &rctx->blocks); ++ ++ rctx->len += SHA256_BLOCK_SIZE; ++ } ++ ++ return ret; ++} ++ ++static void eip93_send_hash_req(struct crypto_async_request *async, dma_addr_t src_addr, ++ u32 len, bool last) ++{ ++ struct ahash_request *req = ahash_request_cast(async); ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ struct eip93_device *mtk = rctx->mtk; ++ struct eip93_descriptor cdesc = { }; ++ int ret; ++ ++ cdesc.pe_ctrl_stat_word = FIELD_PREP(EIP93_PE_CTRL_PE_READY_DES_TRING_OWN, ++ EIP93_PE_CTRL_HOST_READY); ++ cdesc.sa_addr = rctx->sa_record_base; ++ cdesc.arc4_addr = 0; ++ ++ cdesc.state_addr = rctx->sa_state_base; ++ cdesc.src_addr = src_addr; ++ cdesc.pe_length_word = FIELD_PREP(EIP93_PE_LENGTH_HOST_PE_READY, ++ EIP93_PE_LENGTH_HOST_READY); ++ cdesc.pe_length_word |= FIELD_PREP(EIP93_PE_LENGTH_LENGTH, ++ len); ++ ++ cdesc.user_id |= FIELD_PREP(EIP93_PE_USER_ID_DESC_FLAGS, EIP93_DESC_HASH); ++ ++ if (last) { ++ int crypto_async_idr; ++ ++ /* For last block, pass sa_record with CMD_HMAC enabled */ ++ if (IS_HMAC(ctx->flags)) ++ cdesc.sa_addr = rctx->sa_record_hmac_base; ++ ++ if (!rctx->no_finalize) ++ cdesc.pe_ctrl_stat_word |= EIP93_PE_CTRL_PE_HASH_FINAL; ++ ++ scoped_guard(spinlock_bh, &mtk->ring->idr_lock) ++ crypto_async_idr = idr_alloc(&mtk->ring->crypto_async_idr, async, 0, ++ EIP93_RING_NUM - 1, GFP_ATOMIC); ++ ++ cdesc.user_id |= FIELD_PREP(EIP93_PE_USER_ID_CRYPTO_IDR, (u16)crypto_async_idr) | ++ FIELD_PREP(EIP93_PE_USER_ID_DESC_FLAGS, EIP93_DESC_LAST); ++ } ++ ++again: ++ ret = eip93_put_descriptor(mtk, &cdesc); ++ if (ret) { ++ usleep_range(EIP93_RING_BUSY_DELAY, ++ EIP93_RING_BUSY_DELAY * 2); ++ goto again; ++ } ++ ++ /* Writing new descriptor count starts DMA action */ ++ writel(1, mtk->base + EIP93_REG_PE_CD_COUNT); ++} ++ ++static int eip93_hash_update(struct ahash_request *req) ++{ ++ struct crypto_async_request *async = &req->base; ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ unsigned int to_consume = req->nbytes; ++ struct eip93_device *mtk = rctx->mtk; ++ struct mkt_hash_block *block; ++ int read = 0; ++ int ret; ++ ++ /* If the request is 0 length, do nothing */ ++ if (!to_consume) ++ return 0; ++ ++ /* ++ * Check if we are at a second iteration. ++ * 1. Try to fill the first block to 64byte (if not already) ++ * 2. Send full block (if we have more data to consume) ++ */ ++ if (rctx->len > 0) { ++ int offset = SHA256_BLOCK_SIZE - rctx->left_last; ++ ++ block = list_first_entry(&rctx->blocks, ++ struct mkt_hash_block, list); ++ ++ /* Fill the first block */ ++ if (rctx->left_last) { ++ read += sg_pcopy_to_buffer(req->src, sg_nents(req->src), ++ block->data + offset, ++ min(to_consume, rctx->left_last), ++ 0); ++ to_consume -= read; ++ rctx->left_last -= read; ++ } ++ ++ /* Send descriptor if we have more data to consume */ ++ if (to_consume > 0) { ++ block->data_dma = dma_map_single(mtk->dev, block->data, ++ SHA256_BLOCK_SIZE, ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, block->data_dma); ++ if (ret) ++ return ret; ++ ++ eip93_send_hash_req(async, block->data_dma, ++ SHA256_BLOCK_SIZE, false); ++ } ++ } ++ ++ /* ++ * Consume remaining data. ++ * 1. Loop until we consume all the data in block of 64bytes ++ * 2. Send full block of 64bytes ++ * 3. Skip sending last block for future update() or for final() to ++ * enable HASH_FINALIZE bit. ++ */ ++ while (to_consume > 0) { ++ int to_read = min(to_consume, SHA256_BLOCK_SIZE); ++ ++ block = kzalloc(sizeof(*block), GFP_KERNEL); ++ if (!block) ++ return -ENOMEM; ++ ++ read += sg_pcopy_to_buffer(req->src, sg_nents(req->src), ++ block->data, to_read, ++ read); ++ ++ list_add(&block->list, &rctx->blocks); ++ ++ to_consume -= to_read; ++ rctx->left_last = SHA256_BLOCK_SIZE - to_read; ++ ++ /* Send descriptor if we have more data to consume */ ++ if (to_consume > 0) { ++ block->data_dma = dma_map_single(mtk->dev, block->data, ++ SHA256_BLOCK_SIZE, ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, block->data_dma); ++ if (ret) ++ return ret; ++ ++ eip93_send_hash_req(async, block->data_dma, ++ SHA256_BLOCK_SIZE, false); ++ } ++ } ++ ++ /* ++ * Update counter with processed bytes. ++ * This is also used to check if we are at the second iteration ++ * of an update(). ++ */ ++ rctx->len += req->nbytes; ++ ++ return 0; ++} ++ ++void eip93_hash_handle_result(struct crypto_async_request *async, int err) ++{ ++ struct ahash_request *req = ahash_request_cast(async); ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ int digestsize = crypto_ahash_digestsize(ahash); ++ struct sa_state *sa_state = rctx->sa_state; ++ int i; ++ ++ /* Unmap and sync sa_state for host */ ++ dma_unmap_single(rctx->mtk->dev, rctx->sa_state_base, ++ sizeof(*sa_state), DMA_FROM_DEVICE); ++ ++ /* ++ * With no_finalize assume SHA256_DIGEST_SIZE buffer is passed. ++ * This is to handle SHA224 that have a 32 byte intermediate digest. ++ */ ++ if (rctx->no_finalize) ++ digestsize = SHA256_DIGEST_SIZE; ++ ++ /* bytes needs to be swapped for req->result */ ++ if (!IS_HASH_MD5(ctx->flags)) { ++ for (i = 0; i < digestsize / sizeof(u32); i++) { ++ u32 *digest = (u32 *)sa_state->state_i_digest; ++ ++ digest[i] = be32_to_cpu(digest[i]); ++ } ++ } ++ ++ memcpy(req->result, sa_state->state_i_digest, digestsize); ++ ++ kfree(sa_state); ++ eip93_hash_free_data_blocks(req); ++ eip93_hash_free_sa_record(req); ++ ++ ahash_request_complete(req, err); ++} ++ ++static int eip93_hash_final(struct ahash_request *req) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ struct crypto_async_request *async = &req->base; ++ struct eip93_device *mtk = rctx->mtk; ++ struct mkt_hash_block *block; ++ int ret; ++ ++ /* EIP93 can't handle zero bytes hash */ ++ if (!rctx->len && !IS_HMAC(ctx->flags)) { ++ switch ((ctx->flags & EIP93_HASH_MASK)) { ++ case EIP93_HASH_SHA256: ++ memcpy(req->result, sha256_zero_message_hash, ++ SHA256_DIGEST_SIZE); ++ break; ++ case EIP93_HASH_SHA224: ++ memcpy(req->result, sha224_zero_message_hash, ++ SHA224_DIGEST_SIZE); ++ break; ++ case EIP93_HASH_SHA1: ++ memcpy(req->result, sha1_zero_message_hash, ++ SHA1_DIGEST_SIZE); ++ break; ++ case EIP93_HASH_MD5: ++ memcpy(req->result, md5_zero_message_hash, ++ MD5_DIGEST_SIZE); ++ break; ++ default: /* Impossible */ ++ return -EINVAL; ++ } ++ ++ eip93_hash_free_sa_state(req); ++ eip93_hash_free_sa_record(req); ++ ++ return 0; ++ } ++ ++ /* Send last block */ ++ block = list_first_entry(&rctx->blocks, struct mkt_hash_block, list); ++ ++ block->data_dma = dma_map_single(mtk->dev, block->data, ++ SHA256_BLOCK_SIZE, DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, block->data_dma); ++ if (ret) ++ return ret; ++ ++ eip93_send_hash_req(async, block->data_dma, ++ SHA256_BLOCK_SIZE - rctx->left_last, ++ true); ++ ++ return -EINPROGRESS; ++} ++ ++static int eip93_hash_finup(struct ahash_request *req) ++{ ++ int ret; ++ ++ ret = eip93_hash_update(req); ++ if (ret) ++ return ret; ++ ++ return eip93_hash_final(req); ++} ++ ++static int eip93_hash_hmac_setkey(struct crypto_ahash *ahash, const u8 *key, ++ u32 keylen) ++{ ++ unsigned int digestsize = crypto_ahash_digestsize(ahash); ++ struct crypto_tfm *tfm = crypto_ahash_tfm(ahash); ++ struct eip93_hash_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct crypto_ahash *ahash_tfm; ++ struct eip93_hash_reqctx *rctx; ++ struct scatterlist sg[1]; ++ struct ahash_request *req; ++ DECLARE_CRYPTO_WAIT(wait); ++ const char *alg_name; ++ int i, ret = 0; ++ u8 *opad; ++ ++ switch ((ctx->flags & EIP93_HASH_MASK)) { ++ case EIP93_HASH_SHA256: ++ alg_name = "sha256-eip93"; ++ break; ++ case EIP93_HASH_SHA224: ++ alg_name = "sha224-eip93"; ++ break; ++ case EIP93_HASH_SHA1: ++ alg_name = "sha1-eip93"; ++ break; ++ case EIP93_HASH_MD5: ++ alg_name = "md5-eip93"; ++ break; ++ default: /* Impossible */ ++ return -EINVAL; ++ } ++ ++ ahash_tfm = crypto_alloc_ahash(alg_name, 0, 0); ++ if (IS_ERR(ahash_tfm)) ++ return PTR_ERR(ahash_tfm); ++ ++ req = ahash_request_alloc(ahash_tfm, GFP_KERNEL); ++ if (!req) { ++ ret = -ENOMEM; ++ goto err_ahash; ++ } ++ ++ opad = kzalloc(SHA256_BLOCK_SIZE, GFP_KERNEL); ++ if (!opad) { ++ ret = -ENOMEM; ++ goto err_req; ++ } ++ ++ rctx = ahash_request_ctx(req); ++ crypto_init_wait(&wait); ++ ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, ++ crypto_req_done, &wait); ++ ++ /* Hash the key if > SHA256_BLOCK_SIZE */ ++ if (keylen > SHA256_BLOCK_SIZE) { ++ sg_init_one(&sg[0], key, keylen); ++ ++ ahash_request_set_crypt(req, sg, ctx->ipad, keylen); ++ ret = crypto_wait_req(crypto_ahash_digest(req), &wait); ++ ++ keylen = digestsize; ++ } else { ++ memcpy(ctx->ipad, key, keylen); ++ } ++ ++ /* Copy to opad */ ++ memset(ctx->ipad + keylen, 0, SHA256_BLOCK_SIZE - keylen); ++ memcpy(opad, ctx->ipad, SHA256_BLOCK_SIZE); ++ ++ /* Pad with HMAC constants */ ++ for (i = 0; i < SHA256_BLOCK_SIZE; i++) { ++ ctx->ipad[i] ^= HMAC_IPAD_VALUE; ++ opad[i] ^= HMAC_OPAD_VALUE; ++ } ++ ++ sg_init_one(&sg[0], opad, SHA256_BLOCK_SIZE); ++ ++ /* Hash opad */ ++ ahash_request_set_crypt(req, sg, ctx->opad, SHA256_BLOCK_SIZE); ++ ret = crypto_ahash_init(req); ++ if (ret) ++ goto exit; ++ ++ /* Disable HASH_FINALIZE for opad hash */ ++ rctx->no_finalize = true; ++ ++ ret = crypto_wait_req(crypto_ahash_finup(req), &wait); ++ if (ret) ++ goto exit; ++ ++ if (!IS_HASH_MD5(ctx->flags)) { ++ u32 *opad_hash = (u32 *)ctx->opad; ++ ++ for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(u32); i++) ++ opad_hash[i] = cpu_to_be32(opad_hash[i]); ++ } ++ ++exit: ++ kfree(opad); ++err_req: ++ ahash_request_free(req); ++err_ahash: ++ crypto_free_ahash(ahash_tfm); ++ ++ return ret; ++} ++ ++static int eip93_hash_cra_init(struct crypto_tfm *tfm) ++{ ++ struct eip93_hash_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct eip93_alg_template *tmpl = container_of(tfm->__crt_alg, ++ struct eip93_alg_template, alg.ahash.halg.base); ++ ++ crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), ++ sizeof(struct eip93_hash_reqctx)); ++ ++ ctx->mtk = tmpl->mtk; ++ ctx->flags = tmpl->flags; ++ ++ return 0; ++} ++ ++static int eip93_hash_digest(struct ahash_request *req) ++{ ++ int ret; ++ ++ ret = eip93_hash_init(req); ++ if (ret) ++ return ret; ++ ++ return eip93_hash_finup(req); ++} ++ ++static int eip93_hash_import(struct ahash_request *req, const void *in) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ const struct eip93_hash_export_state *state = in; ++ int ret; ++ ++ ret = _eip93_hash_init(req, state->sa_state, state->sa_state_base); ++ if (ret) ++ goto err; ++ ++ rctx->len = state->len; ++ rctx->left_last = state->left_last; ++ memcpy(&rctx->blocks, &state->blocks, sizeof(rctx->blocks)); ++ ++ return 0; ++err: ++ eip93_hash_free_data_blocks(req); ++ eip93_hash_free_sa_state(req); ++ return ret; ++} ++ ++static int eip93_hash_export(struct ahash_request *req, void *out) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct eip93_hash_export_state *state = out; ++ ++ state->sa_state = rctx->sa_state; ++ state->sa_state_base = rctx->sa_state_base; ++ state->len = rctx->len; ++ state->left_last = rctx->left_last; ++ memcpy(&state->blocks, &rctx->blocks, sizeof(rctx->blocks)); ++ ++ return 0; ++} ++ ++struct eip93_alg_template eip93_alg_md5 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_MD5, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = MD5_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "md5", ++ .cra_driver_name = "md5-eip93", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = MD5_HMAC_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_sha1 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_SHA1, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA1_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "sha1", ++ .cra_driver_name = "sha1-eip93", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA1_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_sha224 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_SHA224, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA224_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "sha224", ++ .cra_driver_name = "sha224-eip93", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA224_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_sha256 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_SHA256, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA256_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "sha256", ++ .cra_driver_name = "sha256-eip93", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA256_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_hmac_md5 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_MD5, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .setkey = eip93_hash_hmac_setkey, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = MD5_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "hmac(md5)", ++ .cra_driver_name = "hmac(md5-eip93)", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = MD5_HMAC_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_hmac_sha1 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA1, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .setkey = eip93_hash_hmac_setkey, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA1_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "hmac(sha1)", ++ .cra_driver_name = "hmac(sha1-eip93)", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA1_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_hmac_sha224 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA224, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .setkey = eip93_hash_hmac_setkey, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA224_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "hmac(sha224)", ++ .cra_driver_name = "hmac(sha224-eip93)", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA224_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_hmac_sha256 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA256, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .setkey = eip93_hash_hmac_setkey, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA256_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "hmac(sha256)", ++ .cra_driver_name = "hmac(sha256-eip93)", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA256_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-hash.h +@@ -0,0 +1,72 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++ ++#include "eip93-main.h" ++ ++struct eip93_hash_ctx { ++ struct eip93_device *mtk; ++ u32 flags; ++ ++ u8 ipad[SHA256_BLOCK_SIZE] __aligned(sizeof(u32)); ++ u8 opad[SHA256_DIGEST_SIZE] __aligned(sizeof(u32)); ++}; ++ ++struct eip93_hash_reqctx { ++ struct eip93_device *mtk; ++ ++ struct sa_record *sa_record; ++ dma_addr_t sa_record_base; ++ ++ struct sa_record *sa_record_hmac; ++ dma_addr_t sa_record_hmac_base; ++ ++ struct sa_state *sa_state; ++ dma_addr_t sa_state_base; ++ ++ /* Don't enable HASH_FINALIZE when last block is sent */ ++ bool no_finalize; ++ ++ /* ++ * EIP93 requires data to be accumulated in block of 64 bytes ++ * for intermediate hash calculation. ++ */ ++ u64 len; ++ u32 left_last; ++ struct list_head blocks; ++}; ++ ++struct mkt_hash_block { ++ struct list_head list; ++ u8 data[SHA256_BLOCK_SIZE] __aligned(sizeof(u32)); ++ dma_addr_t data_dma; ++}; ++ ++struct eip93_hash_export_state { ++ u64 len; ++ u32 left_last; ++ struct sa_state *sa_state; ++ dma_addr_t sa_state_base; ++ struct list_head blocks; ++}; ++ ++void eip93_hash_handle_result(struct crypto_async_request *async, int err); ++ ++extern struct eip93_alg_template eip93_alg_md5; ++extern struct eip93_alg_template eip93_alg_sha1; ++extern struct eip93_alg_template eip93_alg_sha224; ++extern struct eip93_alg_template eip93_alg_sha256; ++extern struct eip93_alg_template eip93_alg_hmac_md5; ++extern struct eip93_alg_template eip93_alg_hmac_sha1; ++extern struct eip93_alg_template eip93_alg_hmac_sha224; ++extern struct eip93_alg_template eip93_alg_hmac_sha256; ++ ++#endif /* _EIP93_HASH_H_ */ +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-main.c +@@ -0,0 +1,502 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "eip93-main.h" ++#include "eip93-regs.h" ++#include "eip93-common.h" ++#include "eip93-cipher.h" ++#include "eip93-aes.h" ++#include "eip93-des.h" ++#include "eip93-aead.h" ++#include "eip93-hash.h" ++ ++static struct eip93_alg_template *eip93_algs[] = { ++ &eip93_alg_ecb_des, ++ &eip93_alg_cbc_des, ++ &eip93_alg_ecb_des3_ede, ++ &eip93_alg_cbc_des3_ede, ++ &eip93_alg_ecb_aes, ++ &eip93_alg_cbc_aes, ++ &eip93_alg_ctr_aes, ++ &eip93_alg_rfc3686_aes, ++ &eip93_alg_authenc_hmac_md5_cbc_des, ++ &eip93_alg_authenc_hmac_sha1_cbc_des, ++ &eip93_alg_authenc_hmac_sha224_cbc_des, ++ &eip93_alg_authenc_hmac_sha256_cbc_des, ++ &eip93_alg_authenc_hmac_md5_cbc_des3_ede, ++ &eip93_alg_authenc_hmac_sha1_cbc_des3_ede, ++ &eip93_alg_authenc_hmac_sha224_cbc_des3_ede, ++ &eip93_alg_authenc_hmac_sha256_cbc_des3_ede, ++ &eip93_alg_authenc_hmac_md5_cbc_aes, ++ &eip93_alg_authenc_hmac_sha1_cbc_aes, ++ &eip93_alg_authenc_hmac_sha224_cbc_aes, ++ &eip93_alg_authenc_hmac_sha256_cbc_aes, ++ &eip93_alg_authenc_hmac_md5_rfc3686_aes, ++ &eip93_alg_authenc_hmac_sha1_rfc3686_aes, ++ &eip93_alg_authenc_hmac_sha224_rfc3686_aes, ++ &eip93_alg_authenc_hmac_sha256_rfc3686_aes, ++ &eip93_alg_md5, ++ &eip93_alg_sha1, ++ &eip93_alg_sha224, ++ &eip93_alg_sha256, ++ &eip93_alg_hmac_md5, ++ &eip93_alg_hmac_sha1, ++ &eip93_alg_hmac_sha224, ++ &eip93_alg_hmac_sha256, ++}; ++ ++inline void eip93_irq_disable(struct eip93_device *mtk, u32 mask) ++{ ++ __raw_writel(mask, mtk->base + EIP93_REG_MASK_DISABLE); ++} ++ ++inline void eip93_irq_enable(struct eip93_device *mtk, u32 mask) ++{ ++ __raw_writel(mask, mtk->base + EIP93_REG_MASK_ENABLE); ++} ++ ++inline void eip93_irq_clear(struct eip93_device *mtk, u32 mask) ++{ ++ __raw_writel(mask, mtk->base + EIP93_REG_INT_CLR); ++} ++ ++static void eip93_unregister_algs(unsigned int i) ++{ ++ unsigned int j; ++ ++ for (j = 0; j < i; j++) { ++ switch (eip93_algs[j]->type) { ++ case EIP93_ALG_TYPE_SKCIPHER: ++ crypto_unregister_skcipher(&eip93_algs[j]->alg.skcipher); ++ break; ++ case EIP93_ALG_TYPE_AEAD: ++ crypto_unregister_aead(&eip93_algs[j]->alg.aead); ++ break; ++ case EIP93_ALG_TYPE_HASH: ++ crypto_unregister_ahash(&eip93_algs[i]->alg.ahash); ++ break; ++ } ++ } ++} ++ ++static int eip93_register_algs(struct eip93_device *mtk, u32 supported_algo_flags) ++{ ++ unsigned int i; ++ int ret = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(eip93_algs); i++) { ++ u32 alg_flags = eip93_algs[i]->flags; ++ ++ eip93_algs[i]->mtk = mtk; ++ ++ if ((IS_DES(alg_flags) || IS_3DES(alg_flags)) && ++ !(supported_algo_flags & EIP93_PE_OPTION_TDES)) ++ continue; ++ ++ if (IS_AES(alg_flags)) { ++ if (!(supported_algo_flags & EIP93_PE_OPTION_AES)) ++ continue; ++ ++ if (!IS_HMAC(alg_flags)) { ++ if (supported_algo_flags & EIP93_PE_OPTION_AES_KEY128) ++ eip93_algs[i]->alg.skcipher.max_keysize = ++ AES_KEYSIZE_128; ++ ++ if (supported_algo_flags & EIP93_PE_OPTION_AES_KEY192) ++ eip93_algs[i]->alg.skcipher.max_keysize = ++ AES_KEYSIZE_192; ++ ++ if (supported_algo_flags & EIP93_PE_OPTION_AES_KEY256) ++ eip93_algs[i]->alg.skcipher.max_keysize = ++ AES_KEYSIZE_256; ++ ++ if (IS_RFC3686(alg_flags)) ++ eip93_algs[i]->alg.skcipher.max_keysize += ++ CTR_RFC3686_NONCE_SIZE; ++ } ++ } ++ ++ if (IS_HASH_MD5(alg_flags) && ++ !(supported_algo_flags & EIP93_PE_OPTION_MD5)) ++ continue; ++ ++ if (IS_HASH_SHA1(alg_flags) && ++ !(supported_algo_flags & EIP93_PE_OPTION_SHA_1)) ++ continue; ++ ++ if (IS_HASH_SHA224(alg_flags) && ++ !(supported_algo_flags & EIP93_PE_OPTION_SHA_224)) ++ continue; ++ ++ if (IS_HASH_SHA256(alg_flags) && ++ !(supported_algo_flags & EIP93_PE_OPTION_SHA_256)) ++ continue; ++ ++ switch (eip93_algs[i]->type) { ++ case EIP93_ALG_TYPE_SKCIPHER: ++ ret = crypto_register_skcipher(&eip93_algs[i]->alg.skcipher); ++ break; ++ case EIP93_ALG_TYPE_AEAD: ++ ret = crypto_register_aead(&eip93_algs[i]->alg.aead); ++ break; ++ case EIP93_ALG_TYPE_HASH: ++ ret = crypto_register_ahash(&eip93_algs[i]->alg.ahash); ++ break; ++ } ++ if (ret) ++ goto fail; ++ } ++ ++ return 0; ++ ++fail: ++ eip93_unregister_algs(i); ++ ++ return ret; ++} ++ ++static void eip93_handle_result_descriptor(struct eip93_device *mtk) ++{ ++ struct crypto_async_request *async; ++ struct eip93_descriptor *rdesc; ++ u16 desc_flags, crypto_idr; ++ bool last_entry; ++ int handled, left, err; ++ u32 pe_ctrl_stat; ++ u32 pe_length; ++ ++get_more: ++ handled = 0; ++ ++ left = readl(mtk->base + EIP93_REG_PE_RD_COUNT) & EIP93_PE_RD_COUNT; ++ ++ if (!left) { ++ eip93_irq_clear(mtk, EIP93_INT_RDR_THRESH); ++ eip93_irq_enable(mtk, EIP93_INT_RDR_THRESH); ++ return; ++ } ++ ++ last_entry = false; ++ ++ while (left) { ++ rdesc = eip93_get_descriptor(mtk); ++ if (IS_ERR(rdesc)) { ++ dev_err(mtk->dev, "Ndesc: %d nreq: %d\n", ++ handled, left); ++ err = -EIO; ++ break; ++ } ++ /* make sure DMA is finished writing */ ++ do { ++ pe_ctrl_stat = READ_ONCE(rdesc->pe_ctrl_stat_word); ++ pe_length = READ_ONCE(rdesc->pe_length_word); ++ } while (FIELD_GET(EIP93_PE_CTRL_PE_READY_DES_TRING_OWN, pe_ctrl_stat) != ++ EIP93_PE_CTRL_PE_READY || ++ FIELD_GET(EIP93_PE_LENGTH_HOST_PE_READY, pe_length) != ++ EIP93_PE_LENGTH_PE_READY); ++ ++ err = rdesc->pe_ctrl_stat_word & (EIP93_PE_CTRL_PE_EXT_ERR_CODE | ++ EIP93_PE_CTRL_PE_EXT_ERR | ++ EIP93_PE_CTRL_PE_SEQNUM_ERR | ++ EIP93_PE_CTRL_PE_PAD_ERR | ++ EIP93_PE_CTRL_PE_AUTH_ERR); ++ ++ desc_flags = FIELD_GET(EIP93_PE_USER_ID_DESC_FLAGS, rdesc->user_id); ++ crypto_idr = FIELD_GET(EIP93_PE_USER_ID_CRYPTO_IDR, rdesc->user_id); ++ ++ writel(1, mtk->base + EIP93_REG_PE_RD_COUNT); ++ eip93_irq_clear(mtk, EIP93_INT_RDR_THRESH); ++ ++ handled++; ++ left--; ++ ++ if (desc_flags & EIP93_DESC_LAST) { ++ last_entry = true; ++ break; ++ } ++ } ++ ++ if (!last_entry) ++ goto get_more; ++ ++ /* Get crypto async ref only for last descriptor */ ++ scoped_guard(spinlock_bh, &mtk->ring->idr_lock) { ++ async = idr_find(&mtk->ring->crypto_async_idr, crypto_idr); ++ idr_remove(&mtk->ring->crypto_async_idr, crypto_idr); ++ } ++ ++ /* Parse error in ctrl stat word */ ++ err = eip93_parse_ctrl_stat_err(mtk, err); ++ ++ if (desc_flags & EIP93_DESC_SKCIPHER) ++ eip93_skcipher_handle_result(async, err); ++ ++ if (desc_flags & EIP93_DESC_AEAD) ++ eip93_aead_handle_result(async, err); ++ ++ if (desc_flags & EIP93_DESC_HASH) ++ eip93_hash_handle_result(async, err); ++ ++ goto get_more; ++} ++ ++static void eip93_done_task(unsigned long data) ++{ ++ struct eip93_device *mtk = (struct eip93_device *)data; ++ ++ eip93_handle_result_descriptor(mtk); ++} ++ ++static irqreturn_t eip93_irq_handler(int irq, void *data) ++{ ++ struct eip93_device *mtk = data; ++ u32 irq_status; ++ ++ irq_status = readl(mtk->base + EIP93_REG_INT_MASK_STAT); ++ if (FIELD_GET(EIP93_INT_RDR_THRESH, irq_status)) { ++ eip93_irq_disable(mtk, EIP93_INT_RDR_THRESH); ++ tasklet_schedule(&mtk->ring->done_task); ++ return IRQ_HANDLED; ++ } ++ ++ /* Ignore errors in AUTO mode, handled by the RDR */ ++ eip93_irq_clear(mtk, irq_status); ++ if (irq_status) ++ eip93_irq_disable(mtk, irq_status); ++ ++ return IRQ_NONE; ++} ++ ++static void eip93_initialize(struct eip93_device *mtk, u32 supported_algo_flags) ++{ ++ u32 val; ++ ++ /* Reset PE and rings */ ++ val = EIP93_PE_CONFIG_RST_PE | EIP93_PE_CONFIG_RST_RING; ++ val |= EIP93_PE_TARGET_AUTO_RING_MODE; ++ /* For Auto more, update the CDR ring owner after processing */ ++ val |= EIP93_PE_CONFIG_EN_CDR_UPDATE; ++ writel(val, mtk->base + EIP93_REG_PE_CONFIG); ++ ++ /* Wait for PE and ring to reset */ ++ usleep_range(10, 20); ++ ++ /* Release PE and ring reset */ ++ val = readl(mtk->base + EIP93_REG_PE_CONFIG); ++ val &= ~(EIP93_PE_CONFIG_RST_PE | EIP93_PE_CONFIG_RST_RING); ++ writel(val, mtk->base + EIP93_REG_PE_CONFIG); ++ ++ /* Config Clocks */ ++ val = EIP93_PE_CLOCK_EN_PE_CLK; ++ if (supported_algo_flags & EIP93_PE_OPTION_TDES) ++ val |= EIP93_PE_CLOCK_EN_DES_CLK; ++ if (supported_algo_flags & EIP93_PE_OPTION_AES) ++ val |= EIP93_PE_CLOCK_EN_AES_CLK; ++ if (supported_algo_flags & ++ (EIP93_PE_OPTION_MD5 | EIP93_PE_OPTION_SHA_1 | EIP93_PE_OPTION_SHA_224 | ++ EIP93_PE_OPTION_SHA_256)) ++ val |= EIP93_PE_CLOCK_EN_HASH_CLK; ++ writel(val, mtk->base + EIP93_REG_PE_CLOCK_CTRL); ++ ++ /* Config DMA thresholds */ ++ val = FIELD_PREP(EIP93_PE_OUTBUF_THRESH, 128) | ++ FIELD_PREP(EIP93_PE_INBUF_THRESH, 128); ++ writel(val, mtk->base + EIP93_REG_PE_BUF_THRESH); ++ ++ /* Clear/ack all interrupts before disable all */ ++ eip93_irq_clear(mtk, EIP93_INT_ALL); ++ eip93_irq_disable(mtk, EIP93_INT_ALL); ++ ++ /* Setup CRD threshold to trigger interrupt */ ++ val = FIELD_PREP(EIPR93_PE_CDR_THRESH, EIP93_RING_NUM - EIP93_RING_BUSY); ++ /* ++ * Configure RDR interrupt to be triggered if RD counter is not 0 ++ * for more than 2^(N+10) system clocks. ++ */ ++ val |= FIELD_PREP(EIPR93_PE_RD_TIMEOUT, 5) | EIPR93_PE_TIMEROUT_EN; ++ writel(val, mtk->base + EIP93_REG_PE_RING_THRESH); ++} ++ ++static void eip93_desc_free(struct eip93_device *mtk) ++{ ++ writel(0, mtk->base + EIP93_REG_PE_RING_CONFIG); ++ writel(0, mtk->base + EIP93_REG_PE_CDR_BASE); ++ writel(0, mtk->base + EIP93_REG_PE_RDR_BASE); ++} ++ ++static int eip93_set_ring(struct eip93_device *mtk, struct eip93_desc_ring *ring) ++{ ++ ring->offset = sizeof(struct eip93_descriptor); ++ ring->base = dmam_alloc_coherent(mtk->dev, ++ sizeof(struct eip93_descriptor) * EIP93_RING_NUM, ++ &ring->base_dma, GFP_KERNEL); ++ if (!ring->base) ++ return -ENOMEM; ++ ++ ring->write = ring->base; ++ ring->base_end = ring->base + sizeof(struct eip93_descriptor) * (EIP93_RING_NUM - 1); ++ ring->read = ring->base; ++ ++ return 0; ++} ++ ++static int eip93_desc_init(struct eip93_device *mtk) ++{ ++ struct eip93_desc_ring *cdr = &mtk->ring->cdr; ++ struct eip93_desc_ring *rdr = &mtk->ring->rdr; ++ int ret; ++ u32 val; ++ ++ ret = eip93_set_ring(mtk, cdr); ++ if (ret) ++ return ret; ++ ++ ret = eip93_set_ring(mtk, rdr); ++ if (ret) ++ return ret; ++ ++ writel((u32 __force)cdr->base_dma, mtk->base + EIP93_REG_PE_CDR_BASE); ++ writel((u32 __force)rdr->base_dma, mtk->base + EIP93_REG_PE_RDR_BASE); ++ ++ val = FIELD_PREP(EIP93_PE_RING_SIZE, EIP93_RING_NUM - 1); ++ writel(val, mtk->base + EIP93_REG_PE_RING_CONFIG); ++ ++ atomic_set(&mtk->ring->free, EIP93_RING_NUM - 1); ++ ++ return 0; ++} ++ ++static void eip93_cleanup(struct eip93_device *mtk) ++{ ++ tasklet_kill(&mtk->ring->done_task); ++ ++ /* Clear/ack all interrupts before disable all */ ++ eip93_irq_clear(mtk, EIP93_INT_ALL); ++ eip93_irq_disable(mtk, EIP93_INT_ALL); ++ ++ writel(0, mtk->base + EIP93_REG_PE_CLOCK_CTRL); ++ ++ eip93_desc_free(mtk); ++ ++ idr_destroy(&mtk->ring->crypto_async_idr); ++} ++ ++static int eip93_crypto_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct eip93_device *mtk; ++ u32 ver, algo_flags; ++ int ret; ++ ++ mtk = devm_kzalloc(dev, sizeof(*mtk), GFP_KERNEL); ++ if (!mtk) ++ return -ENOMEM; ++ ++ mtk->dev = dev; ++ platform_set_drvdata(pdev, mtk); ++ ++ mtk->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mtk->base)) ++ return PTR_ERR(mtk->base); ++ ++ mtk->irq = platform_get_irq(pdev, 0); ++ if (mtk->irq < 0) ++ return mtk->irq; ++ ++ ret = devm_request_threaded_irq(mtk->dev, mtk->irq, eip93_irq_handler, ++ NULL, IRQF_ONESHOT, ++ dev_name(mtk->dev), mtk); ++ ++ mtk->ring = devm_kcalloc(mtk->dev, 1, sizeof(*mtk->ring), GFP_KERNEL); ++ if (!mtk->ring) ++ return -ENOMEM; ++ ++ ret = eip93_desc_init(mtk); ++ ++ if (ret) ++ return ret; ++ ++ tasklet_init(&mtk->ring->done_task, eip93_done_task, (unsigned long)mtk); ++ ++ spin_lock_init(&mtk->ring->read_lock); ++ spin_lock_init(&mtk->ring->write_lock); ++ ++ spin_lock_init(&mtk->ring->idr_lock); ++ idr_init(&mtk->ring->crypto_async_idr); ++ ++ algo_flags = readl(mtk->base + EIP93_REG_PE_OPTION_1); ++ ++ eip93_initialize(mtk, algo_flags); ++ ++ /* Init finished, enable RDR interrupt */ ++ eip93_irq_enable(mtk, EIP93_INT_RDR_THRESH); ++ ++ ret = eip93_register_algs(mtk, algo_flags); ++ if (ret) { ++ eip93_cleanup(mtk); ++ return ret; ++ } ++ ++ ver = readl(mtk->base + EIP93_REG_PE_REVISION); ++ /* EIP_EIP_NO:MAJOR_HW_REV:MINOR_HW_REV:HW_PATCH,PE(ALGO_FLAGS) */ ++ dev_info(mtk->dev, "EIP%lu:%lx:%lx:%lx,PE(0x%x:0x%x)\n", ++ FIELD_GET(EIP93_PE_REVISION_EIP_NO, ver), ++ FIELD_GET(EIP93_PE_REVISION_MAJ_HW_REV, ver), ++ FIELD_GET(EIP93_PE_REVISION_MIN_HW_REV, ver), ++ FIELD_GET(EIP93_PE_REVISION_HW_PATCH, ver), ++ algo_flags, ++ readl(mtk->base + EIP93_REG_PE_OPTION_0)); ++ ++ return 0; ++} ++ ++static void eip93_crypto_remove(struct platform_device *pdev) ++{ ++ struct eip93_device *mtk = platform_get_drvdata(pdev); ++ ++ eip93_unregister_algs(ARRAY_SIZE(eip93_algs)); ++ eip93_cleanup(mtk); ++} ++ ++static const struct of_device_id eip93_crypto_of_match[] = { ++ { .compatible = "inside-secure,safexcel-eip93i", }, ++ { .compatible = "inside-secure,safexcel-eip93ie", }, ++ { .compatible = "inside-secure,safexcel-eip93is", }, ++ { .compatible = "inside-secure,safexcel-eip93ies", }, ++ /* IW not supported currently, missing AES-XCB-MAC/AES-CCM */ ++ /* { .compatible = "inside-secure,safexcel-eip93iw", }, */ ++ {} ++}; ++MODULE_DEVICE_TABLE(of, eip93_crypto_of_match); ++ ++static struct platform_driver eip93_crypto_driver = { ++ .probe = eip93_crypto_probe, ++ .remove_new = eip93_crypto_remove, ++ .driver = { ++ .name = "mtk-eip93", ++ .of_match_table = eip93_crypto_of_match, ++ }, ++}; ++module_platform_driver(eip93_crypto_driver); ++ ++MODULE_AUTHOR("Richard van Schagen "); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Mediatek EIP-93 crypto engine driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-main.h +@@ -0,0 +1,155 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include ++ ++#include "eip93-regs.h" ++ ++#define EIP93_RING_BUSY_DELAY 500 ++ ++#define EIP93_RING_NUM 512 ++#define EIP93_RING_BUSY 32 ++#define EIP93_CRA_PRIORITY 1500 ++ ++#define EIP93_RING_SA_STATE_ADDR(base, idx) ((base) + (idx)) ++#define EIP93_RING_SA_STATE_DMA(dma_base, idx) ((u32 __force)(dma_base) + \ ++ ((idx) * sizeof(struct sa_state))) ++ ++/* cipher algorithms */ ++#define EIP93_ALG_DES BIT(0) ++#define EIP93_ALG_3DES BIT(1) ++#define EIP93_ALG_AES BIT(2) ++#define EIP93_ALG_MASK GENMASK(2, 0) ++/* hash and hmac algorithms */ ++#define EIP93_HASH_MD5 BIT(3) ++#define EIP93_HASH_SHA1 BIT(4) ++#define EIP93_HASH_SHA224 BIT(5) ++#define EIP93_HASH_SHA256 BIT(6) ++#define EIP93_HASH_HMAC BIT(7) ++#define EIP93_HASH_MASK GENMASK(6, 3) ++/* cipher modes */ ++#define EIP93_MODE_CBC BIT(8) ++#define EIP93_MODE_ECB BIT(9) ++#define EIP93_MODE_CTR BIT(10) ++#define EIP93_MODE_RFC3686 BIT(11) ++#define EIP93_MODE_MASK GENMASK(10, 8) ++ ++/* cipher encryption/decryption operations */ ++#define EIP93_ENCRYPT BIT(12) ++#define EIP93_DECRYPT BIT(13) ++ ++#define EIP93_BUSY BIT(14) ++ ++/* descriptor flags */ ++#define EIP93_DESC_DMA_IV BIT(0) ++#define EIP93_DESC_IPSEC BIT(1) ++#define EIP93_DESC_FINISH BIT(2) ++#define EIP93_DESC_LAST BIT(3) ++#define EIP93_DESC_FAKE_HMAC BIT(4) ++#define EIP93_DESC_PRNG BIT(5) ++#define EIP93_DESC_HASH BIT(6) ++#define EIP93_DESC_AEAD BIT(7) ++#define EIP93_DESC_SKCIPHER BIT(8) ++#define EIP93_DESC_ASYNC BIT(9) ++ ++#define IS_DMA_IV(desc_flags) ((desc_flags) & EIP93_DESC_DMA_IV) ++ ++#define IS_DES(flags) ((flags) & EIP93_ALG_DES) ++#define IS_3DES(flags) ((flags) & EIP93_ALG_3DES) ++#define IS_AES(flags) ((flags) & EIP93_ALG_AES) ++ ++#define IS_HASH_MD5(flags) ((flags) & EIP93_HASH_MD5) ++#define IS_HASH_SHA1(flags) ((flags) & EIP93_HASH_SHA1) ++#define IS_HASH_SHA224(flags) ((flags) & EIP93_HASH_SHA224) ++#define IS_HASH_SHA256(flags) ((flags) & EIP93_HASH_SHA256) ++#define IS_HMAC(flags) ((flags) & EIP93_HASH_HMAC) ++ ++#define IS_CBC(mode) ((mode) & EIP93_MODE_CBC) ++#define IS_ECB(mode) ((mode) & EIP93_MODE_ECB) ++#define IS_CTR(mode) ((mode) & EIP93_MODE_CTR) ++#define IS_RFC3686(mode) ((mode) & EIP93_MODE_RFC3686) ++ ++#define IS_BUSY(flags) ((flags) & EIP93_BUSY) ++ ++#define IS_ENCRYPT(dir) ((dir) & EIP93_ENCRYPT) ++#define IS_DECRYPT(dir) ((dir) & EIP93_DECRYPT) ++ ++#define IS_CIPHER(flags) ((flags) & (EIP93_ALG_DES | \ ++ EIP93_ALG_3DES | \ ++ EIP93_ALG_AES)) ++ ++#define IS_HASH(flags) ((flags) & (EIP93_HASH_MD5 | \ ++ EIP93_HASH_SHA1 | \ ++ EIP93_HASH_SHA224 | \ ++ EIP93_HASH_SHA256)) ++ ++/** ++ * struct eip93_device - crypto engine device structure ++ */ ++struct eip93_device { ++ void __iomem *base; ++ struct device *dev; ++ struct clk *clk; ++ int irq; ++ struct eip93_ring *ring; ++}; ++ ++struct eip93_desc_ring { ++ void *base; ++ void *base_end; ++ dma_addr_t base_dma; ++ /* write and read pointers */ ++ void *read; ++ void *write; ++ /* descriptor element offset */ ++ u32 offset; ++}; ++ ++struct eip93_state_pool { ++ void *base; ++ dma_addr_t base_dma; ++}; ++ ++struct eip93_ring { ++ struct tasklet_struct done_task; ++ /* command/result rings */ ++ struct eip93_desc_ring cdr; ++ struct eip93_desc_ring rdr; ++ spinlock_t write_lock; ++ spinlock_t read_lock; ++ atomic_t free; ++ /* aync idr */ ++ spinlock_t idr_lock; ++ struct idr crypto_async_idr; ++}; ++ ++enum eip93_alg_type { ++ EIP93_ALG_TYPE_AEAD, ++ EIP93_ALG_TYPE_SKCIPHER, ++ EIP93_ALG_TYPE_HASH, ++}; ++ ++struct eip93_alg_template { ++ struct eip93_device *mtk; ++ enum eip93_alg_type type; ++ u32 flags; ++ union { ++ struct aead_alg aead; ++ struct skcipher_alg skcipher; ++ struct ahash_alg ahash; ++ } alg; ++}; ++ ++#endif /* _EIP93_MAIN_H_ */ +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-regs.h +@@ -0,0 +1,335 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include "mtk_bmt.h" ++ ++#define MAX_BMT_SIZE (250) ++#define MAX_RAW_BAD_BLOCK_SIZE (250) ++#define POOL_GOOD_BLOCK_PERCENT 8/100 ++#define MAX_BMT_PERCENT 1/8 ++ ++typedef struct { ++ char signature[3]; ++ u8 version; ++ u8 bad_count; // this field is useless ++ u8 size; ++ u8 checksum; ++ u8 reseverd[13]; ++} bmt_table_header; ++ ++typedef struct { ++ u16 from; ++ u16 to; ++} bmt_entry; ++ ++typedef struct { ++ bmt_table_header header; ++ bmt_entry table[MAX_BMT_SIZE]; ++} bmt_table; ++ ++typedef struct { ++ char signature[4]; ++ u32 checksum; ++ u8 version; ++ u8 size; ++ u8 reserved[2]; ++} bbt_table_header; ++ ++typedef struct { ++ bbt_table_header header; ++ u16 table[MAX_RAW_BAD_BLOCK_SIZE]; ++} bbt_table; ++ ++bbt_table bbt; ++bmt_table bmt; ++ ++int bmt_index=0xffff; ++int bbt_index=0xffff; ++unsigned int total_blks , system_blks , bmt_blks, _to, _to2, val; ++ ++module_param(bmt_index, int, S_IRUSR | S_IWUSR); ++module_param(bbt_index, int, S_IRUSR | S_IWUSR); ++module_param(total_blks, int, S_IRUSR | S_IWUSR); ++module_param(system_blks, int, S_IRUSR | S_IWUSR); ++module_param(bmt_blks, int, S_IRUSR | S_IWUSR); ++module_param(_to, int, S_IRUSR | S_IWUSR); ++module_param(_to2, int, S_IRUSR | S_IWUSR); ++module_param(val, int, S_IRUSR | S_IWUSR); ++ ++ ++static bool is_bad_raw(int block) { ++ u8 fdm[4]; ++ int ret; ++ ret = bbt_nand_read(blk_pg(block), bmtd.data_buf, bmtd.pg_size, ++ fdm, sizeof(fdm)); ++ if (ret || fdm[0] != 0xff ){ ++ return true; ++ } ++ return false; ++} ++ ++static bool is_bad( int block) { ++ u8 fdm[4]; ++ int ret; ++ ret = bbt_nand_read(blk_pg(block), bmtd.data_buf, bmtd.pg_size, ++ fdm, sizeof(fdm)); ++ //printk("%x %x %x %x\n", fdm[0], fdm[1], fdm[2], fdm[3]); ++ if (ret || fdm[0] != 0xff || fdm[1] != 0xff ){ ++ return true; ++ } ++ return false; ++} ++ ++ ++static bool is_mapped( int block) { ++ u16 mapped_block; ++ u8 fdm[4]; ++ int ret; ++ ++ ret = bbt_nand_read(blk_pg(block), bmtd.data_buf, bmtd.pg_size, ++ fdm, sizeof(fdm)); ++ mapped_block = (fdm[2] << 8) | fdm[3]; ++ //printk("%u is mapped to %d\n", mapped_block); ++ if (mapped_block == 0xffff) ++ return false; ++ else return true; ++} ++ ++static void mark_bad(int block) { ++ u8 fdm[4] = {0xff, 0xff, 0xff, 0xff}; ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 4, ++ .oobbuf = fdm, ++ .datbuf = NULL, ++ .len = 0, ++ }; ++ int retlen; ++ ++ printk("marking bad :%d\n", block); ++ if (block < system_blks) ++ fdm[0] = 0x00; ++ else fdm[1] = 0x00; ++ ++ retlen = bmtd._write_oob(bmtd.mtd, block << bmtd.blk_shift , &ops) ; ++ if (retlen < 0) { ++ printk("marking bad block failed \n"); ++ } ++} ++ ++ ++static void mark_good(int block) { ++ u8 fdm[4] = {0xff, 0xff, 0xff, 0xff}; ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 4, ++ .oobbuf = fdm, ++ .datbuf = NULL, ++ .len = 0, ++ }; ++ int retlen; ++ retlen = bmtd._write_oob(bmtd.mtd, block << bmtd.blk_shift , &ops) ; ++ if (retlen < 0) { ++ printk("marking bad block failed \n"); ++ } ++} ++ ++static void make_mapping(u16 from , u16 to) { ++ u8 fdm[4] = {0xff, 0xff, 0xff , 0xff}; ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 4, ++ .oobbuf = fdm, ++ .datbuf = NULL, ++ .len = 0, ++ }; ++ int retlen; ++ ++ memcpy(fdm + 2, &to, sizeof(to)); // this has to be exactly like this . ++ retlen = bmtd._write_oob(bmtd.mtd, from << bmtd.blk_shift , &ops) ; ++ if (retlen < 0) { ++ printk("marking bad block failed \n"); ++ } ++} ++ ++static u16 bbt_checksum(void) { ++ int i=0; ++ u16 checksum =0; ++ u8 *data = (u8*) &bbt; ++ checksum += bbt.header.version; ++ checksum += bbt.header.size; ++ data += sizeof(bbt_table_header); ++ for (; i < sizeof(bbt.table); i++) ++ checksum += data[i]; ++ return checksum; ++} ++ ++static bool parse_bbt(void) { ++ int i = system_blks; ++ u8 fdm[4]; ++ for (; i < total_blks; i++) { ++ if( !is_bad(i) ++ && !bbt_nand_read(blk_pg(i),(unsigned char *)&bbt, sizeof(bbt), fdm, sizeof(fdm)) ++ && (strncmp(bbt.header.signature , "RAWB", 4)==0) ++ && (bbt.header.checksum == bbt_checksum()) ++ ) { ++ bbt_index = i; ++ return true; ++ } ++ } ++ return false; ++} ++ ++static u8 bmt_checksum(void) { ++ int i; ++ u8 checksum = 0; ++ u8* data = (u8*)&bmt; ++ checksum += bmt.header.version; ++ checksum += bmt.header.size; ++ data += sizeof(bmt_table_header); ++ for (i=0;i system_blks;i--) { ++ if ( !is_bad(i) ++ && !bbt_nand_read(blk_pg(i),(unsigned char *)&bmt, sizeof(bmt), fdm, sizeof(fdm)) ++ && (strncmp(bmt.header.signature , "BMT", 3)==0) ++ && (bmt.header.checksum == bmt_checksum()) ++ ) { ++ bmt_index = i ; ++ return true; ++ } ++ } ++ return false; ++} ++ ++static void variable_setup(void) { ++ unsigned int need_valid_block_num; ++ int valid_blks = 0; ++ int last_blk; ++ ++ total_blks = bmtd.total_blks; ++ last_blk = total_blks - 1; ++ need_valid_block_num = total_blks * POOL_GOOD_BLOCK_PERCENT; ++ ++ for (; last_blk > 0 ;last_blk--) { ++ if (is_bad_raw(last_blk)) { ++ continue; ++ } ++ valid_blks++; ++ if (valid_blks == need_valid_block_num) { ++ break; ++ } ++ } ++ bmt_blks = total_blks - last_blk; ++ system_blks = total_blks - bmt_blks; ++ bmtd.mtd->size = (total_blks - total_blks * MAX_BMT_PERCENT) * bmtd.mtd->erasesize; ++} ++ ++ ++static int find_available_block(bool start_from_end) { ++ int i=system_blks,d=1; ++ int count = 0; ++ if (start_from_end) ++ i=total_blks-1,d=-1; ++ for (; count < (total_blks - system_blks); count++, i+=d) { ++ if(bmt_index == i || bbt_index == i || is_bad(i) || is_mapped(i)) ++ continue; ++ return i ; ++ } ++ //TODO: handle OOM ++ return -1; ++} ++ ++static void update_bmt_bbt( void ) { ++ int retlen = 0; ++ struct mtd_oob_ops ops , ops1; ++ ++ bbt.header.checksum = bbt_checksum(); ++ bmt.header.checksum = bmt_checksum(); ++ ++ if(bbt_index ==0xffff) bbt_index = find_available_block(false); ++ if(bmt_index ==0xffff) bmt_index = find_available_block(true); ++ ++ bbt_nand_erase(bmt_index); ++ bbt_nand_erase(bbt_index); ++ printk("putting back in bbt_index: %d, bmt_index: %d\n" , bbt_index, bmt_index); ++ ++ ops = (struct mtd_oob_ops) { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ .len = sizeof(bmt), ++ .datbuf = (u8 *)&bmt, ++ }; ++ ++retry_bmt: ++ retlen = bmtd._write_oob(bmtd.mtd, bmt_index << bmtd.blk_shift, &ops); ++ if (retlen) { ++ printk("error while write"); ++ mark_bad(bmt_index); ++ if (bmt_index > system_blks) { ++ bmt_index--; ++ goto retry_bmt; ++ } ++ return; ++ } ++ ops1 = (struct mtd_oob_ops) { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ .len = sizeof(bbt), ++ .datbuf = (u8 *)&bbt, ++ }; ++ ++retry_bbt: ++ retlen = bmtd._write_oob(bmtd.mtd, bbt_index << bmtd.blk_shift, &ops1); ++ if (retlen) { ++ printk("error while write"); ++ mark_bad(bbt_index); ++ if (bbt_index < total_blks) { ++ bbt_index++; ++ goto retry_bbt; ++ } ++ return; ++ } ++} ++ ++static bool is_in_bmt(int block) { ++ int i; ++ for (i=0;i= system_blks ;i--) { ++ unsigned short mapped_block; ++ u8 fdm[4]; ++ int ret; ++ ++ if (is_bad(i)) continue; ++ ret = bbt_nand_read(blk_pg(i), bmtd.data_buf, bmtd.pg_size, ++ fdm, sizeof(fdm)); ++ if (ret < 0) ++ mark_bad(i); ++ ++ memcpy(&mapped_block,fdm+2,2); // need to be this way ++ if (mapped_block >= system_blks) continue; ++ printk("block %X was mapped to :%X\n", mapped_block, i); ++ bmt.table[bmt.header.size++] = (bmt_entry){.from = mapped_block , .to = i}; ++ } ++ memset(&bbt,0x00,sizeof(bbt)); ++ memcpy(&bbt.header.signature , "RAWB", 4); ++ bbt.header.version = 1; ++ bbt.header.size = 0; ++ for ( i = 0 ; i < system_blks; i++) { ++ if (is_bad_raw(i) && !is_in_bmt(i)) ++ bbt.table[bbt.header.size++] = (u16)i; ++ } ++ bmt.header.checksum = bmt_checksum(); ++ bbt.header.checksum = bbt_checksum(); ++ update_bmt_bbt(); ++ printk("bbt and bmt reconstructed successfully\n"); ++} ++ ++ ++static bool remap_block(u16 block , u16 mapped_block, int copy_len) { ++ bool mapped_already_in_bbt = false; ++ bool mapped_already_in_bmt = false; ++ bool block_already_in_bbt = false; ++ u16 new_block = find_available_block(false); ++ int i; ++ // TODO check for -1 ++ ++ bbt_nand_erase(new_block); ++ if (copy_len) ++ bbt_nand_copy(new_block , mapped_block , copy_len); ++ ++ for (i=0; i < bmt.header.size; i++) ++ if (bmt.table[i].from == block) { ++ bmt.table[i].to = new_block; ++ mapped_already_in_bmt = true; ++ break; ++ } ++ ++ if (!mapped_already_in_bmt) ++ bmt.table[bmt.header.size++] = (bmt_entry){ .from = block, .to = new_block}; ++ ++ for (i=0;i system_blks) ++ return block; ++ for (i = 0; i < bmt.header.size; i++) ++ if (bmt.table[i].from == block) ++ return bmt.table[i].to; ++ return block; ++} ++ ++static void unmap_block( u16 block) { // not required ++ printk("unmapping is called on block : %d\n", block); ++} ++ ++ ++static int debug( void* data , u64 cmd) { ++ int i; ++ printk("val: %d\n", val); ++ printk("_to: %d\n", _to); ++ if (val == 0 ) { ++ printk("fixing all\n"); ++ for (i=0;ierasesize); ++ mapped_block = get_mapping_block(_to); ++ printk("after mapped to: %d\n", mapped_block); ++ } else if(val ==2 ) { ++ printk("bmt table: \n"); ++ for (i = 0 ; i < bmt.header.size;i++) { ++ printk("%d->%d\n", bmt.table[i].from , bmt.table[i].to); ++ } ++ printk("bbt table\n"); ++ for (i =0;i< bbt.header.size;i++) { ++ printk("%d ", bbt.table[i]); ++ } ++ printk("\n"); ++ } else if(val == 3) { ++ printk("reconstruct from oob\n"); ++ reconstruct_from_oob(); ++ } else if (val == 4) { ++ printk("showing the oobreconstruct_from_oob of %d\n", _to); ++ printk("%d\n",is_bad(_to)); ++ } else if (val == 5 ) { ++ printk("trying to parse_bmt again %d\n", parse_bmt()); ++ } else if (val == 6 ) { ++ printk("marking bad : %d", _to); ++ mark_bad(_to); ++ } else if ( val == 7) { ++ struct mtd_oob_ops opsk = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ .len = sizeof(bmt), ++ .datbuf = (u8 *)&bmt, ++ }; ++ int retlen; ++ printk("parse bmt from the %d block \n", _to); ++ retlen = bmtd._read_oob(bmtd.mtd, _to << bmtd.blk_shift , &opsk); ++ ++ printk("status : %d\n", retlen); ++ } else if (val == 8) { ++ u8 *data; ++ int j; ++ printk("dump bmt hex\n"); ++ data = (u8 *)&bmt; ++ for (j =0;j < 50;j++) { ++ if(j%20==0) printk("\n"); ++ printk("%X ", data[j]); ++ } ++ printk("bbt table\n"); ++ data = (u8 *)&bbt; ++ for (j =0;j < 50;j++) { ++ if(j%20==0) printk("\n"); ++ printk("%X ", data[j]); ++ } ++ } else if (val == 9) { ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ .len = sizeof(bmt), ++ .datbuf = (u8 *)&bmt, ++ }; ++ int retlen; ++ printk("put bmt at index\n"); ++ retlen = bmtd._write_oob(bmtd.mtd, _to << bmtd.blk_shift, &ops); ++ bmt.header.checksum = bmt_checksum(); ++ if (retlen < 0) { ++ printk("error while write"); ++ } ++ } else if (val == 10) { ++ printk("erase block %d\n", _to); ++ bbt_nand_erase(_to); ++ } else if (val == 11) { ++ char *buf1, *buf2; ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ }; ++ struct mtd_oob_ops ops1 = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ }; ++ int retlen; ++ int j; ++ ++ printk("tranfering content from block :%d to %d\n", _to , _to2); ++ bbt_nand_copy(_to2, _to, bmtd.mtd->erasesize); ++ printk("now we check size\n"); ++ ++ buf1 = (char*) kzalloc(sizeof(char) * bmtd.mtd->erasesize , GFP_KERNEL); ++ buf2 = (char*) kzalloc(sizeof(char) * bmtd.mtd->erasesize , GFP_KERNEL); ++ ++ ops.len = sizeof(char) * bmtd.mtd->erasesize; ++ ops.datbuf = buf1; ++ retlen = bmtd._read_oob(bmtd.mtd, _to << bmtd.blk_shift, &ops); ++ if (retlen < 0) { ++ printk("error while write\n"); ++ } ++ ++ ops1.len = sizeof(char) * bmtd.mtd->erasesize; ++ ops1.datbuf = buf2; ++ retlen = bmtd._read_oob(bmtd.mtd, _to << bmtd.blk_shift, &ops1); ++ if (retlen < 0) { ++ printk("error while write"); ++ } ++ for (j = 0 ; j < bmtd.mtd->erasesize ;j++) { ++ if (j%20==0) { ++ printk("\n"); ++ } ++ printk("%X %X ", buf1[j], buf2[j]); ++ } ++ printk("\n"); ++ ++ } ++ return 0; ++} ++ ++ ++const struct mtk_bmt_ops airoha_bmt_ops = { ++ .sig = "bmt", ++ .sig_len = 3, ++ .init = init, ++ .remap_block = remap_block, ++ .unmap_block = unmap_block, ++ .get_mapping_block = get_mapping_block, ++ .debug = debug, ++}; diff --git a/target/linux/airoha/patches-6.6/901-snand-mtk-bmt-support.patch b/target/linux/airoha/patches-6.6/901-snand-mtk-bmt-support.patch new file mode 100644 index 0000000000..de8e880643 --- /dev/null +++ b/target/linux/airoha/patches-6.6/901-snand-mtk-bmt-support.patch @@ -0,0 +1,34 @@ +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) + { +@@ -1346,6 +1347,7 @@ static int spinand_probe(struct spi_mem + if (ret) + return ret; + ++ mtk_bmt_attach(mtd); + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + goto err_spinand_cleanup; +@@ -1353,6 +1355,7 @@ static int spinand_probe(struct spi_mem + return 0; + + err_spinand_cleanup: ++ mtk_bmt_detach(mtd); + spinand_cleanup(spinand); + + return ret; +@@ -1371,6 +1374,7 @@ static int spinand_remove(struct spi_mem + if (ret) + return ret; + ++ mtk_bmt_detach(mtd); + spinand_cleanup(spinand); + + return 0; diff --git a/target/linux/airoha/patches-6.6/902-mtd-parser-add-support-for-Airoha-parser.patch b/target/linux/airoha/patches-6.6/902-mtd-parser-add-support-for-Airoha-parser.patch new file mode 100644 index 0000000000..590d0d9faa --- /dev/null +++ b/target/linux/airoha/patches-6.6/902-mtd-parser-add-support-for-Airoha-parser.patch @@ -0,0 +1,170 @@ +From ca46c5834ba3a74595a93d7a491fa9c943be7c30 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 28 Jul 2024 12:15:53 +0200 +Subject: [PATCH 3/3] mtd: parser: add support for Airoha parser + +Add support for Airoha parser based on a post parse ofpart function. + +Airoha partition table follow normal fixed-partition implementation +with a special implementation for the ART partition. This is always the +past partition and is placed from the end of the flash - the partition +size. + +To enable this special implementation for ART partition, the relevant +node require the "airoha,dynamic-art" compatible. With that declared, +offset value is ignored and real offset is updated with the calculated +value. + +Due to usage of specific bad block management driver, the MTD size might +vary hence the ART partition offset needs to be dynamically parsed and +can't be declared statically. + +Signed-off-by: Christian Marangi +--- + drivers/mtd/parsers/Kconfig | 10 ++++++ + drivers/mtd/parsers/Makefile | 1 + + drivers/mtd/parsers/ofpart_airoha.c | 56 +++++++++++++++++++++++++++++ + drivers/mtd/parsers/ofpart_airoha.h | 18 ++++++++++ + drivers/mtd/parsers/ofpart_core.c | 6 ++++ + 5 files changed, 91 insertions(+) + create mode 100644 drivers/mtd/parsers/ofpart_airoha.c + create mode 100644 drivers/mtd/parsers/ofpart_airoha.h + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -93,6 +93,16 @@ config MTD_OF_PARTS + flash memory node, as described in + Documentation/devicetree/bindings/mtd/mtd.yaml. + ++config MTD_OF_PARTS_AIROHA ++ bool "Airoha EN7815 partitioning support" ++ depends on MTD_OF_PARTS && (ARCH_AIROHA || COMPILE_TEST) ++ default ARCH_AIROHA ++ help ++ This provides partitions parser for Airoha EN7815 family devices ++ that can have dynamic "ART" partition at the end of the flash. ++ It takes care of finding the correct offset and update property ++ with it. ++ + config MTD_OF_PARTS_BCM4908 + bool "BCM4908 partitioning support" + depends on MTD_OF_PARTS && (ARCH_BCMBCA || COMPILE_TEST) +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -7,6 +7,7 @@ obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdl + obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o + obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o + ofpart-y += ofpart_core.o ++ofpart-$(CONFIG_MTD_OF_PARTS_AIROHA) += ofpart_airoha.o + ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o + ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o + obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o +--- /dev/null ++++ b/drivers/mtd/parsers/ofpart_airoha.c +@@ -0,0 +1,56 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2024 Christian Marangi ++ */ ++ ++#include ++#include ++ ++#include "ofpart_airoha.h" ++ ++int airoha_partitions_post_parse(struct mtd_info *mtd, ++ struct mtd_partition *parts, ++ int nr_parts) ++{ ++ struct mtd_partition *part; ++ int len, a_cells, s_cells; ++ struct device_node *pp; ++ struct property *prop; ++ const __be32 *reg; ++ __be32 *new_reg; ++ ++ part = &parts[nr_parts - 1]; ++ pp = part->of_node; ++ ++ /* Skip if ART partition have a valid offset instead of a dynamic one */ ++ if (!of_device_is_compatible(pp, "airoha,dynamic-art")) ++ return 0; ++ ++ /* ART partition is set at the end of flash - size */ ++ part->offset = mtd->size - part->size; ++ ++ /* Update the offset with the new calculate value in DT */ ++ prop = kzalloc(sizeof(*prop), GFP_KERNEL); ++ if (!prop) ++ return -ENOMEM; ++ ++ /* Reg already validated by fixed-partition parser */ ++ reg = of_get_property(pp, "reg", &len); ++ ++ /* Fixed partition */ ++ a_cells = of_n_addr_cells(pp); ++ s_cells = of_n_size_cells(pp); ++ ++ prop->name = "reg"; ++ prop->length = (a_cells + s_cells) * sizeof(__be32); ++ prop->value = kmemdup(reg, (a_cells + s_cells) * sizeof(__be32), ++ GFP_KERNEL); ++ new_reg = prop->value; ++ memset(new_reg, 0, a_cells * sizeof(__be32)); ++ new_reg[a_cells - 1] = cpu_to_be32(part->offset); ++ if (a_cells > 1) ++ new_reg[0] = cpu_to_be32(part->offset >> 32); ++ of_update_property(pp, prop); ++ ++ return 0; ++} +--- /dev/null ++++ b/drivers/mtd/parsers/ofpart_airoha.h +@@ -0,0 +1,18 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __OFPART_AIROHA_H ++#define __OFPART_AIROHA_H ++ ++#ifdef CONFIG_MTD_OF_PARTS_AIROHA ++int airoha_partitions_post_parse(struct mtd_info *mtd, ++ struct mtd_partition *parts, ++ int nr_parts); ++#else ++static inline int airoha_partitions_post_parse(struct mtd_info *mtd, ++ struct mtd_partition *parts, ++ int nr_parts) ++{ ++ return -EOPNOTSUPP; ++} ++#endif ++ ++#endif +--- a/drivers/mtd/parsers/ofpart_core.c ++++ b/drivers/mtd/parsers/ofpart_core.c +@@ -16,6 +16,7 @@ + #include + #include + ++#include "ofpart_airoha.h" + #include "ofpart_bcm4908.h" + #include "ofpart_linksys_ns.h" + +@@ -23,6 +24,10 @@ struct fixed_partitions_quirks { + int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts); + }; + ++static struct fixed_partitions_quirks airoha_partitions_quirks = { ++ .post_parse = airoha_partitions_post_parse, ++}; ++ + static struct fixed_partitions_quirks bcm4908_partitions_quirks = { + .post_parse = bcm4908_partitions_post_parse, + }; +@@ -192,6 +197,7 @@ static const struct of_device_id parse_o + /* Generic */ + { .compatible = "fixed-partitions" }, + /* Customized */ ++ { .compatible = "airoha,fixed-partitions", .data = &airoha_partitions_quirks, }, + { .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, }, + { .compatible = "linksys,ns-partitions", .data = &linksys_ns_partitions_quirks, }, + {},