BOARD:=siflower
BOARDNAME:=Siflower SoCs
FEATURES:=squashfs usb usbgadget source-only
-SUBTARGETS:=sf19a2890
+SUBTARGETS:=sf19a2890 sf21
KERNEL_PATCHVER:=6.6
--- /dev/null
+/*
+ * Copyright 2023 SiFlower Corporation.
+ */
+
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/pinctrl/siflower,sf21-iomux.h>
+#include <dt-bindings/clock/siflower,sf21-topcrm.h>
+#include <dt-bindings/reset/siflower,sf21-reset.h>
+
+/ {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ aliases {
+ serial0 = &uart0;
+ serial1 = &uart1;
+ };
+
+
+ cpus: cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ compatible = "riscv";
+ device_type = "cpu";
+ riscv,isa = "rv64imafdc_svpbmt_zba_zbb_zbc_zbs_zicbom_zicbop_zicboz";
+ riscv,isa-base = "rv64i";
+ riscv,isa-extensions = "i", "m", "a", "f", "d", "c",
+ "sstc", "svinval", "svnapot", "svpbmt", "zba", "zbb", "zbc", "zbs", "zfh", "zicbom", "zicbop", "zicboz", "zicntr", "zicsr", "zifencei", "zihintpause", "zihpm";
+ reg = <0>;
+ i-cache-block-size = <64>;
+ i-cache-size = <32768>;
+ i-cache-sets = <128>;
+ d-cache-block-size = <64>;
+ d-cache-size = <32768>;
+ d-cache-sets = <128>;
+ riscv,cbom-block-size = <64>;
+ riscv,cbop-block-size = <64>;
+ riscv,cboz-block-size = <64>;
+ clocks = <&topcrm CLK_CPU>;
+ next-level-cache = <&l2_cache>;
+ mmu-type = "riscv,sv39";
+
+ cpu0_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+ compatible = "riscv,cpu-intc";
+ interrupt-controller;
+ };
+ };
+
+ cpu@1 {
+ compatible = "riscv";
+ device_type = "cpu";
+ riscv,isa = "rv64imafdc_svpbmt_zba_zbb_zbc_zbs_zicbom_zicbop_zicboz";
+ riscv,isa-base = "rv64i";
+ riscv,isa-extensions = "i", "m", "a", "f", "d", "c",
+ "sstc", "svinval", "svnapot", "svpbmt", "zba", "zbb", "zbc", "zbs", "zfh", "zicbom", "zicbop", "zicboz", "zicntr", "zicsr", "zifencei", "zihintpause", "zihpm";
+ reg = <1>;
+ i-cache-block-size = <64>;
+ i-cache-size = <32768>;
+ i-cache-sets = <128>;
+ d-cache-block-size = <64>;
+ d-cache-size = <32768>;
+ d-cache-sets = <128>;
+ riscv,cbom-block-size = <64>;
+ riscv,cbop-block-size = <64>;
+ riscv,cboz-block-size = <64>;
+ clocks = <&topcrm CLK_CPU>;
+ next-level-cache = <&l2_cache>;
+ mmu-type = "riscv,sv39";
+
+ cpu1_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+ compatible = "riscv,cpu-intc";
+ interrupt-controller;
+ };
+ };
+
+ cpu@2 {
+ compatible = "riscv";
+ device_type = "cpu";
+ riscv,isa = "rv64imafdc_svpbmt_zba_zbb_zbc_zbs_zicbom_zicbop_zicboz";
+ riscv,isa-base = "rv64i";
+ riscv,isa-extensions = "i", "m", "a", "f", "d", "c",
+ "sstc", "svinval", "svnapot", "svpbmt", "zba", "zbb", "zbc", "zbs", "zfh", "zicbom", "zicbop", "zicboz", "zicntr", "zicsr", "zifencei", "zihintpause", "zihpm";
+ reg = <2>;
+ i-cache-block-size = <64>;
+ i-cache-size = <32768>;
+ i-cache-sets = <128>;
+ d-cache-block-size = <64>;
+ d-cache-size = <32768>;
+ d-cache-sets = <128>;
+ riscv,cbom-block-size = <64>;
+ riscv,cbop-block-size = <64>;
+ riscv,cboz-block-size = <64>;
+ clocks = <&topcrm CLK_CPU>;
+ next-level-cache = <&l2_cache>;
+ mmu-type = "riscv,sv39";
+
+ cpu2_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+ compatible = "riscv,cpu-intc";
+ interrupt-controller;
+ };
+ };
+
+ cpu@3 {
+ compatible = "riscv";
+ device_type = "cpu";
+ riscv,isa = "rv64imafdc_svpbmt_zba_zbb_zbc_zbs_zicbom_zicbop_zicboz";
+ riscv,isa-base = "rv64i";
+ riscv,isa-extensions = "i", "m", "a", "f", "d", "c",
+ "sstc", "svinval", "svnapot", "svpbmt", "zba", "zbb", "zbc", "zbs", "zfh", "zicbom", "zicbop", "zicboz", "zicntr", "zicsr", "zifencei", "zihintpause", "zihpm";
+ reg = <3>;
+ i-cache-block-size = <64>;
+ i-cache-size = <32768>;
+ i-cache-sets = <128>;
+ d-cache-block-size = <64>;
+ d-cache-size = <32768>;
+ d-cache-sets = <128>;
+ riscv,cbom-block-size = <64>;
+ riscv,cbop-block-size = <64>;
+ riscv,cboz-block-size = <64>;
+ clocks = <&topcrm CLK_CPU>;
+ next-level-cache = <&l2_cache>;
+ mmu-type = "riscv,sv39";
+
+ cpu3_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+ compatible = "riscv,cpu-intc";
+ interrupt-controller;
+ };
+ };
+
+ l2_cache: l2-cache {
+ compatible = "cache";
+ cache-level = <2>;
+ cache-block-size = <64>;
+ cache-size = <262144>;
+ cache-sets = <256>;
+ cache-unified;
+ };
+ };
+
+ xin25m: xin25m {
+ compatible = "fixed-clock";
+ clock-output-names = "xin25m";
+ clock-frequency = <25000000>;
+ #clock-cells = <0>;
+ };
+
+ pmu {
+ compatible = "riscv,pmu";
+ riscv,event-to-mhpmevent =
+ <0x00003 0x0 0x9b>, // L1 Dcache Access
+ <0x00004 0x0 0x9c>, // L1 Dcache Miss
+ <0x00005 0x0 0x36>, // Branch Instruction
+ <0x00006 0x0 0x38>, // Branch Mispred
+ <0x00008 0x0 0x27>, // Stalled Cycles Frontend
+ <0x00009 0x0 0x28>, // Stalled Cycles Backend
+ <0x10000 0x0 0x0c>, // L1-dcache load access
+ <0x10001 0x0 0x0d>, // L1-dcache load miss
+ <0x10002 0x0 0x0e>, // L1-dcache store access
+ <0x10003 0x0 0x0f>, // L1-dcache store miss
+ <0x10004 0x0 0xa2>, // Dcache Hit Caused by Prefetch
+ <0x10005 0x0 0xa1>, // Dcache Refill Caused by Prefetch
+ <0x10008 0x0 0x01>, // L1-icache Access
+ <0x10009 0x0 0x02>, // L1-icache Miss
+ <0x1000c 0x0 0x9e>, // Icache Prefetch
+ <0x1000d 0x0 0xa0>, // Icache Prefetch Miss
+ <0x10010 0x0 0xa5>, // L2 Access
+ <0x10011 0x0 0xa6>, // L2 Miss
+ <0x10019 0x0 0xa4>, // Load Dtlb Miss
+ <0x1001b 0x0 0xa3>, // Store Dtlb Miss
+ <0x10021 0x0 0x03>; // iTLB Miss
+ riscv,event-to-mhpmcounters =
+ <0x00001 0x00001 0x00000001>, // cycles
+ <0x00002 0x00002 0x00000004>, // instructions
+ <0x00003 0x1ffff 0xfffffff8>; // others
+ riscv,raw-event-to-mhpmcounters =
+ <0x0 0x0 0xffffffff 0xffffff00 0xfffffff8>;
+ };
+
+ timer: timer {
+ compatible = "riscv,timer";
+ };
+
+ reset: reset-controller {
+ compatible = "siflower,sf21-reset";
+ #reset-cells = <1>;
+ siflower,crm = <&topcrm>;
+ };
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ compatible = "simple-bus";
+ dma-noncoherent;
+ ranges;
+ interrupt-parent = <&plic>;
+
+ pcie0: pcie@00200000 {
+ compatible = "siflower,sf21-pcie";
+ status = "disabled";
+ reg = <0x0 0x00000000 0x0 0x200000>,
+ <0x0 0x00200000 0x0 0x100000>,
+ <0x0 0x00300000 0x0 0x080000>,
+ <0x0 0x00380000 0x0 0x080000>,
+ <0x0 0xa0000000 0x0 0x080000>;
+ reg-names = "dbi", "elbi", "atu", "dma", "config";
+ clocks = <&topcrm CLK_SERDES_CSR>,
+ <&topcrm CLK_PCIE_REFP>,
+ <&topcrm CLK_PCIEPLL_FOUT3>;
+ clock-names = "csr", "ref", "phy";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ bus-range = <0x0 0xff>;
+ ranges = <0x81000000 0x0 0xa0080000 0x0 0xa0080000 0x0 0x00080000 /* downstream I/O 256KB */
+ 0x82000000 0x0 0xa0100000 0x0 0xa0100000 0x0 0x2ff00000>; /* non-prefetchable memory */
+ siflower,ctlr-idx = <0>;
+ num-viewport = <8>;
+ interrupts = <124 IRQ_TYPE_LEVEL_HIGH>,
+ <160 IRQ_TYPE_LEVEL_HIGH>, <161 IRQ_TYPE_LEVEL_HIGH>, <162 IRQ_TYPE_LEVEL_HIGH>, <163 IRQ_TYPE_LEVEL_HIGH>, <164 IRQ_TYPE_LEVEL_HIGH>, <165 IRQ_TYPE_LEVEL_HIGH>, <166 IRQ_TYPE_LEVEL_HIGH>, <167 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "intr", "msi0", "msi1", "msi2", "msi3", "msi4", "msi5", "msi6", "msi7";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &plic 151 IRQ_TYPE_EDGE_RISING>,
+ <0 0 0 2 &plic 150 IRQ_TYPE_EDGE_RISING>,
+ <0 0 0 3 &plic 149 IRQ_TYPE_EDGE_RISING>,
+ <0 0 0 4 &plic 148 IRQ_TYPE_EDGE_RISING>;
+ siflower,pcie-sysm = <&pcie_phy>;
+ phys = <&pcie_phy0>;
+ linux,pci-domain = <0>;
+ };
+
+ pcie1: pcie@04200000 {
+ compatible = "siflower,sf21-pcie";
+ status = "disabled";
+ reg = <0x0 0x04000000 0x0 0x200000>,
+ <0x0 0x04200000 0x0 0x100000>,
+ <0x0 0x04300000 0x0 0x080000>,
+ <0x0 0x04380000 0x0 0x080000>,
+ <0x0 0xd0000000 0x0 0x080000>;
+ reg-names = "dbi", "elbi", "atu", "dma", "config";
+ clocks = <&topcrm CLK_SERDES_CSR>,
+ <&topcrm CLK_PCIE_REFP>,
+ <&topcrm CLK_PCIEPLL_FOUT3>;
+ clock-names = "csr", "ref", "phy";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ bus-range = <0x0 0xff>;
+ ranges = <0x81000000 0x0 0xd0080000 0x0 0xd0080000 0x0 0x00080000 /* downstream I/O 256KB */
+ 0x82000000 0x0 0xd0100000 0x0 0xd0100000 0x0 0x2ff00000>; /* non-prefetchable memory */
+ siflower,ctlr-idx = <1>;
+ num-viewport = <8>;
+ interrupts = <125 IRQ_TYPE_LEVEL_HIGH>,
+ <168 IRQ_TYPE_LEVEL_HIGH>, <169 IRQ_TYPE_LEVEL_HIGH>, <170 IRQ_TYPE_LEVEL_HIGH>, <171 IRQ_TYPE_LEVEL_HIGH>, <172 IRQ_TYPE_LEVEL_HIGH>, <173 IRQ_TYPE_LEVEL_HIGH>, <174 IRQ_TYPE_LEVEL_HIGH>, <175 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "intr", "msi0", "msi1", "msi2", "msi3", "msi4", "msi5", "msi6", "msi7";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &plic 159 IRQ_TYPE_EDGE_RISING>,
+ <0 0 0 2 &plic 158 IRQ_TYPE_EDGE_RISING>,
+ <0 0 0 3 &plic 157 IRQ_TYPE_EDGE_RISING>,
+ <0 0 0 4 &plic 156 IRQ_TYPE_EDGE_RISING>;
+ siflower,pcie-sysm = <&pcie_phy>;
+ phys = <&pcie_phy1>;
+ linux,pci-domain = <1>;
+ };
+
+ topcrm: clock-controller@0ce00400 {
+ compatible = "siflower,sf21-topcrm", "syscon";
+ reg = <0x0 0x0ce00400 0x0 0x400>;
+ clocks = <&xin25m>;
+ clock-names = "xin25m";
+ #clock-cells = <1>;
+ };
+
+ i2crst: reset-controller@0ce08400 {
+ compatible = "siflower,sf19a2890-periph-reset";
+ reg = <0x0 0x0ce08400 0x0 0x4>;
+ #reset-cells = <1>;
+ siflower,num-resets = <2>;
+ };
+
+ i2cclk: clock-controller@0ce08404 {
+ compatible = "siflower,sf19a2890-periph-clk";
+ reg = <0x0 0x0ce08404 0x0 0x4>;
+ clocks = <&topcrm CLK_APB>, <&topcrm CLK_APB>;
+ clock-output-names = "i2c0", "i2c1";
+ #clock-cells = <1>;
+ };
+
+ spirst: reset-controller@0ce08800 {
+ compatible = "siflower,sf19a2890-periph-reset";
+ reg = <0x0 0x0ce08800 0x0 0x4>;
+ #reset-cells = <1>;
+ siflower,reset-masks = <0x3 0xc>;
+ };
+
+ spiclk: clock-controller@0ce08804 {
+ compatible = "siflower,sf19a2890-periph-clk";
+ reg = <0x0 0x0ce08804 0x0 0x4>;
+ clocks = <&topcrm CLK_APB>, <&topcrm CLK_APB>, <&topcrm CLK_APB>,
+ <&topcrm CLK_APB>;
+ clock-output-names = "spi0_apb", "spi0_ssp", "spi1_apb",
+ "spi1_ssp";
+ #clock-cells = <1>;
+ };
+
+ uartrst: reset-controller@0ce08c00 {
+ compatible = "siflower,sf19a2890-periph-reset";
+ reg = <0x0 0x0ce08c00 0x0 0x4>;
+ #reset-cells = <1>;
+ siflower,reset-masks = <0x11 0x22>;
+ };
+
+ uartclk: clock-controller@0ce08c04 {
+ compatible = "siflower,sf19a2890-periph-clk";
+ reg = <0x0 0x0ce08c04 0x0 0x4>;
+ clocks = <&topcrm CLK_APB>, <&topcrm CLK_APB>,
+ <&topcrm CLK_UART>, <&topcrm CLK_UART>;
+ clock-output-names = "uart0_apb", "uart1_apb",
+ "uart0", "uart1";
+ siflower,valid-gates = <0x33>;
+ siflower,critical-gates = <0x22>;
+ #clock-cells = <1>;
+ };
+
+ timerst: reset-controller@0ce09800 {
+ compatible = "siflower,sf19a2890-periph-reset";
+ reg = <0x0 0x0ce09800 0x0 0x4>;
+ #reset-cells = <1>;
+ siflower,reset-masks = <0x1>;
+ };
+
+ timerclk: clock-controller@0ce09804 {
+ compatible = "siflower,sf19a2890-periph-clk";
+ reg = <0x0 0x0ce09804 0x0 0x4>;
+ clocks = <&topcrm CLK_APB>;
+ clock-output-names = "timer";
+ #clock-cells = <1>;
+ };
+
+ wdtrst: reset-controller@0ce09c00 {
+ compatible = "siflower,sf19a2890-periph-reset";
+ reg = <0x0 0x0ce09c00 0x0 0x4>;
+ #reset-cells = <1>;
+ siflower,reset-masks = <0x1>;
+ };
+
+ wdtclk: clock-controller@0ce09c04 {
+ compatible = "siflower,sf19a2890-periph-clk";
+ reg = <0x0 0x0ce09c04 0x0 0x4>;
+ clocks = <&topcrm CLK_APB>;
+ clock-output-names = "wdt";
+ #clock-cells = <1>;
+ };
+
+ gpiorst: reset-controller@0ce0a000 {
+ compatible = "siflower,sf19a2890-periph-reset";
+ reg = <0x0 0x0ce0a000 0x0 0x4>;
+ #reset-cells = <1>;
+ siflower,reset-masks = <0x1>;
+ };
+
+ gpioclk: clock-controller@0ce0a004 {
+ compatible = "siflower,sf19a2890-periph-clk";
+ reg = <0x0 0x0ce0a004 0x0 0x4>;
+ clocks = <&topcrm CLK_APB>;
+ clock-output-names = "gpio";
+ #clock-cells = <1>;
+ };
+
+ uart0: uart@c300000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0 0xc300000 0x0 0x1000>;
+ clocks = <&uartclk 2>, <&uartclk 0>;
+ clock-names = "uartclk", "apb_pclk";
+ resets = <&uartrst 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pins>;
+ interrupts = <43 IRQ_TYPE_LEVEL_HIGH>;
+ current-speed = <115200>;
+ status = "disabled";
+ };
+
+ uart1: uart@c301000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x0 0xc301000 0x0 0x1000>;
+ clocks = <&uartclk 3>, <&uartclk 1>;
+ clock-names = "uartclk", "apb_pclk";
+ resets = <&uartrst 1>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart1_pins>;
+ interrupts = <44 IRQ_TYPE_LEVEL_HIGH>;
+ current-speed = <115200>;
+ status = "disabled";
+ };
+
+ spi0: spi@c200000 {
+ compatible = "siflower,sf21-qspi";
+ reg = <0x0 0xc200000 0x0 0x1000>;
+ clocks = <&spiclk 0>, <&spiclk 1>;
+ clock-names = "apb_pclk", "sspclk";
+ resets = <&spirst 0>;
+ interrupts = <39 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi0_pins>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ spi1: spi@c201000 {
+ compatible = "siflower,sf21-qspi";
+ reg = <0x0 0xc201000 0x0 0x1000>;
+ clocks = <&spiclk 2>, <&spiclk 3>;
+ clock-names = "apb_pclk", "sspclk";
+ resets = <&spirst 1>;
+ interrupts = <40 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi1_pins>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ watchdog: watchdog@0c700000 {
+ compatible = "snps,dw-wdt";
+ reg = <0x0 0x0c700000 0x0 0x1000>;
+ interrupts = <51 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&wdtclk 0>;
+ resets = <&wdtrst 0>;
+ };
+
+ usb_phy: phy@0ce02400 {
+ compatible = "siflower,sf21-usb-phy";
+ reg = <0x0 0x0ce02400 0x0 0x14>;
+ clocks = <&topcrm CLK_USBPHY>;
+ clock-names = "usb_phy_clk";
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+
+ pcie_phy: phy@0d810000 {
+ compatible = "siflower,sf21-pcie-phy", "syscon";
+ reg = <0x0 0x0d810000 0x0 0x100>;
+ clocks = <&topcrm CLK_PCIE_REFP>, <&topcrm CLK_SERDES_CSR>;
+ clock-names = "ref", "csr";
+ siflower,topcrm = <&topcrm>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+
+ pcie_phy0: phy@0 {
+ reg = <0>;
+ siflower,num-lanes = <1>;
+ #phy-cells = <0>;
+ };
+
+ pcie_phy1: phy@1 {
+ reg = <1>;
+ siflower,num-lanes = <1>;
+ #phy-cells = <0>;
+ };
+ };
+
+ usb: usb@10000000 {
+ compatible = "siflower,sf19a2890-usb";
+ reg = <0x0 0x10000000 0x0 0x80000>;
+ interrupts = <33 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&topcrm CLK_USB>;
+ clock-names = "otg";
+ resets = <&reset SF21_RESET_USB>;
+ reset-names = "dwc2";
+ dr_mode = "host";
+ phys = <&usb_phy>;
+ phy-names = "usb2-phy";
+ g-rx-fifo-size = <512>;
+ g-np-tx-fifo-size = <128>;
+ g-tx-fifo-size = <128 128 128 128 128 128 128 128
+ 16 16 16 16 16 16 16>;
+ status = "disabled";
+ };
+
+ iram: sram@1c000000 {
+ compatible = "mmio-sram";
+ reg = <0x0 0x1c000000 0x0 0x10000>;
+ clocks = <&topcrm CLK_IRAM>;
+ ranges;
+ };
+
+ xgmac0: ethernet@8000000 {
+ compatible = "siflower,sf21-xgmac";
+ reg = <0x0 0x8000000 0x0 0x4000>;
+ dmas = <&edma>;
+ ethsys = <ðsys>;
+ clocks = <&topcrm CLK_SERDES_CSR>;
+ clock-names = "csr";
+ pcs-handle = <&qsgmii_pcs 0>;
+ phy-mode = "qsgmii";
+ pinctrl-names = "default";
+ pinctrl-0 = <&mac0_mdio_pins>;
+ interrupts = <176 IRQ_TYPE_LEVEL_HIGH>, <20 IRQ_TYPE_LEVEL_HIGH>, <26 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "sbd", "lpi", "pmt";
+ status = "disabled";
+
+ mdio0: mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+
+ xgmac1: ethernet@8004000 {
+ compatible = "siflower,sf21-xgmac";
+ reg = <0x0 0x8004000 0x0 0x4000>;
+ dmas = <&edma>;
+ ethsys = <ðsys>;
+ clocks = <&topcrm CLK_SERDES_CSR>;
+ clock-names = "csr";
+ pcs-handle = <&qsgmii_pcs 1>;
+ phy-mode = "qsgmii";
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH>, <21 IRQ_TYPE_LEVEL_HIGH>, <27 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "sbd", "lpi", "pmt";
+ status = "disabled";
+ };
+
+ xgmac2: ethernet@8008000 {
+ compatible = "siflower,sf21-xgmac";
+ reg = <0x0 0x8008000 0x0 0x4000>;
+ dmas = <&edma>;
+ ethsys = <ðsys>;
+ clocks = <&topcrm CLK_SERDES_CSR>;
+ clock-names = "csr";
+ pcs-handle = <&qsgmii_pcs 2>;
+ phy-mode = "qsgmii";
+ interrupts = <2 IRQ_TYPE_LEVEL_HIGH>, <22 IRQ_TYPE_LEVEL_HIGH>, <28 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "sbd", "lpi", "pmt";
+ status = "disabled";
+ };
+
+ xgmac3: ethernet@800c000 {
+ compatible = "siflower,sf21-xgmac";
+ reg = <0x0 0x800c000 0x0 0x4000>;
+ dmas = <&edma>;
+ ethsys = <ðsys>;
+ clocks = <&topcrm CLK_SERDES_CSR>;
+ clock-names = "csr";
+ pcs-handle = <&qsgmii_pcs 3>;
+ phy-mode = "qsgmii";
+ interrupts = <3 IRQ_TYPE_LEVEL_HIGH>, <23 IRQ_TYPE_LEVEL_HIGH>, <29 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "sbd", "lpi", "pmt";
+ status = "disabled";
+ };
+
+ xgmac4: ethernet@8010000 {
+ compatible = "siflower,sf21-xgmac";
+ reg = <0x0 0x8010000 0x0 0x4000>;
+ dmas = <&edma>;
+ ethsys = <ðsys>;
+ clocks = <&topcrm CLK_SERDES_CSR>;
+ clock-names = "csr";
+ pcs-handle = <&sgmii_pcs 0>;
+ phy-mode = "2500base-x";
+ pinctrl-names = "default";
+ pinctrl-0 = <&mac4_mdio_pins>;
+ interrupts = <4 IRQ_TYPE_LEVEL_HIGH>, <24 IRQ_TYPE_LEVEL_HIGH>, <30 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "sbd", "lpi", "pmt";
+ status = "disabled";
+
+ mdio1: mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+
+ edma: dma-controller@8018000 {
+ compatible = "siflower,sf21-xgmac-dma";
+ reg = <0x0 0x8018000 0x0 0x4000>;
+ #dma-cells = <0>;
+ ethsys = <ðsys>;
+ iram = <&iram>;
+ clocks = <&topcrm CLK_AXI>, <&topcrm CLK_NPU>, <&topcrm CLK_SERDES_CSR>;
+ clock-names = "axi", "npu", "csr";
+ interrupts = <6 IRQ_TYPE_LEVEL_HIGH>, <9 IRQ_TYPE_LEVEL_HIGH>, <10 IRQ_TYPE_LEVEL_HIGH>, <11 IRQ_TYPE_LEVEL_HIGH>, <12 IRQ_TYPE_LEVEL_HIGH>, <13 IRQ_TYPE_LEVEL_HIGH>, <14 IRQ_TYPE_LEVEL_HIGH>, <15 IRQ_TYPE_LEVEL_HIGH>, <16 IRQ_TYPE_LEVEL_HIGH>, <17 IRQ_TYPE_LEVEL_HIGH>, <18 IRQ_TYPE_LEVEL_HIGH>, <19 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "sbd", "tx0", "tx1", "tx2", "tx3", "tx4",
+ "rx0", "rx1", "rx2", "rx3", "rx4", "rxovf";
+ status = "disabled";
+ };
+
+ qsgmii_pcs: xpcs@8800000 {
+ compatible = "siflower,sf21-xpcs";
+ reg = <0x0 0x8800000 0x0 0x800000>;
+ ethsys = <ðsys>;
+ clocks = <&topcrm CLK_ETH_REF_P>, <&topcrm CLK_ETHTSU>, <&topcrm CLK_SERDES_CSR>;
+ clock-names = "ref", "eee", "csr";
+ interrupts = <7 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ sgmii_pcs: xpcs@9000000 {
+ compatible = "siflower,sf21-xpcs";
+ reg = <0x0 0x9000000 0x0 0x800000>;
+ ethsys = <ðsys>;
+ clocks = <&topcrm CLK_ETH_REF_P>, <&topcrm CLK_ETHTSU>, <&topcrm CLK_SERDES_CSR>;
+ clock-names = "ref", "eee", "csr";
+ interrupts = <8 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ gpio: gpio@c800000 {
+ compatible = "siflower,sf19a2890-gpio";
+ reg = <0x0 0xc800000 0x0 0x100000>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupts = <54 IRQ_TYPE_LEVEL_HIGH>, <55 IRQ_TYPE_LEVEL_HIGH>, <56 IRQ_TYPE_LEVEL_HIGH>, <57 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ clocks = <&gpioclk 0>;
+ resets = <&gpiorst 0>;
+
+ ngpios = <41>;
+ gpio-ranges = <&iomux 0 0 41>;
+ };
+
+ i2c0: i2c@c100000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x0 0xc100000 0x0 0x1000>;
+ clocks = <&i2cclk 0>;
+ clock-names = "ref";
+ clock-frequency = <400000>;
+ interrupts = <35 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names="default";
+ pinctrl-0 = <&i2c0_pins>;
+ resets = <&i2crst 0>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@c101000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x0 0xc101000 0x0 0x1000>;
+ clocks = <&i2cclk 1>;
+ clock-frequency = <400000>;
+ interrupts = <36 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+ status = "disabled";
+ };
+
+ ethsys: syscon@ce01800 {
+ compatible = "siflower,ethsys", "syscon";
+ reg = <0x0 0xce01800 0x0 0x800>;
+ };
+
+ dpns: dpns@11000000 {
+ compatible = "siflower,sf21-dpns";
+ reg = <0x0 0x11000000 0x0 0x1000000>;
+ #address-cells = <1>;
+ dma-ranges = <0 0x0 0 0x20000000 0 0x80000000>;
+ interrupts = <65 IRQ_TYPE_EDGE_RISING>, <66 IRQ_TYPE_EDGE_RISING>, <67 IRQ_TYPE_EDGE_RISING>, <68 IRQ_TYPE_EDGE_RISING>, <69 IRQ_TYPE_EDGE_RISING>, <70 IRQ_TYPE_EDGE_RISING>, <71 IRQ_TYPE_EDGE_RISING>, <72 IRQ_TYPE_EDGE_RISING>,
+ <73 IRQ_TYPE_EDGE_RISING>, <74 IRQ_TYPE_EDGE_RISING>, <75 IRQ_TYPE_EDGE_RISING>, <76 IRQ_TYPE_EDGE_RISING>, <77 IRQ_TYPE_EDGE_RISING>, <78 IRQ_TYPE_EDGE_RISING>, <79 IRQ_TYPE_EDGE_RISING>, <80 IRQ_TYPE_EDGE_RISING>,
+ <81 IRQ_TYPE_EDGE_RISING>, <82 IRQ_TYPE_EDGE_RISING>, <83 IRQ_TYPE_EDGE_RISING>, <84 IRQ_TYPE_EDGE_RISING>, <85 IRQ_TYPE_LEVEL_HIGH>, <86 IRQ_TYPE_LEVEL_HIGH>, <87 IRQ_TYPE_EDGE_RISING>, <88 IRQ_TYPE_EDGE_RISING>,
+ <89 IRQ_TYPE_EDGE_RISING>, <90 IRQ_TYPE_EDGE_RISING>, <91 IRQ_TYPE_EDGE_RISING>, <92 IRQ_TYPE_EDGE_RISING>, <93 IRQ_TYPE_EDGE_RISING>, <94 IRQ_TYPE_EDGE_RISING>, <95 IRQ_TYPE_EDGE_RISING>, <96 IRQ_TYPE_EDGE_RISING>,
+ <97 IRQ_TYPE_EDGE_RISING>, <98 IRQ_TYPE_EDGE_RISING>, <99 IRQ_TYPE_EDGE_RISING>, <100 IRQ_TYPE_EDGE_RISING>, <101 IRQ_TYPE_EDGE_RISING>, <102 IRQ_TYPE_EDGE_RISING>, <103 IRQ_TYPE_EDGE_RISING>, <104 IRQ_TYPE_EDGE_RISING>,
+ <105 IRQ_TYPE_EDGE_RISING>, <106 IRQ_TYPE_EDGE_RISING>, <107 IRQ_TYPE_EDGE_RISING>, <108 IRQ_TYPE_EDGE_RISING>, <109 IRQ_TYPE_EDGE_RISING>, <110 IRQ_TYPE_EDGE_RISING>, <111 IRQ_TYPE_EDGE_RISING>, <112 IRQ_TYPE_EDGE_RISING>,
+ <113 IRQ_TYPE_EDGE_RISING>, <114 IRQ_TYPE_EDGE_RISING>, <115 IRQ_TYPE_EDGE_RISING>, <116 IRQ_TYPE_EDGE_RISING>, <117 IRQ_TYPE_EDGE_RISING>, <118 IRQ_TYPE_EDGE_RISING>, <119 IRQ_TYPE_EDGE_RISING>, <120 IRQ_TYPE_EDGE_RISING>,
+ <121 IRQ_TYPE_EDGE_RISING>, <122 IRQ_TYPE_EDGE_RISING>;
+ ethsys = <ðsys>;
+ clocks = <&topcrm CLK_NPU>;
+ resets = <&reset SF21_RESET_NPU>, <&reset SF21_RESET_NPU2DDR_ASYNCBRIDGE>;
+ reset-names = "npu", "npu2ddr";
+ siflower,edma = <&edma>;
+ status = "disabled";
+ };
+
+ syscon@ce00000 {
+ compatible = "siflower,brom-sysm";
+ reg = <0x0 0xce00000 0x0 0x400>;
+ #reset-cells = <1>;
+ };
+
+ syscon@ce00c00 {
+ compatible = "siflower,cpu-sysm";
+ reg = <0x0 0xce00c00 0x0 0x400>;
+ };
+
+ iomux: iomux@ce3c000 {
+ compatible = "pinconf-single";
+ reg = <0x0 0xce3c000 0x0 0xa4>;
+ #pinctrl-cells = <1>;
+
+ pinctrl-single,register-width = <32>;
+ pinctrl-single,function-mask = <FUNC_MODE_MASK>;
+ pinctrl-single,function-off = <0>;
+ pinctrl-single,gpio-range = <&range 0 41 GPIO_MODE>;
+
+ range: gpio-range {
+ #pinctrl-single,gpio-range-cells = <3>;
+ };
+
+ clk_pins: clk_pins {
+ pinctrl-single,pins = <
+ EXT_CLK_IN FUNC_MODE0
+ CLK_OUT FUNC_MODE0
+ >;
+ };
+
+ spi0_pins: spi0_pins {
+ pinctrl-single,pins = <
+ SPI0_TXD FUNC_MODE0
+ SPI0_RXD FUNC_MODE0
+ SPI0_CLK FUNC_MODE0
+ SPI0_CSN FUNC_MODE0
+ SPI0_HOLD FUNC_MODE0
+ SPI0_WP FUNC_MODE0
+ >;
+ };
+
+ jtag_pins: jtag_pins {
+ pinctrl-single,pins = <
+ JTAG_TDO FUNC_MODE0
+ JTAG_TDI FUNC_MODE0
+ JTAG_TMS FUNC_MODE0
+ JTAG_TCK FUNC_MODE0
+ JTAG_RST FUNC_MODE0
+ >;
+ };
+
+ uart0_pins: uart0_pins {
+ pinctrl-single,pins = <
+ JTAG_TDO FUNC_MODE1
+ JTAG_TDI FUNC_MODE1
+ JTAG_TMS FUNC_MODE1
+ JTAG_TCK FUNC_MODE1
+ >;
+ };
+
+ uart1_pins: uart1_pins {
+ pinctrl-single,pins = <
+ UART1_TX FUNC_MODE0
+ UART1_RX FUNC_MODE0
+ >;
+ };
+
+ uart1_full_pins: uart1_full_pins {
+ pinctrl-single,pins = <
+ UART1_TX FUNC_MODE0
+ UART1_RX FUNC_MODE0
+ I2C0_DAT FUNC_MODE1
+ I2C0_CLK FUNC_MODE1
+ >;
+ };
+
+ i2c0_pins: i2c0_pins {
+ pinctrl-single,pins = <
+ I2C0_DAT FUNC_MODE0
+ I2C0_CLK FUNC_MODE0
+ >;
+ };
+
+ perst_pins: perst_pins {
+ pinctrl-single,pins = <
+ I2C0_DAT FUNC_MODE2
+ I2C0_CLK FUNC_MODE2
+ >;
+ };
+
+ i2c1_pins: i2c1_pins {
+ pinctrl-single,pins = <
+ I2C1_DAT FUNC_MODE0
+ I2C1_CLK FUNC_MODE0
+ >;
+ };
+
+ rgmii_pins: rgmii_pins {
+ pinctrl-single,pins = <
+ RGMII_GTX_CLK FUNC_MODE0
+ RGMII_TXD0 FUNC_MODE0
+ RGMII_TXD1 FUNC_MODE0
+ RGMII_TXD2 FUNC_MODE0
+ RGMII_TXD3 FUNC_MODE0
+ RGMII_TXCTL FUNC_MODE0
+ RGMII_RXCLK FUNC_MODE0
+ RGMII_RXD0 FUNC_MODE0
+ RGMII_RXD1 FUNC_MODE0
+ RGMII_RXD2 FUNC_MODE0
+ RGMII_RXD3 FUNC_MODE0
+ RGMII_RXCTL FUNC_MODE0
+ >;
+ };
+
+ spi1_pins: spi1_pins {
+ pinctrl-single,pins = <
+ RGMII_GTX_CLK FUNC_MODE1
+ RGMII_TXCLK FUNC_MODE1
+ RGMII_TXD0 FUNC_MODE1
+ RGMII_TXD1 FUNC_MODE1
+ RGMII_TXD2 FUNC_MODE1
+ RGMII_TXD3 FUNC_MODE1
+ >;
+ };
+
+ mac0_mdio_pins: mac0_mdio_pins {
+ pinctrl-single,pins = <
+ QSGMII_MDIO FUNC_MODE0
+ QSGMII_MDC FUNC_MODE0
+ >;
+ };
+
+ mac4_mdio_pins: mac4_mdio_pins {
+ pinctrl-single,pins = <
+ SXGMII_MDIO FUNC_MODE0
+ SXGMII_MDC FUNC_MODE0
+ >;
+ };
+ };
+
+ plic: interrupt-controller@108000000 {
+ compatible = "thead,c900-plic";
+ reg = <0x1 0x8000000 0x0 0x400000>, <0x0 0x0ce00c34 0x0 0x20>;
+ interrupt-controller;
+ interrupts-extended = <&cpu0_intc 11>, <&cpu0_intc 9>,
+ <&cpu1_intc 11>, <&cpu1_intc 9>,
+ <&cpu2_intc 11>, <&cpu2_intc 9>,
+ <&cpu3_intc 11>, <&cpu3_intc 9>;
+ #interrupt-cells = <2>;
+ riscv,ndev = <176>;
+ };
+
+ interrupt-controller@10c000000 {
+ compatible = "thead,c900-aclint-mswi";
+ reg = <0x1 0xc000000 0x0 0x4000>;
+ interrupts-extended = <&cpu0_intc 3>, <&cpu1_intc 3>,
+ <&cpu2_intc 3>, <&cpu3_intc 3>;
+ };
+
+ timer@10c004000 {
+ compatible = "thead,c900-aclint-mtimer";
+ reg = <0x1 0xc00bff8 0x0 0x8>, <0x1 0xc004000 0x0 0x7ff8>;
+ interrupts-extended = <&cpu0_intc 7>, <&cpu1_intc 7>,
+ <&cpu2_intc 7>, <&cpu3_intc 7>;
+ };
+
+ interrupt-controller@10c00c000 {
+ compatible = "thead,c900-aclint-sswi";
+ reg = <0x1 0xc00c000 0x0 0x1000>;
+ interrupts-extended = <&cpu0_intc 1>, <&cpu1_intc 1>,
+ <&cpu2_intc 1>, <&cpu3_intc 1>;
+ };
+ };
+};
--- /dev/null
+#include "sf21.dtsi"
+/ {
+ compatible = "siflower,sf21a6826";
+};
+
+&cpus {
+ timebase-frequency = <1125000000>;
+};
+
+&topcrm {
+ assigned-clocks = <&topcrm CLK_CMNPLL_VCO>, <&topcrm CLK_PIC>, <&topcrm CLK_AXI>,
+ <&topcrm CLK_AHB>, <&topcrm CLK_APB>, <&topcrm CLK_UART>,
+ <&topcrm CLK_IRAM>, <&topcrm CLK_NPU>, <&topcrm CLK_ETHTSU>,
+ <&topcrm CLK_GMAC_BYP_REF>, <&topcrm CLK_USB>, <&topcrm CLK_USBPHY>,
+ <&topcrm CLK_SERDES_CSR>, <&topcrm CLK_CRYPT_CSR>, <&topcrm CLK_CRYPT_APP>,
+ <&topcrm CLK_IROM>;
+ assigned-clock-rates = <2250000000>, <562500000>, <375000000>,
+ <281250000>, <187500000>, <93750000>,
+ <562500000>, <562500000>, <93750000>,
+ <250000000>, <250000000>, <50000000>,
+ <93750000>, <75000000>, <375000000>,
+ <375000000>;
+};
--- /dev/null
+#include "sf21.dtsi"
+/ {
+ compatible = "siflower,sf21h8898";
+ soc {
+ xgmac5: ethernet@8014000 {
+ compatible = "siflower,sf21-xgmac";
+ reg = <0x0 0x8014000 0x0 0x4000>;
+ dmas = <&edma>;
+ ethsys = <ðsys>;
+ clocks = <&topcrm CLK_SERDES_CSR>, <&topcrm CLK_GMAC_BYP_REF>;
+ clock-names = "csr", "rgmii";
+ phy-mode = "rgmii";
+ pinctrl-names = "default";
+ pinctrl-0 = <&rgmii_pins>;
+ interrupts = <5 IRQ_TYPE_LEVEL_HIGH>, <25 IRQ_TYPE_LEVEL_HIGH>, <31 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "sbd", "lpi", "pmt";
+ status = "disabled";
+ };
+ };
+};
+
+&cpus {
+ timebase-frequency = <1250000000>;
+};
+
+&topcrm {
+ assigned-clocks = <&topcrm CLK_CMNPLL_VCO>, <&topcrm CLK_PIC>, <&topcrm CLK_AXI>,
+ <&topcrm CLK_AHB>, <&topcrm CLK_APB>, <&topcrm CLK_UART>,
+ <&topcrm CLK_IRAM>, <&topcrm CLK_NPU>, <&topcrm CLK_ETHTSU>,
+ <&topcrm CLK_GMAC_BYP_REF>, <&topcrm CLK_USB>, <&topcrm CLK_USBPHY>,
+ <&topcrm CLK_SERDES_CSR>, <&topcrm CLK_CRYPT_CSR>, <&topcrm CLK_CRYPT_APP>,
+ <&topcrm CLK_IROM>;
+ assigned-clock-rates = <2500000000>, <416666666>, <416666666>,
+ <250000000>, <178571428>, <89285714>,
+ <416666666>, <416666666>, <89285714>,
+ <250000000>, <250000000>, <50000000>,
+ <89285714>, <73529411>, <312500000>,
+ <312500000>;
+};
menuconfig CLK_SIFLOWER
bool "Siflower SoC driver support"
- depends on MIPS || COMPILE_TEST
+ depends on MIPS || RISCV || COMPILE_TEST
help
SoC drivers for Siflower Linux-capable SoCs.
config CLK_SF19A2890_PERIPH
bool "Clock driver for Siflower SF19A2890 peripheral clock gates"
- depends on MIPS || COMPILE_TEST
+ depends on MIPS || RISCV || COMPILE_TEST
help
Supports the clock gates for various peripherals in SF19A2890.
If this kernel is meant to run on a Siflower SF19A2890 SoC,
enable this driver.
+config CLK_SF21_TOPCRM
+ bool "Clock driver for Siflower SF21A6826/SF21H8898 Top Clock & Reset Module"
+ depends on RISCV || COMPILE_TEST
+ help
+ Supports the Top Clock & Reset Module IP block found in SF21A6826.
+ If this kernel is meant to run on a Siflower SF21A6826 SoC,
+ enable this driver.
+
endif
obj-$(CONFIG_CLK_SF19A2890) += clk-sf19a2890.o
obj-$(CONFIG_CLK_SF19A2890_PERIPH) += clk-sf19a2890-periph.o
+obj-$(CONFIG_CLK_SF21_TOPCRM) += clk-sf21-topcrm.o
+// SPDX-License-Identifier: GPL-2.0
+
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/slab.h>
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/compiler.h>
+#include <linux/clk-provider.h>
+#include <linux/bitfield.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/rational.h>
+#include <linux/spinlock.h>
+#include <linux/bug.h>
+#include <dt-bindings/clock/siflower,sf21-topcrm.h>
+
+struct sf_clk_common {
+ void __iomem *base;
+ spinlock_t *lock;
+ struct clk_hw hw;
+};
+
+#define SF_CLK_COMMON(_name, _parents, _op, _flags) \
+ { \
+ .hw.init = CLK_HW_INIT_PARENTS(_name, _parents, \
+ _op, _flags), \
+ }
+
+static inline struct sf_clk_common *hw_to_sf_clk_common(struct clk_hw *hw)
+{
+ return container_of(hw, struct sf_clk_common, hw);
+}
+
+static inline u32 sf_readl(struct sf_clk_common *priv, u32 reg)
+{
+ return readl(priv->base + reg);
+}
+
+static inline void sf_writel(struct sf_clk_common *priv, u32 reg, u32 val)
+{
+ return writel(val, priv->base + reg);
+}
+
+static inline void sf_rmw(struct sf_clk_common *priv, u32 reg, u32 clr, u32 set)
+{
+ u32 val;
+
+ val = sf_readl(priv, reg);
+ val &= ~clr;
+ val |= set;
+ sf_writel(priv, reg, val);
+}
+
+#define PLL_CMN_CFG1 0x0
+#define PLL_CMN_BYPASS BIT(27)
+#define PLL_CMN_PD BIT(26)
+#define PLL_CMN_FBDIV GENMASK(25, 14)
+#define PLL_CMN_FBDIV_BITS (25 - 14 + 1)
+#define PLL_CMN_POSTDIV_PD BIT(13)
+#define PLL_CMN_VCO_PD BIT(12)
+#define PLL_CMN_POSTDIV1 GENMASK(11, 9)
+#define PLL_CMN_POSTDIV2 GENMASK(8, 6)
+#define PLL_CMN_REFDIV GENMASK(5, 0)
+#define PLL_CMN_REFDIV_BITS 6
+
+#define PLL_CMN_LOCK 0xc8
+#define PLL_DDR_LOCK 0xcc
+#define PLL_PCIE_LOCK 0xd4
+
+#define CFG_LOAD 0x100
+#define CFG_LOAD_PCIE_PLL BIT(4)
+#define CFG_LOAD_DDR_PLL BIT(2)
+#define CFG_LOAD_CMN_PLL BIT(1)
+#define CFG_LOAD_DIV BIT(0)
+
+static unsigned long sf21_cmnpll_vco_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sf_clk_common *priv = hw_to_sf_clk_common(hw);
+ u32 cfg = sf_readl(priv, PLL_CMN_CFG1);
+ unsigned long refdiv = FIELD_GET(PLL_CMN_REFDIV, cfg);
+ unsigned long fbdiv = FIELD_GET(PLL_CMN_FBDIV, cfg);
+
+ return (parent_rate / refdiv) * fbdiv;
+}
+
+static long sf21_cmnpll_vco_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long fbdiv, refdiv;
+
+ rational_best_approximation(rate, *parent_rate,
+ BIT(PLL_CMN_FBDIV_BITS) - 1,
+ BIT(PLL_CMN_REFDIV_BITS) - 1, &fbdiv,
+ &refdiv);
+ return (*parent_rate / refdiv) * fbdiv;
+}
+
+static int sf21_cmnpll_vco_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sf_clk_common *priv = hw_to_sf_clk_common(hw);
+ unsigned long flags;
+ unsigned long fbdiv, refdiv;
+
+ rational_best_approximation(rate, parent_rate,
+ BIT(PLL_CMN_FBDIV_BITS) - 1,
+ BIT(PLL_CMN_REFDIV_BITS) - 1, &fbdiv,
+ &refdiv);
+
+ spin_lock_irqsave(priv->lock, flags);
+
+ sf_rmw(priv, PLL_CMN_CFG1, 0, PLL_CMN_BYPASS);
+ sf_writel(priv, CFG_LOAD, CFG_LOAD_CMN_PLL);
+ sf_writel(priv, CFG_LOAD, 0);
+
+ sf_rmw(priv, PLL_CMN_CFG1, PLL_CMN_REFDIV | PLL_CMN_FBDIV | PLL_CMN_PD,
+ FIELD_PREP(PLL_CMN_REFDIV, refdiv) |
+ FIELD_PREP(PLL_CMN_FBDIV, fbdiv));
+ sf_writel(priv, CFG_LOAD, CFG_LOAD_CMN_PLL);
+ sf_writel(priv, CFG_LOAD, 0);
+
+ while (!(sf_readl(priv, PLL_CMN_LOCK) & 1))
+ cpu_relax();
+
+ sf_rmw(priv, PLL_CMN_CFG1, PLL_CMN_BYPASS, 0);
+ sf_writel(priv, CFG_LOAD, CFG_LOAD_CMN_PLL);
+ sf_writel(priv, CFG_LOAD, 0);
+
+ spin_unlock_irqrestore(priv->lock, flags);
+ return 0;
+}
+
+static const struct clk_ops sf21_cmnpll_vco_ops = {
+ .recalc_rate = sf21_cmnpll_vco_recalc_rate,
+ .round_rate = sf21_cmnpll_vco_round_rate,
+ .set_rate = sf21_cmnpll_vco_set_rate,
+};
+
+static const char *const clk_pll_parents[] = { "xin25m" };
+
+static struct sf_clk_common cmnpll_vco = SF_CLK_COMMON(
+ "cmnpll_vco", clk_pll_parents, &sf21_cmnpll_vco_ops, 0);
+
+static unsigned long sf21_dualdiv_round_rate(
+ unsigned long rate, unsigned long parent_rate,
+ unsigned int range, unsigned int *diva, unsigned int *divb)
+{
+ unsigned int div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ unsigned int best_diff, da, db, cur_div, cur_diff;
+
+ if (div <= 1) {
+ *diva = 1;
+ *divb = 1;
+ return parent_rate;
+ }
+
+ best_diff = div - 1;
+ *diva = 1;
+ *divb = 1;
+
+ for (da = 1; da <= range; da++) {
+ db = DIV_ROUND_CLOSEST(div, da);
+ if (db > da)
+ db = da;
+
+ cur_div = da * db;
+ if (div > cur_div)
+ cur_diff = div - cur_div;
+ else
+ cur_diff = cur_div - div;
+
+ if (cur_diff < best_diff) {
+ best_diff = cur_diff;
+ *diva = da;
+ *divb = db;
+ }
+ if (cur_diff == 0)
+ break;
+ }
+
+ return parent_rate / *diva / *divb;
+}
+
+static long sf21_cmnpll_postdiv_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned int diva, divb;
+
+ return sf21_dualdiv_round_rate(rate, *parent_rate, 7, &diva,
+ &divb);
+}
+
+static int sf21_cmnpll_postdiv_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sf_clk_common *priv = hw_to_sf_clk_common(hw);
+ unsigned int diva, divb;
+ unsigned long flags;
+
+ sf21_dualdiv_round_rate(rate, parent_rate, 7, &diva, &divb);
+
+ spin_lock_irqsave(priv->lock, flags);
+ sf_rmw(priv, PLL_CMN_CFG1, PLL_CMN_POSTDIV1 | PLL_CMN_POSTDIV2,
+ FIELD_PREP(PLL_CMN_POSTDIV1, diva) |
+ FIELD_PREP(PLL_CMN_POSTDIV2, divb));
+ sf_writel(priv, CFG_LOAD, CFG_LOAD_CMN_PLL);
+ sf_writel(priv, CFG_LOAD, 0);
+ spin_unlock_irqrestore(priv->lock, flags);
+ return 0;
+}
+
+static unsigned long
+sf21_cmnpll_postdiv_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sf_clk_common *priv = hw_to_sf_clk_common(hw);
+ u32 cfg = sf_readl(priv, PLL_CMN_CFG1);
+ unsigned long div1 = FIELD_GET(PLL_CMN_POSTDIV1, cfg);
+ unsigned long div2 = FIELD_GET(PLL_CMN_POSTDIV2, cfg);
+
+ return parent_rate / div1 / div2;
+}
+
+static const struct clk_ops sf21_cmnpll_postdiv_ops = {
+ .recalc_rate = sf21_cmnpll_postdiv_recalc_rate,
+ .round_rate = sf21_cmnpll_postdiv_round_rate,
+ .set_rate = sf21_cmnpll_postdiv_set_rate,
+};
+
+static const char *const clk_cmnpll_postdiv_parents[] = { "cmnpll_vco" };
+
+static struct sf_clk_common cmnpll_postdiv =
+ SF_CLK_COMMON("cmnpll_postdiv", clk_cmnpll_postdiv_parents,
+ &sf21_cmnpll_postdiv_ops, 0);
+
+#define PLL_DDR_CFG1 0x18
+#define PLL_DDR_BYPASS BIT(23)
+#define PLL_DDR_PLLEN BIT(22)
+#define PLL_DDR_4PHASEEN BIT(21)
+#define PLL_DDR_POSTDIVEN BIT(20)
+#define PLL_DDR_DSMEN BIT(19)
+#define PLL_DDR_DACEN BIT(18)
+#define PLL_DDR_DSKEWCALBYP BIT(17)
+#define PLL_DDR_DSKEWCALCNT GENMASK(16, 14)
+#define PLL_DDR_DSKEWCALEN BIT(13)
+#define PLL_DDR_DSKEWCALIN GENMASK(12, 1)
+#define PLL_DDR_DSKEWFASTCAL BIT(0)
+
+#define PLL_DDR_CFG2 0x1c
+#define PLL_DDR_POSTDIV1 GENMASK(29, 27)
+#define PLL_DDR_POSTDIV2 GENMASK(26, 24)
+#define PLL_DDR_FRAC GENMASK(23, 0)
+
+#define PLL_DDR_CFG3 0x20
+#define PLL_DDR_FBDIV GENMASK(17, 6)
+#define PLL_DDR_REFDIV GENMASK(5, 0)
+
+static unsigned long
+sf21_ddrpll_postdiv_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sf_clk_common *priv = hw_to_sf_clk_common(hw);
+ u32 cfg2 = sf_readl(priv, PLL_DDR_CFG2);
+ u32 postdiv1 = FIELD_GET(PLL_DDR_POSTDIV1, cfg2);
+ u32 postdiv2 = FIELD_GET(PLL_DDR_POSTDIV2, cfg2);
+ u32 cfg3 = sf_readl(priv, PLL_DDR_CFG3);
+ u32 fbdiv = FIELD_GET(PLL_DDR_FBDIV, cfg3);
+ u32 refdiv = FIELD_GET(PLL_DDR_REFDIV, cfg3);
+
+ return (parent_rate / refdiv) * fbdiv / postdiv1 / postdiv2;
+}
+
+static const struct clk_ops sf21_ddrpll_postdiv_ops = {
+ .recalc_rate = sf21_ddrpll_postdiv_recalc_rate,
+};
+
+static struct sf_clk_common ddrpll_postdiv = SF_CLK_COMMON(
+ "ddrpll_postdiv", clk_pll_parents, &sf21_ddrpll_postdiv_ops, 0);
+
+#define PLL_PCIE_CFG1 0x4c
+#define PLL_PCIE_PLLEN BIT(31)
+#define PLL_PCIE_POSTDIV0PRE BIT(30)
+#define PLL_PCIE_REFDIV GENMASK(29, 24)
+#define PLL_PCIE_FRAC GENMASK(23, 0)
+
+#define PLL_PCIE_CFG2 0x50
+#define PLL_PCIE_FOUTEN(i) BIT(28 + (i))
+#define PLL_PCIE_BYPASS(i) BIT(24 + (i))
+#define PLL_PCIE_PDIVA_OFFS(i) (21 - 6 * (i))
+#define PLL_PCIE_PDIVB_OFFS(i) (18 - 6 * (i))
+#define PLL_PCIE_PDIV_MASK GENMASK(2, 0)
+
+#define PLL_PCIE_CFG3 0x54
+#define PLL_PCIE_DSKEWFASTCAL BIT(31)
+#define PLL_PCIE_DACEN BIT(30)
+#define PLL_PCIE_DSMEN BIT(29)
+#define PLL_PCIE_DSKEWCALEN BIT(28)
+#define PLL_PCIE_DSKEWCALBYP BIT(27)
+#define PLL_PCIE_DSKEWCALCNT GENMASK(26, 24)
+#define PLL_PCIE_DSKEWCALIN GENMASK(23, 12)
+#define PLL_PCIE_FBDIV GENMASK(11, 0)
+
+static unsigned long
+sf21_pciepll_vco_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct sf_clk_common *priv = hw_to_sf_clk_common(hw);
+ u32 cfg1 = sf_readl(priv, PLL_PCIE_CFG1);
+ unsigned long refdiv = FIELD_GET(PLL_PCIE_REFDIV, cfg1);
+ u32 cfg3 = sf_readl(priv, PLL_PCIE_CFG3);
+ unsigned long fbdiv = FIELD_GET(PLL_PCIE_FBDIV, cfg3);
+
+ return (parent_rate / refdiv) * fbdiv / 4;
+}
+
+static int sf21_pciepll_vco_enable(struct clk_hw *hw)
+{
+ struct sf_clk_common *priv = hw_to_sf_clk_common(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(priv->lock, flags);
+ sf_rmw(priv, PLL_PCIE_CFG1, 0, PLL_PCIE_PLLEN);
+ sf_writel(priv, CFG_LOAD, CFG_LOAD_PCIE_PLL);
+ sf_writel(priv, CFG_LOAD, 0);
+ while (!(sf_readl(priv, PLL_PCIE_LOCK)))
+ ;
+ spin_unlock_irqrestore(priv->lock, flags);
+ return 0;
+}
+
+static void sf21_pciepll_vco_disable(struct clk_hw *hw)
+{
+ struct sf_clk_common *priv = hw_to_sf_clk_common(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(priv->lock, flags);
+ sf_rmw(priv, PLL_PCIE_CFG1, PLL_PCIE_PLLEN, 0);
+ sf_writel(priv, CFG_LOAD, CFG_LOAD_PCIE_PLL);
+ sf_writel(priv, CFG_LOAD, 0);
+ spin_unlock_irqrestore(priv->lock, flags);
+}
+
+static const struct clk_ops sf21_pciepll_vco_ops = {
+ .enable = sf21_pciepll_vco_enable,
+ .disable = sf21_pciepll_vco_disable,
+ .recalc_rate = sf21_pciepll_vco_recalc_rate,
+};
+
+static struct sf_clk_common pciepll_vco =
+ SF_CLK_COMMON("pciepll_vco", clk_pll_parents,
+ &sf21_pciepll_vco_ops, CLK_SET_RATE_GATE);
+
+struct sf21_pciepll_fout {
+ struct sf_clk_common common;
+ u8 index;
+};
+
+static int sf21_pciepll_fout_enable(struct clk_hw *hw)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_pciepll_fout *priv =
+ container_of(cmn_priv, struct sf21_pciepll_fout, common);
+ unsigned long flags;
+
+ spin_lock_irqsave(cmn_priv->lock, flags);
+ sf_rmw(cmn_priv, PLL_PCIE_CFG2, 0, PLL_PCIE_FOUTEN(priv->index));
+ sf_writel(cmn_priv, CFG_LOAD, CFG_LOAD_PCIE_PLL);
+ sf_writel(cmn_priv, CFG_LOAD, 0);
+ spin_unlock_irqrestore(cmn_priv->lock, flags);
+ return 0;
+}
+
+static void sf21_pciepll_fout_disable(struct clk_hw *hw)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_pciepll_fout *priv =
+ container_of(cmn_priv, struct sf21_pciepll_fout, common);
+ unsigned long flags;
+
+ spin_lock_irqsave(cmn_priv->lock, flags);
+ sf_rmw(cmn_priv, PLL_PCIE_CFG2, PLL_PCIE_FOUTEN(priv->index), 0);
+ sf_writel(cmn_priv, CFG_LOAD, CFG_LOAD_PCIE_PLL);
+ sf_writel(cmn_priv, CFG_LOAD, 0);
+ spin_unlock_irqrestore(cmn_priv->lock, flags);
+}
+
+static long sf21_pciepll_fout_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned int diva, divb;
+
+ return sf21_dualdiv_round_rate(rate, *parent_rate, 8, &diva,
+ &divb);
+}
+
+static int sf21_pciepll_fout_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_pciepll_fout *priv =
+ container_of(cmn_priv, struct sf21_pciepll_fout, common);
+ unsigned int diva, divb;
+ unsigned long flags;
+
+ sf21_dualdiv_round_rate(rate, parent_rate, 8, &diva, &divb);
+
+ spin_lock_irqsave(cmn_priv->lock, flags);
+ sf_rmw(cmn_priv, PLL_PCIE_CFG2,
+ (PLL_PCIE_PDIV_MASK << PLL_PCIE_PDIVA_OFFS(priv->index)) |
+ (PLL_PCIE_PDIV_MASK << PLL_PCIE_PDIVB_OFFS(priv->index)),
+ ((diva - 1) << PLL_PCIE_PDIVA_OFFS(priv->index)) |
+ ((divb - 1) << PLL_PCIE_PDIVB_OFFS(priv->index)));
+ sf_writel(cmn_priv, CFG_LOAD, CFG_LOAD_PCIE_PLL);
+ sf_writel(cmn_priv, CFG_LOAD, 0);
+ spin_unlock_irqrestore(cmn_priv->lock, flags);
+ return 0;
+}
+
+static unsigned long
+sf21_pciepll_fout_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_pciepll_fout *priv =
+ container_of(cmn_priv, struct sf21_pciepll_fout, common);
+ int idx = priv->index;
+ u32 cfg2 = sf_readl(cmn_priv, PLL_PCIE_CFG2);
+ ulong pdiva = (cfg2 >> PLL_PCIE_PDIVA_OFFS(idx)) & PLL_PCIE_PDIV_MASK;
+ ulong pdivb = (cfg2 >> PLL_PCIE_PDIVB_OFFS(idx)) & PLL_PCIE_PDIV_MASK;
+
+ return parent_rate / (pdiva + 1) / (pdivb + 1);
+}
+
+static const struct clk_ops sf21_pciepll_fout_ops = {
+ .enable = sf21_pciepll_fout_enable,
+ .disable = sf21_pciepll_fout_disable,
+ .recalc_rate = sf21_pciepll_fout_recalc_rate,
+ .round_rate = sf21_pciepll_fout_round_rate,
+ .set_rate = sf21_pciepll_fout_set_rate,
+};
+
+static const char * const clk_pciepll_fout_parents[] = { "pciepll_vco" };
+
+#define SF21_PCIEPLL_FOUT(_name, _idx, _flags) \
+ struct sf21_pciepll_fout _name = { \
+ .common = SF_CLK_COMMON(#_name, \
+ clk_pciepll_fout_parents, \
+ &sf21_pciepll_fout_ops, \
+ _flags), \
+ .index = _idx, \
+ }
+
+static SF21_PCIEPLL_FOUT(pciepll_fout0, 0, 0);
+static SF21_PCIEPLL_FOUT(pciepll_fout1, 1, 0);
+static SF21_PCIEPLL_FOUT(pciepll_fout2, 2, 0);
+static SF21_PCIEPLL_FOUT(pciepll_fout3, 3, 0);
+
+struct sf21_clk_muxdiv {
+ struct sf_clk_common common;
+ u16 mux;
+ u16 en;
+ u8 div_reg;
+ u8 div_offs;
+};
+
+#define CRM_CLK_SEL(_x) ((_x) * 4 + 0x80)
+#define CRM_CLK_EN 0x8c
+#define CRM_CLK_DIV(_x) ((_x) * 4 + 0x94)
+#define CRM_CLK_DIV_MASK GENMASK(7, 0)
+
+static unsigned long sf21_muxdiv_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_clk_muxdiv *priv =
+ container_of(cmn_priv, struct sf21_clk_muxdiv, common);
+ ulong div_reg = CRM_CLK_DIV(priv->div_reg);
+ u16 div_offs = priv->div_offs;
+ u16 div_val = (sf_readl(cmn_priv, div_reg) >> div_offs) &
+ CRM_CLK_DIV_MASK;
+ div_val += 1;
+ return parent_rate / div_val;
+}
+
+static int sf21_muxdiv_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ unsigned int div;
+
+ div = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate);
+ if (!div)
+ div = 1;
+ else if (div > CRM_CLK_DIV_MASK + 1)
+ div = CRM_CLK_DIV_MASK + 1;
+
+ req->rate = req->best_parent_rate / div;
+ return 0;
+}
+
+static int sf21_muxdiv_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_clk_muxdiv *priv =
+ container_of(cmn_priv, struct sf21_clk_muxdiv, common);
+ ulong div_reg = CRM_CLK_DIV(priv->div_reg);
+ u16 div_offs = priv->div_offs;
+ unsigned long flags;
+ unsigned int div;
+
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ if (div < 1)
+ div = 1;
+ else if (div > CRM_CLK_DIV_MASK + 1)
+ div = CRM_CLK_DIV_MASK + 1;
+ div -= 1;
+
+ spin_lock_irqsave(cmn_priv->lock, flags);
+ sf_rmw(cmn_priv, div_reg, CRM_CLK_DIV_MASK << div_offs,
+ div << div_offs);
+ sf_writel(cmn_priv, CFG_LOAD, CFG_LOAD_DIV);
+ sf_writel(cmn_priv, CFG_LOAD, 0);
+ spin_unlock_irqrestore(cmn_priv->lock, flags);
+ return 0;
+}
+
+static int sf21_muxdiv_enable(struct clk_hw *hw)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_clk_muxdiv *priv =
+ container_of(cmn_priv, struct sf21_clk_muxdiv, common);
+ unsigned long flags;
+
+ spin_lock_irqsave(cmn_priv->lock, flags);
+ sf_rmw(cmn_priv, CRM_CLK_EN, 0, BIT(priv->en));
+ sf_writel(cmn_priv, CFG_LOAD, CFG_LOAD_DIV);
+ sf_writel(cmn_priv, CFG_LOAD, 0);
+ spin_unlock_irqrestore(cmn_priv->lock, flags);
+ return 0;
+}
+
+static void sf21_muxdiv_disable(struct clk_hw *hw)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_clk_muxdiv *priv =
+ container_of(cmn_priv, struct sf21_clk_muxdiv, common);
+ unsigned long flags;
+
+ spin_lock_irqsave(cmn_priv->lock, flags);
+ sf_rmw(cmn_priv, CRM_CLK_EN, BIT(priv->en), 0);
+ sf_writel(cmn_priv, CFG_LOAD, CFG_LOAD_DIV);
+ sf_writel(cmn_priv, CFG_LOAD, 0);
+ spin_unlock_irqrestore(cmn_priv->lock, flags);
+}
+
+static int sf21_muxdiv_is_enabled(struct clk_hw *hw)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_clk_muxdiv *priv =
+ container_of(cmn_priv, struct sf21_clk_muxdiv, common);
+ u32 reg_val = sf_readl(cmn_priv, CRM_CLK_EN);
+
+ return reg_val & (BIT(priv->en)) ? 1 : 0;
+}
+
+static u8 sf21_muxdiv_get_parent(struct clk_hw *hw)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_clk_muxdiv *priv =
+ container_of(cmn_priv, struct sf21_clk_muxdiv, common);
+ ulong mux_reg = CRM_CLK_SEL(priv->mux / 32);
+ u16 mux_offs = priv->mux % 32;
+ u32 reg_val = sf_readl(cmn_priv, mux_reg);
+
+ return reg_val & BIT(mux_offs) ? 1 : 0;
+}
+
+static int sf21_muxdiv_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+ struct sf21_clk_muxdiv *priv =
+ container_of(cmn_priv, struct sf21_clk_muxdiv, common);
+ ulong mux_reg = CRM_CLK_SEL(priv->mux / 32);
+ u16 mux_offs = priv->mux % 32;
+ unsigned long flags;
+
+ spin_lock_irqsave(cmn_priv->lock, flags);
+ if (index)
+ sf_rmw(cmn_priv, mux_reg, 0, BIT(mux_offs));
+ else
+ sf_rmw(cmn_priv, mux_reg, BIT(mux_offs), 0);
+
+ sf_writel(cmn_priv, CFG_LOAD, CFG_LOAD_DIV);
+ sf_writel(cmn_priv, CFG_LOAD, 0);
+ spin_unlock_irqrestore(cmn_priv->lock, flags);
+ return 0;
+}
+
+static const struct clk_ops sf21_clk_muxdiv_ops = {
+ .enable = sf21_muxdiv_enable,
+ .disable = sf21_muxdiv_disable,
+ .is_enabled = sf21_muxdiv_is_enabled,
+ .recalc_rate = sf21_muxdiv_recalc_rate,
+ .determine_rate = sf21_muxdiv_determine_rate,
+ .set_rate = sf21_muxdiv_set_rate,
+ .get_parent = sf21_muxdiv_get_parent,
+ .set_parent = sf21_muxdiv_set_parent,
+};
+
+#define SF21_MUXDIV(_name, _parents, \
+ _mux, _div_reg, _div_offs, _en, _flags) \
+ struct sf21_clk_muxdiv _name = { \
+ .common = SF_CLK_COMMON(#_name, _parents, \
+ &sf21_clk_muxdiv_ops, \
+ _flags), \
+ .mux = _mux, \
+ .en = _en, \
+ .div_reg = _div_reg, \
+ .div_offs = _div_offs, \
+ }
+
+static const char *const clk_periph_parents[] = { "cmnpll_postdiv",
+ "ddrpll_postdiv" };
+static const char *const clk_ddr_parents[] = { "ddrpll_postdiv",
+ "cmnpll_postdiv" };
+static const char *const clk_gmac_usb_parents[] = { "cmnpll_vco",
+ "ddrpll_postdiv" };
+
+static SF21_MUXDIV(muxdiv_cpu, clk_periph_parents, 1, 0, 0, 0, CLK_IGNORE_UNUSED);
+static SF21_MUXDIV(muxdiv_pic, clk_periph_parents, 3, 3, 16, 1, CLK_IGNORE_UNUSED);
+static SF21_MUXDIV(muxdiv_axi, clk_periph_parents, 5, 0, 8, 2, CLK_IS_CRITICAL);
+static SF21_MUXDIV(muxdiv_ahb, clk_periph_parents, 7, 0, 16, 3, CLK_IS_CRITICAL);
+static SF21_MUXDIV(muxdiv_apb, clk_periph_parents, 9, 0, 24, 4, CLK_IS_CRITICAL);
+static SF21_MUXDIV(muxdiv_uart, clk_periph_parents, 11, 1, 0, 5, 0);
+static SF21_MUXDIV(muxdiv_iram, clk_periph_parents, 13, 1, 8, 6, 0);
+static SF21_MUXDIV(muxdiv_npu, clk_periph_parents, 17, 1, 24, 8, 0);
+static SF21_MUXDIV(muxdiv_ddrphy, clk_ddr_parents, 19, 2, 0, 9, CLK_IS_CRITICAL);
+static SF21_MUXDIV(muxdiv_ddr_bypass, clk_ddr_parents, 21, 3, 0, 10, CLK_IS_CRITICAL);
+static SF21_MUXDIV(muxdiv_ethtsu, clk_periph_parents, 25, 2, 16, 12, 0);
+static SF21_MUXDIV(muxdiv_gmac_byp_ref, clk_gmac_usb_parents, 27, 2, 24, 13, 0);
+static SF21_MUXDIV(muxdiv_usb, clk_gmac_usb_parents, 33, 1, 16, 24, 0);
+static SF21_MUXDIV(muxdiv_usbphy, clk_gmac_usb_parents, 35, 2, 8, 25, 0);
+static SF21_MUXDIV(muxdiv_serdes_csr, clk_periph_parents, 47, 5, 0, 20, 0);
+static SF21_MUXDIV(muxdiv_crypt_csr, clk_periph_parents, 49, 5, 8, 21, 0);
+static SF21_MUXDIV(muxdiv_crypt_app, clk_periph_parents, 51, 5, 16, 22, 0);
+static SF21_MUXDIV(muxdiv_irom, clk_periph_parents, 53, 5, 24, 23, CLK_IS_CRITICAL);
+
+static int sf21_mux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ req->rate = req->best_parent_rate;
+ return 0;
+}
+
+static const struct clk_ops sf21_clk_mux_ops = {
+ .get_parent = sf21_muxdiv_get_parent,
+ .set_parent = sf21_muxdiv_set_parent,
+ .determine_rate = sf21_mux_determine_rate,
+};
+
+#define SF21_MUX(_name, _parents, _mux, _flags) \
+ struct sf21_clk_muxdiv _name = { \
+ .common = SF_CLK_COMMON(#_name, _parents, \
+ &sf21_clk_mux_ops, \
+ _flags), \
+ .mux = _mux, \
+ .en = 0, \
+ .div_reg = 0, \
+ .div_offs = 0, \
+ }
+
+static const char * const clk_boot_parents[] = { "muxdiv_irom", "xin25m" };
+
+static SF21_MUX(mux_boot, clk_boot_parents, 30, CLK_IS_CRITICAL);
+
+static const struct clk_ops sf21_clk_div_ops = {
+ .recalc_rate = sf21_muxdiv_recalc_rate,
+ .determine_rate = sf21_muxdiv_determine_rate,
+ .set_rate = sf21_muxdiv_set_rate,
+};
+
+#define SF21_DIV(_name, _parents, _div_reg, _div_offs, _flags) \
+ struct sf21_clk_muxdiv _name = { \
+ .common = SF_CLK_COMMON(#_name, _parents, \
+ &sf21_clk_div_ops, \
+ _flags), \
+ .mux = 0, \
+ .en = 0, \
+ .div_reg = _div_reg, \
+ .div_offs = _div_offs, \
+ }
+
+static SF21_DIV(div_pvt, clk_pll_parents, 3, 8, 0);
+static SF21_DIV(div_pll_test, clk_pll_parents, 3, 24, 0);
+
+static const struct clk_ops sf21_clk_gate_ops = {
+ .enable = sf21_muxdiv_enable,
+ .disable = sf21_muxdiv_disable,
+ .is_enabled = sf21_muxdiv_is_enabled,
+};
+
+#define SF21_GATE(_name, _parents, _en, _flags) \
+ struct sf21_clk_muxdiv _name = { \
+ .common = SF_CLK_COMMON(#_name, \
+ _parents, \
+ &sf21_clk_gate_ops, \
+ _flags), \
+ .mux = 0, \
+ .en = _en, \
+ .div_reg = 0, \
+ .div_offs = 0, \
+ }
+
+static const char * const clk_pcie_parents[] = { "pciepll_fout1" };
+
+static SF21_GATE(pcie_refclk_n, clk_pcie_parents, 15, 0);
+static SF21_GATE(pcie_refclk_p, clk_pcie_parents, 16, 0);
+
+static struct clk_hw_onecell_data sf21_hw_clks = {
+ .num = CLK_MAX,
+ .hws = {
+ [CLK_CMNPLL_VCO] = &cmnpll_vco.hw,
+ [CLK_CMNPLL_POSTDIV] = &cmnpll_postdiv.hw,
+ [CLK_DDRPLL_POSTDIV] = &ddrpll_postdiv.hw,
+ [CLK_PCIEPLL_VCO] = &pciepll_vco.hw,
+ [CLK_PCIEPLL_FOUT0] = &pciepll_fout0.common.hw,
+ [CLK_PCIEPLL_FOUT1] = &pciepll_fout1.common.hw,
+ [CLK_PCIEPLL_FOUT2] = &pciepll_fout2.common.hw,
+ [CLK_PCIEPLL_FOUT3] = &pciepll_fout3.common.hw,
+ [CLK_CPU] = &muxdiv_cpu.common.hw,
+ [CLK_PIC] = &muxdiv_pic.common.hw,
+ [CLK_AXI] = &muxdiv_axi.common.hw,
+ [CLK_AHB] = &muxdiv_ahb.common.hw,
+ [CLK_APB] = &muxdiv_apb.common.hw,
+ [CLK_UART] = &muxdiv_uart.common.hw,
+ [CLK_IRAM] = &muxdiv_iram.common.hw,
+ [CLK_NPU] = &muxdiv_npu.common.hw,
+ [CLK_DDRPHY_REF] = &muxdiv_ddrphy.common.hw,
+ [CLK_DDR_BYPASS] = &muxdiv_ddr_bypass.common.hw,
+ [CLK_ETHTSU] = &muxdiv_ethtsu.common.hw,
+ [CLK_GMAC_BYP_REF] = &muxdiv_gmac_byp_ref.common.hw,
+ [CLK_USB] = &muxdiv_usb.common.hw,
+ [CLK_USBPHY] = &muxdiv_usbphy.common.hw,
+ [CLK_SERDES_CSR] = &muxdiv_serdes_csr.common.hw,
+ [CLK_CRYPT_CSR] = &muxdiv_crypt_csr.common.hw,
+ [CLK_CRYPT_APP] = &muxdiv_crypt_app.common.hw,
+ [CLK_IROM] = &muxdiv_irom.common.hw,
+ [CLK_BOOT] = &mux_boot.common.hw,
+ [CLK_PVT] = &div_pvt.common.hw,
+ [CLK_PLL_TEST] = &div_pll_test.common.hw,
+ [CLK_PCIE_REFN] = &pcie_refclk_n.common.hw,
+ [CLK_PCIE_REFP] = &pcie_refclk_p.common.hw,
+ }
+};
+
+struct sf21_clk_ctrl {
+ void __iomem *base;
+ spinlock_t lock;
+};
+
+static void __init sf21_topcrm_init(struct device_node *node)
+{
+ struct sf21_clk_ctrl *ctrl;
+ int i, ret;
+
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return;
+
+ ctrl->base = of_iomap(node, 0);
+ if (!ctrl->base) {
+ pr_err("failed to map resources.\n");
+ return;
+ }
+
+ spin_lock_init(&ctrl->lock);
+
+ for (i = 0; i < sf21_hw_clks.num; i++) {
+ struct clk_hw *hw = sf21_hw_clks.hws[i];
+ struct sf_clk_common *common;
+
+ if (!hw)
+ continue;
+ common = hw_to_sf_clk_common(hw);
+ common->base = ctrl->base;
+ common->lock = &ctrl->lock;
+ ret = clk_hw_register(NULL, hw);
+ if (ret) {
+ pr_err("Couldn't register clock %d\n", i);
+ goto err;
+ }
+ }
+
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
+ &sf21_hw_clks);
+ if (ret) {
+ pr_err("failed to add hw provider.\n");
+ goto err;
+ }
+ return;
+err:
+ iounmap(ctrl->base);
+}
+
+CLK_OF_DECLARE(sf21_topcrm, "siflower,sf21-topcrm", sf21_topcrm_init);
.remove = sf_gpio_remove,
.driver = {
.name = "siflower_gpio",
+ .owner = THIS_MODULE,
.of_match_table = sf_gpio_ids,
},
};
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0
+#
+# Siflower network device configuration
+#
+
+config NET_VENDOR_SIFLOWER
+ bool "Siflower Ethernet"
+ default y
+ depends on ARCH_SIFLOWER
+
+if NET_VENDOR_SIFLOWER
+
+config NET_SIFLOWER_ETH_DPNS
+ tristate "Siflower DPNS driver"
+ help
+ Support the Dataplane network subsystem of SiFlower SF21A6826/SF21H8898 SoC.
+
+config NET_SIFLOWER_ETH_XGMAC
+ tristate "Siflower Ethernet MAC driver"
+ depends on NET_SIFLOWER_ETH_DPNS
+ select MII
+ select PAGE_POOL
+ select PHYLINK
+ select NET_SIFLOWER_ETH_DMA
+ select NET_SIFLOWER_ETH_XPCS
+ help
+ Support the Ethernet controller of SiFlower SF21A6826/SF21H8898 SoC.
+
+config NET_SIFLOWER_ETH_DMA
+ tristate "Siflower Ethernet DMA driver"
+ depends on NET_SIFLOWER_ETH_DPNS
+ select PAGE_POOL
+ help
+ Support the Ethernet controller of SiFlower SF21A6826/SF21H8898 SoC.
+
+config NET_SIFLOWER_ETH_XPCS
+ tristate "Siflower Ethernet XPCS driver"
+ depends on NET_SIFLOWER_ETH_DPNS
+ select PAGE_POOL
+ help
+ Support the PCS block of SiFlower SF21A6826/SF21H8898 SoC.
+
+if NET_SIFLOWER_ETH_DMA
+
+config NET_SIFLOWER_ETH_USE_INTERNAL_SRAM
+ bool "Use internal SRAM for DMA descriptors"
+ select SRAM
+ help
+ Use internal SRAM instead of system memory for DMA descriptors.
+
+endif # NET_SIFLOWER_ETH_DMA
+
+endif # NET_VENDOR_SIFLOWER
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the siflower soc network device drivers.
+#
+
+sf_dpns-objs := sf_dpns.o sf_dpns_debugfs.o sf_dpns_tmu.o sf_dpns_se.o
+obj-$(CONFIG_NET_SIFLOWER_ETH_DPNS) += sf_dpns.o
+
+obj-$(CONFIG_NET_SIFLOWER_ETH_XGMAC) += sfxgmac.o
+obj-$(CONFIG_NET_SIFLOWER_ETH_DMA) += sfxgmac-dma.o
+obj-$(CONFIG_NET_SIFLOWER_ETH_XPCS) += sfxpcs.o
\ No newline at end of file
--- /dev/null
+#ifndef _SFXGMAC_DMA_H
+#define _SFXGMAC_DMA_H
+
+#include <linux/clk.h>
+#include <linux/genalloc.h>
+#include <linux/if_vlan.h>
+#include <linux/mfd/syscon.h>
+#include <linux/netdevice.h>
+#include <linux/regmap.h>
+
+#ifdef CONFIG_NET_SIFLOWER_ETH_USE_INTERNAL_SRAM
+#define DMA_TX_SIZE 512
+#define DMA_RX_SIZE 512
+#else
+#define DMA_TX_SIZE 2048
+#define DMA_RX_SIZE 2048
+#endif
+#define DPNS_HOST_PORT 6
+#define DPNS_MAX_PORT 27
+#define DMA_CH_MAX 4
+#define DMA_CH_DISABLE 4
+#define DMA_OVPORT_CH 4
+#define SZ_1_5K 0x00000600
+#define SZ_3K 0x00000C00
+
+/* Either 8 (64-bit) or 16 (128-bit), configured in RTL */
+#define DMA_DATAWIDTH 8
+
+/* extra header room for skb to wifi */
+#define NET_WIFI_HEADERROOM_EXTRA SKB_DATA_ALIGN(32)
+
+/* Padding at the beginning of the allocated buffer, passed into skb_reserve */
+#define BUF_PAD (NET_SKB_PAD + NET_IP_ALIGN + NET_WIFI_HEADERROOM_EXTRA)
+
+/* RX Buffer size, calculated by MTU + eth header + double VLAN tag + FCS */
+#define BUF_SIZE(x) ((x) + ETH_HLEN + VLAN_HLEN * 2 + ETH_FCS_LEN)
+
+/* RX Buffer alloc size, with padding and skb_shared_info, passed into
+ * page_pool_dev_alloc_frag */
+#define BUF_SIZE_ALLOC(x) (SKB_DATA_ALIGN(BUF_SIZE(x) + BUF_PAD) + \
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+
+/* RX Buffer size programmed into RBSZ field, must be multiple of datawidth */
+#define BUF_SIZE_ALIGN(x) ALIGN(BUF_SIZE(x) + NET_IP_ALIGN, DMA_DATAWIDTH)
+
+/* Maximum value of the RBSZ field */
+#define BUF_SIZE_ALIGN_MAX ALIGN_DOWN(FIELD_MAX(XGMAC_RBSZ), DMA_DATAWIDTH)
+
+/* TODO: use mtu in priv */
+#define BUF_SIZE_DEFAULT SKB_MAX_HEAD(BUF_PAD)
+#define BUF_SIZE_DEFAULT_ALIGN ALIGN(BUF_SIZE_DEFAULT, DMA_DATAWIDTH)
+
+/* skb handled by dpns flag */
+#define SF_DPNS_FLAG 47
+
+/* skb handled by dpns need to be forwarded */
+#define SF_CB_DPNS_FORWARD 22
+
+struct xgmac_dma_desc {
+ __le32 des0;
+ __le32 des1;
+ __le32 des2;
+ __le32 des3;
+};
+
+struct xgmac_skb_cb {
+ u8 id;
+ bool fastmode;
+};
+
+#define XGMAC_SKB_CB(skb) ((struct xgmac_skb_cb *)(skb)->cb)
+
+struct xgmac_tx_info {
+ dma_addr_t buf;
+ bool map_as_page;
+ unsigned len;
+ bool last_segment;
+};
+
+struct xgmac_txq {
+ struct xgmac_dma_desc *dma_tx ____cacheline_aligned_in_smp;
+ struct sk_buff **tx_skbuff;
+ struct xgmac_tx_info *tx_skbuff_dma;
+ unsigned int cur_tx;
+ unsigned int dirty_tx;
+ dma_addr_t dma_tx_phy;
+ dma_addr_t tx_tail_addr;
+ spinlock_t lock;
+ struct napi_struct napi ____cacheline_aligned_in_smp;
+ u32 idx;
+ u32 irq;
+ bool is_busy;
+};
+
+struct xgmac_dma_rx_buffer {
+ struct page *page;
+ unsigned int offset;
+};
+
+struct xgmac_rxq {
+ struct xgmac_dma_desc *dma_rx ____cacheline_aligned_in_smp;
+ struct page_pool *page_pool;
+ struct xgmac_dma_rx_buffer *buf_pool;
+ unsigned int cur_rx;
+ unsigned int dirty_rx;
+ dma_addr_t dma_rx_phy;
+ dma_addr_t rx_tail_addr;
+ struct napi_struct napi ____cacheline_aligned_in_smp;
+ u32 idx;
+ u32 irq;
+};
+
+enum {
+ DMA_CLK_AXI,
+ DMA_CLK_NPU,
+ DMA_CLK_CSR,
+ DMA_NUM_CLKS
+};
+
+struct xgmac_dma_priv {
+ void __iomem *ioaddr;
+ struct device *dev;
+ struct clk_bulk_data clks[DMA_NUM_CLKS];
+ struct net_device napi_dev;
+ /* RX Queue */
+ struct xgmac_rxq rxq[DMA_CH_MAX];
+
+ /* TX Queue */
+ struct xgmac_txq txq[DMA_CH_MAX];
+
+ /* associated net devices (vports) */
+ struct net_device *ndevs[DPNS_MAX_PORT];
+
+ struct regmap *ethsys;
+ refcount_t refcnt;
+ u32 irq;
+ u8 ifindex;
+#ifdef CONFIG_NET_SIFLOWER_ETH_USE_INTERNAL_SRAM
+ struct gen_pool *genpool;
+#endif
+ u16 rx_alloc_size;
+ u16 rx_buffer_size;
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PAGE_POOL_STATS)
+ struct dentry *dbgdir;
+#endif
+};
+
+netdev_tx_t xgmac_dma_xmit_fast(struct sk_buff *skb, struct net_device *dev);
+int xgmac_dma_open(struct xgmac_dma_priv *priv, struct net_device *dev, u8 id);
+int xgmac_dma_stop(struct xgmac_dma_priv *priv, struct net_device *dev, u8 id);
+
+#endif
--- /dev/null
+#ifndef __SF_DPNS_H__
+#define __SF_DPNS_H__
+#include <asm/mmio.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/reset.h>
+
+#define PKT_ERR_STG_CFG2 0x80038
+#define ARP_REPLY_ERR_OP GENMASK(18, 16)
+#define ARP_REPLY_ERR_DROP BIT(18)
+#define ARP_REPLY_ERR_UP BIT(17)
+#define ARP_REPLY_ERR_FWD BIT(16)
+#define ARP_REQ_ERR_OP GENMASK(14, 12)
+#define ARP_REQ_ERR_DROP BIT(14)
+#define ARP_REQ_ERR_UP BIT(13)
+#define ARP_REQ_ERR_FWD BIT(12)
+
+#define PKT_ERR_ACTION_DROP BIT(2)
+#define PKT_ERR_ACTION_UP BIT(1)
+#define PKT_ERR_ACTION_FWD BIT(0)
+
+#define NPU_MIB_BASE 0x380000
+#define NPU_MIB(x) (NPU_MIB_BASE + (x) * 4)
+#define NPU_MIB_PKT_RCV_PORT(x) (NPU_MIB_BASE + 0x2000 + (x) * 4)
+#define NPU_MIB_NCI_RD_DATA2 (NPU_MIB_BASE + 0x301c)
+#define NPU_MIB_NCI_RD_DATA3 (NPU_MIB_BASE + 0x3020)
+
+struct dpns_priv {
+ void __iomem *ioaddr;
+ struct clk *clk;
+ struct reset_control *npu_rst;
+ struct device *dev;
+ struct dentry *debugfs;
+};
+
+static inline u32 dpns_r32(struct dpns_priv *priv, unsigned reg)
+{
+ return readl(priv->ioaddr + reg);
+}
+
+static inline void dpns_w32(struct dpns_priv *priv, unsigned reg, u32 val)
+{
+ writel(val, priv->ioaddr + reg);
+}
+
+static inline void dpns_rmw(struct dpns_priv *priv, unsigned reg, u32 clr,
+ u32 set)
+{
+ u32 val = dpns_r32(priv, reg);
+ val &= ~clr;
+ val |= set;
+ dpns_w32(priv, reg, val);
+}
+
+int dpns_se_init(struct dpns_priv *priv);
+int dpns_tmu_init(struct dpns_priv *priv);
+void sf_dpns_debugfs_init(struct dpns_priv *priv);
+
+#endif /* __SF_DPNS_H__ */
\ No newline at end of file
--- /dev/null
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Copyright (c) 2022 SiFlower Ltd.
+ */
+#ifndef _SIFLOWER_ETH_H
+#define _SIFLOWER_ETH_H
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/netdevice.h>
+
+struct phylink_pcs *xpcs_port_get(struct platform_device *, unsigned int);
+void xpcs_port_put(struct platform_device *);
+
+#define reg_read(p, reg) readl((p)->ioaddr + (reg))
+#define reg_write(p, reg, val) writel(val, (p)->ioaddr + (reg))
+#define reg_rmw(p, reg, clear, set) \
+ do { \
+ void __iomem *ioaddr = (p)->ioaddr + (reg); \
+ u32 val = readl(ioaddr); \
+ \
+ val &= ~(clear); \
+ val |= (set); \
+ writel(val, ioaddr); \
+ } while (0)
+#define reg_set(p, reg, set) reg_rmw(p, reg, 0, set)
+#define reg_clear(p, reg, clear) reg_rmw(p, reg, clear, 0)
+
+#define priv_to_netdev(p) \
+ ((struct net_device*)((void*)(p) - ALIGN(sizeof(struct net_device), NETDEV_ALIGN)))
+
+#define offset_to_id(addr) FIELD_GET(GENMASK(19, 14), addr)
+
+/* Maximum L2 frame size, including FCS */
+#define MAX_FRAME_SIZE 16383
+#define TSO_MAX_BUFF_SIZE MAX_FRAME_SIZE
+
+/* Ethernet sysm defines */
+#define ETHSYS_MAC(n) ((n) * 4)
+#define ETHSYS_PHY_INTF_SEL GENMASK(17, 16)
+#define ETHSYS_PHY_INTF_RGMII FIELD_PREP(ETHSYS_PHY_INTF_SEL, 1)
+#define ETHSYS_MAC5_CTRL 0xdc
+#define MAC5_PHY_INTF_SEL GENMASK(17, 16)
+#define MAC5_RX_DELAY_EN BIT(24)
+#define MAC5_RX_DELAY GENMASK(23, 16)
+#define MAC5_TX_DELAY_EN BIT(8)
+#define MAC5_TX_DELAY GENMASK(7, 0)
+#define MAC5_DELAY_MASK (MAC5_TX_DELAY_EN | MAC5_TX_DELAY | MAC5_RX_DELAY | MAC5_RX_DELAY_EN)
+#define MAC5_DELAY_STEP 49
+#define MAC5_DELAY_DEFAULT (0x41 * MAC5_DELAY_STEP)
+#define ETHSYS_QSG_CTRL 0x6c
+#define ETHSYS_SG_CTRL 0x70
+#define ETHSYS_REF_RPT_EN BIT(10)
+#define ETHSYS_RST 0x88
+#define ETHSYS_RST_MAC5 BIT(9)
+#define ETHSYS_RATIO_LOAD 0xec
+#define ETHSYS_NPU_BYPASS 0x8c
+#define ETHSYS_RX_QUEUE_ENABLE 0xb4
+#define ETHSYS_MRI_Q_MODE GENMASK(31, 30)
+#define ETHSYS_MRI_OVPORT_MAX GENMASK(21, 16)
+#define ETHSYS_MRI_OVPORT_MIN GENMASK(13, 8)
+#define ETHSYS_MRI_Q_EN 0xb8
+#define ETHSYS_MRI_OVPORT_TOP_PRIO GENMASK(5, 0)
+#define XGMAC_MTL_RXQ_DMA_MAP0 0x00001030
+#define ETHSYS_TX_DIS 0xd4
+#define ETHSYS_QS_SGMII_STATUS 0x128
+#define XGMAC_PORT_DH(n) (BIT(12) >> ((n)*4))
+#define XGMAC_PORT_LINK(n) (BIT(13) >> ((n)*4))
+#define XGMAC_PORT0_SPD_MASK GENMASK(3, 2)
+
+#define GMAC_HI_REG_AE BIT(31)
+
+/* XGMAC Registers */
+#define XGMAC_TX_CONFIG 0x00000000
+#define XGMAC_CONFIG_SS_OFF 29
+#define XGMAC_CONFIG_SS_MASK GENMASK(31, 29)
+#define XGMAC_CONFIG_SS_10000 (0x0 << XGMAC_CONFIG_SS_OFF)
+#define XGMAC_CONFIG_SS_2500_GMII (0x2 << XGMAC_CONFIG_SS_OFF)
+#define XGMAC_CONFIG_SS_1000_GMII (0x3 << XGMAC_CONFIG_SS_OFF)
+#define XGMAC_CONFIG_SS_100_MII (0x4 << XGMAC_CONFIG_SS_OFF)
+#define XGMAC_CONFIG_SS_5000 (0x5 << XGMAC_CONFIG_SS_OFF)
+#define XGMAC_CONFIG_SS_2500 (0x6 << XGMAC_CONFIG_SS_OFF)
+#define XGMAC_CONFIG_SS_10_MII (0x7 << XGMAC_CONFIG_SS_OFF)
+#define XGMAC_CONFIG_SARC GENMASK(22, 20)
+#define XGMAC_CONFIG_SARC_SHIFT 20
+#define XGMAC_CONFIG_JD BIT(16)
+#define XGMAC_CONFIG_IFP BIT(11)
+enum inter_packet_gap {
+ XGMAC_CONTROL_IPG_88 = 0x00000100,
+ XGMAC_CONTROL_IPG_80 = 0x00000200,
+ XGMAC_CONTROL_IPG_72 = 0x00000300,
+ XGMAC_CONTROL_IPG_64 = 0x00000400,
+};
+#define XGMAC_CONFIG_TE BIT(0)
+#define XGMAC_CORE_INIT_TX (XGMAC_CONTROL_IPG_88)
+#define XGMAC_RX_CONFIG 0x00000004
+#define XGMAC_CONFIG_ARPEN BIT(31)
+#define XGMAC_CONFIG_GPSL GENMASK(29, 16)
+#define XGMAC_CONFIG_GPSL_SHIFT 16
+#define XGMAC_CONFIG_HDSMS GENMASK(14, 12)
+#define XGMAC_CONFIG_HDSMS_SHIFT 12
+#define XGMAC_CONFIG_HDSMS_256 (0x2 << XGMAC_CONFIG_HDSMS_SHIFT)
+#define XGMAC_CONFIG_S2KP BIT(11)
+#define XGMAC_CONFIG_LM BIT(10)
+#define XGMAC_CONFIG_IPC BIT(9)
+#define XGMAC_CONFIG_JE BIT(8)
+#define XGMAC_CONFIG_WD BIT(7)
+#define XGMAC_CONFIG_GPSLCE BIT(6)
+#define XGMAC_CONFIG_DCRCC BIT(3)
+#define XGMAC_CONFIG_CST BIT(2)
+#define XGMAC_CONFIG_ACS BIT(1)
+#define XGMAC_CONFIG_RE BIT(0)
+#define XGMAC_CORE_INIT_RX (XGMAC_CONFIG_ACS | XGMAC_CONFIG_CST |\
+ XGMAC_CONFIG_IPC)
+#define XGMAC_PACKET_FILTER 0x00000008
+#define XGMAC_FILTER_RA BIT(31)
+#define XGMAC_FILTER_IPFE BIT(20)
+#define XGMAC_FILTER_VTFE BIT(16)
+#define XGMAC_FILTER_HPF BIT(10)
+#define XGMAC_FILTER_PCF BIT(7)
+#define XGMAC_FILTER_PM BIT(4)
+#define XGMAC_FILTER_HMC BIT(2)
+#define XGMAC_FILTER_PR BIT(0)
+#define XGMAC_WD_JB_TIMEOUT 0xc
+#define XGMAC_PJE BIT(24)
+#define XGMAC_JTO GENMASK(19, 16)
+#define XGMAC_PWE BIT(8)
+#define XGMAC_WTO GENMASK(3, 0)
+#define XGMAC_HASH_TABLE(x) (0x00000010 + (x) * 4)
+#define XGMAC_MAX_HASH_TABLE 8
+#define XGMAC_VLAN_TAG 0x00000050
+#define XGMAC_VLAN_EDVLP BIT(26)
+#define XGMAC_VLAN_VTHM BIT(25)
+#define XGMAC_VLAN_DOVLTC BIT(20)
+#define XGMAC_VLAN_ESVL BIT(18)
+#define XGMAC_VLAN_ETV BIT(16)
+#define XGMAC_VLAN_VID GENMASK(15, 0)
+#define XGMAC_VLAN_HASH_TABLE 0x00000058
+#define XGMAC_VLAN_INCL 0x00000060
+#define XGMAC_VLAN_VLTI BIT(20)
+#define XGMAC_VLAN_CSVL BIT(19)
+#define XGMAC_VLAN_VLC GENMASK(17, 16)
+#define XGMAC_VLAN_VLC_SHIFT 16
+#define XGMAC_RXQ_CTRL0 0x000000a0
+#define XGMAC_RXQEN(x) GENMASK((x) * 2 + 1, (x) * 2)
+#define XGMAC_RXQEN_SHIFT(x) ((x) * 2)
+#define XGMAC_RXQ_CTRL1 0x000000a4
+#define XGMAC_RQ GENMASK(7, 4)
+#define XGMAC_RQ_SHIFT 4
+#define XGMAC_RXQ_CTRL2 0x000000a8
+#define XGMAC_RXQ_CTRL3 0x000000ac
+#define XGMAC_PSRQ(x) GENMASK((x) * 8 + 7, (x) * 8)
+#define XGMAC_PSRQ_SHIFT(x) ((x) * 8)
+#define XGMAC_INT_STATUS 0x000000b0
+#define XGMAC_RGMII_LS BIT(27)
+#define XGMAC_RGMII_SPD GENMASK(26, 25)
+#define XGMAC_RGMII_DM BIT(24)
+#define XGMAC_LS GENMASK(25, 24)
+#define XGMAC_MMCRXIPIS BIT(11)
+#define XGMAC_MMCTXIS BIT(10)
+#define XGMAC_MMCRXIS BIT(9)
+#define XGMAC_MMCIS (XGMAC_MMCRXIPIS | XGMAC_MMCTXIS | XGMAC_MMCRXIS)
+#define XGMAC_LPIIS BIT(5)
+#define XGMAC_PMTIS BIT(4)
+#define XGMAC_SMI BIT(1)
+#define XGMAC_LSI BIT(0)
+#define XGMAC_INT_EN 0x000000b4
+#define XGMAC_TSIE BIT(12)
+#define XGMAC_LPIIE BIT(5)
+#define XGMAC_PMTIE BIT(4)
+#define XGMAC_INT_DEFAULT_EN (XGMAC_LPIIE | XGMAC_PMTIE)
+#define XGMAC_Qx_TX_FLOW_CTRL(x) (0x00000070 + (x) * 4)
+#define XGMAC_PT GENMASK(31, 16)
+#define XGMAC_PT_SHIFT 16
+#define XGMAC_TFE BIT(1)
+#define XGMAC_RX_FLOW_CTRL 0x00000090
+#define XGMAC_UP BIT(1)
+#define XGMAC_RFE BIT(0)
+#define XGMAC_PMT 0x000000c0
+#define XGMAC_GLBLUCAST BIT(9)
+#define XGMAC_RWKPKTEN BIT(2)
+#define XGMAC_MGKPKTEN BIT(1)
+#define XGMAC_PWRDWN BIT(0)
+#define XGMAC_LPI_CTRL 0x000000d0
+#define XGMAC_TXCGE BIT(21)
+#define XGMAC_LPIATE BIT(20)
+#define XGMAC_LPITXA BIT(19)
+#define XGMAC_PLS BIT(17)
+#define XGMAC_LPITXEN BIT(16)
+#define XGMAC_RLPIEX BIT(3)
+#define XGMAC_RLPIEN BIT(2)
+#define XGMAC_TLPIEX BIT(1)
+#define XGMAC_TLPIEN BIT(0)
+#define XGMAC_LPI_TIMER_CTRL 0x000000d4
+#define XGMAC_LPI_LST GENMASK(25, 16)
+#define XGMAC_LPI_LST_DEFAULT 1000
+#define XGMAC_LPI_TWT GENMASK(15, 0)
+#define XGMAC_LPI_TWT_DEFAULT 30
+#define XGMAC_LPI_AUTO_EN 0x000000d8
+#define XGMAC_LPI_AUTO_EN_MAX 0xffff8
+#define XGMAC_LPI_AUTO_EN_DEFAULT 10000
+#define XGMAC_LPI_1US 0x000000dc
+#define XGMAC_VERSION 0x00000110
+#define XGMAC_VERSION_USER GENMASK(23, 16)
+#define XGMAC_VERSION_ID_MASK GENMASK(15, 0)
+#define XGMAC_VERSION_ID 0x7631
+#define XGMAC_HW_FEATURE0 0x0000011c
+#define XGMAC_HWFEAT_SAVLANINS BIT(27)
+#define XGMAC_HWFEAT_RXCOESEL BIT(16)
+#define XGMAC_HWFEAT_TXCOESEL BIT(14)
+#define XGMAC_HWFEAT_EEESEL BIT(13)
+#define XGMAC_HWFEAT_TSSEL BIT(12)
+#define XGMAC_HWFEAT_AVSEL BIT(11)
+#define XGMAC_HWFEAT_RAVSEL BIT(10)
+#define XGMAC_HWFEAT_ARPOFFSEL BIT(9)
+#define XGMAC_HWFEAT_MMCSEL BIT(8)
+#define XGMAC_HWFEAT_MGKSEL BIT(7)
+#define XGMAC_HWFEAT_RWKSEL BIT(6)
+#define XGMAC_HWFEAT_VLHASH BIT(4)
+#define XGMAC_HWFEAT_GMIISEL BIT(1)
+#define XGMAC_HW_FEATURE1 0x00000120
+#define XGMAC_HWFEAT_L3L4FNUM GENMASK(30, 27)
+#define XGMAC_HWFEAT_HASHTBLSZ GENMASK(25, 24)
+#define XGMAC_HWFEAT_RSSEN BIT(20)
+#define XGMAC_HWFEAT_TSOEN BIT(18)
+#define XGMAC_HWFEAT_SPHEN BIT(17)
+#define XGMAC_HWFEAT_ADDR64 GENMASK(15, 14)
+#define XGMAC_HWFEAT_TXFIFOSIZE GENMASK(10, 6)
+#define XGMAC_HWFEAT_RXFIFOSIZE GENMASK(4, 0)
+#define XGMAC_HW_FEATURE2 0x00000124
+#define XGMAC_HWFEAT_PPSOUTNUM GENMASK(26, 24)
+#define XGMAC_HWFEAT_TXCHCNT GENMASK(21, 18)
+#define XGMAC_HWFEAT_RXCHCNT GENMASK(15, 12)
+#define XGMAC_HWFEAT_TXQCNT GENMASK(9, 6)
+#define XGMAC_HWFEAT_RXQCNT GENMASK(3, 0)
+#define XGMAC_HW_FEATURE3 0x00000128
+#define XGMAC_HWFEAT_TBSSEL BIT(27)
+#define XGMAC_HWFEAT_FPESEL BIT(26)
+#define XGMAC_HWFEAT_ESTWID GENMASK(24, 23)
+#define XGMAC_HWFEAT_ESTDEP GENMASK(22, 20)
+#define XGMAC_HWFEAT_ESTSEL BIT(19)
+#define XGMAC_HWFEAT_ASP GENMASK(15, 14)
+#define XGMAC_HWFEAT_DVLAN BIT(13)
+#define XGMAC_HWFEAT_FRPES GENMASK(12, 11)
+#define XGMAC_HWFEAT_FRPPB GENMASK(10, 9)
+#define XGMAC_HWFEAT_FRPSEL BIT(3)
+#define XGMAC_MAC_EXT_CONFIG 0x00000140
+#define XGMAC_HD BIT(24)
+#define XGMAC_MAC_DPP_FSM_INT_STATUS 0x00000150
+#define XGMAC_MAC_FSM_CONTROL 0x00000158
+#define XGMAC_PRTYEN BIT(1)
+#define XGMAC_TMOUTEN BIT(0)
+#define XGMAC_FPE_CTRL_STS 0x00000280
+#define XGMAC_EFPE BIT(0)
+#define XGMAC_ADDRx_HIGH(x) (0x00000300 + (x) * 0x8)
+#define XGMAC_ADDR_MAX 32
+#define XGMAC_AE BIT(31)
+#define XGMAC_DCS GENMASK(19, 16)
+#define XGMAC_DCS_SHIFT 16
+#define XGMAC_ADDRx_LOW(x) (0x00000304 + (x) * 0x8)
+#define XGMAC_L3L4_ADDR_CTRL 0x00000c00
+#define XGMAC_IDDR GENMASK(15, 8)
+#define XGMAC_IDDR_SHIFT 8
+#define XGMAC_IDDR_FNUM 4
+#define XGMAC_TT BIT(1)
+#define XGMAC_XB BIT(0)
+#define XGMAC_L3L4_DATA 0x00000c04
+#define XGMAC_L3L4_CTRL 0x0
+#define XGMAC_L4DPIM0 BIT(21)
+#define XGMAC_L4DPM0 BIT(20)
+#define XGMAC_L4SPIM0 BIT(19)
+#define XGMAC_L4SPM0 BIT(18)
+#define XGMAC_L4PEN0 BIT(16)
+#define XGMAC_L3HDBM0 GENMASK(15, 11)
+#define XGMAC_L3HSBM0 GENMASK(10, 6)
+#define XGMAC_L3DAIM0 BIT(5)
+#define XGMAC_L3DAM0 BIT(4)
+#define XGMAC_L3SAIM0 BIT(3)
+#define XGMAC_L3SAM0 BIT(2)
+#define XGMAC_L3PEN0 BIT(0)
+#define XGMAC_L4_ADDR 0x1
+#define XGMAC_L4DP0 GENMASK(31, 16)
+#define XGMAC_L4DP0_SHIFT 16
+#define XGMAC_L4SP0 GENMASK(15, 0)
+#define XGMAC_L3_ADDR0 0x4
+#define XGMAC_L3_ADDR1 0x5
+#define XGMAC_L3_ADDR2 0x6
+#define XGMAC_L3_ADDR3 0x7
+#define XGMAC_ARP_ADDR 0x00000c10
+#define XGMAC_RSS_CTRL 0x00000c80
+#define XGMAC_UDP4TE BIT(3)
+#define XGMAC_TCP4TE BIT(2)
+#define XGMAC_IP2TE BIT(1)
+#define XGMAC_RSSE BIT(0)
+#define XGMAC_RSS_ADDR 0x00000c88
+#define XGMAC_RSSIA_SHIFT 8
+#define XGMAC_ADDRT BIT(2)
+#define XGMAC_CT BIT(1)
+#define XGMAC_OB BIT(0)
+#define XGMAC_RSS_DATA 0x00000c8c
+#define XGMAC_TIMESTAMP_STATUS 0x00000d20
+#define XGMAC_TXTSC BIT(15)
+#define XGMAC_TXTIMESTAMP_NSEC 0x00000d30
+#define XGMAC_TXTSSTSLO GENMASK(30, 0)
+#define XGMAC_TXTIMESTAMP_SEC 0x00000d34
+#define XGMAC_PPS_CONTROL 0x00000d70
+#define XGMAC_PPS_MAXIDX(x) ((((x) + 1) * 8) - 1)
+#define XGMAC_PPS_MINIDX(x) ((x) * 8)
+#define XGMAC_PPSx_MASK(x) \
+ GENMASK(XGMAC_PPS_MAXIDX(x), XGMAC_PPS_MINIDX(x))
+#define XGMAC_TRGTMODSELx(x, val) \
+ GENMASK(XGMAC_PPS_MAXIDX(x) - 1, XGMAC_PPS_MAXIDX(x) - 2) & \
+ ((val) << (XGMAC_PPS_MAXIDX(x) - 2))
+#define XGMAC_PPSCMDx(x, val) \
+ GENMASK(XGMAC_PPS_MINIDX(x) + 3, XGMAC_PPS_MINIDX(x)) & \
+ ((val) << XGMAC_PPS_MINIDX(x))
+#define XGMAC_PPSCMD_START 0x2
+#define XGMAC_PPSCMD_STOP 0x5
+#define XGMAC_PPSEN0 BIT(4)
+#define XGMAC_PPSx_TARGET_TIME_SEC(x) (0x00000d80 + (x) * 0x10)
+#define XGMAC_PPSx_TARGET_TIME_NSEC(x) (0x00000d84 + (x) * 0x10)
+#define XGMAC_TRGTBUSY0 BIT(31)
+#define XGMAC_PPSx_INTERVAL(x) (0x00000d88 + (x) * 0x10)
+#define XGMAC_PPSx_WIDTH(x) (0x00000d8c + (x) * 0x10)
+
+#define XGMAC_MDIO_ADDR 0x00000200
+#define XGMAC_MDIO_DATA 0x00000204
+#define XGMAC_MDIO_INT_STATUS 0x00000214
+#define XGMAC_MDIO_INT_EN 0x00000218
+#define XGMAC_MDIO_INT_EN_SINGLE BIT(12)
+#define XGMAC_MDIO_C22P 0x00000220
+
+/* MDIO defines */
+#define MII_GMAC_PA GENMASK(15, 11)
+#define MII_GMAC_RA GENMASK(10, 6)
+#define MII_GMAC_CR GENMASK(5, 2)
+#define MII_GMAC_WRITE BIT(1)
+#define MII_GMAC_BUSY BIT(0)
+#define MII_DATA_MASK GENMASK(15, 0)
+#define MII_XGMAC_DA GENMASK(25, 21)
+#define MII_XGMAC_PA GENMASK(20, 16)
+#define MII_XGMAC_RA GENMASK(15, 0)
+#define MII_XGMAC_BUSY BIT(22)
+#define MII_XGMAC_CR GENMASK(21, 19)
+#define MII_XGMAC_SADDR BIT(18)
+#define MII_XGMAC_CMD_SHIFT 16
+#define MII_XGMAC_WRITE (1 << MII_XGMAC_CMD_SHIFT)
+#define MII_XGMAC_READ (3 << MII_XGMAC_CMD_SHIFT)
+#define MII_XGMAC_PSE BIT(30)
+#define MII_XGMAC_CRS BIT(31)
+
+/* XGMAC MMC Registers */
+#define MMC_XGMAC_CONTROL 0x800
+#define MMC_XGMAC_CONTROL_RSTONRD BIT(2)
+#define MMC_XGMAC_CONTROL_RESET BIT(0)
+#define MMC_XGMAC_RX_INT_EN 0x80c
+#define MMC_XGMAC_TX_INT_EN 0x810
+
+#define MMC_XGMAC_TX_OCTET_GB 0x814
+#define MMC_XGMAC_TX_PKT_GB 0x81c
+#define MMC_XGMAC_TX_BROAD_PKT_G 0x824
+#define MMC_XGMAC_TX_MULTI_PKT_G 0x82c
+#define MMC_XGMAC_TX_64OCT_GB 0x834
+#define MMC_XGMAC_TX_65OCT_GB 0x83c
+#define MMC_XGMAC_TX_128OCT_GB 0x844
+#define MMC_XGMAC_TX_256OCT_GB 0x84c
+#define MMC_XGMAC_TX_512OCT_GB 0x854
+#define MMC_XGMAC_TX_1024OCT_GB 0x85c
+#define MMC_XGMAC_TX_UNI_PKT_GB 0x864
+#define MMC_XGMAC_TX_MULTI_PKT_GB 0x86c
+#define MMC_XGMAC_TX_BROAD_PKT_GB 0x874
+#define MMC_XGMAC_TX_UNDER 0x87c
+#define MMC_XGMAC_TX_OCTET_G 0x884
+#define MMC_XGMAC_TX_PKT_G 0x88c
+#define MMC_XGMAC_TX_PAUSE 0x894
+#define MMC_XGMAC_TX_VLAN_PKT_G 0x89c
+#define MMC_XGMAC_TX_LPI_USEC 0x8a4
+#define MMC_XGMAC_TX_LPI_TRAN 0x8a8
+
+#define MMC_XGMAC_RX_PKT_GB 0x900
+#define MMC_XGMAC_RX_OCTET_GB 0x908
+#define MMC_XGMAC_RX_OCTET_G 0x910
+#define MMC_XGMAC_RX_BROAD_PKT_G 0x918
+#define MMC_XGMAC_RX_MULTI_PKT_G 0x920
+#define MMC_XGMAC_RX_CRC_ERR 0x928
+#define MMC_XGMAC_RX_RUNT_ERR 0x930
+#define MMC_XGMAC_RX_JABBER_ERR 0x934
+#define MMC_XGMAC_RX_UNDER 0x938
+#define MMC_XGMAC_RX_OVER 0x93c
+#define MMC_XGMAC_RX_64OCT_GB 0x940
+#define MMC_XGMAC_RX_65OCT_GB 0x948
+#define MMC_XGMAC_RX_128OCT_GB 0x950
+#define MMC_XGMAC_RX_256OCT_GB 0x958
+#define MMC_XGMAC_RX_512OCT_GB 0x960
+#define MMC_XGMAC_RX_1024OCT_GB 0x968
+#define MMC_XGMAC_RX_UNI_PKT_G 0x970
+#define MMC_XGMAC_RX_LENGTH_ERR 0x978
+#define MMC_XGMAC_RX_RANGE 0x980
+#define MMC_XGMAC_RX_PAUSE 0x988
+#define MMC_XGMAC_RX_FIFOOVER_PKT 0x990
+#define MMC_XGMAC_RX_VLAN_PKT_GB 0x998
+#define MMC_XGMAC_RX_WATCHDOG_ERR 0x9a0
+#define MMC_XGMAC_RX_LPI_USEC 0x9a4
+#define MMC_XGMAC_RX_LPI_TRAN 0x9a8
+#define MMC_XGMAC_RX_DISCARD_PKT_GB 0x9ac
+#define MMC_XGMAC_RX_DISCARD_OCT_GB 0x9b4
+#define MMC_XGMAC_RX_ALIGN_ERR_PKT 0x9bc
+
+#define MMC_XGMAC_TX_SINGLE_COL_G 0xa40
+#define MMC_XGMAC_TX_MULTI_COL_G 0xa44
+#define MMC_XGMAC_TX_DEFER 0xa48
+#define MMC_XGMAC_TX_LATE_COL 0xa4c
+#define MMC_XGMAC_TX_EXCESSIVE_COL 0xa50
+#define MMC_XGMAC_TX_CARRIER 0xa54
+#define MMC_XGMAC_TX_EXCESSIVE_DEFER 0xa58
+
+#define MMC_XGMAC_RX_IPC_INTR_MASK 0xa5c
+
+#define MMC_XGMAC_RX_IPV4_PKT_G 0xa64
+#define MMC_XGMAC_RX_IPV4_HDRERR_PKT 0xa6c
+#define MMC_XGMAC_RX_IPV4_NOPAY_PKT 0xa74
+#define MMC_XGMAC_RX_IPV4_FRAG_PKT 0xa7c
+#define MMC_XGMAC_RX_IPV4_UDSBL_PKT 0xa84
+#define MMC_XGMAC_RX_IPV6_PKT_G 0xa8c
+#define MMC_XGMAC_RX_IPV6_HDRERR_PKT 0xa94
+#define MMC_XGMAC_RX_IPV6_NOPAY_PKT 0xa9c
+#define MMC_XGMAC_RX_UDP_PKT_G 0xaa4
+#define MMC_XGMAC_RX_UDP_ERR_PKT 0xaac
+#define MMC_XGMAC_RX_TCP_PKT_G 0xab4
+#define MMC_XGMAC_RX_TCP_ERR_PKT 0xabc
+#define MMC_XGMAC_RX_ICMP_PKT_G 0xac4
+#define MMC_XGMAC_RX_ICMP_ERR_PKT 0xacc
+#define MMC_XGMAC_RX_IPV4_OCTET_G 0xad4
+#define MMC_XGMAC_RX_IPV4_HDRERR_OCTET 0xadc
+#define MMC_XGMAC_RX_IPV4_NOPAY_OCTET 0xae4
+#define MMC_XGMAC_RX_IPV4_FRAG_OCTET 0xaec
+#define MMC_XGMAC_RX_IPV4_UDSBL_OCTET 0xaf4
+#define MMC_XGMAC_RX_IPV6_OCTET_G 0xafc
+#define MMC_XGMAC_RX_IPV6_HDRERR_OCTET 0xb04
+#define MMC_XGMAC_RX_IPV6_NOPAY_OCTET 0xb0c
+#define MMC_XGMAC_RX_UDP_OCTET_G 0xb14
+#define MMC_XGMAC_RX_UDP_ERR_OCTET 0xb1c
+#define MMC_XGMAC_RX_TCP_OCTET_G 0xb24
+#define MMC_XGMAC_RX_TCP_ERR_OCTET 0xb2c
+#define MMC_XGMAC_RX_ICMP_OCTET_G 0xb34
+#define MMC_XGMAC_RX_ICMP_ERR_OCTET 0xb3c
+
+/* MTL Registers */
+#define XGMAC_MTL_OPMODE 0x00001000
+#define XGMAC_FRPE BIT(15)
+#define XGMAC_ETSALG GENMASK(6, 5)
+#define XGMAC_WRR (0x0 << 5)
+#define XGMAC_WFQ (0x1 << 5)
+#define XGMAC_DWRR (0x2 << 5)
+#define XGMAC_RAA BIT(2)
+#define XGMAC_FTS BIT(1)
+#define XGMAC_MTL_INT_STATUS 0x00001020
+#define XGMAC_MTL_RXQ_DMA_MAP0 0x00001030
+#define XGMAC_MTL_RXQ_DMA_MAP1 0x00001034
+#define XGMAC_QxMDMACH(x) GENMASK((x) * 8 + 7, (x) * 8)
+#define XGMAC_QxMDMACH_SHIFT(x) ((x) * 8)
+#define XGMAC_QDDMACH BIT(7)
+#define XGMAC_TC_PRTY_MAP0 0x00001040
+#define XGMAC_TC_PRTY_MAP1 0x00001044
+#define XGMAC_PSTC(x) GENMASK((x) * 8 + 7, (x) * 8)
+#define XGMAC_PSTC_SHIFT(x) ((x) * 8)
+#define XGMAC_MTL_EST_CONTROL 0x00001050
+#define XGMAC_PTOV GENMASK(31, 23)
+#define XGMAC_PTOV_SHIFT 23
+#define XGMAC_SSWL BIT(1)
+#define XGMAC_EEST BIT(0)
+#define XGMAC_MTL_EST_GCL_CONTROL 0x00001080
+#define XGMAC_BTR_LOW 0x0
+#define XGMAC_BTR_HIGH 0x1
+#define XGMAC_CTR_LOW 0x2
+#define XGMAC_CTR_HIGH 0x3
+#define XGMAC_TER 0x4
+#define XGMAC_LLR 0x5
+#define XGMAC_ADDR_SHIFT 8
+#define XGMAC_GCRR BIT(2)
+#define XGMAC_SRWO BIT(0)
+#define XGMAC_MTL_EST_GCL_DATA 0x00001084
+#define XGMAC_MTL_RXP_CONTROL_STATUS 0x000010a0
+#define XGMAC_RXPI BIT(31)
+#define XGMAC_NPE GENMASK(23, 16)
+#define XGMAC_NVE GENMASK(7, 0)
+#define XGMAC_MTL_RXP_IACC_CTRL_ST 0x000010b0
+#define XGMAC_STARTBUSY BIT(31)
+#define XGMAC_WRRDN BIT(16)
+#define XGMAC_ADDR GENMASK(9, 0)
+#define XGMAC_MTL_RXP_IACC_DATA 0x000010b4
+#define XGMAC_MTL_ECC_CONTROL 0x000010c0
+#define XGMAC_MTL_SAFETY_INT_STATUS 0x000010c4
+#define XGMAC_MEUIS BIT(1)
+#define XGMAC_MECIS BIT(0)
+#define XGMAC_MTL_ECC_INT_ENABLE 0x000010c8
+#define XGMAC_RPCEIE BIT(12)
+#define XGMAC_ECEIE BIT(8)
+#define XGMAC_RXCEIE BIT(4)
+#define XGMAC_TXCEIE BIT(0)
+#define XGMAC_MTL_ECC_INT_STATUS 0x000010cc
+#define XGMAC_MTL_TXQ_OPMODE(x) (0x00001100 + 0x80 * (x))
+#define XGMAC_TQS GENMASK(25, 16)
+#define XGMAC_TQS_SHIFT 16
+#define XGMAC_Q2TCMAP GENMASK(10, 8)
+#define XGMAC_Q2TCMAP_SHIFT 8
+#define XGMAC_TTC GENMASK(6, 4)
+#define XGMAC_TTC_SHIFT 4
+#define XGMAC_TXQEN GENMASK(3, 2)
+#define XGMAC_TXQEN_SHIFT 2
+#define XGMAC_TSF BIT(1)
+#define XGMAC_FTQ BIT(0)
+#define XGMAC_MTL_TXQ_DEBUG(x) (0x00001108 + 0x80 * (x))
+#define XGMAC_TRCPSTS BIT(5)
+#define XGMAC_TXQSTS BIT(4)
+#define XGMAC_TWCSTS BIT(3)
+#define XGMAC_TRCSTS GENMASK(2, 1)
+#define XGMAC_TCPAUSED BIT(0)
+#define XGMAC_MTL_TCx_ETS_CONTROL(x) (0x00001110 + 0x80 * (x))
+#define XGMAC_MTL_TCx_QUANTUM_WEIGHT(x) (0x00001118 + 0x80 * (x))
+#define XGMAC_MTL_TCx_SENDSLOPE(x) (0x0000111c + 0x80 * (x))
+#define XGMAC_MTL_TCx_HICREDIT(x) (0x00001120 + 0x80 * (x))
+#define XGMAC_MTL_TCx_LOCREDIT(x) (0x00001124 + 0x80 * (x))
+#define XGMAC_CC BIT(3)
+#define XGMAC_TSA GENMASK(1, 0)
+#define XGMAC_SP (0x0 << 0)
+#define XGMAC_CBS (0x1 << 0)
+#define XGMAC_ETS (0x2 << 0)
+#define XGMAC_MTL_RXQ_OPMODE(x) (0x00001140 + 0x80 * (x))
+#define XGMAC_RQS GENMASK(25, 16)
+#define XGMAC_RQS_SHIFT 16
+#define XGMAC_EHFC BIT(7)
+#define XGMAC_RSF BIT(5)
+#define XGMAC_RTC GENMASK(1, 0)
+#define XGMAC_RTC_SHIFT 0
+#define XGMAC_MTL_RXQ_OVF_CNT(x) (0x00001144 + 0x80 * (x))
+#define XGMAC_MISCNTOVF BIT(31)
+#define XGMAC_MISPKTCNT GENMASK(26, 16)
+#define XGMAC_OVFCNTOVF BIT(15)
+#define XGMAC_OVFPKTCNT GENMASK(10, 0)
+#define XGMAC_MTL_RXQ_DEBUG(x) (0x00001148 + 0x80 * (x))
+#define XGMAC_PRXQ GENMASK(29, 16)
+#define XGMAC_RXQSTS GENMASK(5, 4)
+#define XGMAC_RRCSTS GENMASK(2, 1)
+#define XGMAC_RWCSTS BIT(0)
+#define XGMAC_MTL_RXQ_FLOW_CONTROL(x) (0x00001150 + 0x80 * (x))
+#define XGMAC_RFD GENMASK(31, 17)
+#define XGMAC_RFD_SHIFT 17
+#define XGMAC_RFA GENMASK(15, 1)
+#define XGMAC_RFA_SHIFT 1
+#define XGMAC_MTL_QINTEN(x) (0x00001170 + 0x80 * (x))
+#define XGMAC_RXOIE BIT(16)
+#define XGMAC_MTL_QINT_STATUS(x) (0x00001174 + 0x80 * (x))
+#define XGMAC_RXOVFIS BIT(16)
+#define XGMAC_ABPSIS BIT(1)
+#define XGMAC_TXUNFIS BIT(0)
+#define XGMAC_MAC_REGSIZE (XGMAC_MTL_QINT_STATUS(15) / 4)
+
+#define XGMAC_DMA_MODE 0x00003000
+#define XGMAC_INTM GENMASK(13, 12)
+#define XGMAC_SWR BIT(0)
+#define XGMAC_DMA_SYSBUS_MODE 0x00003004
+#define XGMAC_WR_OSR_LMT GENMASK(29, 24)
+#define XGMAC_WR_OSR_LMT_SHIFT 24
+#define XGMAC_RD_OSR_LMT GENMASK(21, 16)
+#define XGMAC_RD_OSR_LMT_SHIFT 16
+#define XGMAC_EN_LPI BIT(15)
+#define XGMAC_LPI_XIT_PKT BIT(14)
+#define XGMAC_AAL BIT(12)
+#define XGMAC_EAME BIT(11)
+#define XGMAC_BLEN GENMASK(7, 1)
+#define XGMAC_BLEN256 BIT(7)
+#define XGMAC_BLEN128 BIT(6)
+#define XGMAC_BLEN64 BIT(5)
+#define XGMAC_BLEN32 BIT(4)
+#define XGMAC_BLEN16 BIT(3)
+#define XGMAC_BLEN8 BIT(2)
+#define XGMAC_BLEN4 BIT(1)
+#define XGMAC_UNDEF BIT(0)
+#define XGMAC_DMA_INT_STATUS 0x00003008
+#define XGMAC_MTLIS BIT(16)
+#define XGMAC_DMA_DEBUG_STATUS(x) (0x00003020 + 4 * (x))
+#define XGMAC_DMA_DBG_STS3_RDAS BIT(0)
+#define XGMAC_DMA_DBG_STS1_TDAS BIT(0)
+#define XGMAC_TX_EDMA_CTRL 0x00003040
+#define XGMAC_TEDM GENMASK(31, 30)
+#define XGMAC_TDPS GENMASK(29, 0)
+#define XGMAC_RX_EDMA_CTRL 0x00003044
+#define XGMAC_REDM GENMASK(31, 30)
+#define XGMAC_RDPS GENMASK(29, 0)
+#define XGMAC_DMA_TBS_CTRL0 0x00003054
+#define XGMAC_DMA_TBS_CTRL1 0x00003058
+#define XGMAC_DMA_TBS_CTRL2 0x0000305c
+#define XGMAC_DMA_TBS_CTRL3 0x00003060
+#define XGMAC_FTOS GENMASK(31, 8)
+#define XGMAC_FTOV BIT(0)
+#define XGMAC_DEF_FTOS (XGMAC_FTOS | XGMAC_FTOV)
+#define XGMAC_DMA_SAFETY_INT_STATUS 0x00003064
+#define XGMAC_MCSIS BIT(31)
+#define XGMAC_MSUIS BIT(29)
+#define XGMAC_MSCIS BIT(28)
+#define XGMAC_DEUIS BIT(1)
+#define XGMAC_DECIS BIT(0)
+#define XGMAC_DMA_ECC_INT_ENABLE 0x00003068
+#define XGMAC_DCEIE BIT(1)
+#define XGMAC_TCEIE BIT(0)
+#define XGMAC_DMA_ECC_INT_STATUS 0x0000306c
+#define XGMAC_DMA_CH_CONTROL(x) (0x00003100 + 0x80 * (x))
+#define XGMAC_SPH BIT(24)
+#define XGMAC_PBLx8 BIT(16)
+#define XGMAC_DMA_CH_TX_CONTROL(x) (0x00003104 + 0x80 * (x))
+#define XGMAC_EDSE BIT(28)
+#define XGMAC_TxPBL GENMASK(21, 16)
+#define XGMAC_TxPBL_SHIFT 16
+#define XGMAC_TSE BIT(12)
+#define XGMAC_OSP BIT(4)
+#define XGMAC_TXST BIT(0)
+#define XGMAC_DMA_CH_RX_CONTROL(x) (0x00003108 + 0x80 * (x))
+#define XGMAC_RPF BIT(31)
+#define XGMAC_RxPBL GENMASK(21, 16)
+#define XGMAC_RxPBL_SHIFT 16
+#define XGMAC_RBSZ GENMASK(14, 1)
+#define XGMAC_RBSZ_SHIFT 1
+#define XGMAC_RXST BIT(0)
+#define XGMAC_DMA_CH_TxDESC_LADDR(x) (0x00003114 + 0x80 * (x))
+#define XGMAC_DMA_CH_RxDESC_LADDR(x) (0x0000311c + 0x80 * (x))
+#define XGMAC_DMA_CH_TxDESC_TAIL_LPTR(x) (0x00003124 + 0x80 * (x))
+#define XGMAC_DMA_CH_RxDESC_TAIL_LPTR(x) (0x0000312c + 0x80 * (x))
+#define XGMAC_DMA_CH_TxDESC_RING_LEN(x) (0x00003130 + 0x80 * (x))
+#define XGMAC_DMA_CH_RxDESC_RING_LEN(x) (0x00003134 + 0x80 * (x))
+#define XGMAC_OWRQ GENMASK(26, 24)
+#define XGMAC_DMA_CH_INT_EN(x) (0x00003138 + 0x80 * (x))
+#define XGMAC_NIE BIT(15)
+#define XGMAC_AIE BIT(14)
+#define XGMAC_CDEE BIT(13)
+#define XGMAC_FBEE BIT(12)
+#define XGMAC_DDEE BIT(9)
+#define XGMAC_RSE BIT(8)
+#define XGMAC_RBUE BIT(7)
+#define XGMAC_RIE BIT(6)
+#define XGMAC_TBUE BIT(2)
+#define XGMAC_TXSE BIT(1)
+#define XGMAC_TIE BIT(0)
+#define XGMAC_DMA_INT_DEFAULT_EN (XGMAC_DMA_INT_NORMAL_EN | \
+ XGMAC_DMA_INT_ABNORMAL_EN)
+#define XGMAC_DMA_INT_NORMAL_EN (XGMAC_NIE | XGMAC_TIE | XGMAC_RIE)
+#define XGMAC_DMA_INT_ABNORMAL_EN (XGMAC_AIE | XGMAC_RBUE | XGMAC_CDEE | XGMAC_DDEE | XGMAC_FBEE)
+#define XGMAC_DMA_CH_Rx_WATCHDOG(x) (0x0000313c + 0x80 * (x))
+#define XGMAC_PSEL BIT(31)
+#define XGMAC_RBCT GENMASK(25, 16)
+#define XGMAC_RWTU GENMASK(13, 12)
+#define XGMAC_RWT GENMASK(7, 0)
+#define XGMAC_DMA_CH_CUR_TxDESC_LADDR(x) (0x00003144 + 0x80 * (x))
+#define XGMAC_DMA_CH_CUR_RxDESC_LADDR(x) (0x0000314c + 0x80 * (x))
+#define XGMAC_DMA_CH_CUR_TxBUFF_LADDR(x) (0x00003154 + 0x80 * (x))
+#define XGMAC_DMA_CH_CUR_RxBUFF_LADDR(x) (0x0000315c + 0x80 * (x))
+#define XGMAC_DMA_CH_STATUS(x) (0x00003160 + 0x80 * (x))
+#define XGMAC_NIS BIT(15)
+#define XGMAC_AIS BIT(14)
+#define XGMAC_CDE BIT(13)
+#define XGMAC_FBE BIT(12)
+#define XGMAC_DDE BIT(9)
+#define XGMAC_RPS BIT(8)
+#define XGMAC_RBU BIT(7)
+#define XGMAC_RI BIT(6)
+#define XGMAC_TBU BIT(2)
+#define XGMAC_TPS BIT(1)
+#define XGMAC_TI BIT(0)
+#define XGMAC_DMA_CH_DEBUG_STATUS(x) (0x00003164 + 0x80 * (x))
+#define XGMAC_RDTS GENMASK(27, 19)
+#define XGMAC_RDFS GENMASK(18, 16)
+#define XGMAC_TDTS GENMASK(11, 8)
+#define XGMAC_TDRS GENMASK(7, 6)
+#define XGMAC_TDXS GENMASK(5, 3)
+#define XGMAC_TDFS GENMASK(2, 0)
+#define XGMAC_REGSIZE ((0x0000317c + (0x80 * 15)) / 4)
+
+#define XGMAC_DMA_STATUS_MSK_COMMON (XGMAC_NIS | XGMAC_AIS | XGMAC_FBE)
+#define XGMAC_DMA_STATUS_MSK_RX (XGMAC_RBU | XGMAC_RI | \
+ XGMAC_DMA_STATUS_MSK_COMMON)
+#define XGMAC_DMA_STATUS_MSK_TX (XGMAC_TBU | XGMAC_TPS | XGMAC_TI | \
+ XGMAC_DMA_STATUS_MSK_COMMON)
+
+/* Descriptors */
+#define XGMAC_TDES0_LTV BIT(31)
+#define XGMAC_TDES0_LT GENMASK(7, 0)
+#define XGMAC_TDES1_LT GENMASK(31, 8)
+#define XGMAC_TDES2_IVT GENMASK(31, 16)
+#define XGMAC_TDES2_IVT_SHIFT 16
+#define XGMAC_TDES2_IOC BIT(31)
+#define XGMAC_TDES2_TTSE BIT(30)
+#define XGMAC_TDES2_B2L GENMASK(29, 16)
+#define XGMAC_TDES2_B2L_SHIFT 16
+#define XGMAC_TDES2_VTIR GENMASK(15, 14)
+#define XGMAC_TDES2_VTIR_SHIFT 14
+#define XGMAC_TDES2_B1L GENMASK(13, 0)
+#define XGMAC_TDES3_OWN BIT(31)
+#define XGMAC_TDES3_CTXT BIT(30)
+#define XGMAC_TDES3_FD BIT(29)
+#define XGMAC_TDES3_LD BIT(28)
+#define XGMAC_TDES3_CPC GENMASK(27, 26)
+#define XGMAC_TDES3_CPC_SHIFT 26
+#define XGMAC_TDES3_TCMSSV BIT(26)
+#define XGMAC_TDES3_SAIC GENMASK(25, 23)
+#define XGMAC_TDES3_SAIC_SHIFT 23
+#define XGMAC_TDES3_TBSV BIT(24)
+#define XGMAC_TDES3_THL GENMASK(22, 19)
+#define XGMAC_TDES3_THL_SHIFT 19
+#define XGMAC_TDES3_IVTIR GENMASK(19, 18)
+#define XGMAC_TDES3_IVTIR_SHIFT 18
+#define XGMAC_TDES3_TSE BIT(18)
+#define XGMAC_TDES3_IVLTV BIT(17)
+#define XGMAC_TDES3_CIC GENMASK(17, 16)
+#define XGMAC_TDES3_CIC_SHIFT 16
+#define XGMAC_TDES3_TPL GENMASK(17, 0)
+#define XGMAC_TDES3_VLTV BIT(16)
+#define XGMAC_TDES3_VT GENMASK(15, 0)
+#define XGMAC_TDES3_FL GENMASK(14, 0)
+#define XGMAC_TDES3_PIDV BIT(25)
+#define XGMAC_TDES0_QUEUE_ID GENMASK(19, 17)
+#define XGMAC_TDES0_FAST_MODE BIT(16)
+#define XGMAC_TDES0_OVPORT GENMASK(12, 8)
+#define XGMAC_TDES0_IVPORT GENMASK(4, 0)
+
+
+#define XGMAC_RDES0_IP_FRAG GENMASK(31, 30)
+enum {
+ FRAG_NONE,
+ FRAG_FIRST,
+ FRAG_MIDDLE,
+ FRAG_LAST,
+};
+#define XGMAC_RDES0_L3_TYPE GENMASK(29, 28)
+enum {
+ L3_TYPE_IPV4,
+ L3_TYPE_IPV6,
+ L3_TYPE_IPIP6,
+ L3_TYPE_UNKNOWN,
+};
+#define XGMAC_RDES0_L4_TYPE GENMASK(27, 26)
+enum {
+ L4_TYPE_TCP,
+ L4_TYPE_UDP,
+ L4_TYPE_ICMP,
+ L4_TYPE_UNKNOWN,
+};
+#define XGMAC_RDES0_RPT_INDEX GENMASK(25, 22)
+#define XGMAC_RDES0_STA_INDEX GENMASK(21, 12)
+#define XGMAC_RDES0_OVPORT GENMASK(11, 6)
+#define XGMAC_RDES0_IVPORT GENMASK(5, 0)
+#define XGMAC_RDES2_DFRAG BIT(31)
+#define XGMAC_RDES2_OVID GENMASK(27, 16)
+#define XGMAC_RDES3_OWN BIT(31)
+#define XGMAC_RDES3_CTXT BIT(30)
+#define XGMAC_RDES3_FD BIT(29)
+#define XGMAC_RDES3_LD BIT(28)
+#define XGMAC_RDES3_CDA BIT(27)
+#define XGMAC_RDES3_RSV BIT(26)
+#define XGMAC_RDES3_TCI_PRI GENMASK(22, 20)
+#define XGMAC_RDES3_ET GENMASK(19, 16)
+#define XGMAC_RDES3_ES BIT(15)
+#define XGMAC_RDES3_PL GENMASK(13, 0)
+
+#define XGMAC_RDES0_SPORT GENMASK(31, 16)
+#define XGMAC_RDES0_ETH_TYPE GENMASK(15, 0)
+#define XGMAC_RDES1_UP_REASON GENMASK(31, 24)
+#define XGMAC_RDES1_RXHASH GENMASK(23, 8)
+#define XGMAC_RDES1_TNP BIT(6)
+#define XGMAC_RDES1_DSCP GENMASK(5, 0)
+#define XGMAC_RDES2_SMAC_0_31 GENMASK(31, 0)
+#define XGMAC_RDES3_SMAC_32_47 GENMASK(15, 0)
+#define XGMAC_RDES3_PKT_TYPE GENMASK(17, 16)
+enum {
+ PKT_TYPE_UCAST,
+ PKT_TYPE_MCAST,
+ PKT_TYPE_UNKNOWN,
+ PKT_TYPE_BCAST,
+};
+#define XGMAC_RDES3_IOC BIT(30)
+
+#endif
\ No newline at end of file
--- /dev/null
+#include "linux/delay.h"
+#include "linux/reset.h"
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include "dpns.h"
+
+
+
+static int dpns_probe(struct platform_device *pdev)
+{
+ struct dpns_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->dev = &pdev->dev;
+ priv->ioaddr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->ioaddr))
+ return PTR_ERR(priv->ioaddr);
+
+ priv->clk = devm_clk_get_enabled(priv->dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->npu_rst = devm_reset_control_get_exclusive(priv->dev, "npu");
+ if (IS_ERR(priv->npu_rst))
+ return PTR_ERR(priv->npu_rst);
+
+ reset_control_assert(priv->npu_rst);
+ reset_control_deassert(priv->npu_rst);
+
+ ret = dpns_se_init(priv);
+ if (ret)
+ return dev_err_probe(priv->dev, ret, "failed to initialize SE.\n");
+
+ ret = dpns_tmu_init(priv);
+ if (ret)
+ return dev_err_probe(priv->dev, ret, "failed to initialize TMU.\n");
+
+ sf_dpns_debugfs_init(priv);
+ platform_set_drvdata(pdev, priv);
+ return 0;
+}
+
+static int dpns_remove(struct platform_device *pdev) {
+ struct dpns_priv *priv = platform_get_drvdata(pdev);
+ debugfs_remove_recursive(priv->debugfs);
+ reset_control_assert(priv->npu_rst);
+ return 0;
+}
+
+static const struct of_device_id dpns_match[] = {
+ { .compatible = "siflower,sf21-dpns" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dpns_match);
+
+static struct platform_driver dpns_driver = {
+ .probe = dpns_probe,
+ .remove = dpns_remove,
+ .driver = {
+ .name = "sfdpns",
+ .of_match_table = dpns_match,
+ },
+};
+module_platform_driver(dpns_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Qingfang Deng <qingfang.deng@siflower.com.cn>");
+MODULE_DESCRIPTION("NPU stub driver for SF21A6826/SF21H8898 SoC");
\ No newline at end of file
--- /dev/null
+#include <linux/debugfs.h>
+#include "dpns.h"
+#include "dma.h"
+
+static const char * const sf21_dpns_mib_name[] = {
+ "pkt_rcv_drop_port0",
+ "pkt_rcv_drop_port1",
+ "pkt_rcv_drop_port2",
+ "pkt_rcv_drop_port3",
+ "pkt_rcv_drop_port4",
+ "pkt_rcv_drop_port5",
+ "pkt_rcv_drop_port6",
+ "pkt_rcv_drop_spl0",
+ "pkt_rcv_drop_spl1",
+ "pkt_rcv_drop_spl2",
+ "pkt_rcv_drop_spl3",
+ "pkt_rcv_drop_spl4",
+ "pkt_rcv_drop_spl5",
+ "pkt_rcv_drop_spl6",
+ "pkt_rcv_trans_cnt0",
+ "pkt_rcv_trans_cnt1",
+ "pkt_rcv_trans_cnt2",
+ "pkt_rcv_trans_cnt3",
+ "pkt_rcv_trans_cnt4",
+ "pkt_rcv_trans_cnt5",
+ "pkt_rcv_trans_cnt6",
+ "pkt_rcv_total0",
+ "pkt_rcv_total1",
+ "pkt_rcv_total2",
+ "pkt_rcv_total3",
+ "pkt_rcv_total4",
+ "pkt_rcv_total5",
+ "pkt_rcv_total6",
+ "pkt_rcv",
+ "udp",
+ "tcp",
+ "ipv4",
+ "ipv6",
+ "icmpv4",
+ "icmpv6",
+ "other_protocol",
+ "ipv4_sip_eq_dip",
+ "ipv4_icmp_frag",
+ "ipv4_icmp_ping_too_big",
+ "ipv4_udp_sp_eq_dp",
+ "ipv4_tcp_flagchk_err",
+ "ipv4_tcp_sq_eq_dp",
+ "ipv4_tcp_frag_off1",
+ "ipv4_tcp_syn_err",
+ "ipv4_tcp_xmas",
+ "ipv4_tcp_null",
+ "ipv4_tcp_too_short",
+ "ipv4_icmp4_redirect",
+ "ipv4_icmp_smurf",
+ "ipv6_sip_eq_dip",
+ "ipv6_icmp_frag",
+ "ipv6_icmp_ping_too_big",
+ "ipv6_udp_sp_eq_dp",
+ "ipv6_tcp_flagchk_err",
+ "ipv6_tcp_sq_eq_dp",
+ "ipv6_tcp_frag_off1",
+ "ipv6_tcp_syn_err",
+ "ipv6_tcp_xmas",
+ "ipv6_tcp_null",
+ "ipv6_tcp_too_short",
+ "ipv6_icmp4_redirect",
+ "ipv6_icmp_smurf",
+ "ipv4in6_pls",
+ "frame_ismc_pls",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "arp_reply_err_fwd",
+ "arp_req_err_fwd",
+ "pkt_len_less_l2hd_err_fwd",
+ "pkt_len_less_60B_err_fwd",
+ "smac_is_mc_err_fwd",
+ "smac_is_bc_err_fwd",
+ "smac_eq_dmac_err_fwd",
+ "smac_eq_zero_err_fwd",
+ "dmac_eq_zero_err_fwd",
+ "dribble_err_fwd",
+ "runt_err_fwd",
+ "giant_frame_err_fwd",
+ "watchdog_err_fwd",
+ "gmii_err_fwd",
+ "dos_err_fwd",
+ "ttl_err_fwd",
+ "payload_chksum_err_fwd",
+ "ip_version_err_fwd",
+ "ip_hd_chksum_err_fwd",
+ "crc_err_fwd",
+ "pkt_len_err_fwd",
+ "arp_reply_err_up",
+ "arp_req_err_up",
+ "pkt_len_less_l2hd_err_up",
+ "pkt_len_less_60B_err_up",
+ "smac_is_mc_err_up",
+ "smac_is_bc_err_up",
+ "smac_eq_dmac_err_up",
+ "smac_eq_zero_err_up",
+ "dmac_eq_zero_err_up",
+ "dribble_err_up",
+ "runt_err_up",
+ "giant_frame_err_up",
+ "watchdog_err_up",
+ "gmii_err_up",
+ "dos_err_up",
+ "ttl_err_up",
+ "payload_chksum_err_up",
+ "ip_version_err_up",
+ "ip_hd_chksum_err_up",
+ "crc_err_up",
+ "pkt_len_err_up",
+ "arp_reply_err_drop",
+ "arp_req_err_drop",
+ "pkt_len_less_l2hd_err_drop",
+ "pkt_len_less_60B_err_drop",
+ "smac_is_mc_err_drop",
+ "smac_is_bc_err_drop",
+ "smac_eq_dmac_err_drop",
+ "smac_eq_zero_err_drop",
+ "dmac_eq_zero_err_drop",
+ "dribble_err_drop",
+ "runt_err_drop",
+ "giant_frame_err_drop",
+ "watchdog_err_drop",
+ "gmii_err_drop",
+ "dos_err_drop",
+ "ttl_err_drop",
+ "payload_chksum_err_drop",
+ "ip_version_err_drop",
+ "ip_hd_chksum_err_drop",
+ "crc_err_drop",
+ "pkt_len_err_drop",
+ "ivlan_vid_input_mf",
+ "ivlan_vid_pass_mf",
+ "ivlan_vid_port_based_srch",
+ "ivlan_vid_xlt_srch",
+ "ivlan_vid_vfp_srch",
+ "ivlan_vid_port_based_resp",
+ "ivlan_vid_xlt_resp",
+ "ivlan_vid_vfp_resp",
+ "ivlan_vid_port_based_hit",
+ "ivlan_vid_xlt_hit",
+ "ivlan_vid_vfp_hit",
+ "ivlan_vid_output_mf",
+ "ivlan_vid_port_based_pass",
+ "ivlan_vid_cp_drop",
+ "ivlan_vid_cp_up",
+ "ivlan_lkp_input_mf",
+ "ivlan_lkp_pass_mf",
+ "ivlan_lkp_srch",
+ "ivlan_lkp_resp",
+ "ivlan_lkp_hit",
+ "ivlan_lkp_output_mf",
+ "ivlan_lkp_cp_drop",
+ "ivlan_lkp_cp_up",
+ "l2_input_mf",
+ "l2_pass_mf",
+ "l2_flood_spl_srch_cnt",
+ "l2_da_srch",
+ "l2_sa_srch",
+ "l2_flood_spl_resp_cnt",
+ "l2_da_resp",
+ "l2_sa_resp",
+ "l2_flood_spl_cnt",
+ "l2_da_hit",
+ "l2_sa_hit",
+ "l2_output_mf",
+ "l2_cp_drop",
+ "l2_cp_up",
+ "l2_cp_fwd",
+ "l2_cp_up_fwd",
+ "nat_input_mf",
+ "nat_pass_mf",
+ "nat_srch",
+ "nat_resp",
+ "nat_hit",
+ "nat_output_mf",
+ "nat_v4_search",
+ "nat_dnat",
+ "nat_v4_hit",
+ "nat_dnat_hit",
+ "l3_input_mf",
+ "l3_pass_mf",
+ "l3_uc_srch",
+ "l3_mcsg_srch",
+ "l3_uc_resp",
+ "l3_mcsg_resp",
+ "l3_uc_hit",
+ "l3_mcsg_hit",
+ "l3_output_mf",
+ "l3_v6_mf",
+ "l3_mc",
+ "l3_v6_srch",
+ "l3_mc_srch",
+ "l3_v6_hit",
+ "l3_mc_hit",
+ "iacl_input_mf",
+ "iacl_pass_mf",
+ "iacl_srch",
+ "iacl_resp",
+ "iacl_hit",
+ "iacl_output_mf",
+ "iacl_v6",
+ "iacl_v6_srch",
+ "iacl_v6_hit",
+ "tmu_port0_phy_tran",
+ "tmu_port1_phy_tran",
+ "tmu_port2_phy_tran",
+ "tmu_port3_phy_tran",
+ "tmu_port4_phy_tran",
+ "tmu_port5_phy_tran",
+ "tmu_port6_phy_tran",
+ "tmu_port7_phy_tran",
+ "tmu_port8_phy_tran",
+ "tmu_port9_phy_tran",
+ "tmu_port10_phy_tran",
+ "tmu_port11_phy_tran",
+ "tmu_port12_phy_tran",
+ "tmu_port13_phy_tran",
+ "tmu_port14_phy_tran",
+ "tmu_port15_phy_tran",
+ "tmu_port16_phy_tran",
+ "tmu_port17_phy_tran",
+ "tmu_port18_phy_tran",
+ "tmu_port19_phy_tran",
+ "tmu_port20_phy_tran",
+ "tmu_port21_phy_tran",
+ "tmu_port22_phy_tran",
+ "tmu_port23_phy_tran",
+ "tmu_port24_phy_tran",
+ "tmu_port25_phy_tran",
+ "tmu_port26_phy_tran",
+ "tmu_port0_phy_drop_rclm",
+ "tmu_port1_phy_drop_rclm",
+ "tmu_port2_phy_drop_rclm",
+ "tmu_port3_phy_drop_rclm",
+ "tmu_port4_phy_drop_rclm",
+ "tmu_port5_phy_drop_rclm",
+ "tmu_port6_phy_drop_rclm",
+ "tmu_port7_phy_drop_rclm",
+ "tmu_port8_phy_drop_rclm",
+ "tmu_port9_phy_drop_rclm",
+ "tmu_port10_phy_drop_rclm",
+ "tmu_port11_phy_drop_rclm",
+ "tmu_port12_phy_drop_rclm",
+ "tmu_port13_phy_drop_rclm",
+ "tmu_port14_phy_drop_rclm",
+ "tmu_port15_phy_drop_rclm",
+ "tmu_port16_phy_drop_rclm",
+ "tmu_port17_phy_drop_rclm",
+ "tmu_port18_phy_drop_rclm",
+ "tmu_port19_phy_drop_rclm",
+ "tmu_port20_phy_drop_rclm",
+ "tmu_port21_phy_drop_rclm",
+ "tmu_port22_phy_drop_rclm",
+ "tmu_port23_phy_drop_rclm",
+ "tmu_port24_phy_drop_rclm",
+ "tmu_port25_phy_drop_rclm",
+ "tmu_port26_phy_drop_rclm",
+ "tmu_drop_bit_cnt",
+ "nat_cp_drop_cnt",
+ "nat_cp_up_cnt",
+ "nat_fwd_cnt",
+ "nat_cp_fwd_cnt",
+ "l3_cp_up_fwd_cnt",
+ "l3_cp_fwd_cnt",
+ "l3_cp_up_cnt",
+ "l3_drop_bit_cnt",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "arp_intf_input_mf",
+ "arp_intf_pass_mf",
+ "arp_intf_intf_srch",
+ "arp_intf_arp_srch",
+ "arp_intf_intf_resp",
+ "arp_intf_arp_resp",
+ "arp_intf_intf_hit",
+ "arp_intf_arp_hit",
+ "arp_intf_output_mf",
+ "arp_intf_v6_mf",
+ "arp_intf_mc",
+ "arp_intf_v6_srch",
+ "arp_intf_mc_srch",
+ "arp_intf_v6_hit",
+ "arp_intf_mc_hit",
+ "evlan_lkp_input_mf",
+ "evlan_lkp_pass_mf",
+ "evlan_lkp_port_tpid_srch",
+ "evlan_lkp_tag_mem_srch",
+ "evlan_lkp_vlan_srch",
+ "evlan_lkp_port_tpid_resp",
+ "evlan_lkp_tag_mem_resp",
+ "evlan_lkp_vlan_resp",
+ "evlan_lkp_port_tpid_hit",
+ "evlan_lkp_tag_mem_hit",
+ "evlan_lkp_vlan_hit",
+ "evlan_lkp_output_mf",
+ "evlan_lkp_cp_drop",
+ "evlan_lkp_cp_up",
+ "evlan_lkp_cp_fwd",
+ "evlan_act_input_mf",
+ "evlan_act_pass_mf",
+ "evlan_act_srch",
+ "evlan_xlt_srch_cnt",
+ "evlan_act_resp",
+ "evlan_xlt_resp_hit",
+ NULL,
+ "evlan_xlt_hit_cnt",
+ "evlan_act_output_mf",
+ "evlan_act_cp_drop",
+ "evlan_act_cp_cpu",
+ "eacl_input_mf",
+ "eacl_pass_mf",
+ "eacl_srch",
+ "eacl_resp",
+ "eacl_hit",
+ "eacl_output_mf",
+ "eacl_v6",
+ "eacl_v6_srch",
+ "eacl_v6_hit",
+ "md2port_0_data_sof",
+ "md2port_0_data_eof",
+ "md2port_1_data_sof",
+ "md2port_1_data_eof",
+ "md2port_2_data_sof",
+ "md2port_2_data_eof",
+ "md2port_3_data_sof",
+ "md2port_3_data_eof",
+ "md2port_4_data_sof",
+ "md2port_4_data_eof",
+ "md2port_5_data_sof",
+ "md2port_5_data_eof",
+ "md2port_6_data_sof",
+ "md2port_6_data_eof",
+ "pkt_separate_free_cnt",
+ "pkt_whold_free_cnt",
+ "se2md_result_cnt",
+ "md2se_key_cnt",
+ "mem2md_data_cnt",
+ "md2mem_rd_cnt",
+ "modify_drop_cnt",
+ "mipp_cnt[0]",
+ "mipp_cnt[1]",
+ "ipv6_hdr_add",
+ "ipv6_hdr_del",
+ "otpid_replace",
+ "itpid_replace",
+ "ppp_hdr_add",
+ "ppp_hdr_del",
+ "avlan_replace",
+ "avlan_add",
+ "avlan_del",
+ "ovlan_replace",
+ "ovlan_add",
+ "ovlan_del",
+ "ivlan_replace",
+ "ivlan_add",
+ "ivlan_del",
+};
+
+static int
+sf_dpns_mib_show(struct seq_file *m, void *private)
+{
+ struct dpns_priv *priv = m->private;
+ u64 bytes;
+ u32 count;
+ int i;
+ seq_printf(m, "General MIBs:\n");
+ for (i = 0; i < ARRAY_SIZE(sf21_dpns_mib_name); i++) {
+ if (!sf21_dpns_mib_name[i])
+ continue;
+ count = dpns_r32(priv, NPU_MIB(i));
+ seq_printf(m, "name:%-30s packets:%11u\n",
+ sf21_dpns_mib_name[i], count);
+ }
+ seq_printf(m, "Port MIBs:\n");
+ for (i = 0; i < DPNS_MAX_PORT; i++) {
+ count = dpns_r32(priv, NPU_MIB_PKT_RCV_PORT(i));
+ bytes = dpns_r32(priv, NPU_MIB_NCI_RD_DATA2) |
+ (u64)dpns_r32(priv, NPU_MIB_NCI_RD_DATA3) << 32;
+ seq_printf(m,
+ "name:pkt_rcv_port%-18u packets:%11u bytes:%20llu\n",
+ i, count, bytes);
+ }
+ return 0;
+}
+
+static int sf_dpns_mib_open(struct inode *inode, struct file *file)
+{
+ return single_open_size(file, sf_dpns_mib_show, inode->i_private,
+ 56 * (ARRAY_SIZE(sf21_dpns_mib_name) + 1) +
+ 83 * (DPNS_MAX_PORT + 1));
+}
+
+static const struct file_operations sf_dpns_mib_fops = {
+ .owner = THIS_MODULE,
+ .open = sf_dpns_mib_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void sf_dpns_debugfs_init(struct dpns_priv *priv)
+{
+ priv->debugfs = debugfs_create_dir(dev_name(priv->dev), NULL);
+ debugfs_create_file("mib", S_IRUSR, priv->debugfs, priv, &sf_dpns_mib_fops);
+}
--- /dev/null
+#include "dpns.h"
+#include <linux/iopoll.h>
+#include <linux/bitfield.h>
+#include "sf_dpns_se.h"
+
+static int dpns_populate_table(struct dpns_priv *priv)
+{
+ void __iomem *ioaddr = priv->ioaddr;
+ int ret, i;
+ u32 reg;
+
+ dpns_rmw(priv, SE_CONFIG0, SE_IPSPL_ZERO_LIMIT,
+ SE_IPORT_TABLE_VALID);
+ dpns_w32(priv, SE_TB_WRDATA(0), 0xa0000);
+ for (i = 0; i < 6; i++) {
+ reg = SE_TB_OP_WR | FIELD_PREP(SE_TB_OP_REQ_ADDR, i) |
+ FIELD_PREP(SE_TB_OP_REQ_ID, SE_TB_IPORT);
+ dpns_w32(priv, SE_TB_OP, reg);
+ ret = readl_poll_timeout(ioaddr + SE_TB_OP, reg,
+ !(reg & SE_TB_OP_BUSY), 0, 100);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int dpns_se_init(struct dpns_priv *priv) {
+ int ret;
+ u32 reg;
+
+ dpns_w32(priv, SE_CLR_RAM_CTRL, SE_CLR_RAM_ALL);
+ dpns_w32(priv, SE_TCAM_CLR, SE_TCAM_CLR);
+
+ ret = readl_poll_timeout(priv->ioaddr + SE_CLR_RAM_CTRL, reg, !reg, 0,
+ 1000);
+ if (ret)
+ return ret;
+ ret = readl_poll_timeout(priv->ioaddr + SE_TCAM_CLR, reg, !reg, 0,
+ 1000);
+ if (ret)
+ return ret;
+
+ /* Upload ARP packets which NPU considers invalid to host. */
+ dpns_rmw(priv, PKT_ERR_STG_CFG2, ARP_REQ_ERR_OP | ARP_REPLY_ERR_OP,
+ ARP_REQ_ERR_UP | ARP_REPLY_ERR_UP);
+
+ ret = dpns_populate_table(priv);
+ return ret;
+}
\ No newline at end of file
--- /dev/null
+#ifndef __SF_DPNS_SE_H__
+#define __SF_DPNS_SE_H__
+#include "dpns.h"
+
+#define SE_INT_STATUS 0x180000
+#define SE_INT_EVACT_SCH_OVF BIT(14)
+#define SE_INT_L2_MF_SPL_OVF BIT(13)
+#define SE_INT_L2_MP_SCH_OVF BIT(12)
+#define SE_INT_MODIFY_OVF BIT(11)
+#define SE_INT_EVXLT_LKP_OVF BIT(10)
+#define SE_INT_EVLAN_LKP_OVF BIT(9)
+#define SE_INT_EVSCH_OVF BIT(8)
+#define SE_INT_MACSCH_OVF BIT(7)
+#define SE_INT_MSPLS_OVF BIT(6)
+#define SE_INT_MACSPL_LKP_OVF BIT(5)
+#define SE_INT_L2_LKP_BUF_OVF BIT(4)
+#define SE_INT_L2_LKP_SCH_OVF BIT(3)
+#define SE_INT_IVXLT_OVF BIT(2)
+#define SE_INT_IVLKP_OVF BIT(1)
+#define SE_INT_IVPSPL_LKP_OVF BIT(0)
+
+#define SE_CLR_RAM_CTRL 0x180004
+#define SE_CLR_RAM_ALL GENMASK(20, 0)
+
+#define SE_CONFIG0 0x180008
+#define SE_L2_VID_ZERO_MODE BIT(27)
+#define SE_IPSPL_DIS_STEP BIT(26)
+#define SE_IPSPL_CMPT_LEN GENMASK(25, 20)
+#define SE_IPSPL_ZERO_LIMIT BIT(19)
+#define SE_IPSPL_CNT_MODE GENMASK(18, 17)
+#define SE_IVLKP_CFG_DISABLE BIT(16)
+#define SE_IVLKP_CFG_ENTR_MINUS1 GENMASK(15, 10)
+#define SE_PORTBV_TABLE_VALID BIT(9)
+#define SE_IPORT_TABLE_VALID BIT(8)
+#define SE_IVXLT_CFG_DISABLE BIT(7)
+#define SE_IVXLT_CFG_ENTR_MINUS1 GENMASK(6, 1)
+#define SE_IPSPL_MODE BIT(0)
+
+#define SE_CONFIG1 0x18000c
+#define SE_UNUC_SPL_CNT_MODE BIT(30)
+#define SE_L2_HASH_POLY_SEL(x) GENMASK((x) * 3 + 2, (x) * 3)
+
+#define SE_CONFIG2 0x180010
+#define SE_EVACT_TABLE_VALID BIT(31)
+#define SE_EVXLT_CFG_DIS BIT(30)
+#define SE_EVXLT_CFG_ENTR_MINUS1 GENMASK(29, 24)
+#define SE_L2_MFSPL_ZERO_LIMIT BIT(23)
+#define SE_L2_MFSPL_CNT_MODE GENMASK(22, 21)
+#define SE_MFSPL_MODE BIT(20)
+#define SE_MACSPL_ZERO_LIMIT BIT(19)
+#define SE_MACSPL_CNT_MODE GENMASK(18, 17)
+#define SE_PTPID_TABLE_VALID BIT(16)
+#define SE_OTPID_TABLE_VALID BIT(15)
+#define SE_EVLKP_CFG_DIS_TB BIT(14)
+#define SE_EVLKP_CFG_ENTR_MINUS1 GENMASK(13, 8)
+#define SE_INTF_TABLE_VALID BIT(7)
+#define SE_MAC_TABLE_VALID BIT(6)
+#define SE_MACSPL_MODE BIT(5)
+#define SE_L2_AGE_CLR_AFTER_RD BIT(4)
+#define SE_L2_SEG_NUM_MINUS1 GENMASK(3, 0)
+
+#define SE_TB_OP 0x18003c
+#define SE_TB_OP_BUSY BIT(31)
+#define SE_TB_OP_WR BIT(24)
+#define SE_TB_OP_REQ_ID GENMASK(21, 16)
+#define SE_TB_IPORT 1
+#define SE_TB_OP_REQ_ADDR GENMASK(15, 0)
+
+#define SE_TB_WRDATA(x) (0x180040 + 4 * (x))
+#define SE_TB_RDDATA(x) (0x180080 + 4 * (x))
+
+#define SE_TCAM_CLR 0x190004
+#define SE_TCAM_CLR_ALL GENMASK(5, 0)
+#define SE_TCAM_CLR_ACL_SPL BIT(5)
+#define SE_TCAM_CLR_BLK(x) BIT(x)
+
+#endif
\ No newline at end of file
--- /dev/null
+#include <linux/of_device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include "dpns.h"
+#include "sf_dpns_tmu.h"
+
+static u32 tmu_rm32(struct dpns_priv *priv, u32 reg, u32 mask, u32 shift)
+{
+ u32 t;
+
+ t = dpns_r32(priv, reg);
+ t &= mask;
+ t >>= shift;
+
+ return t;
+}
+
+static void tmu_rmw32(struct dpns_priv *priv, u32 reg, u32 mask, u32 shift, u32 val)
+{
+ u32 t;
+
+ val <<= shift;
+ val &= mask;
+ t = dpns_r32(priv, reg);
+ t &= ~mask;
+ t |= val;
+ dpns_w32(priv, reg, t);
+}
+
+static int is_valid_port_idx(struct dpns_priv *priv, u32 port)
+{
+ if (port >= TMU_MAX_PORT_CNT)
+ return 0;
+
+ return 1;
+}
+
+static int is_valid_queue_idx(u32 q)
+{
+ if (q >= QUE_MAX_NUM_PER_PORT)
+ return 0;
+
+ return 1;
+}
+
+static int is_valid_sched_idx(struct dpns_priv *priv, u32 sched)
+{
+ if (sched >= QUE_SCH_NUM_PER_PORT)
+ return 0;
+
+ return 1;
+}
+
+static int is_valid_shaper_idx(struct dpns_priv *priv, u32 shaper)
+{
+ if (shaper >= QUE_SHAPER_NUM_PER_PORT)
+ return 0;
+
+ return 1;
+}
+
+static int tmu_port_writel(struct dpns_priv *priv, u32 port, u32 reg, u32 val)
+{
+ if (!is_valid_port_idx(priv, port))
+ return -EINVAL;
+
+ dpns_w32(priv, TMU_PORT_BASE(port) + reg, val);
+ return 0;
+}
+
+static int tmu_port_rm32(struct dpns_priv *priv, u32 port, u32 reg, u32 mask, u32 shift, u32 *val)
+{
+ if (!is_valid_port_idx(priv, port))
+ return -EINVAL;
+
+ *val = tmu_rm32(priv, TMU_PORT_BASE(port) + reg, mask, shift);
+ return 0;
+}
+
+static int tmu_port_rmw32(struct dpns_priv *priv, u32 port, u32 reg, u32 mask, u32 shift, u32 val)
+{
+ if (!is_valid_port_idx(priv, port))
+ return -EINVAL;
+
+ tmu_rmw32(priv, TMU_PORT_BASE(port) + reg, mask, shift, val);
+
+ return 0;
+}
+
+static int tmu_queue_writel(struct dpns_priv *priv, u32 port, u32 queue, u32 reg, u32 val)
+{
+ if (!is_valid_queue_idx(queue))
+ return -EINVAL;
+
+ return tmu_port_writel(priv, port, TMU_QUEUE_BASE(queue) + reg, val);
+}
+
+static int tmu_sched_writel(struct dpns_priv *priv, u32 port, u32 sched, u32 reg, u32 val)
+{
+ if (!is_valid_sched_idx(priv, sched))
+ return -EINVAL;
+
+ return tmu_port_writel(priv, port, TMU_SCHED_BASE(sched) + reg, val);
+}
+
+static int tmu_shaper_writel(struct dpns_priv *priv, u32 port, u32 shaper, u32 reg, u32 val)
+{
+ if (!is_valid_shaper_idx(priv, shaper))
+ return -EINVAL;
+
+ return tmu_port_writel(priv, port, TMU_SHAPER_BASE(shaper) + reg, val);
+}
+
+static int tmu_shaper_rmw32(struct dpns_priv *priv, u32 port, u32 shaper, u32 reg, u32 mask, u32 shift, u32 val)
+{
+ if (!is_valid_shaper_idx(priv, shaper))
+ return -EINVAL;
+
+ return tmu_port_rmw32(priv, port, TMU_SHAPER_BASE(shaper) + reg, mask, shift, val);
+}
+
+static int tdq_ctrl_is_configurable(struct dpns_priv *priv, u32 port)
+{
+ u32 val = 0;
+ int err;
+
+ if ((err = tmu_port_rm32(priv, port,
+ TMU_TDQ_CTRL,
+ TMU_TDQ_ALLOW_CFG,
+ TMU_TDQ_ALLOW_CFG_SHIFT,
+ &val))) {
+ return 0;
+ }
+
+ return val;
+}
+
+static void tmu_port_queue_cfg(struct dpns_priv *priv, u32 port)
+{
+ int comp;
+
+ for (comp = 0; comp < QUE_MAX_NUM_PER_PORT; comp++) {
+ tmu_queue_writel(priv, port, comp, TMU_PORT_QUEUE_CFG0, 0x00011f00);
+
+ tmu_queue_writel(priv, port, comp, TMU_PORT_QUEUE_CFG1, 0x00000000);
+ tmu_queue_writel(priv, port, comp, TMU_PORT_QUEUE_CFG2, 0x00000000);
+ tmu_queue_writel(priv, port, comp, TMU_PORT_QUEUE_STS0, 0x00000000);
+ tmu_queue_writel(priv, port, comp, TMU_PORT_QUEUE_STS1, 0x00000000);
+ tmu_queue_writel(priv, port, comp, TMU_PORT_QUEUE_STS2, 0x00000000);
+ tmu_queue_writel(priv, port, comp, TMU_PORT_QUEUE_CFG3, 0x000005ee);
+ }
+}
+
+static void tmu_port_sched_cfg(struct dpns_priv *priv, u32 port)
+{
+ int comp;
+ for (comp = 0; comp < QUE_SCH_NUM_PER_PORT; comp++) {
+ tmu_sched_writel(priv, port, comp, TMU_SCH_CTRL, 0x00000000);
+ tmu_sched_writel(priv, port, comp, TMU_SCH_Q0_WEIGHT, 0x00000000);
+ tmu_sched_writel(priv, port, comp, TMU_SCH_Q1_WEIGHT, 0x00000000);
+ tmu_sched_writel(priv, port, comp, TMU_SCH_Q2_WEIGHT, 0x00000000);
+ tmu_sched_writel(priv, port, comp, TMU_SCH_Q3_WEIGHT, 0x00000000);
+ tmu_sched_writel(priv, port, comp, TMU_SCH_Q4_WEIGHT, 0x00000000);
+ tmu_sched_writel(priv, port, comp, TMU_SCH_Q5_WEIGHT, 0x00000000);
+ tmu_sched_writel(priv, port, comp, TMU_SCH_Q6_WEIGHT, 0x00000000);
+ tmu_sched_writel(priv, port, comp, TMU_SCH_Q7_WEIGHT, 0x00000000);
+
+ switch (comp) {
+ case 0:
+ tmu_sched_writel(priv, port, comp, TMU_SCH_QUEUE_ALLOC0, 0x03020100);
+ tmu_sched_writel(priv, port, comp, TMU_SCH_QUEUE_ALLOC1, 0x08080808);
+ break;
+
+ case 1:
+ tmu_sched_writel(priv, port, comp, TMU_SCH_QUEUE_ALLOC0, 0x06050400);
+ tmu_sched_writel(priv, port, comp, TMU_SCH_QUEUE_ALLOC1, 0x08080807);
+ break;
+
+ default:
+ break;
+ }
+
+ tmu_sched_writel(priv, port, comp, TMU_SCH_BIT_RATE, 0x00000000);
+
+ if (comp == 0)
+ tmu_sched_writel(priv, port, comp, TMU_SCH0_POS, 0x00000000);
+ }
+}
+
+static void tmu_port_shaper_cfg(struct dpns_priv *priv, u32 port)
+{
+ int comp;
+ for (comp = 0; comp < QUE_SHAPER_NUM_PER_PORT; comp++) {
+ tmu_shaper_writel(priv, port, comp, TMU_SHP_CTRL, 0x00000000);
+ tmu_shaper_writel(priv, port, comp, TMU_SHP_WEIGHT, 0x00000000);
+ tmu_shaper_writel(priv, port, comp, TMU_SHP_CTRL2, 0x00000000);
+ tmu_shaper_writel(priv, port, comp, TMU_SHP_MIN_CREDIT, 0x0003ff00);
+ tmu_shaper_writel(priv, port, comp, TMU_SHP_MAX_CREDIT, 0x00000400);
+ tmu_shaper_rmw32(priv, port, comp, TMU_SHP_CTRL2, TMU_SHP_POS, TMU_SHP_POS_SHIFT, comp);
+ }
+}
+
+static void _tmu_reset(struct dpns_priv *priv, u32 port)
+{
+ tmu_port_queue_cfg(priv, port);
+ tmu_port_sched_cfg(priv, port);
+ tmu_port_shaper_cfg(priv, port);
+
+ // Cause tmu shaper rate limit not include pkt preamble(8byte)/IFG(12byte)/FCS(4Byte)
+ // so config 24 byte here
+ tmu_port_writel(priv, port, TMU_TDQ_IFG, 0x00000018);
+
+ if (tdq_ctrl_is_configurable(priv, port))
+ tmu_port_writel(priv, port, TMU_TDQ_CTRL, 0x0000002f);
+}
+
+static int tmu_reset(struct dpns_priv *priv)
+{
+ int port;
+
+ dpns_w32(priv, TMU_CTRL, 0x00000006);
+ dpns_w32(priv, TMU_LLM_FIFO_CTRL0, 0x07fe07ff);
+ dpns_w32(priv, TMU_LLM_FIFO_CTRL1, 0x00280024);
+
+ for (port = 0; port < TMU_MAX_PORT_CNT; port++) {
+ _tmu_reset(priv, port);
+ }
+
+ return 0;
+}
+
+int dpns_tmu_init(struct dpns_priv *priv)
+{
+ int err;
+
+ if ((err = tmu_reset(priv)) != 0)
+ return err;
+
+ return err;
+}
--- /dev/null
+#ifndef __SF_TMU_H__
+#define __SF_TMU_H__
+#include <linux/bitfield.h>
+// npu clk is 400MHz in mpw, will change to 600MHz in fullmask
+// TODO: should use mpw define to diff
+#define LIF_SHP_CLKDIV_DEF (3)
+#define LIF_WEIGHT_MAX (0x7ff)
+#define TMU_SHP_CLKDIV_DEF (6)
+#define TMU_SHP_CLKDIV_MAX (15)
+#define TMU_WEIGHT_MAX (256)
+#define TMU_SHP_INT_WGHT_MAX ((TMU_SHP_WEIGHT_INT_MASK) >> TMU_SHP_WEIGHT_INT_SHIFT)
+#define TMU_SHP_FRAC_WGHT_MAX ((TMU_SHP_WEIGHT_FRAC_MASK) >> TMU_SHP_WEIGHT_FRAC_SHIFT)
+
+#define TMU_VERSION_INFO 0x148000
+#define TMU_ID_SHIFT 0
+#define TMU_ID GENMASK(15, 0)
+#define TMU_VERSION_SHIFT 16
+#define TMU_VERSION GENMASK(23, 16)
+#define TMU_REVISION_SHIFT 24
+#define TMU_REVISION GENMASK(31, 24)
+
+#define TMU_CTRL 0x148004
+#define TMU_MF_IN_CNT_EN_SHIFT 1
+#define TMU_MF_IN_CNT_EN BIT(1)
+#define TMU_MD_RDY_EN_SHIFT 2
+#define TMU_MD_RDY_EN BIT(2)
+#define TMU_DEBUG_SEL_SHIFT 3
+#define TMU_DEBUG_SEL GENMASK(8, 3)
+
+#define TMU_LLM_FIFO_CTRL0 0x148008
+#define TMU_LLM_FIFO_FULL_ASSERT_SHIFT 0
+#define TMU_LLM_FIFO_FULL_ASSERT GENMASK(11, 0)
+#define TMU_LLM_FIFO_FULL_NEGATE_SHIFT 16
+#define TMU_LLM_FIFO_FULL_NEGATE GENMASK(27, 16)
+
+#define TMU_LLM_FIFO_CTRL1 0x14800c
+#define TMU_LLM_FIFO_EMPTY_ASSERT_SHIFT 0
+#define TMU_LLM_FIFO_EMPTY_ASSERT GENMASK(11, 0)
+#define TMU_LLM_FIFO_EMPTY_NEGATE_SHIFT 16
+#define TMU_LLM_FIFO_EMPTY_NEGATE GENMASK(27, 16)
+
+#define TMU_RD_CLR_EN 0x2800c0
+#define TMU_BUF_THR0 0x2800d8
+
+/* 36 ports in registers */
+#define TMU_PORT0 0x0000
+
+#define TMU_PORT_SZ 0x2000
+#define TMU_PORT_CNT 36
+#define TMU_PORT_CNT_V1 10
+
+/* 8 queues for each port */
+#define TMU_PORT_QUEUE0 0x100000
+
+#define TMU_PORT_QUEUE_SZ 0x20
+#define TMU_PORT_QUEUE_CNT 8
+
+#define TMU_PORT_QUEUE_CFG0 0x00
+/* 0x00: mix tail drop
+ * 0x01: tail drop (default)
+ * 0x02: WRED
+ * 0x03: buf count tail drop
+ */
+#define TMU_DROP_TYPE_SHIFT 0
+#define TMU_DROP_TYPE GENMASK(1, 0)
+#define TMU_QUEUE_MAX_SHIFT 8
+#define TMU_QUEUE_MAX GENMASK(18, 8)
+#define TMU_QUEUE_MIN_SHIFT 20
+#define TMU_QUEUE_MIN GENMASK(30, 20) // related to WRED
+
+#define TMU_QUEUE_MIX_TAIL_DROP 0x00
+#define TMU_QUEUE_TAIL_DROP 0x01
+#define TMU_QUEUE_WRED 0x02
+#define TMU_QUEUE_BUF_CNT_TAIL_DROP 0x03
+
+/* drop probability of each stage in WRED */
+#define TMU_PORT_QUEUE_CFG1 0x04
+#define TMU_WRED_HW_PROB_STG0_SHIFT 0
+#define TMU_WRED_HW_PROB_STG0 GENMASK(4, 0)
+#define TMU_WRED_HW_PROB_STG1_SHIFT 5
+#define TMU_WRED_HW_PROB_STG1 GENMASK(9, 5)
+#define TMU_WRED_HW_PROB_STG2_SHIFT 10
+#define TMU_WRED_HW_PROB_STG2 GENMASK(14, 10)
+#define TMU_WRED_HW_PROB_STG3_SHIFT 15
+#define TMU_WRED_HW_PROB_STG3 GENMASK(19, 15)
+#define TMU_WRED_HW_PROB_STG4_SHIFT 20
+#define TMU_WRED_HW_PROB_STG4 GENMASK(24, 20)
+#define TMU_WRED_HW_PROB_STG5_SHIFT 25
+#define TMU_WRED_HW_PROB_STG5 GENMASK(29, 25)
+
+#define TMU_PORT_QUEUE_CFG2 0x08
+#define TMU_WRED_HW_PROB_STG6_SHIFT 0
+#define TMU_WRED_HW_PROB_STG6 GENMASK(4, 0)
+#define TMU_WRED_HW_PROB_STG7_SHIFT 5
+#define TMU_WRED_HW_PROB_STG7 GENMASK(9, 5)
+
+#define TMU_WRED_PROB_CNT 8
+
+/* RO */
+#define TMU_PORT_QUEUE_STS0 0x0c
+#define TMU_QUEUE_HEAD_PTR_SHIFT 0
+#define TMU_QUEUE_HEAD_PTR GENMASK(10, 0)
+#define TMU_QUEUE_TAIL_PTR_SHIFT 16
+#define TMU_QUEUE_TAIL_PTR GENMASK(26, 16)
+
+/* RO */
+#define TMU_PORT_QUEUE_STS1 0x10
+#define TMU_QUEUE_PKT_CNT_SHIFT 0
+#define TMU_QUEUE_PKT_CNT GENMASK(11, 0)
+
+/* RO */
+#define TMU_PORT_QUEUE_STS2 0x14
+#define TMU_QUEUE_BUF_CNT_SHIFT 0
+#define TMU_QUEUE_BUF_CNT GENMASK(11, 0)
+
+/* max buffer cell of queue */
+#define TMU_PORT_QUEUE_CFG3 0x18
+#define TMU_QUEUE_BUF_MAX_SHIFT 0
+#define TMU_QUEUE_BUF_MAX GENMASK(11, 0)
+
+/* 2 schedulers (dequeuing) for each port */
+#define TMU_SCH0 0x101000
+#define TMU_SCH1 0x101040
+
+#define TMU_SCH_SZ 0x40
+#define TMU_SCH_CNT 2
+
+#define TMU_SCH_CTRL 0x00
+#define TMU_SCH_ALGO_SHIFT 0
+#define TMU_SCH_ALGO GENMASK(6, 0)
+
+/* TMU_SCH_ALGO */
+#define TMU_SCH_PQ 0x00
+#define TMU_SCH_WFQ 0x01
+#define TMU_SCH_DWRR 0x02
+#define TMU_SCH_RR 0x03
+#define TMU_SCH_WRR 0x04
+
+#define TMU_SCH_Q0_WEIGHT 0x10
+#define TMU_SCH_Q1_WEIGHT 0x14
+#define TMU_SCH_Q2_WEIGHT 0x18
+#define TMU_SCH_Q3_WEIGHT 0x1c
+#define TMU_SCH_Q4_WEIGHT 0x20
+#define TMU_SCH_Q5_WEIGHT 0x24
+#define TMU_SCH_Q6_WEIGHT 0x28
+#define TMU_SCH_Q7_WEIGHT 0x2c
+#define TMU_SCH_Q_WEIGHT_SZ 4
+
+#define TMU_SCH_Q_WEIGHT_CNT 8
+
+/* TMU_SCH_Qn_WEIGHT */
+#define TMU_SCH_QUEUE_WEIGHT_SHIFT 0
+#define TMU_SCH_QUEUE_WEIGHT GENMASK(31, 0)
+
+/* port queue and scheduler selection */
+#define TMU_SCH_QUEUE_ALLOC0 0x30
+#define TMU_SCH_Q0_ALLOC_SHIFT 0
+#define TMU_SCH_Q0_ALLOC GENMASK(3, 0)
+#define TMU_SCH_Q1_ALLOC_SHIFT 8
+#define TMU_SCH_Q1_ALLOC GENMASK(11, 8)
+#define TMU_SCH_Q2_ALLOC_SHIFT 16
+#define TMU_SCH_Q2_ALLOC GENMASK(19, 16)
+#define TMU_SCH_Q3_ALLOC_SHIFT 24
+#define TMU_SCH_Q3_ALLOC GENMASK(27, 24)
+
+#define TMU_SCH_QUEUE_ALLOC1 0x34
+#define TMU_SCH_Q4_ALLOC_SHIFT 0
+#define TMU_SCH_Q4_ALLOC GENMASK(3, 0)
+#define TMU_SCH_Q5_ALLOC_SHIFT 8
+#define TMU_SCH_Q5_ALLOC GENMASK(11, 8)
+#define TMU_SCH_Q6_ALLOC_SHIFT 16
+#define TMU_SCH_Q6_ALLOC GENMASK(19, 16)
+#define TMU_SCH_Q7_ALLOC_SHIFT 24
+#define TMU_SCH_Q7_ALLOC GENMASK(27, 24)
+
+#define TMU_SCH_Q_ALLOC_CNT 8
+
+// schedule by pkt_len or pkt_cnt
+#define TMU_SCH_BIT_RATE 0x38
+#define TMU_SCH_BIT_RATE_SHIFT 0
+#define TMU_SCH_BIT_RATE_MASK GENMASK(31, 0)
+#define TMU_SCH_BIT_RATE_PKT_LEN 0x00
+#define TMU_SCH_BIT_RATE_PKT_CNT 0x01
+
+// RW
+// SCH0 Only, to select how to connect to SCH1
+#define TMU_SCH0_POS 0x3c
+#define TMU_SCH0_POS_SHIFT 0
+#define TMU_SCH0_POS_MASK GENMASK(3, 0)
+
+/* 5 shapers for each port */
+#define TMU_SHP0 0x101080
+
+#define TMU_SHP_SZ 0x0020
+#define TMU_SHP_CNT 6
+
+#define TMU_SHP_CTRL 0x00
+#define TMU_SHP_EN_SHIFT 0
+#define TMU_SHP_EN BIT(0)
+#define TMU_SHP_CLK_DIV_SHIFT 1
+#define TMU_SHP_CLK_DIV GENMASK(31, 1)
+
+/* byte size of per token (credit) */
+#define TMU_SHP_WEIGHT 0x04
+#define TMU_SHP_WEIGHT_FRAC_SHIFT 0
+#define TMU_SHP_WEIGHT_FRAC_MASK GENMASK(11, 0)
+#define TMU_SHP_WEIGHT_INT_SHIFT 12
+#define TMU_SHP_WEIGHT_INT_MASK GENMASK(19, 12)
+
+#define TMU_SHP_MAX_CREDIT 0x08
+#define TMU_SHP_MAX_CREDIT_SHIFT 10
+#define TMU_SHP_MAX_CREDIT_MASK GENMASK(31, 10)
+
+// (fraction part num) = (register fraction part) / (2 ^ 12)
+#define TMU_SHP_FRAC_WEIGHT_2DBL(reg) (((double)(reg)) / (1 << 12))
+#define TMU_SHP_DBL_2FRAC_WEIGHT(dbl) (((double)(dbl)) * (1 << 12))
+
+#define TMU_SHP_CTRL2 0x0c
+#define TMU_SHP_BIT_RATE_SHIFT 0
+#define TMU_SHP_BIT_RATE BIT(0)
+#define TMU_SHP_POS_SHIFT 1
+#define TMU_SHP_POS GENMASK(5, 1) // RW
+#define TMU_SHP_MODE_SHIFT 6
+#define TMU_SHP_MODE BIT(6)
+
+/* TMU_SHP_BIT_RATE */
+#define TMU_SHP_SCHED_PKT_LEN 0
+#define TMU_SHP_SCHED_PKT_CNT 1
+
+/* TMU_SHP_MODE */
+#define TMU_SHP_MODE_KEEP_CREDIT 0
+#define TMU_SHP_MODE_CLEAR_CREDIT 1
+
+#define TMU_SHP_MIN_CREDIT 0x10
+#define TMU_SHP_MIN_CREDIT_SHIFT 0
+#define TMU_SHP_MIN_CREDIT_MASK GENMASK(21, 0)
+
+/* RO */
+#define TMU_SHP_STATUS 0x14
+#define TMU_SHP_CURR_STATUS_SHIFT 0 // shaper is working or not
+#define TMU_SHP_CURR_STATUS BIT(0)
+#define TMU_SHP_CREDIT_CNTR_SHIFT 1
+#define TMU_SHP_CREDIT_CNTR GENMASK(23, 1)
+
+/* dequeue stage configs */
+#define TMU_TDQ 0x101140
+
+/* effective only when TMU_SCH_BIT_RATE is PKT_LEN mode.
+ * ifg value can be used to adjust packet length of scheduler.
+ */
+#define TMU_TDQ_IFG (TMU_TDQ + 0x00)
+#define TMU_TDQ_IIF_CFG_SHIFT 0
+#define TMU_TDQ_IIF_CFG GENMASK(7, 0)
+
+#define TMU_TDQ_CTRL (TMU_TDQ + 0x04)
+#define TMU_SHP_CLK_CNT_EN_SHIFT 0
+#define TMU_SHP_CLK_CNT_EN BIT(0)
+#define TMU_TDQ_HW_EN_SHIFT 1
+#define TMU_TDQ_HW_EN BIT(1)
+#define TMU_SCH0_EN_SHIFT 2
+#define TMU_SCH0_EN BIT(2)
+#define TMU_SCH1_EN_SHIFT 3
+#define TMU_SCH1_EN BIT(3)
+#define TMU_TDQ_ALLOW_CFG_SHIFT 4
+#define TMU_TDQ_ALLOW_CFG BIT(4) // RO, 1 = configurable
+#define TMU_PKT_LEFT_IGNORE_SHIFT 5
+#define TMU_PKT_LEFT_IGNORE BIT(5)
+
+#define TMU_PORT_BASE(port) (TMU_PORT0 + TMU_PORT_SZ * (port))
+#define TMU_QUEUE_BASE(q) (TMU_PORT_QUEUE0 + TMU_PORT_QUEUE_SZ * (q))
+#define TMU_SCHED_BASE(sch) (TMU_SCH0 + TMU_SCH_SZ * (sch))
+#define TMU_SHAPER_BASE(shp) (TMU_SHP0 + TMU_SHP_SZ * (shp))
+
+
+
+#define TMU_MAX_PORT_CNT 10
+#define QUE_MAX_NUM_PER_PORT 8
+#define QUE_SHAPER_NUM_PER_PORT 6
+#define QUE_SCH_NUM_PER_PORT 2
+
+enum TMU_QUEUE_TYPE {
+ TMU_Q_MIX_TAIL_DROP = 0,
+ TMU_Q_TAIL_DROP,
+ TMU_Q_WRED,
+ TMU_Q_BUF_CNT_TAIL_DROP,
+ NUM_TMU_QUEUE_TYPES,
+};
+
+enum TMU_SCHED_ALG {
+ TMU_SCHED_PQ = 0,
+ TMU_SCHED_WFQ,
+ TMU_SCHED_DWRR,
+ TMU_SCHED_RR,
+ TMU_SCHED_WRR,
+ NUM_TMU_SCEHD_ALGS,
+};
+
+enum TMU_BITRATE_MODE {
+ TMU_BITRATE_PKTLEN = 0,
+ TMU_BITRATE_PKTCNT,
+ NUM_TMU_BITRATE_MODES,
+};
+
+static const u8 sched_q_weight_regs[] = {
+ TMU_SCH_Q0_WEIGHT,
+ TMU_SCH_Q1_WEIGHT,
+ TMU_SCH_Q2_WEIGHT,
+ TMU_SCH_Q3_WEIGHT,
+ TMU_SCH_Q4_WEIGHT,
+ TMU_SCH_Q5_WEIGHT,
+ TMU_SCH_Q6_WEIGHT,
+ TMU_SCH_Q7_WEIGHT,
+};
+
+#endif /* __SF_TMU_H__ */
--- /dev/null
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/if_ether.h>
+#include <linux/of_platform.h>
+#include <linux/seq_file.h>
+#include <linux/tcp.h>
+#include <net/page_pool/helpers.h>
+
+#include "sfxgmac-ext.h"
+#include "dma.h"
+#include "eth.h"
+
+struct xgmac_dma_desc_rx {
+ struct xgmac_dma_desc norm;
+ struct xgmac_dma_desc ctxt;
+};
+
+static void xgmac_dma_set_tx_head_ptr(struct xgmac_dma_priv *priv,
+ dma_addr_t addr, u32 queue)
+{
+ reg_write(priv, XGMAC_DMA_CH_TxDESC_LADDR(queue), lower_32_bits(addr));
+}
+
+static void xgmac_dma_set_rx_head_ptr(struct xgmac_dma_priv *priv,
+ dma_addr_t addr, u32 queue)
+{
+ reg_write(priv, XGMAC_DMA_CH_RxDESC_LADDR(queue), lower_32_bits(addr));
+}
+
+static void xgmac_dma_set_tx_tail_ptr(struct xgmac_dma_priv *priv,
+ dma_addr_t addr, u32 queue)
+{
+ reg_write(priv, XGMAC_DMA_CH_TxDESC_TAIL_LPTR(queue), lower_32_bits(addr));
+}
+
+static void xgmac_dma_set_rx_tail_ptr(struct xgmac_dma_priv *priv,
+ dma_addr_t addr, u32 queue)
+{
+ reg_write(priv, XGMAC_DMA_CH_RxDESC_TAIL_LPTR(queue), lower_32_bits(addr));
+}
+
+static void xgmac_dma_set_tx_desc_addr(struct xgmac_dma_desc *p,
+ dma_addr_t addr)
+{
+ p->des0 = cpu_to_le32(lower_32_bits(addr));
+ p->des1 = 0;
+}
+
+static void xgmac_dma_set_rx_desc_addr(struct xgmac_dma_desc *p,
+ dma_addr_t addr)
+{
+ p->des0 = cpu_to_le32(lower_32_bits(addr));
+ p->des1 = 0;
+ p->des2 = 0;
+}
+
+static void xgmac_dma_set_rx_owner(struct xgmac_dma_desc *p, bool int_en)
+{
+ u32 val = XGMAC_RDES3_OWN;
+
+ if (int_en)
+ val |= XGMAC_RDES3_IOC;
+ p->des3 = cpu_to_le32(val);
+}
+
+static void xgmac_dma_init_rx_desc(struct xgmac_dma_desc *p,
+ dma_addr_t addr, bool int_en)
+{
+ xgmac_dma_set_rx_desc_addr(p, addr);
+ xgmac_dma_set_rx_owner(p, int_en);
+}
+
+static __always_inline void
+xgmac_dma_rx_coe_hash(const struct net_device *dev, struct sk_buff *skb,
+ u32 rdes0, u32 rdes_ctx1)
+{
+ bool rxcoe, is_l3, is_l4, not_tunnel, not_frag;
+ u32 hash;
+ /* RX COE is only available if:
+ * 1. It's not an IP fragment
+ * 2. It's not a tunnel packet (4in6, PPPoE, etc.)
+ * 3. Its L4 protocol is known (TCP, UDP, or ICMP)
+ *
+ * If all of the above are true, mark the skb as
+ * CHECKSUM_UNNECESSARY
+ *
+ * Note: bit-wise ops are used to avoid branches
+ */
+ not_frag = FIELD_GET(XGMAC_RDES0_IP_FRAG, rdes0) == FRAG_NONE;
+ not_tunnel = FIELD_GET(XGMAC_RDES1_TNP, rdes_ctx1) != 1;
+ is_l3 = (FIELD_GET(XGMAC_RDES0_L3_TYPE, rdes0) == L3_TYPE_IPV4) |
+ (FIELD_GET(XGMAC_RDES0_L3_TYPE, rdes0) == L3_TYPE_IPV6);
+ is_l4 = FIELD_GET(XGMAC_RDES0_L4_TYPE, rdes0) != L4_TYPE_UNKNOWN;
+
+ rxcoe = !!(dev->features & NETIF_F_RXCSUM) & not_frag & not_tunnel & is_l3 & is_l4;
+
+ skb->ip_summed = rxcoe ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
+
+ /* Fill in skb->hash */
+ hash = FIELD_GET(XGMAC_RDES1_RXHASH, rdes_ctx1);
+ __skb_set_hash(skb, hash, false, is_l4);
+}
+
+static u32 xgmac_dma_tx_avail(struct xgmac_txq *txq)
+{
+ if (txq->dirty_tx > txq->cur_tx)
+ return txq->dirty_tx - txq->cur_tx - 1;
+ else
+ return DMA_TX_SIZE - txq->cur_tx + txq->dirty_tx - 1;
+}
+
+static void xgmac_dma_rx_refill(struct xgmac_dma_priv *priv,
+ struct xgmac_rxq *rxq)
+{
+ u32 entry = rxq->dirty_rx;
+ u16 channel = rxq->idx;
+ s32 dirty;
+
+ if (rxq->dirty_rx <= rxq->cur_rx)
+ dirty = rxq->cur_rx - rxq->dirty_rx;
+ else
+ dirty = DMA_RX_SIZE - rxq->dirty_rx + rxq->cur_rx;
+
+ for (; dirty > 0; dirty -= 2) {
+ struct xgmac_dma_rx_buffer *buf = &rxq->buf_pool[entry];
+ struct xgmac_dma_desc *p = &rxq->dma_rx[entry];
+
+ if (likely(buf->page == NULL)) {
+ buf->page = page_pool_dev_alloc_frag(rxq->page_pool,
+ &buf->offset,
+ priv->rx_alloc_size);
+ if (unlikely(!buf->page))
+ break;
+ }
+
+ xgmac_dma_init_rx_desc(p, page_pool_get_dma_addr(buf->page) + buf->offset + BUF_PAD, false);
+ /* No buffer space required by context descs */
+ xgmac_dma_init_rx_desc(p + 1, 0, false);
+
+ entry = (entry + 2) % DMA_RX_SIZE;
+ }
+
+ rxq->dirty_rx = entry;
+ rxq->rx_tail_addr = rxq->dirty_rx * sizeof(struct xgmac_dma_desc) +
+ rxq->dma_rx_phy;
+
+ xgmac_dma_set_rx_tail_ptr(priv, rxq->rx_tail_addr, channel);
+}
+
+static int xgmac_dma_poll_rx(struct xgmac_rxq *rxq, int budget)
+{
+ struct xgmac_dma_priv *priv = container_of(rxq, struct xgmac_dma_priv,
+ rxq[rxq->idx]);
+ unsigned int next_entry = rxq->cur_rx;
+ int count = 0;
+
+ for (; count < budget; count++) {
+ u32 len, rdes0, rdes2, rdes3, rdes_ctx0, rdes_ctx1, rdes_ctx2, rdes_ctx3, sta_index, rpt_index;
+ struct xgmac_dma_rx_buffer *buf;
+ register struct xgmac_dma_desc_rx rx;
+ struct net_device *netdev;
+ struct sk_buff *skb;
+ unsigned int entry;
+ u8 id, up_reason, vlan_pri, no_frag;
+ u16 ovid, sport, eth_type, dscp, pkt_type, tnp;
+ u64 smac;
+
+ entry = next_entry;
+ buf = &rxq->buf_pool[entry];
+
+ rx = __READ_ONCE(*(struct xgmac_dma_desc_rx *)&rxq->dma_rx[next_entry]);
+ /* check if owned by the DMA otherwise go ahead */
+ if (unlikely(le32_to_cpu(rx.ctxt.des3) & XGMAC_RDES3_OWN))
+ break;
+
+ rxq->cur_rx = (entry + 2) % DMA_RX_SIZE;
+ next_entry = rxq->cur_rx;
+
+ rdes3 = le32_to_cpu(rx.norm.des3);
+ if (unlikely(!(rdes3 & XGMAC_RDES3_LD)))
+ continue;
+
+ if (unlikely(rdes3 & XGMAC_RDES3_ES)) {
+ pr_debug_ratelimited("error type: 0x%lx\n",
+ FIELD_GET(XGMAC_RDES3_ET, rdes3));
+ continue;
+ }
+
+ rdes0 = le32_to_cpu(rx.norm.des0);
+ /* get ivport */
+ id = FIELD_GET(XGMAC_RDES0_IVPORT, rdes0);
+ netdev = priv->ndevs[id];
+ if (unlikely(!netdev))
+ continue;
+
+ /* When memory is tight, the buf->addr may be empty */
+ if (unlikely(!buf->page))
+ break;
+
+ len = FIELD_GET(XGMAC_RDES3_PL, rdes3);
+ dma_sync_single_for_cpu(priv->dev, page_pool_get_dma_addr(buf->page) + buf->offset + BUF_PAD, len, DMA_FROM_DEVICE);
+ prefetch(page_address(buf->page) + buf->offset + BUF_PAD);
+ skb = napi_build_skb(page_address(buf->page) + buf->offset, priv->rx_alloc_size);
+ if (unlikely(!skb))
+ break;
+
+ buf->page = NULL;
+ skb_mark_for_recycle(skb);
+ skb_reserve(skb, BUF_PAD);
+ __skb_put(skb, len);
+
+ rdes2 = le32_to_cpu(rx.norm.des2);
+ ovid = FIELD_GET(XGMAC_RDES2_OVID, rdes2);
+ no_frag = FIELD_GET(XGMAC_RDES2_DFRAG, rdes2);
+ vlan_pri = FIELD_GET(XGMAC_RDES3_TCI_PRI, rdes3);
+ sta_index = FIELD_GET(XGMAC_RDES0_STA_INDEX, rdes0);
+ rpt_index = FIELD_GET(XGMAC_RDES0_RPT_INDEX, rdes0);
+
+ /* get the context descriptor */
+ rdes_ctx0 = le32_to_cpu(rx.ctxt.des0);
+ rdes_ctx1 = le32_to_cpu(rx.ctxt.des1);
+ rdes_ctx2 = le32_to_cpu(rx.ctxt.des2);
+ rdes_ctx3 = le32_to_cpu(rx.ctxt.des3);
+ sport = FIELD_GET(XGMAC_RDES0_SPORT, rdes_ctx0);
+ eth_type = FIELD_GET(XGMAC_RDES0_ETH_TYPE, rdes_ctx0);
+ dscp = FIELD_GET(XGMAC_RDES1_DSCP, rdes_ctx1);
+ tnp = FIELD_GET(XGMAC_RDES1_TNP, rdes_ctx1);
+ up_reason = FIELD_GET(XGMAC_RDES1_UP_REASON, rdes_ctx1);
+ smac = rdes_ctx2 | FIELD_GET(XGMAC_RDES3_SMAC_32_47, rdes_ctx3) << 32;
+ pkt_type = FIELD_GET(XGMAC_RDES3_PKT_TYPE, rdes_ctx3);
+ pr_debug_ratelimited("%s: up_reason:%02x sta_index:%u rpt_index:%u "
+ "ovid:%u vlan_pri:%u no_frag:%u sport:%u eth_type:0x%x "
+ "dscp:%u tnp:%u pkt_type:%u smac:%llx\n",
+ netdev->name, up_reason, sta_index, rpt_index,
+ ovid, vlan_pri, no_frag, sport, eth_type,
+ dscp, tnp, pkt_type, smac);
+
+ xgmac_dma_rx_coe_hash(netdev, skb, rdes0, rdes_ctx1);
+
+ skb_record_rx_queue(skb, rxq->idx);
+ skb->protocol = eth_type_trans(skb, netdev);
+ napi_gro_receive(&rxq->napi, skb);
+ }
+
+ xgmac_dma_rx_refill(priv, rxq);
+
+ return count;
+}
+
+static int xgmac_dma_poll_tx(struct xgmac_txq *txq, int budget)
+{
+ struct xgmac_dma_priv *priv = container_of(txq, struct xgmac_dma_priv,
+ txq[txq->idx]);
+ u32 bytes_compl[DPNS_HOST_PORT] = {}, pkts_compl[DPNS_HOST_PORT] = {};
+ u32 entry, count = 0, i;
+
+ spin_lock_bh(&txq->lock);
+
+ entry = txq->dirty_tx;
+ while (entry != txq->cur_tx && count < budget) {
+ struct xgmac_dma_desc *p = &txq->dma_tx[entry];
+ struct sk_buff *skb = txq->tx_skbuff[entry];
+ u32 tdes3 = le32_to_cpu(READ_ONCE(p->des3));
+
+ /* Check if the descriptor is owned by the DMA */
+ if (unlikely(tdes3 & XGMAC_TDES3_OWN))
+ break;
+
+ /* Check if the descriptor is a context descriptor */
+ if (unlikely(tdes3 & XGMAC_TDES3_CTXT))
+ goto next;
+
+ count++;
+ /* Make sure descriptor fields are read after reading
+ * the own bit.
+ */
+ dma_rmb();
+
+ if (likely(txq->tx_skbuff_dma[entry].buf)) {
+ if (txq->tx_skbuff_dma[entry].map_as_page)
+ dma_unmap_page(priv->dev,
+ txq->tx_skbuff_dma[entry].buf,
+ txq->tx_skbuff_dma[entry].len,
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_single(priv->dev,
+ txq->tx_skbuff_dma[entry].buf,
+ txq->tx_skbuff_dma[entry].len,
+ DMA_TO_DEVICE);
+ txq->tx_skbuff_dma[entry].buf = 0;
+ txq->tx_skbuff_dma[entry].len = 0;
+ txq->tx_skbuff_dma[entry].map_as_page = false;
+ }
+
+ txq->tx_skbuff_dma[entry].last_segment = false;
+
+ if (likely(skb)) {
+ u8 id = XGMAC_SKB_CB(skb)->id;
+
+ if (XGMAC_SKB_CB(skb)->fastmode) {
+ pkts_compl[id]++;
+ bytes_compl[id] += skb->len;
+ }
+ napi_consume_skb(skb, budget);
+ txq->tx_skbuff[entry] = NULL;
+ }
+
+next:
+ entry = (entry + 1) % DMA_TX_SIZE;
+ }
+ txq->dirty_tx = entry;
+
+ for (i = 0; i < DPNS_HOST_PORT; i++) {
+ struct net_device *dev = priv->ndevs[i];
+ struct netdev_queue *queue;
+
+ if (!dev)
+ continue;
+
+ queue = netdev_get_tx_queue(dev, txq->idx);
+ netdev_tx_completed_queue(queue, pkts_compl[i], bytes_compl[i]);
+
+ if (unlikely(netif_tx_queue_stopped(queue) &&
+ xgmac_dma_tx_avail(txq) > DMA_TX_SIZE / 4))
+ netif_tx_wake_queue(queue);
+ }
+
+ spin_unlock_bh(&txq->lock);
+
+ return count;
+}
+
+static int xgmac_dma_napi_rx(struct napi_struct *napi, int budget)
+{
+ struct xgmac_rxq *rxq = container_of(napi, struct xgmac_rxq, napi);
+ int work_done;
+
+ work_done = xgmac_dma_poll_rx(rxq, budget);
+ if (work_done < budget && napi_complete_done(napi, work_done))
+ enable_irq(rxq->irq);
+
+ return work_done;
+}
+
+static int xgmac_dma_napi_tx(struct napi_struct *napi, int budget)
+{
+ struct xgmac_txq *txq = container_of(napi, struct xgmac_txq, napi);
+ int work_done = xgmac_dma_poll_tx(txq, budget);
+
+ if (work_done < budget && napi_complete_done(napi, work_done))
+ enable_irq(txq->irq);
+
+ return work_done;
+}
+
+static irqreturn_t xgmac_dma_irq_misc(int irq, void *dev_id)
+{
+ struct xgmac_dma_priv *priv = dev_id;
+ u32 mtl_status = reg_read(priv, XGMAC_MTL_INT_STATUS);
+ u32 dma_status = reg_read(priv, XGMAC_DMA_INT_STATUS);
+ u32 i;
+
+ //dev_info(priv->dev, "irq %d mtl %#08x dma %#08x\n",
+ // irq, mtl_status, dma_status);
+
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ if (BIT(i) & mtl_status) {
+ u32 status = reg_read(priv, XGMAC_MTL_QINT_STATUS(i));
+
+ if (status & XGMAC_RXOVFIS)
+ pr_debug_ratelimited("RX queue %u overflow\n", i);
+
+ /* Clear interrupts */
+ reg_write(priv, XGMAC_MTL_QINT_STATUS(i), status);
+ }
+
+ if (BIT(i) & dma_status) {
+ u32 status = reg_read(priv, XGMAC_DMA_CH_STATUS(i));
+ status &= ~(XGMAC_TI | XGMAC_TBU | XGMAC_RI);
+
+ /* ABNORMAL interrupts */
+ if (unlikely(status & XGMAC_AIS)) {
+ if (status & XGMAC_CDE)
+ dev_info(priv->dev, "Trigger queue %u CDE\n", i);
+
+ if (status & XGMAC_DDE)
+ dev_info(priv->dev, "Trigger queue %u DDE\n", i);
+
+ if (status & XGMAC_FBE) {
+ dev_info(priv->dev, "Trigger queue %u FBE\n", i);
+ /* TODO: restart TX */
+ }
+
+ if (status & XGMAC_RBU) {
+ pr_debug_ratelimited("Trigger queue %u RBU\n", i);
+ }
+ }
+
+ reg_write(priv, XGMAC_DMA_CH_STATUS(i), status);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t xgmac_dma_irq_tx(int irq, void *dev_id)
+{
+ struct xgmac_txq *txq = dev_id;
+ struct xgmac_dma_priv *priv = container_of(txq, struct xgmac_dma_priv,
+ txq[txq->idx]);
+ u16 channel = txq->idx;
+ u32 status = reg_read(priv, XGMAC_DMA_CH_STATUS(channel)) &
+ (XGMAC_TBU | XGMAC_TI);
+
+ if (unlikely(!status))
+ return IRQ_NONE;
+
+ /* Clear interrupts */
+ reg_write(priv, XGMAC_DMA_CH_STATUS(channel), status);
+
+ /* TX NORMAL interrupts */
+ if (likely(napi_schedule_prep(&txq->napi))) {
+ /* Disable TX interrupt */
+ disable_irq_nosync(irq);
+
+ /* Turn on polling */
+ __napi_schedule_irqoff(&txq->napi);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t xgmac_dma_irq_rx(int irq, void *dev_id)
+{
+ struct xgmac_rxq *rxq = dev_id;
+ struct xgmac_dma_priv *priv = container_of(rxq, struct xgmac_dma_priv,
+ rxq[rxq->idx]);
+ u16 channel = rxq->idx;
+ u32 status = reg_read(priv, XGMAC_DMA_CH_STATUS(channel)) &
+ (XGMAC_RI);
+
+ if (unlikely(!status))
+ return IRQ_NONE;
+
+ /* Clear interrupts */
+ reg_write(priv, XGMAC_DMA_CH_STATUS(channel), status);
+
+ /* RX NORMAL interrupts */
+ if (likely(napi_schedule_prep(&rxq->napi))) {
+ /* Disable RX interrupt */
+ disable_irq_nosync(irq);
+
+ /* Turn on polling */
+ __napi_schedule_irqoff(&rxq->napi);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int xgmac_dma_soft_reset(struct xgmac_dma_priv *priv)
+{
+ unsigned long timeout = jiffies + HZ;
+
+ reg_write(priv, XGMAC_DMA_MODE, XGMAC_SWR);
+ do {
+ if (!(reg_read(priv, XGMAC_DMA_MODE) & XGMAC_SWR))
+ return 0;
+
+ cond_resched();
+ } while (time_after(timeout, jiffies));
+
+ dev_err(priv->dev, "DMA reset timed out\n");
+ return -ETIMEDOUT;
+}
+
+static int xgmac_dma_init(struct xgmac_dma_priv *priv)
+{
+ int ret;
+ u32 i;
+
+ /* DMA SW reset */
+ ret = xgmac_dma_soft_reset(priv);
+ if (ret)
+ return ret;
+
+ /* DMA Configuration */
+ /* Exclude per-channel interrupts from sbd_intr_o */
+ reg_write(priv, XGMAC_DMA_MODE, FIELD_PREP(XGMAC_INTM, 1));
+ reg_rmw(priv, XGMAC_DMA_SYSBUS_MODE,
+ XGMAC_RD_OSR_LMT | XGMAC_WR_OSR_LMT,
+ FIELD_PREP(XGMAC_RD_OSR_LMT, 31) | XGMAC_EN_LPI |
+ FIELD_PREP(XGMAC_WR_OSR_LMT, 31) | XGMAC_UNDEF);
+
+ reg_write(priv, XGMAC_TX_EDMA_CTRL, 1);
+ reg_write(priv, XGMAC_RX_EDMA_CTRL, 1);
+
+ /* enable rx_queue 0 1 2 3 */
+ regmap_write(priv->ethsys, ETHSYS_RX_QUEUE_ENABLE, 0xAA);
+
+ /* Use static RX Queue to DMA mapping
+ * queue 0 to channel 0
+ * queue 1 to channel 1
+ * queue 2 to channel 2
+ * queue 3 to chennel 3
+ * queue 4 to channel 4
+ */
+ reg_write(priv, XGMAC_MTL_RXQ_DMA_MAP0, 0x03020100);
+ reg_write(priv, XGMAC_MTL_RXQ_DMA_MAP1, 0x4);
+
+ /* DMA Channel Configuration
+ * TXQs share 8KB, RXQs share 16KB
+ *
+ * Configured queue size = 256B * (1 + (TQS or RQS field))
+ *
+ * The maximum limit of PBL is queue size / datawidth / 2
+ *
+ * TxPBL must be limited to 32 for Tx COE to work on 1500 MTU, see the
+ * comment in xgmac_dma_xmit() for detail.
+ */
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ /* set RxPBL to 16 beats for ddr schedule with 128bit (16*8) */
+ reg_rmw(priv, XGMAC_DMA_CH_RX_CONTROL(i), XGMAC_RxPBL,
+ FIELD_PREP(XGMAC_RxPBL, 16));
+ /* set TxPBL to 16 beats for ddr schedule with 128bit (16*8) */
+ reg_rmw(priv, XGMAC_DMA_CH_TX_CONTROL(i), XGMAC_TxPBL,
+ FIELD_PREP(XGMAC_TxPBL, 16) | XGMAC_TSE | XGMAC_OSP);
+
+ /* Enable TX queue, store-and-forward mode
+ * each queue size 2k bytes
+ */
+ reg_rmw(priv, XGMAC_MTL_TXQ_OPMODE(i), XGMAC_TQS | XGMAC_TXQEN,
+ FIELD_PREP(XGMAC_TQS, 0x7) | XGMAC_TSF |
+ FIELD_PREP(XGMAC_TXQEN, 0x2));
+
+ /* Enable RX queue
+ * each queue size 4k bytes
+ */
+ reg_write(priv, XGMAC_MTL_RXQ_OPMODE(i),
+ FIELD_PREP(XGMAC_RQS, 0xf));
+ }
+
+ return 0;
+}
+
+static void xgmac_dma_free_rx_buffer(struct xgmac_dma_priv *priv, struct xgmac_rxq *rxq, u32 i)
+{
+ struct xgmac_dma_rx_buffer *buf = &rxq->buf_pool[i];
+
+ if (buf->page) {
+ page_pool_put_full_page(rxq->page_pool, buf->page, false);
+ buf->page = NULL;
+ }
+}
+
+static int xgmac_dma_init_rx_buffers(struct xgmac_dma_priv *priv, struct xgmac_rxq *rxq, u32 i,
+ struct xgmac_dma_desc *p, u16 rx_alloc_size)
+{
+ struct xgmac_dma_rx_buffer *buf = &rxq->buf_pool[i];
+
+ buf->page = page_pool_dev_alloc_frag(rxq->page_pool, &buf->offset, rx_alloc_size);
+ if (!buf->page)
+ return -ENOMEM;
+
+ xgmac_dma_init_rx_desc(p, page_pool_get_dma_addr(buf->page) + buf->offset + BUF_PAD, false);
+
+ return 0;
+}
+
+static int xgmac_dma_init_rx_rings(struct xgmac_dma_priv *priv)
+{
+ u32 queue, i;
+ int ret;
+
+ for (queue = 0; queue < DMA_CH_MAX; queue++) {
+ struct xgmac_rxq *rxq = &priv->rxq[queue];
+
+ for (i = 0; i < DMA_RX_SIZE; i += 2) {
+ struct xgmac_dma_desc *p = &rxq->dma_rx[i];
+
+ ret = xgmac_dma_init_rx_buffers(priv, rxq, i, p, priv->rx_alloc_size);
+ if (ret)
+ goto err_init_rx_buffers;
+ /* No buffer space required by context descs */
+ xgmac_dma_init_rx_desc(p + 1, 0, false);
+ }
+
+ rxq->cur_rx = 0;
+ rxq->dirty_rx = (unsigned int)(i - DMA_RX_SIZE);
+ }
+
+ return 0;
+
+err_init_rx_buffers:
+ while (queue >= 0) {
+ while (--i >= 0)
+ xgmac_dma_free_rx_buffer(priv, &priv->rxq[queue], i);
+
+ if (queue == 0)
+ break;
+
+ i = DMA_RX_SIZE;
+ queue--;
+ }
+
+ return ret;
+}
+
+static void xgmac_dma_init_tx_rings(struct xgmac_dma_priv *priv)
+{
+ u32 queue, i;
+
+ for (queue = 0; queue < DMA_CH_MAX; queue++) {
+ struct xgmac_txq *txq = &priv->txq[queue];
+
+ for (i = 0; i < DMA_TX_SIZE; i++) {
+ struct xgmac_dma_desc *p = &txq->dma_tx[i];
+
+ memset(p, 0, sizeof(*p));
+
+ txq->tx_skbuff_dma[i].buf = 0;
+ txq->tx_skbuff_dma[i].map_as_page = false;
+ txq->tx_skbuff_dma[i].len = 0;
+ txq->tx_skbuff_dma[i].last_segment = false;
+ txq->tx_skbuff[i] = NULL;
+ }
+
+ txq->dirty_tx = 0;
+ txq->cur_tx = 0;
+ }
+}
+
+static void xgmac_dma_free_tx_buffer(struct xgmac_txq *txq, u32 i)
+{
+ struct xgmac_dma_priv *priv = container_of(txq, struct xgmac_dma_priv,
+ txq[txq->idx]);
+
+ if (txq->tx_skbuff_dma[i].buf) {
+ if (txq->tx_skbuff_dma[i].map_as_page)
+ dma_unmap_page(priv->dev,
+ txq->tx_skbuff_dma[i].buf,
+ txq->tx_skbuff_dma[i].len,
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_single(priv->dev,
+ txq->tx_skbuff_dma[i].buf,
+ txq->tx_skbuff_dma[i].len,
+ DMA_TO_DEVICE);
+ }
+
+ if (txq->tx_skbuff[i]) {
+ dev_kfree_skb(txq->tx_skbuff[i]);
+ txq->tx_skbuff[i] = NULL;
+ txq->tx_skbuff_dma[i].buf = 0;
+ txq->tx_skbuff_dma[i].map_as_page = false;
+ }
+}
+
+static void xgmac_dma_free_rx_skbufs(struct xgmac_dma_priv *priv, struct xgmac_rxq *rxq)
+{
+ u32 i;
+
+ for (i = 0; i < DMA_RX_SIZE; i++)
+ xgmac_dma_free_rx_buffer(priv, rxq, i);
+}
+
+static void xgmac_dma_free_tx_skbufs(struct xgmac_txq *txq)
+{
+ u32 i;
+
+ for (i = 0; i < DMA_TX_SIZE; i++)
+ xgmac_dma_free_tx_buffer(txq, i);
+}
+
+static void xgmac_dma_free_rx_descs(struct xgmac_dma_priv *priv)
+{
+ u32 i;
+
+ /* Free RX queue resources */
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ struct xgmac_rxq *rxq = &priv->rxq[i];
+
+ /* Release the DMA RX socket buffers */
+ xgmac_dma_free_rx_skbufs(priv, rxq);
+
+ /* Free DMA RX descs */
+#ifdef CONFIG_NET_SIFLOWER_ETH_USE_INTERNAL_SRAM
+ gen_pool_free(priv->genpool, (uintptr_t)rxq->dma_rx,
+ DMA_RX_SIZE * sizeof(*rxq->dma_rx));
+#else
+ dma_free_coherent(priv->dev, DMA_RX_SIZE * sizeof(*rxq->dma_rx),
+ (void *)rxq->dma_rx, rxq->dma_rx_phy);
+#endif
+
+ kfree(rxq->buf_pool);
+ if (rxq->page_pool)
+ page_pool_destroy(rxq->page_pool);
+ }
+}
+
+static void xgmac_dma_free_tx_descs(struct xgmac_dma_priv *priv)
+{
+ u32 i;
+
+ /* Free TX queue resources */
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ struct xgmac_txq *txq = &priv->txq[i];
+ spin_lock_bh(&txq->lock);
+ txq->is_busy = true;
+ spin_unlock_bh(&txq->lock);
+ /* Release the DMA TX socket buffers */
+ xgmac_dma_free_tx_skbufs(txq);
+
+ /* Free DMA TX descs */
+#ifdef CONFIG_NET_SIFLOWER_ETH_USE_INTERNAL_SRAM
+ gen_pool_free(priv->genpool, (uintptr_t)txq->dma_tx,
+ DMA_TX_SIZE * sizeof(*txq->dma_tx));
+#else
+ dma_free_coherent(priv->dev, DMA_TX_SIZE * sizeof(*txq->dma_tx),
+ txq->dma_tx, txq->dma_tx_phy);
+#endif
+ txq->dma_tx = NULL;
+ kfree(txq->tx_skbuff_dma);
+ txq->tx_skbuff_dma = NULL;
+ kfree(txq->tx_skbuff);
+ txq->tx_skbuff = NULL;
+
+ spin_lock_bh(&txq->lock);
+ txq->is_busy = false;
+ spin_unlock_bh(&txq->lock);
+ }
+}
+
+static int xgmac_dma_alloc_rx_descs(struct xgmac_dma_priv *priv)
+{
+ struct page_pool_params pp_params = {};
+ int ret = -ENOMEM;
+ u32 i;
+
+ pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV | PP_FLAG_PAGE_FRAG;
+ pp_params.pool_size = DMA_RX_SIZE;
+ pp_params.order = 0;
+ pp_params.max_len = PAGE_SIZE;
+ pp_params.nid = dev_to_node(priv->dev);
+ pp_params.dev = priv->dev;
+ pp_params.dma_dir = DMA_FROM_DEVICE;
+
+ /* RX queues buffers and DMA */
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ struct xgmac_rxq *rxq = &priv->rxq[i];
+
+ rxq->page_pool = page_pool_create(&pp_params);
+ if (IS_ERR(rxq->page_pool)) {
+ ret = PTR_ERR(rxq->page_pool);
+ rxq->page_pool = NULL;
+ goto err_dma;
+ }
+
+ rxq->buf_pool = kcalloc(DMA_RX_SIZE, sizeof(*rxq->buf_pool),
+ GFP_KERNEL);
+ if (!rxq->buf_pool)
+ goto err_dma;
+
+#ifdef CONFIG_NET_SIFLOWER_ETH_USE_INTERNAL_SRAM
+ rxq->dma_rx = gen_pool_dma_alloc(priv->genpool, DMA_RX_SIZE *
+ sizeof(*rxq->dma_rx),
+ &rxq->dma_rx_phy);
+#else
+ rxq->dma_rx = dma_alloc_coherent(priv->dev, DMA_RX_SIZE *
+ sizeof(*rxq->dma_rx),
+ &rxq->dma_rx_phy, GFP_KERNEL);
+#endif
+ if (!rxq->dma_rx)
+ goto err_dma;
+ }
+
+ return 0;
+
+err_dma:
+ xgmac_dma_free_rx_descs(priv);
+ return ret;
+}
+
+static int xgmac_dma_alloc_tx_descs(struct xgmac_dma_priv *priv)
+{
+ int ret = -ENOMEM;
+ u32 i;
+
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ struct xgmac_txq *txq = &priv->txq[i];
+
+ txq->tx_skbuff_dma = kcalloc(DMA_TX_SIZE,
+ sizeof(*txq->tx_skbuff_dma),
+ GFP_KERNEL);
+ if (!txq->tx_skbuff_dma)
+ goto err_dma;
+
+ txq->tx_skbuff = kcalloc(DMA_TX_SIZE,
+ sizeof(struct sk_buff *),
+ GFP_KERNEL);
+ if (!txq->tx_skbuff)
+ goto err_dma;
+
+#ifdef CONFIG_NET_SIFLOWER_ETH_USE_INTERNAL_SRAM
+ txq->dma_tx = gen_pool_dma_zalloc(priv->genpool, DMA_TX_SIZE *
+ sizeof(*txq->dma_tx),
+ &txq->dma_tx_phy);
+#else
+ txq->dma_tx = dma_alloc_coherent(priv->dev, DMA_TX_SIZE *
+ sizeof(*txq->dma_tx),
+ &txq->dma_tx_phy, GFP_KERNEL);
+#endif
+ if (!txq->dma_tx)
+ goto err_dma;
+ }
+
+ return 0;
+
+err_dma:
+ xgmac_dma_free_tx_descs(priv);
+ return ret;
+}
+
+static int xgmac_dma_enable(struct xgmac_dma_priv *priv)
+{
+ int ret;
+ u32 i;
+
+ ret = xgmac_dma_alloc_rx_descs(priv);
+ if (ret)
+ return ret;
+
+ ret = xgmac_dma_init_rx_rings(priv);
+ if (ret)
+ return ret;
+
+ ret = xgmac_dma_alloc_tx_descs(priv);
+ if (ret)
+ return ret;
+
+ xgmac_dma_init_tx_rings(priv);
+
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ struct xgmac_rxq *rxq = &priv->rxq[i];
+ struct xgmac_txq *txq = &priv->txq[i];
+
+ /*
+ * Initiate the WDT with packet count of 32 to enable IRQ
+ * set watchdog timer 2048 * 100 * 2.5ns = 0.512ms
+ * */
+ reg_write(priv, XGMAC_DMA_CH_Rx_WATCHDOG(i),
+ XGMAC_PSEL | FIELD_PREP(XGMAC_RBCT, 32) |
+ FIELD_PREP(XGMAC_RWTU, 3) | FIELD_PREP(XGMAC_RWT, 100));
+
+ /* Set RX buffer size */
+ reg_rmw(priv, XGMAC_DMA_CH_RX_CONTROL(i), XGMAC_RBSZ,
+ FIELD_PREP(XGMAC_RBSZ, priv->rx_buffer_size));
+
+ /* Set head pointer */
+ xgmac_dma_set_rx_head_ptr(priv, rxq->dma_rx_phy, i);
+ xgmac_dma_set_tx_head_ptr(priv, txq->dma_tx_phy, i);
+
+ /* Set tail pointer */
+ rxq->rx_tail_addr = DMA_RX_SIZE * sizeof(*rxq->dma_rx) +
+ rxq->dma_rx_phy;
+ txq->tx_tail_addr = txq->dma_tx_phy;
+ xgmac_dma_set_rx_tail_ptr(priv, rxq->rx_tail_addr, i);
+ xgmac_dma_set_tx_tail_ptr(priv, txq->tx_tail_addr, i);
+
+ /* Set ring length */
+ reg_write(priv, XGMAC_DMA_CH_RxDESC_RING_LEN(i),
+ ((DMA_RX_SIZE - 1) | (FIELD_PREP(XGMAC_OWRQ, 7))));
+ reg_write(priv, XGMAC_DMA_CH_TxDESC_RING_LEN(i),
+ DMA_TX_SIZE - 1);
+
+ /* Enable NAPI poll */
+ napi_enable(&rxq->napi);
+ napi_enable(&txq->napi);
+
+ /* Enable interrupt */
+ reg_write(priv, XGMAC_DMA_CH_INT_EN(i),
+ XGMAC_DMA_INT_DEFAULT_EN);
+
+ /* Enable MTL RX overflow interrupt */
+ reg_write(priv, XGMAC_MTL_QINTEN(i), XGMAC_RXOIE);
+
+ /* Start DMA */
+ reg_set(priv, XGMAC_DMA_CH_RX_CONTROL(i), XGMAC_RXST);
+ reg_set(priv, XGMAC_DMA_CH_TX_CONTROL(i), XGMAC_TXST);
+ }
+
+ return 0;
+}
+
+static void xgmac_dma_disable(struct xgmac_dma_priv *priv)
+{
+ u32 i;
+
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ struct xgmac_rxq *rxq = &priv->rxq[i];
+ struct xgmac_txq *txq = &priv->txq[i];
+
+ /* Disable interrupts */
+ reg_write(priv, XGMAC_DMA_CH_INT_EN(i), 0);
+ reg_write(priv, XGMAC_MTL_QINTEN(i), 0);
+
+ /* Disable DMA transfer */
+ reg_clear(priv, XGMAC_DMA_CH_RX_CONTROL(i), XGMAC_RXST);
+ reg_clear(priv, XGMAC_DMA_CH_TX_CONTROL(i), XGMAC_TXST);
+
+ /* Disable NAPI poll */
+ napi_disable(&rxq->napi);
+ napi_disable(&txq->napi);
+
+ /* Clear all pending interrupts */
+ reg_write(priv, XGMAC_DMA_CH_STATUS(i), -1);
+ reg_write(priv, XGMAC_MTL_QINT_STATUS(i), -1);
+ }
+
+ /* Free resources */
+ xgmac_dma_free_rx_descs(priv);
+ xgmac_dma_free_tx_descs(priv);
+}
+
+static void xgmac_dma_stop_queue(struct xgmac_dma_priv *priv, int queue)
+{
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ struct net_device *dev = priv->ndevs[i];
+
+ if (!dev)
+ continue;
+
+ netif_tx_stop_queue(netdev_get_tx_queue(dev, queue));
+ }
+}
+
+static void xgmac_dma_tso_fill_desc(struct xgmac_txq *txq, dma_addr_t des,
+ unsigned int pay_len, bool last_segment)
+{
+ struct xgmac_dma_desc *desc;
+ int tmp_len = pay_len;
+ u32 entry;
+
+ /* 1. put every 2 16K-1 buffers into one desc */
+ while (tmp_len > TSO_MAX_BUFF_SIZE) {
+ bool ld = (last_segment && tmp_len <= TSO_MAX_BUFF_SIZE * 2);
+ entry = txq->cur_tx;
+ entry = (entry + 1) % DMA_TX_SIZE;
+ txq->cur_tx = entry;
+ desc = &txq->dma_tx[entry];
+
+ desc->des0 = cpu_to_le32(des);
+ desc->des1 = cpu_to_le32(des + TSO_MAX_BUFF_SIZE);
+ desc->des2 = cpu_to_le32((ld ? XGMAC_TDES2_IOC : 0) | XGMAC_TDES2_B1L |
+ FIELD_PREP(XGMAC_TDES2_B2L,
+ min(tmp_len - TSO_MAX_BUFF_SIZE,
+ TSO_MAX_BUFF_SIZE)));
+ desc->des3 = cpu_to_le32(XGMAC_TDES3_OWN |
+ (ld ? XGMAC_TDES3_LD : 0));
+
+ tmp_len -= TSO_MAX_BUFF_SIZE * 2;
+ des += TSO_MAX_BUFF_SIZE * 2;
+ }
+ /* 2. put the last buffer, if exists */
+ if (tmp_len > 0) {
+ entry = txq->cur_tx;
+ entry = (entry + 1) % DMA_TX_SIZE;
+ txq->cur_tx = entry;
+ desc = &txq->dma_tx[entry];
+
+ desc->des0 = cpu_to_le32(des);
+ desc->des1 = cpu_to_le32(0);
+ desc->des2 = cpu_to_le32((last_segment ? XGMAC_TDES2_IOC : 0) |
+ FIELD_PREP(XGMAC_TDES2_B1L, tmp_len));
+ desc->des3 = cpu_to_le32(XGMAC_TDES3_OWN |
+ (last_segment ? XGMAC_TDES3_LD : 0));
+ }
+}
+
+static netdev_tx_t xgmac_dma_tso_xmit(struct sk_buff *skb,
+ struct xgmac_dma_priv *priv)
+{
+ u16 queue = skb_get_queue_mapping(skb);
+ u16 channel = queue;
+ u32 size = (queue == DMA_CH_DISABLE) ? SZ_2K : SZ_1_5K;
+ struct xgmac_txq *txq = &priv->txq[queue];
+ struct xgmac_dma_desc *desc, *first, *ctxt;
+ struct xgmac_skb_cb *cb = XGMAC_SKB_CB(skb);
+ u8 nfrags = skb_shinfo(skb)->nr_frags;
+ struct net_device *dev = skb->dev;
+ u32 first_entry, entry, i, tdes0, pay_len;
+ u32 proto_hdr_len, hdr;
+ bool last_segment;
+ dma_addr_t des;
+ u16 mss;
+
+ hdr = tcp_hdrlen(skb);
+ proto_hdr_len = skb_transport_offset(skb) + hdr;
+ pay_len = skb_headlen(skb) - proto_hdr_len; /* no frags */
+
+ spin_lock(&txq->lock);
+ /* Desc availability based on threshold should be enough safe */
+ if (unlikely(xgmac_dma_tx_avail(txq) <
+ max_t(u32, nfrags, (skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE) + 2)) {
+ xgmac_dma_stop_queue(priv, queue);
+ pr_debug_ratelimited("%s: Tx Ring full when queue awake\n",
+ __func__);
+ spin_unlock(&txq->lock);
+ return NETDEV_TX_BUSY;
+ }
+
+ mss = skb_shinfo(skb)->gso_size;
+ /* The header length + MSS + TxPBL must be less than Tx Queue size */
+ mss = min_t(u16, mss, size - 16 - proto_hdr_len - 1);
+
+ entry = txq->cur_tx;
+ desc = &txq->dma_tx[entry];
+ ctxt = desc;
+ /* Prepare TX context descriptor */
+ tdes0 = XGMAC_TDES0_FAST_MODE |
+ FIELD_PREP(XGMAC_TDES0_OVPORT, cb->id) |
+ FIELD_PREP(XGMAC_TDES0_IVPORT, DPNS_HOST_PORT);
+ ctxt->des0 = cpu_to_le32(tdes0);
+ ctxt->des1 = 0;
+ ctxt->des2 = cpu_to_le32(mss);
+
+ entry = (entry + 1) % DMA_TX_SIZE;
+ txq->cur_tx = first_entry = entry;
+ desc = &txq->dma_tx[entry];
+ first = desc;
+
+ /* first descriptor: fill Headers on Buf1 */
+ last_segment = (nfrags == 0);
+ des = dma_map_single(priv->dev, skb->data, skb_headlen(skb),
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(priv->dev, des)))
+ goto drop;
+
+ txq->tx_skbuff_dma[first_entry].buf = des;
+ txq->tx_skbuff_dma[first_entry].len = skb_headlen(skb);
+ first->des0 = cpu_to_le32(des);
+
+ /* Fill start of payload in buff2 of first descriptor */
+ first->des1 = cpu_to_le32(pay_len ? des + proto_hdr_len : 0);
+ if (pay_len > TSO_MAX_BUFF_SIZE) {
+ /* Need more descs if the buffer size > 16383 */
+ pay_len -= TSO_MAX_BUFF_SIZE;
+ first->des2 =
+ cpu_to_le32(FIELD_PREP(XGMAC_TDES2_B1L, proto_hdr_len) |
+ XGMAC_TDES2_B2L);
+ des += proto_hdr_len + TSO_MAX_BUFF_SIZE;
+ } else {
+ first->des2 =
+ cpu_to_le32((last_segment ? XGMAC_TDES2_IOC : 0) |
+ FIELD_PREP(XGMAC_TDES2_B1L, proto_hdr_len) |
+ FIELD_PREP(XGMAC_TDES2_B2L, pay_len));
+ pay_len = 0;
+ }
+ first->des3 = cpu_to_le32(
+ XGMAC_TDES3_OWN | XGMAC_TDES3_FD |
+ (last_segment && !pay_len ? XGMAC_TDES3_LD : 0) |
+ FIELD_PREP(XGMAC_TDES3_THL, hdr / 4) | XGMAC_TDES3_TSE |
+ FIELD_PREP(XGMAC_TDES3_TPL, skb->len - proto_hdr_len));
+
+ /* Put the remaining headlen buffer */
+ xgmac_dma_tso_fill_desc(txq, des, pay_len, last_segment);
+ entry = txq->cur_tx;
+ /* Prepare fragments */
+ for (i = 0; i < nfrags; i++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ des = skb_frag_dma_map(priv->dev, frag, 0, skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(priv->dev, des)))
+ goto drop;
+
+ last_segment = (i == nfrags - 1);
+
+ xgmac_dma_tso_fill_desc(txq, des, skb_frag_size(frag), last_segment);
+ entry = txq->cur_tx;
+ txq->tx_skbuff_dma[entry].buf = des;
+ txq->tx_skbuff_dma[entry].len = skb_frag_size(frag);
+ txq->tx_skbuff_dma[entry].map_as_page = true;
+ }
+ txq->tx_skbuff_dma[entry].last_segment = true;
+ /* Only the last descriptor gets to point to the skb. */
+ txq->tx_skbuff[entry] = skb;
+
+ /* We've used all descriptors we need for this skb, however,
+ * advance cur_tx so that it references a fresh descriptor.
+ * ndo_start_xmit will fill this descriptor the next time it's
+ * called and xgmac_dma_poll_tx may clean up to this descriptor.
+ */
+ entry = (entry + 1) % DMA_TX_SIZE;
+ txq->cur_tx = entry;
+
+ if (unlikely(xgmac_dma_tx_avail(txq) <= (MAX_SKB_FRAGS + 1)))
+ xgmac_dma_stop_queue(priv, queue);
+
+ skb_tx_timestamp(skb);
+ ctxt->des3 = cpu_to_le32(XGMAC_TDES3_OWN | XGMAC_TDES3_CTXT |
+ XGMAC_TDES3_TCMSSV | XGMAC_TDES3_PIDV);
+ dma_wmb();
+ netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len);
+ txq->tx_tail_addr = txq->dma_tx_phy + txq->cur_tx * sizeof(*desc);
+ xgmac_dma_set_tx_tail_ptr(priv, txq->tx_tail_addr, channel);
+ spin_unlock(&txq->lock);
+ return NETDEV_TX_OK;
+drop:
+ dev->stats.tx_dropped++;
+ spin_unlock(&txq->lock);
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+
+netdev_tx_t xgmac_dma_xmit(struct sk_buff *skb, struct xgmac_dma_priv *priv)
+{
+ u16 queue = skb_get_queue_mapping(skb);
+ u16 channel = queue;
+ u32 size = (queue == DMA_CH_DISABLE) ? SZ_2K : SZ_1_5K;
+ struct xgmac_txq *txq = &priv->txq[queue];
+ u8 nfrags = skb_shinfo(skb)->nr_frags;
+ struct xgmac_dma_desc *desc, *first, *ctxt;
+ struct xgmac_skb_cb *cb = XGMAC_SKB_CB(skb);
+ struct net_device *dev = skb->dev;
+ u32 nopaged_len = skb_headlen(skb);
+ u32 first_entry, entry, i, tdes0, cic = 0;
+ bool last_segment;
+ dma_addr_t des;
+
+ if (skb_is_gso(skb) && skb_is_gso_tcp(skb))
+ return xgmac_dma_tso_xmit(skb, priv);
+
+ if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
+ /* Tx COE only works with packets that are LESS THAN the
+ * following number of bytes in size:
+ * TXQ SIZE in bytes – ((PBL + 5)*(DATAWIDTH in bytes))
+ *
+ * Thus for queue size of 2K, Tx COE of 1500 MTU works only if
+ * PBL <= 32.
+ */
+ const unsigned int txcoeovh = (16 + 5) * 8;
+
+ if (unlikely(skb->len >= size - txcoeovh)) {
+ if (unlikely(skb_checksum_help(skb)))
+ goto drop_kfree;
+ } else {
+ cic = XGMAC_TDES3_CIC;
+ }
+ }
+
+ spin_lock(&txq->lock);
+ if (txq->is_busy || (NULL == txq->dma_tx) || (NULL == txq->tx_skbuff_dma) || (NULL == txq->tx_skbuff)) {
+ spin_unlock(&txq->lock);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* We need at least 2 + nfrags free TX descriptors to xmit a packet */
+ if (unlikely(xgmac_dma_tx_avail(txq) < nfrags + 2)) {
+ xgmac_dma_stop_queue(priv, queue);
+ pr_debug_ratelimited("%s: Tx Ring full when queue awake\n",
+ __func__);
+ spin_unlock(&txq->lock);
+ return NETDEV_TX_BUSY;
+ }
+
+ entry = txq->cur_tx;
+ desc = &txq->dma_tx[entry];
+ ctxt = desc;
+
+ /* Prepare TX context descriptor */
+ if (cb->fastmode)
+ tdes0 = XGMAC_TDES0_FAST_MODE |
+ FIELD_PREP(XGMAC_TDES0_OVPORT, cb->id) |
+ FIELD_PREP(XGMAC_TDES0_IVPORT, DPNS_HOST_PORT);
+ else
+ tdes0 = FIELD_PREP(XGMAC_TDES0_OVPORT, DPNS_HOST_PORT) |
+ FIELD_PREP(XGMAC_TDES0_IVPORT, cb->id);
+
+ ctxt->des0 = cpu_to_le32(tdes0);
+ ctxt->des1 = 0;
+ ctxt->des2 = 0;
+
+ entry = (entry + 1) % DMA_TX_SIZE;
+ first_entry = entry;
+ desc = &txq->dma_tx[entry];
+ first = desc;
+
+ for (i = 0; i < nfrags; i++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ u32 len = skb_frag_size(frag);
+
+ last_segment = (i == (nfrags - 1));
+ entry = (entry + 1) % DMA_TX_SIZE;
+ desc = &txq->dma_tx[entry];
+ des = skb_frag_dma_map(priv->dev, frag, 0, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->dev, des))
+ goto drop;
+
+ txq->tx_skbuff_dma[entry].buf = des;
+
+ xgmac_dma_set_tx_desc_addr(desc, des);
+
+ txq->tx_skbuff_dma[entry].map_as_page = true;
+ txq->tx_skbuff_dma[entry].len = len;
+ txq->tx_skbuff_dma[entry].last_segment = last_segment;
+ /* Prepare the descriptor and set the own bit too */
+ desc->des2 = cpu_to_le32(FIELD_PREP(XGMAC_TDES2_B1L, len) |
+ (last_segment ? XGMAC_TDES2_IOC : 0));
+ desc->des3 = cpu_to_le32(XGMAC_TDES3_OWN | cic |
+ (last_segment ? XGMAC_TDES3_LD : 0) |
+ FIELD_PREP(XGMAC_TDES3_FL, skb->len));
+ }
+
+ /* Only the last descriptor gets to point to the skb. */
+ txq->tx_skbuff[entry] = skb;
+
+ /* We've used all descriptors we need for this skb, however,
+ * advance cur_tx so that it references a fresh descriptor.
+ * ndo_start_xmit will fill this descriptor the next time it's
+ * called and xgmac_dma_poll_tx may clean up to this descriptor.
+ */
+ entry = (entry + 1) % DMA_TX_SIZE;
+ txq->cur_tx = entry;
+
+ if (unlikely(xgmac_dma_tx_avail(txq) <= (MAX_SKB_FRAGS + 1)))
+ xgmac_dma_stop_queue(priv, queue);
+
+ skb_tx_timestamp(skb);
+
+ /* Ready to fill the first descriptor and set the OWN bit w/o any
+ * problems because all the descriptors are actually ready to be
+ * passed to the DMA engine.
+ */
+ last_segment = (nfrags == 0);
+ des = dma_map_single(priv->dev, skb->data, nopaged_len, DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->dev, des))
+ goto drop;
+
+ txq->tx_skbuff_dma[first_entry].buf = des;
+ txq->tx_skbuff_dma[first_entry].len = nopaged_len;
+ txq->tx_skbuff_dma[first_entry].last_segment = last_segment;
+ /* Prepare the first descriptor setting the OWN bit too */
+ xgmac_dma_set_tx_desc_addr(first, des);
+ first->des2 = cpu_to_le32(FIELD_PREP(XGMAC_TDES2_B1L, nopaged_len) |
+ (last_segment ? XGMAC_TDES2_IOC : 0));
+ first->des3 = cpu_to_le32(XGMAC_TDES3_OWN | XGMAC_TDES3_FD | cic |
+ (last_segment ? XGMAC_TDES3_LD : 0) |
+ FIELD_PREP(XGMAC_TDES3_FL, skb->len));
+
+ ctxt->des3 = cpu_to_le32(XGMAC_TDES3_OWN | XGMAC_TDES3_CTXT | XGMAC_TDES3_PIDV);
+ /* The descriptor must be set before tail poiner update and then barrier
+ * is needed to make sure that all is coherent before granting the
+ * DMA engine.
+ */
+ dma_wmb();
+
+ if (dev)
+ netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len);
+ txq->tx_tail_addr = txq->dma_tx_phy + txq->cur_tx * sizeof(*desc);
+ xgmac_dma_set_tx_tail_ptr(priv, txq->tx_tail_addr, channel);
+ spin_unlock(&txq->lock);
+ return NETDEV_TX_OK;
+
+drop:
+ if (dev)
+ dev->stats.tx_dropped++;
+ spin_unlock(&txq->lock);
+drop_kfree:
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+netdev_tx_t xgmac_dma_xmit_fast(struct sk_buff *skb, struct net_device *dev)
+{
+ struct gmac_common *priv = netdev_priv(dev);
+ struct xgmac_skb_cb *cb = XGMAC_SKB_CB(skb);
+ netdev_tx_t ret;
+
+ cb->id = priv->id;
+ cb->fastmode = true;
+
+ ret = xgmac_dma_xmit(skb, priv->dma);
+ if (unlikely(ret != NETDEV_TX_OK)) {
+ pr_debug_ratelimited("%s: Tx Ring full when queue awake\n",
+ __func__);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(xgmac_dma_xmit_fast);
+
+int xgmac_dma_open(struct xgmac_dma_priv *priv, struct net_device *dev, u8 id)
+{
+ if (id >= ARRAY_SIZE(priv->ndevs))
+ return -EINVAL;
+
+ if (priv->ndevs[id])
+ return -EBUSY;
+
+ priv->ndevs[id] = dev;
+
+ /* we run multiple netdevs on the same DMA ring so we only bring it
+ * up once
+ */
+ if (!refcount_read(&priv->refcnt)) {
+ int ret = xgmac_dma_enable(priv);
+ if (ret) {
+ priv->ndevs[id] = NULL;
+ return ret;
+ }
+
+ refcount_set(&priv->refcnt, 1);
+ } else {
+ refcount_inc(&priv->refcnt);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(xgmac_dma_open);
+
+int xgmac_dma_stop(struct xgmac_dma_priv *priv, struct net_device *dev, u8 id)
+{
+ if (id >= ARRAY_SIZE(priv->ndevs) || priv->ndevs[id] != dev)
+ return -EINVAL;
+
+ /* only shutdown DMA if this is the last user */
+ if (refcount_dec_and_test(&priv->refcnt))
+ xgmac_dma_disable(priv);
+
+ priv->ndevs[id] = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(xgmac_dma_stop);
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PAGE_POOL_STATS)
+static int xgmac_dma_stats_show(struct seq_file *m, void *v)
+{
+ struct xgmac_dma_priv *priv = m->private;
+ int i;
+
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ struct page_pool_stats stats = {};
+
+ page_pool_get_stats(priv->rxq[i].page_pool, &stats);
+ seq_printf(m, "RX alloc statistics:\n"
+ "fast:\t%llu\n"
+ "slow:\t%llu\n"
+ "empty:\t%llu\n"
+ "refill:\t%llu\n"
+ "waive:\t%llu\n",
+ stats.alloc_stats.fast, stats.alloc_stats.slow,
+ stats.alloc_stats.empty, stats.alloc_stats.refill,
+ stats.alloc_stats.waive);
+
+ seq_printf(m, "RX recycle statistics:\n"
+ "cached:\t%llu\n"
+ "cache_full:\t%llu\n"
+ "ring:\t%llu\n"
+ "ring_full:\t%llu\n"
+ "released_refcnt:\t%llu\n",
+ stats.recycle_stats.cached, stats.recycle_stats.cache_full,
+ stats.recycle_stats.ring, stats.recycle_stats.ring_full,
+ stats.recycle_stats.released_refcnt);
+ }
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(xgmac_dma_stats);
+#endif
+
+static int xgmac_dma_debug_show(struct seq_file *m, void *v)
+{
+ struct xgmac_dma_priv *priv = m->private;
+ int i, j;
+
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ j = (i == DMA_CH_DISABLE) ? DMA_OVPORT_CH : i;
+ spin_lock_bh(&(priv->txq[i].lock));
+ seq_printf(m, "txq %d curr:%d dirty:%d\n",
+ i, priv->txq[i].cur_tx, priv->txq[i].dirty_tx);
+ seq_printf(m, " MTL Opmode:0x%x debug:0x%x\n",
+ reg_read(priv, XGMAC_MTL_TXQ_OPMODE(j)),
+ reg_read(priv, XGMAC_MTL_TXQ_DEBUG(j)));
+ seq_printf(m, "rxq %d curr:%d dirty:%d\n",
+ i, priv->rxq[i].cur_rx, priv->rxq[i].dirty_rx);
+ seq_printf(m, " MTL Opmode:0x%x debug:0x%x\n",
+ reg_read(priv, XGMAC_MTL_RXQ_OPMODE(j)),
+ reg_read(priv, XGMAC_MTL_RXQ_DEBUG(j)));
+ spin_unlock_bh(&(priv->txq[i].lock));
+
+ seq_printf(m, "DMA channel %d status:0x%x debug sts:0x%x\n", j,
+ reg_read(priv, XGMAC_DMA_CH_STATUS(j)),
+ reg_read(priv, XGMAC_DMA_CH_DEBUG_STATUS(j)));
+ seq_printf(m, " TxDesc HAddr:0x%x TAddr:0x%x\n",
+ reg_read(priv, XGMAC_DMA_CH_TxDESC_LADDR(j)),
+ reg_read(priv, XGMAC_DMA_CH_TxDESC_TAIL_LPTR(j)));
+ seq_printf(m, " Cur desAddr:0x%x bufAddr:0x%x\n",
+ reg_read(priv, XGMAC_DMA_CH_CUR_TxDESC_LADDR(j)),
+ reg_read(priv, XGMAC_DMA_CH_CUR_TxBUFF_LADDR(j)));
+ seq_printf(m, " RxDesc HAddr:0x%x TAddr:0x%x\n",
+ reg_read(priv, XGMAC_DMA_CH_RxDESC_LADDR(j)),
+ reg_read(priv, XGMAC_DMA_CH_RxDESC_TAIL_LPTR(j)));
+ seq_printf(m, " Cur desAddr:0x%x bufAddr:0x%x\n",
+ reg_read(priv, XGMAC_DMA_CH_CUR_RxDESC_LADDR(j)),
+ reg_read(priv, XGMAC_DMA_CH_CUR_RxBUFF_LADDR(j)));
+ }
+
+ seq_printf(m, "DMA debug sts0:0x%x sts1:0x%x sts3:0x%x\n",
+ reg_read(priv, XGMAC_DMA_DEBUG_STATUS(0)),
+ reg_read(priv, XGMAC_DMA_DEBUG_STATUS(1)),
+ reg_read(priv, XGMAC_DMA_DEBUG_STATUS(3)));
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(xgmac_dma_debug);
+
+static int xgmac_dma_probe(struct platform_device *pdev)
+{
+ struct xgmac_dma_priv *priv;
+ const char *irq_name;
+ char buf[4];
+ int ret;
+ u32 i;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+ priv->ioaddr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->ioaddr))
+ return PTR_ERR(priv->ioaddr);
+
+ /* Request all clocks at once */
+ priv->clks[DMA_CLK_AXI].id = "axi";
+ priv->clks[DMA_CLK_NPU].id = "npu";
+ priv->clks[DMA_CLK_CSR].id = "csr";
+ ret = devm_clk_bulk_get(&pdev->dev, DMA_NUM_CLKS, priv->clks);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_NET_SIFLOWER_ETH_USE_INTERNAL_SRAM
+ priv->genpool = of_gen_pool_get(pdev->dev.of_node, "iram", 0);
+ if (!priv->genpool)
+ return -ENODEV;
+#endif
+
+ priv->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "ethsys");
+ if (IS_ERR(priv->ethsys))
+ return PTR_ERR(priv->ethsys);
+
+ /* lif/hif reset and release reset*/
+ ret = regmap_clear_bits(priv->ethsys, ETHSYS_RST, BIT(7) | BIT(8));
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(priv->ethsys, ETHSYS_RST, BIT(7) | BIT(8));
+ if (ret)
+ return ret;
+
+ /* set the mapping mode 0
+ * hash random use queue 0-3
+ */
+ ret = regmap_write(priv->ethsys, ETHSYS_MRI_Q_EN, 0x003F003F);
+
+ /* we run multiple netdevs on the same DMA ring so we need a dummy
+ * device for NAPI to work
+ */
+ init_dummy_netdev(&priv->napi_dev);
+
+ /* DMA IRQ */
+ ret = platform_get_irq_byname(pdev, "sbd");
+ if (ret < 0)
+ return ret;
+
+ priv->irq = ret;
+ ret = devm_request_irq(&pdev->dev, ret, xgmac_dma_irq_misc, 0,
+ "xgmac_dma_sbd", priv);
+ if (ret)
+ return ret;
+
+ irq_set_affinity_hint(priv->irq, cpumask_of(1));
+
+ /* TX IRQ */
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ snprintf(buf, sizeof(buf), "tx%u", i);
+ ret = platform_get_irq_byname(pdev, buf);
+ if (ret < 0)
+ goto out_napi_del;
+
+ priv->txq[i].irq = ret;
+
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "xgmac_dma_txq%u", i);
+ if (!irq_name) {
+ ret = -ENOMEM;
+ goto out_napi_del;
+ }
+
+ ret = devm_request_irq(&pdev->dev, ret, xgmac_dma_irq_tx, 0,
+ irq_name, &priv->txq[i]);
+ if (ret)
+ goto out_napi_del;
+
+ priv->txq[i].idx = i;
+ spin_lock_init(&priv->txq[i].lock);
+ netif_napi_add_tx_weight(&priv->napi_dev, &priv->txq[i].napi,
+ xgmac_dma_napi_tx, NAPI_POLL_WEIGHT);
+ irq_set_affinity_hint(priv->txq[i].irq, cpumask_of(i % NR_CPUS));
+ }
+
+ /* RX IRQ */
+#ifdef CONFIG_NET_SIFLOWER_ETH_RX_THREAD
+ strscpy(priv->napi_dev.name, KBUILD_MODNAME, IFNAMSIZ);
+ priv->napi_dev.threaded = 1;
+#endif
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ snprintf(buf, sizeof(buf), "rx%u", i);
+ ret = platform_get_irq_byname(pdev, buf);
+ if (ret < 0)
+ goto out_napi_del;
+
+ priv->rxq[i].irq = ret;
+
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "xgmac_dma_rxq%u", i);
+ if (!irq_name) {
+ ret = -ENOMEM;
+ goto out_napi_del;
+ }
+
+ ret = devm_request_irq(&pdev->dev, ret, xgmac_dma_irq_rx, 0,
+ irq_name, &priv->rxq[i]);
+ if (ret)
+ goto out_napi_del;
+
+ priv->rxq[i].idx = i;
+ netif_napi_add_weight(&priv->napi_dev, &priv->rxq[i].napi,
+ xgmac_dma_napi_rx, NAPI_POLL_WEIGHT);
+ irq_set_affinity_hint(priv->rxq[i].irq, cpumask_of(i % NR_CPUS));
+ }
+
+ priv->rx_alloc_size = BUF_SIZE_ALLOC(ETH_DATA_LEN);
+ priv->rx_buffer_size = BUF_SIZE_ALIGN(ETH_DATA_LEN);
+ platform_set_drvdata(pdev, priv);
+ ret = clk_bulk_prepare_enable(DMA_NUM_CLKS, priv->clks);
+ if (ret)
+ goto out_napi_del;
+
+ ret = xgmac_dma_init(priv);
+ if (ret)
+ goto out_clk_disable;
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PAGE_POOL_STATS)
+ priv->dbgdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ if (IS_ERR(priv->dbgdir)) {
+ ret = PTR_ERR(priv->dbgdir);
+ goto out_clk_disable;
+ }
+ debugfs_create_file("rx_stats", 0444, priv->dbgdir, priv, &xgmac_dma_stats_fops);
+ debugfs_create_file("debug", 0444, priv->dbgdir, priv, &xgmac_dma_debug_fops);
+#endif
+
+ return ret;
+out_clk_disable:
+ clk_bulk_disable_unprepare(DMA_NUM_CLKS, priv->clks);
+out_napi_del:
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ irq_set_affinity_hint(priv->rxq[i].irq, NULL);
+ irq_set_affinity_hint(priv->txq[i].irq, NULL);
+ netif_napi_del(&priv->rxq[i].napi);
+ netif_napi_del(&priv->txq[i].napi);
+ }
+ return ret;
+}
+
+static void xgmac_dma_remove(struct platform_device *pdev)
+{
+ struct xgmac_dma_priv *priv = platform_get_drvdata(pdev);
+ int i;
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PAGE_POOL_STATS)
+ debugfs_remove(priv->dbgdir);
+#endif
+ xgmac_dma_soft_reset(priv);
+ clk_bulk_disable_unprepare(DMA_NUM_CLKS, priv->clks);
+ for (i = 0; i < DMA_CH_MAX; i++) {
+ irq_set_affinity_hint(priv->rxq[i].irq, NULL);
+ irq_set_affinity_hint(priv->txq[i].irq, NULL);
+ netif_napi_del(&priv->rxq[i].napi);
+ netif_napi_del(&priv->txq[i].napi);
+ }
+}
+
+static const struct of_device_id xgmac_dma_match[] = {
+ { .compatible = "siflower,sf21-xgmac-dma" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xgmac_dma_match);
+
+static struct platform_driver xgmac_dma_driver = {
+ .probe = xgmac_dma_probe,
+ .remove_new = xgmac_dma_remove,
+ .driver = {
+ .name = "sfxgmac_dma",
+ .of_match_table = xgmac_dma_match,
+ },
+};
+module_platform_driver(xgmac_dma_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Qingfang Deng <qingfang.deng@siflower.com.cn>");
+MODULE_DESCRIPTION("Ethernet DMA driver for SF21A6826/SF21H8898 SoC");
\ No newline at end of file
--- /dev/null
+#ifndef __XGMAC_EXT_H_
+#define __XGMAC_EXT_H__
+
+#include <linux/clk.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/phylink.h>
+
+#define SF_GMAC_DUNMMY_ID 0xfa
+
+#define GMAC_COMMON_STRUCT \
+ void __iomem *ioaddr; \
+ struct device *dev; \
+ struct clk *csr_clk; \
+ struct xgmac_dma_priv *dma; \
+ struct regmap *ethsys; \
+ struct phylink *phylink; \
+ struct phylink_config phylink_config; \
+ u8 id; \
+ bool phy_supports_eee; \
+ bool tx_lpi_enabled; \
+ struct platform_device *pcs_dev; \
+ void *dp_port;
+
+struct gmac_common {
+ GMAC_COMMON_STRUCT;
+};
+
+#endif
\ No newline at end of file
--- /dev/null
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/etherdevice.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/ethtool.h>
+#include <linux/if.h>
+#include <linux/if_vlan.h>
+#include <linux/crc32.h>
+
+#include "dma.h"
+#include "eth.h"
+#include "sfxgmac-ext.h"
+
+struct xgmac_mib_desc {
+ char name[ETH_GSTRING_LEN];
+ u16 offset;
+};
+#define MIB_DESC(n, o, s) { .name = (n), .offset = (o), }
+
+static const struct xgmac_mib_desc xgmac_mib[] = {
+ MIB_DESC("tx_bytes", MMC_XGMAC_TX_OCTET_GB, 1),
+ MIB_DESC("tx_packets", MMC_XGMAC_TX_PKT_GB, 1),
+ MIB_DESC("tx_broadcast_packets_good", MMC_XGMAC_TX_BROAD_PKT_G, 1),
+ MIB_DESC("tx_multicast_packets_good", MMC_XGMAC_TX_MULTI_PKT_G, 1),
+ MIB_DESC("tx_64_byte_packets", MMC_XGMAC_TX_64OCT_GB, 1),
+ MIB_DESC("tx_65_to_127_byte_packets", MMC_XGMAC_TX_65OCT_GB, 1),
+ MIB_DESC("tx_128_to_255_byte_packets", MMC_XGMAC_TX_128OCT_GB, 1),
+ MIB_DESC("tx_256_to_511_byte_packets", MMC_XGMAC_TX_256OCT_GB, 1),
+ MIB_DESC("tx_512_to_1023_byte_packets", MMC_XGMAC_TX_512OCT_GB, 1),
+ MIB_DESC("tx_1024_to_max_byte_packets", MMC_XGMAC_TX_1024OCT_GB, 1),
+ MIB_DESC("tx_unicast_packets", MMC_XGMAC_TX_UNI_PKT_GB, 1),
+ MIB_DESC("tx_multicast_packets", MMC_XGMAC_TX_MULTI_PKT_GB, 1),
+ MIB_DESC("tx_broadcast_packets", MMC_XGMAC_TX_BROAD_PKT_GB, 1),
+ MIB_DESC("tx_underflow_errors", MMC_XGMAC_TX_UNDER, 1),
+ MIB_DESC("tx_bytes_good", MMC_XGMAC_TX_OCTET_G, 1),
+ MIB_DESC("tx_packets_good", MMC_XGMAC_TX_PKT_G, 1),
+ MIB_DESC("tx_pause_frames", MMC_XGMAC_TX_PAUSE, 1),
+ MIB_DESC("tx_vlan_packets_good", MMC_XGMAC_TX_VLAN_PKT_G, 1),
+ MIB_DESC("tx_lpi_usec", MMC_XGMAC_TX_LPI_USEC, 0),
+ MIB_DESC("tx_lpi_tran", MMC_XGMAC_TX_LPI_TRAN, 0),
+ MIB_DESC("rx_packets", MMC_XGMAC_RX_PKT_GB, 1),
+ MIB_DESC("rx_bytes", MMC_XGMAC_RX_OCTET_GB, 1),
+ MIB_DESC("rx_bytes_good", MMC_XGMAC_RX_OCTET_G, 1),
+ MIB_DESC("rx_broadcast_packets_good", MMC_XGMAC_RX_BROAD_PKT_G, 1),
+ MIB_DESC("rx_multicast_packets_good", MMC_XGMAC_RX_MULTI_PKT_G, 1),
+ MIB_DESC("rx_crc_errors", MMC_XGMAC_RX_CRC_ERR, 1),
+ MIB_DESC("rx_crc_errors_small_packets", MMC_XGMAC_RX_RUNT_ERR, 0),
+ MIB_DESC("rx_crc_errors_giant_packets", MMC_XGMAC_RX_JABBER_ERR, 0),
+ MIB_DESC("rx_undersize_packets_good", MMC_XGMAC_RX_UNDER, 0),
+ MIB_DESC("rx_oversize_packets_good", MMC_XGMAC_RX_OVER, 0),
+ MIB_DESC("rx_64_byte_packets", MMC_XGMAC_RX_64OCT_GB, 1),
+ MIB_DESC("rx_65_to_127_byte_packets", MMC_XGMAC_RX_65OCT_GB, 1),
+ MIB_DESC("rx_128_to_255_byte_packets", MMC_XGMAC_RX_128OCT_GB, 1),
+ MIB_DESC("rx_256_to_511_byte_packets", MMC_XGMAC_RX_256OCT_GB, 1),
+ MIB_DESC("rx_512_to_1023_byte_packets", MMC_XGMAC_RX_512OCT_GB, 1),
+ MIB_DESC("rx_1024_to_max_byte_packets", MMC_XGMAC_RX_1024OCT_GB, 1),
+ MIB_DESC("rx_unicast_packets_good", MMC_XGMAC_RX_UNI_PKT_G, 1),
+ MIB_DESC("rx_length_errors", MMC_XGMAC_RX_LENGTH_ERR, 1),
+ MIB_DESC("rx_out_of_range_errors", MMC_XGMAC_RX_RANGE, 1),
+ MIB_DESC("rx_pause_frames", MMC_XGMAC_RX_PAUSE, 1),
+ MIB_DESC("rx_fifo_overflow_errors", MMC_XGMAC_RX_FIFOOVER_PKT, 1),
+ MIB_DESC("rx_vlan_packets", MMC_XGMAC_RX_VLAN_PKT_GB, 1),
+ MIB_DESC("rx_watchdog_errors", MMC_XGMAC_RX_WATCHDOG_ERR, 0),
+ MIB_DESC("rx_lpi_usec", MMC_XGMAC_RX_LPI_USEC, 0),
+ MIB_DESC("rx_lpi_tran", MMC_XGMAC_RX_LPI_TRAN, 0),
+ MIB_DESC("rx_discard_packets", MMC_XGMAC_RX_DISCARD_PKT_GB, 1),
+ MIB_DESC("rx_discard_bytes", MMC_XGMAC_RX_DISCARD_OCT_GB, 1),
+ MIB_DESC("rx_alignment_errors", MMC_XGMAC_RX_ALIGN_ERR_PKT, 0),
+ MIB_DESC("tx_single_collision_packets", MMC_XGMAC_TX_SINGLE_COL_G, 0),
+ MIB_DESC("tx_multiple_collision_packets", MMC_XGMAC_TX_MULTI_COL_G, 0),
+ MIB_DESC("tx_deferred_packets", MMC_XGMAC_TX_DEFER, 0),
+ MIB_DESC("tx_late_collision_errors", MMC_XGMAC_TX_LATE_COL, 0),
+ MIB_DESC("tx_excessive_collision_errors", MMC_XGMAC_TX_EXCESSIVE_COL, 0),
+ MIB_DESC("tx_carrier_sense_errors", MMC_XGMAC_TX_CARRIER, 0),
+ MIB_DESC("tx_excessive_deferral_errors", MMC_XGMAC_TX_EXCESSIVE_DEFER, 0),
+ MIB_DESC("rx_ipv4_packets_good", MMC_XGMAC_RX_IPV4_PKT_G, 1),
+ MIB_DESC("rx_ipv4_header_error_packets", MMC_XGMAC_RX_IPV4_HDRERR_PKT, 1),
+ MIB_DESC("rx_ipv4_no_payload_packets", MMC_XGMAC_RX_IPV4_NOPAY_PKT, 1),
+ MIB_DESC("rx_ipv4_fragment_packets", MMC_XGMAC_RX_IPV4_FRAG_PKT, 1),
+ MIB_DESC("rx_ipv4_udp_sum_zero_packets", MMC_XGMAC_RX_IPV4_UDSBL_PKT, 1),
+ MIB_DESC("rx_ipv6_packets_good", MMC_XGMAC_RX_IPV6_PKT_G, 1),
+ MIB_DESC("rx_ipv6_header_error_packets", MMC_XGMAC_RX_IPV6_HDRERR_PKT, 1),
+ MIB_DESC("rx_ipv6_no_payload_packets", MMC_XGMAC_RX_IPV6_NOPAY_PKT, 1),
+ MIB_DESC("rx_udp_packets_good", MMC_XGMAC_RX_UDP_PKT_G, 1),
+ MIB_DESC("rx_udp_sum_error_packets", MMC_XGMAC_RX_UDP_ERR_PKT, 1),
+ MIB_DESC("rx_tcp_packets_good", MMC_XGMAC_RX_TCP_PKT_G, 1),
+ MIB_DESC("rx_tcp_sum_error_packets", MMC_XGMAC_RX_TCP_ERR_PKT, 1),
+ MIB_DESC("rx_icmp_packets_good", MMC_XGMAC_RX_ICMP_PKT_G, 1),
+ MIB_DESC("rx_icmp_sum_error_packets", MMC_XGMAC_RX_ICMP_ERR_PKT, 1),
+ MIB_DESC("rx_ipv4_bytes_good", MMC_XGMAC_RX_IPV4_OCTET_G, 1),
+ MIB_DESC("rx_ipv4_header_error_bytes", MMC_XGMAC_RX_IPV4_HDRERR_OCTET, 1),
+ MIB_DESC("rx_ipv4_no_payload_bytes", MMC_XGMAC_RX_IPV4_NOPAY_OCTET, 1),
+ MIB_DESC("rx_ipv4_fragment_bytes", MMC_XGMAC_RX_IPV4_FRAG_OCTET, 1),
+ MIB_DESC("rx_ipv4_udp_sum_zero_bytes", MMC_XGMAC_RX_IPV4_UDSBL_OCTET, 1),
+ MIB_DESC("rx_ipv6_bytes_good", MMC_XGMAC_RX_IPV6_OCTET_G, 1),
+ MIB_DESC("rx_ipv6_header_error_bytes", MMC_XGMAC_RX_IPV6_HDRERR_OCTET, 1),
+ MIB_DESC("rx_ipv6_no_payload_bytes", MMC_XGMAC_RX_IPV6_NOPAY_OCTET, 1),
+ MIB_DESC("rx_udp_bytes_good", MMC_XGMAC_RX_UDP_OCTET_G, 1),
+ MIB_DESC("rx_udp_sum_error_bytes", MMC_XGMAC_RX_UDP_ERR_OCTET, 1),
+ MIB_DESC("rx_tcp_bytes_good", MMC_XGMAC_RX_TCP_OCTET_G, 1),
+ MIB_DESC("rx_tcp_sum_error_bytes", MMC_XGMAC_RX_TCP_ERR_OCTET, 1),
+ MIB_DESC("rx_icmp_bytes_good", MMC_XGMAC_RX_ICMP_OCTET_G, 1),
+ MIB_DESC("rx_icmp_sum_error_bytes", MMC_XGMAC_RX_ICMP_ERR_OCTET, 1),
+};
+
+struct xgmac_priv {
+ GMAC_COMMON_STRUCT;
+ struct mii_bus *mii;
+ wait_queue_head_t mdio_wait;
+ spinlock_t stats_lock;
+ u32 mdio_ctrl;
+ int sbd_irq;
+ char irq_name[16];
+ u64 mib_cache[ARRAY_SIZE(xgmac_mib)];
+ struct phylink_pcs *pcs;
+};
+
+// Used by ndo_get_stats64, don't change this without changing xgmac_mib[]!
+enum {
+ STATS64_TX_PKT_GB = 1,
+ STATS64_TX_UNDER = 13,
+ STATS64_TX_OCTET_G = 14,
+ STATS64_TX_PKT_G = 15,
+ STATS64_RX_PKT_GB = 20,
+ STATS64_RX_OCTET_G = 22,
+ STATS64_RX_BROAD_PKT_G = 23,
+ STATS64_RX_MULTI_PKT_G = 24,
+ STATS64_RX_CRC_ERR = 25,
+ STATS64_RX_UNI_PKT_G = 36,
+ STATS64_RX_LENGTH_ERR = 37,
+ STATS64_RX_RANGE = 38,
+ STATS64_RX_FIFOOVER_PKT = 40,
+ STATS64_RX_ALIGN_ERR_PKT = 47,
+ STATS64_TX_SINGLE_COL_G = 48,
+ STATS64_TX_MULTI_COL_G = 49,
+ STATS64_TX_LATE_COL = 51,
+ STATS64_TX_EXCESSIVE_COL = 52,
+ STATS64_TX_CARRIER = 53,
+};
+
+// Sync MIB counters to software. Caller must hold stats_lock.
+static void xgmac_mib_sync(struct xgmac_priv *priv)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(xgmac_mib); i++)
+ priv->mib_cache[i] += reg_read(priv, xgmac_mib[i].offset);
+}
+
+static void xgmac_mib_irq_enable(struct xgmac_priv *priv)
+{
+ reg_write(priv, MMC_XGMAC_RX_INT_EN, ~0);
+ reg_write(priv, MMC_XGMAC_TX_INT_EN, ~0);
+ reg_write(priv, MMC_XGMAC_RX_IPC_INTR_MASK, 0);
+}
+
+static void xgmac_mib_irq_disable(struct xgmac_priv *priv)
+{
+ reg_write(priv, MMC_XGMAC_RX_INT_EN, 0);
+ reg_write(priv, MMC_XGMAC_TX_INT_EN, 0);
+ reg_write(priv, MMC_XGMAC_RX_IPC_INTR_MASK, ~0);
+}
+
+static void xgmac_mib_sync_begin(struct xgmac_priv *priv)
+{
+ xgmac_mib_irq_disable(priv);
+ spin_lock(&priv->stats_lock);
+}
+
+static void xgmac_mib_sync_end(struct xgmac_priv *priv)
+{
+ spin_unlock(&priv->stats_lock);
+ xgmac_mib_irq_enable(priv);
+}
+
+static int xgmac_mdio_wait(struct xgmac_priv *priv)
+{
+ unsigned long ret;
+ u32 val;
+
+ ret = wait_event_timeout(priv->mdio_wait,
+ !((val = reg_read(priv, XGMAC_MDIO_DATA)) & MII_XGMAC_BUSY),
+ HZ);
+ if (ret)
+ return FIELD_GET(MII_DATA_MASK, val);
+
+ return -ETIMEDOUT;
+}
+
+static int xgmac_mdio_read(struct mii_bus *bus, int addr, int regnum)
+{
+ struct xgmac_priv *priv = bus->priv;
+ u32 reg;
+ int ret;
+
+ ret = xgmac_mdio_wait(priv);
+ if (ret < 0)
+ return ret;
+
+ reg_set(priv, XGMAC_MDIO_C22P, BIT(addr));
+
+ reg = FIELD_PREP(MII_XGMAC_PA, addr) | FIELD_PREP(MII_XGMAC_RA, regnum);
+ reg_write(priv, XGMAC_MDIO_ADDR, reg);
+
+ reg = MII_XGMAC_BUSY | MII_XGMAC_READ | priv->mdio_ctrl;
+ reg_write(priv, XGMAC_MDIO_DATA, reg);
+
+ return xgmac_mdio_wait(priv);
+}
+
+static int xgmac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
+{
+ struct xgmac_priv *priv = bus->priv;
+ u32 reg;
+ int ret;
+
+ ret = xgmac_mdio_wait(priv);
+ if (ret < 0)
+ return ret;
+
+ reg_set(priv, XGMAC_MDIO_C22P, BIT(addr));
+
+ reg = FIELD_PREP(MII_XGMAC_PA, addr) | FIELD_PREP(MII_XGMAC_RA, regnum);
+ reg_write(priv, XGMAC_MDIO_ADDR, reg);
+
+ reg = MII_XGMAC_BUSY | MII_XGMAC_WRITE | priv->mdio_ctrl |
+ FIELD_PREP(MII_DATA_MASK, val);
+ reg_write(priv, XGMAC_MDIO_DATA, reg);
+
+ ret = xgmac_mdio_wait(priv);
+ return ret < 0 ? ret : 0;
+}
+
+static int xgmac_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum)
+{
+ struct xgmac_priv *priv = bus->priv;
+ u32 reg;
+ int ret;
+
+ ret = xgmac_mdio_wait(priv);
+ if (ret < 0)
+ return ret;
+
+ reg_clear(priv, XGMAC_MDIO_C22P, BIT(addr));
+
+ reg = FIELD_PREP(MII_XGMAC_PA, addr) | FIELD_PREP(MII_XGMAC_DA, devnum) |
+ FIELD_PREP(MII_XGMAC_RA, regnum);
+ reg_write(priv, XGMAC_MDIO_ADDR, reg);
+
+ reg = MII_XGMAC_BUSY | MII_XGMAC_READ | priv->mdio_ctrl;
+ reg_write(priv, XGMAC_MDIO_DATA, reg);
+
+ return xgmac_mdio_wait(priv);
+}
+
+static int xgmac_mdio_write_c45(struct mii_bus *bus, int addr, int devnum, int regnum, u16 val)
+{
+ struct xgmac_priv *priv = bus->priv;
+ u32 reg;
+ int ret;
+
+ ret = xgmac_mdio_wait(priv);
+ if (ret < 0)
+ return ret;
+
+ reg_clear(priv, XGMAC_MDIO_C22P, BIT(addr));
+
+ reg = FIELD_PREP(MII_XGMAC_PA, addr) | FIELD_PREP(MII_XGMAC_DA, devnum) |
+ FIELD_PREP(MII_XGMAC_RA, regnum);
+ reg_write(priv, XGMAC_MDIO_ADDR, reg);
+
+ reg = MII_XGMAC_BUSY | MII_XGMAC_WRITE | priv->mdio_ctrl |
+ FIELD_PREP(MII_DATA_MASK, val);
+ reg_write(priv, XGMAC_MDIO_DATA, reg);
+
+ ret = xgmac_mdio_wait(priv);
+ return ret < 0 ? ret : 0;
+}
+
+static int xgmac_mdio_init(struct xgmac_priv *priv)
+{
+ struct device_node *mdio_node = NULL, *np = priv->dev->of_node;
+ u32 csr_freq = clk_get_rate(priv->csr_clk);
+ int ret = -ENOMEM;
+ u32 freq;
+
+ mdio_node = of_get_child_by_name(np, "mdio");
+ if (!mdio_node)
+ return 0;
+
+ if (of_property_read_bool(mdio_node, "suppress-preamble"))
+ priv->mdio_ctrl |= MII_XGMAC_PSE;
+
+ if (of_property_read_u32(mdio_node, "clock-frequency", &freq))
+ freq = 2500000;
+
+ if (freq > DIV_ROUND_UP(csr_freq, 4))
+ dev_warn(priv->dev,
+ "MDC frequency %uHz too high, reducing to %uHz\n",
+ freq, DIV_ROUND_UP(csr_freq, 4));
+ else if (freq < csr_freq / 202)
+ dev_warn(priv->dev,
+ "MDC frequency %uHz too low, increasing to %uHz\n",
+ freq, csr_freq / 202);
+
+ if (freq >= DIV_ROUND_UP(csr_freq, 4))
+ priv->mdio_ctrl |= MII_XGMAC_CRS | FIELD_PREP(MII_XGMAC_CR, 0);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 6))
+ priv->mdio_ctrl |= MII_XGMAC_CRS | FIELD_PREP(MII_XGMAC_CR, 1);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 8))
+ priv->mdio_ctrl |= MII_XGMAC_CRS | FIELD_PREP(MII_XGMAC_CR, 2);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 10))
+ priv->mdio_ctrl |= MII_XGMAC_CRS | FIELD_PREP(MII_XGMAC_CR, 3);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 12))
+ priv->mdio_ctrl |= MII_XGMAC_CRS | FIELD_PREP(MII_XGMAC_CR, 4);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 14))
+ priv->mdio_ctrl |= MII_XGMAC_CRS | FIELD_PREP(MII_XGMAC_CR, 5);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 16))
+ priv->mdio_ctrl |= MII_XGMAC_CRS | FIELD_PREP(MII_XGMAC_CR, 6);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 18))
+ priv->mdio_ctrl |= MII_XGMAC_CRS | FIELD_PREP(MII_XGMAC_CR, 7);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 62))
+ priv->mdio_ctrl |= FIELD_PREP(MII_XGMAC_CR, 0);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 102))
+ priv->mdio_ctrl |= FIELD_PREP(MII_XGMAC_CR, 1);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 122))
+ priv->mdio_ctrl |= FIELD_PREP(MII_XGMAC_CR, 2);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 142))
+ priv->mdio_ctrl |= FIELD_PREP(MII_XGMAC_CR, 3);
+ else if (freq >= DIV_ROUND_UP(csr_freq, 162))
+ priv->mdio_ctrl |= FIELD_PREP(MII_XGMAC_CR, 4);
+ else
+ priv->mdio_ctrl |= FIELD_PREP(MII_XGMAC_CR, 5);
+
+ priv->mii = devm_mdiobus_alloc(priv->dev);
+ if (!priv->mii)
+ goto cleanup;
+
+ priv->mii->name = "xgmac";
+ priv->mii->priv = priv;
+ priv->mii->read = xgmac_mdio_read;
+ priv->mii->write = xgmac_mdio_write;
+ priv->mii->read_c45 = xgmac_mdio_read_c45;
+ priv->mii->write_c45 = xgmac_mdio_write_c45;
+ snprintf(priv->mii->id, MII_BUS_ID_SIZE, "xgmac%u", priv->id);
+ init_waitqueue_head(&priv->mdio_wait);
+ reg_write(priv, XGMAC_MDIO_INT_EN, XGMAC_MDIO_INT_EN_SINGLE);
+
+ ret = devm_of_mdiobus_register(priv->dev, priv->mii, mdio_node);
+cleanup:
+ of_node_put(mdio_node);
+ return ret;
+}
+
+static irqreturn_t xgmac_irq(int irq, void *dev_id)
+{
+ struct xgmac_priv *priv = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ u32 status;
+
+ status = reg_read(priv, XGMAC_INT_STATUS);
+ if (status & XGMAC_MMCIS) {
+ if (spin_trylock(&priv->stats_lock)) {
+ xgmac_mib_sync(priv);
+ spin_unlock(&priv->stats_lock);
+ }
+ ret = IRQ_HANDLED;
+ }
+ if (status & XGMAC_SMI) {
+ reg_read(priv, XGMAC_MDIO_INT_STATUS);
+ wake_up(&priv->mdio_wait);
+ ret = IRQ_HANDLED;
+ }
+ /* LSI is set regardless of LSIE bit in XGMAC_INT_EN */
+ if (status & XGMAC_LSI && reg_read(priv, XGMAC_INT_EN) & XGMAC_LSI) {
+ phylink_mac_change(priv->phylink, status & XGMAC_RGMII_LS);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static void xgmac_write_mac_addr(struct xgmac_priv *priv, const u8 *addr,
+ u32 reg)
+{
+ u32 val;
+
+ /* For MAC Addr registers we have to set the Address Enable (AE)
+ * bit that has no effect on the High Reg 0 where the bit 31 (MO)
+ * is RO.
+ */
+ val = GMAC_HI_REG_AE | (addr[5] << 8) | addr[4];
+ reg_write(priv, XGMAC_ADDRx_HIGH(reg), val);
+ val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
+ reg_write(priv, XGMAC_ADDRx_LOW(reg), val);
+}
+
+static int xgmac_open(struct net_device *dev)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+ int ret;
+
+ ret = phylink_of_phy_connect(priv->phylink, priv->dev->of_node, 0);
+ if (ret)
+ return ret;
+
+ ret = xgmac_dma_open(priv->dma, dev, priv->id);
+ if (ret) {
+ phylink_disconnect_phy(priv->phylink);
+ return ret;
+ }
+
+ phylink_start(priv->phylink);
+ netif_tx_start_all_queues(dev);
+
+ return 0;
+}
+
+static int xgmac_stop(struct net_device *dev)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ phylink_stop(priv->phylink);
+ netif_tx_stop_all_queues(dev);
+ phylink_disconnect_phy(priv->phylink);
+
+ return xgmac_dma_stop(priv->dma, dev, priv->id);
+}
+
+static void xgmac_set_rx_mode(struct net_device *dev)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+ unsigned int value = 0;
+ unsigned int perfect_addr_number = 4;
+
+ pr_debug("%s: # mcasts %d, # unicast %d\n", __func__,
+ netdev_mc_count(dev), netdev_uc_count(dev));
+
+ if (dev->flags & IFF_PROMISC) {
+ value = XGMAC_FILTER_PR;
+ } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
+ value = XGMAC_FILTER_PM; /* pass all multi */
+ }
+
+ /* Handle multiple unicast addresses (perfect filtering) */
+ if (netdev_uc_count(dev) > perfect_addr_number)
+ /* Switch to promiscuous mode if more than unicast
+ * addresses are requested than supported by hardware.
+ */
+ value |= XGMAC_FILTER_PR;
+ else {
+ int reg = 1;
+ struct netdev_hw_addr *ha;
+
+ netdev_for_each_uc_addr(ha, dev) {
+ xgmac_write_mac_addr(priv, ha->addr, reg);
+ reg++;
+ }
+
+ while (reg <= perfect_addr_number) {
+ reg_write(priv, XGMAC_ADDRx_HIGH(reg), 0);
+ reg_write(priv, XGMAC_ADDRx_LOW(reg), 0);
+ reg++;
+ }
+ }
+
+#ifdef FRAME_FILTER_DEBUG
+ /* Enable Receive all mode (to debug filtering_fail errors) */
+ value |= XGMAC_FILTER_RA;
+#endif
+ reg_write(priv, XGMAC_PACKET_FILTER, value);
+}
+
+static int xgmac_set_mac_address(struct net_device *dev, void *p)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+ int ret;
+
+ ret = eth_mac_addr(dev, p);
+ if (ret)
+ return ret;
+
+ xgmac_write_mac_addr(priv, dev->dev_addr, 0);
+
+ return 0;
+}
+
+static int xgmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ return phylink_mii_ioctl(priv->phylink, ifr, cmd);
+}
+
+static int xgmac_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ netdev_features_t diff = dev->features ^ features;
+ struct xgmac_priv *priv = netdev_priv(dev);
+ u32 ctrl = reg_read(priv, XGMAC_RX_CONFIG);
+
+ if (diff & NETIF_F_LOOPBACK) {
+ if (features & NETIF_F_LOOPBACK) {
+ netdev_info(dev, "MAC internal loopback enabled\n");
+ ctrl |= XGMAC_CONFIG_LM;
+ } else {
+ netdev_info(dev, "MAC internal loopback disabled\n");
+ ctrl &= ~XGMAC_CONFIG_LM;
+ }
+ }
+
+ if (diff & NETIF_F_RXFCS) {
+ if (features & NETIF_F_RXFCS) {
+ netdev_info(dev, "MAC FCS stripping disabled\n");
+ ctrl &= ~(XGMAC_CONFIG_ACS | XGMAC_CONFIG_CST);
+ } else {
+ netdev_info(dev, "MAC FCS stripping enabled\n");
+ ctrl |= XGMAC_CONFIG_ACS | XGMAC_CONFIG_CST;
+ }
+ }
+
+ if (diff & NETIF_F_RXCSUM) {
+ if (features & NETIF_F_RXCSUM) {
+ netdev_info(dev, "MAC Rx checksum offload enabled\n");
+ ctrl |= XGMAC_CONFIG_IPC;
+ } else {
+ netdev_info(dev, "MAC Rx checksum offload disabled\n");
+ ctrl &= ~XGMAC_CONFIG_IPC;
+ }
+ }
+
+ if (diff & NETIF_F_RXALL) {
+ u32 rff = reg_read(priv, XGMAC_PACKET_FILTER);
+
+ if (features & NETIF_F_RXALL) {
+ ctrl |= XGMAC_CONFIG_DCRCC;
+ rff |= XGMAC_FILTER_RA;
+ } else {
+ ctrl &= ~XGMAC_CONFIG_DCRCC;
+ rff &= ~XGMAC_FILTER_RA;
+ }
+ reg_write(priv, XGMAC_PACKET_FILTER, rff);
+ }
+ reg_write(priv, XGMAC_RX_CONFIG, ctrl);
+
+ return 0;
+}
+
+static int xgmac_get_phys_port_id(struct net_device *dev,
+ struct netdev_phys_item_id *ppid)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ ppid->id[0] = priv->id;
+ ppid->id_len = 1;
+
+ return 0;
+}
+
+static int xgmac_get_port_parent_id(struct net_device *dev,
+ struct netdev_phys_item_id *ppid)
+{
+ ppid->id[0] = SF_GMAC_DUNMMY_ID;
+ ppid->id_len = 1;
+
+ return 0;
+}
+
+static void xgmac_neigh_destroy(struct net_device *dev,
+ struct neighbour *n)
+{
+ // struct xgmac_priv *priv = netdev_priv(dev);
+
+ /** TODO: call dpns->ops->port_neigh_destroy(dp_port, n); */
+ return;
+}
+static void xgmac_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+ const u64 *mib;
+
+ // Compile-time check in case someone changes the order
+ BUILD_BUG_ON(xgmac_mib[STATS64_TX_PKT_GB].offset != MMC_XGMAC_TX_PKT_GB);
+ BUILD_BUG_ON(xgmac_mib[STATS64_TX_UNDER].offset != MMC_XGMAC_TX_UNDER);
+ BUILD_BUG_ON(xgmac_mib[STATS64_TX_OCTET_G].offset != MMC_XGMAC_TX_OCTET_G);
+ BUILD_BUG_ON(xgmac_mib[STATS64_TX_PKT_G].offset != MMC_XGMAC_TX_PKT_G);
+ BUILD_BUG_ON(xgmac_mib[STATS64_RX_PKT_GB].offset != MMC_XGMAC_RX_PKT_GB);
+ BUILD_BUG_ON(xgmac_mib[STATS64_RX_OCTET_G].offset != MMC_XGMAC_RX_OCTET_G);
+ BUILD_BUG_ON(xgmac_mib[STATS64_RX_BROAD_PKT_G].offset != MMC_XGMAC_RX_BROAD_PKT_G);
+ BUILD_BUG_ON(xgmac_mib[STATS64_RX_MULTI_PKT_G].offset != MMC_XGMAC_RX_MULTI_PKT_G);
+ BUILD_BUG_ON(xgmac_mib[STATS64_RX_CRC_ERR].offset != MMC_XGMAC_RX_CRC_ERR);
+ BUILD_BUG_ON(xgmac_mib[STATS64_RX_UNI_PKT_G].offset != MMC_XGMAC_RX_UNI_PKT_G);
+ BUILD_BUG_ON(xgmac_mib[STATS64_RX_LENGTH_ERR].offset != MMC_XGMAC_RX_LENGTH_ERR);
+ BUILD_BUG_ON(xgmac_mib[STATS64_RX_RANGE].offset != MMC_XGMAC_RX_RANGE);
+ BUILD_BUG_ON(xgmac_mib[STATS64_RX_FIFOOVER_PKT].offset != MMC_XGMAC_RX_FIFOOVER_PKT);
+ BUILD_BUG_ON(xgmac_mib[STATS64_RX_ALIGN_ERR_PKT].offset != MMC_XGMAC_RX_ALIGN_ERR_PKT);
+ BUILD_BUG_ON(xgmac_mib[STATS64_TX_SINGLE_COL_G].offset != MMC_XGMAC_TX_SINGLE_COL_G);
+ BUILD_BUG_ON(xgmac_mib[STATS64_TX_MULTI_COL_G].offset != MMC_XGMAC_TX_MULTI_COL_G);
+ BUILD_BUG_ON(xgmac_mib[STATS64_TX_LATE_COL].offset != MMC_XGMAC_TX_LATE_COL);
+ BUILD_BUG_ON(xgmac_mib[STATS64_TX_EXCESSIVE_COL].offset != MMC_XGMAC_TX_EXCESSIVE_COL);
+ BUILD_BUG_ON(xgmac_mib[STATS64_TX_CARRIER].offset != MMC_XGMAC_TX_CARRIER);
+
+ xgmac_mib_sync_begin(priv);
+ xgmac_mib_sync(priv);
+ mib = priv->mib_cache;
+ stats->rx_packets = mib[STATS64_RX_BROAD_PKT_G] +
+ mib[STATS64_RX_MULTI_PKT_G] +
+ mib[STATS64_RX_UNI_PKT_G];
+ stats->tx_packets = mib[STATS64_TX_PKT_G];
+ stats->rx_bytes = mib[STATS64_RX_OCTET_G];
+ stats->tx_bytes = mib[STATS64_TX_OCTET_G];
+ stats->rx_errors = mib[STATS64_RX_PKT_GB] - stats->rx_packets;
+ stats->tx_errors = mib[STATS64_TX_PKT_GB] - stats->tx_packets;
+ stats->multicast = mib[STATS64_RX_BROAD_PKT_G] +
+ mib[STATS64_RX_MULTI_PKT_G];
+ stats->collisions = mib[STATS64_TX_SINGLE_COL_G] +
+ mib[STATS64_TX_MULTI_COL_G];
+ stats->rx_length_errors = mib[STATS64_RX_LENGTH_ERR] +
+ mib[STATS64_RX_RANGE];
+ stats->rx_over_errors = mib[STATS64_RX_FIFOOVER_PKT];
+ stats->rx_crc_errors = mib[STATS64_RX_CRC_ERR];
+ stats->rx_frame_errors = mib[STATS64_RX_ALIGN_ERR_PKT];
+ stats->tx_aborted_errors = mib[STATS64_TX_EXCESSIVE_COL];
+ stats->tx_carrier_errors = mib[STATS64_TX_CARRIER];
+ stats->tx_fifo_errors = mib[STATS64_TX_UNDER];
+ stats->tx_window_errors = mib[STATS64_TX_LATE_COL];
+ xgmac_mib_sync_end(priv);
+}
+
+static int xgmac_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+ u32 step;
+
+ dev->mtu = new_mtu;
+
+ /* Configure GPSL field in XGMAC_RX_CONFIG. L2 header and FCS must
+ * be taken into account. For a MTU of 1500 bytes, this field is set
+ * to 1518. For VLAN tagged packets, the hardware adds 4 bytes
+ * (single tagged) or 8 bytes (double tagged) to the programmed value.
+ */
+ new_mtu += ETH_HLEN + ETH_FCS_LEN;
+ reg_rmw(priv, XGMAC_RX_CONFIG, XGMAC_CONFIG_GPSL,
+ FIELD_PREP(XGMAC_CONFIG_GPSL, new_mtu) | XGMAC_CONFIG_GPSLCE);
+
+ /* Configure Rx watchdog and Tx jabber threshold, with 1KB step.
+ * Unlike the GPSL field, the hardware does not count the VLAN tag
+ * overhead so it must be manually added.
+ */
+ new_mtu = min(new_mtu + VLAN_HLEN * 2, MAX_FRAME_SIZE);
+ step = DIV_ROUND_UP((u32)new_mtu, SZ_1K);
+ // The step begins at 2KB
+ step = step < 2 ? 0 : step - 2;
+ reg_write(priv, XGMAC_WD_JB_TIMEOUT, XGMAC_PJE | XGMAC_PWE |
+ FIELD_PREP(XGMAC_JTO, step) | FIELD_PREP(XGMAC_WTO, step));
+
+ return 0;
+}
+
+static const struct net_device_ops xgmac_netdev_ops = {
+ .ndo_open = xgmac_open,
+ .ndo_stop = xgmac_stop,
+ .ndo_start_xmit = xgmac_dma_xmit_fast,
+ .ndo_set_rx_mode = xgmac_set_rx_mode,
+ .ndo_set_mac_address = xgmac_set_mac_address,
+ .ndo_do_ioctl = xgmac_ioctl,
+ .ndo_set_features = xgmac_set_features,
+ .ndo_get_phys_port_id = xgmac_get_phys_port_id,
+ .ndo_get_port_parent_id = xgmac_get_port_parent_id,
+ .ndo_neigh_destroy = xgmac_neigh_destroy,
+ .ndo_get_stats64 = xgmac_get_stats64,
+ .ndo_change_mtu = xgmac_change_mtu,
+};
+
+static void xgmac_validate(struct phylink_config *config,
+ unsigned long *supported,
+ struct phylink_link_state *state)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mac_supported) = {};
+
+ phylink_set(mac_supported, 10baseT_Half);
+ phylink_set(mac_supported, 10baseT_Full);
+ phylink_set(mac_supported, 100baseT_Half);
+ phylink_set(mac_supported, 100baseT_Full);
+ phylink_set(mac_supported, 1000baseT_Full);
+ phylink_set(mac_supported, 1000baseX_Full);
+ phylink_set(mac_supported, 1000baseKX_Full);
+ phylink_set(mac_supported, 2500baseT_Full);
+ phylink_set(mac_supported, 2500baseX_Full);
+
+ phylink_set(mac_supported, Autoneg);
+ phylink_set(mac_supported, Pause);
+ phylink_set(mac_supported, Asym_Pause);
+ phylink_set_port_modes(mac_supported);
+
+ linkmode_and(supported, supported, mac_supported);
+ linkmode_and(state->advertising, state->advertising, mac_supported);
+}
+
+static struct xgmac_priv *sfxgmac_phylink_to_port(struct phylink_config *config)
+{
+ return container_of(config, struct xgmac_priv, phylink_config);
+}
+
+static struct phylink_pcs *xgmac_mac_selct_pcs(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ struct xgmac_priv *priv = sfxgmac_phylink_to_port(config);
+
+ return priv->pcs;
+}
+
+static void xgmac_toggle_tx_lpi(struct xgmac_priv *priv)
+{
+ if (priv->phy_supports_eee && priv->tx_lpi_enabled) {
+ reg_set(priv, XGMAC_LPI_CTRL,
+ XGMAC_LPIATE | XGMAC_LPITXA | XGMAC_LPITXEN);
+ } else {
+ reg_clear(priv, XGMAC_LPI_CTRL,
+ XGMAC_LPIATE | XGMAC_LPITXA | XGMAC_LPITXEN);
+ }
+}
+
+static void xgmac_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct xgmac_priv *priv = netdev_priv(to_net_dev(config->dev));
+
+ /* Enable link change interrupt for RGMII */
+ if (phy_interface_mode_is_rgmii(state->interface) &&
+ phylink_autoneg_inband(mode))
+ reg_set(priv, XGMAC_INT_EN, XGMAC_LSI);
+ else
+ reg_clear(priv, XGMAC_INT_EN, XGMAC_LSI);
+}
+
+static void xgmac_mac_reset(struct xgmac_priv *priv)
+{
+ struct net_device *ndev = priv_to_netdev(priv);
+ int ret;
+
+ if (priv->id < 5) {
+ ret = regmap_clear_bits(priv->ethsys, ETHSYS_RST, BIT(priv->id));
+ if (ret)
+ return;
+
+ ret = regmap_set_bits(priv->ethsys, ETHSYS_RST, BIT(priv->id));
+ if (ret)
+ return;
+ }else {
+ ret = regmap_clear_bits(priv->ethsys, ETHSYS_RST, ETHSYS_RST_MAC5);
+ if (ret)
+ return;
+
+ /* set mac5 phy mode to rgmii, should set under mac reset */
+ ret = regmap_write(priv->ethsys, ETHSYS_MAC5_CTRL,
+ FIELD_PREP(MAC5_PHY_INTF_SEL, 1));
+ if (ret)
+ return;
+
+ ret = regmap_set_bits(priv->ethsys, ETHSYS_RST, ETHSYS_RST_MAC5);
+ if (ret)
+ return;
+ }
+
+ regmap_set_bits(priv->ethsys, ETHSYS_TX_DIS, BIT(priv->id));
+ reg_clear(priv, XGMAC_TX_CONFIG, XGMAC_CONFIG_TE);
+ reg_clear(priv, XGMAC_INT_EN, XGMAC_LSI);
+ xgmac_write_mac_addr(priv, ndev->dev_addr, 0);
+ reg_set(priv, MMC_XGMAC_CONTROL,
+ MMC_XGMAC_CONTROL_RESET | MMC_XGMAC_CONTROL_RSTONRD);
+ reg_write(priv, XGMAC_TX_CONFIG, XGMAC_CORE_INIT_TX);
+ reg_write(priv, XGMAC_RX_CONFIG, XGMAC_CORE_INIT_RX);
+ reg_write(priv, XGMAC_WD_JB_TIMEOUT, XGMAC_PJE | XGMAC_PWE);
+ reg_write(priv, XGMAC_VLAN_TAG, XGMAC_VLAN_EDVLP);
+ reg_write(priv, XGMAC_LPI_TIMER_CTRL,
+ FIELD_PREP(XGMAC_LPI_LST, XGMAC_LPI_LST_DEFAULT) |
+ FIELD_PREP(XGMAC_LPI_TWT, XGMAC_LPI_TWT_DEFAULT));
+ reg_write(priv, XGMAC_LPI_AUTO_EN, XGMAC_LPI_AUTO_EN_DEFAULT);
+ xgmac_mib_irq_enable(priv);
+ reg_write(priv, XGMAC_LPI_1US,
+ clk_get_rate(priv->csr_clk) / 1000000 - 1);
+}
+
+static void wait_queue_empty(struct xgmac_priv *priv,
+ phy_interface_t interface)
+{
+ unsigned long timeout = jiffies + HZ/50;
+
+ do {
+ if (!(reg_read(priv, XGMAC_MTL_TXQ_OPMODE(0)) & XGMAC_FTQ))
+ return;
+
+ cond_resched();
+ } while (time_after(timeout, jiffies));
+
+ xgmac_mac_reset(priv);
+ printk("wait queue empty timed out\n");
+}
+
+static void xgmac_mac_link_down(struct phylink_config *config,
+ unsigned int mode, phy_interface_t interface)
+{
+ struct xgmac_priv *priv = netdev_priv(to_net_dev(config->dev));
+
+ regmap_set_bits(priv->ethsys, ETHSYS_TX_DIS, BIT(priv->id));
+ reg_clear(priv, XGMAC_TX_CONFIG, XGMAC_CONFIG_TE);
+ reg_set(priv, XGMAC_MTL_TXQ_OPMODE(0), XGMAC_FTQ);
+ wait_queue_empty(priv, interface);
+ reg_clear(priv, XGMAC_LPI_CTRL, XGMAC_PLS);
+}
+
+static void xgmac_mac_link_up(struct phylink_config *config,
+ struct phy_device *phy, unsigned int mode,
+ phy_interface_t interface, int speed, int duplex,
+ bool tx_pause, bool rx_pause) {
+ struct xgmac_priv *priv = netdev_priv(to_net_dev(config->dev));
+ u32 txc, rxfc, txfc;
+
+ txc = reg_read(priv, XGMAC_TX_CONFIG);
+ txc |= XGMAC_CONFIG_TE;
+ txc &= ~XGMAC_CONFIG_SS_MASK;
+
+ switch (speed) {
+ case SPEED_2500:
+ txc |= XGMAC_CONFIG_SS_2500_GMII;
+ break;
+ case SPEED_1000:
+ txc |= XGMAC_CONFIG_SS_1000_GMII;
+ break;
+ case SPEED_100:
+ txc |= XGMAC_CONFIG_SS_100_MII;
+ break;
+ case SPEED_10:
+ txc |= XGMAC_CONFIG_SS_10_MII;
+ break;
+ default:
+ return;
+ }
+
+ if (duplex == DUPLEX_FULL)
+ reg_clear(priv, XGMAC_MAC_EXT_CONFIG, XGMAC_HD);
+ else
+ reg_set(priv, XGMAC_MAC_EXT_CONFIG, XGMAC_HD);
+
+ rxfc = XGMAC_UP;
+ if (rx_pause)
+ rxfc |= XGMAC_RFE;
+
+ reg_write(priv, XGMAC_RX_FLOW_CTRL, rxfc);
+
+ txfc = 0;
+ if (tx_pause) {
+ txfc |= XGMAC_TFE;
+ if (duplex == DUPLEX_FULL)
+ txfc |= FIELD_PREP(XGMAC_PT, 0x400);
+ }
+ reg_write(priv, XGMAC_Qx_TX_FLOW_CTRL(0), txfc);
+
+ reg_write(priv, XGMAC_TX_CONFIG, txc);
+ reg_set(priv, XGMAC_RX_CONFIG, XGMAC_CONFIG_RE);
+ reg_set(priv, XGMAC_LPI_CTRL, XGMAC_PLS);
+ regmap_clear_bits(priv->ethsys, ETHSYS_TX_DIS, BIT(priv->id));
+
+ if (phy)
+ priv->phy_supports_eee = !phy_init_eee(phy, true);
+ else
+ priv->phy_supports_eee = false;
+
+ xgmac_toggle_tx_lpi(priv);
+}
+
+static const struct phylink_mac_ops xgmac_phylink_mac_ops = {
+ .validate = xgmac_validate,
+ .mac_select_pcs = xgmac_mac_selct_pcs,
+ .mac_config = xgmac_mac_config,
+ .mac_link_down = xgmac_mac_link_down,
+ .mac_link_up = xgmac_mac_link_up,
+};
+
+static void xgmac_ethtool_get_wol(struct net_device *dev,
+ struct ethtool_wolinfo *wol)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ return phylink_ethtool_get_wol(priv->phylink, wol);
+}
+
+static int xgmac_ethtool_set_wol(struct net_device *dev,
+ struct ethtool_wolinfo *wol)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ return phylink_ethtool_set_wol(priv->phylink, wol);
+}
+
+static int xgmac_ethtool_nway_reset(struct net_device *dev)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ return phylink_ethtool_nway_reset(priv->phylink);
+}
+
+static void xgmac_ethtool_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pause)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ phylink_ethtool_get_pauseparam(priv->phylink, pause);
+}
+
+static int xgmac_ethtool_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pause)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ return phylink_ethtool_set_pauseparam(priv->phylink, pause);
+}
+
+
+static void xgmac_ethtool_get_strings(struct net_device *dev, u32 stringset,
+ u8 *data)
+{
+ int i;
+
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(xgmac_mib); i++) {
+ memcpy(data, xgmac_mib[i].name, ETH_GSTRING_LEN);
+ data += ETH_GSTRING_LEN;
+ }
+}
+
+static void xgmac_ethtool_get_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ xgmac_mib_sync_begin(priv);
+ xgmac_mib_sync(priv);
+ memcpy(data, priv->mib_cache, sizeof(priv->mib_cache));
+ xgmac_mib_sync_end(priv);
+}
+
+static int xgmac_ethtool_reset(struct net_device *dev, u32 *flags)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ if (*flags & ETH_RESET_MGMT) {
+ xgmac_mib_sync_begin(priv);
+ reg_set(priv, MMC_XGMAC_CONTROL, MMC_XGMAC_CONTROL_RESET);
+ memset(priv->mib_cache, 0, sizeof(priv->mib_cache));
+ xgmac_mib_sync_end(priv);
+ *flags &= ~ETH_RESET_MGMT;
+ }
+
+ return 0;
+}
+
+static int xgmac_ethtool_get_sset_count(struct net_device *dev, int stringset)
+{
+ switch (stringset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(xgmac_mib);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int xgmac_ethtool_get_link_ksettings(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ return phylink_ethtool_ksettings_get(priv->phylink, cmd);
+}
+
+static int xgmac_ethtool_get_eee(struct net_device *dev, struct ethtool_eee *e)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+ int ret;
+
+ ret = phylink_ethtool_get_eee(priv->phylink, e);
+ if (ret)
+ return ret;
+
+ e->tx_lpi_enabled = priv->tx_lpi_enabled;
+ e->tx_lpi_timer = reg_read(priv, XGMAC_LPI_AUTO_EN);
+
+ return 0;
+}
+
+static int xgmac_ethtool_set_eee(struct net_device *dev, struct ethtool_eee *e)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+ int ret;
+
+ if (e->tx_lpi_timer > XGMAC_LPI_AUTO_EN_MAX)
+ return -EINVAL;
+
+ ret = phylink_ethtool_set_eee(priv->phylink, e);
+ if (ret)
+ return ret;
+
+ priv->tx_lpi_enabled = e->tx_lpi_enabled;
+ xgmac_toggle_tx_lpi(priv);
+
+ reg_write(priv, XGMAC_LPI_AUTO_EN, e->tx_lpi_timer);
+
+ return 0;
+}
+
+static int xgmac_ethtool_set_link_ksettings(struct net_device *dev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ return phylink_ethtool_ksettings_set(priv->phylink, cmd);
+}
+
+static const struct ethtool_ops xgmac_ethtool_ops = {
+ .get_wol = xgmac_ethtool_get_wol,
+ .set_wol = xgmac_ethtool_set_wol,
+ .nway_reset = xgmac_ethtool_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_pauseparam = xgmac_ethtool_get_pauseparam,
+ .set_pauseparam = xgmac_ethtool_set_pauseparam,
+ .get_strings = xgmac_ethtool_get_strings,
+ .get_ethtool_stats = xgmac_ethtool_get_stats,
+ .reset = xgmac_ethtool_reset,
+ .get_sset_count = xgmac_ethtool_get_sset_count,
+ .get_eee = xgmac_ethtool_get_eee,
+ .set_eee = xgmac_ethtool_set_eee,
+ .get_link_ksettings = xgmac_ethtool_get_link_ksettings,
+ .set_link_ksettings = xgmac_ethtool_set_link_ksettings,
+};
+
+static int xgmac_rgmii_delay(struct xgmac_priv *priv, phy_interface_t phy_mode)
+{
+ u32 reg = 0, rxd = MAC5_DELAY_DEFAULT, txd = MAC5_DELAY_DEFAULT;
+
+ of_property_read_u32(priv->dev->of_node, "rx-internal-delay-ps", &rxd);
+ of_property_read_u32(priv->dev->of_node, "tx-internal-delay-ps", &txd);
+
+ rxd = DIV_ROUND_CLOSEST(rxd, MAC5_DELAY_STEP);
+ txd = DIV_ROUND_CLOSEST(txd, MAC5_DELAY_STEP);
+
+ if (rxd > 256 || txd > 256)
+ return -EINVAL;
+
+ if (rxd)
+ reg |= FIELD_PREP(MAC5_RX_DELAY, rxd - 1) | MAC5_RX_DELAY_EN;
+
+ if (txd)
+ reg |= FIELD_PREP(MAC5_TX_DELAY, txd - 1) | MAC5_TX_DELAY_EN;
+
+ return regmap_update_bits(priv->ethsys, ETHSYS_MAC(5),
+ MAC5_DELAY_MASK, reg);
+}
+
+static int xgmac_phy_setup(struct xgmac_priv *priv)
+{
+ struct device *dev = priv->dev;
+ struct device_node *np = dev->of_node;
+ phy_interface_t phy_mode;
+ struct phylink *phylink;
+ struct platform_device *pcs_dev;
+ struct phylink_pcs *pcs = NULL;
+ int ret;
+
+ ret = of_get_phy_mode(np, &phy_mode);
+ if (ret)
+ return ret;
+
+ if (phy_interface_mode_is_rgmii(phy_mode)) {
+ ret = xgmac_rgmii_delay(priv, phy_mode);
+ if (ret)
+ return ret;
+
+ priv->csr_clk = devm_clk_get_enabled(dev, "rgmii");
+ if (IS_ERR(priv->csr_clk))
+ return PTR_ERR(priv->csr_clk);
+
+ if (ret)
+ return ret;
+ } else {
+ struct of_phandle_args pcs_args;
+ ret = of_parse_phandle_with_fixed_args(np, "pcs-handle", 1, 0,
+ &pcs_args);
+ if (ret)
+ return ret;
+
+ pcs_dev = of_find_device_by_node(pcs_args.np);
+ of_node_put(pcs_args.np);
+ if (!pcs_dev)
+ return -ENODEV;
+
+ pcs = xpcs_port_get(pcs_dev, pcs_args.args[0]);
+ if (IS_ERR(pcs))
+ return PTR_ERR(pcs);
+
+ priv->pcs_dev = pcs_dev;
+ }
+
+ priv->phylink_config.dev = &priv_to_netdev(priv)->dev;
+ priv->phylink_config.type = PHYLINK_NETDEV;
+
+ __set_bit(phy_mode, priv->phylink_config.supported_interfaces);
+
+ priv->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000FD |
+ MAC_2500FD;
+
+
+ phylink = phylink_create(&priv->phylink_config, dev->fwnode, phy_mode,
+ &xgmac_phylink_mac_ops);
+ if (IS_ERR(phylink))
+ return PTR_ERR(phylink);
+
+ if (pcs)
+ priv->pcs = pcs;
+
+ priv->phylink = phylink;
+
+ return 0;
+}
+
+static int xgmac_probe(struct platform_device *pdev)
+{
+ static bool mac_disable_tx_set;
+ struct platform_device *dma_pdev;
+ struct device_node *dma_node;
+ struct net_device *ndev;
+ struct xgmac_priv *priv;
+ struct resource *r;
+ u32 ver;
+ int ret;
+
+ dma_node = of_parse_phandle(pdev->dev.of_node, "dmas", 0);
+ if (!dma_node)
+ return -ENODEV;
+
+ dma_pdev = of_find_device_by_node(dma_node);
+ of_node_put(dma_node);
+ if (!dma_pdev)
+ return -ENODEV;
+
+ ndev = devm_alloc_etherdev_mqs(&pdev->dev, sizeof(*priv), DMA_CH_MAX,
+ DMA_CH_MAX);
+ if (!ndev)
+ return -ENOMEM;
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ platform_set_drvdata(pdev, ndev);
+ priv = netdev_priv(ndev);
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->ioaddr = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->ioaddr))
+ return PTR_ERR(priv->ioaddr);
+
+ priv->csr_clk = devm_clk_get_enabled(&pdev->dev, "csr");
+ if (IS_ERR(priv->csr_clk))
+ return PTR_ERR(priv->csr_clk);
+
+ priv->id = offset_to_id(r->start);
+ priv->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "ethsys");
+ if (IS_ERR(priv->ethsys))
+ return PTR_ERR(priv->ethsys);
+
+ if (priv->id < 5) {
+ /* mac reset and release reset*/
+ ret = regmap_clear_bits(priv->ethsys, ETHSYS_RST, BIT(priv->id));
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(priv->ethsys, ETHSYS_RST, BIT(priv->id));
+ if (ret)
+ return ret;
+
+ } else {
+ ret = regmap_clear_bits(priv->ethsys, ETHSYS_RST, ETHSYS_RST_MAC5);
+ if (ret)
+ return ret;
+
+ /* set mac5 phy mode to rgmii, should set under mac reset */
+ ret = regmap_write(priv->ethsys, ETHSYS_MAC5_CTRL,
+ FIELD_PREP(MAC5_PHY_INTF_SEL, 1));
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(priv->ethsys, ETHSYS_RST, ETHSYS_RST_MAC5);
+ if (ret)
+ return ret;
+ }
+
+ if (!mac_disable_tx_set) {
+ /* Disable all MAC Tx, once and only once */
+ mac_disable_tx_set = true;
+ ret = regmap_write(priv->ethsys, ETHSYS_TX_DIS, 0xff);
+ if (ret)
+ return ret;
+ }
+
+ ver = reg_read(priv, XGMAC_VERSION);
+ if (FIELD_GET(XGMAC_VERSION_ID_MASK, ver) != XGMAC_VERSION_ID)
+ return -ENODEV;
+
+ spin_lock_init(&priv->stats_lock);
+ priv->dev = &pdev->dev;
+ priv->dma = platform_get_drvdata(dma_pdev);
+ if (!priv->dma)
+ return -EPROBE_DEFER;
+
+ ndev->base_addr = r->start;
+ ndev->netdev_ops = &xgmac_netdev_ops;
+ ndev->ethtool_ops = &xgmac_ethtool_ops;
+ ndev->features = NETIF_F_RXHASH | NETIF_F_RXCSUM | NETIF_F_GRO |
+ NETIF_F_SG | NETIF_F_LLTX | NETIF_F_HW_TC |
+ NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_TSO |
+ NETIF_F_TSO6;
+ ndev->hw_features = (ndev->features & ~NETIF_F_RXHASH) |
+ NETIF_F_LOOPBACK | NETIF_F_RXFCS | NETIF_F_RXALL |
+ NETIF_F_HW_L2FW_DOFFLOAD;
+ ndev->vlan_features = ndev->features;
+ ndev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
+ ndev->max_mtu = MAX_FRAME_SIZE - ETH_HLEN - ETH_FCS_LEN;
+
+ /* read-clear interrupt status before registering */
+ reg_read(priv, XGMAC_MDIO_INT_STATUS);
+ reg_read(priv, XGMAC_INT_STATUS);
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+
+ reg_clear(priv, XGMAC_INT_EN, XGMAC_LSI);
+ snprintf(priv->irq_name, sizeof(priv->irq_name), "xgmac%u_sbd",
+ priv->id);
+ ret = devm_request_irq(&pdev->dev, ret, xgmac_irq, 0, priv->irq_name,
+ priv);
+ if (ret)
+ return ret;
+
+ ret = xgmac_mdio_init(priv);
+ if (ret)
+ return ret;
+
+ ret = of_get_ethdev_address(pdev->dev.of_node, ndev);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ if (ret) {
+ eth_hw_addr_random(ndev);
+ dev_warn(&pdev->dev, "generated random MAC address %pM\n",
+ ndev->dev_addr);
+ }
+
+ ret = xgmac_phy_setup(priv);
+ if (ret)
+ return ret;
+
+ xgmac_write_mac_addr(priv, ndev->dev_addr, 0);
+ reg_set(priv, MMC_XGMAC_CONTROL,
+ MMC_XGMAC_CONTROL_RESET | MMC_XGMAC_CONTROL_RSTONRD);
+ reg_write(priv, XGMAC_TX_CONFIG, XGMAC_CORE_INIT_TX);
+ reg_write(priv, XGMAC_RX_CONFIG, XGMAC_CORE_INIT_RX);
+ reg_write(priv, XGMAC_WD_JB_TIMEOUT, XGMAC_PJE | XGMAC_PWE);
+ reg_write(priv, XGMAC_VLAN_TAG, XGMAC_VLAN_EDVLP);
+ reg_write(priv, XGMAC_LPI_TIMER_CTRL,
+ FIELD_PREP(XGMAC_LPI_LST, XGMAC_LPI_LST_DEFAULT) |
+ FIELD_PREP(XGMAC_LPI_TWT, XGMAC_LPI_TWT_DEFAULT));
+ reg_write(priv, XGMAC_LPI_AUTO_EN, XGMAC_LPI_AUTO_EN_DEFAULT);
+ xgmac_mib_irq_enable(priv);
+ reg_write(priv, XGMAC_LPI_1US,
+ clk_get_rate(priv->csr_clk) / 1000000 - 1);
+
+ ret = register_netdev(ndev);
+ if (ret)
+ goto phy_cleanup;
+
+ return 0;
+phy_cleanup:
+ phylink_destroy(priv->phylink);
+ if (priv->pcs_dev)
+ xpcs_port_put(priv->pcs_dev);
+ return ret;
+}
+
+static void xgmac_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct xgmac_priv *priv = netdev_priv(dev);
+
+ unregister_netdev(dev);
+ phylink_destroy(priv->phylink);
+ if (priv->pcs_dev)
+ xpcs_port_put(priv->pcs_dev);
+ regmap_clear_bits(priv->ethsys, ETHSYS_RST, BIT(priv->id));
+}
+
+static const struct of_device_id xgmac_match[] = {
+ { .compatible = "siflower,sf21-xgmac" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xgmac_match);
+
+static struct platform_driver xgmac_driver = {
+ .probe = xgmac_probe,
+ .remove_new = xgmac_remove,
+ .driver = {
+ .name = "sfxgmac",
+ .of_match_table = xgmac_match,
+ },
+};
+module_platform_driver(xgmac_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Qingfang Deng <qingfang.deng@siflower.com.cn>");
+MODULE_DESCRIPTION("Ethernet XGMAC driver for SF21A6826/SF21H8898 SoC");
\ No newline at end of file
--- /dev/null
+#define pr_fmt(fmt) "xpcs: " fmt
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phylink.h>
+#include <linux/of_platform.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/regmap.h>
+#include <asm-generic/bug.h>
+
+#include "sfxpcs.h"
+#include "eth.h"
+
+#define DEV_MASK GENMASK(20, 16)
+#define REG_MASK GENMASK(15, 0)
+
+#define DW_PCS_PORTS 4
+#define DW_QSGMII_MMD1 0x1a
+#define DW_QSGMII_MMD2 0x1b
+#define DW_QSGMII_MMD3 0x1c
+#define MDIO_CTRL1 MII_BMCR
+#define MDIO_CTRL1_RESET BMCR_RESET
+
+static inline void *PDE_DATA(const struct inode *inode) {BUG(); return NULL;};
+
+enum {
+ XPCS_CLK_REF,
+ XPCS_CLK_EEE,
+ XPCS_CLK_CSR,
+ XPCS_NUM_CLKS
+};
+
+struct xpcs_port {
+ struct phylink_pcs pcs;
+ unsigned int index;
+};
+
+struct xpcs_priv {
+ void __iomem *ioaddr;
+ struct regmap *ethsys;
+ struct clk_bulk_data clks[XPCS_NUM_CLKS];
+ u8 power_save_count;
+ u8 port_count;
+ u8 id;
+ struct xpcs_port ports[DW_PCS_PORTS];
+};
+
+static int xpcs_qsgmii_port_to_devad(unsigned int port)
+{
+ switch (port) {
+ case 0:
+ return MDIO_MMD_VEND2;
+ case 1:
+ return DW_QSGMII_MMD1;
+ case 2:
+ return DW_QSGMII_MMD2;
+ case 3:
+ return DW_QSGMII_MMD3;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+}
+
+static u16 xpcs_read(struct xpcs_priv *priv, int devad, int reg)
+{
+ ulong r;
+
+ r = FIELD_PREP(REG_MASK, reg) | FIELD_PREP(DEV_MASK, devad);
+ r <<= 2;
+
+ return readw_relaxed(priv->ioaddr + r);
+}
+
+static void xpcs_write(struct xpcs_priv *priv, int devad, int reg, u16 val)
+{
+ ulong r;
+
+ r = FIELD_PREP(REG_MASK, reg) | FIELD_PREP(DEV_MASK, devad);
+ r <<= 2;
+
+ writew_relaxed(val, priv->ioaddr + r);
+}
+
+static inline void xpcs_clear(struct xpcs_priv *priv, int devad, int reg, u16 clear)
+{
+ xpcs_write(priv, devad, reg, xpcs_read(priv, devad, reg) & ~clear);
+}
+
+static inline void xpcs_set(struct xpcs_priv *priv, int devad, int reg, u16 set)
+{
+ xpcs_write(priv, devad, reg, xpcs_read(priv, devad, reg) | set);
+}
+
+static int xpcs_poll_reset(struct xpcs_priv *priv, int devad)
+{
+ int timeout = 100000;
+
+ while (xpcs_read(priv, devad, MDIO_CTRL1) & MDIO_CTRL1_RESET) {
+ if (!--timeout) {
+ pr_err("Timed out waiting for reset\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int xpcs_poll_pg(struct xpcs_priv *priv, int devad, u16 val)
+{
+ u32 timeout = 0;
+
+ while (FIELD_GET(PSEQ_STATE,
+ xpcs_read(priv, devad, DW_VR_MII_DIG_STS)) != val) {
+ if (timeout >= 100) {
+ pr_err("Timed out waiting for power state\n");
+ return -ETIMEDOUT;
+ }
+ timeout++;
+ udelay(100);
+ }
+ return 0;
+}
+
+static int xpcs_serdes_power_down(struct xpcs_priv *priv)
+{
+ xpcs_write(priv, MDIO_MMD_VEND2, MII_BMCR, BMCR_PDOWN);
+ return xpcs_poll_pg(priv, MDIO_MMD_VEND2, PSEQ_STATE_DOWN);
+}
+
+static int xpcs_serdes_power_up(struct xpcs_priv *priv)
+{
+ /* When powered down, this register cannot be read.
+ * speed/duplex/AN will be configured in pcs_config/pcs_link_up.
+ */
+ xpcs_write(priv, MDIO_MMD_VEND2, MII_BMCR, 0);
+ return xpcs_poll_pg(priv, MDIO_MMD_VEND2, PSEQ_STATE_GOOD);
+}
+
+/* Read AN result for 1000Base-X/2500Base-X */
+static void xpcs_8023z_resolve_link(struct xpcs_priv *priv,
+ struct phylink_link_state *state,
+ int fd_bit)
+{
+ bool tx_pause, rx_pause;
+ u16 adv, lpa;
+
+ adv = xpcs_read(priv, MDIO_MMD_VEND2, MII_ADVERTISE);
+ lpa = xpcs_read(priv, MDIO_MMD_VEND2, MII_LPA);
+
+ mii_lpa_mod_linkmode_x(state->lp_advertising, lpa, fd_bit);
+
+ if (linkmode_test_bit(fd_bit, state->advertising) &&
+ linkmode_test_bit(fd_bit, state->lp_advertising)) {
+ state->duplex = DUPLEX_FULL;
+ } else {
+ /* negotiation failure */
+ state->link = false;
+ }
+
+ linkmode_resolve_pause(state->advertising, state->lp_advertising,
+ &tx_pause, &rx_pause);
+
+ if (tx_pause)
+ state->pause |= MLO_PAUSE_TX;
+ if (rx_pause)
+ state->pause |= MLO_PAUSE_RX;
+}
+
+static void xpcs_get_state(struct phylink_pcs *pcs,
+ struct phylink_link_state *state)
+{
+ struct xpcs_port *port;
+ struct xpcs_priv *priv;
+ u16 intrsts, bmsr;
+ int mmd;
+
+ port = container_of(pcs, struct xpcs_port, pcs);
+ priv = container_of(port, struct xpcs_priv, ports[port->index]);
+ bmsr = xpcs_read(priv, MDIO_MMD_VEND2, MII_BMSR);
+
+ state->link = !!(bmsr & BMSR_LSTATUS);
+ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
+ if (!state->link)
+ return;
+
+ switch (state->interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ mmd = xpcs_qsgmii_port_to_devad(port->index);
+ /* For SGMII/QSGMII, link speed and duplex can be read from
+ * DW_VR_MII_AN_INTR_STS */
+ intrsts = xpcs_read(priv, mmd, DW_VR_MII_AN_INTR_STS);
+
+ state->link = !!(intrsts & DW_VR_MII_C37_ANSGM_SP_LNKSTS);
+ if (!state->link)
+ break;
+
+ switch (FIELD_GET(DW_VR_MII_AN_STS_C37_ANSGM_SP, intrsts)) {
+ case DW_VR_MII_C37_ANSGM_SP_10:
+ state->speed = SPEED_10;
+ break;
+ case DW_VR_MII_C37_ANSGM_SP_100:
+ state->speed = SPEED_100;
+ break;
+ case DW_VR_MII_C37_ANSGM_SP_1000:
+ state->speed = SPEED_1000;
+ break;
+ }
+
+ state->duplex = (intrsts & DW_VR_MII_AN_STS_C37_ANSGM_FD) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ state->speed = SPEED_1000;
+ xpcs_8023z_resolve_link(priv, state,
+ ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ state->speed = SPEED_2500;
+ xpcs_8023z_resolve_link(priv, state,
+ ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
+ break;
+ default:
+ break;
+ }
+}
+
+static void xpcs_qsgmii_init(struct xpcs_priv *priv)
+{
+ u16 reg;
+
+ reg = xpcs_read(priv, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL);
+ /* Already configured for QSGMII? skip. */
+ if (FIELD_GET(DW_VR_MII_PCS_MODE_MASK, reg) == DW_VR_MII_PCS_MODE_C37_QSGMII)
+ return;
+
+ reg = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK, DW_VR_MII_PCS_MODE_C37_QSGMII);
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, reg);
+
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MPLL_CTRL1, 0x28);
+
+ reg = xpcs_read(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MPLL_CTRL0);
+ reg &= ~LANE_10BIT_SEL;
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MPLL_CTRL0, reg);
+
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MISC_CTRL1, 0x0);
+
+ reg = xpcs_read(priv, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1);
+ reg &= ~DW_VR_MII_DIG_CTRL1_2G5_EN;
+ reg &= ~DW_VR_MII_DIG_CTRL1_CL37_TMR_OVR_RIDE;
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, reg);
+
+ xpcs_serdes_power_down(priv);
+ xpcs_serdes_power_up(priv);
+}
+
+static void xpcs_1000basex_sgmii_common_init(struct xpcs_priv *priv)
+{
+ u16 reg;
+
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MPLL_CTRL1, 0x28);
+
+ reg = xpcs_read(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MPLL_CTRL0);
+ reg |= LANE_10BIT_SEL;
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MPLL_CTRL0, reg);
+
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MISC_CTRL1, 0xa);
+
+ reg = xpcs_read(priv, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1);
+ reg &= ~DW_VR_MII_DIG_CTRL1_2G5_EN;
+ reg &= ~DW_VR_MII_DIG_CTRL1_CL37_TMR_OVR_RIDE;
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, reg);
+
+ xpcs_serdes_power_down(priv);
+ xpcs_serdes_power_up(priv);
+}
+
+static void xpcs_1000basex_init(struct xpcs_priv *priv)
+{
+ u16 reg;
+
+ reg = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK, DW_VR_MII_PCS_MODE_C37_1000BASEX);
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, reg);
+
+ xpcs_1000basex_sgmii_common_init(priv);
+}
+
+static void xpcs_sgmii_init(struct xpcs_priv *priv)
+{
+ u16 reg;
+
+ reg = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK, DW_VR_MII_PCS_MODE_C37_SGMII);
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, reg);
+
+ xpcs_1000basex_sgmii_common_init(priv);
+}
+
+static void xpcs_2500basex_init(struct xpcs_priv *priv)
+{
+ u16 reg;
+
+ reg = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK, DW_VR_MII_PCS_MODE_C37_1000BASEX);
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, reg);
+
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MPLL_CTRL1, 0x32);
+
+ reg = xpcs_read(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MPLL_CTRL0);
+ reg |= LANE_10BIT_SEL;
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MPLL_CTRL0, reg);
+
+ reg = xpcs_read(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_RXGENCTRL0);
+ reg |= RX_ALIGN_EN_0;
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_RXGENCTRL0, reg);
+
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_MP_6G_MISC_CTRL1, 0x5);
+
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_LINK_TIMER_CTRL,
+ DW_VR_MII_LINK_TIMER_2500BASEX);
+
+ reg = xpcs_read(priv, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1);
+ reg |= DW_VR_MII_DIG_CTRL1_2G5_EN;
+ reg |= DW_VR_MII_DIG_CTRL1_CL37_TMR_OVR_RIDE;
+ xpcs_write(priv, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, reg);
+
+ xpcs_serdes_power_down(priv);
+ xpcs_serdes_power_up(priv);
+}
+
+static int xpcs_config(struct phylink_pcs *pcs, unsigned int mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ struct xpcs_port *port;
+ struct xpcs_priv *priv;
+ u16 val;
+ int mmd;
+
+ port = container_of(pcs, struct xpcs_port, pcs);
+ priv = container_of(port, struct xpcs_priv, ports[port->index]);
+
+ /* Port 1,2,3 only exist in QSGMII mode */
+ if (port->index && interface != PHY_INTERFACE_MODE_QSGMII)
+ return -EINVAL;
+
+ /* Disable AN */
+ mmd = xpcs_qsgmii_port_to_devad(port->index);
+ xpcs_clear(priv, mmd, MII_BMCR, BMCR_ANENABLE);
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_QSGMII:
+ xpcs_qsgmii_init(priv);
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+ xpcs_sgmii_init(priv);
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ xpcs_2500basex_init(priv);
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ xpcs_1000basex_init(priv);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Enable interrupt for in-band status */
+ val = xpcs_read(priv, mmd, DW_VR_MII_AN_CTRL);
+ if (phylink_autoneg_inband(mode))
+ val |= DW_VR_MII_AN_INTR_EN;
+ else
+ val &= ~DW_VR_MII_AN_INTR_EN;
+ xpcs_write(priv, mmd, DW_VR_MII_AN_CTRL, val);
+
+ if (interface != PHY_INTERFACE_MODE_2500BASEX) {
+ val = xpcs_read(priv, mmd, DW_VR_MII_DIG_CTRL1);
+ /* Enable speed auto switch for SGMII/QSGMII */
+ val |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
+ xpcs_write(priv, mmd, DW_VR_MII_DIG_CTRL1, val);
+ }
+
+ /* Configure AN ADV for 802.3z modes */
+ if (phy_interface_mode_is_8023z(interface)) {
+ int fd_bit;
+ u16 adv;
+
+ fd_bit = interface == PHY_INTERFACE_MODE_1000BASEX ?
+ ETHTOOL_LINK_MODE_1000baseX_Full_BIT :
+ ETHTOOL_LINK_MODE_2500baseX_Full_BIT;
+ adv = linkmode_adv_to_mii_adv_x(advertising, fd_bit);
+
+ xpcs_write(priv, MDIO_MMD_VEND2, MII_ADVERTISE, adv);
+ }
+
+ /* Enable AN */
+ if (interface != PHY_INTERFACE_MODE_2500BASEX)
+ xpcs_write(priv, mmd, MII_BMCR, BMCR_ANENABLE);
+
+ return 0;
+}
+
+static void xpcs_an_restart(struct phylink_pcs *pcs)
+{
+ struct xpcs_port *port;
+ struct xpcs_priv *priv;
+ int mmd;
+
+ port = container_of(pcs, struct xpcs_port, pcs);
+ priv = container_of(port, struct xpcs_priv, ports[port->index]);
+
+ mmd = xpcs_qsgmii_port_to_devad(port->index);
+ xpcs_set(priv, mmd, MII_BMCR, BMCR_ANRESTART);
+}
+
+static void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+ phy_interface_t interface, int speed, int duplex)
+{
+ struct xpcs_port *port;
+ struct xpcs_priv *priv;
+ u16 bmcr;
+ int mmd;
+
+ /* Skip speed and duplex configuration for SGMII/QSGMII in-band */
+ if (phylink_autoneg_inband(mode) &&
+ !phy_interface_mode_is_8023z(interface))
+ return;
+
+ /* 1000/2500 BaseX should only use the max speed */
+ if (phy_interface_mode_is_8023z(interface))
+ speed = SPEED_1000;
+
+ port = container_of(pcs, struct xpcs_port, pcs);
+ priv = container_of(port, struct xpcs_priv, ports[port->index]);
+
+ mmd = xpcs_qsgmii_port_to_devad(port->index);
+ bmcr = xpcs_read(priv, mmd, MII_BMCR);
+ bmcr &= ~(BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_SPEED10);
+
+ switch (speed) {
+ case SPEED_2500:
+ case SPEED_1000:
+ bmcr |= BMCR_SPEED1000;
+ break;
+ case SPEED_100:
+ bmcr |= BMCR_SPEED100;
+ break;
+ case SPEED_10:
+ bmcr |= BMCR_SPEED10;
+ break;
+ }
+ if (duplex == DUPLEX_FULL)
+ bmcr |= BMCR_FULLDPLX;
+ else
+ bmcr &= ~BMCR_FULLDPLX;
+
+ xpcs_write(priv, mmd, MII_BMCR, bmcr);
+}
+
+static const struct phylink_pcs_ops xpcs_phylink_ops = {
+ .pcs_get_state = xpcs_get_state,
+ .pcs_config = xpcs_config,
+ .pcs_an_restart = xpcs_an_restart,
+ .pcs_link_up = xpcs_link_up,
+};
+
+struct phylink_pcs *xpcs_port_get(struct platform_device *pdev,
+ unsigned int port)
+{
+ struct xpcs_priv *priv = platform_get_drvdata(pdev);
+
+ if (port >= DW_PCS_PORTS)
+ return ERR_PTR(-EINVAL);
+
+ priv->port_count++;
+ priv->power_save_count++;
+ return &priv->ports[port].pcs;
+}
+EXPORT_SYMBOL(xpcs_port_get);
+
+
+
+void xpcs_port_put(struct platform_device *pdev)
+{
+ struct xpcs_priv *priv = platform_get_drvdata(pdev);
+
+ priv->port_count--;
+ priv->power_save_count--;
+}
+EXPORT_SYMBOL(xpcs_port_put);
+
+static irqreturn_t xpcs_irq(int irq, void *dev_id)
+{
+ struct xpcs_priv *priv = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ int i;
+
+ for (i = 0; i < DW_PCS_PORTS; i++) {
+ int mmd = xpcs_qsgmii_port_to_devad(i);
+ u16 intrsts = xpcs_read(priv, mmd, DW_VR_MII_AN_INTR_STS);
+ bool up;
+
+ if (!(intrsts & DW_VR_MII_C37_ANCMPLT_INTR))
+ continue;
+
+ xpcs_write(priv, mmd, DW_VR_MII_AN_INTR_STS, 0);
+ up = xpcs_read(priv, MDIO_MMD_VEND2, MII_BMSR) & BMSR_LSTATUS;
+ up |= intrsts & DW_VR_MII_C37_ANSGM_SP_LNKSTS;
+ phylink_pcs_change(&priv->ports[i].pcs, up);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int xpcs_probe(struct platform_device *pdev)
+{
+ struct xpcs_priv *priv;
+ struct resource *r;
+ int ret, i;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+ for (i = 0; i < DW_PCS_PORTS; i++) {
+ priv->ports[i].index = i;
+ priv->ports[i].pcs.ops = &xpcs_phylink_ops;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->ioaddr = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(priv->ioaddr))
+ return PTR_ERR(priv->ioaddr);
+
+ priv->id = !!(r->start & BIT(24));
+ priv->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "ethsys");
+ if (IS_ERR(priv->ethsys))
+ return PTR_ERR(priv->ethsys);
+
+ priv->clks[XPCS_CLK_REF].id = "ref";
+ priv->clks[XPCS_CLK_EEE].id = "eee";
+ priv->clks[XPCS_CLK_CSR].id = "csr";
+ ret = devm_clk_bulk_get(&pdev->dev, XPCS_NUM_CLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(XPCS_NUM_CLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(priv->ethsys, ETHSYS_RST, BIT(5 + priv->id));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->ethsys,
+ ETHSYS_QSG_CTRL + priv->id * sizeof(u32), 0x601);
+ if (ret)
+ return ret;
+
+ /* set ethtsuclk to 100MHz */
+ ret = clk_set_rate(priv->clks[XPCS_CLK_EEE].clk, 100000000);
+ if (ret)
+ return ret;
+
+ /* Soft reset the PCS */
+ xpcs_write(priv, MDIO_MMD_VEND2, MII_BMCR, BMCR_RESET);
+ ret = xpcs_poll_reset(priv, MDIO_MMD_VEND2);
+ if (ret)
+ return ret;
+
+ /* Enable EEE */
+ xpcs_set(priv, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0,
+ DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN);
+
+ /* Start from the power up state */
+ ret = xpcs_serdes_power_up(priv);
+ if (ret)
+ return ret;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, ret, xpcs_irq, 0, KBUILD_MODNAME, priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int xpcs_remove(struct platform_device *pdev)
+{
+ struct xpcs_priv *priv = platform_get_drvdata(pdev);
+
+ clk_bulk_disable_unprepare(XPCS_NUM_CLKS, priv->clks);
+ return regmap_clear_bits(priv->ethsys, ETHSYS_RST, BIT(5 + priv->id));
+}
+
+static const struct of_device_id xpcs_match[] = {
+ { .compatible = "siflower,sf21-xpcs" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xpcs_match);
+
+static struct platform_driver xpcs_driver = {
+ .probe = xpcs_probe,
+ .remove = xpcs_remove,
+ .driver = {
+ .name = "sfxpcs",
+ .of_match_table = xpcs_match,
+ },
+};
+module_platform_driver(xpcs_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Qingfang Deng <qingfang.deng@siflower.com.cn>");
+MODULE_DESCRIPTION("XPCS driver for SF21A6826/SF21H8898 SoC");
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+
+#define SYNOPSYS_XPCS_ID 0x7996ced0
+#define SYNOPSYS_XPCS_MASK 0xffffffff
+
+/* Vendor regs access */
+#define DW_VENDOR BIT(15)
+
+/* VR_XS_PCS */
+#define DW_USXGMII_RST BIT(10)
+#define DW_USXGMII_EN BIT(9)
+#define DW_VR_XS_PCS_DIG_STS 0x0010
+#define DW_RXFIFO_ERR GENMASK(6, 5)
+
+/* SR_MII */
+#define DW_USXGMII_FULL BIT(8)
+#define DW_USXGMII_SS_MASK (BIT(13) | BIT(6) | BIT(5))
+#define DW_USXGMII_10000 (BIT(13) | BIT(6))
+#define DW_USXGMII_5000 (BIT(13) | BIT(5))
+#define DW_USXGMII_2500 (BIT(5))
+#define DW_USXGMII_1000 (BIT(6))
+#define DW_USXGMII_100 (BIT(13))
+#define DW_USXGMII_10 (0)
+
+/* SR_AN */
+#define DW_SR_AN_ADV1 0x10
+#define DW_SR_AN_ADV2 0x11
+#define DW_SR_AN_ADV3 0x12
+#define DW_SR_AN_LP_ABL1 0x13
+#define DW_SR_AN_LP_ABL2 0x14
+#define DW_SR_AN_LP_ABL3 0x15
+
+/* Clause 73 Defines */
+/* AN_LP_ABL1 */
+#define DW_C73_PAUSE BIT(10)
+#define DW_C73_ASYM_PAUSE BIT(11)
+#define DW_C73_AN_ADV_SF 0x1
+/* AN_LP_ABL2 */
+#define DW_C73_1000KX BIT(5)
+#define DW_C73_10000KX4 BIT(6)
+#define DW_C73_10000KR BIT(7)
+/* AN_LP_ABL3 */
+#define DW_C73_2500KX BIT(0)
+#define DW_C73_5000KR BIT(1)
+
+/* Clause 37 Defines */
+/* VR MII MMD registers offsets */
+#define DW_VR_MII_MMD_CTRL 0x0000
+#define DW_VR_MII_DIG_CTRL1 0x8000
+#define DW_VR_MII_AN_CTRL 0x8001
+#define DW_VR_MII_AN_INTR_STS 0x8002
+#define DW_VR_MII_DBG_CTRL 0x8005
+#define DW_VR_MII_LINK_TIMER_CTRL 0x800a
+#define DW_VR_MII_DIG_STS 0x8010
+#define DW_VR_MII_MP_6G_RXGENCTRL0 0x8058
+#define DW_VR_MII_MP_6G_MPLL_CTRL0 0x8078
+#define DW_VR_MII_MP_6G_MPLL_CTRL1 0x8079
+#define DW_VR_MII_MP_6G_MISC_CTRL1 0x809a
+/* Enable 2.5G Mode */
+#define DW_VR_MII_DIG_CTRL1_2G5_EN BIT(2)
+/* EEE Mode Control Register */
+#define DW_VR_MII_EEE_MCTRL0 0x8006
+#define DW_VR_MII_EEE_MCTRL1 0x800b
+#define DW_VR_MII_DIG_CTRL2 0x80e1
+
+/* VR_MII_DIG_CTRL1 */
+#define DW_VR_MII_DIG_CTRL1_EN_25G_MODE BIT(2)
+#define DW_VR_MII_DIG_CTRL1_CL37_TMR_OVR_RIDE BIT(3)
+#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9)
+
+/* VR_MII_DIG_CTRL2 */
+#define DW_VR_MII_DIG_CTRL2_TX_POL_INV BIT(4)
+#define DW_VR_MII_DIG_CTRL2_RX_POL_INV BIT(0)
+
+/* VR_MII_AN_CTRL */
+#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3
+#define DW_VR_MII_TX_CONFIG_MASK BIT(3)
+#define DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII 0x1
+#define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII 0x0
+#define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT 1
+#define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1)
+#define DW_VR_MII_PCS_MODE_C37_1000BASEX 0x0
+#define DW_VR_MII_PCS_MODE_C37_SGMII 0x2
+#define DW_VR_MII_PCS_MODE_C37_QSGMII 0x3
+#define DW_VR_MII_AN_INTR_EN BIT(0)
+
+/* VR_MII_AN_INTR_STS */
+#define DW_VR_MII_AN_STS_C37_ANSGM_FD BIT(1)
+#define DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT 2
+#define DW_VR_MII_AN_STS_C37_ANSGM_SP GENMASK(3, 2)
+#define DW_VR_MII_C37_ANSGM_SP_10 0x0
+#define DW_VR_MII_C37_ANSGM_SP_100 0x1
+#define DW_VR_MII_C37_ANSGM_SP_1000 0x2
+#define DW_VR_MII_C37_ANSGM_SP_LNKSTS BIT(4)
+#define DW_VR_MII_C37_ANCMPLT_INTR BIT(0)
+
+/* VR_MII_LINK_TIMER_CTRL */
+#define DW_VR_MII_LINK_TIMER_2500BASEX 0x2faf
+
+/* VR_MII_DIG_STS */
+#define PSEQ_STATE GENMASK(4, 2)
+#define PSEQ_STATE_GOOD 4
+#define PSEQ_STATE_DOWN 6
+
+/* VR_MII_MP_6G_MPLL_CTRL0 */
+#define LANE_10BIT_SEL BIT(1)
+
+/* VR_MII_MP_6G_RXGENCTRL0 */
+#define RX_ALIGN_EN_0 BIT(4)
+
+/* SR MII MMD Control defines */
+#define AN_CL37_EN BIT(12) /* Enable Clause 37 auto-nego */
+#define SGMII_SPEED_SS13 BIT(13) /* SGMII speed along with SS6 */
+#define SGMII_SPEED_SS6 BIT(6) /* SGMII speed along with SS13 */
+
+/* VR MII EEE Control 0 defines */
+#define DW_VR_MII_EEE_LTX_EN BIT(0) /* LPI Tx Enable */
+#define DW_VR_MII_EEE_LRX_EN BIT(1) /* LPI Rx Enable */
+#define DW_VR_MII_EEE_TX_QUIET_EN BIT(2) /* Tx Quiet Enable */
+#define DW_VR_MII_EEE_RX_QUIET_EN BIT(3) /* Rx Quiet Enable */
+#define DW_VR_MII_EEE_TX_EN_CTRL BIT(4) /* Tx Control Enable */
+#define DW_VR_MII_EEE_RX_EN_CTRL BIT(7) /* Rx Control Enable */
+
+#define DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT 8
+#define DW_VR_MII_EEE_MULT_FACT_100NS GENMASK(11, 8)
+
+/* VR MII EEE Control 1 defines */
+#define DW_VR_MII_EEE_TRN_LPI BIT(0) /* Transparent Mode Enable */
+
+/* Additional MMDs for QSGMII */
+#define DW_QSGMII_MMD1 0x1a
+#define DW_QSGMII_MMD2 0x1b
+#define DW_QSGMII_MMD3 0x1c
+
+/* PMA MMD registers */
+#define XS_PMA_MMD_BaseAddress 0x8020
+#define VR_XS_PMA_RX_LSTS (XS_PMA_MMD_BaseAddress + 0x0)
+#define VR_XS_PMA_RX_LSTS_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_TX_GENCTRL0 (XS_PMA_MMD_BaseAddress + 0x10)
+#define VR_XS_PMA_MP_12G_16G_25G_TX_GENCTRL0_RegisterResetValue 0x1000
+#define VR_XS_PMA_MP_12G_16G_25G_TX_GENCTRL1 (XS_PMA_MMD_BaseAddress + 0x11)
+#define VR_XS_PMA_MP_12G_16G_25G_TX_GENCTRL1_RegisterResetValue 0x1510
+#define VR_XS_PMA_MP_12G_16G_TX_GENCTRL2 (XS_PMA_MMD_BaseAddress + 0x12)
+#define VR_XS_PMA_MP_12G_16G_TX_GENCTRL2_RegisterResetValue 0x300
+#define VR_XS_PMA_MP_12G_16G_25G_TX_BOOST_CTRL (XS_PMA_MMD_BaseAddress + 0x13)
+#define VR_XS_PMA_MP_12G_16G_25G_TX_BOOST_CTRL_RegisterResetValue 0xf
+#define VR_XS_PMA_MP_12G_16G_25G_TX_RATE_CTRL (XS_PMA_MMD_BaseAddress + 0x14)
+#define VR_XS_PMA_MP_12G_16G_25G_TX_RATE_CTRL_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL (XS_PMA_MMD_BaseAddress + 0x15)
+#define VR_XS_PMA_MP_12G_16G_25G_TX_POWER_STATE_CTRL_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL0 (XS_PMA_MMD_BaseAddress + 0x16)
+#define VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL0_RegisterResetValue 0x2800
+#define VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL1 (XS_PMA_MMD_BaseAddress + 0x17)
+#define VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL1_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_25G_TX_GENCTRL3 (XS_PMA_MMD_BaseAddress + 0x1c)
+#define VR_XS_PMA_MP_16G_25G_TX_GENCTRL3_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_25G_TX_GENCTRL4 (XS_PMA_MMD_BaseAddress + 0x1d)
+#define VR_XS_PMA_MP_16G_25G_TX_GENCTRL4_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_25G_TX_MISC_CTRL0 (XS_PMA_MMD_BaseAddress + 0x1e)
+#define VR_XS_PMA_MP_16G_25G_TX_MISC_CTRL0_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_TX_STS (XS_PMA_MMD_BaseAddress + 0x20)
+#define VR_XS_PMA_MP_12G_16G_25G_TX_STS_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_RX_GENCTRL0 (XS_PMA_MMD_BaseAddress + 0x30)
+#define VR_XS_PMA_MP_12G_16G_25G_RX_GENCTRL0_RegisterResetValue 0x101
+#define VR_XS_PMA_MP_12G_16G_25G_RX_GENCTRL1 (XS_PMA_MMD_BaseAddress + 0x31)
+#define VR_XS_PMA_MP_12G_16G_25G_RX_GENCTRL1_RegisterResetValue 0x1100
+#define VR_XS_PMA_MP_12G_16G_RX_GENCTRL2 (XS_PMA_MMD_BaseAddress + 0x32)
+#define VR_XS_PMA_MP_12G_16G_RX_GENCTRL2_RegisterResetValue 0x300
+#define VR_XS_PMA_MP_12G_16G_RX_GENCTRL3 (XS_PMA_MMD_BaseAddress + 0x33)
+#define VR_XS_PMA_MP_12G_16G_RX_GENCTRL3_RegisterResetValue 0x1
+#define VR_XS_PMA_MP_12G_16G_25G_RX_RATE_CTRL (XS_PMA_MMD_BaseAddress + 0x34)
+#define VR_XS_PMA_MP_12G_16G_25G_RX_RATE_CTRL_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL (XS_PMA_MMD_BaseAddress + 0x35)
+#define VR_XS_PMA_MP_12G_16G_25G_RX_POWER_STATE_CTRL_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_RX_CDR_CTRL (XS_PMA_MMD_BaseAddress + 0x36)
+#define VR_XS_PMA_MP_12G_16G_25G_RX_CDR_CTRL_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_RX_ATTN_CTRL (XS_PMA_MMD_BaseAddress + 0x37)
+#define VR_XS_PMA_MP_12G_16G_25G_RX_ATTN_CTRL_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_25G_RX_EQ_CTRL0 (XS_PMA_MMD_BaseAddress + 0x38)
+#define VR_XS_PMA_MP_16G_25G_RX_EQ_CTRL0_RegisterResetValue 0x5550
+#define VR_XS_PMA_MP_12G_16G_25G_RX_EQ_CTRL4 (XS_PMA_MMD_BaseAddress + 0x3c)
+#define VR_XS_PMA_MP_12G_16G_25G_RX_EQ_CTRL4_RegisterResetValue 0x11
+#define VR_XS_PMA_MP_16G_25G_RX_EQ_CTRL5 (XS_PMA_MMD_BaseAddress + 0x3d)
+#define VR_XS_PMA_MP_16G_25G_RX_EQ_CTRL5_RegisterResetValue 0x30
+#define VR_XS_PMA_MP_12G_16G_25G_DFE_TAP_CTRL0 (XS_PMA_MMD_BaseAddress + 0x3e)
+#define VR_XS_PMA_MP_12G_16G_25G_DFE_TAP_CTRL0_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_RX_STS (XS_PMA_MMD_BaseAddress + 0x40)
+#define VR_XS_PMA_MP_12G_16G_25G_RX_STS_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_25G_RX_PPM_STS0 (XS_PMA_MMD_BaseAddress + 0x41)
+#define VR_XS_PMA_MP_16G_25G_RX_PPM_STS0_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_RX_CDR_CTRL1 (XS_PMA_MMD_BaseAddress + 0x44)
+#define VR_XS_PMA_MP_16G_RX_CDR_CTRL1_RegisterResetValue 0x111
+#define VR_XS_PMA_MP_16G_25G_RX_PPM_CTRL0 (XS_PMA_MMD_BaseAddress + 0x45)
+#define VR_XS_PMA_MP_16G_25G_RX_PPM_CTRL0_RegisterResetValue 0x12
+#define VR_XS_PMA_MP_16G_25G_RX_GENCTRL4 (XS_PMA_MMD_BaseAddress + 0x48)
+#define VR_XS_PMA_MP_16G_25G_RX_GENCTRL4_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_25G_RX_MISC_CTRL0 (XS_PMA_MMD_BaseAddress + 0x49)
+#define VR_XS_PMA_MP_16G_25G_RX_MISC_CTRL0_RegisterResetValue 0x12
+#define VR_XS_PMA_MP_16G_25G_RX_IQ_CTRL0 (XS_PMA_MMD_BaseAddress + 0x4b)
+#define VR_XS_PMA_MP_16G_25G_RX_IQ_CTRL0_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_MPLL_CMN_CTRL (XS_PMA_MMD_BaseAddress + 0x50)
+#define VR_XS_PMA_MP_12G_16G_25G_MPLL_CMN_CTRL_RegisterResetValue 0x1
+#define VR_XS_PMA_MP_12G_16G_MPLLA_CTRL0 (XS_PMA_MMD_BaseAddress + 0x51)
+#define VR_XS_PMA_MP_12G_16G_MPLLA_CTRL0_RegisterResetValue 0x21
+#define VR_XS_PMA_MP_16G_MPLLA_CTRL1 (XS_PMA_MMD_BaseAddress + 0x52)
+#define VR_XS_PMA_MP_16G_MPLLA_CTRL1_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_MPLLA_CTRL2 (XS_PMA_MMD_BaseAddress + 0x53)
+#define VR_XS_PMA_MP_12G_16G_MPLLA_CTRL2_RegisterResetValue 0x600
+#define VR_XS_PMA_MP_12G_16G_MPLLB_CTRL0 (XS_PMA_MMD_BaseAddress + 0x54)
+#define VR_XS_PMA_MP_12G_16G_MPLLB_CTRL0_RegisterResetValue 0x8000
+#define VR_XS_PMA_MP_16G_MPLLB_CTRL1 (XS_PMA_MMD_BaseAddress + 0x55)
+#define VR_XS_PMA_MP_16G_MPLLB_CTRL1_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_MPLLB_CTRL2 (XS_PMA_MMD_BaseAddress + 0x56)
+#define VR_XS_PMA_MP_12G_16G_MPLLB_CTRL2_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_MPLLA_CTRL3 (XS_PMA_MMD_BaseAddress + 0x57)
+#define VR_XS_PMA_MP_16G_MPLLA_CTRL3_RegisterResetValue 0xa016
+#define VR_XS_PMA_MP_16G_MPLLB_CTRL3 (XS_PMA_MMD_BaseAddress + 0x58)
+#define VR_XS_PMA_MP_16G_MPLLB_CTRL3_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_MPLLA_CTRL4 (XS_PMA_MMD_BaseAddress + 0x59)
+#define VR_XS_PMA_MP_16G_MPLLA_CTRL4_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_MPLLA_CTRL5 (XS_PMA_MMD_BaseAddress + 0x5a)
+#define VR_XS_PMA_MP_16G_MPLLA_CTRL5_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_MPLLB_CTRL4 (XS_PMA_MMD_BaseAddress + 0x5b)
+#define VR_XS_PMA_MP_16G_MPLLB_CTRL4_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_16G_MPLLB_CTRL5 (XS_PMA_MMD_BaseAddress + 0x5c)
+#define VR_XS_PMA_MP_16G_MPLLB_CTRL5_RegisterResetValue 0x0
+#define VR_XS_PMA_MP_12G_16G_25G_MISC_CTRL0 (XS_PMA_MMD_BaseAddress + 0x70)
+#define VR_XS_PMA_MP_12G_16G_25G_MISC_CTRL0_RegisterResetValue 0x5100
+#define VR_XS_PMA_MP_12G_16G_25G_REF_CLK_CTRL (XS_PMA_MMD_BaseAddress + 0x71)
+#define VR_XS_PMA_MP_12G_16G_25G_REF_CLK_CTRL_RegisterResetValue 0x71
+#define VR_XS_PMA_MP_12G_16G_25G_REF_CLK_CTRL_REF_RPT_CLK_EN BIT(8)
+#define VR_XS_PMA_MP_12G_16G_25G_VCO_CAL_LD0 (XS_PMA_MMD_BaseAddress + 0x72)
+#define VR_XS_PMA_MP_12G_16G_25G_VCO_CAL_LD0_RegisterResetValue 0x549
+#define VR_XS_PMA_MP_16G_25G_VCO_CAL_REF0 (XS_PMA_MMD_BaseAddress + 0x76)
+#define VR_XS_PMA_MP_16G_25G_VCO_CAL_REF0_RegisterResetValue 0x29
+#define VR_XS_PMA_MP_12G_16G_25G_MISC_STS (XS_PMA_MMD_BaseAddress + 0x78)
+#define VR_XS_PMA_MP_12G_16G_25G_MISC_STS_RegisterResetValue 0x200
+#define VR_XS_PMA_MP_12G_16G_25G_MISC_CTRL1 (XS_PMA_MMD_BaseAddress + 0x79)
+#define VR_XS_PMA_MP_12G_16G_25G_MISC_CTRL1_RegisterResetValue 0xffff
+#define VR_XS_PMA_MP_12G_16G_25G_EEE_CTRL (XS_PMA_MMD_BaseAddress + 0x7a)
+#define VR_XS_PMA_MP_12G_16G_25G_EEE_CTRL_RegisterResetValue 0x4f
+#define VR_XS_PMA_MP_12G_16G_25G_SRAM (XS_PMA_MMD_BaseAddress + 0x7b)
+#define VR_XS_PMA_MP_12G_16G_25G_SRAM_INIT_DN BIT(0)
+#define VR_XS_PMA_MP_12G_16G_25G_SRAM_EXT_LD_DN BIT(1)
+#define VR_XS_PMA_MP_16G_25G_MISC_CTRL2 (XS_PMA_MMD_BaseAddress + 0x7c)
+#define VR_XS_PMA_MP_16G_25G_MISC_CTRL2_RegisterResetValue 0x1
+#define VR_XS_PMA_SNPS_CR_CTRL (XS_PMA_MMD_BaseAddress + 0x80)
+#define VR_XS_PMA_SNPS_CR_CTRL_START_BUSY BIT(0)
+#define VR_XS_PMA_SNPS_CR_CTRL_WR_RDN BIT(1)
+#define VR_XS_PMA_SNPS_CR_ADDR (XS_PMA_MMD_BaseAddress + 0x81)
+#define VR_XS_PMA_SNPS_CR_DATA (XS_PMA_MMD_BaseAddress + 0x82)
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * drivers/net/phy/siflower.c
+ *
+ * Driver for Siflower PHYs
+ *
+ * Copyright (c) 2023 Siflower, Inc.
+ *
+ * Support : Siflower Phys:
+ * Giga phys: p1211f, p1240
+ */
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/phy.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+/* for wol feature */
+#include <linux/netdevice.h>
+
+/* WOL Enable Flag:
+ * disable by default to enable system WOL feature of phy
+ * please define this phy to 1 otherwise, define it to 0.
+ */
+#define SIFLOWER_PHY_WOL_FEATURE_ENABLE 0
+#define SIFLOWER_PHY_WOL_PASSWD_ENABLE 0
+
+#define SIFLOWER_PHY_MODE_SET_ENABLE 0
+#define SIFLOWER_PHY_RXC_DELAY_SET_ENABLE 0
+#define SIFLOWER_PHY_RXC_DELAY_VAL 0x40
+#define SIFLOWER_PHY_TXC_DELAY_VAL 0x40
+#define SIFLOWER_PHY_CLK_OUT_125M_ENABLE 1
+
+#define SFPHY_GLB_DISABLE 0
+#define SFPHY_GLB_ENABLE 1
+#define SFPHY_LINK_DOWN 0
+#define SFPHY_LINK_UP 1
+/* Mask used for ID comparisons */
+#define SIFLOWER_PHY_ID_MASK 0xffffffff
+
+/* SF1211F PHY IDs */
+#define SF1211F_PHY_ID 0xADB40412
+/* SF1240 PHY IDs */
+#define SF1240_PHY_ID 0xADB40411
+
+/* SF1211F PHY LED */
+#define SF1211F_EXTREG_LED0 0x1E33 // 0
+#define SF1211F_EXTREG_LED1 0x1E34 // 00101111
+#define SF1211F_EXTREG_LED2 0x1E35 // 0x40
+/* SF1240 PHY BX LED */
+#define SF1240_EXTREG_LEDCTRL 0x0621
+#define SF1240_EXTREG_LED0_1 0x0700
+#define SF1240_EXTREG_LED0_2 0x0701
+#define SF1240_EXTREG_LED1_1 0x0702
+#define SF1240_EXTREG_LED1_2 0x0703
+#define SF1240_EXTREG_LED2_1 0x0706
+#define SF1240_EXTREG_LED2_2 0x0707
+#define SF1240_EXTREG_LED3_1 0x0708
+#define SF1240_EXTREG_LED3_2 0x0709
+#define SF1240_EXTREG_LED4_1 0x070C
+#define SF1240_EXTREG_LED4_2 0x070D
+#define SF1240_EXTREG_LED5_1 0x070E
+#define SF1240_EXTREG_LED5_2 0x070F
+#define SF1240_EXTREG_LED6_1 0x0712
+#define SF1240_EXTREG_LED6_2 0x0713
+#define SF1240_EXTREG_LED7_1 0x0714
+#define SF1240_EXTREG_LED7_2 0x0715
+
+/* PHY MODE OPSREG*/
+#define SF1211F_EXTREG_GET_PORT_PHY_MODE 0x062B
+#define SF1211F_EXTREG_PHY_MODE_MASK 0x0070
+/* Magic Packet MAC address registers */
+#define SIFLOWER_MAGIC_PACKET_MAC_ADDR 0x0229
+/* Magic Packet MAC Passwd registers */
+#define SIFLOWER_MAGIC_PACKET_PASSWD_ADDR 0x022F
+#define SIFLOWER_PHY_WOL_PULSE_MODE_SET 0x062a
+
+/* Magic Packet MAC Passwd Val*/
+#define SIFLOWER_MAGIC_PACKET_PASSWD1 0x11
+#define SIFLOWER_MAGIC_PACKET_PASSWD2 0x22
+#define SIFLOWER_MAGIC_PACKET_PASSWD3 0x33
+#define SIFLOWER_MAGIC_PACKET_PASSWD4 0x44
+#define SIFLOWER_MAGIC_PACKET_PASSWD5 0x55
+#define SIFLOWER_MAGIC_PACKET_PASSWD6 0x66
+
+/* Siflower wol config register */
+#define SIFLOWER_WOL_CFG_REG0 0x0220
+#define SIFLOWER_WOL_CFG_REG1 0x0221
+#define SIFLOWER_WOL_CFG_REG2 0x0222
+#define SIFLOWER_WOL_STA_REG 0x0223
+/* 8 PHY MODE */
+#define SF1211F_EXTREG_PHY_MODE_UTP_TO_RGMII 0x00
+#define SF1211F_EXTREG_PHY_MODE_FIBER_TO_RGMII 0x10
+#define SF1211F_EXTREG_PHY_MODE_UTP_OR_FIBER_TO_RGMII 0x20
+#define SF1211F_EXTREG_PHY_MODE_UTP_TO_SGMII 0x30
+#define SF1211F_EXTREG_PHY_MODE_SGMII_PHY_TO_RGMII_MAC 0x40
+#define SF1211F_EXTREG_PHY_MODE_SGMII_MAC_TO_RGMII_PHY 0x50
+#define SF1211F_EXTREG_PHY_MODE_UTP_TO_FIBER_AUTO 0x60
+#define SF1211F_EXTREG_PHY_MODE_UTP_TO_FIBER_FORCE 0x70
+
+/* PHY EXTRW OPSREG */
+#define SF1211F_EXTREG_ADDR 0x0E
+#define SF1211F_EXTREG_DATA 0x0D
+/* PHY PAGE SPACE */
+#define SFPHY_REG_UTP_SPACE 0
+#define SFPHY_REG_FIBER_SPACE 1
+
+/* PHY PAGE SELECT */
+#define SF1211F_EXTREG_PHY_MODE_PAGE_SELECT 0x0016
+#define SFPHY_REG_UTP_SPACE_SETADDR 0x0000
+#define SFPHY_REG_FIBER_SPACE_SETADDR 0x0100
+//utp
+#define UTP_REG_PAUSE_CAP 0x0400 /* Can pause */
+#define UTP_REG_PAUSE_ASYM 0x0800 /* Can pause asymetrically */
+//fiber
+#define FIBER_REG_PAUSE_CAP 0x0080 /* Can pause */
+#define FIBER_REG_PAUSE_ASYM 0x0100 /* Can pause asymetrically */
+
+/* specific status register */
+#define SIFLOWER_SPEC_REG 0x0011
+
+/* Interrupt Enable Register */
+#define SIFLOWER_INTR_REG 0x0017
+/* WOL TYPE */
+#define SIFLOWER_WOL_TYPE BIT(0)
+/* WOL Pulse Width */
+#define SIFLOWER_WOL_WIDTH1 BIT(1)
+#define SIFLOWER_WOL_WIDTH2 BIT(2)
+/* WOL dest addr check enable */
+#define SIFLOWER_WOL_SECURE_CHECK BIT(5)
+/* WOL crc check enable */
+#define SIFLOWER_WOL_CRC_CHECK BIT(4)
+/* WOL dest addr check enable */
+#define SIFLOWER_WOL_DESTADDR_CHECK BIT(5)
+/* WOL Event Interrupt Enable */
+#define SIFLOWER_WOL_INTR_EN BIT(2)
+/* WOL Enable */
+#define SIFLOWER_WOL_EN BIT(7)
+
+#define SIFLOWER_WOL_RESTARTANEG BIT(9)
+/* GET PHY MODE */
+#define SFPHY_MODE_CURR sfphy_get_port_type(phydev)
+
+enum siflower_port_type_e
+{
+ SFPHY_PORT_TYPE_UTP,
+ SFPHY_PORT_TYPE_FIBER,
+ SFPHY_PORT_TYPE_COMBO,
+ SFPHY_PORT_TYPE_EXT
+};
+enum siflower_wol_type_e
+{
+ SFPHY_WOL_TYPE_LEVEL,
+ SFPHY_WOL_TYPE_PULSE,
+ SFPHY_WOL_TYPE_EXT
+};
+
+enum siflower_wol_width_e
+{
+ SFPHY_WOL_WIDTH_84MS,
+ SFPHY_WOL_WIDTH_168MS,
+ SFPHY_WOL_WIDTH_336MS,
+ SFPHY_WOL_WIDTH_672MS,
+ SFPHY_WOL_WIDTH_EXT
+};
+
+typedef struct siflower_wol_cfg_s
+{
+ int wolen;
+ int type;
+ int width;
+ int secure;
+ int checkcrc;
+ int checkdst;
+}siflower_wol_cfg_t;
+
+static int sf1211f_phy_ext_read(struct phy_device *phydev, u32 regnum)
+{
+ int ret, val, oldpage = 0, oldval = 0;
+
+ phy_lock_mdio_bus(phydev);
+
+ ret = __phy_read(phydev, SF1211F_EXTREG_ADDR);
+ if (ret < 0)
+ goto err_handle;
+ oldval = ret;
+
+ /* Force change to utp page */
+ ret = __phy_read(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT);//get old page
+ if (ret < 0)
+ goto err_handle;
+ oldpage = ret;
+
+ ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, SFPHY_REG_UTP_SPACE_SETADDR);
+ if (ret < 0)
+ goto err_handle;
+
+ /* Default utp ext rw */
+ ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, regnum);
+ if (ret < 0)
+ goto err_handle;
+
+ ret = __phy_read(phydev, SF1211F_EXTREG_DATA);
+ if (ret < 0)
+ goto err_handle;
+ val = ret;
+
+ /* Recover to old page */
+ ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, oldpage);
+ if (ret < 0)
+ goto err_handle;
+
+ ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, oldval);
+ if (ret < 0)
+ goto err_handle;
+ ret = val;
+
+err_handle:
+ phy_unlock_mdio_bus(phydev);
+ return ret;
+}
+
+static int sf1211f_phy_ext_write(struct phy_device *phydev, u32 regnum, u16 val)
+{
+ int ret, oldpage = 0, oldval = 0;
+
+ phy_lock_mdio_bus(phydev);
+
+ ret = __phy_read(phydev, SF1211F_EXTREG_ADDR);
+ if (ret < 0)
+ goto err_handle;
+ oldval = ret;
+
+ /* Force change to utp page */
+ ret = __phy_read(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT); //get old page
+ if (ret < 0)
+ goto err_handle;
+ oldpage = ret;
+
+ ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, SFPHY_REG_UTP_SPACE_SETADDR);
+ if (ret < 0)
+ goto err_handle;
+
+ /* Default utp ext rw */
+ ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, regnum);
+ if (ret < 0)
+ goto err_handle;
+
+ ret = __phy_write(phydev, SF1211F_EXTREG_DATA, val);
+ if (ret < 0)
+ goto err_handle;
+
+ /* Recover to old page */
+ ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, oldpage);
+ if (ret < 0)
+ goto err_handle;
+
+ ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, oldval);
+ if (ret < 0)
+ goto err_handle;
+
+err_handle:
+ phy_unlock_mdio_bus(phydev);
+ return ret;
+
+}
+
+static int siflower_phy_select_reg_page(struct phy_device *phydev, int space)
+{
+ int ret;
+ if (space == SFPHY_REG_UTP_SPACE)
+ ret = phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, SFPHY_REG_UTP_SPACE_SETADDR);
+ else
+ ret = phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, SFPHY_REG_FIBER_SPACE_SETADDR);
+ return ret;
+}
+
+static int siflower_phy_get_reg_page(struct phy_device *phydev)
+{
+ return phy_read(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT);
+}
+
+static int siflower_phy_ext_read(struct phy_device *phydev, u32 regnum)
+{
+ return sf1211f_phy_ext_read(phydev, regnum);
+}
+
+
+static int siflower_phy_ext_write(struct phy_device *phydev, u32 regnum, u16 val)
+{
+ return sf1211f_phy_ext_write(phydev, regnum, val);
+}
+
+static int sfphy_page_read(struct phy_device *phydev, int page, u32 regnum)
+{
+ int ret, val, oldpage = 0, oldval = 0;
+
+ phy_lock_mdio_bus(phydev);
+
+ ret = __phy_read(phydev, SF1211F_EXTREG_ADDR);
+ if (ret < 0)
+ goto err_handle;
+ oldval = ret;
+
+ ret = __phy_read(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT);
+ if (ret < 0)
+ goto err_handle;
+ oldpage = ret;
+
+ //Select page
+ ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, (page << 8));
+ if (ret < 0)
+ goto err_handle;
+
+ ret = __phy_read(phydev, regnum);
+ if (ret < 0)
+ goto err_handle;
+ val = ret;
+
+ /* Recover to old page */
+ ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, oldpage);
+ if (ret < 0)
+ goto err_handle;
+
+ ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, oldval);
+ if (ret < 0)
+ goto err_handle;
+ ret = val;
+
+err_handle:
+ phy_unlock_mdio_bus(phydev);
+ return ret;
+}
+
+static int sfphy_page_write(struct phy_device *phydev, int page, u32 regnum, u16 value)
+{
+ int ret, oldpage = 0, oldval = 0;
+
+ phy_lock_mdio_bus(phydev);
+
+ ret = __phy_read(phydev, SF1211F_EXTREG_ADDR);
+ if (ret < 0)
+ goto err_handle;
+ oldval = ret;
+
+ ret = __phy_read(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT);
+ if (ret < 0)
+ goto err_handle;
+ oldpage = ret;
+
+ //Select page
+ ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, (page << 8));
+ if(ret<0)
+ goto err_handle;
+
+ ret = __phy_write(phydev, regnum, value);
+ if(ret<0)
+ goto err_handle;
+
+ /* Recover to old page */
+ ret = __phy_write(phydev, SF1211F_EXTREG_PHY_MODE_PAGE_SELECT, oldpage);
+ if (ret < 0)
+ goto err_handle;
+
+ ret = __phy_write(phydev, SF1211F_EXTREG_ADDR, oldval);
+ if (ret < 0)
+ goto err_handle;
+
+err_handle:
+ phy_unlock_mdio_bus(phydev);
+ return ret;
+}
+
+//get port type
+static int sfphy_get_port_type(struct phy_device *phydev)
+{
+ int ret, mode;
+
+ ret = siflower_phy_ext_read(phydev, SF1211F_EXTREG_GET_PORT_PHY_MODE);
+ if (ret < 0)
+ return ret;
+ ret &= SF1211F_EXTREG_PHY_MODE_MASK;
+
+ if (ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_RGMII ||
+ ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_SGMII) {
+ mode = SFPHY_PORT_TYPE_UTP;
+ } else if (ret == SF1211F_EXTREG_PHY_MODE_FIBER_TO_RGMII ||
+ ret == SF1211F_EXTREG_PHY_MODE_SGMII_PHY_TO_RGMII_MAC ||
+ ret == SF1211F_EXTREG_PHY_MODE_SGMII_MAC_TO_RGMII_PHY) {
+ mode = SFPHY_PORT_TYPE_FIBER;
+ } else {
+ mode = SFPHY_PORT_TYPE_COMBO;
+ }
+
+ return mode;
+}
+
+static int sfphy_restart_aneg(struct phy_device *phydev)
+{
+ int ret, ctl;
+
+ ctl = sfphy_page_read(phydev, SFPHY_REG_FIBER_SPACE, MII_BMCR);
+ if (ctl < 0)
+ return ctl;
+ ctl |= BMCR_ANENABLE;
+ ret = sfphy_page_write(phydev, SFPHY_REG_FIBER_SPACE, MII_BMCR, ctl);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int sf1211f_config_aneg(struct phy_device *phydev)
+{
+ int ret, phymode, oldpage = 0;
+
+ phymode = SFPHY_MODE_CURR;
+
+ if (phymode == SFPHY_PORT_TYPE_UTP || phymode == SFPHY_PORT_TYPE_COMBO) {
+ oldpage = siflower_phy_get_reg_page(phydev);
+ if (oldpage < 0)
+ return oldpage;
+ ret = siflower_phy_select_reg_page(phydev, SFPHY_REG_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ ret = genphy_config_aneg(phydev);
+ if (ret < 0)
+ return ret;
+ ret = siflower_phy_select_reg_page(phydev, oldpage);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (phymode == SFPHY_PORT_TYPE_FIBER || phymode == SFPHY_PORT_TYPE_COMBO) {
+ oldpage = siflower_phy_get_reg_page(phydev);
+ if (oldpage < 0)
+ return oldpage;
+ ret = siflower_phy_select_reg_page(phydev, SFPHY_REG_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+ if (AUTONEG_ENABLE != phydev->autoneg)
+ return genphy_setup_forced(phydev);
+ ret = sfphy_restart_aneg(phydev);
+ if (ret < 0)
+ return ret;
+ ret = siflower_phy_select_reg_page(phydev, oldpage);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+int sf1211f_aneg_done(struct phy_device *phydev)
+{
+ int val = 0;
+
+ val = phy_read(phydev, 0x16);
+
+ if (val == SFPHY_REG_FIBER_SPACE_SETADDR) {
+ val = phy_read(phydev, 0x1);
+ val = phy_read(phydev, 0x1);
+ return (val < 0) ? val : (val & BMSR_LSTATUS);
+ }
+
+ return genphy_aneg_done(phydev);
+}
+
+#if (SIFLOWER_PHY_WOL_FEATURE_ENABLE)
+static void siflower_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+{
+ int val = 0;
+ wol->supported = WAKE_MAGIC;
+ wol->wolopts = 0;
+
+ val = siflower_phy_ext_read(phydev, SIFLOWER_WOL_CFG_REG1);
+ if (val < 0)
+ return;
+
+ if (val & SIFLOWER_WOL_EN)
+ wol->wolopts |= WAKE_MAGIC;
+
+ return;
+}
+
+static int siflower_wol_en_cfg(struct phy_device *phydev, siflower_wol_cfg_t wol_cfg)
+{
+ int ret, val0,val1;
+
+ val0 = siflower_phy_ext_read(phydev, SIFLOWER_WOL_CFG_REG0);
+ if (val0 < 0)
+ return val0;
+ val1 = siflower_phy_ext_read(phydev, SIFLOWER_WOL_CFG_REG1);
+ if (val1 < 0)
+ return val1;
+ if (wol_cfg.wolen) {
+ val1 |= SIFLOWER_WOL_EN;
+ if (wol_cfg.type == SFPHY_WOL_TYPE_LEVEL) {
+ val0 |= SIFLOWER_WOL_TYPE;
+ } else if (wol_cfg.type == SFPHY_WOL_TYPE_PULSE) {
+ ret = siflower_phy_ext_write(phydev, SIFLOWER_PHY_WOL_PULSE_MODE_SET, 0x04);//set int pin pulse
+ if (ret < 0)
+ return ret;
+ val0 &= ~SIFLOWER_WOL_TYPE;
+ if (wol_cfg.width == SFPHY_WOL_WIDTH_84MS) {
+ val0 &= ~SIFLOWER_WOL_WIDTH1;
+ val0 &= ~SIFLOWER_WOL_WIDTH2;
+ } else if (wol_cfg.width == SFPHY_WOL_WIDTH_168MS) {
+ val0 |= SIFLOWER_WOL_WIDTH1;
+ val0 &= ~SIFLOWER_WOL_WIDTH2;
+ } else if (wol_cfg.width == SFPHY_WOL_WIDTH_336MS) {
+ val0 &= ~SIFLOWER_WOL_WIDTH1;
+ val0 |= SIFLOWER_WOL_WIDTH2;
+ } else if (wol_cfg.width == SFPHY_WOL_WIDTH_672MS) {
+ val0 |= SIFLOWER_WOL_WIDTH1;
+ val0 |= SIFLOWER_WOL_WIDTH2;
+ }
+ }
+ if (wol_cfg.secure == SFPHY_GLB_ENABLE)
+ val1 |= SIFLOWER_WOL_SECURE_CHECK;
+ else
+ val1 &= ~SIFLOWER_WOL_SECURE_CHECK;
+ if (wol_cfg.checkcrc == SFPHY_GLB_ENABLE)
+ val0 |= SIFLOWER_WOL_CRC_CHECK;
+ else
+ val0 &= ~SIFLOWER_WOL_CRC_CHECK;
+ if (wol_cfg.checkdst == SFPHY_GLB_ENABLE)
+ val0 |= SIFLOWER_WOL_DESTADDR_CHECK;
+ else
+ val0 &= ~SIFLOWER_WOL_DESTADDR_CHECK;
+ } else {
+ val1 &= ~SIFLOWER_WOL_EN;
+ }
+
+ ret = siflower_phy_ext_write(phydev, SIFLOWER_WOL_CFG_REG0, val0);
+ if (ret < 0)
+ return ret;
+ ret = siflower_phy_ext_write(phydev, SIFLOWER_WOL_CFG_REG1, val1);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int siflower_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+{
+ int ret, val, i, phymode;
+ siflower_wol_cfg_t wol_cfg;
+
+ phymode = SFPHY_MODE_CURR;
+ memset(&wol_cfg,0,sizeof(siflower_wol_cfg_t));
+
+ if (wol->wolopts & WAKE_MAGIC) {
+ if (phymode == SFPHY_PORT_TYPE_UTP || phymode == SFPHY_PORT_TYPE_COMBO) {
+ /* Enable the WOL interrupt */
+ val = sfphy_page_read(phydev, SFPHY_REG_UTP_SPACE, SIFLOWER_INTR_REG);
+ val |= SIFLOWER_WOL_INTR_EN;
+ ret = sfphy_page_write(phydev, SFPHY_REG_UTP_SPACE, SIFLOWER_INTR_REG, val);
+ if (ret < 0)
+ return ret;
+ }
+ if (phymode == SFPHY_PORT_TYPE_FIBER || phymode == SFPHY_PORT_TYPE_COMBO) {
+ /* Enable the WOL interrupt */
+ val = sfphy_page_read(phydev, SFPHY_REG_FIBER_SPACE, SIFLOWER_INTR_REG);
+ val |= SIFLOWER_WOL_INTR_EN;
+ ret = sfphy_page_write(phydev, SFPHY_REG_FIBER_SPACE, SIFLOWER_INTR_REG, val);
+ if (ret < 0)
+ return ret;
+ }
+ /* Set the WOL config */
+ wol_cfg.wolen = SFPHY_GLB_ENABLE;
+ wol_cfg.type = SFPHY_WOL_TYPE_PULSE;
+ wol_cfg.width = SFPHY_WOL_WIDTH_672MS;
+ wol_cfg.checkdst = SFPHY_GLB_ENABLE;
+ wol_cfg.checkcrc = SFPHY_GLB_ENABLE;
+ ret = siflower_wol_en_cfg(phydev, wol_cfg);
+ if (ret < 0)
+ return ret;
+
+ /* Store the device address for the magic packet */
+ for(i = 0; i < 6; ++i) {
+ ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_MAC_ADDR - i,
+ ((phydev->attached_dev->dev_addr[i])));
+ if (ret < 0)
+ return ret;
+ }
+#if SIFLOWER_PHY_WOL_PASSWD_ENABLE
+ /* Set passwd for the magic packet */
+ ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR, SIFLOWER_MAGIC_PACKET_PASSWD1);
+ if (ret < 0)
+ return ret;
+ ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR - 1, SIFLOWER_MAGIC_PACKET_PASSWD2);
+ if (ret < 0)
+ return ret;
+ ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR - 2, SIFLOWER_MAGIC_PACKET_PASSWD3);
+ if (ret < 0)
+ return ret;
+ ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR - 3, SIFLOWER_MAGIC_PACKET_PASSWD4);
+ if (ret < 0)
+ return ret;
+ ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR - 4, SIFLOWER_MAGIC_PACKET_PASSWD5);
+ if (ret < 0)
+ return ret;
+ ret = siflower_phy_ext_write(phydev, SIFLOWER_MAGIC_PACKET_PASSWD_ADDR - 5, SIFLOWER_MAGIC_PACKET_PASSWD6);
+ if (ret < 0)
+ return ret;
+#endif
+ } else {
+ wol_cfg.wolen = SFPHY_GLB_DISABLE;
+ wol_cfg.type = SFPHY_WOL_TYPE_EXT;
+ wol_cfg.width = SFPHY_WOL_WIDTH_EXT;
+ wol_cfg.checkdst = SFPHY_GLB_DISABLE;
+ wol_cfg.checkcrc = SFPHY_GLB_DISABLE;
+ ret = siflower_wol_en_cfg(phydev, wol_cfg);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (val == SF1211F_EXTREG_PHY_MODE_UTP_TO_SGMII) {
+ val = sfphy_page_read(phydev, SFPHY_REG_UTP_SPACE, MII_BMCR);
+ val |= SIFLOWER_WOL_RESTARTANEG;
+ ret = sfphy_page_write(phydev, SFPHY_REG_UTP_SPACE, MII_BMCR, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+static int sf1211f_rxc_txc_init(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = (siflower_phy_ext_read(phydev, SF1211F_EXTREG_GET_PORT_PHY_MODE) &
+ SF1211F_EXTREG_PHY_MODE_MASK);
+ if (ret < 0)
+ return ret;
+
+ if ((ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_SGMII) ||
+ (ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_FIBER_AUTO) ||
+ (ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_FIBER_FORCE))
+ return 0;
+
+ // Init rxc and enable rxc
+ if (ret == SF1211F_EXTREG_PHY_MODE_UTP_TO_RGMII) {
+ ret = phy_read(phydev, 0x11);
+ if ((ret & 0x4) == 0x0) {
+ ret = siflower_phy_ext_write(phydev,0x1E0C, 0x17);
+ if (ret < 0)
+ return ret;
+ ret = siflower_phy_ext_write(phydev,0x1E58, 0x00);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID){
+ // Init rxc delay
+ ret = siflower_phy_ext_write(phydev,0x0282, SIFLOWER_PHY_RXC_DELAY_VAL);
+ if (ret < 0)
+ return ret;
+ }
+ else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID){
+ // Init txc delay
+ ret = siflower_phy_ext_write(phydev,0x0281, SIFLOWER_PHY_TXC_DELAY_VAL);
+ if (ret < 0)
+ return ret;
+ }
+ else if(phydev->interface == PHY_INTERFACE_MODE_RGMII_ID){
+ ret = siflower_phy_ext_write(phydev,0x0282, SIFLOWER_PHY_RXC_DELAY_VAL);
+ if (ret < 0)
+ return ret;
+ ret = siflower_phy_ext_write(phydev,0x0281, SIFLOWER_PHY_TXC_DELAY_VAL);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int sf1211f_config_opt(struct phy_device *phydev)
+{
+ int ret;
+ //100M utp optimise
+ ret = siflower_phy_ext_write(phydev, 0x0149, 0x84);
+ if (ret < 0)
+ return ret;
+
+ ret = siflower_phy_ext_write(phydev, 0x014A, 0x86);
+ if (ret < 0)
+ return ret;
+
+ ret = siflower_phy_ext_write(phydev, 0x023C, 0x81);
+ if (ret < 0)
+ return ret;
+
+ //1000M utp optimise
+ ret = siflower_phy_ext_write(phydev, 0x0184, 0x85);
+ if (ret < 0)
+ return ret;
+
+ ret = siflower_phy_ext_write(phydev, 0x0185, 0x86);
+ if (ret < 0)
+ return ret;
+
+ ret = siflower_phy_ext_write(phydev, 0x0186, 0x85);
+ if (ret < 0)
+ return ret;
+
+ ret = siflower_phy_ext_write(phydev, 0x0187, 0x86);
+ if (ret < 0)
+ return ret;
+ return ret;
+}
+#if SIFLOWER_PHY_CLK_OUT_125M_ENABLE
+static int sf1211f_clkout_init(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = siflower_phy_ext_write(phydev, 0x0272 , 0x09);
+
+ return ret;
+}
+#endif
+
+#if SIFLOWER_PHY_MODE_SET_ENABLE
+//set mode
+static int phy_mode_set(struct phy_device *phydev, u16 phyMode)
+{
+ int ret, num = 0;
+
+ ret = siflower_phy_ext_read(phydev, 0xC417);
+ if (ret < 0)
+ return ret;
+
+ ret = (ret & 0xF0) | (0x8 | phyMode);
+
+ ret = siflower_phy_ext_write(phydev, 0xC417, ret);
+ if (ret < 0)
+ return ret;
+
+ while ((siflower_phy_ext_read(phydev, 0xC415) & 0x07) != phyMode) {
+ msleep(10);
+ if(++num == 5) {
+ printk("Phy Mode Set Time Out!\r\n");
+ break;
+ }
+ }
+
+ while (siflower_phy_ext_read(phydev, 0xC413) != 0) {
+ msleep(10);
+ if(++num == 10) {
+ printk("Phy Mode Set Time Out!\r\n");
+ break;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+int sf1240_config_init(struct phy_device *phydev)
+{
+ int ret;
+ ret = genphy_read_abilities(phydev);
+ if (ret < 0)
+ return ret;
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ phydev->supported, ESTATUS_1000_TFULL);
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ phydev->advertising, ESTATUS_1000_TFULL);
+ return 0;
+}
+
+int sf1211f_config_init(struct phy_device *phydev)
+{
+ int ret, phymode;
+
+#if SIFLOWER_PHY_WOL_FEATURE_ENABLE
+ struct ethtool_wolinfo wol;
+#endif
+
+#if SIFLOWER_PHY_MODE_SET_ENABLE
+ ret = phy_mode_set(phydev, 0x0);
+ if (ret < 0)
+ return ret;
+#endif
+ phymode = SFPHY_MODE_CURR;
+
+ if (phymode == SFPHY_PORT_TYPE_UTP || phymode == SFPHY_PORT_TYPE_COMBO) {
+ siflower_phy_select_reg_page(phydev, SFPHY_REG_UTP_SPACE);
+ ret = genphy_read_abilities(phydev);
+ if (ret < 0)
+ return ret;
+ } else {
+ siflower_phy_select_reg_page(phydev, SFPHY_REG_FIBER_SPACE);
+ ret = genphy_read_abilities(phydev);
+ if (ret < 0)
+ return ret;
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ phydev->supported, ESTATUS_1000_TFULL);
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ phydev->advertising, ESTATUS_1000_TFULL);
+ }
+
+ ret = sf1211f_rxc_txc_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = sf1211f_config_opt(phydev);
+ if (ret < 0)
+ return ret;
+
+#if SIFLOWER_PHY_CLK_OUT_125M_ENABLE
+ ret = sf1211f_clkout_init(phydev);
+ if (ret < 0)
+ return ret;
+#endif
+
+#if SIFLOWER_PHY_WOL_FEATURE_ENABLE
+ wol.wolopts = 0;
+ wol.supported = WAKE_MAGIC;
+ wol.wolopts |= WAKE_MAGIC;
+ siflower_set_wol(phydev, &wol);
+#endif
+
+ return 0;
+}
+
+static struct phy_driver sf_phy_drivers[] = {
+ {
+ .phy_id = SF1211F_PHY_ID,
+ .phy_id_mask = SIFLOWER_PHY_ID_MASK,
+ .name = "SF1211F Gigabit Ethernet",
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .config_init = sf1211f_config_init,
+ .config_aneg = sf1211f_config_aneg,
+ .aneg_done = sf1211f_aneg_done,
+ .write_mmd = genphy_write_mmd_unsupported,
+ .read_mmd = genphy_read_mmd_unsupported,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+#if SIFLOWER_PHY_WOL_FEATURE_ENABLE
+ .get_wol = &siflower_get_wol,
+ .set_wol = &siflower_set_wol,
+#endif
+ },
+
+ {
+ .phy_id = SF1240_PHY_ID,
+ .phy_id_mask = SIFLOWER_PHY_ID_MASK,
+ .name = "SF1240 Gigabit Ethernet",
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .config_init = sf1240_config_init,
+ .config_aneg = genphy_config_aneg,
+ .write_mmd = genphy_write_mmd_unsupported,
+ .read_mmd = genphy_read_mmd_unsupported,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ },
+};
+
+/* for linux 4.x */
+module_phy_driver(sf_phy_drivers);
+
+static struct mdio_device_id __maybe_unused siflower_phy_tbl[] = {
+ { SF1211F_PHY_ID, SIFLOWER_PHY_ID_MASK },
+ { SF1240_PHY_ID, SIFLOWER_PHY_ID_MASK },
+ {},
+};
+
+MODULE_DEVICE_TABLE(mdio, siflower_phy_tbl);
+
+MODULE_DESCRIPTION("Siflower PHY driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Siflower SF21A6826/SF21H8898 PCIE driver
+ *
+ * Author: Chuanhong Guo <gch981213@gmail.com>
+ */
+
+#include <linux/phy/phy.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+#include <linux/reset.h>
+
+#include "pcie-designware.h"
+
+#define SF_PCIE_MAX_TIMEOUT 10000
+
+#define ELBI_REG0 0x0
+#define APP_LTSSM_ENABLE BIT(23)
+
+#define to_sf_pcie(x) dev_get_drvdata((x)->dev)
+
+#define SYSM_PCIE_SET 0x0
+#define PCIE_DEVTYPE_EP 0
+#define PCIE_DEVTYPE_RC 4
+
+#define SYSM_PCIE_INIT 0x4
+#define SYSM_PCIE_CLK_EN 0x9c
+
+enum sf_pcie_regfield_ids {
+ DEVICE_TYPE,
+ PERST_N_OUT,
+ PERST_N,
+ BUTTON_RSTN,
+ POWER_UP_RSTN,
+ ACLK_M_EN,
+ ACLK_S_EN,
+ ACLK_C_EN,
+ HCLK_EN,
+ SYSM_REGFIELD_MAX,
+};
+
+static const struct reg_field pcie0_sysm_regs[SYSM_REGFIELD_MAX] = {
+ [DEVICE_TYPE] = REG_FIELD(SYSM_PCIE_SET, 0, 3),
+ [PERST_N_OUT] = REG_FIELD(SYSM_PCIE_INIT, 15, 15),
+ [PERST_N] = REG_FIELD(SYSM_PCIE_INIT, 5, 5),
+ [BUTTON_RSTN] = REG_FIELD(SYSM_PCIE_INIT, 4, 4),
+ [POWER_UP_RSTN] = REG_FIELD(SYSM_PCIE_INIT, 3, 3),
+ [ACLK_M_EN] = REG_FIELD(SYSM_PCIE_CLK_EN, 3, 3),
+ [ACLK_S_EN] = REG_FIELD(SYSM_PCIE_CLK_EN, 2, 2),
+ [ACLK_C_EN] = REG_FIELD(SYSM_PCIE_CLK_EN, 1, 1),
+ [HCLK_EN] = REG_FIELD(SYSM_PCIE_CLK_EN, 0, 0),
+};
+
+static const struct reg_field pcie1_sysm_regs[SYSM_REGFIELD_MAX] = {
+ [DEVICE_TYPE] = REG_FIELD(SYSM_PCIE_SET, 4, 7),
+ [PERST_N_OUT] = REG_FIELD(SYSM_PCIE_INIT, 16, 16),
+ [PERST_N] = REG_FIELD(SYSM_PCIE_INIT, 8, 8),
+ [BUTTON_RSTN] = REG_FIELD(SYSM_PCIE_INIT, 7, 7),
+ [POWER_UP_RSTN] = REG_FIELD(SYSM_PCIE_INIT, 6, 6),
+ [ACLK_M_EN] = REG_FIELD(SYSM_PCIE_CLK_EN, 8, 8),
+ [ACLK_S_EN] = REG_FIELD(SYSM_PCIE_CLK_EN, 7, 7),
+ [ACLK_C_EN] = REG_FIELD(SYSM_PCIE_CLK_EN, 6, 6),
+ [HCLK_EN] = REG_FIELD(SYSM_PCIE_CLK_EN, 5, 5),
+};
+
+struct sf_pcie {
+ struct dw_pcie pci;
+ void __iomem *elbi;
+ struct clk *csr_clk;
+ struct clk *ref_clk;
+ struct phy *phy;
+ struct regmap *pciesys;
+ struct regmap_field *pciesys_reg[SYSM_REGFIELD_MAX];
+ struct gpio_desc *reset_gpio;
+};
+
+static void sf_pcie_enable_part_lanes_rxei_exit(struct sf_pcie *sf_pcie)
+{
+ u32 val;
+ val = readl(sf_pcie->pci.dbi_base + 0x708);
+ val = val | 0x1 << 22;
+ writel(val, sf_pcie->pci.dbi_base + 0x708);
+ val = readl(sf_pcie->pci.dbi_base + 0x708);
+ msleep(20);
+}
+
+static void sf_pcie_enable_speed_change(struct sf_pcie *sf_pcie)
+{
+ u32 val;
+ val = readl(sf_pcie->pci.dbi_base + 0x80c);
+ val = val | 0x1 << 17;
+ writel(val, sf_pcie->pci.dbi_base + 0x80c);
+ val = readl(sf_pcie->pci.dbi_base + 0x80c);
+ msleep(20);
+}
+
+static int sf_pcie_clk_enable(struct sf_pcie *sf_pcie)
+{
+ int ret;
+ ret = clk_prepare_enable(sf_pcie->csr_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(sf_pcie->ref_clk);
+ if (ret)
+ return ret;
+
+ regmap_field_write(sf_pcie->pciesys_reg[ACLK_M_EN], 1);
+ regmap_field_write(sf_pcie->pciesys_reg[ACLK_S_EN], 1);
+ regmap_field_write(sf_pcie->pciesys_reg[ACLK_C_EN], 1);
+ regmap_field_write(sf_pcie->pciesys_reg[HCLK_EN], 1);
+ return 0;
+}
+
+static void sf_pcie_clk_disable(struct sf_pcie *sf_pcie)
+{
+ regmap_field_write(sf_pcie->pciesys_reg[ACLK_M_EN], 0);
+ regmap_field_write(sf_pcie->pciesys_reg[ACLK_S_EN], 0);
+ regmap_field_write(sf_pcie->pciesys_reg[ACLK_C_EN], 0);
+ regmap_field_write(sf_pcie->pciesys_reg[HCLK_EN], 0);
+ clk_disable_unprepare(sf_pcie->csr_clk);
+ clk_disable_unprepare(sf_pcie->ref_clk);
+}
+
+static int sf_pcie_phy_enable(struct sf_pcie *pcie)
+{
+ int ret;
+
+ ret = phy_init(pcie->phy);
+ if (ret)
+ return ret;
+ return phy_power_on(pcie->phy);
+}
+
+static int sf_pcie_phy_disable(struct sf_pcie *pcie)
+{
+ int ret;
+
+ ret = phy_power_off(pcie->phy);
+ if (ret)
+ return ret;
+ return phy_exit(pcie->phy);
+}
+
+static void sf_pcie_ltssm_set_en(struct sf_pcie *pcie, bool enable)
+{
+ u32 val;
+
+ val = readl(pcie->elbi + ELBI_REG0);
+ if (enable)
+ val |= APP_LTSSM_ENABLE;
+ else
+ val &= ~APP_LTSSM_ENABLE;
+ writel(val, pcie->elbi + ELBI_REG0);
+}
+
+static void sf_pcie_set_reset(struct sf_pcie *pcie, bool assert) {
+ regmap_field_write(pcie->pciesys_reg[PERST_N], !assert);
+ regmap_field_write(pcie->pciesys_reg[BUTTON_RSTN], !assert);
+ regmap_field_write(pcie->pciesys_reg[POWER_UP_RSTN], !assert);
+}
+
+/*
+ * The bus interconnect subtracts address offset from the request
+ * before sending it to PCIE slave port. Since DT puts config space
+ * at the beginning, we can obtain the address offset from there and
+ * subtract it.
+ */
+static u64 sf_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 cpu_addr)
+{
+ struct dw_pcie_rp *pp = &pci->pp;
+
+ return cpu_addr - pp->cfg0_base;
+}
+
+static int sf_pcie_host_init(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct sf_pcie *sf_pcie = to_sf_pcie(pci);
+ int ret;
+
+ ret = sf_pcie_clk_enable(sf_pcie);
+ if (ret)
+ return dev_err_probe(sf_pcie->pci.dev, ret,
+ "failed to enable pcie clocks.\n");
+
+ sf_pcie_set_reset(sf_pcie, true);
+
+ ret = regmap_field_write(sf_pcie->pciesys_reg[DEVICE_TYPE], PCIE_DEVTYPE_RC);
+ if (ret)
+ return ret;
+
+ gpiod_set_value_cansleep(sf_pcie->reset_gpio, 1);
+
+ ret = sf_pcie_phy_enable(sf_pcie);
+ if (ret)
+ return ret;
+
+ /* TODO: release power-down */
+ msleep(100);
+
+ sf_pcie_set_reset(sf_pcie, false);
+
+ dw_pcie_dbi_ro_wr_en(pci);
+ sf_pcie_enable_part_lanes_rxei_exit(sf_pcie);
+
+ gpiod_set_value_cansleep(sf_pcie->reset_gpio, 0);
+ return 0;
+}
+
+void sf_pcie_host_deinit(struct dw_pcie_rp *pp) {
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct sf_pcie *sf_pcie = to_sf_pcie(pci);
+
+ sf_pcie_set_reset(sf_pcie, true);
+
+ sf_pcie_phy_disable(sf_pcie);
+
+ gpiod_set_value_cansleep(sf_pcie->reset_gpio, 1);
+
+ sf_pcie_clk_disable(sf_pcie);
+}
+
+static const struct dw_pcie_host_ops sf_pcie_host_ops = {
+ .host_init = sf_pcie_host_init,
+ .host_deinit = sf_pcie_host_deinit,
+};
+
+static int sf_pcie_start_link(struct dw_pcie *pci)
+{
+ struct sf_pcie *pcie = to_sf_pcie(pci);
+ /*
+ * before link up with GEN1, we should config the field
+ * DIRECTION_SPEED_CHANGE of GEN2_CTRL_OFF register to insure
+ * the LTSSM to initiate a speed change to Gen2 or Gen3 after
+ * the link is initialized at Gen1 speed.
+ */
+ sf_pcie_enable_speed_change(pcie);
+
+ sf_pcie_ltssm_set_en(pcie, true);
+ return 0;
+}
+
+static void sf_pcie_stop_link(struct dw_pcie *pci) {
+ struct sf_pcie *pcie = to_sf_pcie(pci);
+
+ sf_pcie_ltssm_set_en(pcie, false);
+}
+
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .cpu_addr_fixup = sf_pcie_cpu_addr_fixup,
+ .start_link = sf_pcie_start_link,
+ .stop_link = sf_pcie_stop_link,
+};
+
+static int sf_pcie_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct sf_pcie *sf_pcie;
+ int ret;
+ u32 ctlr_id;
+
+ sf_pcie = devm_kzalloc(dev, sizeof(*sf_pcie), GFP_KERNEL);
+ if (!sf_pcie)
+ return -ENOMEM;
+
+ sf_pcie->pci.dev = dev;
+ sf_pcie->pci.ops = &dw_pcie_ops;
+ sf_pcie->pci.pp.ops = &sf_pcie_host_ops;
+
+ platform_set_drvdata(pdev, sf_pcie);
+
+ sf_pcie->csr_clk = devm_clk_get(dev, "csr");
+ if (IS_ERR(sf_pcie->csr_clk))
+ return PTR_ERR(sf_pcie->csr_clk);
+
+ sf_pcie->ref_clk = devm_clk_get(dev, "ref");
+ if (IS_ERR(sf_pcie->ref_clk))
+ return PTR_ERR(sf_pcie->ref_clk);
+
+ sf_pcie->reset_gpio =
+ devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(sf_pcie->reset_gpio)) {
+ return dev_err_probe(dev, PTR_ERR(sf_pcie->reset_gpio),
+ "unable to get reset gpio\n");
+ }
+
+ sf_pcie->pciesys = syscon_regmap_lookup_by_phandle(
+ pdev->dev.of_node, "siflower,pcie-sysm");
+ if (IS_ERR(sf_pcie->pciesys))
+ return PTR_ERR(sf_pcie->pciesys);
+
+ sf_pcie->phy = devm_phy_get(dev, NULL);
+ if (IS_ERR(sf_pcie->phy))
+ return PTR_ERR(sf_pcie->phy);
+
+ sf_pcie->elbi = devm_platform_ioremap_resource_byname(pdev, "elbi");
+ if (IS_ERR(sf_pcie->elbi)) {
+ return PTR_ERR(sf_pcie->elbi);
+ }
+
+ ret = of_property_read_u32(node, "siflower,ctlr-idx", &ctlr_id);
+ if (ret) {
+ ctlr_id = 0;
+ }
+
+ ret = devm_regmap_field_bulk_alloc(
+ dev, sf_pcie->pciesys, sf_pcie->pciesys_reg,
+ ctlr_id ? pcie1_sysm_regs : pcie0_sysm_regs, SYSM_REGFIELD_MAX);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to alloc regmap fields.\n");
+
+ ret = dw_pcie_host_init(&sf_pcie->pci.pp);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize host\n");
+
+ return 0;
+}
+
+static int sf_pcie_remove(struct platform_device *pdev)
+{
+ struct sf_pcie *pcie = platform_get_drvdata(pdev);
+
+ dw_pcie_host_deinit(&pcie->pci.pp);
+ return 0;
+
+}
+
+static const struct of_device_id sf_pcie_of_match[] = {
+ { .compatible = "siflower,sf21-pcie", },
+ {},
+};
+
+static struct platform_driver sf_pcie_driver = {
+ .driver = {
+ .name = "sf21-pcie",
+ .of_match_table = sf_pcie_of_match,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = sf_pcie_probe,
+ .remove = sf_pcie_remove,
+};
+
+module_platform_driver(sf_pcie_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Chuanhong Guo <gch981213@gmail.com>");
+MODULE_DESCRIPTION("PCIe Controller driver for SF21A6826/SF21H8898 SoC");
select GENERIC_PHY
help
Enable this to support the USB2.0 PHY on the SIFLOWER SF19A2890.
+
+config PHY_SF21_PCIE
+ tristate "Siflower SF21A6826/SF21H8898 PCIE PHY driver"
+ default n
+ depends on HAS_IOMEM
+ select GENERIC_PHY
+ help
+ Enable this to support the PCIE PHY on the Siflower SF21A6826 SoC.
+
+
+config PHY_SF21_USB
+ tristate "Siflower SF21A6826/SF21H8898 USB2.0 PHY driver"
+ default n
+ depends on HAS_IOMEM
+ select GENERIC_PHY
+ help
+ Enable this to support the USB2.0 PHY on the Siflower SF21A6826/SF21H8898 SoC.
obj-$(CONFIG_PHY_SF19A2890_USB) += phy-sf19a2890-usb.o
-
+obj-$(CONFIG_PHY_SF21_PCIE) += phy-sf21-pcie.o
+obj-$(CONFIG_PHY_SF21_USB) += phy-sf21-usb.o
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/mfd/syscon.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+
+#define SF_PCIE_PHY_NLANES 2
+
+#define TOPCRM_LVDS0_CFG 0xe8
+#define TOPCRM_LVDS1_CFG 0x120
+#define LVDS_BIAS_EN BIT(20)
+#define LVDS_PULLDN BIT(19)
+#define LVDS_SCHMITT_EN BIT(18)
+#define LVDS_TX_CM BIT(17)
+#define LVDS_RXCM_EN BIT(16)
+#define LVDS_RTERM_VAL GENMASK(15, 13)
+#define LVDS_TXDRV GENMASK(12, 9)
+#define LVDS_IEN_N BIT(8)
+#define LVDS_IEN_P BIT(7)
+#define LVDS_OEN_N BIT(6)
+#define LVDS_OEN_P BIT(5)
+#define LVDS_RTERM_EN BIT(4)
+#define LVDS_RXEN BIT(3)
+#define LVDS_TXEN BIT(2)
+#define LVDS_VBIAS_SEL BIT(0)
+
+#define PCIE_SYSM_SET 0x0
+#define PCIE_LANE_MUX GENMASK(9, 8)
+#define PHY0_L0_PHY1_L1 (0 << 8)
+#define PHY0_L0L1 (1 << 8)
+#define PHY0_L1_PHY1_L0 (2 << 8)
+#define PHY1_L0L1 (3 << 8)
+
+#define PCIE_SYSM_INIT 0x4
+#define PCIE_L1_REPEAT_CLK_EN BIT(10)
+#define PCIE_L0_REPEAT_CLK_EN BIT(9)
+#define PCIE_L1_RSTN BIT(2)
+#define PCIE_L0_RSTN BIT(1)
+#define PCIE_PHY_RSTN BIT(0)
+
+
+struct sf21_pcie_phy_inst {
+ struct phy *phy;
+ u8 idx;
+ u8 num_lanes;
+ u8 lvds_idx;
+};
+
+struct sf21_pcie_phy {
+ struct device *dev;
+ struct clk *refclk;
+ struct clk *csrclk;
+ struct regmap *pcie_regmap;
+ struct regmap *topcrm_regmap;
+ struct mutex lock;
+ int nlanes_enabled;
+ struct sf21_pcie_phy_inst insts[2];
+};
+
+static struct sf21_pcie_phy_inst *phy_to_instance(struct phy *phy)
+{
+ return phy_get_drvdata(phy);
+}
+
+static struct sf21_pcie_phy *
+instance_to_priv(struct sf21_pcie_phy_inst *inst)
+{
+ return container_of(inst, struct sf21_pcie_phy, insts[inst->idx]);
+}
+
+static int sf_pcie_phy_lvds_on(struct sf21_pcie_phy *priv, int idx)
+{
+ return regmap_set_bits(priv->topcrm_regmap,
+ idx ? TOPCRM_LVDS1_CFG : TOPCRM_LVDS0_CFG,
+ LVDS_TXEN | LVDS_BIAS_EN);
+}
+
+static int sf_pcie_phy_lvds_off(struct sf21_pcie_phy *priv, int idx)
+{
+ return regmap_clear_bits(priv->topcrm_regmap,
+ idx ? TOPCRM_LVDS1_CFG : TOPCRM_LVDS0_CFG,
+ LVDS_TXEN | LVDS_BIAS_EN);
+}
+
+static int sf_pcie_lane_on(struct sf21_pcie_phy *priv, int idx)
+{
+ return regmap_set_bits(priv->pcie_regmap, PCIE_SYSM_INIT,
+ idx ? PCIE_L1_RSTN : PCIE_L0_RSTN);
+}
+
+static int sf_pcie_lane_off(struct sf21_pcie_phy *priv, int idx)
+{
+ return regmap_clear_bits(priv->pcie_regmap, PCIE_SYSM_INIT,
+ idx ? PCIE_L1_RSTN : PCIE_L0_RSTN);
+}
+
+static int sf21_pcie_phy_power_on(struct phy *phy)
+{
+ struct sf21_pcie_phy_inst *inst = phy_to_instance(phy);
+ struct sf21_pcie_phy *priv = instance_to_priv(inst);
+ int ret;
+ mutex_lock(&priv->lock);
+ if (SF_PCIE_PHY_NLANES - priv->nlanes_enabled < inst->num_lanes) {
+ dev_err(priv->dev, "too many lanes requested for PHY %u\n",
+ inst->idx);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (inst->num_lanes == 2) {
+ regmap_update_bits(priv->pcie_regmap, PCIE_SYSM_SET,
+ PCIE_LANE_MUX,
+ inst->idx ? PHY1_L0L1 : PHY0_L0L1);
+ } else {
+ regmap_update_bits(priv->pcie_regmap, PCIE_SYSM_SET,
+ PCIE_LANE_MUX, PHY0_L0_PHY1_L1);
+ }
+
+ /*
+ * The PCIE clock goes like:
+ * internal refclk -- serdes0 -- serdes1 -- LVDS0
+ * \- LVDS1
+ * Both clock repeaters must be enabled at PHY power-on,
+ * otherwise there's no PCIE reference clock output.
+ */
+
+ if (!priv->nlanes_enabled) {
+ ret = clk_prepare_enable(priv->refclk);
+ if (ret)
+ goto out;
+
+ ret = regmap_set_bits(priv->pcie_regmap, PCIE_SYSM_INIT, PCIE_PHY_RSTN);
+ if (ret)
+ goto out;
+
+ ret = regmap_set_bits(priv->pcie_regmap, PCIE_SYSM_INIT,
+ PCIE_L0_REPEAT_CLK_EN |
+ PCIE_L1_REPEAT_CLK_EN);
+ if (ret)
+ goto out;
+ }
+
+ priv->nlanes_enabled += inst->num_lanes;
+
+ ret = sf_pcie_phy_lvds_on(priv, inst->lvds_idx);
+ if (ret)
+ goto out;
+
+ ret = sf_pcie_lane_on(priv, inst->idx);
+ if (ret)
+ goto out;
+ if (inst->num_lanes == 2)
+ ret = sf_pcie_lane_on(priv, !inst->idx);
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int sf21_pcie_phy_power_off(struct phy *phy)
+{
+ struct sf21_pcie_phy_inst *inst = phy_to_instance(phy);
+ struct sf21_pcie_phy *priv = instance_to_priv(inst);
+ int ret;
+ mutex_lock(&priv->lock);
+
+ if (inst->num_lanes == 2) {
+ ret = sf_pcie_lane_off(priv, !inst->idx);
+ if (ret)
+ goto out;
+ }
+
+ ret = sf_pcie_lane_off(priv, inst->idx);
+ if (ret)
+ goto out;
+
+ ret = sf_pcie_phy_lvds_off(priv, inst->lvds_idx);
+ if (ret)
+ goto out;
+ priv->nlanes_enabled -= inst->num_lanes;
+
+ if (!priv->nlanes_enabled) {
+ ret = regmap_clear_bits(priv->pcie_regmap, PCIE_SYSM_INIT, PCIE_PHY_RSTN);
+ if (ret)
+ goto out;
+
+ ret = regmap_clear_bits(priv->pcie_regmap, PCIE_SYSM_INIT,
+ PCIE_L0_REPEAT_CLK_EN |
+ PCIE_L1_REPEAT_CLK_EN);
+ if (ret)
+ goto out;
+ clk_disable_unprepare(priv->refclk);
+ }
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static const struct phy_ops sf21_pcie_phy_ops = {
+ .power_on = sf21_pcie_phy_power_on,
+ .power_off = sf21_pcie_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int sf21_pcie_phy_probe(struct platform_device *pdev)
+{
+ struct sf21_pcie_phy *p_phy;
+ struct phy_provider *provider;
+ struct phy *phy;
+ struct device_node *child;
+ int num_insts = 0;
+ u32 reg_idx, num_lanes, lvds_idx;
+ int ret;
+
+ p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL);
+ if (!p_phy)
+ return -ENOMEM;
+
+ p_phy->dev = &pdev->dev;
+ platform_set_drvdata(pdev, p_phy);
+
+ p_phy->refclk = devm_clk_get(p_phy->dev, "ref");
+ if (IS_ERR(p_phy->refclk))
+ return dev_err_probe(p_phy->dev, PTR_ERR(p_phy->refclk),
+ "Failed to get phy reference clock.\n");
+
+ p_phy->csrclk = devm_clk_get_enabled(p_phy->dev, "csr");
+ if (IS_ERR(p_phy->csrclk))
+ return dev_err_probe(p_phy->dev, PTR_ERR(p_phy->csrclk),
+ "Failed to get enabled phy csr clock.\n");
+
+ p_phy->pcie_regmap = syscon_node_to_regmap(pdev->dev.of_node);
+ if (IS_ERR(p_phy->pcie_regmap))
+ return dev_err_probe(p_phy->dev, PTR_ERR(p_phy->pcie_regmap),
+ "Failed to get regmap.\n");
+
+ p_phy->topcrm_regmap = syscon_regmap_lookup_by_phandle(
+ pdev->dev.of_node, "siflower,topcrm");
+ if (IS_ERR(p_phy->topcrm_regmap))
+ return dev_err_probe(p_phy->dev, PTR_ERR(p_phy->topcrm_regmap),
+ "Failed to get regmap for topcrm.\n");
+
+ p_phy->nlanes_enabled = 0;
+ mutex_init(&p_phy->lock);
+
+ regmap_clear_bits(p_phy->pcie_regmap, PCIE_SYSM_INIT,
+ PCIE_L1_RSTN | PCIE_L0_RSTN | PCIE_PHY_RSTN);
+
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
+ ret = of_property_read_u32(child, "reg", ®_idx);
+ if (ret)
+ return dev_err_probe(
+ p_phy->dev, ret,
+ "failed to read reg of child node %d.\n",
+ num_insts);
+
+ if (reg_idx > 1) {
+ dev_err(p_phy->dev, "PHY reg should be 0 or 1.\n");
+ return -EINVAL;
+ }
+
+ p_phy->insts[reg_idx].idx = reg_idx;
+
+ ret = of_property_read_u32(child, "siflower,num-lanes",
+ &num_lanes);
+ if (ret)
+ return dev_err_probe(
+ p_phy->dev, ret,
+ "failed to read num-lanes of phy@%u.\n",
+ reg_idx);
+
+ if (num_lanes != 1 && num_lanes != 2) {
+ dev_err(p_phy->dev,
+ "One PHY can only request 1 or 2 serdes lanes.\n");
+ return -EINVAL;
+ }
+
+ p_phy->insts[reg_idx].num_lanes = num_lanes;
+
+ /* LVDS provides PCIE reference clock and is a separated block. */
+ ret = of_property_read_u32(child, "siflower,lvds-idx",
+ &lvds_idx);
+ if (ret)
+ p_phy->insts[reg_idx].lvds_idx = reg_idx;
+ else
+ p_phy->insts[reg_idx].lvds_idx = lvds_idx;
+
+ phy = devm_phy_create(p_phy->dev, child,
+ &sf21_pcie_phy_ops);
+ if (IS_ERR(phy))
+ return dev_err_probe(p_phy->dev, PTR_ERR(phy),
+ "failed to register phy@%d.\n",
+ reg_idx);
+
+ phy_set_drvdata(phy, &p_phy->insts[reg_idx]);
+ p_phy->insts[reg_idx].phy = phy;
+
+ num_insts++;
+ if (num_insts >= 2)
+ break;
+ }
+
+ provider = devm_of_phy_provider_register(p_phy->dev, of_phy_simple_xlate);
+ if (IS_ERR(provider))
+ return dev_err_probe(p_phy->dev, PTR_ERR(provider),
+ "Failed to register PHY provider.\n");
+
+ return 0;
+}
+
+static const struct of_device_id sf21_pcie_phy_of_match[] = {
+ { .compatible = "siflower,sf21-pcie-phy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sf21_pcie_phy_of_match);
+
+static struct platform_driver sf21_pcie_phy_driver = {
+ .probe = sf21_pcie_phy_probe,
+ .driver = {
+ .name = "sf21-pcie-phy",
+ .of_match_table = sf21_pcie_phy_of_match,
+ },
+};
+module_platform_driver(sf21_pcie_phy_driver);
+
+MODULE_AUTHOR("Chuanhong Guo <gch981213@gmail.com>");
+MODULE_DESCRIPTION("Siflower SF21A6826/SF21H8898 PCIE PHY driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#define SF21_USB_PHY_RESET_OFFSET 0xC
+#define RST_PRST BIT(0)
+#define RST_PHY_POR BIT(1)
+#define RST_PHY_PORT BIT(2)
+
+struct sf21_usb_phy {
+ struct device *dev;
+ struct clk *phy_clk;
+ struct clk *bus_clk;
+ void __iomem *base;
+};
+
+static int sf21_usb_phy_power_on(struct phy *phy)
+{
+ struct sf21_usb_phy *p_phy = phy_get_drvdata(phy);
+ int ret;
+
+ writel(RST_PRST | RST_PHY_POR | RST_PHY_PORT,
+ p_phy->base + SF21_USB_PHY_RESET_OFFSET);
+
+ ret = clk_prepare_enable(p_phy->phy_clk);
+ if (ret < 0) {
+ dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret);
+ return ret;
+ }
+
+ writel(RST_PRST, p_phy->base + SF21_USB_PHY_RESET_OFFSET);
+ usleep_range(50, 1000);
+ writel(0, p_phy->base + SF21_USB_PHY_RESET_OFFSET);
+ udelay(5);
+
+ return ret;
+}
+
+static int sf21_usb_phy_power_off(struct phy *phy)
+{
+ struct sf21_usb_phy *p_phy = phy_get_drvdata(phy);
+
+ writel(RST_PRST | RST_PHY_POR | RST_PHY_PORT,
+ p_phy->base + SF21_USB_PHY_RESET_OFFSET);
+ clk_disable_unprepare(p_phy->phy_clk);
+ return 0;
+}
+
+static const struct phy_ops sf21_usb_phy_ops = {
+ .power_on = sf21_usb_phy_power_on,
+ .power_off = sf21_usb_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int sf21_usb_phy_probe(struct platform_device *pdev)
+{
+ struct sf21_usb_phy *p_phy;
+ struct phy_provider *provider;
+ struct phy *phy;
+
+ p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL);
+ if (!p_phy)
+ return -ENOMEM;
+
+ p_phy->dev = &pdev->dev;
+ platform_set_drvdata(pdev, p_phy);
+
+ p_phy->phy_clk = devm_clk_get(p_phy->dev, "usb_phy_clk");
+ if (IS_ERR(p_phy->phy_clk))
+ return dev_err_probe(p_phy->dev, PTR_ERR(p_phy->phy_clk),
+ "Failed to get usb_phy clock.\n");
+
+ p_phy->base = devm_platform_ioremap_resource(pdev, 0);
+
+ phy = devm_phy_create(p_phy->dev, NULL, &sf21_usb_phy_ops);
+ if (IS_ERR(phy))
+ return dev_err_probe(p_phy->dev, PTR_ERR(phy),
+ "Failed to create PHY.\n");
+
+ phy_set_drvdata(phy, p_phy);
+
+ provider = devm_of_phy_provider_register(p_phy->dev, of_phy_simple_xlate);
+ if (IS_ERR(provider))
+ return dev_err_probe(p_phy->dev, PTR_ERR(provider),
+ "Failed to register PHY provider.\n");
+
+ return 0;
+}
+
+static const struct of_device_id sf21_usb_phy_of_match[] = {
+ { .compatible = "siflower,sf21-usb-phy", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sf21_usb_phy_of_match);
+
+static struct platform_driver sf21_usb_phy_driver = {
+ .probe = sf21_usb_phy_probe,
+ .driver = {
+ .name = "sf21-usb-phy",
+ .of_match_table = sf21_usb_phy_of_match,
+ },
+};
+module_platform_driver(sf21_usb_phy_driver);
+
+MODULE_AUTHOR("Ziying Wu <ziying.wu@siflower.com.cn>");
+MODULE_DESCRIPTION("Siflower SF21A6826/SF21H8898 USB2.0 PHY driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <dt-bindings/reset/siflower,sf21-reset.h>
+
+#define SF21_SOFT_RESET 0xC0
+
+struct sf21_reset_data {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+};
+
+static inline int sf21_reset_shift(unsigned long id)
+{
+ switch (id) {
+ case SF21_RESET_GIC:
+ case SF21_RESET_AXI:
+ case SF21_RESET_AHB:
+ case SF21_RESET_APB:
+ case SF21_RESET_IRAM:
+ return id + 1;
+ case SF21_RESET_NPU:
+ case SF21_RESET_DDR_CTL:
+ case SF21_RESET_DDR_PHY:
+ case SF21_RESET_DDR_PWR_OK_IN:
+ case SF21_RESET_DDR_CTL_APB:
+ case SF21_RESET_DDR_PHY_APB:
+ return id + 2;
+ case SF21_RESET_USB:
+ return id + 8;
+ case SF21_RESET_PVT:
+ case SF21_RESET_SERDES_CSR:
+ return id + 11;
+ case SF21_RESET_CRYPT_CSR:
+ case SF21_RESET_CRYPT_APP:
+ case SF21_RESET_NPU2DDR_ASYNCBRIDGE:
+ case SF21_RESET_IROM:
+ return id + 14;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sf21_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct sf21_reset_data *rd;
+ int shift;
+ u32 mask;
+
+ rd = container_of(rcdev, struct sf21_reset_data, rcdev);
+
+ shift = sf21_reset_shift(id);
+ mask = BIT(shift);
+ return regmap_update_bits(rd->regmap, SF21_SOFT_RESET, mask, 0);
+}
+
+static int sf21_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct sf21_reset_data *rd;
+ int shift;
+ u32 mask;
+
+ rd = container_of(rcdev, struct sf21_reset_data, rcdev);
+
+ shift = sf21_reset_shift(id);
+ mask = BIT(shift);
+ return regmap_update_bits(rd->regmap, SF21_SOFT_RESET, mask, mask);
+}
+
+static int sf21_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct sf21_reset_data *rd;
+ int shift, ret;
+ u32 mask;
+ u32 reg;
+
+ rd = container_of(rcdev, struct sf21_reset_data, rcdev);
+ ret = regmap_read(rd->regmap, SF21_SOFT_RESET, ®);
+ if (ret)
+ return ret;
+
+ shift = sf21_reset_shift(id);
+ mask = BIT(shift);
+ return !!(reg & mask);
+}
+
+static const struct reset_control_ops sf21_reset_ops = {
+ .assert = sf21_reset_assert,
+ .deassert = sf21_reset_deassert,
+ .status = sf21_reset_status,
+};
+
+static int sf21_reset_probe(struct platform_device *pdev)
+{
+ struct sf21_reset_data *rd;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *node;
+
+ rd = devm_kzalloc(dev, sizeof(*rd), GFP_KERNEL);
+ if (!rd)
+ return -ENOMEM;
+
+ node = of_parse_phandle(np, "siflower,crm", 0);
+ rd->regmap = syscon_node_to_regmap(node);
+
+ if (IS_ERR(rd->regmap))
+ return PTR_ERR(rd->regmap);
+
+ rd->rcdev.owner = THIS_MODULE;
+ rd->rcdev.nr_resets = SF21_RESET_MAX + 1;
+ rd->rcdev.ops = &sf21_reset_ops;
+ rd->rcdev.of_node = np;
+
+ return devm_reset_controller_register(dev, &rd->rcdev);
+}
+
+static const struct of_device_id sf21_reset_dt_ids[] = {
+ { .compatible = "siflower,sf21-reset" },
+ {},
+};
+
+static struct platform_driver sf21_reset_driver = {
+ .probe = sf21_reset_probe,
+ .driver = {
+ .name = "sf21-reset",
+ .of_match_table = sf21_reset_dt_ids,
+ },
+};
+builtin_platform_driver(sf21_reset_driver);
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * A driver for Siflower SF21A6826/SF21H8898 QSPI controller.
+ *
+ * Based on the AMBA PL022 driver:
+ * Copyright (C) 2008-2012 ST-Ericsson AB
+ * Copyright (C) 2006 STMicroelectronics Pvt. Ltd.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+
+#include <linux/spi/spi-mem.h>
+#include <linux/spi/spi.h>
+
+#define SF_SSP_FIFO_DEPTH 0x100
+
+#define SSP_CR0 0x000
+#define SSP_CR1 0x004
+#define SSP_DR 0x008
+#define SSP_SR 0x00C
+#define SSP_CPSR 0x010
+#define SSP_IMSC 0x014
+#define SSP_RIS 0x018
+#define SSP_MIS 0x01C
+#define SSP_ICR 0x020
+#define SSP_DMACR 0x024
+#define SSP_FIFO_LEVEL 0x028
+#define SSP_EXSPI_CMD0 0x02C
+#define SSP_EXSPI_CMD1 0x030
+#define SSP_EXSPI_CMD2 0x034
+
+/* SSP Control Register 0 - SSP_CR0 */
+#define SSP_CR0_EXSPI_FRAME (0x3 << 4)
+#define SSP_CR0_SPO (0x1 << 6)
+#define SSP_CR0_SPH (0x1 << 7)
+#define SSP_CR0_BIT_MODE(x) ((x)-1)
+#define SSP_SCR_SHFT 8
+
+/* SSP Control Register 1 - SSP_CR1 */
+#define SSP_CR1_MASK_SSE (0x1 << 1)
+
+/* SSP Status Register - SSP_SR */
+#define SSP_SR_MASK_TFE (0x1 << 0) /* Transmit FIFO empty */
+#define SSP_SR_MASK_TNF (0x1 << 1) /* Transmit FIFO not full */
+#define SSP_SR_MASK_RNE (0x1 << 2) /* Receive FIFO not empty */
+#define SSP_SR_MASK_RFF (0x1 << 3) /* Receive FIFO full */
+#define SSP_SR_MASK_BSY (0x1 << 4) /* Busy Flag */
+
+/* SSP FIFO Threshold Register - SSP_FIFO_LEVEL */
+#define SSP_FIFO_LEVEL_RX GENMASK(14, 8) /* Receive FIFO watermark */
+#define SSP_FIFO_LEVEL_TX GENMASK(6, 0) /* Transmit FIFO watermark */
+#define DFLT_THRESH_RX 32
+#define DFLT_THRESH_TX 32
+
+/* SSP Raw Interrupt Status Register - SSP_RIS */
+#define SSP_RIS_MASK_RORRIS (0x1 << 0) /* Receive Overrun */
+#define SSP_RIS_MASK_RTRIS (0x1 << 1) /* Receive Timeout */
+#define SSP_RIS_MASK_RXRIS (0x1 << 2) /* Receive FIFO Raw Interrupt status */
+#define SSP_RIS_MASK_TXRIS (0x1 << 3) /* Transmit FIFO Raw Interrupt status */
+
+/* EXSPI command register 0 SSP_EXSPI_CMD0 */
+#define EXSPI_CMD0_CMD_COUNT BIT(0) /* cmd byte, must be set at last */
+#define EXSPI_CMD0_ADDR_COUNT GENMASK(2, 1) /* addr bytes */
+#define EXSPI_CMD0_EHC_COUNT BIT(3) /* Set 1 for 4-byte address mode */
+#define EXSPI_CMD0_TX_COUNT GENMASK(14, 4) /* TX data bytes */
+#define EXSPI_CMD0_VALID BIT(15) /* Set 1 to make the cmd to be run */
+
+/* EXSPI command register 1 SSP_EXSPI_CMD1 */
+#define EXSPI_CMD1_DUMMY_COUNT GENMASK(3, 0) /* dummy bytes */
+#define EXSPI_CMD1_RX_COUNT GENMASK(14, 4) /* RX data bytes */
+
+/* EXSPI command register 2 SSP_EXSPI_CMD2 */
+/* Set 1 for 1-wire, 2 for 2-wire, 3 for 4-wire */
+#define EXSPI_CMD2_CMD_IO_MODE GENMASK(1, 0) /* cmd IO mode */
+#define EXSPI_CMD2_ADDR_IO_MODE GENMASK(3, 2) /* addr IO mode */
+#define EXSPI_CMD2_DATA_IO_MODE GENMASK(5, 4) /* data IO mode */
+
+/* SSP Clock Defaults */
+#define SSP_DEFAULT_CLKRATE 0x2
+#define SSP_DEFAULT_PRESCALE 0x40
+
+/* SSP Clock Parameter ranges */
+#define CPSDVR_MIN 0x02
+#define CPSDVR_MAX 0xFE
+#define SCR_MIN 0x00
+#define SCR_MAX 0xFF
+
+#define SF_READ_TIMEOUT (10 * HZ)
+#define MAX_S_BUF 100
+
+struct sf_qspi {
+ void __iomem *base;
+ struct clk *clk, *apbclk;
+ struct device *dev;
+};
+
+struct ssp_clock_params {
+ u32 freq;
+ u8 cpsdvsr; /* value from 2 to 254 (even only!) */
+ u8 scr; /* value from 0 to 255 */
+};
+
+struct chip_data {
+ u32 freq;
+ u32 cr0;
+ u16 cpsr;
+};
+
+static void sf_qspi_flush_rxfifo(struct sf_qspi *s)
+{
+ while (readw(s->base + SSP_SR) & SSP_SR_MASK_RNE)
+ readw(s->base + SSP_DR);
+}
+
+static int sf_qspi_wait_not_busy(struct sf_qspi *s)
+{
+ unsigned long timeout = jiffies + SF_READ_TIMEOUT;
+
+ do {
+ if (!(readw(s->base + SSP_SR) & SSP_SR_MASK_BSY))
+ return 0;
+
+ cond_resched();
+ } while (time_after(timeout, jiffies));
+
+ dev_err(s->dev, "I/O timed out\n");
+ return -ETIMEDOUT;
+}
+
+static int sf_qspi_wait_rx_not_empty(struct sf_qspi *s)
+{
+ unsigned long timeout = jiffies + SF_READ_TIMEOUT;
+
+ do {
+ if (readw(s->base + SSP_SR) & SSP_SR_MASK_RNE)
+ return 0;
+
+ cond_resched();
+ } while (time_after(timeout, jiffies));
+
+ dev_err(s->dev, "read timed out\n");
+ return -ETIMEDOUT;
+}
+
+static int sf_qspi_wait_rxfifo(struct sf_qspi *s)
+{
+ unsigned long timeout = jiffies + SF_READ_TIMEOUT;
+
+ do {
+ if (readw(s->base + SSP_RIS) & SSP_RIS_MASK_RXRIS)
+ return 0;
+
+ cond_resched();
+ } while (time_after(timeout, jiffies));
+
+ dev_err(s->dev, "read timed out\n");
+ return -ETIMEDOUT;
+}
+
+static void sf_qspi_enable(struct sf_qspi *s)
+{
+ /* Enable the SPI hardware */
+ writew(SSP_CR1_MASK_SSE, s->base + SSP_CR1);
+}
+
+static void sf_qspi_disable(struct sf_qspi *s)
+{
+ /* Disable the SPI hardware */
+ writew(0, s->base + SSP_CR1);
+}
+
+static void sf_qspi_xmit(struct sf_qspi *s, unsigned int nbytes, const u8 *out)
+{
+ while (nbytes--)
+ writew(*out++, s->base + SSP_DR);
+}
+
+static int sf_qspi_rcv(struct sf_qspi *s, unsigned int nbytes, u8 *in)
+{
+ int ret, i;
+
+ while (nbytes >= DFLT_THRESH_RX) {
+ /* wait for RX FIFO to reach the threshold */
+ ret = sf_qspi_wait_rxfifo(s);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < DFLT_THRESH_RX; i++)
+ *in++ = readw(s->base + SSP_DR);
+
+ nbytes -= DFLT_THRESH_RX;
+ }
+
+ /* read the remaining data */
+ while (nbytes) {
+ ret = sf_qspi_wait_rx_not_empty(s);
+ if (ret)
+ return ret;
+
+ *in++ = readw(s->base + SSP_DR);
+ nbytes--;
+ }
+
+ return 0;
+}
+
+static void sf_qspi_set_param(struct sf_qspi *s, const struct spi_mem_op *op)
+{
+ unsigned int tx_count = 0, rx_count = 0;
+ u8 cmd_io, addr_io, data_io;
+ u8 cmd_count, addr_count, ehc_count;
+
+ cmd_io = op->cmd.buswidth == 4 ? 3 : op->cmd.buswidth;
+ addr_io = op->addr.buswidth == 4 ? 3 : op->addr.buswidth;
+ data_io = op->data.buswidth == 4 ? 3 : op->data.buswidth;
+
+ if (op->data.nbytes) {
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ rx_count = op->data.nbytes;
+ else
+ tx_count = op->data.nbytes;
+ }
+ if (op->addr.nbytes > 3) {
+ addr_count = 3;
+ ehc_count = 1;
+ } else {
+ addr_count = op->addr.nbytes;
+ ehc_count = 0;
+ }
+ cmd_count = op->cmd.nbytes;
+
+ writew(FIELD_PREP(EXSPI_CMD2_CMD_IO_MODE, cmd_io) |
+ FIELD_PREP(EXSPI_CMD2_ADDR_IO_MODE, addr_io) |
+ FIELD_PREP(EXSPI_CMD2_DATA_IO_MODE, data_io),
+ s->base + SSP_EXSPI_CMD2);
+ writew(FIELD_PREP(EXSPI_CMD1_DUMMY_COUNT, op->dummy.nbytes) |
+ FIELD_PREP(EXSPI_CMD1_RX_COUNT, rx_count),
+ s->base + SSP_EXSPI_CMD1);
+ writew(EXSPI_CMD0_VALID |
+ FIELD_PREP(EXSPI_CMD0_CMD_COUNT, op->cmd.nbytes) |
+ FIELD_PREP(EXSPI_CMD0_ADDR_COUNT, addr_count) |
+ FIELD_PREP(EXSPI_CMD0_EHC_COUNT, ehc_count) |
+ FIELD_PREP(EXSPI_CMD0_TX_COUNT, tx_count),
+ s->base + SSP_EXSPI_CMD0);
+}
+
+static int sf_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ struct sf_qspi *s = spi_controller_get_devdata(mem->spi->master);
+ struct chip_data *chip = spi_get_ctldata(mem->spi);
+ unsigned int pops = 0;
+ int ret, i, op_len;
+ const u8 *tx_buf = NULL;
+ u8 *rx_buf = NULL, op_buf[MAX_S_BUF];
+
+ writew(chip->cr0, s->base + SSP_CR0);
+ writew(chip->cpsr, s->base + SSP_CPSR);
+
+ if (op->data.nbytes) {
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ rx_buf = op->data.buf.in;
+ else
+ tx_buf = op->data.buf.out;
+ }
+ op_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
+ sf_qspi_set_param(s, op);
+
+ op_buf[pops++] = op->cmd.opcode;
+ if (op->addr.nbytes) {
+ for (i = 0; i < op->addr.nbytes; i++)
+ op_buf[pops + i] = op->addr.val >>
+ (8 * (op->addr.nbytes - i - 1));
+ pops += op->addr.nbytes;
+ }
+
+ sf_qspi_flush_rxfifo(s);
+ memset(op_buf + pops, 0xff, op->dummy.nbytes);
+ sf_qspi_xmit(s, op_len, op_buf);
+ if (tx_buf) {
+ sf_qspi_xmit(s, op->data.nbytes, tx_buf);
+ }
+ sf_qspi_enable(s);
+ if (rx_buf)
+ ret = sf_qspi_rcv(s, op->data.nbytes, rx_buf);
+ else
+ ret = sf_qspi_wait_not_busy(s);
+
+ sf_qspi_disable(s);
+
+ return ret;
+}
+
+static int sf_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ u32 nbytes;
+
+ nbytes = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
+ if (nbytes >= SF_SSP_FIFO_DEPTH)
+ return -ENOTSUPP;
+
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ op->data.nbytes =
+ min_t(unsigned int, op->data.nbytes, SF_SSP_FIFO_DEPTH);
+ else
+ op->data.nbytes = min_t(unsigned int, op->data.nbytes,
+ SF_SSP_FIFO_DEPTH - nbytes);
+
+ return 0;
+}
+
+static bool sf_qspi_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ if (!spi_mem_default_supports_op(mem, op))
+ return false;
+
+ /* dummy buswidth must be the same as addr */
+ if (op->addr.nbytes && op->dummy.nbytes &&
+ op->addr.buswidth != op->dummy.buswidth)
+ return false;
+
+ return true;
+}
+
+static inline u32 spi_rate(u32 rate, u16 cpsdvsr, u16 scr)
+{
+ return rate / (cpsdvsr * (1 + scr));
+}
+
+static int calculate_effective_freq(struct sf_qspi *s, int freq,
+ struct ssp_clock_params *clk_freq)
+{
+ /* Lets calculate the frequency parameters */
+ u16 cpsdvsr = CPSDVR_MIN;
+ u32 rate, rate_scaled, max_tclk, min_tclk, scr;
+ u32 best_freq = 0, best_cpsdvsr = 0, best_scr = 0, tmp, found = 0;
+
+ rate = clk_get_rate(s->clk);
+ /* cpsdvscr = 2 & scr 0 */
+ max_tclk = spi_rate(rate, CPSDVR_MIN, SCR_MIN);
+ if (freq > max_tclk) {
+ dev_warn(
+ s->dev,
+ "Requested SPI frequency %d Hz is more than maximum: %d Hz\n",
+ freq, max_tclk);
+ clk_freq->freq = max_tclk;
+ clk_freq->cpsdvsr = CPSDVR_MIN;
+ clk_freq->scr = SCR_MIN;
+ return 0;
+ }
+
+ /* cpsdvsr = 254 & scr = 255 */
+ min_tclk = spi_rate(rate, CPSDVR_MAX, SCR_MAX);
+ if (freq < min_tclk) {
+ dev_err(s->dev,
+ "Requested SPI frequency %d Hz is less than minimum: %d Hz\n",
+ freq, min_tclk);
+ return -EINVAL;
+ }
+
+ /*
+ * best_freq will give closest possible available rate (<= requested
+ * freq) for all values of scr & cpsdvsr.
+ */
+ while ((cpsdvsr <= CPSDVR_MAX) && !found) {
+ rate_scaled = rate / cpsdvsr;
+
+ if (rate_scaled < freq)
+ break;
+
+ scr = DIV_ROUND_UP(rate_scaled, freq) - 1;
+ if (scr > SCR_MAX)
+ continue;
+
+ tmp = spi_rate(rate, cpsdvsr, scr);
+
+ /*
+ * If found exact value, mark found and break.
+ * If found more closer value, update and break.
+ */
+ if (tmp > best_freq) {
+ best_freq = tmp;
+ best_cpsdvsr = cpsdvsr;
+ best_scr = scr;
+
+ if (tmp == freq)
+ found = 1;
+ }
+
+ cpsdvsr += 2;
+ }
+
+ clk_freq->freq = best_freq;
+ clk_freq->cpsdvsr = (u8) (best_cpsdvsr & 0xFF);
+ clk_freq->scr = (u8) (best_scr & 0xFF);
+ dev_dbg(s->dev,
+ "SSP Target Frequency is: %u, Effective Frequency is %u\n",
+ freq, best_freq);
+ dev_dbg(s->dev, "SSP cpsdvsr = %d, scr = %d\n",
+ clk_freq->cpsdvsr, clk_freq->scr);
+
+ return 0;
+}
+
+static int sf_qspi_setup(struct spi_device *spi)
+{
+ struct sf_qspi *s = spi_controller_get_devdata(spi->controller);
+ struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0 };
+ struct chip_data *chip;
+ int ret = 0;
+ u16 cr0 = 0;
+
+ if (!spi->max_speed_hz)
+ return -EINVAL;
+
+ ret = calculate_effective_freq(s, spi->max_speed_hz, &clk_freq);
+ if (ret < 0)
+ return ret;
+
+ chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ cr0 = SSP_CR0_BIT_MODE(8);
+ cr0 |= clk_freq.scr << 8;
+ /*set module*/
+ cr0 &= ~(SSP_CR0_SPH | SSP_CR0_SPO);
+ if (spi->mode & SPI_CPHA)
+ cr0 |= SSP_CR0_SPH;
+ if (spi->mode & SPI_CPOL)
+ cr0 |= SSP_CR0_SPO;
+ cr0 |= SSP_CR0_EXSPI_FRAME;
+
+ chip->freq = clk_freq.freq;
+ chip->cr0 = cr0;
+ chip->cpsr = clk_freq.cpsdvsr;
+
+ spi_set_ctldata(spi, chip);
+ return 0;
+}
+
+static void sf_qspi_cleanup(struct spi_device *spi)
+{
+ struct chip_data *chip = spi_get_ctldata(spi);
+
+ spi_set_ctldata(spi, NULL);
+ kfree(chip);
+}
+
+static const struct spi_controller_mem_ops sf_qspi_mem_ops = {
+ .supports_op = sf_qspi_supports_op,
+ .adjust_op_size = sf_qspi_adjust_op_size,
+ .exec_op = sf_qspi_exec_op,
+};
+
+static int sf_qspi_probe(struct platform_device *pdev)
+{
+ struct spi_controller *master;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct sf_qspi *s;
+ int ret;
+
+ master = devm_spi_alloc_master(&pdev->dev, sizeof(*s));
+ if (!master)
+ return -ENOMEM;
+ master->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL |
+ SPI_TX_QUAD;
+ s = spi_controller_get_devdata(master);
+ s->dev = dev;
+ platform_set_drvdata(pdev, s);
+
+ s->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(s->base))
+ return dev_err_probe(dev, PTR_ERR(s->base),
+ "failed to remap memory resources.\n");
+
+ s->clk = devm_clk_get_enabled(dev, "sspclk");
+ if (IS_ERR(s->clk))
+ return dev_err_probe(dev, PTR_ERR(s->clk),
+ "failed to get and enable sspclk.\n");
+
+ s->apbclk = devm_clk_get_enabled(dev, "apb_pclk");
+ if (IS_ERR(s->apbclk))
+ return dev_err_probe(dev, PTR_ERR(s->apbclk),
+ "failed to get and enable apb_pclk.\n");
+
+ master->cleanup = sf_qspi_cleanup;
+ master->setup = sf_qspi_setup;
+ master->use_gpio_descriptors = true;
+ master->mem_ops = &sf_qspi_mem_ops;
+ master->dev.of_node = np;
+
+ writew(FIELD_PREP(SSP_FIFO_LEVEL_RX, DFLT_THRESH_RX) |
+ FIELD_PREP(SSP_FIFO_LEVEL_TX, DFLT_THRESH_TX),
+ s->base + SSP_FIFO_LEVEL);
+
+ ret = devm_spi_register_controller(dev, master);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to register controller.\n");
+
+ return 0;
+}
+
+static const struct of_device_id sf_qspi_ids[] = {
+ {.compatible = "siflower,sf21-qspi"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, sf_qspi_ids);
+
+static struct platform_driver sf_qspi_driver = {
+ .driver = {
+ .name = "sf21_qspi",
+ .of_match_table = sf_qspi_ids,
+ },
+ .probe = sf_qspi_probe,
+};
+module_platform_driver(sf_qspi_driver);
--- /dev/null
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+
+#define CLK_CMNPLL_VCO 0
+#define CLK_CMNPLL_POSTDIV 1
+
+#define CLK_DDRPLL_POSTDIV 2
+
+#define CLK_PCIEPLL_VCO 3
+#define CLK_PCIEPLL_FOUT0 4
+#define CLK_PCIEPLL_FOUT1 5
+#define CLK_PCIEPLL_FOUT2 6
+#define CLK_ETH_REF_P CLK_PCIEPLL_FOUT2
+#define CLK_PCIEPLL_FOUT3 7
+
+#define CLK_CPU 8
+#define CLK_PIC 9
+#define CLK_AXI 10
+#define CLK_AHB 11
+#define CLK_APB 12
+#define CLK_UART 13
+#define CLK_IRAM 14
+#define CLK_NPU 15
+#define CLK_DDRPHY_REF 16
+#define CLK_DDR_BYPASS 17
+#define CLK_ETHTSU 18
+#define CLK_GMAC_BYP_REF 19
+
+#define CLK_USB 20
+#define CLK_USBPHY 21
+#define CLK_SERDES_CSR 22
+#define CLK_CRYPT_CSR 23
+#define CLK_CRYPT_APP 24
+#define CLK_IROM 25
+
+#define CLK_BOOT 26
+
+#define CLK_PVT 27
+#define CLK_PLL_TEST 28
+
+#define CLK_PCIE_REFN 29
+#define CLK_PCIE_REFP 30
+
+#define CLK_MAX 31
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __DT_BINDINGS_SF21_IOMUX_H__
+#define __DT_BINDINGS_SF21_IOMUX_H__
+
+#define SW_DS 0xf /* Drive strength */
+#define SW_ST (1 << 4) /* Schmitt enable */
+#define SW_PD (1 << 5) /* Pull-down enable */
+#define SW_PU (1 << 6) /* Pull-up enable */
+#define SW_OEN (1 << 7) /* Output disable [sic] */
+#define SW_IE (1 << 8) /* Input enable */
+#define MODE_BIT0 (1 << 9) /* Function mode LSB */
+#define MODE_BIT1 (1 << 10) /* Function mode MSB */
+#define FMUX_SEL (1 << 11) /* GPIO mode enable */
+#define FUNC_SW_SEL (1 << 12) /* Function mode enable */
+
+#define FUNC_MODE_MASK 0x1f80
+#define FUNC_MODE0 (FUNC_SW_SEL | SW_IE)
+#define FUNC_MODE1 (FUNC_MODE0 | MODE_BIT0)
+#define FUNC_MODE2 (FUNC_MODE0 | MODE_BIT1)
+#define FUNC_MODE3 (FUNC_MODE0 | MODE_BIT0 | MODE_BIT1)
+#define GPIO_MODE (FUNC_MODE0 | FMUX_SEL)
+
+#define EXT_CLK_IN 0x00
+#define CLK_OUT 0x04
+#define SPI0_TXD 0x08
+#define SPI0_RXD 0x0c
+#define SPI0_CLK 0x10
+#define SPI0_CSN 0x14
+#define SPI0_HOLD 0x18
+#define SPI0_WP 0x1c
+#define JTAG_TDO 0x20
+#define JTAG_TDI 0x24
+#define JTAG_TMS 0x28
+#define JTAG_TCK 0x2c
+#define JTAG_RST 0x30
+#define UART1_TX 0x34
+#define UART1_RX 0x38
+#define I2C0_DAT 0x3c
+#define I2C0_CLK 0x40
+#define I2C1_DAT 0x44
+#define I2C1_CLK 0x48
+#define PWM0 0x4c
+#define PWM1 0x50
+#define RGMII_GTX_CLK 0x54
+#define RGMII_TXCLK 0x58
+#define RGMII_TXD0 0x5c
+#define RGMII_TXD1 0x60
+#define RGMII_TXD2 0x64
+#define RGMII_TXD3 0x68
+#define RGMII_TXCTL 0x6c
+#define RGMII_RXCLK 0x70
+#define RGMII_RXD0 0x74
+#define RGMII_RXD1 0x78
+#define RGMII_RXD2 0x7c
+#define RGMII_RXD3 0x80
+#define RGMII_RXCTL 0x84
+#define QSGMII_MDIO 0x88
+#define QSGMII_MDC 0x8c
+#define SXGMII_MDIO 0x90
+#define SXGMII_MDC 0x94
+#define DGS_INT 0x98
+#define PHY_RSTN 0x9c
+#define PHY_INT 0xa0
+#endif
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _SF21_RESETS_H
+#define _SF21_RESETS_H
+
+#define SF21_RESET_GIC 0
+#define SF21_RESET_AXI 1
+#define SF21_RESET_AHB 2
+#define SF21_RESET_APB 3
+#define SF21_RESET_IRAM 4
+
+#define SF21_RESET_NPU 5
+#define SF21_RESET_DDR_CTL 6
+#define SF21_RESET_DDR_PHY 7
+#define SF21_RESET_DDR_PWR_OK_IN 8
+#define SF21_RESET_DDR_CTL_APB 9
+#define SF21_RESET_DDR_PHY_APB 10
+
+#define SF21_RESET_USB 11
+
+#define SF21_RESET_PVT 12
+#define SF21_RESET_SERDES_CSR 13
+
+#define SF21_RESET_CRYPT_CSR 14
+#define SF21_RESET_CRYPT_APP 15
+#define SF21_RESET_NPU2DDR_ASYNCBRIDGE 16
+#define SF21_RESET_IROM 17
+#define SF21_RESET_MAX 17
+#endif
--- /dev/null
+
+define Device/Default
+ PROFILES = Default $$(DEVICE_NAME)
+ BLOCKSIZE := 64k
+ KERNEL = kernel-bin | lzma
+ KERNEL_INITRAMFS = kernel-bin | lzma | \
+ fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb with-initrd | pad-to 128k
+ KERNEL_LOADADDR := 0x20000000
+ FILESYSTEMS := squashfs
+ DEVICE_DTS_DIR := ../dts
+ IMAGES := sysupgrade.bin
+ IMAGE/sysupgrade.bin = append-kernel | fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb external-static-with-rootfs | pad-rootfs | append-metadata
+endef
+
+define Device/NAND
+ KERNEL := kernel-bin | gzip
+ KERNEL_INITRAMFS = kernel-bin | lzma | \
+ fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb with-initrd | pad-to 128k
+ IMAGE/sysupgrade.bin = append-kernel | fit gzip $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb external-static-with-rootfs | append-metadata
+endef
endef
$(eval $(call KernelPackage,phy-sf19a2890-usb))
+
+define KernelPackage/phy-sf21-usb
+ TITLE:=Siflower SF21 USB 2.0 PHY Driver
+ KCONFIG:=CONFIG_PHY_SF21_USB
+ DEPENDS:=@TARGET_siflower_sf21
+ SUBMENU:=$(USB_MENU)
+ FILES:=$(LINUX_DIR)/drivers/phy/siflower/phy-sf21-usb.ko
+ AUTOLOAD:=$(call AutoLoad,45,phy-sf21-usb,1)
+endef
+
+define KernelPackage/phy-sf21-usb/description
+ Support for Siflower SF21 USB 2.0 PHY connected to the USB
+ controller.
+endef
+
+$(eval $(call KernelPackage,phy-sf21-usb))
+++ /dev/null
-From c2ec4604afb39904c01dfe38ca8289c446b898bb Mon Sep 17 00:00:00 2001
-From: Chuanhong Guo <gch981213@gmail.com>
-Date: Tue, 20 Aug 2024 08:32:17 +0800
-Subject: [PATCH 1/9] mips: add support for Siflower SF19A2890
-
-Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
----
- arch/mips/Kconfig | 29 +++++++++++++++++++++++++++++
- arch/mips/generic/Platform | 1 +
- 2 files changed, 30 insertions(+)
-
---- a/arch/mips/Kconfig
-+++ b/arch/mips/Kconfig
-@@ -861,6 +861,35 @@ config SIBYTE_BIGSUR
- select ZONE_DMA32 if 64BIT
- select SWIOTLB if ARCH_DMA_ADDR_T_64BIT && PCI
-
-+config MACH_SIFLOWER_MIPS
-+ bool "Siflower MIPS SoCs"
-+ select MIPS_GENERIC
-+ select ARM_AMBA
-+ select BOOT_RAW
-+ select CEVT_R4K
-+ select CLKSRC_MIPS_GIC
-+ select COMMON_CLK
-+ select CPU_MIPSR2_IRQ_EI
-+ select CPU_MIPSR2_IRQ_VI
-+ select CSRC_R4K
-+ select DMA_NONCOHERENT
-+ select IRQ_MIPS_CPU
-+ select MIPS_CPU_SCACHE
-+ select MIPS_GIC
-+ select MIPS_L1_CACHE_SHIFT_5
-+ select NO_EXCEPT_FILL
-+ select SMP_UP if SMP
-+ select SYS_HAS_CPU_MIPS32_R2
-+ select SYS_SUPPORTS_32BIT_KERNEL
-+ select SYS_SUPPORTS_LITTLE_ENDIAN
-+ select SYS_SUPPORTS_MIPS16
-+ select SYS_SUPPORTS_MIPS_CPS
-+ select SYS_SUPPORTS_MULTITHREADING
-+ select USE_OF
-+ help
-+ Select this to build a kernel which supports SoCs from Siflower
-+ with MIPS InterAptiv cores, like Siflower SF19A2890.
-+
- config SNI_RM
- bool "SNI RM200/300/400"
- select ARC_MEMORY
---- a/arch/mips/generic/Platform
-+++ b/arch/mips/generic/Platform
-@@ -10,6 +10,7 @@
-
- # Note: order matters, keep the asm/mach-generic include last.
- cflags-$(CONFIG_MACH_INGENIC_SOC) += -I$(srctree)/arch/mips/include/asm/mach-ingenic
-+cflags-$(CONFIG_MACH_SIFLOWER_MIPS) += -I$(srctree)/arch/mips/include/asm/mach-siflower
- cflags-$(CONFIG_MIPS_GENERIC) += -I$(srctree)/arch/mips/include/asm/mach-generic
-
- load-$(CONFIG_MIPS_GENERIC) += 0xffffffff80100000
--- /dev/null
+From: Oleksij Rempel <o.rempel@pengutronix.de>
+Date: Tue, 12 Dec 2023 06:41:43 +0100
+Subject: [PATCH 01/20] net: phy: c45: add genphy_c45_pma_read_ext_abilities()
+ function
+
+Move part of the genphy_c45_pma_read_abilities() code to a separate
+function.
+
+Some PHYs do not implement PMA/PMD status 2 register (Register 1.8) but
+do implement PMA/PMD extended ability register (Register 1.11). To make
+use of it, we need to be able to access this part of code separately.
+
+Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Link: https://lore.kernel.org/r/20231212054144.87527-2-o.rempel@pengutronix.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+(cherry picked from commit 0c476157085fe2ad13b9bec70ea672e86647fa1a)
+---
+ drivers/net/phy/phy-c45.c | 129 ++++++++++++++++++++++----------------
+ include/linux/phy.h | 1 +
+ 2 files changed, 75 insertions(+), 55 deletions(-)
+
+--- a/drivers/net/phy/phy-c45.c
++++ b/drivers/net/phy/phy-c45.c
+@@ -920,6 +920,79 @@ int genphy_c45_pma_baset1_read_abilities
+ EXPORT_SYMBOL_GPL(genphy_c45_pma_baset1_read_abilities);
+
+ /**
++ * genphy_c45_pma_read_ext_abilities - read supported link modes from PMA
++ * @phydev: target phy_device struct
++ *
++ * Read the supported link modes from the PMA/PMD extended ability register
++ * (Register 1.11).
++ */
++int genphy_c45_pma_read_ext_abilities(struct phy_device *phydev)
++{
++ int val;
++
++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE);
++ if (val < 0)
++ return val;
++
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
++ phydev->supported,
++ val & MDIO_PMA_EXTABLE_10GBLRM);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
++ phydev->supported,
++ val & MDIO_PMA_EXTABLE_10GBT);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
++ phydev->supported,
++ val & MDIO_PMA_EXTABLE_10GBKX4);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
++ phydev->supported,
++ val & MDIO_PMA_EXTABLE_10GBKR);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
++ phydev->supported,
++ val & MDIO_PMA_EXTABLE_1000BT);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
++ phydev->supported,
++ val & MDIO_PMA_EXTABLE_1000BKX);
++
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
++ phydev->supported,
++ val & MDIO_PMA_EXTABLE_100BTX);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
++ phydev->supported,
++ val & MDIO_PMA_EXTABLE_100BTX);
++
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
++ phydev->supported,
++ val & MDIO_PMA_EXTABLE_10BT);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
++ phydev->supported,
++ val & MDIO_PMA_EXTABLE_10BT);
++
++ if (val & MDIO_PMA_EXTABLE_NBT) {
++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
++ MDIO_PMA_NG_EXTABLE);
++ if (val < 0)
++ return val;
++
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
++ phydev->supported,
++ val & MDIO_PMA_NG_EXTABLE_2_5GBT);
++
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
++ phydev->supported,
++ val & MDIO_PMA_NG_EXTABLE_5GBT);
++ }
++
++ if (val & MDIO_PMA_EXTABLE_BT1) {
++ val = genphy_c45_pma_baset1_read_abilities(phydev);
++ if (val < 0)
++ return val;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(genphy_c45_pma_read_ext_abilities);
++
++/**
+ * genphy_c45_pma_read_abilities - read supported link modes from PMA
+ * @phydev: target phy_device struct
+ *
+@@ -962,63 +1035,9 @@ int genphy_c45_pma_read_abilities(struct
+ val & MDIO_PMA_STAT2_10GBER);
+
+ if (val & MDIO_PMA_STAT2_EXTABLE) {
+- val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE);
++ val = genphy_c45_pma_read_ext_abilities(phydev);
+ if (val < 0)
+ return val;
+-
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
+- phydev->supported,
+- val & MDIO_PMA_EXTABLE_10GBLRM);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+- phydev->supported,
+- val & MDIO_PMA_EXTABLE_10GBT);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+- phydev->supported,
+- val & MDIO_PMA_EXTABLE_10GBKX4);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+- phydev->supported,
+- val & MDIO_PMA_EXTABLE_10GBKR);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+- phydev->supported,
+- val & MDIO_PMA_EXTABLE_1000BT);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+- phydev->supported,
+- val & MDIO_PMA_EXTABLE_1000BKX);
+-
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+- phydev->supported,
+- val & MDIO_PMA_EXTABLE_100BTX);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+- phydev->supported,
+- val & MDIO_PMA_EXTABLE_100BTX);
+-
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+- phydev->supported,
+- val & MDIO_PMA_EXTABLE_10BT);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+- phydev->supported,
+- val & MDIO_PMA_EXTABLE_10BT);
+-
+- if (val & MDIO_PMA_EXTABLE_NBT) {
+- val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+- MDIO_PMA_NG_EXTABLE);
+- if (val < 0)
+- return val;
+-
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+- phydev->supported,
+- val & MDIO_PMA_NG_EXTABLE_2_5GBT);
+-
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+- phydev->supported,
+- val & MDIO_PMA_NG_EXTABLE_5GBT);
+- }
+-
+- if (val & MDIO_PMA_EXTABLE_BT1) {
+- val = genphy_c45_pma_baset1_read_abilities(phydev);
+- if (val < 0)
+- return val;
+- }
+ }
+
+ /* This is optional functionality. If not supported, we may get an error
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1896,6 +1896,7 @@ int genphy_c45_an_config_aneg(struct phy
+ int genphy_c45_an_disable_aneg(struct phy_device *phydev);
+ int genphy_c45_read_mdix(struct phy_device *phydev);
+ int genphy_c45_pma_read_abilities(struct phy_device *phydev);
++int genphy_c45_pma_read_ext_abilities(struct phy_device *phydev);
+ int genphy_c45_pma_baset1_read_abilities(struct phy_device *phydev);
+ int genphy_c45_read_eee_abilities(struct phy_device *phydev);
+ int genphy_c45_pma_baset1_read_master_slave(struct phy_device *phydev);
+++ /dev/null
-From fcb96cb774abf14375326c41cedd237d6c8f6e94 Mon Sep 17 00:00:00 2001
-From: Chuanhong Guo <gch981213@gmail.com>
-Date: Tue, 20 Aug 2024 08:33:01 +0800
-Subject: [PATCH 2/9] clk: add drivers for sf19a2890
-
-Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
----
- drivers/clk/Kconfig | 1 +
- drivers/clk/Makefile | 1 +
- 2 files changed, 2 insertions(+)
-
---- a/drivers/clk/Kconfig
-+++ b/drivers/clk/Kconfig
-@@ -489,6 +489,7 @@ source "drivers/clk/renesas/Kconfig"
- source "drivers/clk/rockchip/Kconfig"
- source "drivers/clk/samsung/Kconfig"
- source "drivers/clk/sifive/Kconfig"
-+source "drivers/clk/siflower/Kconfig"
- source "drivers/clk/socfpga/Kconfig"
- source "drivers/clk/sprd/Kconfig"
- source "drivers/clk/starfive/Kconfig"
---- a/drivers/clk/Makefile
-+++ b/drivers/clk/Makefile
-@@ -116,6 +116,7 @@ obj-y += renesas/
- obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
- obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
- obj-$(CONFIG_CLK_SIFIVE) += sifive/
-+obj-$(CONFIG_CLK_SIFLOWER) += siflower/
- obj-y += socfpga/
- obj-$(CONFIG_PLAT_SPEAR) += spear/
- obj-y += sprd/
--- /dev/null
+From: Frank Sae <Frank.Sae@motor-comm.com>
+Date: Sun, 1 Sep 2024 01:35:25 -0700
+Subject: [PATCH 02/20] net: phy: Optimize phy speed mask to be compatible to
+ yt8821
+
+yt8521 and yt8531s as Gigabit transceiver use bit15:14(bit9 reserved
+default 0) as phy speed mask, yt8821 as 2.5G transceiver uses bit9 bit15:14
+as phy speed mask.
+
+Be compatible to yt8821, reform phy speed mask and phy speed macro.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+
+(cherry picked from commit 8d878c87b5c45ae64b0aecd4aac71e210d19173f)
+---
+ drivers/net/phy/motorcomm.c | 13 +++++--------
+ 1 file changed, 5 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -47,12 +47,10 @@
+
+ /* Specific Status Register */
+ #define YTPHY_SPECIFIC_STATUS_REG 0x11
+-#define YTPHY_SSR_SPEED_MODE_OFFSET 14
+-
+-#define YTPHY_SSR_SPEED_MODE_MASK (BIT(15) | BIT(14))
+-#define YTPHY_SSR_SPEED_10M 0x0
+-#define YTPHY_SSR_SPEED_100M 0x1
+-#define YTPHY_SSR_SPEED_1000M 0x2
++#define YTPHY_SSR_SPEED_MASK ((0x3 << 14) | BIT(9))
++#define YTPHY_SSR_SPEED_10M ((0x0 << 14))
++#define YTPHY_SSR_SPEED_100M ((0x1 << 14))
++#define YTPHY_SSR_SPEED_1000M ((0x2 << 14))
+ #define YTPHY_SSR_DUPLEX_OFFSET 13
+ #define YTPHY_SSR_DUPLEX BIT(13)
+ #define YTPHY_SSR_PAGE_RECEIVED BIT(12)
+@@ -1188,8 +1186,7 @@ static int yt8521_adjust_status(struct p
+ else
+ duplex = DUPLEX_FULL; /* for fiber, it always DUPLEX_FULL */
+
+- speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >>
+- YTPHY_SSR_SPEED_MODE_OFFSET;
++ speed_mode = status & YTPHY_SSR_SPEED_MASK;
+
+ switch (speed_mode) {
+ case YTPHY_SSR_SPEED_10M:
--- /dev/null
+From: Frank Sae <Frank.Sae@motor-comm.com>
+Date: Sun, 1 Sep 2024 01:35:26 -0700
+Subject: [PATCH 03/20] net: phy: Add driver for Motorcomm yt8821 2.5G ethernet
+ phy
+
+Add a driver for the motorcomm yt8821 2.5G ethernet phy. Verified the
+driver on BPI-R3(with MediaTek MT7986(Filogic 830) SoC) development board,
+which is developed by Guangdong Bipai Technology Co., Ltd..
+
+yt8821 2.5G ethernet phy works in AUTO_BX2500_SGMII or FORCE_BX2500
+interface, supports 2.5G/1000M/100M/10M speeds, and wol(magic package).
+
+Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
+Reviewed-by: Sai Krishna <saikrishnag@marvell.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+
+(cherry picked from commit b671105b88c3bb9acc1fb61a3ee2ca0ece60cb8d)
+---
+ drivers/net/phy/motorcomm.c | 671 +++++++++++++++++++++++++++++++++++-
+ 1 file changed, 667 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+- * Motorcomm 8511/8521/8531/8531S PHY driver.
++ * Motorcomm 8511/8521/8531/8531S/8821 PHY driver.
+ *
+ * Author: Peter Geis <pgwipeout@gmail.com>
+ * Author: Frank <Frank.Sae@motor-comm.com>
+@@ -17,8 +17,8 @@
+ #define PHY_ID_YT8521 0x0000011a
+ #define PHY_ID_YT8531 0x4f51e91b
+ #define PHY_ID_YT8531S 0x4f51e91a
+-
+-/* YT8521/YT8531S Register Overview
++#define PHY_ID_YT8821 0x4f51ea19
++/* YT8521/YT8531S/YT8821 Register Overview
+ * UTP Register space | FIBER Register space
+ * ------------------------------------------------------------
+ * | UTP MII | FIBER MII |
+@@ -51,6 +51,8 @@
+ #define YTPHY_SSR_SPEED_10M ((0x0 << 14))
+ #define YTPHY_SSR_SPEED_100M ((0x1 << 14))
+ #define YTPHY_SSR_SPEED_1000M ((0x2 << 14))
++#define YTPHY_SSR_SPEED_10G ((0x3 << 14))
++#define YTPHY_SSR_SPEED_2500M ((0x0 << 14) | BIT(9))
+ #define YTPHY_SSR_DUPLEX_OFFSET 13
+ #define YTPHY_SSR_DUPLEX BIT(13)
+ #define YTPHY_SSR_PAGE_RECEIVED BIT(12)
+@@ -269,12 +271,89 @@
+ #define YT8531_SCR_CLK_SRC_REF_25M 4
+ #define YT8531_SCR_CLK_SRC_SSC_25M 5
+
++#define YT8821_SDS_EXT_CSR_CTRL_REG 0x23
++#define YT8821_SDS_EXT_CSR_VCO_LDO_EN BIT(15)
++#define YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN BIT(8)
++
++#define YT8821_UTP_EXT_PI_CTRL_REG 0x56
++#define YT8821_UTP_EXT_PI_RST_N_FIFO BIT(5)
++#define YT8821_UTP_EXT_PI_TX_CLK_SEL_AFE BIT(4)
++#define YT8821_UTP_EXT_PI_RX_CLK_3_SEL_AFE BIT(3)
++#define YT8821_UTP_EXT_PI_RX_CLK_2_SEL_AFE BIT(2)
++#define YT8821_UTP_EXT_PI_RX_CLK_1_SEL_AFE BIT(1)
++#define YT8821_UTP_EXT_PI_RX_CLK_0_SEL_AFE BIT(0)
++
++#define YT8821_UTP_EXT_VCT_CFG6_CTRL_REG 0x97
++#define YT8821_UTP_EXT_FECHO_AMP_TH_HUGE GENMASK(15, 8)
++
++#define YT8821_UTP_EXT_ECHO_CTRL_REG 0x336
++#define YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000 GENMASK(14, 8)
++
++#define YT8821_UTP_EXT_GAIN_CTRL_REG 0x340
++#define YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000 GENMASK(6, 0)
++
++#define YT8821_UTP_EXT_RPDN_CTRL_REG 0x34E
++#define YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 BIT(15)
++#define YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500 BIT(7)
++#define YT8821_UTP_EXT_RPDN_IPR_SHT_2500 GENMASK(6, 0)
++
++#define YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG 0x36A
++#define YT8821_UTP_EXT_TH_20DB_2500 GENMASK(15, 0)
++
++#define YT8821_UTP_EXT_TRACE_CTRL_REG 0x372
++#define YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500 GENMASK(14, 8)
++#define YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500 GENMASK(6, 0)
++
++#define YT8821_UTP_EXT_ALPHA_IPR_CTRL_REG 0x374
++#define YT8821_UTP_EXT_ALPHA_SHT_2500 GENMASK(14, 8)
++#define YT8821_UTP_EXT_IPR_LNG_2500 GENMASK(6, 0)
++
++#define YT8821_UTP_EXT_PLL_CTRL_REG 0x450
++#define YT8821_UTP_EXT_PLL_SPARE_CFG GENMASK(7, 0)
++
++#define YT8821_UTP_EXT_DAC_IMID_CH_2_3_CTRL_REG 0x466
++#define YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG GENMASK(14, 8)
++#define YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG GENMASK(6, 0)
++
++#define YT8821_UTP_EXT_DAC_IMID_CH_0_1_CTRL_REG 0x467
++#define YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG GENMASK(14, 8)
++#define YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG GENMASK(6, 0)
++
++#define YT8821_UTP_EXT_DAC_IMSB_CH_2_3_CTRL_REG 0x468
++#define YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG GENMASK(14, 8)
++#define YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG GENMASK(6, 0)
++
++#define YT8821_UTP_EXT_DAC_IMSB_CH_0_1_CTRL_REG 0x469
++#define YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG GENMASK(14, 8)
++#define YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG GENMASK(6, 0)
++
++#define YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG 0x4B3
++#define YT8821_UTP_EXT_MU_COARSE_FR_F_FFE GENMASK(14, 12)
++#define YT8821_UTP_EXT_MU_COARSE_FR_F_FBE GENMASK(10, 8)
++
++#define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG 0x4B5
++#define YT8821_UTP_EXT_MU_FINE_FR_F_FFE GENMASK(14, 12)
++#define YT8821_UTP_EXT_MU_FINE_FR_F_FBE GENMASK(10, 8)
++
++#define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG 0x4D2
++#define YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER GENMASK(7, 4)
++#define YT8821_UTP_EXT_VGA_LPF1_CAP_2500 GENMASK(3, 0)
++
++#define YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG 0x4D3
++#define YT8821_UTP_EXT_VGA_LPF2_CAP_OTHER GENMASK(7, 4)
++#define YT8821_UTP_EXT_VGA_LPF2_CAP_2500 GENMASK(3, 0)
++
++#define YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG 0x660
++#define YT8821_UTP_EXT_NFR_TX_ABILITY BIT(3)
+ /* Extended Register end */
+
+ #define YTPHY_DTS_OUTPUT_CLK_DIS 0
+ #define YTPHY_DTS_OUTPUT_CLK_25M 25000000
+ #define YTPHY_DTS_OUTPUT_CLK_125M 125000000
+
++#define YT8821_CHIP_MODE_AUTO_BX2500_SGMII 0
++#define YT8821_CHIP_MODE_FORCE_BX2500 1
++
+ struct yt8521_priv {
+ /* combo_advertising is used for case of YT8521 in combo mode,
+ * this means that yt8521 may work in utp or fiber mode which depends
+@@ -2250,6 +2329,572 @@ static int yt8521_get_features(struct ph
+ return ret;
+ }
+
++/**
++ * yt8821_get_features - read mmd register to get 2.5G capability
++ * @phydev: target phy_device struct
++ *
++ * Returns: 0 or negative errno code
++ */
++static int yt8821_get_features(struct phy_device *phydev)
++{
++ int ret;
++
++ ret = genphy_c45_pma_read_ext_abilities(phydev);
++ if (ret < 0)
++ return ret;
++
++ return genphy_read_abilities(phydev);
++}
++
++/**
++ * yt8821_get_rate_matching - read register to get phy chip mode
++ * @phydev: target phy_device struct
++ * @iface: PHY data interface type
++ *
++ * Returns: rate matching type or negative errno code
++ */
++static int yt8821_get_rate_matching(struct phy_device *phydev,
++ phy_interface_t iface)
++{
++ int val;
++
++ val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG);
++ if (val < 0)
++ return val;
++
++ if (FIELD_GET(YT8521_CCR_MODE_SEL_MASK, val) ==
++ YT8821_CHIP_MODE_FORCE_BX2500)
++ return RATE_MATCH_PAUSE;
++
++ return RATE_MATCH_NONE;
++}
++
++/**
++ * yt8821_aneg_done() - determines the auto negotiation result
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * Returns: 0(no link)or 1(utp link) or negative errno code
++ */
++static int yt8821_aneg_done(struct phy_device *phydev)
++{
++ return yt8521_aneg_done_paged(phydev, YT8521_RSSR_UTP_SPACE);
++}
++
++/**
++ * yt8821_serdes_init() - serdes init
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * Returns: 0 or negative errno code
++ */
++static int yt8821_serdes_init(struct phy_device *phydev)
++{
++ int old_page;
++ int ret = 0;
++ u16 mask;
++ u16 set;
++
++ old_page = phy_select_page(phydev, YT8521_RSSR_FIBER_SPACE);
++ if (old_page < 0) {
++ phydev_err(phydev, "Failed to select page: %d\n",
++ old_page);
++ goto err_restore_page;
++ }
++
++ ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_SDS_EXT_CSR_VCO_LDO_EN |
++ YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN;
++ set = YT8821_SDS_EXT_CSR_VCO_LDO_EN;
++ ret = ytphy_modify_ext(phydev, YT8821_SDS_EXT_CSR_CTRL_REG, mask,
++ set);
++
++err_restore_page:
++ return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8821_utp_init() - utp init
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * Returns: 0 or negative errno code
++ */
++static int yt8821_utp_init(struct phy_device *phydev)
++{
++ int old_page;
++ int ret = 0;
++ u16 mask;
++ u16 save;
++ u16 set;
++
++ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
++ if (old_page < 0) {
++ phydev_err(phydev, "Failed to select page: %d\n",
++ old_page);
++ goto err_restore_page;
++ }
++
++ mask = YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 |
++ YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500 |
++ YT8821_UTP_EXT_RPDN_IPR_SHT_2500;
++ set = YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 |
++ YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500;
++ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_RPDN_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER |
++ YT8821_UTP_EXT_VGA_LPF1_CAP_2500;
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG,
++ mask, 0);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_VGA_LPF2_CAP_OTHER |
++ YT8821_UTP_EXT_VGA_LPF2_CAP_2500;
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG,
++ mask, 0);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500 |
++ YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500;
++ set = FIELD_PREP(YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500, 0x5a) |
++ FIELD_PREP(YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500, 0x3c);
++ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_TRACE_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_IPR_LNG_2500;
++ set = FIELD_PREP(YT8821_UTP_EXT_IPR_LNG_2500, 0x6c);
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_ALPHA_IPR_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000;
++ set = FIELD_PREP(YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000, 0x2a);
++ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_ECHO_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000;
++ set = FIELD_PREP(YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000, 0x22);
++ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_GAIN_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_TH_20DB_2500;
++ set = FIELD_PREP(YT8821_UTP_EXT_TH_20DB_2500, 0x8000);
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_MU_COARSE_FR_F_FFE |
++ YT8821_UTP_EXT_MU_COARSE_FR_F_FBE;
++ set = FIELD_PREP(YT8821_UTP_EXT_MU_COARSE_FR_F_FFE, 0x7) |
++ FIELD_PREP(YT8821_UTP_EXT_MU_COARSE_FR_F_FBE, 0x7);
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_MU_FINE_FR_F_FFE |
++ YT8821_UTP_EXT_MU_FINE_FR_F_FBE;
++ set = FIELD_PREP(YT8821_UTP_EXT_MU_FINE_FR_F_FFE, 0x2) |
++ FIELD_PREP(YT8821_UTP_EXT_MU_FINE_FR_F_FBE, 0x2);
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ /* save YT8821_UTP_EXT_PI_CTRL_REG's val for use later */
++ ret = ytphy_read_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG);
++ if (ret < 0)
++ goto err_restore_page;
++
++ save = ret;
++
++ mask = YT8821_UTP_EXT_PI_TX_CLK_SEL_AFE |
++ YT8821_UTP_EXT_PI_RX_CLK_3_SEL_AFE |
++ YT8821_UTP_EXT_PI_RX_CLK_2_SEL_AFE |
++ YT8821_UTP_EXT_PI_RX_CLK_1_SEL_AFE |
++ YT8821_UTP_EXT_PI_RX_CLK_0_SEL_AFE;
++ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG,
++ mask, 0);
++ if (ret < 0)
++ goto err_restore_page;
++
++ /* restore YT8821_UTP_EXT_PI_CTRL_REG's val */
++ ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG, save);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_FECHO_AMP_TH_HUGE;
++ set = FIELD_PREP(YT8821_UTP_EXT_FECHO_AMP_TH_HUGE, 0x38);
++ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_VCT_CFG6_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_NFR_TX_ABILITY;
++ set = YT8821_UTP_EXT_NFR_TX_ABILITY;
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_PLL_SPARE_CFG;
++ set = FIELD_PREP(YT8821_UTP_EXT_PLL_SPARE_CFG, 0xe9);
++ ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_PLL_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG |
++ YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG;
++ set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG, 0x64) |
++ FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG, 0x64);
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_DAC_IMID_CH_2_3_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG |
++ YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG;
++ set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG, 0x64) |
++ FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG, 0x64);
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_DAC_IMID_CH_0_1_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG |
++ YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG;
++ set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG, 0x64) |
++ FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG, 0x64);
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_DAC_IMSB_CH_2_3_CTRL_REG,
++ mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ mask = YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG |
++ YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG;
++ set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG, 0x64) |
++ FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG, 0x64);
++ ret = ytphy_modify_ext(phydev,
++ YT8821_UTP_EXT_DAC_IMSB_CH_0_1_CTRL_REG,
++ mask, set);
++
++err_restore_page:
++ return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8821_auto_sleep_config() - phy auto sleep config
++ * @phydev: a pointer to a &struct phy_device
++ * @enable: true enable auto sleep, false disable auto sleep
++ *
++ * Returns: 0 or negative errno code
++ */
++static int yt8821_auto_sleep_config(struct phy_device *phydev,
++ bool enable)
++{
++ int old_page;
++ int ret = 0;
++
++ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
++ if (old_page < 0) {
++ phydev_err(phydev, "Failed to select page: %d\n",
++ old_page);
++ goto err_restore_page;
++ }
++
++ ret = ytphy_modify_ext(phydev,
++ YT8521_EXTREG_SLEEP_CONTROL1_REG,
++ YT8521_ESC1R_SLEEP_SW,
++ enable ? 1 : 0);
++
++err_restore_page:
++ return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8821_soft_reset() - soft reset utp and serdes
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * Returns: 0 or negative errno code
++ */
++static int yt8821_soft_reset(struct phy_device *phydev)
++{
++ return ytphy_modify_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG,
++ YT8521_CCR_SW_RST, 0);
++}
++
++/**
++ * yt8821_config_init() - phy initializatioin
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * Returns: 0 or negative errno code
++ */
++static int yt8821_config_init(struct phy_device *phydev)
++{
++ u8 mode = YT8821_CHIP_MODE_AUTO_BX2500_SGMII;
++ int ret;
++ u16 set;
++
++ if (phydev->interface == PHY_INTERFACE_MODE_2500BASEX)
++ mode = YT8821_CHIP_MODE_FORCE_BX2500;
++
++ set = FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, mode);
++ ret = ytphy_modify_ext_with_lock(phydev,
++ YT8521_CHIP_CONFIG_REG,
++ YT8521_CCR_MODE_SEL_MASK,
++ set);
++ if (ret < 0)
++ return ret;
++
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
++ phydev->possible_interfaces);
++
++ if (mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) {
++ __set_bit(PHY_INTERFACE_MODE_SGMII,
++ phydev->possible_interfaces);
++
++ phydev->rate_matching = RATE_MATCH_NONE;
++ } else if (mode == YT8821_CHIP_MODE_FORCE_BX2500) {
++ phydev->rate_matching = RATE_MATCH_PAUSE;
++ }
++
++ ret = yt8821_serdes_init(phydev);
++ if (ret < 0)
++ return ret;
++
++ ret = yt8821_utp_init(phydev);
++ if (ret < 0)
++ return ret;
++
++ /* disable auto sleep */
++ ret = yt8821_auto_sleep_config(phydev, false);
++ if (ret < 0)
++ return ret;
++
++ /* soft reset */
++ return yt8821_soft_reset(phydev);
++}
++
++/**
++ * yt8821_adjust_status() - update speed and duplex to phydev
++ * @phydev: a pointer to a &struct phy_device
++ * @val: read from YTPHY_SPECIFIC_STATUS_REG
++ */
++static void yt8821_adjust_status(struct phy_device *phydev, int val)
++{
++ int speed, duplex;
++ int speed_mode;
++
++ duplex = FIELD_GET(YTPHY_SSR_DUPLEX, val);
++ speed_mode = val & YTPHY_SSR_SPEED_MASK;
++ switch (speed_mode) {
++ case YTPHY_SSR_SPEED_10M:
++ speed = SPEED_10;
++ break;
++ case YTPHY_SSR_SPEED_100M:
++ speed = SPEED_100;
++ break;
++ case YTPHY_SSR_SPEED_1000M:
++ speed = SPEED_1000;
++ break;
++ case YTPHY_SSR_SPEED_2500M:
++ speed = SPEED_2500;
++ break;
++ default:
++ speed = SPEED_UNKNOWN;
++ break;
++ }
++
++ phydev->speed = speed;
++ phydev->duplex = duplex;
++}
++
++/**
++ * yt8821_update_interface() - update interface per current speed
++ * @phydev: a pointer to a &struct phy_device
++ */
++static void yt8821_update_interface(struct phy_device *phydev)
++{
++ if (!phydev->link)
++ return;
++
++ switch (phydev->speed) {
++ case SPEED_2500:
++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
++ break;
++ case SPEED_1000:
++ case SPEED_100:
++ case SPEED_10:
++ phydev->interface = PHY_INTERFACE_MODE_SGMII;
++ break;
++ default:
++ phydev_warn(phydev, "phy speed err :%d\n", phydev->speed);
++ break;
++ }
++}
++
++/**
++ * yt8821_read_status() - determines the negotiated speed and duplex
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * Returns: 0 or negative errno code
++ */
++static int yt8821_read_status(struct phy_device *phydev)
++{
++ int link;
++ int ret;
++ int val;
++
++ ret = ytphy_write_ext_with_lock(phydev,
++ YT8521_REG_SPACE_SELECT_REG,
++ YT8521_RSSR_UTP_SPACE);
++ if (ret < 0)
++ return ret;
++
++ ret = genphy_read_status(phydev);
++ if (ret < 0)
++ return ret;
++
++ if (phydev->autoneg_complete) {
++ ret = genphy_c45_read_lpa(phydev);
++ if (ret < 0)
++ return ret;
++ }
++
++ ret = phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
++ if (ret < 0)
++ return ret;
++
++ val = ret;
++
++ link = val & YTPHY_SSR_LINK;
++ if (link)
++ yt8821_adjust_status(phydev, val);
++
++ if (link) {
++ if (phydev->link == 0)
++ phydev_dbg(phydev,
++ "%s, phy addr: %d, link up\n",
++ __func__, phydev->mdio.addr);
++ phydev->link = 1;
++ } else {
++ if (phydev->link == 1)
++ phydev_dbg(phydev,
++ "%s, phy addr: %d, link down\n",
++ __func__, phydev->mdio.addr);
++ phydev->link = 0;
++ }
++
++ val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG);
++ if (val < 0)
++ return val;
++
++ if (FIELD_GET(YT8521_CCR_MODE_SEL_MASK, val) ==
++ YT8821_CHIP_MODE_AUTO_BX2500_SGMII)
++ yt8821_update_interface(phydev);
++
++ return 0;
++}
++
++/**
++ * yt8821_modify_utp_fiber_bmcr - bits modify a PHY's BMCR register
++ * @phydev: the phy_device struct
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * NOTE: Convenience function which allows a PHY's BMCR register to be
++ * modified as new register value = (old register value & ~mask) | set.
++ *
++ * Returns: 0 or negative errno code
++ */
++static int yt8821_modify_utp_fiber_bmcr(struct phy_device *phydev,
++ u16 mask, u16 set)
++{
++ int ret;
++
++ ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_UTP_SPACE,
++ mask, set);
++ if (ret < 0)
++ return ret;
++
++ return yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_FIBER_SPACE,
++ mask, set);
++}
++
++/**
++ * yt8821_suspend() - suspend the hardware
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * Returns: 0 or negative errno code
++ */
++static int yt8821_suspend(struct phy_device *phydev)
++{
++ int wol_config;
++
++ wol_config = ytphy_read_ext_with_lock(phydev,
++ YTPHY_WOL_CONFIG_REG);
++ if (wol_config < 0)
++ return wol_config;
++
++ /* if wol enable, do nothing */
++ if (wol_config & YTPHY_WCR_ENABLE)
++ return 0;
++
++ return yt8821_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN);
++}
++
++/**
++ * yt8821_resume() - resume the hardware
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * Returns: 0 or negative errno code
++ */
++static int yt8821_resume(struct phy_device *phydev)
++{
++ int wol_config;
++ int ret;
++
++ /* disable auto sleep */
++ ret = yt8821_auto_sleep_config(phydev, false);
++ if (ret < 0)
++ return ret;
++
++ wol_config = ytphy_read_ext_with_lock(phydev,
++ YTPHY_WOL_CONFIG_REG);
++ if (wol_config < 0)
++ return wol_config;
++
++ /* if wol enable, do nothing */
++ if (wol_config & YTPHY_WCR_ENABLE)
++ return 0;
++
++ return yt8821_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0);
++}
++
+ static struct phy_driver motorcomm_phy_drvs[] = {
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
+@@ -2305,11 +2950,28 @@ static struct phy_driver motorcomm_phy_d
+ .suspend = yt8521_suspend,
+ .resume = yt8521_resume,
+ },
++ {
++ PHY_ID_MATCH_EXACT(PHY_ID_YT8821),
++ .name = "YT8821 2.5Gbps PHY",
++ .get_features = yt8821_get_features,
++ .read_page = yt8521_read_page,
++ .write_page = yt8521_write_page,
++ .get_wol = ytphy_get_wol,
++ .set_wol = ytphy_set_wol,
++ .config_aneg = genphy_config_aneg,
++ .aneg_done = yt8821_aneg_done,
++ .config_init = yt8821_config_init,
++ .get_rate_matching = yt8821_get_rate_matching,
++ .read_status = yt8821_read_status,
++ .soft_reset = yt8821_soft_reset,
++ .suspend = yt8821_suspend,
++ .resume = yt8821_resume,
++ },
+ };
+
+ module_phy_driver(motorcomm_phy_drvs);
+
+-MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver");
++MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821 PHY driver");
+ MODULE_AUTHOR("Peter Geis");
+ MODULE_AUTHOR("Frank");
+ MODULE_LICENSE("GPL");
+@@ -2319,6 +2981,7 @@ static const struct mdio_device_id __may
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
++ { PHY_ID_MATCH_EXACT(PHY_ID_YT8821) },
+ { /* sentinel */ }
+ };
+
+++ /dev/null
-From 819d2a48d45f3734c876186e651917bae69be9ba Mon Sep 17 00:00:00 2001
-From: Chuanhong Guo <gch981213@gmail.com>
-Date: Tue, 20 Aug 2024 08:33:43 +0800
-Subject: [PATCH 3/9] reset: add support for sf19a2890
-
-Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
----
- drivers/reset/Kconfig | 8 ++++++++
- drivers/reset/Makefile | 1 +
- 2 files changed, 9 insertions(+)
-
---- a/drivers/reset/Kconfig
-+++ b/drivers/reset/Kconfig
-@@ -211,6 +211,14 @@ config RESET_SCMI
- This driver uses SCMI Message Protocol to interact with the
- firmware controlling all the reset signals.
-
-+config RESET_SF19A2890_PERIPH
-+ bool "Siflower SF19A2890 Peripheral Reset Controller Driver"
-+ default MACH_SIFLOWER_MIPS
-+ depends on HAS_IOMEM
-+ help
-+ This enables reset controller driver for peripheral reset blocks
-+ found on Siflower SF19A2890 SoC.
-+
- config RESET_SIMPLE
- bool "Simple Reset Controller Driver" if COMPILE_TEST || EXPERT
- default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC
---- a/drivers/reset/Makefile
-+++ b/drivers/reset/Makefile
-@@ -29,6 +29,7 @@ obj-$(CONFIG_RESET_QCOM_PDC) += reset-qc
- obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
- obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
- obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
-+obj-$(CONFIG_RESET_SF19A2890_PERIPH) += reset-sf19a2890-periph.o
- obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
- obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
- obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
+++ /dev/null
-From 1d37455eacb1d0c262ae6aaecadf27964cbf97d8 Mon Sep 17 00:00:00 2001
-From: Chuanhong Guo <gch981213@gmail.com>
-Date: Tue, 20 Aug 2024 08:33:57 +0800
-Subject: [PATCH 4/9] gpio: add support for siflower socs
-
----
- drivers/gpio/Kconfig | 8 ++++++++
- drivers/gpio/Makefile | 1 +
- 2 files changed, 9 insertions(+)
-
---- a/drivers/gpio/Kconfig
-+++ b/drivers/gpio/Kconfig
-@@ -576,6 +576,14 @@ config GPIO_SIFIVE
- help
- Say yes here to support the GPIO device on SiFive SoCs.
-
-+config GPIO_SIFLOWER
-+ tristate "SiFlower GPIO support"
-+ depends on OF_GPIO
-+ depends on MACH_SIFLOWER_MIPS || COMPILE_TEST
-+ select GPIOLIB_IRQCHIP
-+ help
-+ GPIO controller driver for SiFlower SoCs.
-+
- config GPIO_SIOX
- tristate "SIOX GPIO support"
- depends on SIOX
---- a/drivers/gpio/Makefile
-+++ b/drivers/gpio/Makefile
-@@ -143,6 +143,7 @@ obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio
- obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
- obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
- obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
-+obj-$(CONFIG_GPIO_SIFLOWER) += gpio-siflower.o
- obj-$(CONFIG_GPIO_SIM) += gpio-sim.o
- obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
- obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Tue, 20 Aug 2024 08:32:17 +0800
+Subject: [PATCH 04/20] mips: add support for Siflower SF19A2890
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ arch/mips/Kconfig | 29 +++++++++++++++++++++++++++++
+ arch/mips/generic/Platform | 1 +
+ 2 files changed, 30 insertions(+)
+
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -861,6 +861,35 @@ config SIBYTE_BIGSUR
+ select ZONE_DMA32 if 64BIT
+ select SWIOTLB if ARCH_DMA_ADDR_T_64BIT && PCI
+
++config MACH_SIFLOWER_MIPS
++ bool "Siflower MIPS SoCs"
++ select MIPS_GENERIC
++ select ARM_AMBA
++ select BOOT_RAW
++ select CEVT_R4K
++ select CLKSRC_MIPS_GIC
++ select COMMON_CLK
++ select CPU_MIPSR2_IRQ_EI
++ select CPU_MIPSR2_IRQ_VI
++ select CSRC_R4K
++ select DMA_NONCOHERENT
++ select IRQ_MIPS_CPU
++ select MIPS_CPU_SCACHE
++ select MIPS_GIC
++ select MIPS_L1_CACHE_SHIFT_5
++ select NO_EXCEPT_FILL
++ select SMP_UP if SMP
++ select SYS_HAS_CPU_MIPS32_R2
++ select SYS_SUPPORTS_32BIT_KERNEL
++ select SYS_SUPPORTS_LITTLE_ENDIAN
++ select SYS_SUPPORTS_MIPS16
++ select SYS_SUPPORTS_MIPS_CPS
++ select SYS_SUPPORTS_MULTITHREADING
++ select USE_OF
++ help
++ Select this to build a kernel which supports SoCs from Siflower
++ with MIPS InterAptiv cores, like Siflower SF19A2890.
++
+ config SNI_RM
+ bool "SNI RM200/300/400"
+ select ARC_MEMORY
+--- a/arch/mips/generic/Platform
++++ b/arch/mips/generic/Platform
+@@ -10,6 +10,7 @@
+
+ # Note: order matters, keep the asm/mach-generic include last.
+ cflags-$(CONFIG_MACH_INGENIC_SOC) += -I$(srctree)/arch/mips/include/asm/mach-ingenic
++cflags-$(CONFIG_MACH_SIFLOWER_MIPS) += -I$(srctree)/arch/mips/include/asm/mach-siflower
+ cflags-$(CONFIG_MIPS_GENERIC) += -I$(srctree)/arch/mips/include/asm/mach-generic
+
+ load-$(CONFIG_MIPS_GENERIC) += 0xffffffff80100000
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Tue, 20 Aug 2024 08:33:01 +0800
+Subject: [PATCH 05/20] clk: add drivers for siflower socs
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ drivers/clk/Kconfig | 1 +
+ drivers/clk/Makefile | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/drivers/clk/Kconfig
++++ b/drivers/clk/Kconfig
+@@ -489,6 +489,7 @@ source "drivers/clk/renesas/Kconfig"
+ source "drivers/clk/rockchip/Kconfig"
+ source "drivers/clk/samsung/Kconfig"
+ source "drivers/clk/sifive/Kconfig"
++source "drivers/clk/siflower/Kconfig"
+ source "drivers/clk/socfpga/Kconfig"
+ source "drivers/clk/sprd/Kconfig"
+ source "drivers/clk/starfive/Kconfig"
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -116,6 +116,7 @@ obj-y += renesas/
+ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
+ obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
+ obj-$(CONFIG_CLK_SIFIVE) += sifive/
++obj-$(CONFIG_CLK_SIFLOWER) += siflower/
+ obj-y += socfpga/
+ obj-$(CONFIG_PLAT_SPEAR) += spear/
+ obj-y += sprd/
+++ /dev/null
-From 59c6a4972b584d986f72fe8d7c55930fdf799bc8 Mon Sep 17 00:00:00 2001
-From: Chuanhong Guo <gch981213@gmail.com>
-Date: Tue, 20 Aug 2024 08:34:20 +0800
-Subject: [PATCH 5/9] pinctrl: add driver for siflower sf19a2890
-
----
- drivers/pinctrl/Kconfig | 10 ++++++++++
- drivers/pinctrl/Makefile | 1 +
- 2 files changed, 11 insertions(+)
-
---- a/drivers/pinctrl/Kconfig
-+++ b/drivers/pinctrl/Kconfig
-@@ -417,6 +417,16 @@ config PINCTRL_ROCKCHIP
- help
- This support pinctrl and GPIO driver for Rockchip SoCs.
-
-+config PINCTRL_SF19A2890
-+ tristate "Siflower SF19A2890 pinctrl driver"
-+ depends on OF && (MACH_SIFLOWER_MIPS || COMPILE_TEST)
-+ select PINMUX
-+ select PINCONF
-+ select GENERIC_PINCONF
-+ default MACH_SIFLOWER_MIPS
-+ help
-+ Say Y here to enable the Siflower SF19A2890 pinctrl driver.
-+
- config PINCTRL_SINGLE
- tristate "One-register-per-pin type device tree based pinctrl driver"
- depends on OF
---- a/drivers/pinctrl/Makefile
-+++ b/drivers/pinctrl/Makefile
-@@ -43,6 +43,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-p
- obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
- obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
- obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
-+obj-$(CONFIG_PINCTRL_SF19A2890) += pinctrl-sf19a2890.o
- obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
- obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
- obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Tue, 20 Aug 2024 08:33:43 +0800
+Subject: [PATCH 06/20] reset: add support for sf19a2890
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ drivers/reset/Kconfig | 8 ++++++++
+ drivers/reset/Makefile | 1 +
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/reset/Kconfig
++++ b/drivers/reset/Kconfig
+@@ -211,6 +211,14 @@ config RESET_SCMI
+ This driver uses SCMI Message Protocol to interact with the
+ firmware controlling all the reset signals.
+
++config RESET_SF19A2890_PERIPH
++ bool "Siflower SF19A2890 Peripheral Reset Controller Driver"
++ default MACH_SIFLOWER_MIPS
++ depends on HAS_IOMEM
++ help
++ This enables reset controller driver for peripheral reset blocks
++ found on Siflower SF19A2890 SoC.
++
+ config RESET_SIMPLE
+ bool "Simple Reset Controller Driver" if COMPILE_TEST || EXPERT
+ default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC
+--- a/drivers/reset/Makefile
++++ b/drivers/reset/Makefile
+@@ -29,6 +29,7 @@ obj-$(CONFIG_RESET_QCOM_PDC) += reset-qc
+ obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
+ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
+ obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
++obj-$(CONFIG_RESET_SF19A2890_PERIPH) += reset-sf19a2890-periph.o
+ obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
+ obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
+ obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
+++ /dev/null
-From baa6c00f7a88b28f6838a9743f66c9f7f4716e25 Mon Sep 17 00:00:00 2001
-From: Chuanhong Guo <gch981213@gmail.com>
-Date: Tue, 20 Aug 2024 08:34:42 +0800
-Subject: [PATCH 6/9] stmmac: add support for sf19a2890
-
----
- drivers/net/ethernet/stmicro/stmmac/Kconfig | 9 +++++++++
- drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
- 2 files changed, 10 insertions(+)
-
---- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
-+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
-@@ -142,6 +142,15 @@ config DWMAC_ROCKCHIP
- This selects the Rockchip RK3288 SoC glue layer support for
- the stmmac device driver.
-
-+config DWMAC_SF19A2890
-+ tristate "Siflower SF19A2890 GMAC support"
-+ default MACH_SIFLOWER_MIPS
-+ help
-+ Support for GMAC on Siflower SF19A2890 SoC.
-+
-+ This selects the Siflower SF19A2890 SoC glue layer support for
-+ the stmmac device driver.
-+
- config DWMAC_SOCFPGA
- tristate "SOCFPGA dwmac support"
- default ARCH_INTEL_SOCFPGA
---- a/drivers/net/ethernet/stmicro/stmmac/Makefile
-+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
-@@ -21,6 +21,7 @@ obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-me
- obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o
- obj-$(CONFIG_DWMAC_QCOM_ETHQOS) += dwmac-qcom-ethqos.o
- obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o
-+obj-$(CONFIG_DWMAC_SF19A2890) += dwmac-sf19a2890.o
- obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o
- obj-$(CONFIG_DWMAC_STARFIVE) += dwmac-starfive.o
- obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Tue, 20 Aug 2024 08:33:57 +0800
+Subject: [PATCH 07/20] gpio: add support for siflower socs
+
+Add support for the GPIO controller on Siflower SoCs.
+This controller is found on Siflower SF19A2890 (MIPS) and SF21A6826
+(RISC-V)
+
+Signed-off-by: Qingfang Deng <qingfang.deng@siflower.com.cn>
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ drivers/gpio/Kconfig | 8 ++++++++
+ drivers/gpio/Makefile | 1 +
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -576,6 +576,14 @@ config GPIO_SIFIVE
+ help
+ Say yes here to support the GPIO device on SiFive SoCs.
+
++config GPIO_SIFLOWER
++ tristate "SiFlower GPIO support"
++ depends on OF_GPIO
++ depends on MACH_SIFLOWER_MIPS || RISCV || COMPILE_TEST
++ select GPIOLIB_IRQCHIP
++ help
++ GPIO controller driver for SiFlower SoCs.
++
+ config GPIO_SIOX
+ tristate "SIOX GPIO support"
+ depends on SIOX
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -143,6 +143,7 @@ obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio
+ obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
+ obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
+ obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
++obj-$(CONFIG_GPIO_SIFLOWER) += gpio-siflower.o
+ obj-$(CONFIG_GPIO_SIM) += gpio-sim.o
+ obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
+ obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o
+++ /dev/null
-From 68817a14ae9dff587cee8515e68c67cba89b39ab Mon Sep 17 00:00:00 2001
-From: Chuanhong Guo <gch981213@gmail.com>
-Date: Mon, 9 Sep 2024 10:18:33 +0800
-Subject: [PATCH 7/9] phy: add support for SF19A2890 USB PHY
-
-Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
----
- drivers/phy/Kconfig | 1 +
- drivers/phy/Makefile | 1 +
- 2 files changed, 2 insertions(+)
-
---- a/drivers/phy/Kconfig
-+++ b/drivers/phy/Kconfig
-@@ -90,6 +90,7 @@ source "drivers/phy/ralink/Kconfig"
- source "drivers/phy/renesas/Kconfig"
- source "drivers/phy/rockchip/Kconfig"
- source "drivers/phy/samsung/Kconfig"
-+source "drivers/phy/siflower/Kconfig"
- source "drivers/phy/socionext/Kconfig"
- source "drivers/phy/st/Kconfig"
- source "drivers/phy/starfive/Kconfig"
---- a/drivers/phy/Makefile
-+++ b/drivers/phy/Makefile
-@@ -29,6 +29,7 @@ obj-y += allwinner/ \
- renesas/ \
- rockchip/ \
- samsung/ \
-+ siflower/ \
- socionext/ \
- st/ \
- starfive/ \
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Tue, 20 Aug 2024 08:34:20 +0800
+Subject: [PATCH 08/20] pinctrl: add driver for siflower sf19a2890
+
+---
+ drivers/pinctrl/Kconfig | 10 ++++++++++
+ drivers/pinctrl/Makefile | 1 +
+ 2 files changed, 11 insertions(+)
+
+--- a/drivers/pinctrl/Kconfig
++++ b/drivers/pinctrl/Kconfig
+@@ -417,6 +417,16 @@ config PINCTRL_ROCKCHIP
+ help
+ This support pinctrl and GPIO driver for Rockchip SoCs.
+
++config PINCTRL_SF19A2890
++ tristate "Siflower SF19A2890 pinctrl driver"
++ depends on OF && (MACH_SIFLOWER_MIPS || COMPILE_TEST)
++ select PINMUX
++ select PINCONF
++ select GENERIC_PINCONF
++ default MACH_SIFLOWER_MIPS
++ help
++ Say Y here to enable the Siflower SF19A2890 pinctrl driver.
++
+ config PINCTRL_SINGLE
+ tristate "One-register-per-pin type device tree based pinctrl driver"
+ depends on OF
+--- a/drivers/pinctrl/Makefile
++++ b/drivers/pinctrl/Makefile
+@@ -43,6 +43,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-p
+ obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
+ obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
+ obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
++obj-$(CONFIG_PINCTRL_SF19A2890) += pinctrl-sf19a2890.o
+ obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
+ obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
+ obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
+++ /dev/null
-From 29282086f215ae723e6d2c139d23094e699ba5bb Mon Sep 17 00:00:00 2001
-From: Chuanhong Guo <gch981213@gmail.com>
-Date: Mon, 9 Sep 2024 16:46:53 +0800
-Subject: [PATCH 8/9] usb: dwc2: add support for Siflower SF19A2890
-
-Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
----
- drivers/usb/dwc2/params.c | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
---- a/drivers/usb/dwc2/params.c
-+++ b/drivers/usb/dwc2/params.c
-@@ -200,6 +200,14 @@ static void dwc2_set_amcc_params(struct
- p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
- }
-
-+static void dwc2_set_sf19a2890_params(struct dwc2_hsotg *hsotg)
-+{
-+ struct dwc2_core_params *p = &hsotg->params;
-+
-+ p->max_transfer_size = 65535;
-+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT;
-+}
-+
- static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
- {
- struct dwc2_core_params *p = &hsotg->params;
-@@ -294,6 +302,8 @@ const struct of_device_id dwc2_of_match_
- .data = dwc2_set_amlogic_a1_params },
- { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
- { .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params },
-+ { .compatible = "siflower,sf19a2890-usb",
-+ .data = dwc2_set_sf19a2890_params },
- { .compatible = "st,stm32f4x9-fsotg",
- .data = dwc2_set_stm32f4x9_fsotg_params },
- { .compatible = "st,stm32f4x9-hsotg" },
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Tue, 20 Aug 2024 08:34:42 +0800
+Subject: [PATCH 09/20] stmmac: add support for sf19a2890
+
+---
+ drivers/net/ethernet/stmicro/stmmac/Kconfig | 9 +++++++++
+ drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
+ 2 files changed, 10 insertions(+)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
++++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
+@@ -142,6 +142,15 @@ config DWMAC_ROCKCHIP
+ This selects the Rockchip RK3288 SoC glue layer support for
+ the stmmac device driver.
+
++config DWMAC_SF19A2890
++ tristate "Siflower SF19A2890 GMAC support"
++ default MACH_SIFLOWER_MIPS
++ help
++ Support for GMAC on Siflower SF19A2890 SoC.
++
++ This selects the Siflower SF19A2890 SoC glue layer support for
++ the stmmac device driver.
++
+ config DWMAC_SOCFPGA
+ tristate "SOCFPGA dwmac support"
+ default ARCH_INTEL_SOCFPGA
+--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
+@@ -21,6 +21,7 @@ obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-me
+ obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o
+ obj-$(CONFIG_DWMAC_QCOM_ETHQOS) += dwmac-qcom-ethqos.o
+ obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o
++obj-$(CONFIG_DWMAC_SF19A2890) += dwmac-sf19a2890.o
+ obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o
+ obj-$(CONFIG_DWMAC_STARFIVE) += dwmac-starfive.o
+ obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o
+++ /dev/null
-From 0b04c37a1aae523025195c29a6477cf26234d26c Mon Sep 17 00:00:00 2001
-From: Chuanhong Guo <gch981213@gmail.com>
-Date: Tue, 10 Sep 2024 09:10:27 +0800
-Subject: [PATCH 9/9] usb: dwc2: handle OTG interrupt regardless of GINTSTS
-
-The DWC OTG 3.30a found on Siflower SF19A2890 has battery charger
-support enabled. It triggers MultVallpChng interrupt (bit 20 of
-GOTGINT) but doesn't set OTGInt in GINTSTS. As a result, this
-interrupt is never handled, and linux disables USB interrupt
-because "nobody cares".
-
-Handle OTG interrupt in IRQ handler regardless of whether the
-OTGInt bit in GINTSTS is set or not.
-
-Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
----
- drivers/usb/dwc2/core_intr.c | 11 ++++++++---
- 1 file changed, 8 insertions(+), 3 deletions(-)
-
---- a/drivers/usb/dwc2/core_intr.c
-+++ b/drivers/usb/dwc2/core_intr.c
-@@ -79,7 +79,7 @@ static void dwc2_handle_mode_mismatch_in
- *
- * @hsotg: Programming view of DWC_otg controller
- */
--static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
-+static irqreturn_t dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
- {
- u32 gotgint;
- u32 gotgctl;
-@@ -87,6 +87,10 @@ static void dwc2_handle_otg_intr(struct
-
- gotgint = dwc2_readl(hsotg, GOTGINT);
- gotgctl = dwc2_readl(hsotg, GOTGCTL);
-+
-+ if (!gotgint)
-+ return IRQ_NONE;
-+
- dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint,
- dwc2_op_state_str(hsotg));
-
-@@ -229,6 +233,7 @@ static void dwc2_handle_otg_intr(struct
-
- /* Clear GOTGINT */
- dwc2_writel(hsotg, gotgint, GOTGINT);
-+ return IRQ_HANDLED;
- }
-
- /**
-@@ -842,6 +847,8 @@ irqreturn_t dwc2_handle_common_intr(int
- hsotg->frame_number = (dwc2_readl(hsotg, HFNUM)
- & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
-
-+ retval = dwc2_handle_otg_intr(hsotg);
-+
- gintsts = dwc2_read_common_intr(hsotg);
- if (gintsts & ~GINTSTS_PRTINT)
- retval = IRQ_HANDLED;
-@@ -855,8 +862,6 @@ irqreturn_t dwc2_handle_common_intr(int
-
- if (gintsts & GINTSTS_MODEMIS)
- dwc2_handle_mode_mismatch_intr(hsotg);
-- if (gintsts & GINTSTS_OTGINT)
-- dwc2_handle_otg_intr(hsotg);
- if (gintsts & GINTSTS_CONIDSTSCHNG)
- dwc2_handle_conn_id_status_change_intr(hsotg);
- if (gintsts & GINTSTS_DISCONNINT)
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Mon, 9 Sep 2024 10:18:33 +0800
+Subject: [PATCH 10/20] phy: add support for Siflower USB PHYs
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ drivers/phy/Kconfig | 1 +
+ drivers/phy/Makefile | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/drivers/phy/Kconfig
++++ b/drivers/phy/Kconfig
+@@ -90,6 +90,7 @@ source "drivers/phy/ralink/Kconfig"
+ source "drivers/phy/renesas/Kconfig"
+ source "drivers/phy/rockchip/Kconfig"
+ source "drivers/phy/samsung/Kconfig"
++source "drivers/phy/siflower/Kconfig"
+ source "drivers/phy/socionext/Kconfig"
+ source "drivers/phy/st/Kconfig"
+ source "drivers/phy/starfive/Kconfig"
+--- a/drivers/phy/Makefile
++++ b/drivers/phy/Makefile
+@@ -29,6 +29,7 @@ obj-y += allwinner/ \
+ renesas/ \
+ rockchip/ \
+ samsung/ \
++ siflower/ \
+ socionext/ \
+ st/ \
+ starfive/ \
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Mon, 9 Sep 2024 16:46:53 +0800
+Subject: [PATCH 11/20] usb: dwc2: add support for Siflower SF19A2890
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ drivers/usb/dwc2/params.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/usb/dwc2/params.c
++++ b/drivers/usb/dwc2/params.c
+@@ -200,6 +200,14 @@ static void dwc2_set_amcc_params(struct
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
+ }
+
++static void dwc2_set_sf19a2890_params(struct dwc2_hsotg *hsotg)
++{
++ struct dwc2_core_params *p = &hsotg->params;
++
++ p->max_transfer_size = 65535;
++ p->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT;
++}
++
+ static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
+ {
+ struct dwc2_core_params *p = &hsotg->params;
+@@ -294,6 +302,8 @@ const struct of_device_id dwc2_of_match_
+ .data = dwc2_set_amlogic_a1_params },
+ { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
+ { .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params },
++ { .compatible = "siflower,sf19a2890-usb",
++ .data = dwc2_set_sf19a2890_params },
+ { .compatible = "st,stm32f4x9-fsotg",
+ .data = dwc2_set_stm32f4x9_fsotg_params },
+ { .compatible = "st,stm32f4x9-hsotg" },
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Tue, 10 Sep 2024 09:10:27 +0800
+Subject: [PATCH 12/20] usb: dwc2: handle OTG interrupt regardless of GINTSTS
+
+The DWC OTG 3.30a found on Siflower SF19A2890 has battery charger
+support enabled. It triggers MultVallpChng interrupt (bit 20 of
+GOTGINT) but doesn't set OTGInt in GINTSTS. As a result, this
+interrupt is never handled, and linux disables USB interrupt
+because "nobody cares".
+
+Handle OTG interrupt in IRQ handler regardless of whether the
+OTGInt bit in GINTSTS is set or not.
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ drivers/usb/dwc2/core_intr.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/dwc2/core_intr.c
++++ b/drivers/usb/dwc2/core_intr.c
+@@ -79,7 +79,7 @@ static void dwc2_handle_mode_mismatch_in
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ */
+-static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
++static irqreturn_t dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
+ {
+ u32 gotgint;
+ u32 gotgctl;
+@@ -87,6 +87,10 @@ static void dwc2_handle_otg_intr(struct
+
+ gotgint = dwc2_readl(hsotg, GOTGINT);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
++
++ if (!gotgint)
++ return IRQ_NONE;
++
+ dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint,
+ dwc2_op_state_str(hsotg));
+
+@@ -229,6 +233,7 @@ static void dwc2_handle_otg_intr(struct
+
+ /* Clear GOTGINT */
+ dwc2_writel(hsotg, gotgint, GOTGINT);
++ return IRQ_HANDLED;
+ }
+
+ /**
+@@ -842,6 +847,8 @@ irqreturn_t dwc2_handle_common_intr(int
+ hsotg->frame_number = (dwc2_readl(hsotg, HFNUM)
+ & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
+
++ retval = dwc2_handle_otg_intr(hsotg);
++
+ gintsts = dwc2_read_common_intr(hsotg);
+ if (gintsts & ~GINTSTS_PRTINT)
+ retval = IRQ_HANDLED;
+@@ -855,8 +862,6 @@ irqreturn_t dwc2_handle_common_intr(int
+
+ if (gintsts & GINTSTS_MODEMIS)
+ dwc2_handle_mode_mismatch_intr(hsotg);
+- if (gintsts & GINTSTS_OTGINT)
+- dwc2_handle_otg_intr(hsotg);
+ if (gintsts & GINTSTS_CONIDSTSCHNG)
+ dwc2_handle_conn_id_status_change_intr(hsotg);
+ if (gintsts & GINTSTS_DISCONNINT)
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Sat, 14 Sep 2024 11:57:35 +0800
+Subject: [PATCH 13/20] riscv: add Siflower RISC-V SoC family Kconfig support
+
+Siflower RISC-V SoCs, including SF21A6826, SF21H8898 and some other
+upcomping chips, are RISC-V chips with T-Head C908 cores for home
+routers and gateways. Add a Kconfig entry named ARCH_SIFLOWER for
+them.
+Notably these chips uses ARM PL011 for UART. ARM_AMBA is selected
+for its driver.
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ arch/riscv/Kconfig.socs | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/arch/riscv/Kconfig.socs
++++ b/arch/riscv/Kconfig.socs
+@@ -22,6 +22,12 @@ config SOC_SIFIVE
+ help
+ This enables support for SiFive SoC platform hardware.
+
++config ARCH_SIFLOWER
++ bool "Siflower RISC-V SoCs"
++ select ARM_AMBA if TTY
++ help
++ This enables support for Siflower RISC-V SoC platform hardware.
++
+ config ARCH_STARFIVE
+ def_bool SOC_STARFIVE
+
--- /dev/null
+From: Qingfang Deng <qingfang.deng@siflower.com.cn>
+Date: Sat, 14 Sep 2024 12:00:59 +0800
+Subject: [PATCH 14/20] riscv: add an option for efficient unaligned access
+
+Some riscv cpus like T-Head C908 allows unaligned memory access,
+and we don't need to force an alignment on compiler level.
+Add an option for that.
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ arch/riscv/Kconfig | 11 +++++++++++
+ arch/riscv/Makefile | 2 ++
+ 2 files changed, 13 insertions(+)
+
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -639,6 +639,17 @@ config THREAD_SIZE_ORDER
+ Specify the Pages of thread stack size (from 4KB to 64KB), which also
+ affects irq stack size, which is equal to thread stack size.
+
++config RISCV_EFFICIENT_UNALIGNED_ACCESS
++ bool "Assume the system supports fast unaligned memory accesses"
++ depends on NONPORTABLE
++ select HAVE_EFFICIENT_UNALIGNED_ACCESS
++ help
++ Assume that the system supports fast unaligned memory accesses. When
++ enabled, this option improves the performance of the kernel on such
++ systems. However, the kernel and userspace programs will run much more
++ slowly, or will not be able to run at all, on systems that do not
++ support efficient unaligned memory accesses.
++
+ endmenu # "Platform type"
+
+ menu "Kernel features"
+--- a/arch/riscv/Makefile
++++ b/arch/riscv/Makefile
+@@ -104,7 +104,9 @@ KBUILD_AFLAGS_MODULE += $(call as-option
+ # unaligned accesses. While unaligned accesses are explicitly allowed in the
+ # RISC-V ISA, they're emulated by machine mode traps on all extant
+ # architectures. It's faster to have GCC emit only aligned accesses.
++ifneq ($(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS),y)
+ KBUILD_CFLAGS += $(call cc-option,-mstrict-align)
++endif
+
+ ifeq ($(CONFIG_STACKPROTECTOR_PER_TASK),y)
+ prepare: stack_protector_prepare
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Sat, 14 Sep 2024 16:51:36 +0800
+Subject: [PATCH 15/20] reset: add support for sf21a6826/sf21h8898
+
+---
+ drivers/reset/Kconfig | 5 +++++
+ drivers/reset/Makefile | 1 +
+ 2 files changed, 6 insertions(+)
+
+--- a/drivers/reset/Kconfig
++++ b/drivers/reset/Kconfig
+@@ -219,6 +219,11 @@ config RESET_SF19A2890_PERIPH
+ This enables reset controller driver for peripheral reset blocks
+ found on Siflower SF19A2890 SoC.
+
++config RESET_SF21
++ tristate "Siflower SF21A6826/SF21H8898 Reset Controller Driver"
++ help
++ This enables the reset controller driver for Siflower SF21A6826/SF21H8898.
++
+ config RESET_SIMPLE
+ bool "Simple Reset Controller Driver" if COMPILE_TEST || EXPERT
+ default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC
+--- a/drivers/reset/Makefile
++++ b/drivers/reset/Makefile
+@@ -30,6 +30,7 @@ obj-$(CONFIG_RESET_RASPBERRYPI) += reset
+ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
+ obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
+ obj-$(CONFIG_RESET_SF19A2890_PERIPH) += reset-sf19a2890-periph.o
++obj-$(CONFIG_RESET_SF21) += reset-sf21.o
+ obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
+ obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
+ obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Thu, 19 Sep 2024 09:23:27 +0800
+Subject: [PATCH 16/20] spi: spi-mem: allow gpio cs in spi_mem_exec_op
+
+spi_mem_exec_op can use gpio cs, either by not asserting the native
+cs or switching the native cs pin to GPIO mode with pinctrl.
+
+Allow calling exec_op when GPIO CS present and control GPIO CS
+before and after calling exec_op.
+If exec_op decided to return -EOPNOTSUPP, the code will assert and
+deassert GPIO CS without clock pulsing, which should be fine on most
+SPI slaves.
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ drivers/spi/spi-mem.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/spi/spi-mem.c
++++ b/drivers/spi/spi-mem.c
+@@ -325,13 +325,19 @@ int spi_mem_exec_op(struct spi_mem *mem,
+ if (!spi_mem_internal_supports_op(mem, op))
+ return -ENOTSUPP;
+
+- if (ctlr->mem_ops && ctlr->mem_ops->exec_op && !spi_get_csgpiod(mem->spi, 0)) {
++ if (ctlr->mem_ops && ctlr->mem_ops->exec_op) {
+ ret = spi_mem_access_start(mem);
+ if (ret)
+ return ret;
+
++ if (spi_get_csgpiod(mem->spi, 0))
++ gpiod_set_value_cansleep(spi_get_csgpiod(mem->spi, 0), 1);
++
+ ret = ctlr->mem_ops->exec_op(mem, op);
+
++ if (spi_get_csgpiod(mem->spi, 0))
++ gpiod_set_value_cansleep(spi_get_csgpiod(mem->spi, 0), 0);
++
+ spi_mem_access_end(mem);
+
+ /*
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Thu, 19 Sep 2024 10:02:16 +0800
+Subject: [PATCH 17/20] spi: add support for sf21-qspi
+
+Add support for the QSPI controller found on Siflower SF21A6826
+and SF21H8898.
+It is based on ARM PL022, with custom modifications to support
+Dual/Quad SPI modes.
+A new driver is created because this modified controller is
+supported under the SPI-MEM framework. While the setup procedure
+is a bit similar to the spi-pl022.c, there aren't much code
+shared between them.
+
+Signed-off-by: Qingfang Deng <qingfang.deng@siflower.com.cn>
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ drivers/spi/Kconfig | 7 +++++++
+ drivers/spi/Makefile | 1 +
+ 2 files changed, 8 insertions(+)
+
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -940,6 +940,13 @@ config SPI_SIFIVE
+ help
+ This exposes the SPI controller IP from SiFive.
+
++config SPI_SF21_QSPI
++ tristate "Siflower SF21A6826/SF21H8898 QSPI controller"
++ depends on ARCH_SIFLOWER || COMPILE_TEST
++ help
++ This enables support for the SPI controller present on the
++ Siflower SF21A6826/SF21H8898 SoCs.
++
+ config SPI_SLAVE_MT27XX
+ tristate "MediaTek SPI slave device"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -125,6 +125,7 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hsp
+ obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
+ obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
+ obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o
++obj-$(CONFIG_SPI_SF21_QSPI) += spi-sf21-qspi.o
+ obj-$(CONFIG_SPI_SLAVE_MT27XX) += spi-slave-mt27xx.o
+ obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.o
+ obj-$(CONFIG_SPI_SPRD) += spi-sprd.o
--- /dev/null
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Fri, 29 Nov 2024 16:34:49 +0800
+Subject: [PATCH 18/20] pci: dw-pcie add support for sf21 pcie
+
+Add support for the PCIE controller found on Siflower SF21A6826
+and SF21H8898.
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ drivers/pci/controller/dwc/Kconfig | 9 +++++++++
+ drivers/pci/controller/dwc/Makefile | 1 +
+ 2 files changed, 10 insertions(+)
+
+--- a/drivers/pci/controller/dwc/Kconfig
++++ b/drivers/pci/controller/dwc/Kconfig
+@@ -317,6 +317,15 @@ config PCIE_FU740
+ Say Y here if you want PCIe controller support for the SiFive
+ FU740.
+
++config PCIE_SF21
++ bool "Siflower SF21A6826/SF21H8898 PCIe controller"
++ depends on ARCH_SIFLOWER || COMPILE_TEST
++ depends on PCI_MSI
++ select PCIE_DW_HOST
++ help
++ Say Y here to enable support of the Siflower SF21A6826/SF21H8898
++ PCIe controller.
++
+ config PCIE_UNIPHIER
+ bool "Socionext UniPhier PCIe controller (host mode)"
+ depends on ARCH_UNIPHIER || COMPILE_TEST
+--- a/drivers/pci/controller/dwc/Makefile
++++ b/drivers/pci/controller/dwc/Makefile
+@@ -22,6 +22,7 @@ obj-$(CONFIG_PCIE_KEEMBAY) += pcie-keemb
+ obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o
+ obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o
+ obj-$(CONFIG_PCI_MESON) += pci-meson.o
++obj-$(CONFIG_PCIE_SF21) += pcie-sf21.o
+ obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o
+ obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
+ obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
--- /dev/null
+From: "haoming.chen" <haoming.chen@siflower.com.cn>
+Date: Thu, 7 Nov 2024 20:18:59 +0800
+Subject: [PATCH 19/20] net: phy: add support for Siflower SF23P1211 &
+ SF23P1240
+
+Signed-off-by: haoming.chen <haoming.chen@siflower.com.cn>
+---
+ drivers/net/phy/Kconfig | 5 +++++
+ drivers/net/phy/Makefile | 1 +
+ 2 files changed, 6 insertions(+)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -482,3 +482,8 @@ endif # PHYLIB
+ config MICREL_KS8995MA
+ tristate "Micrel KS8995MA 5-ports 10/100 managed Ethernet switch"
+ depends on SPI
++
++config SIFLOWER_PHY
++ tristate "Siflower PHYs"
++ help
++ Currently supports the SF1211F, SF1240 gigabit PHY.
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -107,3 +107,4 @@ obj-$(CONFIG_STE10XP) += ste10Xp.o
+ obj-$(CONFIG_TERANETICS_PHY) += teranetics.o
+ obj-$(CONFIG_VITESSE_PHY) += vitesse.o
+ obj-$(CONFIG_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
++obj-$(CONFIG_SIFLOWER_PHY) += siflower.o
+\ No newline at end of file
--- /dev/null
+From: "haoming.chen" <haoming.chen@siflower.com.cn>
+Date: Tue, 26 Nov 2024 16:38:13 +0800
+Subject: [PATCH 20/20] net: ethernet: add support for Siflower DPNS
+
+Change-Id: Ie8fc30e4714eaa666563b1a85e22d0eb8ee778b5
+Signed-off-by: haoming.chen <haoming.chen@siflower.com.cn>
+---
+ drivers/net/ethernet/Kconfig | 1 +
+ drivers/net/ethernet/Makefile | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/drivers/net/ethernet/Kconfig
++++ b/drivers/net/ethernet/Kconfig
+@@ -192,5 +192,6 @@ source "drivers/net/ethernet/wangxun/Kco
+ source "drivers/net/ethernet/wiznet/Kconfig"
+ source "drivers/net/ethernet/xilinx/Kconfig"
+ source "drivers/net/ethernet/xircom/Kconfig"
++source "drivers/net/ethernet/siflower/Kconfig"
+
+ endif # ETHERNET
+--- a/drivers/net/ethernet/Makefile
++++ b/drivers/net/ethernet/Makefile
+@@ -104,3 +104,4 @@ obj-$(CONFIG_NET_VENDOR_XILINX) += xilin
+ obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
+ obj-$(CONFIG_NET_VENDOR_SYNOPSYS) += synopsys/
+ obj-$(CONFIG_NET_VENDOR_PENSANDO) += pensando/
++obj-$(CONFIG_NET_VENDOR_SIFLOWER) += siflower/
--- /dev/null
+
+. /lib/functions.sh
+. /lib/functions/uci-defaults.sh
+. /lib/functions/system.sh
+
+siflower_setup_interfaces()
+{
+ local board="$1"
+
+ case $board in
+ *)
+ ucidef_set_interfaces_lan_wan 'eth0 eth1 eth2' 'eth3'
+ ;;
+ esac
+}
+
+board_config_update
+board=$(board_name)
+siflower_setup_interfaces $board
+board_config_flush
+
+exit 0
--- /dev/null
+REQUIRE_IMAGE_METADATA=1
+RAMFS_COPY_BIN='fitblk'
+
+platform_do_upgrade() {
+ local board=$(board_name)
+
+ case "$board" in
+ *)
+ default_do_upgrade "$1"
+ ;;
+ esac
+}
+
+PART_NAME=firmware
+
+platform_check_image() {
+ local board=$(board_name)
+ local magic="$(get_magic_long "$1")"
+
+ [ "$#" -gt 1 ] && return 1
+
+ case "$board" in
+ *)
+ [ "$magic" != "d00dfeed" ] && {
+ echo "Invalid image type."
+ return 1
+ }
+ return 0
+ ;;
+ esac
+
+ return 0
+}
--- /dev/null
+CONFIG_64BIT=y
+CONFIG_ARCH_DMA_ADDR_T_64BIT=y
+CONFIG_ARCH_DMA_DEFAULT_COHERENT=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=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_MAX=17
+CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y
+CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y
+# CONFIG_ARCH_RV32I is not set
+CONFIG_ARCH_RV64I=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_ARCH_SIFLOWER=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_STACKWALK=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_ARCH_THEAD is not set
+CONFIG_ARCH_WANTS_THP_SWAP=y
+CONFIG_ARM_AMBA=y
+# CONFIG_AX45MP_L2_CACHE is not set
+CONFIG_BLK_MQ_PCI=y
+CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y
+CONFIG_CLK_SF19A2890_PERIPH=y
+CONFIG_CLK_SF21_TOPCRM=y
+CONFIG_CLK_SIFLOWER=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_CMDLINE="root=/dev/fit0"
+CONFIG_CMDLINE_FALLBACK=y
+CONFIG_CMODEL_MEDANY=y
+# CONFIG_CMODEL_MEDLOW is not set
+CONFIG_COMMON_CLK=y
+CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
+# CONFIG_COMPAT_32BIT_TIME is not set
+CONFIG_CONFIGFS_FS=y
+CONFIG_CONTEXT_TRACKING=y
+CONFIG_CONTEXT_TRACKING_IDLE=y
+CONFIG_CPU_MITIGATIONS=y
+CONFIG_CPU_RMAP=y
+CONFIG_CRC16=y
+CONFIG_CRYPTO_HASH_INFO=y
+CONFIG_CRYPTO_HW=y
+CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
+CONFIG_CRYPTO_LIB_GF128MUL=y
+CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1
+CONFIG_CRYPTO_LIB_SHA1=y
+CONFIG_CRYPTO_LIB_UTILS=y
+# CONFIG_CRYPTO_PCRYPT is not set
+CONFIG_CRYPTO_ZSTD=y
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEVPORT is not set
+CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC=y
+CONFIG_DMA_DIRECT_REMAP=y
+CONFIG_DTC=y
+CONFIG_DW_WATCHDOG=y
+CONFIG_EDAC_SUPPORT=y
+# CONFIG_ERRATA_ANDES is not set
+# CONFIG_ERRATA_SIFIVE is not set
+# CONFIG_ERRATA_THEAD is not set
+CONFIG_EXCLUSIVE_SYSTEM_RAM=y
+CONFIG_FIXED_PHY=y
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_FPU=y
+CONFIG_FS_IOMAP=y
+CONFIG_FUNCTION_ALIGNMENT=0
+CONFIG_FWNODE_MDIO=y
+CONFIG_FW_LOADER_PAGED_BUF=y
+CONFIG_FW_LOADER_SYSFS=y
+CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE=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_CSUM=y
+CONFIG_GENERIC_EARLY_IOREMAP=y
+CONFIG_GENERIC_ENTRY=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_IPI_MUX=y
+CONFIG_GENERIC_IRQ_MULTI_HANDLER=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_GPIOLIB_IRQCHIP=y
+CONFIG_GPIO_CDEV=y
+CONFIG_GPIO_SIFLOWER=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_IOPORT_MAP=y
+CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_DOMAIN_HIERARCHY=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_STACKS=y
+CONFIG_IRQ_WORK=y
+CONFIG_JUMP_LABEL=y
+CONFIG_KCMP=y
+CONFIG_LED_TRIGGER_PHY=y
+CONFIG_LIBFDT=y
+CONFIG_LOCK_DEBUGGING_SUPPORT=y
+CONFIG_LOCK_SPIN_ON_OWNER=y
+CONFIG_MARVELL_PHY=y
+CONFIG_MDIO_BUS=y
+CONFIG_MDIO_DEVICE=y
+CONFIG_MDIO_DEVRES=y
+CONFIG_MFD_SYSCON=y
+CONFIG_MIGRATION=y
+CONFIG_MMIOWB=y
+CONFIG_MMU_LAZY_TLB_REFCOUNT=y
+CONFIG_MODULES_USE_ELF_RELA=y
+CONFIG_MODULE_SECTIONS=y
+CONFIG_MOTORCOMM_PHY=y
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_NAND_CORE=y
+CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_SPI_NAND=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y
+CONFIG_MTD_SPLIT_FIT_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_NVMEM=y
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NET_EGRESS=y
+CONFIG_NET_FLOW_LIMIT=y
+CONFIG_NET_INGRESS=y
+CONFIG_NET_SELFTESTS=y
+CONFIG_NET_SIFLOWER_ETH_DMA=y
+CONFIG_NET_SIFLOWER_ETH_DPNS=y
+CONFIG_NET_SIFLOWER_ETH_USE_INTERNAL_SRAM=y
+CONFIG_NET_SIFLOWER_ETH_XGMAC=y
+CONFIG_NET_SIFLOWER_ETH_XPCS=y
+CONFIG_NET_SWITCHDEV=y
+CONFIG_NET_VENDOR_SIFLOWER=y
+CONFIG_NET_XGRESS=y
+CONFIG_NONPORTABLE=y
+CONFIG_NO_HZ=y
+CONFIG_NO_HZ_COMMON=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NR_CPUS=4
+CONFIG_NVMEM=y
+CONFIG_NVMEM_LAYOUTS=y
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_KOBJ=y
+CONFIG_OF_MDIO=y
+CONFIG_PAGE_OFFSET=0xff60000000000000
+CONFIG_PAGE_POOL=y
+CONFIG_PAGE_POOL_STATS=y
+CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
+CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIE_DW=y
+CONFIG_PCIE_DW_HOST=y
+CONFIG_PCIE_SF21=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_DOMAINS_GENERIC=y
+CONFIG_PCI_MSI=y
+CONFIG_PER_VMA_LOCK=y
+CONFIG_PGTABLE_LEVELS=5
+CONFIG_PHYLIB=y
+CONFIG_PHYLIB_LEDS=y
+CONFIG_PHYLINK=y
+CONFIG_PHYS_ADDR_T_64BIT=y
+# CONFIG_PHYS_RAM_BASE_FIXED is not set
+# CONFIG_PHY_SF19A2890_USB is not set
+CONFIG_PHY_SF21_PCIE=y
+# CONFIG_PHY_SF21_USB is not set
+CONFIG_PINCTRL=y
+CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_PREEMPT_NONE_BUILD=y
+CONFIG_PTP_1588_CLOCK_OPTIONAL=y
+CONFIG_QUEUED_RWLOCKS=y
+CONFIG_RANDSTRUCT_NONE=y
+CONFIG_RAS=y
+CONFIG_RATIONAL=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_MMIO=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_RESET_SF19A2890_PERIPH=y
+CONFIG_RESET_SF21=y
+CONFIG_RFS_ACCEL=y
+CONFIG_RISCV=y
+CONFIG_RISCV_ALTERNATIVE=y
+# CONFIG_RISCV_BOOT_SPINWAIT is not set
+CONFIG_RISCV_DMA_NONCOHERENT=y
+CONFIG_RISCV_EFFICIENT_UNALIGNED_ACCESS=y
+CONFIG_RISCV_INTC=y
+CONFIG_RISCV_ISA_C=y
+# CONFIG_RISCV_ISA_FALLBACK is not set
+CONFIG_RISCV_ISA_SVNAPOT=y
+CONFIG_RISCV_ISA_SVPBMT=y
+# CONFIG_RISCV_ISA_V is not set
+CONFIG_RISCV_ISA_ZBB=y
+CONFIG_RISCV_ISA_ZICBOM=y
+CONFIG_RISCV_ISA_ZICBOZ=y
+CONFIG_RISCV_SBI=y
+# CONFIG_RISCV_SBI_V01 is not set
+CONFIG_RISCV_TIMER=y
+CONFIG_RPS=y
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+# CONFIG_SERIAL_8250 is not set
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SGL_ALLOC=y
+CONFIG_SIFIVE_PLIC=y
+CONFIG_SIFLOWER_PHY=y
+CONFIG_SMP=y
+CONFIG_SOCK_RX_QUEUE_MAPPING=y
+# CONFIG_SOC_MICROCHIP_POLARFIRE is not set
+# CONFIG_SOC_SIFIVE is not set
+# CONFIG_SOC_STARFIVE is not set
+# CONFIG_SOC_VIRT is not set
+CONFIG_SOFTIRQ_ON_OWN_STACK=y
+CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_MEM=y
+CONFIG_SPI_SF21_QSPI=y
+# CONFIG_SQUASHFS_COMPILE_DECOMP_MULTI_PERCPU is not set
+CONFIG_SQUASHFS_COMPILE_DECOMP_SINGLE=y
+CONFIG_SQUASHFS_DECOMP_SINGLE=y
+CONFIG_SRAM=y
+CONFIG_SWIOTLB=y
+CONFIG_SWPHY=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+CONFIG_THREAD_INFO_IN_TASK=y
+CONFIG_THREAD_SIZE_ORDER=2
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TIMER_OF=y
+CONFIG_TIMER_PROBE=y
+CONFIG_TOOLCHAIN_HAS_V=y
+CONFIG_TOOLCHAIN_HAS_ZBB=y
+CONFIG_TOOLCHAIN_HAS_ZIHINTPAUSE=y
+CONFIG_TOOLCHAIN_NEEDS_EXPLICIT_ZICSR_ZIFENCEI=y
+CONFIG_TREE_RCU=y
+CONFIG_TREE_SRCU=y
+CONFIG_TUNE_GENERIC=y
+CONFIG_UBIFS_FS=y
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+# CONFIG_UBIFS_FS_LZO is not set
+# CONFIG_UBIFS_FS_ZLIB is not set
+CONFIG_UIMAGE_FIT_BLK=y
+CONFIG_USB_SUPPORT=y
+CONFIG_VMAP_STACK=y
+CONFIG_WATCHDOG_CORE=y
+CONFIG_XPS=y
+CONFIG_XXHASH=y
+CONFIG_ZONE_DMA32=y
+CONFIG_ZSTD_COMMON=y
+CONFIG_ZSTD_COMPRESS=y
+CONFIG_ZSTD_DECOMPRESS=y
--- /dev/null
+ARCH:=riscv64
+SUBTARGET:=sf21
+BOARDNAME:=Siflower SF21A6826/SF21H8898 based boards
+FEATURES+=fpu nand separate_ramdisk
+DEFAULT_PACKAGES += fitblk
+KERNELNAME:=Image
+
+define Target/Description
+ Build firmware images for Siflower SF21A6826/SF21H8898 based boards.
+endef