summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml1
-rw-r--r--Documentation/devicetree/bindings/memory-controllers/baikal,bt1-ddrc.yaml94
-rw-r--r--Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-common.yaml85
-rw-r--r--Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml57
-rw-r--r--Documentation/devicetree/bindings/mfd/baikal,bt1-boot-con.yaml112
-rw-r--r--Documentation/devicetree/bindings/mfd/baikal,bt1-sys-con.yaml133
-rw-r--r--Documentation/devicetree/bindings/mfd/syscon.yaml3
-rw-r--r--Documentation/devicetree/bindings/mips/cpus.yaml1
-rw-r--r--Documentation/devicetree/bindings/mtd/mtd-physmap.yaml4
-rw-r--r--Documentation/devicetree/bindings/mtd/mtd.yaml2
-rw-r--r--Documentation/devicetree/bindings/net/baikal,bt1-gmac.yaml169
-rw-r--r--Documentation/devicetree/bindings/net/baikal,bt1-xgmac.yaml212
-rw-r--r--Documentation/devicetree/bindings/net/ethernet-controller.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/marvell,88x2222.yaml89
-rw-r--r--Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml2
-rw-r--r--Documentation/devicetree/bindings/net/snps,dwmac.yaml27
-rw-r--r--Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml3
-rw-r--r--Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml37
-rw-r--r--Documentation/devicetree/bindings/sram/sram.yaml59
-rw-r--r--Documentation/networking/device_drivers/ethernet/stmicro/stmmac.rst4
-rw-r--r--MAINTAINERS36
-rw-r--r--arch/mips/Kbuild.platforms1
-rw-r--r--arch/mips/Kconfig67
-rw-r--r--arch/mips/Kconfig.debug3
-rw-r--r--arch/mips/boot/compressed/Makefile1
-rw-r--r--arch/mips/boot/compressed/uart-bt1.c2
-rw-r--r--arch/mips/boot/dts/Makefile1
-rw-r--r--arch/mips/boot/dts/baikal/Makefile14
-rw-r--r--arch/mips/boot/dts/baikal/bfk16p.dts214
-rw-r--r--arch/mips/boot/dts/baikal/bfk2.dts192
-rw-r--r--arch/mips/boot/dts/baikal/bfk3.dts149
-rw-r--r--arch/mips/boot/dts/baikal/bfkp.dtsi299
-rw-r--r--arch/mips/boot/dts/baikal/bt1-gen.dts334
-rw-r--r--arch/mips/boot/dts/baikal/bt1-oclk.dtsi87
-rw-r--r--arch/mips/boot/dts/baikal/bt1.dtsi1257
-rw-r--r--arch/mips/boot/dts/baikal/cb2-em406.dts21
-rw-r--r--arch/mips/boot/dts/baikal/em406.dtsi378
-rw-r--r--arch/mips/boot/dts/baikal/em416.dtsi359
-rw-r--r--arch/mips/boot/dts/baikal/kr-sfp.dtsi45
-rw-r--r--arch/mips/boot/dts/baikal/krkx4-mv-sfp.dtsi105
-rw-r--r--arch/mips/boot/dts/baikal/tf306.dts380
-rw-r--r--arch/mips/boot/dts/baikal/xa1-em406.dts94
-rw-r--r--arch/mips/boot/dts/baikal/xa1-em416.dts52
-rw-r--r--arch/mips/bt1/Kconfig145
-rw-r--r--arch/mips/bt1/Makefile9
-rw-r--r--arch/mips/bt1/Platform16
-rw-r--r--arch/mips/bt1/early_printk.c40
-rw-r--r--arch/mips/bt1/init.c281
-rw-r--r--arch/mips/bt1/irq.c59
-rw-r--r--arch/mips/configs/baikal_t1_defconfig480
-rw-r--r--arch/mips/configs/bfkp_defconfig467
-rw-r--r--arch/mips/configs/em406_defconfig458
-rw-r--r--arch/mips/configs/em416_defconfig447
-rw-r--r--arch/mips/configs/tf306_defconfig450
-rw-r--r--arch/mips/include/asm/dmi.h2
-rw-r--r--arch/mips/include/asm/io.h18
-rw-r--r--arch/mips/include/asm/mach-bt1/cpu-feature-overrides.h144
-rw-r--r--arch/mips/include/asm/mach-bt1/irq.h60
-rw-r--r--arch/mips/include/asm/mach-bt1/kernel-entry-init.h232
-rw-r--r--arch/mips/include/asm/mach-bt1/kmalloc.h8
-rw-r--r--arch/mips/include/asm/mach-bt1/memory.h118
-rw-r--r--arch/mips/include/asm/mach-bt1/spaces.h40
-rw-r--r--arch/mips/include/asm/mach-generic/spaces.h2
-rw-r--r--arch/mips/include/asm/mips-cm.h15
-rw-r--r--arch/mips/include/asm/video.h8
-rw-r--r--arch/mips/kernel/cps-vec.S14
-rw-r--r--arch/mips/kernel/relocate.c6
-rw-r--r--arch/mips/mm/dma-noncoherent.c19
-rw-r--r--arch/mips/mm/physaddr.c4
-rw-r--r--arch/mips/net/bpf_jit_comp.c191
-rw-r--r--arch/mips/net/bpf_jit_comp.h14
-rw-r--r--arch/mips/net/bpf_jit_comp32.c21
-rw-r--r--arch/mips/net/bpf_jit_comp64.c35
-rw-r--r--drivers/dma/dw-edma/dw-edma-v0-core.c4
-rw-r--r--drivers/dma/dw-edma/dw-edma-v0-regs.h3
-rw-r--r--drivers/dma/dw/core.c144
-rw-r--r--drivers/edac/Kconfig8
-rw-r--r--drivers/edac/Makefile1
-rw-r--r--drivers/edac/dmc520_edac.c4
-rw-r--r--drivers/edac/edac_mc.c137
-rw-r--r--drivers/edac/edac_mc.h4
-rw-r--r--drivers/edac/pasemi_edac.c5
-rw-r--r--drivers/edac/synopsys_edac.c3101
-rw-r--r--drivers/edac/zynq_edac.c506
-rw-r--r--drivers/hwmon/bt1-pvt.h4
-rw-r--r--drivers/i2c/busses/Kconfig1
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c67
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig11
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile7
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/chain_mode.c166
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h82
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/descs.h4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/descs_com.h201
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-bt1.c227
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c33
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c16
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c11
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c3
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100.h6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h65
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c468
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c58
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c26
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4.h38
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c130
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c149
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c84
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h55
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c15
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h18
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c63
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h133
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c222
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c210
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c113
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/enh_desc.c449
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.c78
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.h125
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/norm_desc.c203
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/ring_mode.c141
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h87
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c120
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_gpio.c519
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_gpio.h39
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c2884
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c7
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c89
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h13
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c82
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c38
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c14
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c331
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c5
-rw-r--r--drivers/net/pcs/Makefile2
-rw-r--r--drivers/net/pcs/pcs-xpcs-plat.c3
-rw-r--r--drivers/net/pcs/pcs-xpcs-pma.c131
-rw-r--r--drivers/net/pcs/pcs-xpcs.c278
-rw-r--r--drivers/net/pcs/pcs-xpcs.h38
-rw-r--r--drivers/net/phy/Kconfig14
-rw-r--r--drivers/net/phy/marvell-88x2222.c1472
-rw-r--r--drivers/net/phy/phy-core.c1
-rw-r--r--drivers/net/phy/phy_caps.c1
-rw-r--r--drivers/net/phy/phylink.c4
-rw-r--r--drivers/net/phy/realtek/realtek_main.c37
-rw-r--r--drivers/pci/controller/dwc/pcie-bt1.c171
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.c3
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.h7
-rw-r--r--drivers/spi/Kconfig27
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-dw-bt1.c331
-rw-r--r--drivers/spi/spi-dw-core.c1
-rw-r--r--drivers/tty/serial/8250/8250_dma.c2
-rw-r--r--drivers/tty/serial/8250/8250_dw.c10
-rw-r--r--drivers/tty/serial/8250/8250_early.c1
-rw-r--r--include/dt-bindings/net/mv-phy-88x2222.h25
-rw-r--r--include/dt-bindings/soc/bt1-boot-mode.h14
-rw-r--r--include/linux/edac.h6
-rw-r--r--include/linux/marvell_phy.h1
-rw-r--r--include/linux/pcs/pcs-xpcs.h2
-rw-r--r--include/linux/phy.h4
-rw-r--r--include/linux/stmmac.h8
164 files changed, 19802 insertions, 3632 deletions
diff --git a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml
index 18c0a7c18bc85..55e69f39d774b 100644
--- a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml
+++ b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml
@@ -21,6 +21,7 @@ properties:
- enum:
- renesas,r9a06g032-dma
- const: renesas,rzn1-dma
+ - const: baikal,bt1-dmac
"#dma-cells":
minimum: 3
diff --git a/Documentation/devicetree/bindings/memory-controllers/baikal,bt1-ddrc.yaml b/Documentation/devicetree/bindings/memory-controllers/baikal,bt1-ddrc.yaml
new file mode 100644
index 0000000000000..041acd768865a
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/baikal,bt1-ddrc.yaml
@@ -0,0 +1,94 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/memory-controllers/baikal,bt1-ddrc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 DDR Controller
+
+maintainers:
+ - Serge Semin <fancer.lancer@gmail.com>
+
+description:
+ Baikal-T1 DDRC is based on the DW uMCTL2 DDRC IP-core v2.51a with DDR2
+ and DDR3 protocol capability, 32-bit data bus + 8-bit ECC + up to 2
+ SDRAM ranks. There are individual IRQs for each ECC and DFI events.
+ The dedicated scrubber clock source is absent since it's fully
+ synchronous to the core clock.
+
+allOf:
+ - $ref: /schemas/memory-controllers/snps,dw-umctl2-common.yaml#
+
+properties:
+ compatible:
+ const: baikal,bt1-ddrc
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 4
+
+ interrupt-names:
+ items:
+ - const: dfi_e
+ - const: ecc_ce
+ - const: ecc_ue
+ - const: ecc_sbr
+
+ clocks:
+ maxItems: 3
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: aclk
+ - const: core
+
+ resets:
+ maxItems: 2
+
+ reset-names:
+ items:
+ - const: arst
+ - const: core
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - interrupts
+ - interrupt-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/mips-gic.h>
+ #include <dt-bindings/clock/bt1-ccu.h>
+ #include <dt-bindings/reset/bt1-ccu.h>
+
+ memory-controller@1f042000 {
+ compatible = "baikal,bt1-ddrc";
+ reg = <0x1f042000 0x1000>;
+
+ interrupts = <GIC_SHARED 96 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 97 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 98 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 99 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dfi_e", "ecc_ce", "ecc_ue", "ecc_sbr";
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_DDR_CLK>,
+ <&ccu_pll CCU_DDR_PLL>;
+ clock-names = "pclk", "aclk", "core";
+
+ resets = <&ccu_axi CCU_AXI_DDR_RST>,
+ <&ccu_sys CCU_SYS_DDR_INIT_RST>;
+ reset-names = "arst", "core";
+
+ phys = <mc_phy>;
+ phy-names = "pub";
+ };
+...
diff --git a/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-common.yaml b/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-common.yaml
new file mode 100644
index 0000000000000..279568274a0ff
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-common.yaml
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/memory-controllers/snps,dw-umctl2-common.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Synopsys DesignWare Universal Multi-Protocol Memory Controller
+
+maintainers:
+ - Krzysztof Kozlowski <krzk@kernel.org>
+ - Manish Narani <manish.narani@xilinx.com>
+ - Michal Simek <michal.simek@xilinx.com>
+
+description:
+ Synopsys DesignWare Enhanced uMCTL2 DDR Memory Controller is capable of
+ working with the memory devices supporting up to (LP)DDR4 protocol. It can
+ be equipped with SEC/DEC ECC feature if DRAM data bus width is either
+ 16-bits or 32-bits or 64-bits wide.
+
+select: false
+
+properties:
+ interrupts:
+ description:
+ DW uMCTL2 DDRC IP-core provides individual IRQ signal for each event":"
+ ECC Corrected Error, ECC Uncorrected Error, ECC Address Protection,
+ Scrubber-Done signal, DFI Parity/CRC Error. Some platforms may have the
+ signals merged before they reach the IRQ controller or have some of them
+ absent in case if the corresponding feature is unavailable/disabled.
+ minItems: 1
+ maxItems: 5
+
+ interrupt-names:
+ minItems: 1
+ maxItems: 5
+ oneOf:
+ - description: Common ECC CE/UE/Scrubber/DFI Errors IRQ
+ items:
+ - const: ecc
+ - description: Individual ECC CE/UE/Scrubber/DFI Errors IRQs
+ items:
+ enum: [ ecc_ce, ecc_ue, ecc_ap, ecc_sbr, dfi_e ]
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ description:
+ A standard set of the clock sources contains CSRs bus clock, AXI-ports
+ reference clock, DDRC core clock, Scrubber standalone clock
+ (synchronous to the DDRC clock).
+ minItems: 1
+ maxItems: 4
+
+ clock-names:
+ minItems: 1
+ maxItems: 4
+ items:
+ enum: [ pclk, aclk, core, sbr ]
+
+ resets:
+ description:
+ Each clock domain can have separate reset signal.
+ minItems: 1
+ maxItems: 4
+
+ reset-names:
+ minItems: 1
+ maxItems: 4
+ items:
+ enum: [ prst, arst, core, sbr ]
+
+ phys:
+ description:
+ Controller is equipped with PUB+PHY device to actually access SDRAM
+ chips.
+ maxItems: 1
+
+ phy-names:
+ items:
+ - const: pub
+
+additionalProperties: true
+
+...
diff --git a/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml b/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml
index 87ff9ee098f5e..80b25d2fa9746 100644
--- a/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml
+++ b/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml
@@ -20,6 +20,11 @@ description: |
controller. It has an optional SEC/DEC ECC support in 64- and 32-bits
bus width configurations.
+allOf:
+ - $ref: /schemas/memory-controllers/snps,dw-umctl2-common.yaml#
+
+# Please create a separate DT-schema for your DW uMCTL2 DDR controller
+# with more detailed properties definition.
properties:
compatible:
oneOf:
@@ -31,62 +36,12 @@ properties:
- description: Xilinx ZynqMP DDR controller v2.40a
const: xlnx,zynqmp-ddrc-2.40a
- interrupts:
- description:
- DW uMCTL2 DDRC IP-core provides individual IRQ signal for each event":"
- ECC Corrected Error, ECC Uncorrected Error, ECC Address Protection,
- Scrubber-Done signal, DFI Parity/CRC Error. Some platforms may have the
- signals merged before they reach the IRQ controller or have some of them
- absent in case if the corresponding feature is unavailable/disabled.
- minItems: 1
- maxItems: 5
-
- interrupt-names:
- minItems: 1
- maxItems: 5
- oneOf:
- - description: Common ECC CE/UE/Scrubber/DFI Errors IRQ
- items:
- - const: ecc
- - description: Individual ECC CE/UE/Scrubber/DFI Errors IRQs
- items:
- enum: [ ecc_ce, ecc_ue, ecc_ap, ecc_sbr, dfi_e ]
-
- reg:
- maxItems: 1
-
- clocks:
- description:
- A standard set of the clock sources contains CSRs bus clock, AXI-ports
- reference clock, DDRC core clock, Scrubber standalone clock
- (synchronous to the DDRC clock).
- minItems: 1
- maxItems: 4
-
- clock-names:
- minItems: 1
- maxItems: 4
- items:
- enum: [ pclk, aclk, core, sbr ]
-
- resets:
- description:
- Each clock domain can have separate reset signal.
- minItems: 1
- maxItems: 4
-
- reset-names:
- minItems: 1
- maxItems: 4
- items:
- enum: [ prst, arst, core, sbr ]
-
required:
- compatible
- reg
- interrupts
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/mfd/baikal,bt1-boot-con.yaml b/Documentation/devicetree/bindings/mfd/baikal,bt1-boot-con.yaml
new file mode 100644
index 0000000000000..0827e98c96a31
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/baikal,bt1-boot-con.yaml
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/baikal,bt1-boot-con.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 SoC Boot Controller
+
+maintainers:
+ - Serge Semin <fancer.lancer@gmail.com>
+
+description:
+ Baikal-T1 SoC is equipped with a Boot Controller which is responsible for
+ the SoC proper boot up procedure. Depending on the external pin state the
+ system can boot up either from the internal ROM or from the externally attached
+ SPI flash (at least of 16MB) or from the internal SRAM (the 64KB of executional
+ code is pre-loaded from the external SPI flash).
+
+allOf:
+ - $ref: /schemas/mfd/syscon.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: baikal,bt1-boot-con
+ - const: syscon
+ - const: simple-mfd
+
+ reg:
+ items:
+ - description:
+ Baikal-T1 Boot Controller CSR space. It doesn't include many
+ settings':' corrent boot mode, SPI controller access mux, SRAM
+ access mux and device ID.
+ - description: Mirrored first 4MB of the boot SPI flash memory
+
+ reg-names:
+ items:
+ - const: boot
+ - const: mirror
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 1
+
+ ranges: true
+
+ little-endian: true
+
+ mux-controller:
+ $ref: /schemas/mux/reg-mux.yaml#
+
+ rom@1bfc0000:
+ $ref: /schemas/mtd/mtd-physmap.yaml#
+
+ spi@1f040100:
+ $ref: /schemas/spi/snps,dw-apb-ssi.yaml#
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ syscon@1f04d000 {
+ compatible = "baikal,bt1-boot-con", "syscon", "simple-mfd";
+ reg = <0x1f040000 0x1000>,
+ <0x1fc00000 0x400000>;
+ reg-names = "boot", "mirror";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ little-endian;
+ reg-io-width = <4>;
+
+ mux-controller {
+ compatible = "mmio-mux";
+ #mux-control-cells = <1>;
+
+ mux-reg-masks = <0x0 0x100>, <0x4 0x1>;
+ idle-states = <0x1>, <0x0>;
+ };
+
+ rom@1bfc0000 {
+ compatible = "baikal,bt1-int-rom", "mtd-rom";
+ reg = <0x1bfc0000 0x10000>;
+
+ no-unaligned-direct-access;
+ bank-width = <4>;
+ };
+
+ spi@1f040100 {
+ compatible = "baikal,bt1-sys-ssi";
+ reg = <0x1f040100 0x900>,
+ <0x1c000000 0x1000000>;
+ reg-names = "config", "map";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mux-controls = <&boot_mux 0>;
+
+ clocks = <&ccu_sys 1>;
+ clock-names = "ssi_clk";
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/mfd/baikal,bt1-sys-con.yaml b/Documentation/devicetree/bindings/mfd/baikal,bt1-sys-con.yaml
new file mode 100644
index 0000000000000..4d36257d8676a
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/baikal,bt1-sys-con.yaml
@@ -0,0 +1,133 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/baikal,bt1-sys-con.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 SoC System Controller
+
+maintainers:
+ - Serge Semin <fancer.lancer@gmail.com>
+
+description:
+ Baikal-T1 SoC is equipped with a System Controller which is responsible for
+ the SoC components setting up and consists of the next sub-blocks':'
+ PLL/AXI-bus/System devices Clocks Control Units, P5600 CM2 L2-RAM controller,
+ CPU cores reboot flag, persistent across reboots register, indirectly
+ accessible DW APB I2C controller, Boot Controller with a pre-installed memory
+ mapped firmware and a resource limited DW APB SSI, which is also can be used
+ to transparently access an external SPI flash by means of a dedicated IO
+ memory region.
+
+allOf:
+ - $ref: /schemas/mfd/syscon.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: baikal,bt1-sys-con
+ - const: syscon
+ - const: simple-mfd
+
+ reg:
+ description:
+ Baikal-T1 System Controller CSR space. It includes CCU (Clock Control
+ Unit), L2 settings, Reboot flag and Reboot tolerant register, System I2C
+ controller CSRs.
+ maxItems: 1
+
+ reg-names:
+ const: sys
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 1
+
+ ranges: true
+
+ little-endian: true
+
+ clock-controller@1f04d000:
+ $ref: /schemas/clock/baikal,bt1-ccu-pll.yaml#
+
+ clock-controller@1f04d030:
+ $ref: /schemas/clock/baikal,bt1-ccu-div.yaml#
+
+ clock-controller@1f04d060:
+ $ref: /schemas/clock/baikal,bt1-ccu-div.yaml#
+
+ l2@1f04d028:
+ $ref: /schemas/cache/baikal,bt1-l2-ctl.yaml#
+
+ reboot:
+ $ref: /schemas/power/reset/syscon-reboot.yaml#
+
+ reboot-mode:
+ $ref: /schemas/power/reset/syscon-reboot-mode.yaml#
+
+ i2c@1f04d100:
+ $ref: /schemas/i2c/snps,designware-i2c.yaml#
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ syscon@1f04d000 {
+ compatible = "baikal,bt1-sys-con", "syscon", "simple-mfd";
+ reg = <0x1f04d000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ little-endian;
+ reg-io-width = <4>;
+
+ clock-controller@1f04d000 {
+ compatible = "baikal,bt1-ccu-pll";
+ reg = <0x1f04d000 0x028>;
+ #clock-cells = <1>;
+
+ clocks = <&clk25m>;
+ clock-names = "ref_clk";
+ };
+
+ clock-controller@1f04d030 {
+ compatible = "baikal,bt1-ccu-axi";
+ reg = <0x1f04d030 0x030>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+
+ clocks = <&ccu_pll 1>,
+ <&ccu_pll 2>,
+ <&ccu_pll 3>;
+ clock-names = "sata_clk", "pcie_clk", "eth_clk";
+ };
+
+ l2@1f04d028 {
+ compatible = "baikal,bt1-l2-ctl";
+ reg = <0x1f04d028 0x004>;
+
+ baikal,l2-ws-latency = <0>;
+ baikal,l2-tag-latency = <0>;
+ baikal,l2-data-latency = <1>;
+ };
+
+ i2c@1f04d100 {
+ compatible = "baikal,bt1-sys-i2c";
+ reg = <0x1f04d100 0x010>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <0 32 4>;
+
+ clocks = <&ccu_sys 1>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml
index e57add2bacd30..5df388e02ea3a 100644
--- a/Documentation/devicetree/bindings/mfd/syscon.yaml
+++ b/Documentation/devicetree/bindings/mfd/syscon.yaml
@@ -260,8 +260,7 @@ properties:
- const: microchip,mpfs-sysreg-scb
- const: syscon
- reg:
- maxItems: 1
+ reg: true
resets:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/mips/cpus.yaml b/Documentation/devicetree/bindings/mips/cpus.yaml
index d3677f53f1423..c20b81f228b18 100644
--- a/Documentation/devicetree/bindings/mips/cpus.yaml
+++ b/Documentation/devicetree/bindings/mips/cpus.yaml
@@ -24,6 +24,7 @@ properties:
- brcm,bmips5000
- brcm,bmips5200
- img,i6500
+ - img,p5600
- ingenic,xburst-fpu1.0-mxu1.1
- ingenic,xburst-fpu2.0-mxu2.0
- ingenic,xburst-mxu1.0
diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml b/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml
index a9ec3ca002c7d..b1e75623f5a96 100644
--- a/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml
+++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml
@@ -56,6 +56,10 @@ properties:
- cypress,cy7c1019dv33-10zsxi
- arm,vexpress-psram
- const: mtd-ram
+ - items:
+ - enum:
+ - baikal,bt1-int-rom
+ - const: mtd-rom
- enum:
- cfi-flash
- jedec-flash
diff --git a/Documentation/devicetree/bindings/mtd/mtd.yaml b/Documentation/devicetree/bindings/mtd/mtd.yaml
index 5a2d06c96c0d1..e8170a88f3d04 100644
--- a/Documentation/devicetree/bindings/mtd/mtd.yaml
+++ b/Documentation/devicetree/bindings/mtd/mtd.yaml
@@ -14,7 +14,7 @@ select: false
properties:
$nodename:
- pattern: "^(flash|.*sram|nand)(@.*)?$"
+ pattern: "^(flash|rom|sram-controller|.*sram|nand)(@.*)?$"
label:
description:
diff --git a/Documentation/devicetree/bindings/net/baikal,bt1-gmac.yaml b/Documentation/devicetree/bindings/net/baikal,bt1-gmac.yaml
new file mode 100644
index 0000000000000..7e29400964f08
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/baikal,bt1-gmac.yaml
@@ -0,0 +1,169 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/baikal,bt1-gmac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 DW GMAC Network Interface
+
+maintainers:
+ - Serge Semin <fancer.lancer@gmail.com>
+
+description:
+ Baikal-T1 is equipped with two DW GMAC v3.73a network interfaces. Each of
+ them doesn't have any on-SoC PHY attached, but instead exports RGMII
+ interface to connect any compatible physical layer transceiver.
+
+select:
+ properties:
+ compatible:
+ contains:
+ const: baikal,bt1-gmac
+
+ required:
+ - compatible
+
+allOf:
+ - $ref: "snps,dwmac.yaml#"
+
+properties:
+ compatible:
+ items:
+ - const: baikal,bt1-gmac
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ const: macirq
+
+ clocks:
+ minItems: 4
+ maxItems: 4
+
+ clock-names:
+ minItems: 4
+ maxItems: 4
+ contains:
+ enum:
+ - stmmaceth
+ - pclk
+ - tx
+ - ptp_ref
+
+ resets:
+ maxItems: 1
+
+ reset-names:
+ const: stmmaceth
+
+ ngpios:
+ description:
+ Baikal-T1 GMAC have been created with one GPI and one GPO ports
+ enabled. So there are total two GPIOs available.
+ const: 2
+
+ phy-mode:
+ description:
+ Bailal-T1 GMACs always add 2ns delay of TXC with respect to TXD.
+ So either PCB or PHY shall add the RXC delay.
+ enum: [rgmii-id, rxgmii-txid]
+
+ tx-internal-delay-ps:
+ description:
+ DW MAC Tx clocks generator has been designed to always add 2ns delay
+ of TXC with respect to TXD.
+ const: 2000
+
+ rx-fifo-depth:
+ const: 16384
+
+ tx-fifo-depth:
+ const: 16384
+
+ snps,perfect-filter-entries:
+ const: 8
+
+ snps,multicast-filter-bins:
+ const: 0
+
+ snps,data-width:
+ const: 16
+
+ stmmac-axi-config:
+ type: object
+
+ properties:
+ snps,wr_osr_lmt:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Maximum write outstanding requests is limited with 4
+ maximum: 3
+
+ snps,rd_osr_lmt:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Maximum read outstanding requests is limited with 4
+ maximum: 3
+
+ snps,blen:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description: AXI-bus burst length width is limited with just 4 bits
+ items:
+ enum: [16, 8, 4, 0]
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-names
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - phy-mode
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ ethernet@1f05e000 {
+ compatible = "baikal,bt1-gmac";
+ reg = <0x1f05e000 0x2000>;
+ #address-cells = <1>;
+ #size-cells = <2>;
+
+ interrupts = <72>;
+ interrupt-names = "macirq";
+
+ clocks = <&ccu_sys 1>, <&ccu_axi 3>, <&ccu_sys 2>, <&ccu_sys 3>;
+ clock-names = "pclk", "stmmaceth", "tx", "ptp_ref";
+
+ resets = <&ccu_axi 3>;
+ reset-names = "stmmaceth";
+
+ ngpios = <2>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ phy-mode = "rgmii-id";
+ tx-internal-delay-ps = <2000>;
+
+ rx-fifo-depth = <16384>;
+ tx-fifo-depth = <16384>;
+
+ snps,pbl = <32>;
+ snps,data-width = <16>;
+
+ snps,axi-config = <&gmac_axi_config>;
+
+ gmac_axi_config: stmmac-axi-config {
+ snps,wr_osr_lmt = <0x3>;
+ snps,rd_osr_lmt = <0x3>;
+ snps,blen = <0 0 0 0 16 8 4>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/net/baikal,bt1-xgmac.yaml b/Documentation/devicetree/bindings/net/baikal,bt1-xgmac.yaml
new file mode 100644
index 0000000000000..948a2aaaeb6e1
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/baikal,bt1-xgmac.yaml
@@ -0,0 +1,212 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/baikal,bt1-xgmac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 DW XGMAC Network Interface
+
+maintainers:
+ - Serge Semin <fancer.lancer@gmail.com>
+
+description:
+ Baikal-T1 is equipped with one DW XGMAC v2.11a network interfaces. It has
+ an DW XPCS v3.11b attached supporting the 10GBase-KR/KX4 external interfaces.
+
+select:
+ properties:
+ compatible:
+ contains:
+ const: baikal,bt1-xgmac
+
+ required:
+ - compatible
+
+allOf:
+ - $ref: "snps,dwmac.yaml#"
+
+properties:
+ compatible:
+ items:
+ - const: baikal,bt1-xgmac
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ minItems: 5
+ maxItems: 5
+
+ interrupt-names:
+ minItems: 5
+ maxItems: 5
+ items:
+ enum:
+ - macirq
+ - tx-queue-0
+ - tx-queue-1
+ - rx-queue-0
+ - rx-queue-1
+
+ clocks:
+ minItems: 4
+ maxItems: 4
+
+ clock-names:
+ minItems: 4
+ maxItems: 4
+ contains:
+ enum:
+ - stmmaceth
+ - pclk
+ - tx
+ - ptp_ref
+
+ resets:
+ maxItems: 1
+
+ reset-names:
+ const: stmmaceth
+
+ phy-mode:
+ description:
+ Bailal-T1 XGMAC supports 10Gbase-KR/KX4 interfaces via the always
+ attached XPCS block. The XGMAC and XPCS are connected with the XGMII
+ interface. So in case of the external interface auto-negotiation
+ implied by the platform use XGMII phy-mode.
+ enum: [xgmii, xaui, 10gbase-r, 10gbase-kr]
+
+ rx-fifo-depth:
+ const: 32768
+
+ tx-fifo-depth:
+ const: 32768
+
+ snps,perfect-filter-entries:
+ const: 8
+
+ snps,multicast-filter-bins:
+ const: 64
+
+ snps,tso: true
+
+ snps,data-width:
+ const: 16
+
+ stmmac-axi-config:
+ type: object
+
+ properties:
+ snps,wr_osr_lmt:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Maximum write outstanding requests is limited with 8
+ maximum: 7
+
+ snps,rd_osr_lmt:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Maximum read outstanding requests is limited with 8
+ maximum: 7
+
+ snps,blen:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description: AXI-bus burst length width is limited with just 4 bits
+ items:
+ enum: [16, 8, 4, 0]
+
+ rx-queues-config:
+ type: object
+
+ properties:
+ snps,rx-queues-to-use:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: At most four MTL Rx Queue supported with two DMA-channels
+ minimum: 1
+ maximum: 4
+
+ tx-queues-config:
+ type: object
+
+ properties:
+ snps,tx-queues-to-use:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: At most two MTL Tx Queue supported
+ minimum: 1
+ maximum: 2
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-names
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - phy-mode
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ ethernet@1f054000 {
+ compatible = "baikal,bt1-xgmac";
+ reg = <0x1f054000 0x4000>;
+
+ interrupts = <74>, <75>, <76>, <77>, <78>;
+ interrupt-names = "macirq", "tx-queue-0", "tx-queue-1",
+ "rx-queue-0", "rx-queue-1";
+
+ clocks = <&ccu_sys 4>, <&ccu_axi 4>, <&ccu_sys 5>, <&ccu_sys 6>;
+ clock-names = "pclk", "stmmaceth", "tx", "ptp_ref";
+
+ resets = <&ccu_axi 4>;
+ reset-names = "stmmaceth";
+
+ phy-mode = "xgmii";
+
+ rx-fifo-depth = <32768>;
+ tx-fifo-depth = <32768>;
+
+ snps,pbl = <32>;
+ snps,data-width = <16>;
+
+ snps,axi-config = <&gmac0_axi_config>;
+ snps,mtl-rx-config = <&xgmac_rxq_config>;
+ snps,mtl-tx-config = <&xgmac_txq_config>;
+
+ xgmac_axi_config: stmmac-axi-config {
+ snps,wr_osr_lmt = <0x7>;
+ snps,rd_osr_lmt = <0x7>;
+ snps,blen = <0 0 0 0 16 8 4>;
+ };
+
+ xgmac_rxq_config: rx-queues-config {
+ snps,rx-queues-to-use = <2>;
+ snps,rx-sched-sp;
+
+ queue0 {
+ snps,map-to-dma-channel = <0>;
+ };
+
+ queue1 {
+ snps,map-to-dma-channel = <1>;
+ };
+ };
+
+ xgmac_txq_config: tx-queues-config {
+ snps,tx-queues-to-use = <2>;
+ snps,tx-sched-sp;
+
+ queue0 {
+ snps,dcb-algorithm;
+ snps,priority = <0x1>;
+ };
+
+ queue1 {
+ snps,dcb-algorithm;
+ snps,priority = <0x2>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
index 1bafd687dcb18..06ab21669eed9 100644
--- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml
+++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
@@ -69,6 +69,7 @@ properties:
- trgmii
- 1000base-x
- 2500base-x
+ - 10gbase-x
- 5gbase-r
- rxaui
- xaui
diff --git a/Documentation/devicetree/bindings/net/marvell,88x2222.yaml b/Documentation/devicetree/bindings/net/marvell,88x2222.yaml
new file mode 100644
index 0000000000000..e34fd4bb39223
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/marvell,88x2222.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/marvell,88x2222.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell 88x2222 PHY
+
+maintainers:
+ - Andrew Lunn <andrew@lunn.ch>
+ - Heiner Kallweit <hkallweit1@gmail.com>
+ - Russell King <linux@armlinux.org.uk>
+
+description: |
+ The Marvell 88X2222 transceiver is a fully integrated single chip solution
+ providing end-to-end data transmission over fiber-optic networks as well as
+ Twinax® copper links. It is a 2-port device that performs all physical layer
+ functions associated with 10GBASE-R, and 1000BASE-X.
+
+ The line-side interface supports 2 ports of 10GBASE-R and 1000BASE-X. The
+ line side also supports Clause 73 AP Auto-Negotiation. The host-side
+ interface supports 4 ports of 10GBASE-R, RXAUI, 1000BASE-X, and 2 ports of
+ XAUI. Any port from the host side can be attached to any port on the line
+ side as long as the speeds match.
+
+allOf:
+ - $ref: ethernet-phy.yaml#
+
+properties:
+ compatible:
+ enum:
+ - ethernet-phy-id0141.0f10
+ - ethernet-phy-id0141.31b0
+
+ gpio-controller: true
+
+ '#gpio-cells':
+ const: 2
+
+ ngpios:
+ default: 2
+ minimum: 1
+ maximum: 12
+
+ gpio-line-names:
+ minItems: 1
+ maxItems: 12
+
+ gpio-reserved-ranges:
+ minItems: 1
+ maxItems: 12
+
+ interrupt-controller: true
+
+ '#interrupt-cells':
+ const: 2
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/net/mv-phy-88x2222.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-phy@c {
+ /* Only needed to make DT lint tools working. Do not copy/paste
+ * it into the real DTS files.
+ */
+ compatible = "ethernet-phy-id0141.0f10";
+ reg = <0x0c>;
+
+ interrupt-parent = <&pic>;
+ interrupts = <27 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-reserved-ranges = <MV_88X2222_LED0 1>,
+ <MV_88X2222_LED1 1>,
+ <MV_88X2222_SDA 1>,
+ <MV_88X2222_SCL 1>;
+
+ };
+ };
diff --git a/Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml b/Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml
index e77eec9ac9ee0..f83b2037bf17c 100644
--- a/Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml
+++ b/Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml
@@ -41,6 +41,8 @@ properties:
const: snps,dw-xpcs-gen5-10g
- description: Synopsys DesignWare XPCS with Consumer Gen5 12G PMA
const: snps,dw-xpcs-gen5-12g
+ - description: Baikal-T1 XPCS (DW XPCS with Consumer Gen5 10G PMA)
+ const: baikal,bt1-xpcs
reg:
items:
diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml
index 38bc34dc4f09b..b8186b9aa971f 100644
--- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml
+++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml
@@ -62,6 +62,8 @@ properties:
- amlogic,meson8m2-dwmac
- amlogic,meson-gxbb-dwmac
- amlogic,meson-axg-dwmac
+ - baikal,bt1-gmac
+ - baikal,bt1-xgmac
- ingenic,jz4775-mac
- ingenic,x1000-mac
- ingenic,x1600-mac
@@ -185,6 +187,23 @@ properties:
power-domains:
maxItems: 1
+ ngpios:
+ description:
+ Total number of GPIOs the MAC supports. The property shall include both
+ the GPI and GPO ports width.
+ minimum: 1
+ maximum: 32
+
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+
+ interrupt-controller: true
+
+ "#interrupt-cells":
+ const: 2
+
mac-mode:
$ref: ethernet-controller.yaml#/properties/phy-connection-type
description:
@@ -546,6 +565,13 @@ properties:
Enables the TSO feature otherwise it will be managed by MAC HW capability
register.
+ snps,data-width:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ System data bus width is limited with 8 and 16 bytes for DW
+ xGMAC and with 4, 8 and 16 for DW GMACs of recent versions.
+ enum: [4, 8, 16]
+
mdio:
$ref: mdio.yaml#
unevaluatedProperties: false
@@ -638,6 +664,7 @@ allOf:
- allwinner,sun8i-r40-gmac
- allwinner,sun8i-v3s-emac
- allwinner,sun50i-a64-emac
+ - baikal,bt1-xgmac
- loongson,ls2k-dwmac
- loongson,ls7a-dwmac
- ingenic,jz4775-mac
diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
index 6efe43089a74d..79f791dad6cc4 100644
--- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
+++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
@@ -43,6 +43,9 @@ properties:
compatible:
oneOf:
- items:
+ - const: baikal,bt1-uart
+ - const: snps,dw-apb-uart
+ - items:
- const: renesas,r9a06g032-uart
- const: renesas,rzn1-uart
- const: snps,dw-apb-uart
diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
index 8ebebcebca160..94af951f8cd02 100644
--- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
+++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
@@ -26,6 +26,26 @@ allOf:
properties:
compatible:
contains:
+ enum:
+ - baikal,bt1-sys-ssi
+ then:
+ properties:
+ mux-controls:
+ maxItems: 1
+ reg-names:
+ items:
+ - const: config
+ - const: map
+ required:
+ - mux-controls
+ - reg-names
+ else:
+ required:
+ - interrupts
+ - if:
+ properties:
+ compatible:
+ contains:
const: amd,pensando-elba-spi
then:
required:
@@ -60,6 +80,10 @@ properties:
const: intel,mountevans-imc-ssi
- description: AMD Pensando Elba SoC SPI Controller
const: amd,pensando-elba-spi
+ - description: Baikal-T1 SPI Controller
+ const: baikal,bt1-ssi
+ - description: Baikal-T1 System Boot SPI Controller
+ const: baikal,bt1-sys-ssi
- description: Canaan Kendryte K210 SoS SPI Controller
const: canaan,k210-spi
- description: Renesas RZ/N1 SPI Controller
@@ -151,7 +175,6 @@ required:
- "#address-cells"
- "#size-cells"
- clocks
- - interrupts
examples:
- |
@@ -172,4 +195,16 @@ examples:
rx-sample-delay-ns = <7>;
};
};
+ - |
+ spi@1f040100 {
+ compatible = "baikal,bt1-sys-ssi";
+ reg = <0x1f040100 0x900>,
+ <0x1c000000 0x1000000>;
+ reg-names = "config", "map";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ mux-controls = <&boot_mux>;
+ clocks = <&ccu_sys>;
+ clock-names = "ssi_clk";
+ };
...
diff --git a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml
index c451140962c86..72bfd4cd78df9 100644
--- a/Documentation/devicetree/bindings/sram/sram.yaml
+++ b/Documentation/devicetree/bindings/sram/sram.yaml
@@ -22,7 +22,7 @@ description: |+
properties:
$nodename:
- pattern: "^sram(@.*)?"
+ pattern: "^sram(-controller)?(@.*)?"
compatible:
contains:
@@ -37,6 +37,7 @@ properties:
- qcom,kaanapali-imem
- qcom,rpm-msg-ram
- rockchip,rk3288-pmu-sram
+ - baikal,bt1-sram
reg:
maxItems: 1
@@ -47,6 +48,15 @@ properties:
A list of phandle and clock specifier pair that controls the single
SRAM clock.
+ clock-names: true
+
+ resets:
+ description:
+ A list of phandle and reset specifier pair that controls the SRAM
+ state reset.
+
+ reset-names: true
+
"#address-cells":
const: 1
@@ -90,6 +100,7 @@ patternProperties:
- arm,juno-scp-shmem
- arm,scmi-shmem
- arm,scp-shmem
+ - baikal,bt1-boot-sram
- qcom,pil-reloc-info
- renesas,smp-sram
- rockchip,rk3066-smp-sram
@@ -144,21 +155,37 @@ required:
- compatible
- reg
-if:
- not:
- properties:
- compatible:
- contains:
- enum:
- - qcom,rpm-msg-ram
- - rockchip,rk3288-pmu-sram
-then:
- required:
- - "#address-cells"
- - "#size-cells"
- - ranges
-
-additionalProperties: false
+allOf:
+ - $ref: /schemas/mux/mux-consumer.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: baikal,bt1-sram
+ then:
+ properties:
+ mux-controls:
+ maxItems: 1
+ required:
+ - mux-controls
+ else:
+ properties:
+ mux-controls: false
+ - if:
+ properties:
+ compatible:
+ not:
+ contains:
+ enum:
+ - qcom,rpm-msg-ram
+ - rockchip,rk3288-pmu-sram
+ then:
+ required:
+ - "#address-cells"
+ - "#size-cells"
+ - ranges
+
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/networking/device_drivers/ethernet/stmicro/stmmac.rst b/Documentation/networking/device_drivers/ethernet/stmicro/stmmac.rst
index 5d46e50361294..c0e6f1e7538c9 100644
--- a/Documentation/networking/device_drivers/ethernet/stmicro/stmmac.rst
+++ b/Documentation/networking/device_drivers/ethernet/stmicro/stmmac.rst
@@ -495,6 +495,10 @@ is used to configure the AMBA bridge to generate more efficient STBus traffic::
int has_xgmac;
+37) Total number of supported GPIOs::
+
+ u32 ngpios;
+
::
}
diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e12fe1f0..25294cf88d8ce 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3788,6 +3788,7 @@ F: arch/arm/mach-zynq/
F: drivers/clocksource/timer-cadence-ttc.c
F: drivers/cpuidle/cpuidle-zynq.c
F: drivers/edac/synopsys_edac.c
+F: drivers/edac/zynq_edac.c
F: drivers/i2c/busses/i2c-cadence.c
F: drivers/i2c/busses/i2c-xiic.c
F: drivers/mmc/host/sdhci-of-arasan.c
@@ -4412,6 +4413,14 @@ F: drivers/video/backlight/
F: include/linux/backlight.h
F: include/linux/pwm_backlight.h
+BAIKAL-T1 PVT HARDWARE MONITOR DRIVER
+M: Serge Semin <fancer.lancer@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
+F: Documentation/hwmon/bt1-pvt.rst
+F: drivers/hwmon/bt1-pvt.[ch]
+
BARCO P50 GPIO DRIVER
M: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
M: Peter Korsgaard <peter.korsgaard@barco.com>
@@ -17677,6 +17686,22 @@ F: drivers/platform/mips/
F: include/dt-bindings/mips/
F: include/linux/platform_data/pic32.h
+MIPS BAIKAL-T1 PLATFORM
+M: Serge Semin <fancer.lancer@gmail.com>
+L: linux-mips@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/bus/baikal,bt1-*.yaml
+F: Documentation/devicetree/bindings/clock/baikal,bt1-*.yaml
+F: Documentation/devicetree/bindings/memory-controllers/baikal,bt1-ddrc.yaml
+F: Documentation/devicetree/bindings/mfd/baikal,bt1-*.yaml
+F: arch/mips/baikal-t1/
+F: arch/mips/boot/dts/baikal-t1/
+F: arch/mips/include/asm/mach-baikal-t1/
+F: drivers/bus/bt1-*.c
+F: drivers/clk/baikal-t1/
+F: drivers/memory/bt1-l2-ctl.c
+F: drivers/mtd/maps/physmap-bt1-rom.[ch]
+
MIPS BOSTON DEVELOPMENT BOARD
M: Paul Burton <paulburton@kernel.org>
L: linux-mips@vger.kernel.org
@@ -20297,8 +20322,7 @@ M: Jingoo Han <jingoohan1@gmail.com>
M: Manivannan Sadhasivam <mani@kernel.org>
L: linux-pci@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml
-F: Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml
+F: Documentation/devicetree/bindings/pci/snps,dw-pcie*.yaml
F: drivers/pci/controller/dwc/*designware*
F: include/linux/pcie-dwc.h
@@ -20482,6 +20506,13 @@ S: Maintained
F: Documentation/devicetree/bindings/pci/axis,artpec*
F: drivers/pci/controller/dwc/*artpec*
+PCIE DRIVER FOR BAIKAL-T1
+M: Serge Semin <fancer.lancer@gmail.com>
+L: linux-pci@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml
+F: drivers/pci/controller/dwc/pcie-bt1.c
+
PCIE DRIVER FOR CAVIUM THUNDERX
M: Robert Richter <rric@kernel.org>
L: linux-pci@vger.kernel.org
@@ -25629,6 +25660,7 @@ F: drivers/net/ethernet/synopsys/
SYNOPSYS DESIGNWARE ETHERNET XPCS DRIVER
L: netdev@vger.kernel.org
S: Orphan
+F: drivers/net/pcs/pcs-xpcs-pma.c
F: drivers/net/pcs/pcs-xpcs.c
F: drivers/net/pcs/pcs-xpcs.h
F: include/linux/pcs/pcs-xpcs.h
diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms
index 41a00fa860c18..c3c49e5e14785 100644
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -4,6 +4,7 @@
platform-$(CONFIG_MIPS_ALCHEMY) += alchemy/
platform-$(CONFIG_ATH25) += ath25/
platform-$(CONFIG_ATH79) += ath79/
+platform-$(CONFIG_MIPS_BAIKAL_T1) += bt1/
platform-$(CONFIG_BCM47XX) += bcm47xx/
platform-$(CONFIG_BCM63XX) += bcm63xx/
platform-$(CONFIG_BMIPS_GENERIC) += bmips/
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index e48b62b4dc489..ce39ba19afe10 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -555,6 +555,54 @@ config MACH_LOONGSON64
and Loongson-2F which will be removed), developed by the Institute
of Computing Technology (ICT), Chinese Academy of Sciences (CAS).
+config MIPS_BAIKAL_T1
+ bool "MIPS Baikal-T1 SoC"
+ imply MIPS_CPS
+ select BOOT_ELF32
+ select BOOT_RAW
+ select USE_OF
+ select GENERIC_ISA_DMA
+ select DMA_NONCOHERENT
+ select SWIOTLB if ARCH_DMA_ADDR_T_64BIT
+ select SCHED_HRTICK
+ select HAVE_PCI
+ select PCI_DRIVERS_GENERIC
+ select COMMON_CLK
+ select ARCH_HAS_RESET_CONTROLLER
+ select UHI_BOOT
+ select MIPS_CPU_SCACHE
+ select IRQ_MIPS_CPU
+ select MIPS_GIC
+ select CLKSRC_MIPS_GIC
+ select CEVT_R4K
+ select CSRC_R4K
+ select HARDIRQS_SW_RESEND
+ select DW_APB_TIMER_OF
+ select MIPS_EXTERNAL_TIMER
+ select GENERIC_CLOCKEVENTS_MIN_ADJUST
+ select SMP_UP if SMP
+ select EDAC_SUPPORT
+ select EDAC_ATOMIC_SCRUB
+ select SOC_BUS
+ select STRONG_UC_ORDERING
+ select SYS_SUPPORTS_MIPS_CPS
+ select SYS_HAS_CPU_MIPS32_R2
+ select SYS_HAS_CPU_MIPS32_R3_5
+ select SYS_HAS_CPU_MIPS32_R5
+ select SYS_HAS_CPU_P5600
+ select SYS_HAS_EARLY_PRINTK
+ select SYS_SUPPORTS_LITTLE_ENDIAN
+ select SYS_SUPPORTS_HIGHMEM
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select SYS_SUPPORTS_RELOCATABLE
+ select SYS_SUPPORTS_ZBOOT
+ select SYS_SUPPORTS_ZBOOT_UART_PROM
+ select CPU_MIPSR2_IRQ_VI
+ select CPU_MIPSR2_IRQ_EI
+ select MIPS_L1_CACHE_SHIFT_5
+ help
+ This enables support of Baikal Electronics Baikal-T1 SoC platform.
+
config MIPS_MALTA
bool "MIPS Malta board"
select ARCH_MAY_HAVE_PC_FDC
@@ -1064,6 +1112,7 @@ source "arch/mips/ath79/Kconfig"
source "arch/mips/bcm47xx/Kconfig"
source "arch/mips/bcm63xx/Kconfig"
source "arch/mips/bmips/Kconfig"
+source "arch/mips/bt1/Kconfig"
source "arch/mips/econet/Kconfig"
source "arch/mips/generic/Kconfig"
source "arch/mips/ingenic/Kconfig"
@@ -1453,6 +1502,7 @@ config CPU_MIPS32_R5
bool "MIPS32 Release 5"
depends on SYS_HAS_CPU_MIPS32_R5
select CPU_HAS_PREFETCH
+ select CPU_NO_LOAD_STORE_LR
select CPU_SUPPORTS_32BIT_KERNEL
select CPU_SUPPORTS_HIGHMEM
select CPU_SUPPORTS_MSA
@@ -1519,6 +1569,7 @@ config CPU_MIPS64_R5
bool "MIPS64 Release 5"
depends on SYS_HAS_CPU_MIPS64_R5
select CPU_HAS_PREFETCH
+ select CPU_NO_LOAD_STORE_LR
select CPU_SUPPORTS_32BIT_KERNEL
select CPU_SUPPORTS_64BIT_KERNEL
select CPU_SUPPORTS_HIGHMEM
@@ -1554,6 +1605,7 @@ config CPU_P5600
bool "MIPS Warrior P5600"
depends on SYS_HAS_CPU_P5600
select CPU_HAS_PREFETCH
+ select CPU_NO_LOAD_STORE_LR
select CPU_SUPPORTS_32BIT_KERNEL
select CPU_SUPPORTS_HIGHMEM
select CPU_SUPPORTS_MSA
@@ -1783,7 +1835,6 @@ config CPU_MIPS32_R5_FEATURES
config CPU_MIPS32_R5_XPA
bool "Extended Physical Addressing (XPA)"
depends on CPU_MIPS32_R5_FEATURES
- depends on !EVA
depends on !PAGE_SIZE_4KB
depends on SYS_SUPPORTS_HIGHMEM
select XPA
@@ -1986,6 +2037,14 @@ config WEAK_ORDERING
#
config WEAK_REORDERING_BEYOND_LLSC
bool
+
+#
+# CPU may not reorder reads and writes R->R, R->W, W->R, W->W within Uncached
+# Cacheability and Coherency Attribute (CCA=2)
+#
+config STRONG_UC_ORDERING
+ bool
+
endmenu
#
@@ -2578,7 +2637,7 @@ config WAR_MIPS34K_MISSED_ITLB
#
config HIGHMEM
bool "High Memory Support"
- depends on 32BIT && CPU_SUPPORTS_HIGHMEM && SYS_SUPPORTS_HIGHMEM && !CPU_MIPS32_3_5_EVA
+ depends on 32BIT && CPU_SUPPORTS_HIGHMEM && SYS_SUPPORTS_HIGHMEM
select KMAP_LOCAL
config CPU_SUPPORTS_HIGHMEM
@@ -2703,9 +2762,9 @@ config HW_PERF_EVENTS
config DMI
bool "Enable DMI scanning"
- depends on MACH_LOONGSON64
+ depends on MACH_LOONGSON64 || MIPS_BAIKAL_T1
select DMI_SCAN_MACHINE_NON_EFI_FALLBACK
- default y
+ default MACH_LOONGSON64
help
Enabled scanning of DMI to identify machine quirks. Say Y
here unless you have verified that your setup is not
diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug
index f4ae7900fcd35..85bf874f5b93f 100644
--- a/arch/mips/Kconfig.debug
+++ b/arch/mips/Kconfig.debug
@@ -138,6 +138,7 @@ config MIPS_CPS_NS16550
config MIPS_CPS_NS16550_BASE
hex "UART Base Address"
default 0x1b0003f8 if MIPS_MALTA
+ default 0x1f04a000 if MIPS_BAIKAL_T1
default 0
help
The base address of the ns16550 compatible UART on which to output
@@ -147,6 +148,7 @@ config MIPS_CPS_NS16550_BASE
config MIPS_CPS_NS16550_SHIFT
int "UART Register Shift"
+ default 2 if MIPS_BAIKAL_T1
default 0
help
The number of bits to shift ns16550 register indices by in order to
@@ -155,6 +157,7 @@ config MIPS_CPS_NS16550_SHIFT
config MIPS_CPS_NS16550_WIDTH
int "UART Register Width"
+ default 4 if MIPS_BAIKAL_T1
default 1
help
ns16550 registers width. UART registers IO access methods will be
diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile
index e0b8ec9a95162..e5fca5ba890a3 100644
--- a/arch/mips/boot/compressed/Makefile
+++ b/arch/mips/boot/compressed/Makefile
@@ -42,6 +42,7 @@ vmlinuzobjs-$(CONFIG_DEBUG_ZBOOT) += $(obj)/dbg.o
vmlinuzobjs-$(CONFIG_SYS_SUPPORTS_ZBOOT_UART16550) += $(obj)/uart-16550.o
vmlinuzobjs-$(CONFIG_SYS_SUPPORTS_ZBOOT_UART_PROM) += $(obj)/uart-prom.o
vmlinuzobjs-$(CONFIG_MIPS_ALCHEMY) += $(obj)/uart-alchemy.o
+vmlinuzobjs-$(CONFIG_MIPS_BAIKAL_T1) += $(obj)/uart-bt1.o
vmlinuzobjs-$(CONFIG_ATH79) += $(obj)/uart-ath79.o
endif
diff --git a/arch/mips/boot/compressed/uart-bt1.c b/arch/mips/boot/compressed/uart-bt1.c
new file mode 100644
index 0000000000000..a580b561f0d1e
--- /dev/null
+++ b/arch/mips/boot/compressed/uart-bt1.c
@@ -0,0 +1,2 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "../../bt1/early_printk.c"
diff --git a/arch/mips/boot/dts/Makefile b/arch/mips/boot/dts/Makefile
index 36c5e4c6e6406..c93a25ad5e11d 100644
--- a/arch/mips/boot/dts/Makefile
+++ b/arch/mips/boot/dts/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+subdir-y += baikal
subdir-y += brcm
subdir-y += cavium-octeon
subdir-y += econet
diff --git a/arch/mips/boot/dts/baikal/Makefile b/arch/mips/boot/dts/baikal/Makefile
new file mode 100644
index 0000000000000..b081f15086dcc
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+#
+# Baikal-T1 DTBs makefile
+#
+dtb-$(CONFIG_BT1_DTB_ALL) += bt1-gen.dtb bfk16p.dtb bfk2.dtb bfk3.dtb \
+ xa1-em406.dtb cb2-em406.dtb xa1-em416.dtb \
+ tf306.dtb
+dtb-$(CONFIG_BT1_DTB_GENERIC) += bt1-gen.dtb
+dtb-$(CONFIG_BT1_DTB_BFKP) += bfk16p.dtb bfk2.dtb bfk3.dtb
+dtb-$(CONFIG_BT1_DTB_EM406) += xa1-em406.dtb cb2-em406.dtb
+dtb-$(CONFIG_BT1_DTB_EM416) += xa1-em416.dtb
+dtb-$(CONFIG_BT1_DTB_TF306) += tf306.dtb
diff --git a/arch/mips/boot/dts/baikal/bfk16p.dts b/arch/mips/boot/dts/baikal/bfk16p.dts
new file mode 100644
index 0000000000000..61fffe567ca9e
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/bfk16p.dts
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal Electronics BFK v1.6+ evaluation board device tree
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "bfkp.dtsi"
+
+/ {
+ model = "Baikal Electronics BFK v1.6+ Evaluation Board";
+ compatible = "baikal,bfk16p", "baikal,bt1";
+
+ aliases {
+ spd0 = &spd;
+ mtd2 = &test_flash10;
+ mtd3 = &test_flash11;
+ };
+
+ memory {
+ /*
+ * Soldered 8GB RAM +ECC (DD21..DD30):
+ * low memory - 128MB, high memory - 8GB-256MB.
+ */
+ device_type = "memory";
+ reg = <0 0x00000000 0 0x08000000>,
+ <0 0x20000000 0x1 0xf0000000>;
+ };
+
+ clocks {
+ usb_hub_clk: clock-oscillator-usb-hub {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "usbhub24m";
+ };
+ };
+
+ /*
+ * Use XP23 to set bootcfg[1:0] by installing the jumpers:
+ * 7-8: b00 - ROM mode
+ * 1-2 7-8: b01 - SRAM mode
+ * default: b10 - Flash mode
+ */
+};
+
+&spi0 {
+ /*
+ * CS0 bus is connected to XP19 and DD71 (N74CBTLV3 NO pins) which SEL
+ * can be tied to GND by J41 jumper redirecting bus to XP20 and BMC
+ */
+};
+
+&port0 {
+ gpio-line-names = "XP27:4", "XP26:10", "XP26:8", "XP27:2", "XP25:1",
+ "XP26:2", "XP26:4", "XP25:4", "XP26:5", "XP29:11",
+ "XP29:9", "XP29:7", "XP25:6", "nCPU_RDY", "XP26:7",
+ "XP25:9", "nRTC_IRQ", "XP25:5", "XP25:3", "XP25:7",
+ "XP26:1", "XP26:3", "XP25:10", "XP27:3", "XP26:6",
+ "XP27:6", "XP27:1", "XP29:12", "XP25:8", "XP26:9",
+ "XP27:5", "nSD_CD";
+};
+
+&port1 {
+ gpio-line-names = "nBOOT_SS_DIS", "", "";
+};
+
+&i2c1 {
+ codec: audio-codec@18 {
+ compatible = "ti,tlv320dac3101";
+ reg = <0x18>;
+ };
+
+ /* Connected to XP17 pins SDA->3, SCL->4 */
+
+ /* Connected to XP29 pins SDA->5, SCL->3 */
+};
+
+&i2c2 {
+ /* EEPROM is empty by default */
+ spd: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+
+ pagesize = <8>;
+ };
+
+ fw: eeprom@51 {
+ compatible = "atmel,24cs02";
+ reg = <0x51>;
+
+ pagesize = <8>;
+ };
+
+ rtc: rtc@56 {
+ compatible = "abracon,abeoz9";
+ reg = <0x56>;
+
+ interrupt-parent = <&port0>;
+ interrupts = <16 IRQ_TYPE_LEVEL_LOW>;
+
+ trickle-resistor-ohms = <5000>;
+ };
+
+ hub: usb-hub@58 {
+ compatible = "microchip,usb2422";
+ reg = <0x58>;
+
+ /* Configs are set by the bootloader */
+ skip-config;
+ };
+
+ /* Connected to XP15 pins SDA->3, SCL->4 */
+};
+
+&uart0 {
+ /* Connected to XS9 RS232:1 port */
+};
+
+&uart1 {
+ /* Connected to XS9 RS232:2 port */
+};
+
+&spi1 {
+ num-cs = <4>;
+
+ /*
+ * XP11 pins port switches between native CS0 and port0:2 GPIO.
+ * XP12 pins port switches between native CS1 and port0:28 GPIO.
+ * Install the 1-2 jumpers to activate the GPIO CS.
+ */
+ cs-gpios = <&port0 2 GPIO_ACTIVE_LOW>, <&port0 28 GPIO_ACTIVE_LOW>,
+ <0>, <0>;
+
+ /* Micron N25Q256A13EF */
+ test_flash10: flash@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+
+ spi-max-frequency = <25000000>;
+ m25p,fast-read;
+ };
+
+ /* Micron N25Q256A13EF */
+ test_flash11: flash@1 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <1>;
+
+ spi-max-frequency = <25000000>;
+ m25p,fast-read;
+ };
+
+ /* Won't work with native CS due to no high CS capability */
+ mmc: mmc@2 {
+ compatible = "mmc-spi-slot";
+ reg = <2>;
+
+ voltage-ranges = <3200 3400>;
+ spi-max-frequency = <25000000>;
+ /* Card-Detect GPIO */
+ gpios = <&port0 31 GPIO_ACTIVE_LOW>;
+ };
+
+ /* CS3 bus is connected to XP10 pins MOSI->1, MISO->3, CLK->5, CS3->7 */
+};
+
+&spi2 {
+ num-cs = <4>;
+
+ /*
+ * XP13 pins port switches between native CS0 and port0:17 GPIO.
+ * Install the 1-2 jumper to activate the GPIO CS.
+ */
+ cs-gpios = <&port0 17 GPIO_ACTIVE_LOW>, <0>, <0>, <0>;
+
+ spi_i2s: cpld@0 {
+ compatible = "baikal,spi-i2s-cpld";
+ reg = <0>;
+ };
+
+ /*
+ * CS1, CS2 and CS3 buses are connected to XP18 pins MOSI->1, MISO->3,
+ * CLK->5, CS1->7, CS2->8, CS3->6.
+ */
+};
+
+&xgmac {
+ /* Possible protocols: 10gbase-r only */
+ phy-mode = "10gbase-r";
+};
+
+&usb {
+ /* Microchip USB2422 */
+ usb-hub@0 {
+ compatible = "usb424,2422";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ clocks = <&usb_hub_clk>;
+ clock-names = "xtal";
+
+ /* Two USB type-A ports */
+ };
+};
diff --git a/arch/mips/boot/dts/baikal/bfk2.dts b/arch/mips/boot/dts/baikal/bfk2.dts
new file mode 100644
index 0000000000000..944a8adeca4f4
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/bfk2.dts
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal Electronics BFK v2.x evaluation board device tree
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "bfkp.dtsi"
+
+/ {
+ model = "Baikal Electronics BFK v2.x Evaluation Board";
+ compatible = "baikal,bfk2", "baikal,bt1";
+
+ aliases {
+ spd0 = &spd;
+ mtd2 = &test_flash10;
+ mtd3 = &test_flash11;
+ };
+
+ memory {
+ /*
+ * Assume at least 512MB of RAM detected in SO-DIMM (DIMM1):
+ * low memory - 128MB, high memory - 256MB.
+ */
+ device_type = "memory";
+ reg = <0 0x00000000 0 0x08000000>,
+ <0 0x20000000 0 0x10000000>;
+ };
+
+ /*
+ * Use J27/J28 to set bootcfg[1:0] by installing the jumpers:
+ * J28: b00 - ROM mode
+ * J27 J28: b01 - SRAM mode
+ * default: b10 - Flash mode
+ */
+};
+
+&i2c0 {
+ status = "okay";
+
+ /* Connected to J38 pins 1.8V->1, GND->2, SDA->3, SCL->4 */
+};
+
+&spi0 {
+ /*
+ * CS0 bus is connected to J22 and U44 (N74CBTLV3 NO pins) which SEL
+ * can be tied to GND by J41 jumper and BMC redirecting bus to J39 and BMC.
+ *
+ * Note DD71 OE can be tied to 3.3V by the J42 jumper disabling the
+ * multiplexer. But this seems useless seeing the Boot SPI-flash will
+ * be just fully unavailable.
+ *
+ * Note J45 must have 1-2 jumper to activate Boot SPI-flash chip-select.
+ * Installing 2-3 jumper there will redirect the chip-select to the Boot
+ * SD-card slot (J44) which is unusable due to both the SoC not being
+ * able to start from there and the SPI-controller native CS nature.
+ */
+};
+
+&port0 {
+ gpio-line-names = "", "", "", "", "nLED_GRN",
+ "nLED_BLU", "nLED_RED", "", "J26:6", "J13:11",
+ "J13:9", "J13:7", "J26:8", "J26:10", "J26:12",
+ "J26:14", "nRTC_IRQ", "", "nSD_CD2", "J26:5",
+ "J26:7", "J26:9", "J26:11", "", "J26:13",
+ "J26:15in", "J26:17", "J13:12", "", "",
+ "J26:16in", "nSD_CD1";
+};
+
+&port1 {
+ gpio-line-names = "J25:1", "J25:2", "J25:3";
+};
+
+&i2c1 {
+ /* Connected to J30 pins SDA->3, SCL->4 */
+
+ /* Connected to J13 pins SDA->5, SCL->3 */
+};
+
+&i2c2 {
+ /* EEPROM on SO-DIMM (DIMM1) */
+ spd: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+
+ pagesize = <8>;
+ };
+
+ fw: eeprom@54 {
+ compatible = "atmel,24cs04";
+ reg = <0x54>;
+
+ pagesize = <8>;
+ };
+
+ rtc: rtc@56 {
+ compatible = "abracon,abeoz9";
+ reg = <0x56>;
+
+ interrupt-parent = <&port0>;
+ interrupts = <16 IRQ_TYPE_LEVEL_LOW>;
+
+ trickle-resistor-ohms = <5000>;
+ };
+
+ /* Connected to J43 pins SDA->3, SCL->4 */
+};
+
+&uart0 {
+ /*
+ * Connected to U60 CP2105 SCI port through U59 (SN74CBTLV input pins)
+ * which SEL can be tied to GND by J52 jumper redirecting interface to
+ * J53 TX->2, RX->3.
+ */
+};
+
+&uart1 {
+ /*
+ * Connected to U60 CP2105 ECI port through U59 (SN74CBTLV input pins)
+ * which SEL can be tied to GND by J52 jumper redirecting interface to
+ * J54 TX->2, RX->3.
+ */
+};
+
+&spi1 {
+ num-cs = <4>;
+
+ /*
+ * J37 pins port switches between native CS0 and port0:23 GPIO.
+ * J34 pins port switches between native CS1 and port0:28 GPIO.
+ * J33 pins port switches between native CS2 and port0:29 GPIO.
+ * Install the 1-2 jumpers to activate the GPIO CS.
+ */
+ cs-gpios = <&port0 23 GPIO_ACTIVE_LOW>, <&port0 28 GPIO_ACTIVE_LOW>,
+ <&port0 29 GPIO_ACTIVE_LOW>, <0>;
+
+ /* Micron N25Q256A13EF */
+ test_flash10: flash@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+
+ spi-max-frequency = <25000000>;
+ m25p,fast-read;
+ };
+
+ /* Micron N25Q256A13EF */
+ test_flash11: flash@1 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <1>;
+
+ spi-max-frequency = <25000000>;
+ m25p,fast-read;
+ };
+
+ /* Won't work with native CS due to no high CS capability */
+ mmc: mmc@2 {
+ compatible = "mmc-spi-slot";
+ reg = <2>;
+
+ voltage-ranges = <3200 3400>;
+ spi-max-frequency = <25000000>;
+ /* Card-Detect GPIO */
+ gpios = <&port0 31 GPIO_ACTIVE_LOW>;
+ };
+
+ /* CS3 bus is connected to J29 pins MOSI->2, MISO->4, CLK->6, CS3->5 */
+};
+
+&spi2 {
+ num-cs = <4>;
+
+ /*
+ * J35 pins port switches between native CS0 and port0:17 GPIO.
+ * Install the 1-2 jumper to activate the GPIO CS.
+ */
+ cs-gpios = <&port1 2 GPIO_ACTIVE_LOW>, <0>, <0>, <0>;
+
+ /*
+ * CS1, CS2 and CS3 buses are connected to J49 pins MOSI->1, MISO->3,
+ * CLK->5, CS0->7, CS1->6, CS2|CS3->8. CS2|CS3 is determined by J50
+ * pins: 1-2 - CS2, 2-3 - CS3.
+ */
+};
diff --git a/arch/mips/boot/dts/baikal/bfk3.dts b/arch/mips/boot/dts/baikal/bfk3.dts
new file mode 100644
index 0000000000000..3fc0d9b4371bb
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/bfk3.dts
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal Electronics BFK v3.x evaluation board device tree
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "bfkp.dtsi"
+
+/ {
+ model = "Baikal Electronics BFK v3.x Evaluation Board";
+ compatible = "baikal,bfk3", "baikal,bt1";
+
+ aliases {
+ spd0 = &spd;
+ mtd2 = &test_flash11;
+ };
+
+ memory {
+ /*
+ * Assume at least 512MB of RAM detected in SO-DIMM (XS21):
+ * low memory - 128MB, high memory - 256MB.
+ */
+ device_type = "memory";
+ reg = <0 0x00000000 0 0x08000000>,
+ <0 0x20000000 0 0x10000000>;
+ };
+
+ /*
+ * Use XP9/XP8 to set bootcfg[1:0] by installing the jumpers:
+ * XP8: b00 - ROM mode
+ * XP9 XP8: b01 - SRAM mode
+ * default: b10 - Flash mode
+ */
+};
+
+&i2c0 {
+ status = "okay";
+
+ /* Connected to XP25 RPi I2C SDA, SCL (RPi 40 pinout) */
+};
+
+&spi0 {
+ /*
+ * CS0 bus is connected to DD1 (N74CBTLV3 NO pins) which SEL can be
+ * tied to GND by XP1 jumper and BMC redirecting bus to BMC.
+ *
+ * Note DD1 OE can be tied to 3.3V by the XP2 jumper disabling the
+ * multiplexer. This will redirect the Boot SPI-flash to XP12 MOSI->2
+ * MISO->4, CLK->6, CS0->5
+ */
+};
+
+&port0 {
+ gpio-line-names = "XP25:7", "XP25:11", "XP25:12", "XP25:13", "XP25:15",
+ "XP25:16", "XP25:18", "XP25:22", "XP25:29", "XP26:11",
+ "XP26:9", "XP26:7", "XP25:31", "XP25:32", "XP25:33",
+ "XP25:35", "XP25:36", "XP25:26", "XP25:37", "XP25:38",
+ "XP25:40", "XP27:1", "XP27:2", "XP27:3", "XP27:4",
+ "XP27:5", "XP27:6", "XP26:12", "XP27:7", "XP27:8",
+ "XP27:9", "XP27:10";
+};
+
+&i2c1 {
+ /* Connected to XP25 RPi reserved pins SDA->27, SCL->28 */
+
+ /* Connected to XP26 pins SDA->5, SCL->3 */
+};
+
+&i2c2 {
+ /* EEPROM on SO-DIMM (XS21) */
+ spd: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+
+ pagesize = <8>;
+ };
+
+ fw: eeprom@54 {
+ compatible = "atmel,24cs04";
+ reg = <0x54>;
+
+ pagesize = <8>;
+ };
+
+ rtc: rtc@56 {
+ compatible = "abracon,abeoz9";
+ reg = <0x56>;
+
+ trickle-resistor-ohms = <5000>;
+ };
+
+ /* Connected to XP28 pins SDA->3, SCL->4 */
+};
+
+&uart0 {
+ /* Connected to DD24 CP2105 SCI port */
+};
+
+&uart1 {
+ /*
+ * Connected to XP13 pins TXD->3, RXD->4
+ * Install 3-5 and 4-6 jumpers to redirect the interface to DD24
+ * (CP2105 ECI port).
+ * Install 1-3 and 2-4 jumpers to redirect the interface to XP25
+ * RPi UART pins TX->8, RX->10.
+ */
+};
+
+&spi1 {
+ num-cs = <2>;
+
+ /*
+ * XP20 pins port switches between native CS0 and port1:0 GPIO.
+ * XP21 pins port switches between native CS1 and port1:1 GPIO.
+ * Install the 1-2 jumpers to activate the GPIO CS.
+ */
+ cs-gpios = <&port1 0 GPIO_ACTIVE_LOW>, <&port1 1 GPIO_ACTIVE_LOW>;
+
+ /* CS0 bus is connected to XP14 pins MOSI->2, MISO->4, CLK->6, CS0->5 */
+
+ /* Micron N25Q256A13EF */
+ test_flash11: flash@1 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <1>;
+
+ spi-max-frequency = <25000000>;
+ m25p,fast-read;
+ };
+};
+
+&spi2 {
+ num-cs = <2>;
+
+ /*
+ * XP19 pins port switches between native CS0 and port1:2 GPIO.
+ * Install the 1-2 jumper to activate the GPIO CS.
+ */
+ cs-gpios = <&port1 2 GPIO_ACTIVE_LOW>, <&port0 17 GPIO_ACTIVE_LOW>;
+
+ /* CS0 and CS1 buses are connected to XP25 RPi SPI pins */
+};
diff --git a/arch/mips/boot/dts/baikal/bfkp.dtsi b/arch/mips/boot/dts/baikal/bfkp.dtsi
new file mode 100644
index 0000000000000..90a34585a8659
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/bfkp.dtsi
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal Electronics BFK+ evaluation board series common device tree
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "bt1.dtsi"
+
+/ {
+ chosen {
+ bootargs = "console=ttyS0,115200n8 earlycon maxcpus=2";
+ stdout-path = "serial0:115200n8";
+
+ /* Bootloader timer if TIMER config is enabled */
+ tick-timer = <&timer_dw0>;
+
+ /* Bootloader may use these props to pass the initrd image */
+ linux,initrd-start = <0 0>;
+ linux,initrd-end = <0 0>;
+ };
+
+ aliases {
+ mtd1 = &boot_flash;
+ };
+
+ clocks {
+ /*
+ * SATA/PCIe/xGMAC reference clocks are provided either by the
+ * IDT 5P49V5901 (BFK v1.6/v3.x) or by the Skyworks Si5342
+ * (BFK v2.x) clock generators which are out of the SoC reach
+ * and either initialized by the embedded BMC or pre-configured
+ * via the chips pin straps
+ */
+ xgmac_ref_clk: clock-oscillator-xgmac {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <156250000>;
+ clock-output-names = "xgmac156m";
+ };
+
+ pcie_ref_clk: clock-oscillator-pcie {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <100000000>;
+ clock-output-names = "pcie100m";
+ };
+
+ sata_ref_clk: clock-oscillator-sata {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <100000000>;
+ clock-output-names = "sata100m";
+ };
+
+ usb_phy_clk: clock-oscillator-usb-phy {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "usbphy24m";
+ };
+
+ gmac0_phy_clk: clock-oscillator-gmac0-phy {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "gmac0phy25m";
+ };
+
+ gmac1_phy_clk: clock-oscillator-gmac1-phy {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "gmac1phy25m";
+ };
+ };
+};
+
+&l2 {
+ status = "okay";
+};
+
+&int_rom {
+ status = "okay";
+};
+
+&spi0 {
+ num-cs = <1>;
+
+ status = "okay";
+
+ /* Micron N25Q128A11 */
+ boot_flash: flash@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+
+ spi-max-frequency = <25000000>;
+ m25p,fast-read;
+ };
+};
+
+&gpio0 {
+ status = "okay";
+};
+
+&gpio1 {
+ status = "okay";
+};
+
+&i2c1 {
+ status = "okay";
+
+ /* Board Management Controller (STM32F205) */
+ bmc: bmc@8 {
+ compatible = "baikal,bt1-bmc";
+ reg = <0x08>;
+ };
+};
+
+&i2c2 {
+ status = "okay";
+};
+
+&timer_dw0 {
+ status = "okay";
+};
+
+&timer_dw1 {
+ status = "okay";
+};
+
+&timer_dw2 {
+ status = "okay";
+};
+
+&uart0 {
+ status = "okay";
+};
+
+&uart1 {
+ status = "okay";
+};
+
+&wdt {
+ status = "okay";
+};
+
+&spi1 {
+ status = "okay";
+};
+
+&spi2 {
+ status = "okay";
+};
+
+&pvt {
+ status = "okay";
+};
+
+&efuse {
+ status = "okay";
+};
+
+&pcie {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_M_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_S_CLK>,
+ <&pcie_ref_clk>;
+ clock-names = "dbi", "mstr", "slv", "ref";
+
+ status = "okay";
+};
+
+&sram {
+ status = "okay";
+};
+
+&dma {
+ status = "okay";
+};
+
+&mc {
+ status = "okay";
+};
+
+&mc_phy {
+ status = "okay";
+};
+
+&sata {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_SATA_CLK>,
+ <&sata_ref_clk>;
+ clock-names = "pclk", "aclk", "ref";
+
+ status = "okay";
+};
+
+&sata0 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&sata1 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&xgmac {
+ mac-address = [ 00 20 13 ba 1c a1 ];
+
+ status = "okay";
+};
+
+&xgmac_pcs {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_sys CCU_SYS_XGMAC_REF_CLK>,
+ <&xgmac_ref_clk>;
+ clock-names = "csr", "core", "pad";
+
+ status = "okay";
+};
+
+&hwa {
+ status = "okay";
+};
+
+&gmac0 {
+ mac-address = [ 00 26 58 80 01 02 ];
+
+ phy-handle = <&gmac0_phy>;
+ managed = "in-band-status";
+
+ status = "okay";
+};
+
+&gmac0_mdio {
+ reset-gpios = <&gmac0 1 GPIO_ACTIVE_LOW>;
+ reset-delay-us = <10200>;
+ reset-post-delay-us = <1000>;
+
+ /* Micrel KSZ9031RNX */
+ gmac0_phy: ethernet-phy@3 {
+ compatible = "ethernet-phy-id0022.1620";
+ reg = <0x3>;
+
+ interrupt-parent = <&gmac0>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+
+ clocks = <&gmac0_phy_clk>;
+ clock-names = "ref";
+ };
+};
+
+&gmac1 {
+ mac-address = [ 00 26 58 80 01 03 ];
+
+ phy-handle = <&gmac1_phy>;
+ managed = "in-band-status";
+
+ status = "okay";
+};
+
+&gmac1_mdio {
+ reset-gpios = <&gmac1 1 GPIO_ACTIVE_LOW>;
+ reset-delay-us = <10200>;
+ reset-post-delay-us = <1000>;
+
+ /* Micrel KSZ9031RNX */
+ gmac1_phy: ethernet-phy@3 {
+ compatible = "ethernet-phy-id0022.1620";
+ reg = <0x3>;
+
+ interrupt-parent = <&gmac1>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+
+ clocks = <&gmac1_phy_clk>;
+ clock-names = "ref";
+ };
+};
+
+&usb {
+ status = "okay";
+
+ ulpi {
+ /* Microchip USB3315 */
+ phy {
+ clocks = <&usb_phy_clk>;
+ clock-names = "ref";
+ };
+ };
+};
diff --git a/arch/mips/boot/dts/baikal/bt1-gen.dts b/arch/mips/boot/dts/baikal/bt1-gen.dts
new file mode 100644
index 0000000000000..58f6d1e48d2de
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/bt1-gen.dts
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 generic platform device tree
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "bt1.dtsi"
+
+/ {
+ model = "Baikal-T1 Generic Platform";
+ compatible = "baikal,bt1";
+
+ chosen {
+ /*
+ * Note of having both EARLY_PRINTK and SERIAL_EARLYCON
+ * activated at the same time. If they both refer to the same
+ * device, you'll end up with duplicated log messages.
+ * Here by passing 'earlycon' to the kernel we'll activate it
+ * to parse the stdout-path property to find the early console
+ * device. System console will be then activated in accordance
+ * with it if 'console=' parameter isn't passed. Any of the
+ * following consoles are valid: ttyS{0,1}/uart{0,1} (which
+ * alias is serial{0,1}), early_fdc (CDMM-JTAG serial iface).
+ */
+ bootargs = "console=ttyS0,115200n8 earlycon maxcpus=2";
+ stdout-path = "serial0:115200n8";
+
+ /* Bootloader timer if TIMER config is enabled */
+ tick-timer = <&timer_dw0>;
+
+ /* It's implied that the bootloader updates the initrd address */
+ linux,initrd-start = <0 0>;
+ linux,initrd-end = <0 0>;
+ };
+
+ /*
+ * Declare SPD I2C EEPROM and MTD device with boot SPI-flash aliases.
+ * Former one will be used by the bootloader for the DDR3 controller
+ * initialization. The later device will be used as initial firmware
+ * storage and UID resource.
+ */
+ aliases {
+ spd0 = &spd;
+ mtd1 = &boot_flash;
+ };
+
+ memory {
+ /*
+ * Declare required low-memory and additional 256MB of high-
+ * memory, which due to the DW uMCTL2 controller specific setup
+ * nearly always exists as being remapped upper part of the
+ * first memory chip. Without low-level remapping that segment
+ * is hidden behind the MMIO region and isn't reachable.
+ * NOTE. For the reason of having MMIO above the very first
+ * 128MB of the low memory, the second 128MB of the physical
+ * memory is always unavailable as being hidden behind MMIO
+ * and non-remappable by DW uMCTL2.
+ */
+ device_type = "memory";
+ reg = <0 0x00000000 0 0x08000000>,
+ <0 0x20000000 0 0x10000000>;
+ };
+
+ /* Standard xGMAC/PCIe/SATA reference clocks setup */
+ clocks {
+ xgmac_ref_clk: clock-oscillator-xgmac {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <156250000>;
+ clock-output-names = "xgmac156m";
+ };
+
+ pcie_ref_clk: clock-oscillator-pcie {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <100000000>;
+ clock-output-names = "pcie100m";
+ };
+
+ sata_ref_clk: clock-oscillator-sata {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <100000000>;
+ clock-output-names = "sata100m";
+ };
+ };
+};
+
+&l2 {
+ status = "okay";
+};
+
+&i2c0 {
+ status = "okay";
+};
+
+&int_rom {
+ status = "okay";
+};
+
+&spi0 {
+ num-cs = <1>;
+
+ status = "okay";
+
+ /*
+ * Most likely an SPI-nor flash will be always installed on each
+ * device with Baikal-T1 SoC on board. There is no just better
+ * alternative to boot a normal system on that CPU.
+ * Note Baikal-T1 is able to transparently access up to 16MB flash,
+ * so the system bootloader size can not exceed that limit, but an
+ * attached SPI-flash can as long as it supports 3bytes addressing
+ * of the lowest partition.
+ */
+ boot_flash: flash@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+
+ spi-max-frequency = <25000000>;
+ m25p,fast-read;
+ };
+};
+
+&gpio0 {
+ status = "okay";
+};
+
+&gpio1 {
+ status = "okay";
+};
+
+&i2c1 {
+ status = "okay";
+};
+
+&i2c2 {
+ status = "okay";
+
+ /* For the devices with SO-DIMM slot */
+ spd: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+
+ pagesize = <8>;
+ };
+};
+
+&timer_dw0 {
+ status = "okay";
+};
+
+&timer_dw1 {
+ status = "okay";
+};
+
+&timer_dw2 {
+ status = "okay";
+};
+
+&uart0 {
+ status = "okay";
+};
+
+&uart1 {
+ status = "okay";
+};
+
+&wdt {
+ status = "okay";
+};
+
+&spi1 {
+ num-cs = <4>;
+
+ status = "okay";
+};
+
+&spi2 {
+ num-cs = <4>;
+
+ status = "okay";
+};
+
+&pvt {
+ status = "okay";
+};
+
+&efuse {
+ status = "okay";
+};
+
+&pcie {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_M_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_S_CLK>,
+ <&pcie_ref_clk>;
+ clock-names = "dbi", "mstr", "slv", "ref";
+
+
+ status = "okay";
+};
+
+&sram {
+ status = "okay";
+};
+
+&dma {
+ status = "okay";
+};
+
+&mc {
+ status = "okay";
+};
+
+&mc_phy {
+ status = "okay";
+};
+
+&sata {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_SATA_CLK>,
+ <&sata_ref_clk>;
+ clock-names = "pclk", "aclk", "ref";
+
+ status = "okay";
+};
+
+&sata0 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&sata1 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&xgmac {
+ mac-address = [ 00 20 13 ba 1c a1 ];
+
+ status = "okay";
+};
+
+&xgmac_pcs {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_sys CCU_SYS_XGMAC_REF_CLK>,
+ <&xgmac_ref_clk>;
+ clock-names = "csr", "core", "pad";
+
+ status = "okay";
+};
+
+&hwa {
+ status = "okay";
+};
+
+&gmac0 {
+ mac-address = [ 7a 72 6c 4a 7a 07 ];
+
+ phy-handle = <&gmac0_phy>;
+
+ status = "okay";
+};
+
+&gmac0_mdio {
+ /*
+ * By default use the GPO/GPI available on DW GMAC to reset the MDIO
+ * bus and to detect the PHY events. Most of the known Baikal-T1
+ * devices have been designed that way. Note using GPO for a particular
+ * PHY node reset won't work unless PHY address and PHY IDs (802.3
+ * clause 22) have been directly specified in the PHY sub-node.
+ */
+ reset-gpios = <&gmac0 1 GPIO_ACTIVE_LOW>;
+ reset-delay-us = <10000>;
+ reset-post-delay-us = <30000>;
+
+ /*
+ * We don't know actual PHY address on a generic device. Let the driver
+ * auto scan the MDIO bus looking for the IEEE 802.3 Clause 22
+ * compatible PHY.
+ */
+ gmac0_phy: ethernet-phy {
+ compatible = "ethernet-phy-ieee802.3-c22";
+
+ interrupt-parent = <&gmac0>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+ };
+};
+
+&gmac1 {
+ mac-address = [ 7a 72 6c 4a 7b 07 ];
+
+ phy-handle = <&gmac1_phy>;
+
+ status = "okay";
+};
+
+&gmac1_mdio {
+ /*
+ * By default use the GPO/GPI available on DW GMAC to reset the MDIO
+ * bus and to detect the PHY events. Most of the known Baikal-T1
+ * devices have been designed that way. Note using GPO for a particular
+ * PHY node reset won't work unless PHY address and PHY IDs (802.3
+ * clause 22) have been directly specified in the PHY sub-node.
+ */
+ reset-gpios = <&gmac1 1 GPIO_ACTIVE_LOW>;
+ reset-delay-us = <10000>;
+ reset-post-delay-us = <30000>;
+
+ /*
+ * We don't know actual PHY address on a generic device. Let the driver
+ * auto scan the MDIO bus looking for the IEEE 802.3 Clause 22
+ * compatible PHY.
+ */
+ gmac1_phy: ethernet-phy {
+ compatible = "ethernet-phy-ieee802.3-c22";
+
+ interrupt-parent = <&gmac1>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+ };
+};
+
+&usb {
+ status = "okay";
+};
diff --git a/arch/mips/boot/dts/baikal/bt1-oclk.dtsi b/arch/mips/boot/dts/baikal/bt1-oclk.dtsi
new file mode 100644
index 0000000000000..86da0f70777b5
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/bt1-oclk.dtsi
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 SoC overclocking device tree
+ */
+
+#include <dt-bindings/clock/bt1-ccu.h>
+
+/*
+ * WARNING! This file provides the Baikal-T1 SoC overclocking settings. Since
+ * the specified clock rates are officially unsupported there is no firm
+ * guarantee the system will stably work if they are applied. So use it at your
+ * own risk.
+ */
+
+&cpu_opp {
+ opp-1300000000 {
+ opp-hz = /bits/ 64 <1300000000>;
+ clock-latency-ns = <20000>;
+ turbo-mode;
+ };
+
+ opp-1400000000 {
+ opp-hz = /bits/ 64 <1400000000>;
+ clock-latency-ns = <20000>;
+ turbo-mode;
+ };
+
+ opp-1500000000 {
+ opp-hz = /bits/ 64 <1500000000>;
+ clock-latency-ns = <20000>;
+ turbo-mode;
+ };
+};
+
+/*
+ * In general the system is working well with the CSRs bus rate above 50MHz
+ * and up to 300MHz, but it hasn't been fully tested yet. For instance, DW DMA
+ * won't work well with APB clock being greater than 200 MHz. So if you mean to
+ * use the DMA-based communications over the I2C/UART/SPI interfaces don't
+ * exceed the 200MHz limit.
+ */
+&apb {
+ assigned-clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ assigned-clock-rates = <200000000>;
+};
+
+/*
+ * For this to work well the overclocked rates must be set on the fully
+ * disabled PCIe controller.
+ */
+&pcie {
+ assigned-clocks = <&ccu_axi CCU_AXI_PCIE_M_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_S_CLK>;
+ assigned-clock-rates = <600000000>, <600000000>;
+};
+
+&sram {
+ assigned-clocks = <&ccu_axi CCU_AXI_SRAM_CLK>;
+ assigned-clock-rates = <250000000>;
+};
+
+&sata {
+ assigned-clocks = <&ccu_axi CCU_AXI_SATA_CLK>;
+ assigned-clock-rates = <300000000>;
+};
+
+&xgmac {
+ assigned-clocks = <&ccu_axi CCU_AXI_XGMAC_CLK>;
+ assigned-clock-rates = <312500000>;
+};
+
+&gmac0 {
+ assigned-clocks = <&ccu_axi CCU_AXI_GMAC0_CLK>;
+ assigned-clock-rates = <250000000>;
+};
+
+&gmac1 {
+ assigned-clocks = <&ccu_axi CCU_AXI_GMAC1_CLK>;
+ assigned-clock-rates = <250000000>;
+};
+
+&usb {
+ assigned-clocks = <&ccu_axi CCU_AXI_USB_CLK>;
+ assigned-clock-rates = <300000000>;
+};
diff --git a/arch/mips/boot/dts/baikal/bt1.dtsi b/arch/mips/boot/dts/baikal/bt1.dtsi
new file mode 100644
index 0000000000000..f39884f8249a1
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/bt1.dtsi
@@ -0,0 +1,1257 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 SoC device tree
+ */
+
+#include <dt-bindings/dma/dw-dmac.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/mips-gic.h>
+#include <dt-bindings/thermal/thermal.h>
+#include <dt-bindings/ata/ahci.h>
+
+#include <dt-bindings/clock/bt1-ccu.h>
+#include <dt-bindings/reset/bt1-ccu.h>
+#include <dt-bindings/soc/bt1-boot-mode.h>
+
+/ {
+ model = "Baikal-T1 SoC";
+ compatible = "baikal,bt1";
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ interrupt-parent = <&gic>;
+
+ aliases {
+ serial0 = &uart0;
+ serial1 = &uart1;
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
+ i2c2 = &i2c2;
+ spi0 = &spi0;
+ spi1 = &spi1;
+ spi2 = &spi2;
+ mtd0 = &int_rom;
+ mc0 = &mc;
+ ethernet0 = &gmac0;
+ ethernet1 = &gmac1;
+ ethernet2 = &xgmac;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu-map {
+ cluster0 {
+ core0 {
+ cpu = <&cpu0>;
+ };
+
+ core1 {
+ cpu = <&cpu1>;
+ };
+ };
+ };
+
+ cpu0: cpu@0 {
+ compatible = "baikal,bt1", "img,p5600";
+ device_type = "cpu";
+ reg = <0x0>;
+ #cooling-cells = <2>;
+
+ clocks = <&ccu_pll CCU_CPU_PLL>, <&ccu_axi CCU_AXI_MAIN_CLK>;
+ clock-names = "cpu_clk", "bus_clk";
+
+ operating-points-v2 = <&cpu_opp>;
+ };
+
+ cpu1: cpu@1 {
+ compatible = "baikal,bt1", "img,p5600";
+ device_type = "cpu";
+ reg = <0x1>;
+ #cooling-cells = <2>;
+
+ clocks = <&ccu_pll CCU_CPU_PLL>, <&ccu_axi CCU_AXI_MAIN_CLK>;
+ clock-names = "cpu_clk", "bus_clk";
+
+ operating-points-v2 = <&cpu_opp>;
+ };
+ };
+
+ gic: gic@1bdc0000 {
+ compatible = "mti,gic";
+ reg = <0 0x1bdc0000 0 0x20000>;
+
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ mti,reserved-ipi-vectors = <108 4>;
+
+ timer_gic: timer {
+ compatible = "mti,gic-timer";
+
+ interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+
+ clocks = <&ccu_pll CCU_CPU_PLL>;
+ };
+
+ /* TODO Add GIC Watchdog support */
+ };
+
+ cpc: cpc@1bde0000 {
+ compatible = "mti,mips-cpc";
+ reg = <0 0x1bde0000 0 0x8000>;
+ };
+
+ cdmm: cdmm@1bde8000 {
+ compatible = "mti,mips-cdmm";
+ reg = <0 0x1bde8000 0 0x8000>;
+ };
+
+ /*
+ * FIXME TODO Update the mips-cm.c to get the based addresses
+ * from here as it's done in arch/mips/kernel/mips-cpc.c and in
+ * drivers/bus/mips_cdmm.c
+ * Note. This isn't that easy to do, since
+ * setup_arch()
+ * +-> cpu_probe();
+ * +-> mips_cm_probe();
+ * +-> prom_init();
+ * +-> ...
+ * +-> device_tree_init()
+ * +-> unflatten_and_copy_device_tree();
+ * +-> mips_cpc_probe();
+ * +-> ...
+ */
+ cm2: cm2@1fbf8000 {
+ compatible = "mti,mips-cm";
+ reg = <0 0x1fbf8000 0 0x8000>,
+ <0 0x1fbf0000 0 0x1000>;
+ reg-names = "gcr", "l2sync";
+ };
+
+ /*
+ * Note setting up too low CPU frequency may cause time-critical
+ * applications not working correctly. For instance in order to have
+ * the DW APB SSI memory interface (EEPROM-read and Tx-only) working
+ * correctly with the whole CPU clock range defined below we had to
+ * accordingly constraint the SPI bus speed.
+ */
+ cpu_opp: opp-table {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp-300000000 {
+ opp-hz = /bits/ 64 <300000000>;
+ clock-latency-ns = <20000>;
+ };
+
+ opp-400000000 {
+ opp-hz = /bits/ 64 <400000000>;
+ clock-latency-ns = <20000>;
+ };
+
+ opp-500000000 {
+ opp-hz = /bits/ 64 <500000000>;
+ clock-latency-ns = <20000>;
+ };
+
+ opp-600000000 {
+ opp-hz = /bits/ 64 <600000000>;
+ clock-latency-ns = <20000>;
+ };
+
+ opp-700000000 {
+ opp-hz = /bits/ 64 <700000000>;
+ clock-latency-ns = <20000>;
+ };
+
+ opp-800000000 {
+ opp-hz = /bits/ 64 <800000000>;
+ clock-latency-ns = <20000>;
+ };
+
+ opp-900000000 {
+ opp-hz = /bits/ 64 <900000000>;
+ clock-latency-ns = <20000>;
+ };
+
+ opp-1000000000 {
+ opp-hz = /bits/ 64 <1000000000>;
+ clock-latency-ns = <20000>;
+ };
+
+ opp-1100000000 {
+ opp-hz = /bits/ 64 <1100000000>;
+ clock-latency-ns = <20000>;
+ };
+
+ opp-1200000000 {
+ opp-hz = /bits/ 64 <1200000000>;
+ clock-latency-ns = <20000>;
+ };
+ };
+
+ thermal-zones {
+ cpu-thermal {
+ polling-delay-passive = <250>;
+ polling-delay = <1000>;
+
+ thermal-sensors = <&pvt>;
+
+ trips {
+ cpu_alert0: trip0 {
+ temperature = <80000>;
+ hysteresis = <2000>;
+ type = "active";
+ };
+
+ cpu_alert1: trip1 {
+ temperature = <90000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu_warn: trip2 {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "hot";
+ };
+
+ cpu_crit: trip3 {
+ temperature = <110000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+
+ cooling-maps {
+ map-alert1 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+ };
+ };
+
+ /* External fixed reference clocks */
+ clocks {
+ ref_clk: clock-oscillator-ref {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "ref25m";
+ };
+ };
+
+ apb: bus@1f059000 {
+ compatible = "baikal,bt1-apb", "simple-bus";
+ reg = <0 0x1f059000 0 0x1000>,
+ <0 0x1d000000 0 0x2040000>;
+ reg-names = "ehb", "nodev";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ ranges = <0x1bfc0000 0 0x1bfc0000 0x03c38000>,
+ <0x1fc00000 0 0x1fc00000 0x00400000>;
+
+ interrupts = <GIC_SHARED 16 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "pclk";
+
+ resets = <&ccu_sys CCU_SYS_APB_RST>;
+ reset-names = "prst";
+
+ syscon: syscon@1f04d000 {
+ compatible = "baikal,bt1-sys-con", "syscon", "simple-mfd";
+ reg = <0x1f04d000 0x1000>;
+ reg-names = "sys";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ little-endian;
+ reg-io-width = <4>;
+
+ ccu_pll: clock-controller@1f04d000 {
+ compatible = "baikal,bt1-ccu-pll";
+ reg = <0x1f04d000 0x028>;
+ #clock-cells = <1>;
+
+ clocks = <&ref_clk>;
+ clock-names = "ref_clk";
+ };
+
+ ccu_axi: clock-controller@1f04d030 {
+ compatible = "baikal,bt1-ccu-axi";
+ reg = <0x1f04d030 0x030>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+
+ clocks = <&ccu_pll CCU_SATA_PLL>,
+ <&ccu_pll CCU_PCIE_PLL>,
+ <&ccu_pll CCU_ETH_PLL>;
+ clock-names = "sata_clk", "pcie_clk", "eth_clk";
+ };
+
+ ccu_sys: clock-controller@1f04d060 {
+ compatible = "baikal,bt1-ccu-sys";
+ reg = <0x1f04d060 0x0a0>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+
+ clocks = <&ref_clk>,
+ <&ccu_pll CCU_SATA_PLL>,
+ <&ccu_pll CCU_PCIE_PLL>,
+ <&ccu_pll CCU_ETH_PLL>;
+ clock-names = "ref_clk", "sata_clk", "pcie_clk",
+ "eth_clk";
+ };
+
+ l2: l2@1f04d028 {
+ compatible = "baikal,bt1-l2-ctl";
+ reg = <0x1f04d028 0x004>;
+
+ baikal,l2-ws-latency = <0>;
+ baikal,l2-tag-latency = <0>;
+ baikal,l2-data-latency = <1>;
+
+ status = "disabled";
+ };
+
+ reboot {
+ compatible = "syscon-reboot";
+ offset = <0x118>;
+
+ mask = <0x1>;
+ value = <0x1>;
+ };
+
+ reboot-mode {
+ compatible = "syscon-reboot-mode";
+ offset = <0x154>;
+
+ mode-normal = <RCR_BOOT_NORMAL>;
+ mode-loader = <RCR_BOOT_LOADER>;
+ mode-recovery = <RCR_BOOT_RECOVERY>;
+ };
+
+ i2c0: i2c@1f04d100 {
+ compatible = "baikal,bt1-sys-i2c";
+ reg = <0x1f04d100 0x010>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 32 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-frequency = <400000>;
+
+ status = "disabled";
+ };
+ };
+
+ bootcon: syscon@1f040000 {
+ compatible = "baikal,bt1-boot-con", "syscon", "simple-mfd";
+ reg = <0x1f040000 0x1000>,
+ <0x1fc00000 0x400000>;
+ reg-names = "boot", "mirror";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ little-endian;
+ reg-io-width = <4>;
+
+ boot_mux: mux-controller {
+ compatible = "mmio-mux";
+ #mux-control-cells = <1>;
+
+ mux-reg-masks = <0x0 0x100>, <0x4 0x1>;
+ idle-states = <0x1>, <0x0>;
+ };
+
+ int_rom: rom@1bfc0000 {
+ compatible = "baikal,bt1-int-rom", "mtd-rom";
+ reg = <0x1bfc0000 0x10000>;
+
+ no-unaligned-direct-access;
+ bank-width = <4>;
+
+ status = "disabled";
+ };
+
+ /*
+ * Note that using the dirmap region stalls the APB bus
+ * until an IO operation is finished. It may cause
+ * significant lags in concurrent access to the system
+ * MMIO, since each SPI flash dword read operation takes
+ * at least 2.56 us to be finished (cmd + addr + data).
+ */
+ spi0: spi@1f040100 {
+ compatible = "baikal,bt1-sys-ssi";
+ reg = <0x1f040100 0x900>,
+ <0x1c000000 0x1000000>;
+ reg-names = "config", "map";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mux-controls = <&boot_mux 0>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ssi_clk";
+
+ status = "disabled";
+ };
+ };
+
+ gpio0: gpio@1f044000 {
+ compatible = "snps,dw-apb-gpio";
+ reg = <0x1f044000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_sys CCU_SYS_GPIO_CLK>;
+ clock-names = "bus", "db";
+
+ status = "disabled";
+
+ port0: gpio-port@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ reg = <0>;
+
+ interrupts = <GIC_SHARED 19 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpios = <32>;
+ };
+ };
+
+ gpio1: gpio@1f045000 {
+ compatible = "snps,dw-apb-gpio";
+ reg = <0x1f045000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_sys CCU_SYS_GPIO_CLK>;
+ clock-names = "bus", "db";
+
+ status = "disabled";
+
+ port1: gpio-port@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ reg = <0>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpios = <3>;
+ };
+ };
+
+ i2c1: i2c@1f046000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x1f046000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 33 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_I2C1_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ref", "pclk";
+ clock-frequency = <400000>;
+
+ dmas = <&dma 4 0 1 0xff>, <&dma 5 0 1 0xff>;
+ dma-names = "tx", "rx";
+
+ status = "disabled";
+ };
+
+ i2c2: i2c@1f047000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x1f047000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 34 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_I2C2_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ref", "pclk";
+ clock-frequency = <400000>;
+
+ dmas = <&dma 6 0 1 0xff>, <&dma 7 0 1 0xff>;
+ dma-names = "tx", "rx";
+
+ status = "disabled";
+ };
+
+ timer_dw0: timer@1f049000 {
+ compatible = "snps,dw-apb-timer";
+ reg = <0x1f049000 0x14>;
+
+ interrupts = <GIC_SHARED 24 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_TIMER0_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "timer", "pclk";
+
+ status = "disabled";
+ };
+
+ timer_dw1: timer@1f049014 {
+ compatible = "snps,dw-apb-timer";
+ reg = <0x1f049014 0x14>;
+
+ interrupts = <GIC_SHARED 25 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_TIMER1_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "timer", "pclk";
+
+ status = "disabled";
+ };
+
+ timer_dw2: timer@1f049028 {
+ compatible = "snps,dw-apb-timer";
+ reg = <0x1f049028 0x14>;
+
+ interrupts = <GIC_SHARED 26 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_TIMER2_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "timer", "pclk";
+
+ status = "disabled";
+ };
+
+ uart0: serial@1f04a000 {
+ compatible = "baikal,bt1-uart", "snps,dw-apb-uart";
+ reg = <0x1f04a000 0x1000>;
+
+ interrupts = <GIC_SHARED 48 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_UART_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "baudclk", "apb_pclk";
+
+ dmas = <&dma 0 0 1 0xff>, <&dma 1 0 1 0xff>;
+ dma-names = "tx", "rx";
+
+ dcd-override;
+ dsr-override;
+ cts-override;
+ ri-override;
+
+ /* earlycon settings */
+ reg-io-width = <4>;
+ reg-shift = <2>;
+
+ status = "disabled";
+ };
+
+ uart1: serial@1f04b000 {
+ compatible = "baikal,bt1-uart", "snps,dw-apb-uart";
+ reg = <0x1f04b000 0x1000>;
+
+ interrupts = <GIC_SHARED 49 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_UART_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "baudclk", "apb_pclk";
+
+ dmas = <&dma 2 0 1 0xff>, <&dma 3 0 1 0xff>;
+ dma-names = "tx", "rx";
+
+ /* earlycon settings */
+ reg-io-width = <4>;
+ reg-shift = <2>;
+
+ status = "disabled";
+ };
+
+ wdt: watchdog@1f04c000 {
+ compatible = "snps,dw-wdt";
+ reg = <0x1f04c000 0x1000>;
+
+ interrupts = <GIC_SHARED 17 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_WDT_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "tclk", "pclk";
+
+ /* Adjust ref-clock rate for better TOPs granularity */
+ assigned-clocks = <&ccu_sys CCU_SYS_WDT_CLK>;
+ assigned-clock-rates = <65534>;
+
+ snps,watchdog-tops = <0x000000ff 0x000001ff 0x000003ff
+ 0x000007ff 0x0000ffff 0x0001ffff
+ 0x0003ffff 0x0007ffff 0x000fffff
+ 0x001fffff 0x003fffff 0x007fffff
+ 0x00ffffff 0x01ffffff 0x03ffffff
+ 0x07ffffff>;
+
+ status = "disabled";
+ };
+
+ /*
+ * It's highly recommended to use all DW APB SSI controllers
+ * with GPIO-based CS, due to the native CS being automatically
+ * asserted/de-asserted on transmissions. Such HW design isn't
+ * that suitable for the kernel SPI subsystem, so GPIO-based CS
+ * will help to prevent very nasty, hard-to-fix errors.
+ */
+ spi1: spi@1f04e000 {
+ compatible = "baikal,bt1-ssi";
+ reg = <0x1f04e000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 40 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ssi_clk";
+
+ /*
+ * Make sure Rx DMA channels have higher priority. Note
+ * also that first two DW DMAC channels aren't suitable
+ * for the well-balanced Tx and Rx SPI transfers.
+ */
+ dmas = <&dma 8 0 1 0xe0>, <&dma 9 0 1 0x1c>;
+ dma-names = "tx", "rx";
+
+ reg-io-width = <4>;
+
+ status = "disabled";
+ };
+
+ spi2: spi@1f04f000 {
+ compatible = "baikal,bt1-ssi";
+ reg = <0x1f04f000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 41 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ssi_clk";
+
+ /*
+ * Make sure Rx DMA channels have higher priority. Note
+ * also that first two DW DMAC channels aren't suitable
+ * for the well-balanced Tx and Rx SPI transfers.
+ */
+ dmas = <&dma 10 0 1 0xe0>, <&dma 11 0 1 0x1c>;
+ dma-names = "tx", "rx";
+
+ reg-io-width = <4>;
+
+ status = "disabled";
+ };
+
+ pvt: temperature-sensor@1f200000 {
+ compatible = "baikal,bt1-pvt";
+ reg = <0x1f200000 0x1000>;
+ #thermal-sensor-cells = <0>;
+
+ interrupts = <GIC_SHARED 31 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_PVT_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ref", "pclk";
+
+ status = "disabled";
+ };
+
+ efuse: efuse@1f201000 {
+ compatible = "baikal,bt1-efuse";
+ reg = <0x1f201000 0x1000>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "pclk";
+
+ status = "disabled";
+ };
+ };
+
+ axi: bus@1f05a000 {
+ compatible = "baikal,bt1-axi", "simple-bus";
+ reg = <0 0x1f05a000 0 0x1000>,
+ <0 0x1f04d110 0 0x8>;
+ reg-names = "qos", "ehb";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ #interconnect-cells = <1>;
+
+ /*
+ * CPU can find the AXI-accessible devices over the next MMIO
+ * ranges.
+ */
+ ranges = <0 0x08000000 0 0x08000000 0 0x13dc0000>,
+ <0 0x1bf80000 0 0x1bf80000 0 0x00040000>,
+ <0 0x1bfc0000 0 0x1bfc0000 0 0x03c38000>;
+
+ /*
+ * Not all AXI-bus DMA-capable devices can reach any address in
+ * the physical memory space. SATA/USB/GMACx are limited to work
+ * with the lowest 4GB of memory. Here we set the normal DMA
+ * ranges mapping, while device-specific dma-ranges or device
+ * driver software must make sure the devices have been
+ * restricted on working with the permited memory range.
+ */
+ dma-ranges = <0 0 0 0 0x100 0>;
+
+ interrupts = <GIC_SHARED 127 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_axi CCU_AXI_MAIN_CLK>;
+ clock-names = "aclk";
+
+ resets = <&ccu_axi CCU_AXI_MAIN_RST>;
+ reset-names = "arst";
+
+ syscon = <&syscon>;
+
+ /*
+ * Note the (dma-)ranges mapping must be 64K aligned due to
+ * iATU constraints (lowest 16 bits aren't writable). Also
+ * note that we have to split the MEM-range up into two so
+ * one of them would be 256MB-aligned as some of the PCIe
+ * peripherals require. It can be done since AXI-interconnect
+ * doesn't permit the PCIe-master to access the MMIO-range
+ * anyway, so we can freely use the memory range above
+ * 0x1bfc0000 locally within the PCIe space.
+ */
+ pcie: pcie@1f052000 {
+ compatible = "baikal,bt1-pcie";
+ device_type = "pci";
+ reg = <0 0x1f052000 0 0x1000>,
+ <0 0x1f053000 0 0x1000>,
+ <0 0x1bdb0000 0 0x10000>;
+ reg-names = "dbi", "dbi2", "config";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges = <0x82000000 0 0x08000000 0 0x08000000 0 0x03da0000>, /* mem */
+ <0x82000000 0 0x10000000 0 0x0bda0000 0 0x10000000>, /* mem */
+ <0x81000000 0 0x0bda0000 0 0x1bda0000 0 0x00010000>; /* io */
+ bus-range = <0x0 0xff>;
+
+ interrupts = <GIC_SHARED 80 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 81 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 82 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 83 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 84 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 85 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 86 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 87 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 88 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 89 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 90 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 91 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 92 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 93 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dma0", "dma1", "dma2", "dma3",
+ "dma4", "dma5", "dma6", "dma7",
+ "msi", "aer", "pme", "hp", "bw_mg",
+ "l_eq";
+
+ /*
+ * Note 1. External reference clock source is required
+ * for the interface to work (name "ref").
+ * Note 2. PCIe AXI M and S clocks and resets are
+ * defined from the system interrconnected point of
+ * view so the AXI master and slave interfaces are
+ * actually connected to the DW PCIe RC AXI Slave and
+ * Master ports respectively.
+ */
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_S_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_M_CLK>;
+ clock-names = "dbi", "mstr", "slv";
+
+ resets = <&ccu_axi CCU_AXI_PCIE_S_RST>,
+ <&ccu_axi CCU_AXI_PCIE_M_RST>,
+ <&ccu_sys CCU_SYS_PCIE_PWR_RST>,
+ <&ccu_sys CCU_SYS_PCIE_HOT_RST>,
+ <&ccu_sys CCU_SYS_PCIE_PCS_PHY_RST>,
+ <&ccu_sys CCU_SYS_PCIE_CORE_RST>,
+ <&ccu_sys CCU_SYS_PCIE_PIPE0_RST>,
+ <&ccu_sys CCU_SYS_PCIE_STICKY_RST>,
+ <&ccu_sys CCU_SYS_PCIE_NSTICKY_RST>;
+ reset-names = "mstr", "slv", "pwr", "hot", "phy",
+ "core", "pipe", "sticky", "non-sticky";
+
+ baikal,bt1-syscon = <&syscon>;
+
+ num-lanes = <4>;
+ max-link-speed = <3>;
+
+ status = "disabled";
+ };
+
+ sram: sram-controller@1bf80000 {
+ compatible = "baikal,bt1-sram", "mmio-sram";
+ reg = <0 0x1bf80000 0 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0 0x1bf80000 0x10000>;
+
+ clocks = <&ccu_axi CCU_AXI_SRAM_CLK>;
+ clock-names = "aclk";
+
+ resets = <&ccu_axi CCU_AXI_SRAM_RST>;
+ reset-names = "arst";
+
+ mux-controls = <&boot_mux 1>;
+
+ status = "disabled";
+
+ boot-sram@0 {
+ compatible = "baikal,bt1-boot-sram";
+ reg = <0 0x10000>;
+ label="Internal SRAM";
+ export;
+ };
+ };
+
+ dma: dma-controller@1f041000 {
+ compatible = "baikal,bt1-dmac", "snps,dma-spear1340";
+ reg = <0 0x1f041000 0 0x1000>;
+ #dma-cells = <4>;
+
+ interrupts = <GIC_SHARED 56 IRQ_TYPE_LEVEL_HIGH>;
+
+ /* Clock rate up to 200MHz */
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "hclk";
+
+ dma-channels = <8>;
+ dma-requests = <12>;
+ dma-masters = <2>;
+
+ chan_allocation_order = <0>;
+ chan_priority = <0>;
+ block_size = <4095>;
+ data-width = <16 4>;
+ multi-block = <0 0 0 0 0 0 0 0>;
+ snps,max-burst-len = <16 16 4 4 4 4 4 4>;
+
+ status = "disabled";
+ };
+
+ mc: memory-controller@1f042000 {
+ compatible = "baikal,bt1-ddrc";
+ reg = <0 0x1f042000 0 0x1000>;
+
+ interrupts = <GIC_SHARED 96 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 97 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 98 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 99 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dfi_e", "ecc_ce", "ecc_ue", "ecc_sbr";
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_DDR_CLK>,
+ <&ccu_pll CCU_DDR_PLL>;
+ clock-names = "pclk", "aclk", "core";
+
+ resets = <&ccu_axi CCU_AXI_DDR_D_RST>,
+ <&ccu_sys CCU_SYS_DDR_INIT_RST>;
+ reset-names = "arst", "core";
+
+ phys = <&mc_phy>;
+ phy-names = "pub";
+
+ status = "disabled";
+ };
+
+ mc_phy: memory-controller-phy@1f043000 {
+ compatible = "baikal,bt1-ddrc-phy";
+ reg = <0 0x1f043000 0 0x1000>;
+ #phy-cells = <0>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_pll CCU_DDR_PLL>;
+ clock-names = "pclk", "core";
+
+ status = "disabled";
+ };
+
+ /*
+ * DWC AHCI SATA controller has been configured with 32-bits
+ * AMBA Master Address Bus width. Make sure any buffer
+ * allocated above that limit is bounced down to the permitted
+ * memory space before being passed to the device.
+ */
+ sata: sata@1f050000 {
+ compatible = "baikal,bt1-ahci";
+ reg = <0 0x1f050000 0 0x2000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 64 IRQ_TYPE_LEVEL_HIGH>;
+
+ /* Using an external 100MHz clock source is preferable */
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_SATA_CLK>,
+ <&ccu_sys CCU_SYS_SATA_REF_CLK>;
+ clock-names = "pclk", "aclk", "ref";
+
+ resets = <&ccu_axi CCU_AXI_SATA_RST>,
+ <&ccu_sys CCU_SYS_SATA_REF_RST>;
+ reset-names = "arst", "ref";
+
+ ports-implemented = <0x3>;
+
+ status = "disabled";
+
+ sata0: sata-port@0 {
+ reg = <0>;
+
+ snps,tx-ts-max = <16>;
+ snps,rx-ts-max = <16>;
+
+ status = "disabled";
+ };
+
+ sata1: sata-port@1 {
+ reg = <1>;
+
+ snps,tx-ts-max = <16>;
+ snps,rx-ts-max = <16>;
+
+ status = "disabled";
+ };
+ };
+
+ xgmac: ethernet@1f054000 {
+ compatible = "baikal,bt1-xgmac";
+ reg = <0 0x1f054000 0 0x4000>;
+
+ interrupts = <GIC_SHARED 74 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 75 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 76 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 77 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 78 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "macirq", "tx-queue-0", "tx-queue-1",
+ "rx-queue-0", "rx-queue-1";
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_XGMAC_CLK>,
+ <&ccu_sys CCU_SYS_XGMAC_REF_CLK>,
+ <&ccu_sys CCU_SYS_XGMAC_PTP_CLK>;
+ clock-names = "pclk", "stmmaceth", "tx", "ptp_ref";
+
+ resets = <&ccu_axi CCU_AXI_XGMAC_RST>;
+ reset-names = "stmmaceth";
+
+ pcs-handle = <&xgmac_pcs>;
+
+ /*
+ * xgmii - auto-negotiate 10GBASE-KX4/KR protocols
+ * (set the managed property to "in-band-status")
+ * 10gbase-r - select 10GBASE-R protocol
+ * 10gbase-x/xaui - select 10GBASE-X protocol
+ * (set the managed property to "auto")
+ */
+ phy-mode = "xgmii";
+
+ managed = "in-band-status";
+
+ rx-fifo-depth = <32768>;
+ tx-fifo-depth = <32768>;
+
+ /*
+ * Actual burst length will be (4 * 8 * 16) = 512 bytes
+ * due to the snps,no-pbl-x8 property absence and having
+ * the AXI bus data width of 128 bits.
+ * (AAL prevents random bus errors on high MTU)
+ */
+ snps,pbl = <4>;
+ snps,data-width = <16>;
+ snps,aal;
+
+ snps,axi-config = <&xgmac_axi_config>;
+ snps,mtl-rx-config = <&xgmac_rxq_config>;
+ snps,mtl-tx-config = <&xgmac_txq_config>;
+
+ /* Enable TSO for all DMA channels (TxPBL >= 4 && PBLx8) */
+ snps,tso;
+
+ snps,perfect-filter-entries = <8>;
+ snps,multicast-filter-bins = <64>;
+ local-mac-address = [ 00 20 13 ba 1c a1 ];
+
+ status = "disabled";
+
+ xgmac_axi_config: stmmac-axi-config {
+ snps,wr_osr_lmt = <0x7>;
+ snps,rd_osr_lmt = <0x7>;
+ /* It's AXI3 bus so up to 16 xfers */
+ snps,blen = <0 0 0 0 16 8 4>;
+ snps,fb; /* Unnecessary. but let's try */
+ };
+
+ /* Up to 4 queues and 2 DMA-channels are available */
+ xgmac_rxq_config: rx-queues-config {
+ snps,rx-queues-to-use = <2>;
+ snps,rx-sched-sp;
+
+ queue0 {
+ snps,map-to-dma-channel = <0>;
+ snps,dcb-algorithm;
+ snps,priority = <0x1>;
+ };
+
+ queue1 {
+ snps,map-to-dma-channel = <1>;
+ snps,dcb-algorithm;
+ snps,priority = <0x2>;
+ };
+
+ queue2 {
+ snps,map-to-dma-channel = <0>;
+ snps,dcb-algorithm;
+ snps,priority = <0x4>;
+ };
+
+ queue3 {
+ snps,map-to-dma-channel = <1>;
+ snps,dcb-algorithm;
+ snps,priority = <0x8>;
+ };
+ };
+
+ /* Up to 2 queues and 2 DMA-channels are available */
+ xgmac_txq_config: tx-queues-config {
+ snps,tx-queues-to-use = <2>;
+ snps,tx-sched-wrr;
+
+ queue0 {
+ snps,weight = <0x100>;
+ snps,dcb-algorithm;
+ snps,priority = <0x3>;
+ };
+
+ queue1 {
+ snps,weight = <0x100>;
+ snps,dcb-algorithm;
+ snps,priority = <0x3>;
+ };
+ };
+ };
+
+ hwa: hwa@1f05b000 {
+ compatible = "baikal,bt1-hwa";
+ reg = <0 0x1f05b000 0 0x1000>,
+ <0 0x1f05c000 0 0x1000>;
+ reg-names = "core", "dma";
+
+ interrupts = <GIC_SHARED 104 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_HWA_CLK>,
+ <&ccu_sys CCU_SYS_HWA_CLK>;
+ clock-names = "pclk", "aclk", "ref";
+
+ resets = <&ccu_axi CCU_AXI_HWA_RST>;
+ reset-names = "arst";
+
+ status = "disabled";
+ };
+
+ xgmac_pcs: ethernet-pcs@1f05d000 {
+ compatible = "baikal,bt1-xpcs";
+ reg = <0 0x1f05d000 0 0x1000>;
+ reg-names = "indirect";
+
+ reg-io-width = <4>;
+
+ interrupts = <GIC_SHARED 79 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_sys CCU_SYS_XGMAC_REF_CLK>;
+ clock-names = "csr", "core";
+
+ status = "disabled";
+ };
+
+ gmac0: ethernet@1f05e000 {
+ compatible = "baikal,bt1-gmac";
+ reg = <0 0x1f05e000 0 0x2000>;
+ #address-cells = <1>;
+ #size-cells = <2>;
+ dma-ranges = <0 0 0 0x1 0>;
+
+ interrupts = <GIC_SHARED 72 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "macirq";
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_GMAC0_CLK>,
+ <&ccu_sys CCU_SYS_GMAC0_TX_CLK>,
+ <&ccu_sys CCU_SYS_GMAC0_PTP_CLK>;
+ clock-names = "pclk", "stmmaceth", "tx", "ptp_ref";
+
+ resets = <&ccu_axi CCU_AXI_GMAC0_RST>;
+ reset-names = "stmmaceth";
+
+ /* DW GMAC is configured to export 1xGPI and 1xGPO */
+ ngpios = <2>;
+ gpio-line-names = "G0i", "G0o";
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ /*
+ * MAC always adds 2ns delay of TXC with respect to TXD
+ * so either PCB or PHY shall add some RXC delay.
+ *
+ * Note MAC supports getting the in-band link status
+ * over the RGMII interface as long as the attached
+ * RGMII PHY supports it. The feature can be enabled
+ * by specifying managed = "in-band-status".
+ */
+ phy-mode = "rgmii-id";
+ tx-internal-delay-ps = <2000>;
+
+ rx-fifo-depth = <16384>;
+ tx-fifo-depth = <16384>;
+
+ /*
+ * Actual burst length will be (4 * 8 * 16) = 512 bytes
+ * due to the snps,no-pbl-x8 property absence and having
+ * the AXI bus data width of 128 bits.
+ */
+ snps,pbl = <4>;
+ snps,data-width = <16>;
+
+ snps,axi-config = <&gmac0_axi_config>;
+
+ snps,perfect-filter-entries = <8>;
+ snps,multicast-filter-bins = <0>;
+ loacl-mac-address = [ 7a 72 6c 4a 7a 07 ];
+
+ status = "disabled";
+
+ gmac0_axi_config: stmmac-axi-config {
+ snps,wr_osr_lmt = <0x3>;
+ snps,rd_osr_lmt = <0x3>;
+ snps,blen = <0 0 0 0 16 8 4>;
+ };
+
+ gmac0_mdio: mdio {
+ compatible = "snps,dwmac-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+
+ gmac1: ethernet@1f060000 {
+ compatible = "baikal,bt1-gmac";
+ reg = <0 0x1f060000 0 0x2000>;
+ #address-cells = <1>;
+ #size-cells = <2>;
+ dma-ranges = <0 0 0 0x1 0>;
+
+ interrupts = <GIC_SHARED 73 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "macirq";
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_GMAC1_CLK>,
+ <&ccu_sys CCU_SYS_GMAC1_TX_CLK>,
+ <&ccu_sys CCU_SYS_GMAC1_PTP_CLK>;
+ clock-names = "pclk", "stmmaceth", "tx", "ptp_ref";
+
+ resets = <&ccu_axi CCU_AXI_GMAC1_RST>;
+ reset-names = "stmmaceth";
+
+ /* DW GMAC is configured to export 1xGPI and 1xGPO */
+ ngpios = <2>;
+ gpio-line-names = "G1i", "G1o";
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ /*
+ * MAC always adds 2ns delay of TXC with respect to TXD
+ * so either PCB or PHY shall add some RXC delay.
+ *
+ * Note MAC supports getting the in-band link status
+ * over the RGMII interface as long as the attached
+ * RGMII PHY supports it. The feature can be enabled
+ * by specifying managed = "in-band-status".
+ */
+ phy-mode = "rgmii-id";
+ tx-internal-delay-ps = <2000>;
+
+ rx-fifo-depth = <16384>;
+ tx-fifo-depth = <16384>;
+
+ /*
+ * Actual burst length will be (4 * 8 * 16) = 512 bytes
+ * due to the snps,no-pbl-x8 property absence and having
+ * the AXI bus data width of 128 bits.
+ */
+ snps,pbl = <4>;
+ snps,data-width = <16>;
+
+ snps,axi-config = <&gmac1_axi_config>;
+
+ snps,perfect-filter-entries = <8>;
+ snps,multicast-filter-bins = <0>;
+ loacl-mac-address = [ 7a 72 6c 4a 7b 07 ];
+
+ status = "disabled";
+
+ gmac1_axi_config: stmmac-axi-config {
+ snps,wr_osr_lmt = <0x3>;
+ snps,rd_osr_lmt = <0x3>;
+ snps,blen = <0 0 0 0 16 8 4>;
+ };
+
+ gmac1_mdio: mdio {
+ compatible = "snps,dwmac-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+
+ usb: usb@1f100000 {
+ compatible = "baikal,bt1-usb3", "snps,dwc3";
+ reg = <0 0x1f100000 0 0x100000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 68 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "host";
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_USB_CLK>,
+ <&ccu_sys CCU_SYS_USB_CLK>;
+ clock-names = "pclk", "bus_early", "ref";
+
+ resets = <&ccu_axi CCU_AXI_USB_RST>;
+ reset-names = "arst";
+
+ dr_mode = "host";
+ phy_type = "ulpi";
+ maximum-speed = "high-speed";
+
+ snps,incr-burst-type-adjustment = <1 4 8 16>;
+
+ status = "disabled";
+ };
+ };
+};
diff --git a/arch/mips/boot/dts/baikal/cb2-em406.dts b/arch/mips/boot/dts/baikal/cb2-em406.dts
new file mode 100644
index 0000000000000..4e09a21f5cfc4
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/cb2-em406.dts
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Edelweiss EM406 carrier board device tree
+ */
+
+/dts-v1/;
+
+#include "em406.dtsi"
+
+/ {
+ model = "Edelweiss EM406 Carrier Board v2.x";
+ compatible = "edelweiss,cb2-em406", "edelweiss,em406", "baikal,bt1";
+
+ chosen {
+ /* RS232 tranciever is attached to the UART0 port */
+ bootargs = "console=ttyS0,115200n8 earlycon maxcpus=2";
+ stdout-path = "serial0:115200n8";
+ };
+};
diff --git a/arch/mips/boot/dts/baikal/em406.dtsi b/arch/mips/boot/dts/baikal/em406.dtsi
new file mode 100644
index 0000000000000..b2baaa5666302
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/em406.dtsi
@@ -0,0 +1,378 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Edelweiss EM406 Mezzanine Card device tree
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "bt1.dtsi"
+
+/ {
+ model = "Edelweiss EM406 Mezzanine Card";
+ compatible = "edelweiss,em406", "baikal,bt1";
+
+ chosen {
+ /*
+ * Use MIPS eJTAG FDC #1 as alternative early-console:
+ * "console=early_fdc,115200n8 maxcpus=2"
+ * Note the debug probe must be attached to drain FDC TX FIFO.
+ */
+ bootargs = "console=ttyS0,115200n8 earlycon maxcpus=2";
+ stdout-path = "serial0:115200n8";
+
+ /* Bootloader timer if TIMER config is enabled */
+ tick-timer = <&timer_dw0>;
+
+ /* Bootloader may use these props to pass the initrd image */
+ linux,initrd-start = <0 0>;
+ linux,initrd-end = <0 0>;
+ };
+
+ aliases {
+ spd0 = &spd;
+ mtd1 = &boot_flash;
+ };
+
+ memory {
+ /*
+ * The mezzanine card is equiepped with at least 2GB memory:
+ * low memory - 128MB, high memory - 256MB + 1.5GB. The RAM
+ * can be extended up to 8GB with additional 2GB ECC.
+ */
+ device_type = "memory";
+ reg = <0 0x00000000 0 0x08000000>,
+ <0 0x20000000 0 0x70000000>;
+ };
+
+ clocks {
+ usb_phy_clk: clock-oscillator-usb-phy {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "usbphy24m";
+ };
+ };
+};
+
+&l2 {
+ status = "okay";
+};
+
+&i2c0 {
+ status = "okay";
+
+ /* Connected to X1 SMARC LCD I2C SDA, SCL (SMARC v1.1) */
+};
+
+&int_rom {
+ status = "okay";
+};
+
+&spi0 {
+ num-cs = <1>;
+
+ status = "okay";
+
+ /* CS0 bus is connected to DD34 (TS3A5018R NO pins) handled by BMC */
+
+ /* Micron MT25QU128ADQ0 */
+ boot_flash: flash@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+
+ spi-max-frequency = <25000000>;
+ m25p,fast-read;
+
+ /* Tie X1:S39 to GND to redirect device to X1 SMARC SPI0 */
+ };
+};
+
+&gpio0 {
+ status = "okay";
+};
+
+&port0 {
+ /* Connected to X1 SMARC pins */
+ gpio-line-names = "S106", "S108", "S144", "S142", "S123",
+ "S122", "S121", "P117", "S113", "P118",
+ "S117", "S120", "P115", "P116", "P111",
+ "P113", "P119", "S116", "S118", "S115",
+ "S114", "P112", "P114", "P110", "S112",
+ "nUSB_RST", "S109", "S145", "S111", "P109",
+ "S107", "P108";
+};
+
+&i2c1 {
+ status = "okay";
+
+ /* Connected to X1 SMARC GP I2C SDA, SCL (SMARC v2) */
+};
+
+&i2c2 {
+ status = "okay";
+
+ /* Board Management Controller (STM32L151XB) */
+ bmc: bmc@8 {
+ compatible = "edelweiss,bt1-bmc";
+ reg = <0x08>;
+ };
+
+ /* EM406-specific system control expander */
+ smarc_gpio: gpio@20 {
+ compatible = "nxp,pca9535";
+ reg = <0x20>;
+
+ gpio-controller; /* 16 */
+ #gpio-cells = <2>;
+
+ /* IRQ can be optionally fused up to port0:29 GPIO */
+
+ gpio-line-names = "MAJ0", "MAJ1", "MAJ2", "MAJ3", "",
+ "SLEEP", "LID", "nCHRG", "nCHRG_PRSNT",
+ "nLOWBAT", "CAR_STB", "", "",
+ "nPCI_WAKE", "", "PCI_RST";
+ };
+
+ temp: temperature-sensor@48 {
+ compatible = "ti,tmp102";
+ reg = <0x48>;
+ };
+
+ /* EEPROM is empty by default */
+ spd: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+
+ pagesize = <8>;
+ };
+
+ rtc: rtc@51 {
+ compatible = "nxp,pcf85263";
+ reg = <0x51>;
+ };
+
+ fw: eeprom@53 {
+ compatible = "atmel,24c32";
+ reg = <0x53>;
+
+ pagesize = <32>;
+ };
+
+ clk_gen: clock@6a {
+ compatible = "idt,5p49v6901";
+ reg = <0x6a>;
+ #clock-cells = <1>;
+
+ clocks = <&ref_clk>;
+ clock-names = "xin";
+
+ /* OUT0: Core act., OUT3: Ext PCIe */
+ };
+
+ /* Connected to X1 SMARC PM I2C SDA, SCL (SMARC v2) */
+};
+
+&timer_dw0 {
+ status = "okay";
+};
+
+&timer_dw1 {
+ status = "okay";
+};
+
+&timer_dw2 {
+ status = "okay";
+};
+
+&uart0 {
+ /* Connected to X1 SMARC SER0 pins */
+ status = "okay";
+};
+
+&uart1 {
+ /* Connected to X1 SMARC SER1 pins */
+ status = "okay";
+};
+
+&wdt {
+ status = "okay";
+};
+
+&spi1 {
+ num-cs = <4>;
+
+ status = "okay";
+
+ /* Connected to X1 SMARC SPI1 pins and CS2->S59, CS3->S56 */
+};
+
+&spi2 {
+ num-cs = <4>;
+
+ status = "okay";
+
+ /*
+ * Connected to X1 SMARC pins MOSI->S40, MISO->S41, CLK->S38, CS0->S37,
+ * CS1->S35, CS2->S44, CS3->S51
+ */
+};
+
+&pvt {
+ status = "okay";
+};
+
+&efuse {
+ status = "okay";
+};
+
+&pcie {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_M_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_S_CLK>,
+ <&clk_gen 2>;
+ clock-names = "dbi", "mstr", "slv", "ref";
+
+ /* PCIe PERST# signal */
+ reset-gpios = <&smarc_gpio 15 GPIO_ACTIVE_HIGH>;
+
+ status = "okay";
+};
+
+&sram {
+ status = "okay";
+};
+
+&dma {
+ status = "okay";
+};
+
+&mc {
+ status = "okay";
+};
+
+&mc_phy {
+ status = "okay";
+};
+
+&sata {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_SATA_CLK>,
+ <&clk_gen 4>;
+ clock-names = "pclk", "aclk", "ref";
+
+ status = "okay";
+};
+
+&sata0 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+
+ /* Connected to X1 SMARC SATA pins */
+};
+
+&sata1 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+
+ /* Connected to X1 SMARC pins TXP->P10, TXN->P11, RXP->P13, RXN->P14 */
+};
+
+&xgmac {
+ mac-address = [ 00 26 58 80 01 01 ];
+
+ status = "okay";
+};
+
+&xgmac_pcs {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_sys CCU_SYS_XGMAC_REF_CLK>,
+ <&clk_gen 1>;
+ clock-names = "csr", "core", "pad";
+
+ status = "okay";
+};
+
+&hwa {
+ status = "okay";
+};
+
+&gmac0 {
+ mac-address = [ 00 26 58 80 01 02 ];
+
+ phy-handle = <&gmac0_phy>;
+ managed = "in-band-status";
+
+ status = "okay";
+};
+
+&gmac0_mdio {
+ reset-gpios = <&gmac0 1 GPIO_ACTIVE_LOW>;
+ reset-delay-us = <11000>;
+ reset-post-delay-us = <31000>;
+
+ /* Realtek RTL8211E */
+ gmac0_phy: ethernet-phy@3 {
+ compatible = "ethernet-phy-id001c.c915";
+ reg = <0x3>;
+
+ interrupt-parent = <&gmac0>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+
+ clocks = <&clk_gen 0>;
+ clock-names = "xtal";
+
+ /* Connected to X1 SMARC GBE pins */
+ };
+};
+
+&gmac1 {
+ mac-address = [ 00 26 58 80 01 03 ];
+
+ phy-handle = <&gmac1_phy>;
+ managed = "in-band-status";
+
+ status = "okay";
+};
+
+&gmac1_mdio {
+ reset-gpios = <&gmac1 1 GPIO_ACTIVE_LOW>;
+ reset-delay-us = <11000>;
+ reset-post-delay-us = <31000>;
+
+ /* Realtek RTL8211E */
+ gmac1_phy: ethernet-phy@3 {
+ compatible = "ethernet-phy-id001c.c915";
+ reg = <0x3>;
+
+ interrupt-parent = <&gmac1>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+
+ clocks = <&clk_gen 0>;
+ clock-names = "xtal";
+
+ /* Connected to X1 SMARC AFB_DIFF pins */
+ };
+};
+
+&usb {
+ status = "okay";
+
+ ulpi {
+ /* Microchip USB3315 */
+ phy {
+ clocks = <&usb_phy_clk>;
+ clock-names = "ref";
+
+ reset-gpios = <&port0 25 GPIO_ACTIVE_LOW>;
+
+ /* Connected to X1 SMARC USB1 pins */
+ };
+ };
+};
diff --git a/arch/mips/boot/dts/baikal/em416.dtsi b/arch/mips/boot/dts/baikal/em416.dtsi
new file mode 100644
index 0000000000000..955c2cf5a8a0b
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/em416.dtsi
@@ -0,0 +1,359 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Edelweiss EM416 Mezzanine Card device tree
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/net/mscc-phy-vsc8531.h>
+
+#include "bt1.dtsi"
+
+/ {
+ model = "Edelweiss EM416 Mezzanine Card";
+ compatible = "edelweiss,em416", "baikal,bt1";
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8 earlycon maxcpus=2";
+ stdout-path = "serial0:115200n8";
+
+ /* Bootloader timer if TIMER config is enabled */
+ tick-timer = <&timer_dw0>;
+
+ /* Bootloader may use these props to pass the initrd image */
+ linux,initrd-start = <0 0>;
+ linux,initrd-end = <0 0>;
+ };
+
+ aliases {
+ spd0 = &spd;
+ mtd1 = &boot_flash;
+ };
+
+ memory {
+ /*
+ * The card is always equipped with 2GB of RAM:
+ * low memory - 128MB, high memory - 256MB + 1.5GB.
+ */
+ device_type = "memory";
+ reg = <0 0x00000000 0 0x08000000>,
+ <0 0x20000000 0 0x70000000>;
+ };
+
+ clocks {
+ vc6_clk: clock-oscillator-vc6 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "vc25m";
+ };
+
+ gmac0_phy_clk: clock-oscillator-gmac0-phy {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "gmac0phy25m";
+ };
+
+ gmac1_phy_clk: clock-oscillator-gmac1-phy {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "gmac1phy25m";
+ };
+ };
+};
+
+&l2 {
+ status = "okay";
+};
+
+&int_rom {
+ status = "okay";
+};
+
+&spi0 {
+ num-cs = <1>;
+
+ status = "okay";
+
+ /* CS0 bus is connected to DD10 (TS3A5018R NO pins) handled by BMC */
+
+ /* Micron MT25QU128ABA1 */
+ boot_flash: flash@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+
+ spi-max-frequency = <25000000>;
+ m25p,fast-read;
+
+ /*
+ * Use BMC console to redirect device to XP2 MOSI->38,
+ * MISO->40, CLK->39, CS0->37
+ */
+ };
+};
+
+&gpio0 {
+ status = "okay";
+};
+
+&port0 {
+ /* GP - general purpose */
+ gpio-line-names = "", "", "", "", "", "", "", "",
+ "nPCI_RST", "nPCI_PRSNT", "", "", "",
+ "nUSB_RST", "nSD_CS", "nSD_CD", "", "", "",
+ "", "", "", "GP", "GP", "GP", "GP", "nEXP_INT",
+ "GP", "GP", "GP", "GP", "GP", "GP";
+};
+
+&i2c1 {
+ status = "okay";
+};
+
+&i2c2 {
+ status = "okay";
+
+ /* Board Management Controller (STM32F078CB) */
+ bmc: bmc@8 {
+ compatible = "edelweiss,bt1-bmc";
+ reg = <0x08>;
+ };
+
+ shred_gpio: gpio@21 {
+ compatible = "nxp,pcf8574";
+ reg = <0x21>;
+
+ gpio-controller; /* 8 */
+ #gpio-cells = <2>;
+
+ /* Kind of useless though... */
+ interrupt-parent = <&port0>;
+ interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ /* EEPROM is empty by default */
+ spd: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+
+ pagesize = <8>;
+ };
+
+ fw: eeprom@53 {
+ compatible = "atmel,24c32";
+ reg = <0x53>;
+
+ pagesize = <32>;
+ };
+
+ clk_gen: clock@6a {
+ compatible = "idt,5p49v6901";
+ reg = <0x6a>;
+ #clock-cells = <1>;
+
+ clocks = <&vc6_clk>;
+ clock-names = "xin";
+
+ /* OUT0: Core alt., OUT1: Ext PCIe */
+ };
+};
+
+&timer_dw0 {
+ status = "okay";
+};
+
+&timer_dw1 {
+ status = "okay";
+};
+
+&timer_dw2 {
+ status = "okay";
+};
+
+&uart0 {
+ status = "okay";
+};
+
+&uart1 {
+ /* Connected to DD14 (BMC) */
+ status = "okay";
+};
+
+&wdt {
+ status = "okay";
+};
+
+&spi1 {
+ num-cs = <2>;
+
+ /*
+ * R40/R39 resistors switch between native CS1 and port0:14 GPIO.
+ * Solder the R39 resistor in to activate the GPIO CS.
+ */
+ cs-gpios = <0>, <&port0 14 GPIO_ACTIVE_LOW>;
+
+ status = "okay";
+
+ /* Won't work with native CS due to no high CS capability */
+ mmc: mmc@1 {
+ compatible = "mmc-spi-slot";
+ reg = <1>;
+
+ voltage-ranges = <3200 3400>;
+ spi-max-frequency = <25000000>;
+ /* Card-Detect GPIO */
+ gpios = <&port0 15 GPIO_ACTIVE_LOW>;
+ };
+
+};
+
+&spi2 {
+ num-cs = <4>;
+
+ status = "okay";
+};
+
+&pvt {
+ status = "okay";
+};
+
+&efuse {
+ status = "okay";
+};
+
+&pcie {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_M_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_S_CLK>,
+ <&clk_gen 2>;
+ clock-names = "dbi", "mstr", "slv", "ref";
+
+ /* PCIe PERST# signal */
+ reset-gpios = <&port0 8 GPIO_ACTIVE_LOW>;
+
+ status = "okay";
+};
+
+&sram {
+ status = "okay";
+};
+
+&dma {
+ status = "okay";
+};
+
+&mc {
+ status = "okay";
+};
+
+&mc_phy {
+ status = "okay";
+};
+
+&sata {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_SATA_CLK>,
+ <&clk_gen 4>;
+ clock-names = "pclk", "aclk", "ref";
+
+ status = "okay";
+};
+
+&sata0 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&sata1 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&hwa {
+ status = "okay";
+};
+
+&gmac0 {
+ mac-address = [ 00 26 58 79 00 05 ];
+
+ phy-handle = <&gmac0_phy>;
+
+ status = "okay";
+};
+
+&gmac0_mdio {
+ reset-gpios = <&gmac0 1 GPIO_ACTIVE_LOW>;
+ reset-delay-us = <2000>;
+ reset-post-delay-us = <16000>;
+
+ /* Microsemi VSC8531XMW */
+ gmac0_phy: ethernet-phy@1 {
+ compatible = "ethernet-phy-id0007.0570";
+ reg = <0x1>;
+
+ interrupt-parent = <&gmac0>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+
+ clocks = <&gmac0_phy_clk>;
+ clock-names = "xtal";
+
+ vsc8531,vddmac = <1800>;
+ vsc8531,edge-slowdown = <0>;
+ vsc8531,led-0-mode = <VSC8531_LINK_1000_ACTIVITY>;
+ vsc8531,led-1-mode = <VSC8531_LINK_100_ACTIVITY>;
+ };
+};
+
+&gmac1 {
+ mac-address = [ 00 26 58 79 00 06 ];
+
+ phy-handle = <&gmac1_phy>;
+
+ status = "okay";
+};
+
+&gmac1_mdio {
+ reset-gpios = <&gmac1 1 GPIO_ACTIVE_LOW>;
+ reset-delay-us = <2000>;
+ reset-post-delay-us = <16000>;
+
+ /* Microsemi VSC8531XMW */
+ gmac1_phy: ethernet-phy@2 {
+ compatible = "ethernet-phy-id0007.0570";
+ reg = <0x2>;
+
+ interrupt-parent = <&gmac1>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+
+ clocks = <&gmac1_phy_clk>;
+ clock-names = "xtal";
+
+ vsc8531,vddmac = <1800>;
+ vsc8531,edge-slowdown = <0>;
+ vsc8531,led-0-mode = <VSC8531_LINK_1000_ACTIVITY>;
+ vsc8531,led-1-mode = <VSC8531_LINK_100_ACTIVITY>;
+ };
+};
+
+&usb {
+ status = "okay";
+
+ ulpi {
+ /* Microchip USB3315 */
+ phy {
+ clocks = <&clk_gen 3>;
+ clock-names = "ref";
+
+ reset-gpios = <&port0 13 GPIO_ACTIVE_LOW>;
+ };
+ };
+};
diff --git a/arch/mips/boot/dts/baikal/kr-sfp.dtsi b/arch/mips/boot/dts/baikal/kr-sfp.dtsi
new file mode 100644
index 0000000000000..59cfb4d6bf301
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/kr-sfp.dtsi
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal Electronics KR SFP+ Mezzanine Card device tree
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+/*
+ * TODO Convert this file to be a subordinate of a nexus node/connector when the
+ * kernel gets ready for it:
+ * 3: I2C SCL
+ * 5: I2C SDA
+ * 7: SFP Mod ABS
+ * 9: SFP Tx Disable
+ * 11: SFP Tx Fault
+ * 12: SFP Rx Los
+ * 17,18,19,20: TxN3,RxN3,TxP3,RxP3 - Lane 3 10GBASE-X
+ * 23,24,25,26: TxN2,RxN2,TxP2,RxP2 - Lane 2 10GBASE-X
+ * 29,30,31,32: TxN1,RxN1,TxP1,RxP1 - Lane 1 10GBASE-X
+ * 35,36,37,38: TxN0,RxN0,TxP0,RxP0 - Lane 0 10GBASE-X/10GBASE-R
+ */
+
+/ {
+ xgmac_sfp: sfp {
+ compatible = "sff,sfp";
+
+ i2c-bus = <&i2c1>;
+
+ los-gpios = <&port0 27 GPIO_ACTIVE_HIGH>;
+ mod-def0-gpios = <&port0 11 GPIO_ACTIVE_LOW>;
+ tx-disable-gpios = <&port0 10 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+ tx-fault-gpios = <&port0 9 GPIO_ACTIVE_HIGH>;
+ };
+};
+
+&xgmac_pcs {
+ sfp = <&xgmac_sfp>;
+};
+
+&xgmac {
+ phy-mode = "10gbase-r";
+};
diff --git a/arch/mips/boot/dts/baikal/krkx4-mv-sfp.dtsi b/arch/mips/boot/dts/baikal/krkx4-mv-sfp.dtsi
new file mode 100644
index 0000000000000..f0f0adb68ea3a
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/krkx4-mv-sfp.dtsi
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal Electronics KR/KX4 Marvell 88x2222 SFP+ Mezzanine Card device tree
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/net/mv-phy-88x2222.h>
+
+/*
+ * TODO Convert this file to be a subordinate of a nexus node/connector when the
+ * kernel gets ready for it:
+ * 3: I2C SCL
+ * 5: I2C SDA
+ * 7: MDI/MDIO
+ * 9: MDO
+ * 11: MDC
+ * 12: PHY INT
+ * 17,18,19,20: TxN3,RxN3,TxP3,RxP3 - Lane 3 10GBASE-X
+ * 23,24,25,26: TxN2,RxN2,TxP2,RxP2 - Lane 2 10GBASE-X
+ * 29,30,31,32: TxN1,RxN1,TxP1,RxP1 - Lane 1 10GBASE-X
+ * 35,36,37,38: TxN0,RxN0,TxP0,RxP0 - Lane 0 10GBASE-X/10GBASE-R
+ */
+
+/ {
+ aliases {
+ mdio-gpio0 = &xgmac_mdio;
+ };
+
+ xgmac_mdio: mdio {
+ compatible = "virtual,mdio-gpio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* PORT0.9 - MDC, PORT0.10 - MDO, PORT0.11 - MDI/MDIO */
+ gpios = <&port0 9 GPIO_ACTIVE_HIGH>, <&port0 11 GPIO_ACTIVE_HIGH>,
+ <&port0 10 GPIO_ACTIVE_HIGH>;
+
+ reset-gpios = <&xgmac_gpio 0 GPIO_ACTIVE_HIGH>;
+ reset-delay-us = <10000>;
+ reset-post-delay-us = <10000>;
+
+ xgmac_phy: ethernet-phy@c {
+ compatible = "ethernet-phy-ieee802.3-c45";
+ reg = <0x0c>;
+
+ interrupt-parent = <&port0>;
+ interrupts = <27 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ gpio-controller; /* 12 */
+ #gpio-cells = <2>;
+ gpio-reserved-ranges = <MV_88X2222_LED0 1>,
+ <MV_88X2222_LED1 1>,
+ <MV_88X2222_SDA 1>,
+ <MV_88X2222_SCL 1>;
+
+ sfp = <&xgmac_sfp>;
+ };
+ };
+
+ xgmac_sfp: sfp {
+ compatible = "sff,sfp";
+
+ i2c-bus = <&xgmac_phy>;
+
+ mod-def0-gpios = <&xgmac_phy MV_88X2222_MOD_ABS GPIO_ACTIVE_LOW>;
+ tx-fault-gpios = <&xgmac_phy MV_88X2222_TX_FAULT GPIO_ACTIVE_HIGH>;
+ los-gpios = <&xgmac_phy MV_88X2222_RX_LOS GPIO_ACTIVE_HIGH>;
+ tx-disable-gpios = <&xgmac_phy MV_88X2222_TX_DISABLE GPIO_ACTIVE_HIGH>;
+ };
+};
+
+&xgmac {
+ /* Possible protocols: 10gbase-r, 10gbase-x/xaui */
+ phy-mode = "10gbase-r";
+
+ managed = "auto";
+
+ phy-handle = <&xgmac_phy>;
+};
+
+&i2c1 {
+ /* Marvell PHY Reset-controller (NXP PCA9500 8-bit GPIO) */
+ xgmac_gpio: gpio@20 {
+ compatible = "nxp,pcf8574";
+ reg = <0x20>;
+
+ gpio-controller; /* 8 */
+ #gpio-cells = <2>;
+
+ gpio-line-names = "RST_PHY", "", "", "", "", "", "", "";
+ };
+
+ /* Mezzanine card firmware (NXP PCA9500 2-kbit EEPROM) */
+ xgmac_fw: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+
+ pagesize = <4>;
+ };
+};
diff --git a/arch/mips/boot/dts/baikal/tf306.dts b/arch/mips/boot/dts/baikal/tf306.dts
new file mode 100644
index 0000000000000..1047875c79172
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/tf306.dts
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Edelweiss TF306 mini-ITX board device tree
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+#include "bt1.dtsi"
+
+/ {
+ model = "Edelweiss TF306 mini-ITX Board";
+ compatible = "edelweiss,tf306", "baikal,bt1";
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8 earlycon maxcpus=2";
+ stdout-path = "serial0:115200n8";
+
+ /* Bootloader timer if TIMER config is enabled */
+ tick-timer = <&timer_dw0>;
+
+ /* Bootloader is supposed to pass the initrd image address */
+ linux,initrd-start = <0 0>;
+ linux,initrd-end = <0 0>;
+ };
+
+ aliases {
+ spd0 = &spd;
+ mtd1 = &boot_flash;
+ };
+
+ memory {
+ /*
+ * Assume at least 512MB of RAM detected in SO-DIMM (XS1):
+ * low memory - 128MB, high memory - 256MB.
+ */
+ device_type = "memory";
+ reg = <0 0x00000000 0 0x08000000>,
+ <0 0x20000000 0 0x10000000>;
+ };
+
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ framebuffer: framebuffer@7000000 {
+ reg = <0 0x07000000 0 0x01000000>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led-0 {
+ label = "Disk Activity";
+
+ gpios = <&port0 18 GPIO_ACTIVE_HIGH>;
+ linux,default-trigger = "disk-activity";
+ };
+ };
+
+ clocks {
+ vc6_clk: clock-oscillator-vc {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "vc25m";
+ };
+
+ ssd_clk: clock-oscillator-ssd {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "ssd25m";
+ };
+
+ gmac0_phy_clk: clock-oscillator-gmac0-phy {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ clock-output-names = "gmac0phy25m";
+ };
+ };
+};
+
+&l2 {
+ status = "okay";
+};
+
+&int_rom {
+ status = "okay";
+};
+
+&spi0 {
+ num-cs = <1>;
+
+ status = "okay";
+
+ /* CS0 bus is connected to DD24 (TS3A5018R NO pins) handled by BMC */
+
+ /* Micron N25Q128A11 */
+ boot_flash: flash@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+
+ spi-max-frequency = <25000000>;
+ m25p,fast-read;
+
+ /*
+ * Use BMC console to redirect device to XP3 MOSI->10,
+ * MISO->9, CLK->8, CS0->7
+ */
+ };
+};
+
+&gpio0 {
+ status = "okay";
+};
+
+&port0 {
+ gpio-line-names = "", "", "", "", "", "", "", "", "", "", "",
+ "nRTC_INT", "nVIDEO_RST", "nUSB_RST", "nSD_CS",
+ "nSD_CD", "", "", "HDD_LED", "", "", "", "", "", "",
+ "nEXP_INT", "", "", "", "", "", "";
+};
+
+&i2c2 {
+ status = "okay";
+
+ /* Board Management Controller (STM32F078CB) */
+ bmc: bmc@8 {
+ compatible = "edelweiss,bt1-bmc";
+ reg = <0x08>;
+ };
+
+ shred_gpio: gpio@21 {
+ compatible = "nxp,pcf8574";
+ reg = <0x21>;
+
+ gpio-controller; /* 8 */
+ #gpio-cells = <2>;
+
+ /* Kind of useless though... */
+ interrupt-parent = <&port0>;
+ interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ hub: usb-hub@2c {
+ compatible = "microchip,usb2517";
+ reg = <0x2c>;
+
+ /* Configs are set by the bootloader */
+ skip-config;
+ };
+
+ spd: eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+
+ pagesize = <8>;
+ };
+
+ rtc: rtc@51 {
+ compatible = "nxp,pcf2129";
+ reg = <0x51>;
+
+ interrupt-parent = <&port0>;
+ interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+ };
+
+ fw: eeprom@53 {
+ compatible = "atmel,24c32";
+ reg = <0x53>;
+
+ pagesize = <32>;
+ };
+
+ clk_gen: clock@6a {
+ compatible = "idt,5p49v6901";
+ reg = <0x6a>;
+ #clock-cells = <1>;
+
+ clocks = <&vc6_clk>;
+ clock-names = "xin";
+
+ /* OUT0: Core alt. */
+ };
+};
+
+&timer_dw0 {
+ status = "okay";
+};
+
+&timer_dw1 {
+ status = "okay";
+};
+
+&timer_dw2 {
+ status = "okay";
+};
+
+&uart0 {
+ status = "okay";
+};
+
+&uart1 {
+ /* Connected to DD28 (BMC) */
+ status = "okay";
+};
+
+&wdt {
+ status = "okay";
+};
+
+&spi1 {
+ num-cs = <2>;
+
+ /*
+ * R505/R504 resistors switch between native CS1 and port0:14 GPIO.
+ * Solder the R504 resistor in to activate the GPIO CS.
+ */
+ cs-gpios = <0>, <&port0 14 GPIO_ACTIVE_LOW>;
+
+ status = "okay";
+
+ /* Won't work with native CS due to no high CS capability */
+ mmc: mmc@1 {
+ compatible = "mmc-spi-slot";
+ reg = <1>;
+
+ voltage-ranges = <3200 3400>;
+ spi-max-frequency = <25000000>;
+ /* Card-Detect GPIO */
+ gpios = <&port0 15 GPIO_ACTIVE_LOW>;
+ };
+};
+
+&pvt {
+ status = "okay";
+};
+
+&efuse {
+ status = "okay";
+};
+
+&pcie {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_M_CLK>,
+ <&ccu_axi CCU_AXI_PCIE_S_CLK>,
+ <&clk_gen 2>;
+ clock-names = "dbi", "mstr", "slv", "ref";
+
+ reset-gpios = <&port0 12 GPIO_ACTIVE_LOW>;
+
+ status = "okay";
+
+ /* Silicon Motion SM750 VGA */
+ video@0,0 {
+ comptible = "pci126f,0750", "pciclass,030000";
+ reg = <0 0 0 0 0>;
+
+ clocks = <&clk_gen 1>;
+ clock-names = "ref";
+
+ memory-region = <&framebuffer>;
+ memory-region-names = "framebuffer";
+ };
+};
+
+&sram {
+ status = "okay";
+};
+
+&dma {
+ status = "okay";
+};
+
+&mc {
+ status = "okay";
+};
+
+&mc_phy {
+ status = "okay";
+};
+
+&sata {
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_SATA_CLK>,
+ <&clk_gen 4>;
+ clock-names = "pclk", "aclk", "ref";
+
+ status = "okay";
+};
+
+&sata0 {
+ /* Connected to InnoDisk DENSD-16GD06 (16GB SSD) */
+ status = "okay";
+};
+
+&sata1 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&hwa {
+ status = "okay";
+};
+
+&gmac0 {
+ mac-address = [ 00 26 58 78 10 00 ];
+
+ phy-handle = <&gmac0_phy>;
+ managed = "in-band-status";
+
+ status = "okay";
+};
+
+&gmac0_mdio {
+ reset-gpios = <&gmac0 1 GPIO_ACTIVE_LOW>;
+ reset-delay-us = <11000>;
+ reset-post-delay-us = <31000>;
+
+ /* Realtek RTL8211E */
+ gmac0_phy: ethernet-phy@1 {
+ compatible = "ethernet-phy-id001c.c915";
+ reg = <0x1>;
+
+ interrupt-parent = <&gmac0>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+
+ clocks = <&gmac0_phy_clk>;
+ clock-names = "xtal";
+ };
+};
+
+&usb {
+ reset-gpios = <&port0 13 GPIO_ACTIVE_LOW>;
+
+ status = "okay";
+
+ ulpi {
+ /* Microchip USB3315 */
+ phy {
+ clocks = <&clk_gen 3>;
+ clock-names = "ref";
+
+ reset-gpios = <&port0 13 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ /* Microchip USB2517 */
+ hub@0 {
+ compatible = "usb424,2517";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ clocks = <&clk_gen 3>;
+ clock-names = "xtal";
+
+ /* Realtek ALC4040-CG */
+ audio@0 {
+ compatible = "usbbda,4040";
+ reg = <0>;
+ };
+
+ /* The rest of the ports are USB type-A */
+ };
+};
diff --git a/arch/mips/boot/dts/baikal/xa1-em406.dts b/arch/mips/boot/dts/baikal/xa1-em406.dts
new file mode 100644
index 0000000000000..3d548233d4114
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/xa1-em406.dts
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Edelweiss EM406 debug board device tree
+ */
+
+/dts-v1/;
+
+#include "em406.dtsi"
+
+/ {
+ model = "Edelweiss EM406 XA1 Debug Board";
+ compatible = "edelweiss,xa1-em406", "edelweiss,em406", "baikal,bt1";
+
+ aliases {
+ mtd2 = &test_flash10;
+ mtd3 = &test_flash20;
+ };
+};
+
+&i2c0 {
+ test_temp: temperature-sensor@48 {
+ compatible = "ti,tmp102";
+ reg = <0x48>;
+ };
+};
+
+&i2c1 {
+ test_gpio0: gpio@22 {
+ compatible = "ti,tca6424";
+ reg = <0x22>;
+
+ gpio-controller; /* 24 */
+ #gpio-cells = <2>;
+
+ gpio-line-names = "BT_GPIO0", "BT_GPIO1", "BT_GPIO2", "BT_GPIO3",
+ "BT_GPIO4", "BT_GPIO5", "BT_GPIO6", "BT_GPIO7",
+ "BT_GPIO8", "BT_GPIO9", "BT_GPIO10", "BT_GPIO11",
+ "BT_GPIO12", "BT_GPIO13", "BT_GPIO14", "BT_GPIO15",
+ "BT_GPIO16", "BT_GPIO17", "BT_GPIO18", "BT_GPIO19",
+ "BT_GPIO20", "BT_GPIO21", "BT_GPIO22", "";
+ };
+
+ test_gpio1: gpio@23 {
+ compatible = "ti,tca6424";
+ reg = <0x23>;
+
+ gpio-controller; /* 24 */
+ #gpio-cells = <2>;
+
+ gpio-line-names = "BT_GPIO23", "BT_GPIO24", "BT_GPIO26", "BT_GPIO27",
+ "BT_GPIO28", "BT_GPIO29", "BT_GPIO30", "BT_GPIO31",
+ "nLID", "nSLEEP", "nCHRG", "nCHRG_PRSNT",
+ "nLOWBAT", "nCAR_STB", "nPCI_WAKE", "CAR_PWR_ON",
+ "nRST_OUT", "nPCI_ATT_BUT", "SPI1_CS_SEL0",
+ "SPI1_CS_SEL1", "SPI2_CS_SEL0", "SPI2_CS_SEL1",
+ "", "";
+ };
+};
+
+&spi1 {
+ num-cs = <4>;
+
+ /* CS is determined by test_gpio1:SPI1_CS_SEL[0:1] */
+
+ /* Micron MT25QU128ABA1 */
+ test_flash10: flash@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+
+ m25p,fast-read;
+ spi-max-frequency = <25000000>;
+ };
+};
+
+&spi2 {
+ num-cs = <4>;
+
+ /* CS is determined by test_gpio1:SPI2_CS_SEL[0:1] */
+
+ /* Micron MT25QU128ABA1 */
+ test_flash20: flash@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+
+ m25p,fast-read;
+ spi-max-frequency = <25000000>;
+ };
+};
diff --git a/arch/mips/boot/dts/baikal/xa1-em416.dts b/arch/mips/boot/dts/baikal/xa1-em416.dts
new file mode 100644
index 0000000000000..2fc92f906f1e0
--- /dev/null
+++ b/arch/mips/boot/dts/baikal/xa1-em416.dts
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Edelweiss EM416 debug board device tree
+ */
+
+/dts-v1/;
+
+#include "em416.dtsi"
+
+/ {
+ model = "Edelweiss EM416 XA1 Debug Board";
+ compatible = "edelweiss,xa1-em416", "edelweiss,em416", "baikal,bt1";
+
+ aliases {
+ mtd2 = &test_flash20;
+ };
+};
+
+&i2c1 {
+ test_gpio0: gpio@20 {
+ compatible = "nxp,pca9535";
+ reg = <0x20>;
+
+ gpio-controller; /* 16 */
+ #gpio-cells = <2>;
+
+ gpio-line-names = "BMC_GPIO0", "BMC_GPIO1", "BMC_GPIO2", "BMC_GPIO3",
+ "BT_GPIO21", "BT_GPIO22", "BT_GPIO23", "BT_GPIO24",
+ "BT_GPIO26", "BT_GPIO27", "BT_GPIO28", "BT_GPIO29",
+ "BT_GPIO30", "BT_GPIO31", "SPI2_CS_SEL0",
+ "SPI2_CS_SEL1";
+ };
+};
+
+&spi2 {
+ num-cs = <4>;
+
+ /* CS is determined by test_gpio0:SPI2_CS_SEL[0:1] */
+
+ /* Micron MT25QU128ABA1 */
+ test_flash20: flash@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0>;
+
+ m25p,fast-read;
+ spi-max-frequency = <25000000>;
+ };
+};
diff --git a/arch/mips/bt1/Kconfig b/arch/mips/bt1/Kconfig
new file mode 100644
index 0000000000000..203639122e16b
--- /dev/null
+++ b/arch/mips/bt1/Kconfig
@@ -0,0 +1,145 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+#
+# Baikal-T1 platform configs
+#
+if MIPS_BAIKAL_T1
+
+config BT1_DEBUG
+ bool "Enable SoC/kernel debug options"
+ select EXPERT
+ select DEBUG_KERNEL
+ select DEBUG_ZBOOT
+ select DEBUG_MEMORY_INIT
+ select DEBUG_HIGHMEM if HIGHMEM
+ select DEBUG_STACKOVERFLOW
+ select RCU_TRACE
+ select EDAC_DEBUG if EDAC
+ select SCACHE_DEBUGFS
+ select GENERIC_IRQ_DEBUGFS
+ select CMA_DEBUGFS if CMA
+ select MIPS_CPS_NS16550_BOOL if MIPS_CPS
+ help
+ Use this option if you at the process of the kernel drivers
+ platform code development.
+
+config BT1_EARLY_UART
+ int "Default UART device for early printk and zboot"
+ range 0 1
+ default 0
+ help
+ There are two DW APB UART-based serial interfaces available on
+ Baikal-T1 SoC. By this option you can select one of them to be used
+ to print early logs and zboot debug symbols. Note having both
+ EARLY_PRINTK and SERIAL_EARLYCON configs enabled is prune to
+ getting duplicated log messages if both of these sub-systems are
+ using the same console. In case if you need to have the logs on both
+ UART devices make sure that this parameter and 'stdout-path' DT
+ property point to the different serial devices.
+
+config BT1_CPU_FEATURE_OVERRIDES
+ bool "Declare CPU features"
+ help
+ By default nearly all the MIPS IP-core features are detectable on
+ runtime. Corresponding cpu_has_* flags are constantly checked in
+ the code to enabled/disable corresponding platform features. Since
+ we indend to build the Baikal-T1 CPU specific kernel there is no
+ need in such flexibility, so we can freely define these flags with
+ values known at build-time. By doing so we not only decrease the
+ kernel size, but also speed it up.
+
+ If unsure, say N.
+
+config BT1_SWIOTLB_SIZE
+ int "SWIOTLB size in MiB" if SWIOTLB
+ range 4 64
+ default 8
+ help
+ Due to the Baikal-T1 main interconnect controller invalid synthesis
+ parameters, SATA/USB/GMACx aren't able to access the physical memory
+ higher than 4GiB. So in case if XPA is enabled and bootloader states
+ there is more than 4GiB of physical memory, we need to have the
+ SWIOTLB declared. Since by default SWIOTLB consumes too much memory
+ we create a custom table with compile-time configurable buffer size.
+
+choice
+ prompt "Baikal-T1 SoC based boards devicetree"
+ default BT1_DTB_NONE
+ help
+ Select a devicetree of the board with Baikal-T1 SoC installed.
+
+config BT1_DTB_NONE
+ bool "None"
+
+config BT1_DTB_ALL
+ bool "All"
+
+config BT1_DTB_GENERIC
+ bool "Generic Baikal-T1 Board"
+ help
+ This option provides a dtb for the generic Baikal-T1 boards. It just
+ activates all the Baikal-T1 SoC peripherals. So all the run-time
+ detectable and pre-initialized devices will work out-of-box while
+ the rest of the platform devices will be left untouched.
+
+config BT1_DTB_BFKP
+ bool "Baikal Electronics BFK v1.6+/v2.x/v3.x"
+ help
+ This option provides dtbs for the Baikal Electronics BFK v1.6+, v2.x
+ and v3.x boards equipped with the production version of the chip.
+ Naturally these are the Baikal-T1 SoC evaluation boards specifically
+ designed for the SoC-based platforms and software prototyping.
+
+config BT1_DTB_EM406
+ bool "Edelweiss EM406"
+ help
+ This option provides a dtb for the Edelweiss EM406 Mezzanine
+ Card-based boards. It has been designed as 82x80 SMARC v1.1 (Smart
+ Mobility ARChitecture v1.1) module with all the chip interfaces
+ connected to the edge fingers and destined to simplify the Baikal-T1
+ SoC based platforms development.
+
+config BT1_DTB_EM416
+ bool "Edelweiss EM416"
+ help
+ This option provides a dtb for the Edelweiss EM416 Mezzanine
+ Card-based boards. It has been designed to work in the industrial
+ environment as a replaceable module with almost all the chip
+ interfaces connected to the edge fingers and destined to simplify
+ the Baikal-T1 SoC based critical environment platforms development.
+
+config BT1_DTB_TF306
+ bool "Edelweiss TF306"
+ help
+ This option provides a dtb for the Edelweiss TF306 mini-ITX board.
+ It is based on the Baikal-T1 SoC designed to be of mITX form-factor
+ with DDR3 SoDIMM connector, Silicon Motion SM750 graphic chip,
+ ALC4040 HD audio and high-speed interfaces like SATA, USB2.0,
+ 1Gbit Ethernet, microSD.
+
+endchoice
+
+menu "Baikal-T1 Errata"
+
+config BT1_ERRATA_JR_LS_BUG
+ bool "Fix load/store bonding and JR prediction bug"
+ help
+ Early Baikal-T1 chips had problems when load/store bonding and JR
+ prediction were enabled. Switch these features off if you are using
+ the engineering version of the chip.
+
+ If unsure, say N.
+
+config BT1_ERRATA_GMAC_SPEED_INV_BUG
+ bool "Fix DW GMAC 10/100Mbit link speed bug"
+ help
+ DW GMAC on early Baikal-T1 chip releases had an inverted 10/100Mbit
+ MAC speed settings. So when 10Mbit link is requested then 100Mbit MAC
+ link speed should be setup and vise-versa.
+
+ If unsure, say N.
+
+endmenu
+
+endif # MIPS_BAIKAL_T1
diff --git a/arch/mips/bt1/Makefile b/arch/mips/bt1/Makefile
new file mode 100644
index 0000000000000..9052fa7ca6f6d
--- /dev/null
+++ b/arch/mips/bt1/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+#
+# Baikal-T1 platform code makefile
+#
+obj-y += init.o irq.o
+
+obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
diff --git a/arch/mips/bt1/Platform b/arch/mips/bt1/Platform
new file mode 100644
index 0000000000000..82016b641af91
--- /dev/null
+++ b/arch/mips/bt1/Platform
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+#
+# Baikal-T1 platform makefile
+#
+platform-$(CONFIG_MIPS_BAIKAL_T1) += bt1/
+cflags-$(CONFIG_MIPS_BAIKAL_T1) += -I$(srctree)/arch/mips/include/asm/mach-bt1
+ifdef CONFIG_EVA
+ load-$(CONFIG_MIPS_BAIKAL_T1) += 0xffffffff00100000
+ zload-$(CONFIG_MIPS_BAIKAL_T1) += 0xffffffff00100000
+else
+ load-$(CONFIG_MIPS_BAIKAL_T1) += 0xffffffff80100000
+ zload-$(CONFIG_MIPS_BAIKAL_T1) += 0xffffffff85100000
+endif
+all-$(CONFIG_MIPS_BAIKAL_T1) := $(COMPRESSION_FNAME).bin
diff --git a/arch/mips/bt1/early_printk.c b/arch/mips/bt1/early_printk.c
new file mode 100644
index 0000000000000..949741decf323
--- /dev/null
+++ b/arch/mips/bt1/early_printk.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ * Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
+ * Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 early printk
+ */
+#include <linux/args.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/serial_reg.h>
+
+#include <asm/setup.h>
+
+#include <asm/mach-bt1/memory.h>
+
+#define BT1_UART_BASE(_id) \
+ (void *)KSEG1ADDR(CONCATENATE(BT1_UART, CONCATENATE(_id, _BASE)))
+
+void prom_putchar(char c)
+{
+ void __iomem *uart_base = BT1_UART_BASE(CONFIG_BT1_EARLY_UART);
+ unsigned int timeout = 50000;
+ int status, bits;
+
+ bits = UART_LSR_TEMT | UART_LSR_THRE;
+
+ do {
+ status = __raw_readl(uart_base + (UART_LSR << 2));
+
+ if (--timeout == 0)
+ break;
+ } while ((status & bits) != bits);
+
+ if (timeout)
+ __raw_writel(c, uart_base + (UART_TX << 2));
+}
diff --git a/arch/mips/bt1/init.c b/arch/mips/bt1/init.c
new file mode 100644
index 0000000000000..ce3104087f495
--- /dev/null
+++ b/arch/mips/bt1/init.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ * Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
+ * Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 platform initialization
+ */
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/libfdt.h>
+#include <linux/limits.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_clk.h>
+#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/swiotlb.h>
+#include <linux/sys_soc.h>
+
+#include <asm/bootinfo.h>
+#include <asm/cdmm.h>
+#include <asm/cpu-info.h>
+#include <asm/io.h>
+#include <asm/mips-cps.h>
+#include <asm/mipsregs.h>
+#include <asm/pci.h>
+#include <asm/pgtable.h>
+#include <asm/prom.h>
+#include <asm/setup.h>
+#include <asm/smp-ops.h>
+#include <asm/smp.h>
+#include <asm/time.h>
+
+#include <asm/mach-bt1/memory.h>
+
+static __initdata const void *fdt;
+
+/*
+ * The following configuration have been used to synthesize the Baikal-T1
+ * MIPS Warroir P5600 core:
+ * 1) SI_EVAReset = 0 - boot in legacy (not EVA) memory layout mode after
+ * reset.
+ * 2) SI_UseExceptionBase = 0 - core uses legacy BEV mode, which selects
+ * 0xBFC00000 to be exception vector by default after reset.
+ * 3) SI_ExceptionBase[31:12] = 0xBFC00000 - externally set default exception
+ * SI_ExceptionBasePA[31:29] = 0x0 base address. It is used when
+ * CP0.CONFIG5.K = 1.
+ * 4) SI_EICPresent = 0 - even though GIC is always attached to the cores,
+ * this pin is hardwaired to the state of the
+ * GIC_VX_CTL_EIC bit.
+ */
+
+/*
+ * Redefine the MIPS CDMM phys base method to be used at the earliest boot
+ * stage before DT is parsed.
+ */
+#ifdef CONFIG_MIPS_EJTAG_FDC_EARLYCON
+
+phys_addr_t mips_cdmm_phys_base(void)
+{
+ return BT1_P5600_CDMM_BASE;
+}
+
+#endif /* CONFIG_MIPS_EJTAG_FDC_EARLYCON */
+
+/*
+ * We have to redefine the L2-sync phys base method, since the default
+ * region overlaps the Baikal-T1 boot memory following the CM2 GCRs.
+ */
+phys_addr_t mips_cm_l2sync_phys_base(void)
+{
+ return BT1_P5600_GCR_L2SYNC_BASE;
+}
+
+void __init *plat_get_fdt(void)
+{
+ const char *str;
+
+ /* Return already found fdt. */
+ if (fdt)
+ return (void *)fdt;
+
+ /*
+ * Generic method will search for appended, built-in and UHI DTBs.
+ * Some older version of Baikal-T1 bootloader could also pass DTB via
+ * the FW arg3 slot. So check that option too.
+ */
+ fdt = get_fdt();
+ if (fdt) {
+ str = (fw_arg0 == -2) ? "UHI" : "Built-in/Appended";
+ } else if (fw_arg3) {
+ str = "Legacy";
+ fdt = (void *)fw_arg3;
+ } else {
+ panic("No DTB found. Can't continue.");
+ }
+
+ /*
+ * DTB base address can be as physical address in case of passed as
+ * legacy arg3 or as address within legacy KSEG0 region passed by
+ * U-boot, but always in the low 128MB. Convert the address to the
+ * kernel virtual space for unification and EVA support as well.
+ */
+ fdt = phys_to_virt(CPHYSADDR(fdt));
+
+ if (fdt_check_header(fdt))
+ panic("No valid DTB found. Can't continue.");
+
+ pr_info("DTB found at: 0x%lx (%s)\n", (unsigned long)fdt, str);
+
+ return (void *)fdt;
+}
+
+#ifdef CONFIG_RELOCATABLE
+
+void __init plat_fdt_relocated(void *new_location)
+{
+ fdt = NULL;
+
+ /*
+ * Forget about the way dtb has been passed at the system startup. Use
+ * UHI always.
+ */
+ fw_arg0 = -2;
+ fw_arg1 = (unsigned long)new_location;
+}
+
+#endif /* CONFIG_RELOCATABLE */
+
+void __init prom_init(void)
+{
+ if (IS_ENABLED(CONFIG_EVA) && (read_c0_config5() & MIPS_CONF5_K))
+ pr_info("Enhanced Virtual Addressing (EVA) enabled\n");
+
+ /*
+ * Disable Legacy SYNC transaction performed on the L2/Memory port.
+ * This shall significantly improve the concurrent MMIO access
+ * performance.
+ */
+ change_gcr_control(CM_GCR_CONTROL_SYNCDIS, CM_GCR_CONTROL_SYNCDIS);
+
+ plat_get_fdt();
+}
+
+void __init plat_mem_setup(void)
+{
+ memblock_add(BT1_LOMEM_BASE, BT1_LOMEM_SIZE);
+
+ memblock_add(BT1_HIMEM_BASE, BT1_HIMEM_SIZE);
+
+#ifdef CONFIG_PCI
+ PCIBIOS_MIN_IO = 0x100;
+#endif
+
+ __dt_setup_arch((void *)fdt);
+}
+
+void __init device_tree_init(void)
+{
+ int err;
+
+ unflatten_and_copy_device_tree();
+
+ mips_cpc_probe();
+
+ err = register_cps_smp_ops();
+ if (err)
+ err = register_up_smp_ops();
+}
+
+#ifdef CONFIG_SWIOTLB
+
+void __init plat_swiotlb_setup(void)
+{
+ phys_addr_t top;
+
+ /*
+ * Skip SWIOTLB initialization since there is no that much memory to
+ * cause the peripherals invalid access.
+ */
+ top = memblock_end_of_DRAM();
+ if (top <= SIZE_MAX)
+ return;
+
+ /*
+ * Override the default SWIOTLB size with the configuration value.
+ * Note a custom size has been passed via the kernel parameter it won't
+ * be overwritten.
+ */
+ swiotlb_adjust_size(CONFIG_BT1_SWIOTLB_SIZE * SZ_1M);
+ swiotlb_init(true, SWIOTLB_VERBOSE);
+}
+
+#endif /* CONFIG_SWIOTLB */
+
+void __init prom_free_prom_memory(void) {}
+
+#define HZ_TO_MHZ(_hz) (_hz / 1000000)
+#define HZ_GET_KHZ(_hz) ((_hz / 1000) % 1000)
+void __init plat_time_init(void)
+{
+ struct device_node *np;
+ unsigned long rate;
+ struct clk *clk;
+
+ of_clk_init(NULL);
+
+ np = of_get_cpu_node(0, NULL);
+ if (!np) {
+ pr_err("Failed to get CPU of node\n");
+ goto err_timer_probe;
+ }
+
+ clk = of_clk_get(np, 0);
+ if (IS_ERR(clk)) {
+ pr_err("Failed to get CPU clock (%ld)\n", PTR_ERR(clk));
+ goto err_timer_probe;
+ }
+
+ /* CPU count/compare timer runs at half the CPU frequency. */
+ rate = clk_get_rate(clk);
+ mips_hpt_frequency = rate / 2;
+
+ pr_info("MIPS CPU frequency: %lu.%03lu MHz\n",
+ HZ_TO_MHZ(rate), HZ_GET_KHZ(rate));
+ pr_info("MIPS CPU count/compare timer frequency: %u.%03u MHz\n",
+ HZ_TO_MHZ(mips_hpt_frequency), HZ_GET_KHZ(mips_hpt_frequency));
+
+ clk_put(clk);
+
+err_timer_probe:
+ timer_probe();
+}
+
+const char *get_system_type(void)
+{
+ return "Baikal-T1 SoC";
+}
+
+static struct bt1_soc {
+ struct soc_device_attribute dev_attr;
+ char revision[16];
+ char id[16];
+} soc;
+
+static int __init soc_setup(void)
+{
+ unsigned int cpuid = boot_cpu_data.processor_id;
+ struct soc_device *soc_dev;
+ struct device *parent = NULL;
+ int ret = 0;
+
+ soc.dev_attr.machine = mips_get_machine_name();
+ soc.dev_attr.family = get_system_type();
+ soc.dev_attr.revision = soc.revision;
+ soc.dev_attr.soc_id = soc.id;
+
+ snprintf(soc.revision, sizeof(soc.revision) - 1, "%u.%u.%u",
+ (cpuid >> 5) & 0x07, (cpuid >> 2) & 0x07, cpuid & 0x03);
+ snprintf(soc.id, sizeof(soc.id) - 1, "0x%08X",
+ readl(phys_to_virt(BT1_BOOT_CTRL_BASE + BT1_BOOT_CTRL_DRID)));
+
+ soc_dev = soc_device_register(&soc.dev_attr);
+ if (IS_ERR(soc_dev)) {
+ ret = PTR_ERR(soc_dev);
+ goto err_return;
+ }
+
+ parent = soc_device_to_device(soc_dev);
+
+err_return:
+ return ret;
+}
+arch_initcall(soc_setup);
diff --git a/arch/mips/bt1/irq.c b/arch/mips/bt1/irq.c
new file mode 100644
index 0000000000000..9890dd0ee1b3d
--- /dev/null
+++ b/arch/mips/bt1/irq.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 IRQ initialization
+ */
+#include <linux/irqchip.h>
+
+#include <asm/irq.h>
+#include <asm/irq_cpu.h>
+#include <asm/mips-cps.h>
+#include <asm/mipsregs.h>
+#include <asm/time.h>
+
+int get_c0_fdc_int(void)
+{
+ return gic_get_c0_fdc_int();
+}
+
+int get_c0_perfcount_int(void)
+{
+ return gic_get_c0_perfcount_int();
+}
+
+unsigned int get_c0_compare_int(void)
+{
+ return gic_get_c0_compare_int();
+}
+
+/*
+ * If CP0.Cause.IV == 1 and cpu_has_veic = 1 the next method isn't supposed
+ * to be called ever. Otherwise we just handle a vectored interrupt, which was
+ * routed to the generic exception vector.
+ */
+#if !defined(CONFIG_IRQ_MIPS_CPU)
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ extern unsigned long vi_handlers[];
+ unsigned int cause = (read_c0_cause() & CAUSEF_IP) >> CAUSEB_IP2;
+ void (*isr)(void) = (void *)vi_handlers[cause];
+
+ if (cause && isr)
+ isr();
+ else if (cause && !isr)
+ panic("Vectored interrupt %u handler is empty\n", cause);
+ else
+ spurious_interrupt();
+}
+
+#endif /* !CONFIG_IRQ_MIPS_CPU */
+
+void __init arch_init_irq(void)
+{
+ if (!cpu_has_veic)
+ mips_cpu_irq_init();
+
+ irqchip_init();
+}
diff --git a/arch/mips/configs/baikal_t1_defconfig b/arch/mips/configs/baikal_t1_defconfig
new file mode 100644
index 0000000000000..424717abb3fb8
--- /dev/null
+++ b/arch/mips/configs/baikal_t1_defconfig
@@ -0,0 +1,480 @@
+CONFIG_LOCALVERSION="-bt1"
+CONFIG_DEFAULT_HOSTNAME="baikal"
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CLOCKSOURCE_WATCHDOG_MAX_SKEW_US=100
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_JIT=y
+CONFIG_BPF_JIT_ALWAYS_ON=y
+CONFIG_BPF_PRELOAD=y
+CONFIG_BPF_PRELOAD_UMD=y
+CONFIG_PREEMPT=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_CGROUP_MISC=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_CHECKPOINT_RESTORE=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CMDLINE_LOG_WRAP_IDEAL_LEN=100
+CONFIG_SYSFS_SYSCALL=y
+CONFIG_EXPERT=y
+CONFIG_PROFILING=y
+CONFIG_MIPS_BAIKAL_T1=y
+CONFIG_BT1_CPU_FEATURE_OVERRIDES=y
+CONFIG_BT1_DTB_ALL=y
+CONFIG_CPU_P5600=y
+CONFIG_CPU_MIPS32_R5_FEATURES=y
+CONFIG_CPU_MIPS32_R5_XPA=y
+CONFIG_ZBOOT_LOAD_ADDRESS=0x85100000
+CONFIG_ARCH_FORCE_MAX_ORDER=11
+CONFIG_CPU_HAS_MSA=y
+CONFIG_DMI=y
+CONFIG_NR_CPUS=2
+CONFIG_IEEE754_DEFAULT_RELAXED=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_PAGE_SIZE_16KB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_UNLOAD_TAINT_TRACKING=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLOCK_LEGACY_AUTOLOAD is not set
+CONFIG_BLK_DEV_BSGLIB=y
+# CONFIG_IOSCHED_BFQ is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_PACKET_DIAG=y
+CONFIG_UNIX=y
+CONFIG_UNIX_DIAG=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_XDP_SOCKETS=y
+CONFIG_XDP_SOCKETS_DIAG=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_NET_IPIP=y
+CONFIG_NET_IPGRE_DEMUX=y
+CONFIG_NET_IPGRE=y
+CONFIG_NET_FOU_IP_TUNNELS=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
+CONFIG_INET_RAW_DIAG=y
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=y
+CONFIG_TCP_CONG_WESTWOOD=y
+CONFIG_TCP_CONG_HTCP=y
+CONFIG_TCP_CONG_HSTCP=y
+CONFIG_TCP_CONG_BBR=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_GRE=y
+CONFIG_IPV6_SEG6_LWTUNNEL=y
+CONFIG_MPTCP=y
+CONFIG_NETWORK_PHY_TIMESTAMPING=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_PROCFS=y
+CONFIG_NF_TABLES=y
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_LOG=y
+CONFIG_NFT_LIMIT=y
+CONFIG_NFT_MASQ=y
+CONFIG_NFT_REDIR=y
+CONFIG_NFT_NAT=y
+CONFIG_NFT_REJECT=y
+CONFIG_NFT_HASH=y
+CONFIG_NETFILTER_XT_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_NAT=y
+CONFIG_NETFILTER_XT_TARGET_NETMAP=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=y
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_CGROUP=y
+CONFIG_NETFILTER_XT_MATCH_CPU=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_OWNER=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_RECENT=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=y
+CONFIG_NFT_DUP_IPV4=y
+CONFIG_NF_TABLES_ARP=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_TARGET_SYNPROXY=y
+CONFIG_NFT_DUP_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_EUI64=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_BRIDGE=y
+CONFIG_BRIDGE_VLAN_FILTERING=y
+CONFIG_VLAN_8021Q=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_SFQ=y
+CONFIG_NET_SCH_CBS=y
+CONFIG_NET_SCH_ETF=y
+CONFIG_NET_SCH_TAPRIO=y
+CONFIG_NET_SCH_MQPRIO=y
+CONFIG_NET_SCH_SKBPRIO=y
+CONFIG_NET_SCH_QFQ=y
+CONFIG_NET_SCH_FQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_SCH_DEFAULT=y
+CONFIG_NET_CLS_BASIC=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_PERF=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_CLS_BPF=y
+CONFIG_NET_CLS_FLOWER=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_PEDIT=y
+CONFIG_NET_ACT_SIMP=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_NET_ACT_VLAN=y
+CONFIG_NET_ACT_BPF=y
+CONFIG_NET_ACT_SKBMOD=y
+CONFIG_DCB=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_NETLINK_DIAG=y
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_CGROUP_NET_CLASSID=y
+CONFIG_BPF_STREAM_PARSER=y
+CONFIG_NET_PKTGEN=y
+# CONFIG_WIRELESS is not set
+CONFIG_PAGE_POOL_STATS=y
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEAER_INJECT=y
+CONFIG_PCIE_ECRC=y
+CONFIG_PCIEASPM_PERFORMANCE=y
+CONFIG_PCI_MSI=y
+CONFIG_PCIE_BUS_PERFORMANCE=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_PCIE_BT1=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DEBUG_DRIVER=y
+CONFIG_DEBUG_DEVRES=y
+CONFIG_BT1_APB=y
+CONFIG_BT1_AXI=y
+CONFIG_MIPS_CDMM=y
+CONFIG_DMI_SYSFS=y
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONED_MASTER=y
+CONFIG_MTD_ROM=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_PHYSMAP_BT1_ROM=y
+CONFIG_MTD_SPI_NOR=y
+# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BLOCK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=4
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=2
+CONFIG_BLK_DEV_RAM_SIZE=32768
+CONFIG_BLK_DEV_NVME=y
+CONFIG_NVME_MULTIPATH=y
+CONFIG_NVME_VERBOSE_ERRORS=y
+CONFIG_NVME_HWMON=y
+CONFIG_SRAM=y
+CONFIG_EEPROM_AT24=y
+CONFIG_RAID_ATTRS=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_AHCI_DWC=y
+# CONFIG_ATA_SFF is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_AGERE is not set
+# CONFIG_NET_VENDOR_ALACRITECH is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_VENDOR_AQUANTIA is not set
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_ASIX is not set
+# CONFIG_NET_VENDOR_ATHEROS is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CADENCE is not set
+# CONFIG_NET_VENDOR_CAVIUM is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_CORTINA is not set
+# CONFIG_NET_VENDOR_DAVICOM is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_ENGLEDER is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_FUNGIBLE is not set
+# CONFIG_NET_VENDOR_GOOGLE is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_HUAWEI is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_ADI is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_META is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MICROSEMI is not set
+# CONFIG_NET_VENDOR_MICROSOFT is not set
+# CONFIG_NET_VENDOR_MUCSE is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_NET_VENDOR_NI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_NET_VENDOR_PACKET_ENGINES is not set
+# CONFIG_NET_VENDOR_PENSANDO is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
+# CONFIG_NET_VENDOR_BROCADE is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_SOCIONEXT is not set
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_SELFTESTS=y
+CONFIG_DWMAC_BT1=y
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
+# CONFIG_NET_VENDOR_TOSHIBA is not set
+# CONFIG_NET_VENDOR_VERTEXCOM is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WANGXUN is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_NET_VENDOR_XILINX is not set
+CONFIG_SFP=y
+CONFIG_MARVELL_PHY=y
+CONFIG_MARVELL_10G_PHY=y
+CONFIG_MARVELL_88X2222_PHY=y
+CONFIG_MARVELL_88X2222_GPIO=y
+CONFIG_MARVELL_88X2222_I2C=y
+CONFIG_MICREL_PHY=y
+CONFIG_MICROCHIP_PHY=y
+CONFIG_MICROSEMI_PHY=y
+CONFIG_REALTEK_PHY=y
+CONFIG_SMSC_PHY=y
+CONFIG_VITESSE_PHY=y
+CONFIG_MDIO_BITBANG=y
+CONFIG_MDIO_GPIO=y
+# CONFIG_USB_NET_DRIVERS is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT_CONSOLE is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_LEGACY_PTY_COUNT=16
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_DW=y
+# CONFIG_SERIAL_8250_PERICOM is not set
+CONFIG_MIPS_EJTAG_FDC_TTY=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_DESIGNWARE_CORE=y
+CONFIG_I2C_SLAVE=y
+CONFIG_I2C_SLAVE_EEPROM=y
+CONFIG_SPI=y
+CONFIG_SPI_DESIGNWARE=y
+CONFIG_SPI_DW_DMA=y
+CONFIG_SPI_DW_MMIO=y
+CONFIG_SPI_DW_BT1=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_PCF857X=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SYSCON_REBOOT_MODE=y
+CONFIG_SENSORS_BT1_PVT=y
+CONFIG_SENSORS_BT1_PVT_ALARMS=y
+CONFIG_SENSORS_TMP102=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_STATISTICS=y
+CONFIG_THERMAL_GOV_FAIR_SHARE=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_CPU_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_SYSFS=y
+CONFIG_WATCHDOG_PRETIMEOUT_GOV=y
+CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP=y
+CONFIG_DW_WATCHDOG=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_HRTIMER=y
+# CONFIG_SND_PCI is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SND_MIPS is not set
+CONFIG_SND_USB_AUDIO=y
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_ULPI_BUS=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_UAS=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_ULPI=y
+# CONFIG_USB_DWC3_GENERIC_PLAT is not set
+CONFIG_USB_HUB_USB251XB=y
+CONFIG_MMC=y
+CONFIG_MMC_SPI=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_EDAC=y
+CONFIG_EDAC_DEBUG=y
+CONFIG_EDAC_SYNOPSYS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_ABEOZ9=y
+CONFIG_RTC_DRV_PCF85363=y
+CONFIG_RTC_DRV_PCF2127=y
+CONFIG_DMADEVICES=y
+CONFIG_DW_DMAC=y
+CONFIG_DW_EDMA=y
+CONFIG_SYNC_FILE=y
+# CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL is not set
+CONFIG_COMMON_CLK_VC5=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_MEMORY=y
+CONFIG_BT1_L2_CTL=y
+CONFIG_NVMEM_U_BOOT_ENV=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_FANOTIFY=y
+CONFIG_FUSE_FS=y
+# CONFIG_FUSE_IO_URING is not set
+CONFIG_OVERLAY_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_NTFS3_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_WBUF_VERIFY=y
+CONFIG_UBIFS_FS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V2=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_0=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_USE_LEGACY_DNS=y
+# CONFIG_NFS_V4_2_READ_PLUS is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_866=y
+CONFIG_NLS_CODEPAGE_1251=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_5=y
+CONFIG_NLS_KOI8_R=y
+CONFIG_NLS_UTF8=y
+CONFIG_INIT_STACK_NONE=y
+CONFIG_CRYPTO_USER=y
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_BLOWFISH=y
+CONFIG_CRYPTO_CCM=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_CRC32=y
+CONFIG_CRYPTO_USER_API_HASH=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO_DWARF4=y
+CONFIG_DEBUG_INFO_BTF=y
+CONFIG_FRAME_WARN=1024
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_VMLINUX_MAP=y
+CONFIG_SCHEDSTATS=y
+# CONFIG_EARLY_PRINTK is not set
+# CONFIG_RUNTIME_TESTING_MENU is not set
diff --git a/arch/mips/configs/bfkp_defconfig b/arch/mips/configs/bfkp_defconfig
new file mode 100644
index 0000000000000..d8862d2b7804d
--- /dev/null
+++ b/arch/mips/configs/bfkp_defconfig
@@ -0,0 +1,467 @@
+CONFIG_LOCALVERSION="-bt1"
+CONFIG_DEFAULT_HOSTNAME="baikal"
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CLOCKSOURCE_WATCHDOG_MAX_SKEW_US=100
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_JIT=y
+CONFIG_BPF_JIT_ALWAYS_ON=y
+CONFIG_BPF_PRELOAD=y
+CONFIG_BPF_PRELOAD_UMD=y
+CONFIG_PREEMPT=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_CGROUP_MISC=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_CHECKPOINT_RESTORE=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CMDLINE_LOG_WRAP_IDEAL_LEN=100
+CONFIG_SYSFS_SYSCALL=y
+CONFIG_EXPERT=y
+CONFIG_PROFILING=y
+CONFIG_MIPS_BAIKAL_T1=y
+CONFIG_BT1_CPU_FEATURE_OVERRIDES=y
+CONFIG_BT1_DTB_BFKP=y
+CONFIG_CPU_P5600=y
+CONFIG_CPU_MIPS32_R5_FEATURES=y
+CONFIG_CPU_MIPS32_R5_XPA=y
+CONFIG_ZBOOT_LOAD_ADDRESS=0x85100000
+CONFIG_ARCH_FORCE_MAX_ORDER=11
+CONFIG_CPU_HAS_MSA=y
+CONFIG_DMI=y
+CONFIG_NR_CPUS=2
+CONFIG_IEEE754_DEFAULT_RELAXED=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_PAGE_SIZE_16KB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_UNLOAD_TAINT_TRACKING=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLOCK_LEGACY_AUTOLOAD is not set
+CONFIG_BLK_DEV_BSGLIB=y
+# CONFIG_IOSCHED_BFQ is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_PACKET_DIAG=y
+CONFIG_UNIX=y
+CONFIG_UNIX_DIAG=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_XDP_SOCKETS=y
+CONFIG_XDP_SOCKETS_DIAG=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_NET_IPIP=y
+CONFIG_NET_IPGRE_DEMUX=y
+CONFIG_NET_IPGRE=y
+CONFIG_NET_FOU_IP_TUNNELS=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
+CONFIG_INET_RAW_DIAG=y
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=y
+CONFIG_TCP_CONG_WESTWOOD=y
+CONFIG_TCP_CONG_HTCP=y
+CONFIG_TCP_CONG_HSTCP=y
+CONFIG_TCP_CONG_BBR=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_GRE=y
+CONFIG_IPV6_SEG6_LWTUNNEL=y
+CONFIG_MPTCP=y
+CONFIG_NETWORK_PHY_TIMESTAMPING=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_PROCFS=y
+CONFIG_NF_TABLES=y
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_LOG=y
+CONFIG_NFT_LIMIT=y
+CONFIG_NFT_MASQ=y
+CONFIG_NFT_REDIR=y
+CONFIG_NFT_NAT=y
+CONFIG_NFT_REJECT=y
+CONFIG_NFT_HASH=y
+CONFIG_NETFILTER_XT_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_NAT=y
+CONFIG_NETFILTER_XT_TARGET_NETMAP=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=y
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_CGROUP=y
+CONFIG_NETFILTER_XT_MATCH_CPU=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_OWNER=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_RECENT=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=y
+CONFIG_NFT_DUP_IPV4=y
+CONFIG_NF_TABLES_ARP=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_TARGET_SYNPROXY=y
+CONFIG_NFT_DUP_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_EUI64=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_BRIDGE=y
+CONFIG_BRIDGE_VLAN_FILTERING=y
+CONFIG_VLAN_8021Q=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_SFQ=y
+CONFIG_NET_SCH_CBS=y
+CONFIG_NET_SCH_ETF=y
+CONFIG_NET_SCH_TAPRIO=y
+CONFIG_NET_SCH_MQPRIO=y
+CONFIG_NET_SCH_SKBPRIO=y
+CONFIG_NET_SCH_QFQ=y
+CONFIG_NET_SCH_FQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_SCH_DEFAULT=y
+CONFIG_NET_CLS_BASIC=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_PERF=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_CLS_BPF=y
+CONFIG_NET_CLS_FLOWER=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_PEDIT=y
+CONFIG_NET_ACT_SIMP=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_NET_ACT_VLAN=y
+CONFIG_NET_ACT_BPF=y
+CONFIG_NET_ACT_SKBMOD=y
+CONFIG_DCB=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_NETLINK_DIAG=y
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_CGROUP_NET_CLASSID=y
+CONFIG_BPF_STREAM_PARSER=y
+CONFIG_NET_PKTGEN=y
+# CONFIG_WIRELESS is not set
+CONFIG_PAGE_POOL_STATS=y
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEAER_INJECT=y
+CONFIG_PCIE_ECRC=y
+CONFIG_PCIEASPM_PERFORMANCE=y
+CONFIG_PCI_MSI=y
+CONFIG_PCIE_BUS_PERFORMANCE=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_PCIE_BT1=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DEBUG_DRIVER=y
+CONFIG_DEBUG_DEVRES=y
+CONFIG_BT1_APB=y
+CONFIG_BT1_AXI=y
+CONFIG_MIPS_CDMM=y
+CONFIG_DMI_SYSFS=y
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONED_MASTER=y
+CONFIG_MTD_ROM=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_PHYSMAP_BT1_ROM=y
+CONFIG_MTD_SPI_NOR=y
+# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BLOCK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=4
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=2
+CONFIG_BLK_DEV_RAM_SIZE=32768
+CONFIG_BLK_DEV_NVME=y
+CONFIG_NVME_MULTIPATH=y
+CONFIG_NVME_VERBOSE_ERRORS=y
+CONFIG_NVME_HWMON=y
+CONFIG_SRAM=y
+CONFIG_EEPROM_AT24=y
+CONFIG_RAID_ATTRS=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_AHCI_DWC=y
+# CONFIG_ATA_SFF is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_AGERE is not set
+# CONFIG_NET_VENDOR_ALACRITECH is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_VENDOR_AQUANTIA is not set
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_ASIX is not set
+# CONFIG_NET_VENDOR_ATHEROS is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CADENCE is not set
+# CONFIG_NET_VENDOR_CAVIUM is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_CORTINA is not set
+# CONFIG_NET_VENDOR_DAVICOM is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_ENGLEDER is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_FUNGIBLE is not set
+# CONFIG_NET_VENDOR_GOOGLE is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_HUAWEI is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_ADI is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_META is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MICROSEMI is not set
+# CONFIG_NET_VENDOR_MICROSOFT is not set
+# CONFIG_NET_VENDOR_MUCSE is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_NET_VENDOR_NI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_NET_VENDOR_PACKET_ENGINES is not set
+# CONFIG_NET_VENDOR_PENSANDO is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
+# CONFIG_NET_VENDOR_BROCADE is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_SOCIONEXT is not set
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_SELFTESTS=y
+CONFIG_DWMAC_BT1=y
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
+# CONFIG_NET_VENDOR_TOSHIBA is not set
+# CONFIG_NET_VENDOR_VERTEXCOM is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WANGXUN is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_NET_VENDOR_XILINX is not set
+CONFIG_SFP=y
+CONFIG_MARVELL_PHY=y
+CONFIG_MARVELL_10G_PHY=y
+CONFIG_MARVELL_88X2222_PHY=y
+CONFIG_MARVELL_88X2222_GPIO=y
+CONFIG_MARVELL_88X2222_I2C=y
+CONFIG_MICREL_PHY=y
+CONFIG_MDIO_BITBANG=y
+CONFIG_MDIO_GPIO=y
+# CONFIG_USB_NET_DRIVERS is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT_CONSOLE is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_LEGACY_PTY_COUNT=16
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_DW=y
+# CONFIG_SERIAL_8250_PERICOM is not set
+CONFIG_MIPS_EJTAG_FDC_TTY=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_DESIGNWARE_CORE=y
+CONFIG_I2C_SLAVE=y
+CONFIG_I2C_SLAVE_EEPROM=y
+CONFIG_SPI=y
+CONFIG_SPI_DESIGNWARE=y
+CONFIG_SPI_DW_DMA=y
+CONFIG_SPI_DW_MMIO=y
+CONFIG_SPI_DW_BT1=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_PCF857X=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SYSCON_REBOOT_MODE=y
+CONFIG_SENSORS_BT1_PVT=y
+CONFIG_SENSORS_BT1_PVT_ALARMS=y
+CONFIG_SENSORS_TMP102=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_STATISTICS=y
+CONFIG_THERMAL_GOV_FAIR_SHARE=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_CPU_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_SYSFS=y
+CONFIG_WATCHDOG_PRETIMEOUT_GOV=y
+CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP=y
+CONFIG_DW_WATCHDOG=y
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_USB_ULPI_BUS=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_UAS=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_ULPI=y
+# CONFIG_USB_DWC3_GENERIC_PLAT is not set
+CONFIG_USB_HUB_USB251XB=y
+CONFIG_MMC=y
+CONFIG_MMC_SPI=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_EDAC=y
+CONFIG_EDAC_DEBUG=y
+CONFIG_EDAC_SYNOPSYS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_ABEOZ9=y
+CONFIG_RTC_DRV_PCF85363=y
+CONFIG_DMADEVICES=y
+CONFIG_DW_DMAC=y
+CONFIG_DW_EDMA=y
+CONFIG_SYNC_FILE=y
+# CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL is not set
+CONFIG_COMMON_CLK_VC5=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_MEMORY=y
+CONFIG_BT1_L2_CTL=y
+CONFIG_NVMEM_U_BOOT_ENV=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_FANOTIFY=y
+CONFIG_FUSE_FS=y
+# CONFIG_FUSE_IO_URING is not set
+CONFIG_OVERLAY_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_NTFS3_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_WBUF_VERIFY=y
+CONFIG_UBIFS_FS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V2=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_0=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_USE_LEGACY_DNS=y
+# CONFIG_NFS_V4_2_READ_PLUS is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_866=y
+CONFIG_NLS_CODEPAGE_1251=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_5=y
+CONFIG_NLS_KOI8_R=y
+CONFIG_NLS_UTF8=y
+CONFIG_INIT_STACK_NONE=y
+CONFIG_CRYPTO_USER=y
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_BLOWFISH=y
+CONFIG_CRYPTO_CCM=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_CRC32=y
+CONFIG_CRYPTO_USER_API_HASH=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO_DWARF4=y
+CONFIG_DEBUG_INFO_BTF=y
+CONFIG_FRAME_WARN=1024
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_VMLINUX_MAP=y
+CONFIG_SCHEDSTATS=y
+# CONFIG_EARLY_PRINTK is not set
+# CONFIG_RUNTIME_TESTING_MENU is not set
diff --git a/arch/mips/configs/em406_defconfig b/arch/mips/configs/em406_defconfig
new file mode 100644
index 0000000000000..b7640fd948b4a
--- /dev/null
+++ b/arch/mips/configs/em406_defconfig
@@ -0,0 +1,458 @@
+CONFIG_LOCALVERSION="-bt1"
+CONFIG_DEFAULT_HOSTNAME="baikal"
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CLOCKSOURCE_WATCHDOG_MAX_SKEW_US=100
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_JIT=y
+CONFIG_BPF_JIT_ALWAYS_ON=y
+CONFIG_BPF_PRELOAD=y
+CONFIG_BPF_PRELOAD_UMD=y
+CONFIG_PREEMPT=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_CGROUP_MISC=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_CHECKPOINT_RESTORE=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CMDLINE_LOG_WRAP_IDEAL_LEN=100
+CONFIG_SYSFS_SYSCALL=y
+CONFIG_EXPERT=y
+CONFIG_PROFILING=y
+CONFIG_MIPS_BAIKAL_T1=y
+CONFIG_BT1_CPU_FEATURE_OVERRIDES=y
+CONFIG_BT1_DTB_EM406=y
+CONFIG_CPU_P5600=y
+CONFIG_CPU_MIPS32_R5_FEATURES=y
+CONFIG_CPU_MIPS32_R5_XPA=y
+CONFIG_ZBOOT_LOAD_ADDRESS=0x85100000
+CONFIG_ARCH_FORCE_MAX_ORDER=11
+CONFIG_CPU_HAS_MSA=y
+CONFIG_DMI=y
+CONFIG_NR_CPUS=2
+CONFIG_IEEE754_DEFAULT_RELAXED=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_PAGE_SIZE_16KB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_UNLOAD_TAINT_TRACKING=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLOCK_LEGACY_AUTOLOAD is not set
+CONFIG_BLK_DEV_BSGLIB=y
+# CONFIG_IOSCHED_BFQ is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_PACKET_DIAG=y
+CONFIG_UNIX=y
+CONFIG_UNIX_DIAG=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_XDP_SOCKETS=y
+CONFIG_XDP_SOCKETS_DIAG=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_NET_IPIP=y
+CONFIG_NET_IPGRE_DEMUX=y
+CONFIG_NET_IPGRE=y
+CONFIG_NET_FOU_IP_TUNNELS=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
+CONFIG_INET_RAW_DIAG=y
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=y
+CONFIG_TCP_CONG_WESTWOOD=y
+CONFIG_TCP_CONG_HTCP=y
+CONFIG_TCP_CONG_HSTCP=y
+CONFIG_TCP_CONG_BBR=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_GRE=y
+CONFIG_IPV6_SEG6_LWTUNNEL=y
+CONFIG_MPTCP=y
+CONFIG_NETWORK_PHY_TIMESTAMPING=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_PROCFS=y
+CONFIG_NF_TABLES=y
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_LOG=y
+CONFIG_NFT_LIMIT=y
+CONFIG_NFT_MASQ=y
+CONFIG_NFT_REDIR=y
+CONFIG_NFT_NAT=y
+CONFIG_NFT_REJECT=y
+CONFIG_NFT_HASH=y
+CONFIG_NETFILTER_XT_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_NAT=y
+CONFIG_NETFILTER_XT_TARGET_NETMAP=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=y
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_CGROUP=y
+CONFIG_NETFILTER_XT_MATCH_CPU=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_OWNER=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_RECENT=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=y
+CONFIG_NFT_DUP_IPV4=y
+CONFIG_NF_TABLES_ARP=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_TARGET_SYNPROXY=y
+CONFIG_NFT_DUP_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_EUI64=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_BRIDGE=y
+CONFIG_BRIDGE_VLAN_FILTERING=y
+CONFIG_VLAN_8021Q=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_SFQ=y
+CONFIG_NET_SCH_CBS=y
+CONFIG_NET_SCH_ETF=y
+CONFIG_NET_SCH_TAPRIO=y
+CONFIG_NET_SCH_MQPRIO=y
+CONFIG_NET_SCH_SKBPRIO=y
+CONFIG_NET_SCH_QFQ=y
+CONFIG_NET_SCH_FQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_SCH_DEFAULT=y
+CONFIG_NET_CLS_BASIC=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_PERF=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_CLS_BPF=y
+CONFIG_NET_CLS_FLOWER=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_PEDIT=y
+CONFIG_NET_ACT_SIMP=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_NET_ACT_VLAN=y
+CONFIG_NET_ACT_BPF=y
+CONFIG_NET_ACT_SKBMOD=y
+CONFIG_DCB=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_NETLINK_DIAG=y
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_CGROUP_NET_CLASSID=y
+CONFIG_BPF_STREAM_PARSER=y
+CONFIG_NET_PKTGEN=y
+# CONFIG_WIRELESS is not set
+CONFIG_PAGE_POOL_STATS=y
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEAER_INJECT=y
+CONFIG_PCIE_ECRC=y
+CONFIG_PCIEASPM_PERFORMANCE=y
+CONFIG_PCI_MSI=y
+CONFIG_PCIE_BUS_PERFORMANCE=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_PCIE_BT1=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DEBUG_DRIVER=y
+CONFIG_DEBUG_DEVRES=y
+CONFIG_BT1_APB=y
+CONFIG_BT1_AXI=y
+CONFIG_MIPS_CDMM=y
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONED_MASTER=y
+CONFIG_MTD_ROM=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_PHYSMAP_BT1_ROM=y
+CONFIG_MTD_SPI_NOR=y
+# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BLOCK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=4
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=2
+CONFIG_BLK_DEV_RAM_SIZE=32768
+CONFIG_BLK_DEV_NVME=y
+CONFIG_NVME_MULTIPATH=y
+CONFIG_NVME_VERBOSE_ERRORS=y
+CONFIG_NVME_HWMON=y
+CONFIG_SRAM=y
+CONFIG_EEPROM_AT24=y
+CONFIG_RAID_ATTRS=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_AHCI_DWC=y
+# CONFIG_ATA_SFF is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_AGERE is not set
+# CONFIG_NET_VENDOR_ALACRITECH is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_VENDOR_AQUANTIA is not set
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_ASIX is not set
+# CONFIG_NET_VENDOR_ATHEROS is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CADENCE is not set
+# CONFIG_NET_VENDOR_CAVIUM is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_CORTINA is not set
+# CONFIG_NET_VENDOR_DAVICOM is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_ENGLEDER is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_FUNGIBLE is not set
+# CONFIG_NET_VENDOR_GOOGLE is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_HUAWEI is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_ADI is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_META is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MICROSEMI is not set
+# CONFIG_NET_VENDOR_MICROSOFT is not set
+# CONFIG_NET_VENDOR_MUCSE is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_NET_VENDOR_NI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_NET_VENDOR_PACKET_ENGINES is not set
+# CONFIG_NET_VENDOR_PENSANDO is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
+# CONFIG_NET_VENDOR_BROCADE is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_SOCIONEXT is not set
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_SELFTESTS=y
+CONFIG_DWMAC_BT1=y
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
+# CONFIG_NET_VENDOR_TOSHIBA is not set
+# CONFIG_NET_VENDOR_VERTEXCOM is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WANGXUN is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_NET_VENDOR_XILINX is not set
+CONFIG_SFP=y
+CONFIG_MARVELL_PHY=y
+CONFIG_MARVELL_10G_PHY=y
+CONFIG_MARVELL_88X2222_PHY=y
+CONFIG_MARVELL_88X2222_GPIO=y
+CONFIG_MARVELL_88X2222_I2C=y
+CONFIG_REALTEK_PHY=y
+CONFIG_MDIO_BITBANG=y
+CONFIG_MDIO_GPIO=y
+# CONFIG_USB_NET_DRIVERS is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT_CONSOLE is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_LEGACY_PTY_COUNT=16
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_DW=y
+# CONFIG_SERIAL_8250_PERICOM is not set
+CONFIG_MIPS_EJTAG_FDC_TTY=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_DESIGNWARE_CORE=y
+CONFIG_I2C_SLAVE=y
+CONFIG_I2C_SLAVE_EEPROM=y
+CONFIG_SPI=y
+CONFIG_SPI_DESIGNWARE=y
+CONFIG_SPI_DW_DMA=y
+CONFIG_SPI_DW_MMIO=y
+CONFIG_SPI_DW_BT1=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_PCF857X=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SYSCON_REBOOT_MODE=y
+CONFIG_SENSORS_BT1_PVT=y
+CONFIG_SENSORS_BT1_PVT_ALARMS=y
+CONFIG_SENSORS_TMP102=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_STATISTICS=y
+CONFIG_THERMAL_GOV_FAIR_SHARE=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_CPU_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_SYSFS=y
+CONFIG_WATCHDOG_PRETIMEOUT_GOV=y
+CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP=y
+CONFIG_DW_WATCHDOG=y
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_USB_ULPI_BUS=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_UAS=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_ULPI=y
+CONFIG_EDAC=y
+CONFIG_EDAC_DEBUG=y
+CONFIG_EDAC_SYNOPSYS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PCF85363=y
+CONFIG_DMADEVICES=y
+CONFIG_DW_DMAC=y
+CONFIG_DW_EDMA=y
+CONFIG_SYNC_FILE=y
+# CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL is not set
+CONFIG_COMMON_CLK_VC5=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_MEMORY=y
+CONFIG_BT1_L2_CTL=y
+CONFIG_NVMEM_U_BOOT_ENV=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_FANOTIFY=y
+CONFIG_FUSE_FS=y
+# CONFIG_FUSE_IO_URING is not set
+CONFIG_OVERLAY_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_NTFS3_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_WBUF_VERIFY=y
+CONFIG_UBIFS_FS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V2=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_0=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_USE_LEGACY_DNS=y
+# CONFIG_NFS_V4_2_READ_PLUS is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_866=y
+CONFIG_NLS_CODEPAGE_1251=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_5=y
+CONFIG_NLS_KOI8_R=y
+CONFIG_NLS_UTF8=y
+CONFIG_INIT_STACK_NONE=y
+CONFIG_CRYPTO_USER=y
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_BLOWFISH=y
+CONFIG_CRYPTO_CCM=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_CRC32=y
+CONFIG_CRYPTO_USER_API_HASH=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO_DWARF4=y
+CONFIG_DEBUG_INFO_BTF=y
+CONFIG_FRAME_WARN=1024
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_VMLINUX_MAP=y
+CONFIG_SCHEDSTATS=y
+# CONFIG_EARLY_PRINTK is not set
+# CONFIG_RUNTIME_TESTING_MENU is not set
diff --git a/arch/mips/configs/em416_defconfig b/arch/mips/configs/em416_defconfig
new file mode 100644
index 0000000000000..1aa7f56c3493c
--- /dev/null
+++ b/arch/mips/configs/em416_defconfig
@@ -0,0 +1,447 @@
+CONFIG_LOCALVERSION="-bt1"
+CONFIG_DEFAULT_HOSTNAME="baikal"
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CLOCKSOURCE_WATCHDOG_MAX_SKEW_US=100
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_JIT=y
+CONFIG_BPF_JIT_ALWAYS_ON=y
+CONFIG_BPF_PRELOAD=y
+CONFIG_BPF_PRELOAD_UMD=y
+CONFIG_PREEMPT=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_CGROUP_MISC=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_CHECKPOINT_RESTORE=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CMDLINE_LOG_WRAP_IDEAL_LEN=100
+CONFIG_SYSFS_SYSCALL=y
+CONFIG_EXPERT=y
+CONFIG_PROFILING=y
+CONFIG_MIPS_BAIKAL_T1=y
+CONFIG_BT1_CPU_FEATURE_OVERRIDES=y
+CONFIG_BT1_DTB_EM416=y
+CONFIG_CPU_P5600=y
+CONFIG_CPU_MIPS32_R5_FEATURES=y
+CONFIG_CPU_MIPS32_R5_XPA=y
+CONFIG_ZBOOT_LOAD_ADDRESS=0x85100000
+CONFIG_ARCH_FORCE_MAX_ORDER=11
+CONFIG_CPU_HAS_MSA=y
+CONFIG_NR_CPUS=2
+CONFIG_IEEE754_DEFAULT_RELAXED=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_PAGE_SIZE_16KB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_UNLOAD_TAINT_TRACKING=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLOCK_LEGACY_AUTOLOAD is not set
+CONFIG_BLK_DEV_BSGLIB=y
+# CONFIG_IOSCHED_BFQ is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_PACKET_DIAG=y
+CONFIG_UNIX=y
+CONFIG_UNIX_DIAG=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_XDP_SOCKETS=y
+CONFIG_XDP_SOCKETS_DIAG=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_NET_IPIP=y
+CONFIG_NET_IPGRE_DEMUX=y
+CONFIG_NET_IPGRE=y
+CONFIG_NET_FOU_IP_TUNNELS=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
+CONFIG_INET_RAW_DIAG=y
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=y
+CONFIG_TCP_CONG_WESTWOOD=y
+CONFIG_TCP_CONG_HTCP=y
+CONFIG_TCP_CONG_HSTCP=y
+CONFIG_TCP_CONG_BBR=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_GRE=y
+CONFIG_IPV6_SEG6_LWTUNNEL=y
+CONFIG_MPTCP=y
+CONFIG_NETWORK_PHY_TIMESTAMPING=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_PROCFS=y
+CONFIG_NF_TABLES=y
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_LOG=y
+CONFIG_NFT_LIMIT=y
+CONFIG_NFT_MASQ=y
+CONFIG_NFT_REDIR=y
+CONFIG_NFT_NAT=y
+CONFIG_NFT_REJECT=y
+CONFIG_NFT_HASH=y
+CONFIG_NETFILTER_XT_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_NAT=y
+CONFIG_NETFILTER_XT_TARGET_NETMAP=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=y
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_CGROUP=y
+CONFIG_NETFILTER_XT_MATCH_CPU=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_OWNER=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_RECENT=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=y
+CONFIG_NFT_DUP_IPV4=y
+CONFIG_NF_TABLES_ARP=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_TARGET_SYNPROXY=y
+CONFIG_NFT_DUP_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_EUI64=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_BRIDGE=y
+CONFIG_BRIDGE_VLAN_FILTERING=y
+CONFIG_VLAN_8021Q=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_SFQ=y
+CONFIG_NET_SCH_CBS=y
+CONFIG_NET_SCH_ETF=y
+CONFIG_NET_SCH_TAPRIO=y
+CONFIG_NET_SCH_MQPRIO=y
+CONFIG_NET_SCH_SKBPRIO=y
+CONFIG_NET_SCH_QFQ=y
+CONFIG_NET_SCH_FQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_SCH_DEFAULT=y
+CONFIG_NET_CLS_BASIC=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_PERF=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_CLS_BPF=y
+CONFIG_NET_CLS_FLOWER=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_PEDIT=y
+CONFIG_NET_ACT_SIMP=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_NET_ACT_VLAN=y
+CONFIG_NET_ACT_BPF=y
+CONFIG_NET_ACT_SKBMOD=y
+CONFIG_DCB=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_NETLINK_DIAG=y
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_CGROUP_NET_CLASSID=y
+CONFIG_BPF_STREAM_PARSER=y
+CONFIG_NET_PKTGEN=y
+# CONFIG_WIRELESS is not set
+CONFIG_PAGE_POOL_STATS=y
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEAER_INJECT=y
+CONFIG_PCIE_ECRC=y
+CONFIG_PCIEASPM_PERFORMANCE=y
+CONFIG_PCI_MSI=y
+CONFIG_PCIE_BUS_PERFORMANCE=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_PCIE_BT1=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DEBUG_DRIVER=y
+CONFIG_DEBUG_DEVRES=y
+CONFIG_BT1_APB=y
+CONFIG_BT1_AXI=y
+CONFIG_MIPS_CDMM=y
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONED_MASTER=y
+CONFIG_MTD_ROM=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_PHYSMAP_BT1_ROM=y
+CONFIG_MTD_SPI_NOR=y
+# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BLOCK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=4
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=2
+CONFIG_BLK_DEV_RAM_SIZE=32768
+CONFIG_BLK_DEV_NVME=y
+CONFIG_NVME_MULTIPATH=y
+CONFIG_NVME_VERBOSE_ERRORS=y
+CONFIG_NVME_HWMON=y
+CONFIG_SRAM=y
+CONFIG_EEPROM_AT24=y
+CONFIG_RAID_ATTRS=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_AHCI_DWC=y
+# CONFIG_ATA_SFF is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_AGERE is not set
+# CONFIG_NET_VENDOR_ALACRITECH is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_VENDOR_AQUANTIA is not set
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_ASIX is not set
+# CONFIG_NET_VENDOR_ATHEROS is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CADENCE is not set
+# CONFIG_NET_VENDOR_CAVIUM is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_CORTINA is not set
+# CONFIG_NET_VENDOR_DAVICOM is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_ENGLEDER is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_FUNGIBLE is not set
+# CONFIG_NET_VENDOR_GOOGLE is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_HUAWEI is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_ADI is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_META is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MICROSEMI is not set
+# CONFIG_NET_VENDOR_MICROSOFT is not set
+# CONFIG_NET_VENDOR_MUCSE is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_NET_VENDOR_NI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_NET_VENDOR_PACKET_ENGINES is not set
+# CONFIG_NET_VENDOR_PENSANDO is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
+# CONFIG_NET_VENDOR_BROCADE is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_SOCIONEXT is not set
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_SELFTESTS=y
+CONFIG_DWMAC_BT1=y
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
+# CONFIG_NET_VENDOR_TOSHIBA is not set
+# CONFIG_NET_VENDOR_VERTEXCOM is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WANGXUN is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_NET_VENDOR_XILINX is not set
+CONFIG_MICROSEMI_PHY=y
+# CONFIG_USB_NET_DRIVERS is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT_CONSOLE is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_LEGACY_PTY_COUNT=16
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_DW=y
+# CONFIG_SERIAL_8250_PERICOM is not set
+CONFIG_MIPS_EJTAG_FDC_TTY=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_DESIGNWARE_CORE=y
+CONFIG_I2C_SLAVE=y
+CONFIG_I2C_SLAVE_EEPROM=y
+CONFIG_SPI=y
+CONFIG_SPI_DESIGNWARE=y
+CONFIG_SPI_DW_DMA=y
+CONFIG_SPI_DW_MMIO=y
+CONFIG_SPI_DW_BT1=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_PCF857X=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SYSCON_REBOOT_MODE=y
+CONFIG_SENSORS_BT1_PVT=y
+CONFIG_SENSORS_BT1_PVT_ALARMS=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_STATISTICS=y
+CONFIG_THERMAL_GOV_FAIR_SHARE=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_CPU_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_SYSFS=y
+CONFIG_WATCHDOG_PRETIMEOUT_GOV=y
+CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP=y
+CONFIG_DW_WATCHDOG=y
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_USB_ULPI_BUS=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_UAS=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_ULPI=y
+CONFIG_MMC=y
+CONFIG_MMC_SPI=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PCF85363=y
+CONFIG_DMADEVICES=y
+CONFIG_DW_DMAC=y
+CONFIG_DW_EDMA=y
+CONFIG_SYNC_FILE=y
+# CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL is not set
+CONFIG_COMMON_CLK_VC5=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_MEMORY=y
+CONFIG_BT1_L2_CTL=y
+CONFIG_NVMEM_U_BOOT_ENV=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_FANOTIFY=y
+CONFIG_FUSE_FS=y
+# CONFIG_FUSE_IO_URING is not set
+CONFIG_OVERLAY_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_NTFS3_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_WBUF_VERIFY=y
+CONFIG_UBIFS_FS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V2=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_0=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_USE_LEGACY_DNS=y
+# CONFIG_NFS_V4_2_READ_PLUS is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_866=y
+CONFIG_NLS_CODEPAGE_1251=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_5=y
+CONFIG_NLS_KOI8_R=y
+CONFIG_NLS_UTF8=y
+CONFIG_INIT_STACK_NONE=y
+CONFIG_CRYPTO_USER=y
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_BLOWFISH=y
+CONFIG_CRYPTO_CCM=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_CRC32=y
+CONFIG_CRYPTO_USER_API_HASH=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO_DWARF4=y
+CONFIG_DEBUG_INFO_BTF=y
+CONFIG_FRAME_WARN=1024
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_VMLINUX_MAP=y
+CONFIG_SCHEDSTATS=y
+# CONFIG_EARLY_PRINTK is not set
+# CONFIG_RUNTIME_TESTING_MENU is not set
diff --git a/arch/mips/configs/tf306_defconfig b/arch/mips/configs/tf306_defconfig
new file mode 100644
index 0000000000000..fbaecb128ec73
--- /dev/null
+++ b/arch/mips/configs/tf306_defconfig
@@ -0,0 +1,450 @@
+CONFIG_LOCALVERSION="-bt1"
+CONFIG_DEFAULT_HOSTNAME="baikal"
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CLOCKSOURCE_WATCHDOG_MAX_SKEW_US=100
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_JIT=y
+CONFIG_BPF_JIT_ALWAYS_ON=y
+CONFIG_BPF_PRELOAD=y
+CONFIG_BPF_PRELOAD_UMD=y
+CONFIG_PREEMPT=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_CGROUP_MISC=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_CHECKPOINT_RESTORE=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CMDLINE_LOG_WRAP_IDEAL_LEN=100
+CONFIG_SYSFS_SYSCALL=y
+CONFIG_EXPERT=y
+CONFIG_PROFILING=y
+CONFIG_MIPS_BAIKAL_T1=y
+CONFIG_BT1_CPU_FEATURE_OVERRIDES=y
+CONFIG_BT1_DTB_TF306=y
+CONFIG_CPU_P5600=y
+CONFIG_CPU_MIPS32_R5_FEATURES=y
+CONFIG_CPU_MIPS32_R5_XPA=y
+CONFIG_ZBOOT_LOAD_ADDRESS=0x85100000
+CONFIG_ARCH_FORCE_MAX_ORDER=11
+CONFIG_CPU_HAS_MSA=y
+CONFIG_NR_CPUS=2
+CONFIG_IEEE754_DEFAULT_RELAXED=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_PAGE_SIZE_16KB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_UNLOAD_TAINT_TRACKING=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLOCK_LEGACY_AUTOLOAD is not set
+CONFIG_BLK_DEV_BSGLIB=y
+# CONFIG_IOSCHED_BFQ is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_PACKET_DIAG=y
+CONFIG_UNIX=y
+CONFIG_UNIX_DIAG=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_XDP_SOCKETS=y
+CONFIG_XDP_SOCKETS_DIAG=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
+CONFIG_INET_RAW_DIAG=y
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=y
+CONFIG_TCP_CONG_WESTWOOD=y
+CONFIG_TCP_CONG_HTCP=y
+CONFIG_TCP_CONG_HSTCP=y
+CONFIG_TCP_CONG_BBR=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_MPTCP=y
+CONFIG_NETWORK_PHY_TIMESTAMPING=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_PROCFS=y
+CONFIG_NF_TABLES=y
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_LOG=y
+CONFIG_NFT_LIMIT=y
+CONFIG_NFT_MASQ=y
+CONFIG_NFT_REDIR=y
+CONFIG_NFT_NAT=y
+CONFIG_NFT_REJECT=y
+CONFIG_NFT_HASH=y
+CONFIG_NETFILTER_XT_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_NAT=y
+CONFIG_NETFILTER_XT_TARGET_NETMAP=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=y
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_CGROUP=y
+CONFIG_NETFILTER_XT_MATCH_CPU=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_OWNER=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_RECENT=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=y
+CONFIG_NFT_DUP_IPV4=y
+CONFIG_NF_TABLES_ARP=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_TARGET_SYNPROXY=y
+CONFIG_NFT_DUP_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_EUI64=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_BRIDGE=y
+CONFIG_BRIDGE_VLAN_FILTERING=y
+CONFIG_VLAN_8021Q=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_SFQ=y
+CONFIG_NET_SCH_CBS=y
+CONFIG_NET_SCH_ETF=y
+CONFIG_NET_SCH_TAPRIO=y
+CONFIG_NET_SCH_MQPRIO=y
+CONFIG_NET_SCH_SKBPRIO=y
+CONFIG_NET_SCH_QFQ=y
+CONFIG_NET_SCH_FQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_SCH_DEFAULT=y
+CONFIG_NET_CLS_BASIC=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_PERF=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_CLS_BPF=y
+CONFIG_NET_CLS_FLOWER=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_PEDIT=y
+CONFIG_NET_ACT_SIMP=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_NET_ACT_VLAN=y
+CONFIG_NET_ACT_BPF=y
+CONFIG_NET_ACT_SKBMOD=y
+CONFIG_DCB=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_NETLINK_DIAG=y
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_CGROUP_NET_CLASSID=y
+CONFIG_BPF_STREAM_PARSER=y
+CONFIG_NET_PKTGEN=y
+# CONFIG_WIRELESS is not set
+CONFIG_PAGE_POOL_STATS=y
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEAER_INJECT=y
+CONFIG_PCIE_ECRC=y
+CONFIG_PCIEASPM_PERFORMANCE=y
+CONFIG_PCI_MSI=y
+CONFIG_PCIE_BUS_PERFORMANCE=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_PCIE_BT1=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DEBUG_DRIVER=y
+CONFIG_DEBUG_DEVRES=y
+CONFIG_BT1_APB=y
+CONFIG_BT1_AXI=y
+CONFIG_MIPS_CDMM=y
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONED_MASTER=y
+CONFIG_MTD_ROM=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_PHYSMAP_BT1_ROM=y
+CONFIG_MTD_SPI_NOR=y
+# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BLOCK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=4
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=2
+CONFIG_BLK_DEV_RAM_SIZE=32768
+CONFIG_BLK_DEV_NVME=y
+CONFIG_NVME_MULTIPATH=y
+CONFIG_NVME_VERBOSE_ERRORS=y
+CONFIG_NVME_HWMON=y
+CONFIG_SRAM=y
+CONFIG_EEPROM_AT24=y
+CONFIG_RAID_ATTRS=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_AHCI_DWC=y
+# CONFIG_ATA_SFF is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_AGERE is not set
+# CONFIG_NET_VENDOR_ALACRITECH is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_VENDOR_AQUANTIA is not set
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_ASIX is not set
+# CONFIG_NET_VENDOR_ATHEROS is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CADENCE is not set
+# CONFIG_NET_VENDOR_CAVIUM is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_CORTINA is not set
+# CONFIG_NET_VENDOR_DAVICOM is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_ENGLEDER is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_FUNGIBLE is not set
+# CONFIG_NET_VENDOR_GOOGLE is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_HUAWEI is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_ADI is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_META is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MICROSEMI is not set
+# CONFIG_NET_VENDOR_MICROSOFT is not set
+# CONFIG_NET_VENDOR_MUCSE is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_NET_VENDOR_NI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_NET_VENDOR_PACKET_ENGINES is not set
+# CONFIG_NET_VENDOR_PENSANDO is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
+# CONFIG_NET_VENDOR_BROCADE is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_SOCIONEXT is not set
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_SELFTESTS=y
+CONFIG_DWMAC_BT1=y
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
+# CONFIG_NET_VENDOR_TOSHIBA is not set
+# CONFIG_NET_VENDOR_VERTEXCOM is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WANGXUN is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_NET_VENDOR_XILINX is not set
+CONFIG_REALTEK_PHY=y
+# CONFIG_USB_NET_DRIVERS is not set
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT_CONSOLE is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_LEGACY_PTY_COUNT=16
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_DW=y
+# CONFIG_SERIAL_8250_PERICOM is not set
+CONFIG_MIPS_EJTAG_FDC_TTY=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_DESIGNWARE_CORE=y
+CONFIG_I2C_SLAVE=y
+CONFIG_I2C_SLAVE_EEPROM=y
+CONFIG_SPI=y
+CONFIG_SPI_DESIGNWARE=y
+CONFIG_SPI_DW_DMA=y
+CONFIG_SPI_DW_MMIO=y
+CONFIG_SPI_DW_BT1=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_PCF857X=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SYSCON_REBOOT_MODE=y
+CONFIG_SENSORS_BT1_PVT=y
+CONFIG_SENSORS_BT1_PVT_ALARMS=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_STATISTICS=y
+CONFIG_THERMAL_GOV_FAIR_SHARE=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_CPU_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_SYSFS=y
+CONFIG_WATCHDOG_PRETIMEOUT_GOV=y
+CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP=y
+CONFIG_DW_WATCHDOG=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_HRTIMER=y
+# CONFIG_SND_PCI is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SND_MIPS is not set
+CONFIG_SND_USB_AUDIO=y
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_ULPI_BUS=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_UAS=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_ULPI=y
+# CONFIG_USB_DWC3_GENERIC_PLAT is not set
+CONFIG_USB_HUB_USB251XB=y
+CONFIG_MMC=y
+CONFIG_MMC_SPI=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_EDAC=y
+CONFIG_EDAC_DEBUG=y
+CONFIG_EDAC_SYNOPSYS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PCF2127=y
+CONFIG_DMADEVICES=y
+CONFIG_DW_DMAC=y
+CONFIG_DW_EDMA=y
+CONFIG_SYNC_FILE=y
+# CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL is not set
+CONFIG_COMMON_CLK_VC5=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_MEMORY=y
+CONFIG_BT1_L2_CTL=y
+CONFIG_NVMEM_U_BOOT_ENV=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_FANOTIFY=y
+CONFIG_FUSE_FS=y
+# CONFIG_FUSE_IO_URING is not set
+CONFIG_OVERLAY_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_NTFS3_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_WBUF_VERIFY=y
+CONFIG_UBIFS_FS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V2=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_0=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_USE_LEGACY_DNS=y
+# CONFIG_NFS_V4_2_READ_PLUS is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_866=y
+CONFIG_NLS_CODEPAGE_1251=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_5=y
+CONFIG_NLS_KOI8_R=y
+CONFIG_NLS_UTF8=y
+CONFIG_INIT_STACK_NONE=y
+CONFIG_CRYPTO_USER=y
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_BLOWFISH=y
+CONFIG_CRYPTO_CCM=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_CRC32=y
+CONFIG_CRYPTO_USER_API_HASH=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_SCHEDSTATS=y
+# CONFIG_EARLY_PRINTK is not set
+# CONFIG_RUNTIME_TESTING_MENU is not set
diff --git a/arch/mips/include/asm/dmi.h b/arch/mips/include/asm/dmi.h
index dc397f630c660..b14620393727a 100644
--- a/arch/mips/include/asm/dmi.h
+++ b/arch/mips/include/asm/dmi.h
@@ -15,6 +15,8 @@
#if defined(CONFIG_MACH_LOONGSON64)
#define SMBIOS_ENTRY_POINT_SCAN_START 0xFFFE000
+#elif defined(CONFIG_MIPS_BAIKAL_T1)
+#define SMBIOS_ENTRY_POINT_SCAN_START 0x0
#endif
#endif /* _ASM_DMI_H */
diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index 980187a830536..f26828b475352 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -177,7 +177,7 @@ void iounmap(const volatile void __iomem *addr);
#define war_io_reorder_wmb() barrier()
#endif
-#define __BUILD_MEMORY_SINGLE(pfx, bwlq, type, barrier, relax, irq) \
+#define __BUILD_MEMORY_SINGLE(pfx, bwlq, type, relax, irq) \
\
static inline void pfx##write##bwlq(type val, \
volatile void __iomem *mem) \
@@ -185,7 +185,7 @@ static inline void pfx##write##bwlq(type val, \
volatile type *__mem; \
type __val; \
\
- if (barrier) \
+ if (!(relax && IS_ENABLED(CONFIG_STRONG_UC_ORDERING))) \
iobarrier_rw(); \
else \
war_io_reorder_wmb(); \
@@ -226,7 +226,7 @@ static inline type pfx##read##bwlq(const volatile void __iomem *mem) \
\
__mem = (void *)__swizzle_addr_##bwlq((unsigned long)(mem)); \
\
- if (barrier) \
+ if (!(relax && IS_ENABLED(CONFIG_STRONG_UC_ORDERING))) \
iobarrier_rw(); \
\
if (sizeof(type) != sizeof(u64) || sizeof(u64) == sizeof(long)) \
@@ -258,14 +258,14 @@ static inline type pfx##read##bwlq(const volatile void __iomem *mem) \
return pfx##ioswab##bwlq(__mem, __val); \
}
-#define __BUILD_IOPORT_SINGLE(pfx, bwlq, type, barrier, relax) \
+#define __BUILD_IOPORT_SINGLE(pfx, bwlq, type, relax) \
\
static inline void pfx##out##bwlq(type val, unsigned long port) \
{ \
volatile type *__addr; \
type __val; \
\
- if (barrier) \
+ if (!(relax && IS_ENABLED(CONFIG_STRONG_UC_ORDERING))) \
iobarrier_rw(); \
else \
war_io_reorder_wmb(); \
@@ -289,7 +289,7 @@ static inline type pfx##in##bwlq(unsigned long port) \
\
BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long)); \
\
- if (barrier) \
+ if (!(relax && IS_ENABLED(CONFIG_STRONG_UC_ORDERING))) \
iobarrier_rw(); \
\
__val = *__addr; \
@@ -302,7 +302,7 @@ static inline type pfx##in##bwlq(unsigned long port) \
#define __BUILD_MEMORY_PFX(bus, bwlq, type, relax) \
\
-__BUILD_MEMORY_SINGLE(bus, bwlq, type, 1, relax, 1)
+__BUILD_MEMORY_SINGLE(bus, bwlq, type, relax, 1)
#define BUILDIO_MEM(bwlq, type) \
\
@@ -322,7 +322,7 @@ __BUILD_MEMORY_PFX(__mem_, q, u64, 0)
#endif
#define __BUILD_IOPORT_PFX(bus, bwlq, type) \
- __BUILD_IOPORT_SINGLE(bus, bwlq, type, 1, 0)
+ __BUILD_IOPORT_SINGLE(bus, bwlq, type, 0)
#define BUILDIO_IOPORT(bwlq, type) \
__BUILD_IOPORT_PFX(_, bwlq, type) \
@@ -337,7 +337,7 @@ BUILDIO_IOPORT(q, u64)
#define __BUILDIO(bwlq, type) \
\
-__BUILD_MEMORY_SINGLE(____raw_, bwlq, type, 1, 0, 0)
+__BUILD_MEMORY_SINGLE(____raw_, bwlq, type, 0, 0)
__BUILDIO(q, u64)
diff --git a/arch/mips/include/asm/mach-bt1/cpu-feature-overrides.h b/arch/mips/include/asm/mach-bt1/cpu-feature-overrides.h
new file mode 100644
index 0000000000000..090600494f6ad
--- /dev/null
+++ b/arch/mips/include/asm/mach-bt1/cpu-feature-overrides.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 core features override
+ */
+#ifndef __ASM_MACH_BT1_CPU_FEATURE_OVERRIDES_H__
+#define __ASM_MACH_BT1_CPU_FEATURE_OVERRIDES_H__
+
+#ifdef CONFIG_BT1_CPU_FEATURE_OVERRIDES
+
+#define cpu_has_tlb 1
+/* Don't override FTLB flag otherwise 'noftlb' option won't work. */
+/* #define cpu_has_ftlb 1 */
+#define cpu_has_tlbinv 1
+#define cpu_has_segments 1
+#define cpu_has_eva 1
+#define cpu_has_htw 1
+#define cpu_has_ldpte 0
+#define cpu_has_rixiex 1
+#define cpu_has_maar 1
+#define cpu_has_rw_llb 1
+
+#define cpu_has_3kex 0
+#define cpu_has_4kex 1
+#define cpu_has_3k_cache 0
+#define cpu_has_4k_cache 1
+#define cpu_has_tx39_cache 0
+#define cpu_has_octeon_cache 0
+
+/* Don't override FPU flags otherwise 'nofpu' option won't work. */
+/* #define cpu_has_fpu 1 */
+/* #define raw_cpu_has_fpu 1 */
+#define cpu_has_32fpr 1
+
+#define cpu_has_counter 1
+#define cpu_has_watch 1
+#define cpu_has_divec 1
+#define cpu_has_vce 0
+#define cpu_has_cache_cdex_p 0
+#define cpu_has_cache_cdex_s 0
+#define cpu_has_prefetch 1
+#define cpu_has_mcheck 1
+#define cpu_has_ejtag 1
+#define cpu_has_llsc 1
+#define cpu_has_bp_ghist 0
+#define cpu_has_guestctl0ext 1 /* ? */
+#define cpu_has_guestctl1 1 /* ? */
+#define cpu_has_guestctl2 1 /* ? */
+#define cpu_has_guestid 1
+#define cpu_has_drg 0
+#define cpu_has_mips16 0
+#define cpu_has_mips16e2 0
+#define cpu_has_mdmx 0
+#define cpu_has_mips3d 0
+#define cpu_has_smartmips 0
+
+#define cpu_has_rixi 1
+
+#define cpu_has_mmips 0
+#define cpu_has_lpa 1
+#define cpu_has_mvh 1
+#define cpu_has_xpa 1
+#define cpu_has_vtag_icache 0
+#define cpu_has_dc_aliases 0
+#define cpu_has_ic_fills_f_dc 0
+#define cpu_has_pindexed_dcache 0
+/* Depends on the MIPS_CM/SMP configs. */
+/* #define cpu_icache_snoops_remote_store 1 */
+
+/*
+ * MIPS P5600 Warrior is based on the MIPS32 Release 2 architecture, which
+ * makes it backward compatible with all 32bits early MIPS architecture
+ * releases.
+ */
+#define cpu_has_mips_1 1
+#define cpu_has_mips_2 1
+#define cpu_has_mips_3 0
+#define cpu_has_mips_4 0
+#define cpu_has_mips_5 0
+#define cpu_has_mips32r1 1
+#define cpu_has_mips32r2 1
+#define cpu_has_mips32r5 1
+#define cpu_has_mips32r6 0
+#define cpu_has_mips64r1 0
+#define cpu_has_mips64r2 0
+#define cpu_has_mips64r6 0
+#define cpu_has_mips_r2_exec_hazard 0
+
+#define cpu_has_clo_clz 1
+#define cpu_has_wsbh 1
+#define cpu_has_dsp 0
+#define cpu_has_dsp2 0
+#define cpu_has_dsp3 0
+#define cpu_has_loongson_mmi 0
+#define cpu_has_loongson_cam 0
+#define cpu_has_loongson_ext 0
+#define cpu_has_loongson_ext2 0
+#define cpu_has_mipsmt 0
+#define cpu_has_vp 0
+#define cpu_has_userlocal 1
+
+#define cpu_has_nofpuex 0
+#define cpu_has_64bits 0
+#define cpu_has_64bit_zero_reg 0
+#define cpu_has_64bit_gp_regs 0
+#define cpu_has_64bit_addresses 0
+/*
+ * VINT is hardwired to 1 by P5600 design while VEIC as being SI_EICPresent
+ * and seeing we always have MIPS GIC available in the chip must have been set
+ * to 1. Alas the IP core engineers mistakenly made it to be wired with
+ * GIC_VX_CTL_EIC bit. Lets fix it by manually setting the flag to 1.
+ */
+#define cpu_has_vint 1
+#define cpu_has_veic 1
+/* Chaches line size is fixed by P5600 design. */
+#define cpu_has_inclusive_pcaches 0
+#define cpu_dcache_line_size() 32
+#define cpu_icache_line_size() 32
+#define cpu_scache_line_size() 32
+#define cpu_tcache_line_size() 0
+#define cpu_hwrena_impl_bits 0
+#define cpu_has_perf_cntr_intr_bit 1
+#define cpu_has_vz 1
+#define cpu_has_msa 1
+#define cpu_has_ufr 1
+#define cpu_has_fre 0
+#define cpu_has_cdmm 1
+#define cpu_has_small_pages 0
+#define cpu_has_nan_legacy 0
+#define cpu_has_nan_2008 1
+#define cpu_has_ebase_wg 1
+#define cpu_has_badinstr 1
+#define cpu_has_badinstrp 1
+#define cpu_has_contextconfig 1
+#define cpu_has_perf 1
+#define cpu_has_mac2008_only 0
+#define cpu_has_mmid 0
+#define cpu_has_mm_sysad 0
+#define cpu_has_mm_full 1
+
+#endif /* CONFIG_BT1_CPU_FEATURE_OVERRIDES */
+
+#endif /* __ASM_MACH_BT1_CPU_FEATURE_OVERRIDES_H__ */
diff --git a/arch/mips/include/asm/mach-bt1/irq.h b/arch/mips/include/asm/mach-bt1/irq.h
new file mode 100644
index 0000000000000..68b4d6849078d
--- /dev/null
+++ b/arch/mips/include/asm/mach-bt1/irq.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 IRQ numbers declaration
+ */
+#ifndef __ASM_MACH_BT1_IRQ_H__
+#define __ASM_MACH_BT1_IRQ_H__
+
+#define NR_IRQS 255
+#define MIPS_CPU_IRQ_BASE 0
+
+#define BT1_APB_EHB_IRQ 16
+#define BT1_WDT_IRQ 17
+#define BT1_GPIO32_IRQ 19
+#define BT1_TIMER0_IRQ 24
+#define BT1_TIMER1_IRQ 25
+#define BT1_TIMER2_IRQ 26
+#define BT1_PVT_IRQ 31
+#define BT1_I2C1_IRQ 33
+#define BT1_I2C2_IRQ 34
+#define BT1_SPI1_IRQ 40
+#define BT1_SPI2_IRQ 41
+#define BT1_UART0_IRQ 48
+#define BT1_UART1_IRQ 49
+#define BT1_DMAC_IRQ 56
+#define BT1_SATA_IRQ 64
+#define BT1_USB_IRQ 68
+#define BT1_GMAC0_IRQ 72
+#define BT1_GMAC1_IRQ 73
+#define BT1_XGMAC_IRQ 74
+#define BT1_XGMAC_TX0_IRQ 75
+#define BT1_XGMAC_TX1_IRQ 76
+#define BT1_XGMAC_RX0_IRQ 77
+#define BT1_XGMAC_RX1_IRQ 78
+#define BT1_XGMAC_XPCS_IRQ 79
+#define BT1_PCIE_EDMA_TX0_IRQ 80
+#define BT1_PCIE_EDMA_TX1_IRQ 81
+#define BT1_PCIE_EDMA_TX2_IRQ 82
+#define BT1_PCIE_EDMA_TX3_IRQ 83
+#define BT1_PCIE_EDMA_RX0_IRQ 84
+#define BT1_PCIE_EDMA_RX1_IRQ 85
+#define BT1_PCIE_EDMA_RX2_IRQ 86
+#define BT1_PCIE_EDMA_RX3_IRQ 87
+#define BT1_PCIE_MSI_IRQ 88
+#define BT1_PCIE_AER_IRQ 89
+#define BT1_PCIE_PME_IRQ 90
+#define BT1_PCIE_HP_IRQ 91
+#define BT1_PCIE_BW_IRQ 92
+#define BT1_PCIE_L_REQ_IRQ 93
+#define BT1_DDR_DFI_E_IRQ 96
+#define BT1_DDR_ECC_CE_IRQ 97
+#define BT1_DDR_ECC_UE_IRQ 98
+#define BT1_DDR_ECC_SBR_IRQ 99
+#define BT1_HWA_IRQ 104
+#define BT1_AXI_EHB_IRQ 127
+
+#include_next <irq.h>
+
+#endif /* __ASM_MACH_BT1_IRQ_H__ */
diff --git a/arch/mips/include/asm/mach-bt1/kernel-entry-init.h b/arch/mips/include/asm/mach-bt1/kernel-entry-init.h
new file mode 100644
index 0000000000000..b5a87a3803d3b
--- /dev/null
+++ b/arch/mips/include/asm/mach-bt1/kernel-entry-init.h
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 platform low-level initialization
+ */
+#ifndef __ASM_MACH_BT1_KERNEL_ENTRY_INIT_H__
+#define __ASM_MACH_BT1_KERNEL_ENTRY_INIT_H__
+
+#include <asm/addrspace.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+
+#include <asm/mach-bt1/memory.h>
+
+#define MIPS_SEGCFG_SET(cfg, pa, am, eu, c) \
+ (((pa << MIPS_SEGCFG_PA_SHIFT) | \
+ (MIPS_SEGCFG_## am << MIPS_SEGCFG_AM_SHIFT) | \
+ (eu << MIPS_SEGCFG_EU_SHIFT) | \
+ (c << MIPS_SEGCFG_C_SHIFT)) << (16 * (cfg % 2)))
+
+ /*
+ * Enable SegCtlX.CFGY_C attribute affecting segments caching
+ */
+ .macro platform_eva_cca
+
+ .set push
+ .set reorder
+
+ mfc0 t0, CP0_CONFIG5
+ li t1, MIPS_CONF5_K
+ or t0, t0, t1
+ mtc0 t0, CP0_CONFIG5
+
+ sync
+ la t1, 1f
+ jr.hb t1
+ nop
+1:
+
+ .set pop
+ .endm
+
+ /*
+ * Prepare segments for EVA boot:
+ *
+ * =========================== Mappings ===============================
+ * CFG Virtual memory Physical memory CCA Mapping
+ * 5 0x00000000 0x3fffffff 0x00000000 0x3ffffffff CWB MUSUK (xkseg0)
+ * 4 0x40000000 0x7fffffff 0x40000000 0x7ffffffff CWB MUSUK (xkseg0)
+ * 3 0x80000000 0x9fffffff 0x80000000 0x9ffffffff CWB MUSUK (xkseg0)
+ * Flat 2.5GB physical mem
+ *
+ * 2 0xa0000000 0xbfffffff 0x00000000 0x1ffffffff UC MUSUK (kseg1)
+ * 1 0xc0000000 0xdfffffff - CWB MSK (kseg2)
+ * 0 0xe0000000 0xffffffff - CWB MK (kseg3)
+ * where CCA encoding:
+ * UCA = 7 Uncached Accelerated (Write-combine), non-coherent;
+ * CWB = 5 Cacheable, coherent, write-back, write-allocate,
+ * read misses request Shared (SMP);
+ * CWBE = 4 Cacheable, coherent, write-back, write-allocate,
+ * read misses request Exclusive (SMP);
+ * WB = 3 Cacheable, non-coherent, write-back, write allocate
+ * UC = 2 Uncached non-coherent;
+ * mappings encoding:
+ * UK = 0 Kernel-only unmapped region;
+ * MK = 1 Kernel-only mapped region;
+ * MSK = 2 Supervisor and kernel mapped region;
+ * MUSK = 3 User, Supervisor and kernel mapped region;
+ * MUSUK = 4 Used to implement a fully-mapped flat address space in
+ * user and supervisor modes, with unmapped regions which
+ * appear in kernel mode;
+ * USK = 5 Supervisor and kernel unmapped region;
+ * UUSK = 7 Unrestricted unmapped region
+ *
+ * The following code uses the t0 and t1 registers without previously
+ * preserving them.
+ *
+ */
+ .macro platform_eva_init
+
+ .set push
+ .set reorder
+
+ /* SegCtl0 */
+ li t0, MIPS_SEGCFG_SET(0, 0, MK, 0, 0) | \
+ MIPS_SEGCFG_SET(1, 0, MSK, 0, 0)
+ mtc0 t0, CP0_SEGCTL0
+
+ /* SegCtl1 */
+ li t0, MIPS_SEGCFG_SET(2, 0, MUSUK, 1, 2) | \
+ MIPS_SEGCFG_SET(3, 4, MUSUK, 1, 5)
+ mtc0 t0, CP0_SEGCTL1
+
+ /* SegCtl2 */
+ li t0, MIPS_SEGCFG_SET(4, 2, MUSUK, 1, 5) | \
+ MIPS_SEGCFG_SET(5, 0, MUSUK, 1, 5)
+ mtc0 t0, CP0_SEGCTL2
+
+ /* Make sure C0 is written */
+ sync
+ ehb
+
+ /* Enable EVAs CCA */
+ platform_eva_cca
+
+ .set pop
+ .endm
+
+ /*
+ * Prepare segments for LEGACY boot:
+ *
+ * =========================== Mappings =============================
+ * CFG Virtual memory Physical memory CCA Mapping
+ * 5 0x00000000 0x3fffffff - CWB MUSK (kuseg)
+ * 4 0x40000000 0x7fffffff - CWB MUSK (kuseg)
+ * 3 0x80000000 0x9fffffff 0x00000000 0x1ffffffff CWB UK (kseg0)
+ * 2 0xa0000000 0xbfffffff 0x00000000 0x1ffffffff UC UK (kseg1)
+ * 1 0xc0000000 0xdfffffff - CWB MSK (kseg2)
+ * 0 0xe0000000 0xffffffff - CWB MK (kseg3)
+ *
+ * Note K0 = 2 by default on MIPS Warrior P5600, but bootloader changes
+ * it to K0 = 3. It must be K0 = 5 for SMP to work correctly.
+ *
+ * The following code uses the t0 and t1 registers without previously
+ * preserving them.
+ *
+ */
+ .macro platform_legacy_init
+
+ .set push
+ .set reorder
+
+ /* SegCtl0 */
+ li t0, MIPS_SEGCFG_SET(0, 0, MK, 0, 0) | \
+ MIPS_SEGCFG_SET(1, 0, MSK, 0, 0)
+ mtc0 t0, CP0_SEGCTL0
+
+ /* SegCtl1 */
+ li t0, MIPS_SEGCFG_SET(2, 0, UK, 1, 2) | \
+ MIPS_SEGCFG_SET(3, 0, UK, 1, 5)
+ mtc0 t0, CP0_SEGCTL1
+
+ /* SegCtl2 */
+ li t0, MIPS_SEGCFG_SET(4, 2, MUSK, 1, 0) | \
+ MIPS_SEGCFG_SET(5, 0, MUSK, 1, 0)
+ mtc0 t0, CP0_SEGCTL2
+
+ /* Make sure C0 is written */
+ sync
+ ehb
+
+ /* Enable EVAs CCA */
+ platform_eva_cca
+
+ .set pop
+ .endm
+
+ /*
+ * Baikal-T1 engineering chip had problems if the next features
+ * were enabled.
+ */
+ .macro platform_errata_jr_ls_fix
+
+ .set push
+ .set reorder
+
+ /* Disable load/store bonding. */
+ mfc0 t0, CP0_CONFIG, 6
+ lui t1, (MTI_CONF6_DLSB >> 16)
+ or t0, t0, t1
+ /* Disable all JR prediction except JR $31. */
+ ori t0, t0, MTI_CONF6_JRCD
+ mtc0 t0, CP0_CONFIG, 6
+ sync
+ jal mips_ihb
+ nop
+
+ /* Disable all JR $31 prediction through return prediction stack. */
+ mfc0 t0, CP0_CONFIG, 7
+ ori t0, t0, MIPS_CONF7_RPS
+ mtc0 t0, CP0_CONFIG, 7
+ sync
+ jal mips_ihb
+ nop
+
+ .set pop
+ .endm
+
+ /*
+ * Setup Baikal-T1 platform specific setups of the memory segments
+ * layout. In case if the kernel is built for engineering version
+ * of the chip some errata must be fixed.
+ */
+ .macro kernel_entry_setup
+
+ sync
+ ehb
+
+#ifdef CONFIG_EVA
+ platform_eva_init
+#else
+ platform_legacy_init
+#endif
+
+#ifdef CONFIG_BT1_ERRATA_JR_LS_BUG
+ platform_errata_jr_ls_fix
+#endif
+
+ .endm
+
+ /*
+ * Do SMP slave processor setup necessary before we can safely execute
+ * C code.
+ */
+ .macro smp_slave_setup
+
+ sync
+ ehb
+
+#ifdef CONFIG_EVA
+ platform_eva_init
+#else
+ platform_legacy_init
+#endif
+
+#ifdef CONFIG_BT1_ERRATA_JR_LS_BUG
+ platform_errata_jr_ls_fix
+#endif
+
+ .endm
+#endif /* __ASM_MACH_BT1_KERNEL_ENTRY_INIT_H__ */
diff --git a/arch/mips/include/asm/mach-bt1/kmalloc.h b/arch/mips/include/asm/mach-bt1/kmalloc.h
new file mode 100644
index 0000000000000..7ad20f623f246
--- /dev/null
+++ b/arch/mips/include/asm/mach-bt1/kmalloc.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_MACH_BT1_KMALLOC_H
+#define __ASM_MACH_BT1_KMALLOC_H
+
+/* The default of 128 bytes wastes too much, use 32 (D,I,S cacheline size) */
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
+
+#endif /* __ASM_MACH_BT1_KMALLOC_H */
diff --git a/arch/mips/include/asm/mach-bt1/memory.h b/arch/mips/include/asm/mach-bt1/memory.h
new file mode 100644
index 0000000000000..ad51b14f17d6f
--- /dev/null
+++ b/arch/mips/include/asm/mach-bt1/memory.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 SoC Static Memory Mapping
+ */
+#ifndef __ASM_MACH_BT1_MEMORY_H__
+#define __ASM_MACH_BT1_MEMORY_H__
+
+#include <linux/sizes.h>
+#include <asm/addrspace.h>
+
+#define BT1_LOMEM_BASE 0x00000000
+#define BT1_LOMEM_SIZE SZ_128M
+
+#define BT1_MMIO_START BT1_PCIE_MAP_BASE
+#define BT1_MMIO_SIZE (SZ_128M + SZ_256M)
+
+#define BT1_PCIE_MAP_BASE 0x08000000
+#define BT1_PCIE_MAP_SIZE 0x13DC0000
+
+#define BT1_P5600_GIC_BASE 0x1BDC0000
+#define BT1_P5600_GIC_SIZE SZ_128K
+#define BT1_P5600_CPC_BASE 0x1BDE0000
+#define BT1_P5600_CPC_SIZE SZ_32K
+#define BT1_P5600_CDMM_BASE 0x1BDE8000
+#define BT1_P5600_CDMM_SIZE SZ_32K
+
+#define BT1_SRAM_BASE 0x1BF80000
+#define BT1_SRAM_SIZE SZ_64K
+#define BT1_ROM_BASE 0x1BFC0000
+#define BT1_ROM_SIZE SZ_64K
+#define BT1_FLASH_BASE 0x1C000000
+#define BT1_FLASH_SIZE SZ_16M
+
+#define BT1_BOOT_CTRL_BASE 0x1F040000
+#define BT1_BOOT_CTRL_SIZE SZ_4K
+#define BT1_BOOT_CTRL_CSR 0x00
+#define BT1_BOOT_CTRL_MAR 0x04
+#define BT1_BOOT_CTRL_DRID 0x08
+#define BT1_BOOT_CTRL_VID 0x0C
+
+#define BT1_DMAC_BASE 0x1F041000
+#define BT1_DMAC_SIZE SZ_4K
+#define BT1_DDR_UMCTL2_BASE 0x1F042000
+#define BT1_DDR_UMCTL2_SIZE SZ_4K
+#define BT1_DDR_PHY_BASE 0x1F043000
+#define BT1_DDR_PHY_SIZE SZ_4K
+#define BT1_GPIO32_BASE 0x1F044000
+#define BT1_GPIO32_SIZE SZ_4K
+#define BT1_GPIO3_BASE 0x1F045000
+#define BT1_GPIO3_SIZE SZ_4K
+#define BT1_I2C1_BASE 0x1F046000
+#define BT1_I2C1_SIZE SZ_4K
+#define BT1_I2C2_BASE 0x1F047000
+#define BT1_I2C2_SIZE SZ_4K
+#define BT1_TIMERS_BASE 0x1F049000
+#define BT1_TIMERS_SIZE SZ_4K
+#define BT1_UART0_BASE 0x1F04A000
+#define BT1_UART0_SIZE SZ_4K
+#define BT1_UART1_BASE 0x1F04B000
+#define BT1_UART1_SIZE SZ_4K
+#define BT1_WDT_BASE 0x1F04C000
+#define BT1_WDT_SIZE SZ_4K
+#define BT1_CCU_BASE 0x1F04D000
+#define BT1_CCU_SIZE SZ_4K
+#define BT1_SPI1_BASE 0x1F04E000
+#define BT1_SPI1_SIZE SZ_4K
+#define BT1_SPI2_BASE 0x1F04F000
+#define BT1_SPI2_SIZE SZ_4K
+#define BT1_SATA_BASE 0x1F050000
+#define BT1_SATA_SIZE SZ_4K
+#define BT1_PCIE_BASE 0x1F052000
+#define BT1_PCIE_SIZE SZ_4K
+#define BT1_PCIE_DBI2_BASE 0x1F053000
+#define BT1_PCIE_DBI2_SIZE SZ_4K
+#define BT1_XGMAC_BASE 0x1F054000
+#define BT1_XGMAC_SIZE SZ_16K
+#define BT1_APB_EHB_BASE 0x1F059000
+#define BT1_APB_EHB_SIZE SZ_4K
+#define BT1_MAIN_IC_BASE 0x1F05A000
+#define BT1_MAIN_IC_SIZE SZ_4K
+#define BT1_HWA_BASE 0x1F05B000
+#define BT1_HWA_SIZE SZ_8K
+#define BT1_XGMAC_XPCS_BASE 0x1F05D000
+#define BT1_XGMAC_XPCS_SIZE SZ_4K
+#define BT1_GMAC0_BASE 0x1F05E000
+#define BT1_GMAC0_SIZE SZ_8K
+#define BT1_GMAC1_BASE 0x1F060000
+#define BT1_GMAC1_SIZE SZ_8K
+#define BT1_USB_BASE 0x1F100000
+#define BT1_USB_SIZE SZ_1M
+#define BT1_PVT_BASE 0x1F200000
+#define BT1_PVT_SIZE SZ_4K
+#define BT1_EFUSE_BASE 0x1F201000
+#define BT1_EFUSE_SIZE SZ_4K
+
+#define BT1_P5600_GCR_L2SYNC_BASE 0x1FBF0000
+#define BT1_P5600_GCR_L2SYNC_SIZE SZ_4K
+#define BT1_P5600_GCB_BASE 0x1FBF8000
+#define BT1_P5600_GCB_SIZE SZ_8K
+#define BT1_P5600_CLCB_BASE 0x1FBFA000
+#define BT1_P5600_CLCB_SIZE SZ_8K
+#define BT1_P5600_COCB_BASE 0x1FBFC000
+#define BT1_P5600_COCB_SIZE SZ_8K
+#define BT1_P5600_DBG_BASE 0x1FBFE000
+#define BT1_P5600_DBG_SIZE SZ_8K
+#define BT1_BOOT_MAP_BASE 0x1FC00000
+#define BT1_BOOT_MAP_SIZE SZ_4M
+
+#define BT1_DEFAULT_BEV KSEG1ADDR(BT1_BOOT_MAP_BASE)
+
+#define BT1_MMIO_END BT1_HIMEM_BASE
+
+#define BT1_HIMEM_BASE 0x20000000
+#define BT1_HIMEM_SIZE SZ_256M
+
+#endif /* __ASM_MACH_BT1_MEMORY_H__ */
diff --git a/arch/mips/include/asm/mach-bt1/spaces.h b/arch/mips/include/asm/mach-bt1/spaces.h
new file mode 100644
index 0000000000000..32d80d5233f23
--- /dev/null
+++ b/arch/mips/include/asm/mach-bt1/spaces.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 SoC Memory Spaces
+ */
+#ifndef __ASM_MACH_BT1_SPACES_H__
+#define __ASM_MACH_BT1_SPACES_H__
+
+/*
+ * Baikal-T1 memory mapping:
+ *
+ * 0x00000000 - 0x07ffffff: 1st RAM region, 128MB
+ * 0x08000000 - 0x1fffffff: I/O, GIC, CPC, Flash, etc
+ * 0x20000000 - 0x20fffffff: 2nd RAM region - up to 8GB (XPA)
+ *
+ * The legacy MIPS memory layout provides the traditional 0x80000000->0x0
+ * and 0xa0000000->0x0 virt-to-phys mapping of the lowest 512MB. In case of
+ * EVA there is a 1:1 virtual-physical mapping up to 0x9fffffff address
+ * with kernel located within the lowest 128MB memory anyway. The uncached
+ * mapping has been preserved in the 0xa0000000 - 0xbffffffff range.
+ */
+#ifdef CONFIG_EVA
+
+#define PAGE_OFFSET _AC(0x0, UL)
+#define PHYS_OFFSET _AC(0x0, UL)
+#define CAC_BASE _AC(0x0, UL)
+#define HIGHMEM_START _AC(0xa0000000, UL)
+
+#endif /* CONFIG_EVA */
+
+#define PCI_IOBASE ((void __iomem *)mips_io_port_base)
+#define PCI_IOSIZE SZ_64K
+#define IO_SPACE_LIMIT (PCI_IOSIZE - 1)
+
+#define pci_remap_iospace pci_remap_iospace
+
+#include <asm/mach-generic/spaces.h>
+
+#endif /* __ASM_MACH_BT1_SPACES_H__ */
diff --git a/arch/mips/include/asm/mach-generic/spaces.h b/arch/mips/include/asm/mach-generic/spaces.h
index 6332b6cbf7eef..cb79b57579343 100644
--- a/arch/mips/include/asm/mach-generic/spaces.h
+++ b/arch/mips/include/asm/mach-generic/spaces.h
@@ -30,7 +30,9 @@
#endif /* __ASSEMBLER__ */
#ifdef CONFIG_32BIT
+#ifndef CAC_BASE
#define CAC_BASE _AC(0x80000000, UL)
+#endif
#ifndef IO_BASE
#define IO_BASE _AC(0xa0000000, UL)
#endif
diff --git a/arch/mips/include/asm/mips-cm.h b/arch/mips/include/asm/mips-cm.h
index 407f253bb4a1c..75afedf940dcd 100644
--- a/arch/mips/include/asm/mips-cm.h
+++ b/arch/mips/include/asm/mips-cm.h
@@ -178,6 +178,21 @@ GCR_ACCESSOR_RW(64, 0x008, base)
#define CM_GCR_BASE_CMDEFTGT_IOCU0 2
#define CM_GCR_BASE_CMDEFTGT_IOCU1 3
+/* GCR_CONTROL - Global CM2 Settings */
+GCR_ACCESSOR_RW(64, 0x010, control)
+#define CM_GCR_CONTROL_SYNCCTL BIT(16)
+#define CM_GCR_CONTROL_SYNCDIS BIT(5)
+#define CM_GCR_CONTROL_IVU_EN BIT(4)
+#define CM_GCR_CONTROL_SHST_EN BIT(3)
+#define CM_GCR_CONTROL_PARK_EN BIT(2)
+#define CM_GCR_CONTROL_MMIO_LIMIT_DIS BIT(1)
+#define CM_GCR_CONTROL_SPEC_READ_EN BIT(0)
+
+/* GCR_CONTROL2 - Global CM2 Settings (continue) */
+GCR_ACCESSOR_RW(64, 0x018, control2)
+#define CM_GCR_CONTROL2_L2_CACHEOP_LIMIT GENMASK(19, 16)
+#define CM_GCR_CONTROL2_L1_CACHEOP_LIMIT GENMASK(3, 0)
+
/* GCR_ACCESS - Controls core/IOCU access to GCRs */
GCR_ACCESSOR_RW(32, 0x020, access)
#define CM_GCR_ACCESS_ACCESSEN GENMASK(7, 0)
diff --git a/arch/mips/include/asm/video.h b/arch/mips/include/asm/video.h
index 007c106d980fd..c79e777ea465c 100644
--- a/arch/mips/include/asm/video.h
+++ b/arch/mips/include/asm/video.h
@@ -3,14 +3,6 @@
#include <asm/page.h>
-static inline pgprot_t pgprot_framebuffer(pgprot_t prot,
- unsigned long vm_start, unsigned long vm_end,
- unsigned long offset)
-{
- return pgprot_noncached(prot);
-}
-#define pgprot_framebuffer pgprot_framebuffer
-
/*
* MIPS doesn't define __raw_ I/O macros, so the helpers
* in <asm-generic/video.h> don't generate fb_readq() and
diff --git a/arch/mips/kernel/cps-vec.S b/arch/mips/kernel/cps-vec.S
index 2ae7034a3d5c4..ac8f7c91b0fdf 100644
--- a/arch/mips/kernel/cps-vec.S
+++ b/arch/mips/kernel/cps-vec.S
@@ -117,19 +117,19 @@ LEAF(mips_cps_core_boot)
mtc0 t0, CP0_CONFIG
ehb
- /* Jump to kseg0 */
- PTR_LA t0, 1f
- jr t0
- nop
-
/*
* We're up, cached & coherent. Perform any EVA initialization necessary
* before we access memory.
*/
-1: eva_init
+ eva_init
+
+ /* Jump to the actual kernel address kseg0/xkseg (legacy/EVA) */
+ PTR_LA t0, 1f
+ jr t0
+ nop
/* Retrieve boot configuration pointers */
- jal mips_cps_get_bootcfg
+1: jal mips_cps_get_bootcfg
nop
/* Skip core-level init if we started up coherent */
diff --git a/arch/mips/kernel/relocate.c b/arch/mips/kernel/relocate.c
index 59833210542ff..f10601c8e53ae 100644
--- a/arch/mips/kernel/relocate.c
+++ b/arch/mips/kernel/relocate.c
@@ -286,10 +286,10 @@ static inline void __init *determine_relocation_address(void)
static inline void __init *determine_relocation_address(void)
{
/*
- * Choose a new address for the kernel
- * For now we'll hard code the destination
+ * Choose a new address for the kernel. For now we'll hard code
+ * the destination to vmlinux base + 16MB.
*/
- return (void *)0xffffffff81000000;
+ return (void *)(VMLINUX_LOAD_ADDRESS + 0x01000000);
}
#endif
diff --git a/arch/mips/mm/dma-noncoherent.c b/arch/mips/mm/dma-noncoherent.c
index ab4f2a75a7d01..faba6778eb4c5 100644
--- a/arch/mips/mm/dma-noncoherent.c
+++ b/arch/mips/mm/dma-noncoherent.c
@@ -51,7 +51,24 @@ void arch_dma_prep_coherent(struct page *page, size_t size)
void *arch_dma_set_uncached(void *addr, size_t size)
{
- return (void *)(__pa(addr) + UNCAC_BASE);
+ phys_addr_t paddr = virt_to_phys(addr);
+
+ /*
+ * Directly-mapped cached region can be up to 2.5GB on EVA with
+ * still reserving 512MB for the uncached direct mapping. Use the
+ * mapped space to coherently access the 512MB+ memory. Make sure
+ * the page is vunmapped by the callee antagonist like
+ * dma_direct_free().
+ */
+ if (IS_ENABLED(CONFIG_EVA) && paddr >= CSEGX_SIZE) {
+ pgprot_t prot = pgprot_noncached(PAGE_KERNEL);
+ struct page *page = virt_to_page(addr);
+
+ return dma_common_contiguous_remap(page, size, prot,
+ __builtin_return_address(0));
+ }
+
+ return (void *)((unsigned long)paddr + UNCAC_BASE);
}
static inline void dma_sync_virt_for_device(void *addr, size_t size,
diff --git a/arch/mips/mm/physaddr.c b/arch/mips/mm/physaddr.c
index a6b1bf82057a1..8a135bef0b1d9 100644
--- a/arch/mips/mm/physaddr.c
+++ b/arch/mips/mm/physaddr.c
@@ -22,9 +22,7 @@ static inline bool __debug_virt_addr_valid(unsigned long x)
if (x == MAX_DMA_ADDRESS)
return true;
- return x >= PAGE_OFFSET && (KSEGX(x) < KSEG2 ||
- IS_ENABLED(CONFIG_EVA) ||
- !IS_ENABLED(CONFIG_HIGHMEM));
+ return x >= PAGE_OFFSET && (KSEGX(x) < KSEG2 || !IS_ENABLED(CONFIG_HIGHMEM));
}
phys_addr_t __virt_to_phys(volatile const void *x)
diff --git a/arch/mips/net/bpf_jit_comp.c b/arch/mips/net/bpf_jit_comp.c
index e355dfca44008..b10270777d0f3 100644
--- a/arch/mips/net/bpf_jit_comp.c
+++ b/arch/mips/net/bpf_jit_comp.c
@@ -200,6 +200,15 @@ void emit_mov_i(struct jit_context *ctx, u8 dst, s32 imm)
clobber_reg(ctx, dst);
}
+/* dst = imm (register width, no optimizations) */
+void emit_full_mov_i(struct jit_context *ctx, u8 dst, s32 imm)
+{
+ emit(ctx, lui, dst, (s16)((u32)imm >> 16));
+ emit(ctx, ori, dst, dst, (u16)(imm & 0xffff));
+
+ clobber_reg(ctx, dst);
+}
+
/* dst = src (register width) */
void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src)
{
@@ -881,6 +890,10 @@ static int build_body(struct jit_context *ctx)
/* Store the end offset, where the epilogue begins */
ctx->descriptors[prog->len] = ctx->jit_index;
+
+ /* Increase the number of JIT-compile passes for the callee to note */
+ ctx->passes++;
+
return 0;
}
@@ -911,10 +924,8 @@ bool bpf_jit_needs_zext(void)
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
- struct bpf_prog *tmp, *orig_prog = prog;
- struct bpf_binary_header *header = NULL;
- struct jit_context ctx;
- bool tmp_blinded = false;
+ struct bpf_prog *orig_prog = prog;
+ struct jit_context *ctx;
unsigned int tmp_idx;
unsigned int image_size;
u8 *image_ptr;
@@ -926,34 +937,47 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
*/
if (!prog->jit_requested)
return orig_prog;
+
+ /*
+ * If the BPF-program has been already JITed and the JIT-context hasn't
+ * been saved, the extra pass isn't required and just return the passed
+ * image. Otherwise restore the context and procceed with the extra
+ * pass.
+ */
+ if (prog->jited && !prog->aux->jit_data) {
+ return orig_prog;
+ } else if (prog->aux->jit_data) {
+ ctx = prog->aux->jit_data;
+ ctx->extra_pass = true;
+ tmp_idx = ctx->jit_index;
+ image_ptr = (void *)ctx->target;
+ image_size = prog->jited_len;
+ goto skip_init_ctx;
+ }
+
/*
* If constant blinding was enabled and we failed during blinding
* then we must fall back to the interpreter. Otherwise, we save
* the new JITed code.
*/
- tmp = bpf_jit_blind_constants(prog);
- if (IS_ERR(tmp))
+ prog = bpf_jit_blind_constants(prog);
+ if (IS_ERR(prog))
return orig_prog;
- if (tmp != prog) {
- tmp_blinded = true;
- prog = tmp;
- }
-
- memset(&ctx, 0, sizeof(ctx));
- ctx.program = prog;
/*
- * Not able to allocate memory for descriptors[], then
+ * Not able to allocate memory for context and descriptors[], then
* we must fall back to the interpreter
*/
- ctx.descriptors = kcalloc(prog->len + 1, sizeof(*ctx.descriptors),
- GFP_KERNEL);
- if (ctx.descriptors == NULL)
- goto out_err;
+ ctx = kzalloc(struct_size(ctx, descriptors, prog->len + 1), GFP_KERNEL);
+ if (ctx == NULL)
+ goto err_free_clone;
+
+ ctx->program = prog;
/* First pass discovers used resources */
- if (build_body(&ctx) < 0)
- goto out_err;
+ if (build_body(ctx) < 0)
+ goto err_free_context;
+
/*
* Second pass computes instruction offsets.
* If any PC-relative branches are out of range, a sequence of
@@ -964,76 +988,107 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* converted, to guarantee offset table convergence within a
* fixed number of iterations.
*/
- ctx.jit_index = 0;
- build_prologue(&ctx);
- tmp_idx = ctx.jit_index;
+ ctx->jit_index = 0;
+ build_prologue(ctx);
+ tmp_idx = ctx->jit_index;
tries = JIT_MAX_ITERATIONS;
do {
- ctx.jit_index = tmp_idx;
- ctx.changes = 0;
+ ctx->jit_index = tmp_idx;
+ ctx->changes = 0;
if (tries == 2)
- set_convert_flag(&ctx, true);
- if (build_body(&ctx) < 0)
- goto out_err;
- } while (ctx.changes > 0 && --tries > 0);
+ set_convert_flag(ctx, true);
+ if (build_body(ctx) < 0)
+ goto err_free_context;
+ } while (ctx->changes > 0 && --tries > 0);
- if (WARN_ONCE(ctx.changes > 0, "JIT offsets failed to converge"))
- goto out_err;
+ if (WARN_ONCE(ctx->changes > 0, "JIT offsets failed to converge"))
+ goto err_free_context;
- build_epilogue(&ctx, MIPS_R_RA);
+ build_epilogue(ctx, MIPS_R_RA);
/* Now we know the size of the structure to make */
- image_size = sizeof(u32) * ctx.jit_index;
- header = bpf_jit_binary_alloc(image_size, &image_ptr,
- sizeof(u32), jit_fill_hole);
+ image_size = sizeof(u32) * ctx->jit_index;
+ ctx->header = bpf_jit_binary_alloc(image_size, &image_ptr,
+ sizeof(u32), jit_fill_hole);
/*
* Not able to allocate memory for the structure then
* we must fall back to the interpretation
*/
- if (header == NULL)
- goto out_err;
+ if (ctx->header == NULL)
+ goto err_free_context;
/* Actual pass to generate final JIT code */
- ctx.target = (u32 *)image_ptr;
- ctx.jit_index = 0;
+ ctx->target = (u32 *)image_ptr;
/*
- * If building the JITed code fails somehow,
- * we fall back to the interpretation.
+ * If building the JITed code fails somehow fall back to the
+ * interpretation in both cases - initial and extra passes.
*/
- build_prologue(&ctx);
- if (build_body(&ctx) < 0)
- goto out_err;
- build_epilogue(&ctx, MIPS_R_RA);
+skip_init_ctx:
+ ctx->jit_index = 0;
+ build_prologue(ctx);
+ if (build_body(ctx) < 0)
+ goto err_clean_prog;
+ build_epilogue(ctx, MIPS_R_RA);
+
+ /* Sanity check extra JIT-compile pass */
+ if (ctx->extra_pass && ctx->jit_index != tmp_idx) {
+ pr_err_once("multi-func JIT bug %d != %d\n",
+ ctx->jit_index, tmp_idx);
+ goto err_clean_prog;
+ }
- /* Populate line info meta data */
- set_convert_flag(&ctx, false);
- bpf_prog_fill_jited_linfo(prog, &ctx.descriptors[1]);
+ prog->bpf_func = (void *)ctx->target;
+ prog->jited = 1;
+ prog->jited_len = image_size;
- /* Set as read-only exec and flush instruction cache */
- if (bpf_jit_binary_lock_ro(header))
- goto out_err;
- flush_icache_range((unsigned long)header,
- (unsigned long)&ctx.target[ctx.jit_index]);
+ /*
+ * Finalize the image and free the context after the extra JIT-compile
+ * pass or if no extra pass implied
+ */
+ if (ctx->extra_pass || !prog->is_func) {
+ /* Populate line info meta data */
+ set_convert_flag(ctx, false);
+ bpf_prog_fill_jited_linfo(prog, &ctx->descriptors[1]);
+
+ if (bpf_jit_enable > 1) {
+ bpf_jit_dump(prog->len, image_size,
+ ctx->passes + ctx->extra_pass,
+ ctx->target);
+ }
- if (bpf_jit_enable > 1)
- bpf_jit_dump(prog->len, image_size, 2, ctx.target);
+ /* Set as read-only exec and flush instruction cache */
+ if (bpf_jit_binary_lock_ro(ctx->header))
+ goto err_clean_prog;
+ flush_icache_range((unsigned long)ctx->header,
+ (unsigned long)&ctx->target[ctx->jit_index]);
- prog->bpf_func = (void *)ctx.target;
- prog->jited = 1;
- prog->jited_len = image_size;
+ /* JIT-compile context is no longer needed */
+ prog->aux->jit_data = NULL;
+ kfree(ctx);
+ } else {
+ prog->aux->jit_data = ctx;
+ }
+
+ if (prog != orig_prog)
+ bpf_jit_prog_release_other(prog, orig_prog);
-out:
- if (tmp_blinded)
- bpf_jit_prog_release_other(prog, prog == orig_prog ?
- tmp : orig_prog);
- kfree(ctx.descriptors);
return prog;
-out_err:
- prog = orig_prog;
- if (header)
- bpf_jit_binary_free(header);
- goto out;
+err_clean_prog:
+ orig_prog->bpf_func = NULL;
+ orig_prog->jited = 0;
+ orig_prog->jited_len = 0;
+
+ bpf_jit_binary_free(ctx->header);
+
+err_free_context:
+ kfree(ctx);
+
+err_free_clone:
+ if (prog != orig_prog)
+ bpf_jit_prog_release_other(orig_prog, prog);
+
+ return orig_prog;
}
diff --git a/arch/mips/net/bpf_jit_comp.h b/arch/mips/net/bpf_jit_comp.h
index a37fe20818eb9..e258d7ff61a05 100644
--- a/arch/mips/net/bpf_jit_comp.h
+++ b/arch/mips/net/bpf_jit_comp.h
@@ -74,16 +74,19 @@
/* JIT context for an eBPF program */
struct jit_context {
struct bpf_prog *program; /* The eBPF program being JITed */
- u32 *descriptors; /* eBPF to JITed CPU insn descriptors */
- u32 *target; /* JITed code buffer */
- u32 bpf_index; /* Index of current BPF program insn */
- u32 jit_index; /* Index of current JIT target insn */
+ struct bpf_binary_header *header; /* The eBPF JITed binary header */
+ bool extra_pass; /* Extra compile pass flag */
+ u32 passes; /* Number of compile passes performed */
u32 changes; /* Number of PC-relative branch conv */
u32 accessed; /* Bit mask of read eBPF registers */
u32 clobbered; /* Bit mask of modified CPU registers */
u32 stack_size; /* Total allocated stack size in bytes */
u32 saved_size; /* Size of callee-saved registers */
u32 stack_used; /* Stack size used for function calls */
+ u32 jit_index; /* Index of current JIT target insn */
+ u32 *target; /* JITed code buffer */
+ u32 bpf_index; /* Index of current BPF program insn */
+ u32 descriptors[]; /* eBPF to JITed CPU insn descriptors */
};
/* Emit the instruction if the JIT memory space has been allocated */
@@ -159,6 +162,9 @@ int get_offset(const struct jit_context *ctx, int off);
/* dst = imm (32-bit) */
void emit_mov_i(struct jit_context *ctx, u8 dst, s32 imm);
+/* dst = imm (32-bit, no optimizations) */
+void emit_full_mov_i(struct jit_context *ctx, u8 dst, s32 imm);
+
/* dst = src (32-bit) */
void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src);
diff --git a/arch/mips/net/bpf_jit_comp32.c b/arch/mips/net/bpf_jit_comp32.c
index 40a878b672f5d..ab1fe3b7b0c0f 100644
--- a/arch/mips/net/bpf_jit_comp32.c
+++ b/arch/mips/net/bpf_jit_comp32.c
@@ -1266,17 +1266,18 @@ static int emit_call(struct jit_context *ctx, const struct bpf_insn *insn)
u64 addr;
/* Decode the call address */
- if (bpf_jit_get_func_addr(ctx->program, insn, false,
+ if (bpf_jit_get_func_addr(ctx->program, insn, ctx->extra_pass,
&addr, &fixed) < 0)
return -1;
- if (!fixed)
- return -1;
/* Push stack arguments */
push_regs(ctx, JIT_STACK_REGS, 0, JIT_RESERVED_STACK);
- /* Emit function call */
- emit_mov_i(ctx, MIPS_R_T9, addr);
+ /* Emit function call (BPF-to-BPF calls require full address dump) */
+ if (fixed)
+ emit_mov_i(ctx, MIPS_R_T9, addr);
+ else
+ emit_full_mov_i(ctx, MIPS_R_T9, addr);
emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9);
emit(ctx, nop); /* Delay slot */
@@ -1648,8 +1649,14 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
break;
/* dst = imm64 */
case BPF_LD | BPF_IMM | BPF_DW:
- emit_mov_i(ctx, lo(dst), imm);
- emit_mov_i(ctx, hi(dst), insn[1].imm);
+ /* Function calls need full address dump */
+ if (bpf_pseudo_func(insn)) {
+ emit_full_mov_i(ctx, lo(dst), imm);
+ emit_full_mov_i(ctx, hi(dst), insn[1].imm);
+ } else {
+ emit_mov_i(ctx, lo(dst), imm);
+ emit_mov_i(ctx, hi(dst), insn[1].imm);
+ }
return 1;
/* LDX: dst = *(size *)(src + off) */
case BPF_LDX | BPF_MEM | BPF_W:
diff --git a/arch/mips/net/bpf_jit_comp64.c b/arch/mips/net/bpf_jit_comp64.c
index fa7e9aa37f498..ba58870009024 100644
--- a/arch/mips/net/bpf_jit_comp64.c
+++ b/arch/mips/net/bpf_jit_comp64.c
@@ -154,6 +154,22 @@ static void emit_mov_i64(struct jit_context *ctx, u8 dst, u64 imm64)
clobber_reg(ctx, dst);
}
+/* dst = imm (64-bit, no optimizations) */
+static void emit_full_mov_i64(struct jit_context *ctx, u8 dst, u64 imm64)
+{
+ u16 half = imm64 >> 48;
+ int k;
+
+ emit(ctx, ori, dst, MIPS_R_ZERO, half);
+ for (k = 1; k < 4; k++) {
+ half = imm64 >> (48 - 16 * k);
+
+ emit(ctx, dsll_safe, dst, dst, 16);
+ emit(ctx, ori, dst, dst, half);
+ }
+ clobber_reg(ctx, dst);
+}
+
/* ALU immediate operation (64-bit) */
static void emit_alu_i64(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
{
@@ -444,17 +460,18 @@ static int emit_call(struct jit_context *ctx, const struct bpf_insn *insn)
u64 addr;
/* Decode the call address */
- if (bpf_jit_get_func_addr(ctx->program, insn, false,
+ if (bpf_jit_get_func_addr(ctx->program, insn, ctx->extra_pass,
&addr, &fixed) < 0)
return -1;
- if (!fixed)
- return -1;
/* Push caller-saved registers on stack */
push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, 0, 0);
- /* Emit function call */
- emit_mov_i64(ctx, tmp, addr & JALR_MASK);
+ /* Emit function call (BPF-to-BPF calls require full address dump) */
+ if (fixed)
+ emit_mov_i64(ctx, tmp, addr & JALR_MASK);
+ else
+ emit_full_mov_i64(ctx, tmp, addr & JALR_MASK);
emit(ctx, jalr, MIPS_R_RA, tmp);
emit(ctx, nop); /* Delay slot */
@@ -642,6 +659,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
s32 imm = insn->imm;
s32 val, rel;
u8 alu, jmp;
+ u64 imm64;
switch (code) {
/* ALU operations */
@@ -818,7 +836,12 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
break;
/* dst = imm64 */
case BPF_LD | BPF_IMM | BPF_DW:
- emit_mov_i64(ctx, dst, (u32)imm | ((u64)insn[1].imm << 32));
+ /* Function calls need full address dump */
+ imm64 = ((u64)insn[1].imm << 32) | (u32)imm;
+ if (bpf_pseudo_func(insn))
+ emit_full_mov_i64(ctx, dst, imm64);
+ else
+ emit_mov_i64(ctx, dst, imm64);
return 1;
/* LDX: dst = *(size *)(src + off) */
case BPF_LDX | BPF_MEM | BPF_W:
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index b75fdaffad9a4..2fb6ba4854c14 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -414,7 +414,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
SET_RW_32(dw, chan->dir, int_mask, tmp);
/* Linked list error */
tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
- tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
+ tmp |= FIELD_PREP(EDMA_V0_LL_LOCAL_ABORT_INT_MASK, BIT(chan->id));
+ if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL))
+ tmp |= FIELD_PREP(EDMA_V0_LL_REMOTE_ABORT_INT_MASK, BIT(chan->id));
SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
/* Channel control */
SET_CH_32(dw, chan->dir, chan->id, ch_control1,
diff --git a/drivers/dma/dw-edma/dw-edma-v0-regs.h b/drivers/dma/dw-edma/dw-edma-v0-regs.h
index e175f7b20480e..ffc544afc5e39 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-regs.h
+++ b/drivers/dma/dw-edma/dw-edma-v0-regs.h
@@ -19,7 +19,8 @@
#define EDMA_V0_READ_CH_COUNT_MASK GENMASK(19, 16)
#define EDMA_V0_CH_STATUS_MASK GENMASK(6, 5)
#define EDMA_V0_DOORBELL_CH_MASK GENMASK(2, 0)
-#define EDMA_V0_LINKED_LIST_ERR_MASK GENMASK(7, 0)
+#define EDMA_V0_LL_REMOTE_ABORT_INT_MASK GENMASK(7, 0)
+#define EDMA_V0_LL_LOCAL_ABORT_INT_MASK GENMASK(23, 16)
#define EDMA_V0_CH_ODD_MSI_DATA_MASK GENMASK(31, 16)
#define EDMA_V0_CH_EVEN_MSI_DATA_MASK GENMASK(15, 0)
diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index dd75f97a33b3d..fbc46cbfe2591 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -39,6 +39,8 @@
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)
+static u32 dwc_get_hard_llp_desc_residue(struct dw_dma_chan *dwc, struct dw_desc *desc);
+
/*----------------------------------------------------------------------*/
static struct device *chan2dev(struct dma_chan *chan)
@@ -141,6 +143,12 @@ static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc)
channel_clear_bit(dw, CH_EN, dwc->mask);
while (dma_readl(dw, CH_EN) & dwc->mask)
cpu_relax();
+
+ dma_writel(dw, CLEAR.XFER, dwc->mask);
+ dma_writel(dw, CLEAR.BLOCK, dwc->mask);
+ dma_writel(dw, CLEAR.SRC_TRAN, dwc->mask);
+ dma_writel(dw, CLEAR.DST_TRAN, dwc->mask);
+ dma_writel(dw, CLEAR.ERROR, dwc->mask);
}
/*----------------------------------------------------------------------*/
@@ -257,34 +265,6 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc,
dmaengine_desc_callback_invoke(&cb, NULL);
}
-static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc)
-{
- struct dw_desc *desc, *_desc;
- LIST_HEAD(list);
- unsigned long flags;
-
- spin_lock_irqsave(&dwc->lock, flags);
- if (dma_readl(dw, CH_EN) & dwc->mask) {
- dev_err(chan2dev(&dwc->chan),
- "BUG: XFER bit set, but channel not idle!\n");
-
- /* Try to continue after resetting the channel... */
- dwc_chan_disable(dw, dwc);
- }
-
- /*
- * Submit queued descriptors ASAP, i.e. before we go through
- * the completed ones.
- */
- list_splice_init(&dwc->active_list, &list);
- dwc_dostart_first_queued(dwc);
-
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- list_for_each_entry_safe(desc, _desc, &list, desc_node)
- dwc_descriptor_complete(dwc, desc, true);
-}
-
/* Returns how many bytes were already received from source */
static inline u32 dwc_get_sent(struct dw_dma_chan *dwc)
{
@@ -297,14 +277,13 @@ static inline u32 dwc_get_sent(struct dw_dma_chan *dwc)
static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
{
- dma_addr_t llp;
struct dw_desc *desc, *_desc;
struct dw_desc *child;
u32 status_xfer;
unsigned long flags;
+ LIST_HEAD(list);
spin_lock_irqsave(&dwc->lock, flags);
- llp = channel_readl(dwc, LLP);
status_xfer = dma_readl(dw, RAW.XFER);
if (status_xfer & dwc->mask) {
@@ -341,9 +320,26 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags);
}
+ /*
+ * No more active descriptors left to handle. So submit the
+ * queued descriptors and finish up the already handled ones.
+ */
+ if (dma_readl(dw, CH_EN) & dwc->mask) {
+ dev_err(chan2dev(&dwc->chan),
+ "BUG: XFER bit set, but channel not idle!\n");
+
+ /* Try to continue after resetting the channel... */
+ dwc_chan_disable(dw, dwc);
+ }
+
+ list_splice_init(&dwc->active_list, &list);
+ dwc_dostart_first_queued(dwc);
+
spin_unlock_irqrestore(&dwc->lock, flags);
- dwc_complete_all(dw, dwc);
+ list_for_each_entry_safe(desc, _desc, &list, desc_node)
+ dwc_descriptor_complete(dwc, desc, true);
+
return;
}
@@ -358,41 +354,16 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
return;
}
- dev_vdbg(chan2dev(&dwc->chan), "%s: llp=%pad\n", __func__, &llp);
+ dev_vdbg(chan2dev(&dwc->chan), "%s: hard LLP mode\n", __func__);
list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) {
- /* Initial residue value */
- desc->residue = desc->total_len;
-
- /* Check first descriptors addr */
- if (desc->txd.phys == DWC_LLP_LOC(llp)) {
+ desc->residue = dwc_get_hard_llp_desc_residue(dwc, desc);
+ if (desc->residue) {
spin_unlock_irqrestore(&dwc->lock, flags);
return;
}
- /* Check first descriptors llp */
- if (lli_read(desc, llp) == llp) {
- /* This one is currently in progress */
- desc->residue -= dwc_get_sent(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
- return;
- }
-
- desc->residue -= desc->len;
- list_for_each_entry(child, &desc->tx_list, desc_node) {
- if (lli_read(child, llp) == llp) {
- /* Currently in progress */
- desc->residue -= dwc_get_sent(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
- return;
- }
- desc->residue -= child->len;
- }
-
- /*
- * No descriptors so far seem to be in progress, i.e.
- * this one must be done.
- */
+ /* No data left to be send. Finalize the transfer then */
spin_unlock_irqrestore(&dwc->lock, flags);
dwc_descriptor_complete(dwc, desc, true);
spin_lock_irqsave(&dwc->lock, flags);
@@ -976,6 +947,45 @@ static struct dw_desc *dwc_find_desc(struct dw_dma_chan *dwc, dma_cookie_t c)
return NULL;
}
+static u32 dwc_get_soft_llp_desc_residue(struct dw_dma_chan *dwc, struct dw_desc *desc)
+{
+ u32 residue = desc->residue;
+
+ if (residue)
+ residue -= dwc_get_sent(dwc);
+
+ return residue;
+}
+
+static u32 dwc_get_hard_llp_desc_residue(struct dw_dma_chan *dwc, struct dw_desc *desc)
+{
+ u32 residue = desc->total_len;
+ struct dw_desc *child;
+ dma_addr_t llp;
+
+ llp = channel_readl(dwc, LLP);
+
+ /* Check first descriptor for been pending to be fetched by DMAC */
+ if (desc->txd.phys == DWC_LLP_LOC(llp))
+ return residue;
+
+ /* Check first descriptor LLP to see if it's currently in-progress */
+ if (lli_read(desc, llp) == llp)
+ return residue - dwc_get_sent(dwc);
+
+ /* Check subordinate LLPs to find the currently in-progress desc */
+ residue -= desc->len;
+ list_for_each_entry(child, &desc->tx_list, desc_node) {
+ if (lli_read(child, llp) == llp)
+ return residue - dwc_get_sent(dwc);
+
+ residue -= child->len;
+ }
+
+ /* Shall return zero if no in-progress desc found */
+ return residue;
+}
+
static u32 dwc_get_residue_and_status(struct dw_dma_chan *dwc, dma_cookie_t cookie,
enum dma_status *status)
{
@@ -988,9 +998,11 @@ static u32 dwc_get_residue_and_status(struct dw_dma_chan *dwc, dma_cookie_t cook
desc = dwc_find_desc(dwc, cookie);
if (desc) {
if (desc == dwc_first_active(dwc)) {
- residue = desc->residue;
- if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue)
- residue -= dwc_get_sent(dwc);
+ if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags))
+ residue = dwc_get_soft_llp_desc_residue(dwc, desc);
+ else
+ residue = dwc_get_hard_llp_desc_residue(dwc, desc);
+
if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags))
*status = DMA_PAUSED;
} else {
@@ -1016,12 +1028,6 @@ dwc_tx_status(struct dma_chan *chan,
if (ret == DMA_COMPLETE)
return ret;
- dwc_scan_descriptors(to_dw_dma(chan->device), dwc);
-
- ret = dma_cookie_status(chan, cookie, txstate);
- if (ret == DMA_COMPLETE)
- return ret;
-
dma_set_residue(txstate, dwc_get_residue_and_status(dwc, cookie, &ret));
return ret;
}
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index a44b85c440cad..5cee7343683b7 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -471,7 +471,6 @@ config EDAC_ARMADA_XP
config EDAC_SYNOPSYS
tristate "Synopsys DDR Memory Controller"
- depends on ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || ARCH_MXC
help
Support for error detection and correction on the Synopsys DDR
memory controller.
@@ -526,6 +525,13 @@ config EDAC_DMC520
Support for error detection and correction on the
SoCs with ARM DMC-520 DRAM controller.
+config EDAC_ZYNQ
+ tristate "Xilinx Zynq A05 DDR Memory Controller"
+ depends on ARCH_ZYNQ || COMPILE_TEST
+ help
+ Support for error detection and correction on the Xilinx Zynq A05
+ DDR memory controller.
+
config EDAC_ZYNQMP
tristate "Xilinx ZynqMP OCM Controller"
depends on ARCH_ZYNQMP || COMPILE_TEST
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index a37534300ab92..f4b32855096a7 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o
obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o
obj-$(CONFIG_EDAC_DMC520) += dmc520_edac.o
obj-$(CONFIG_EDAC_NPCM) += npcm_edac.o
+obj-$(CONFIG_EDAC_ZYNQ) += zynq_edac.o
obj-$(CONFIG_EDAC_ZYNQMP) += zynqmp_edac.o
obj-$(CONFIG_EDAC_VERSAL) += versal_edac.o
obj-$(CONFIG_EDAC_LOONGSON) += loongson_edac.o
diff --git a/drivers/edac/dmc520_edac.c b/drivers/edac/dmc520_edac.c
index 64a4d0a07032c..791890a042903 100644
--- a/drivers/edac/dmc520_edac.c
+++ b/drivers/edac/dmc520_edac.c
@@ -173,8 +173,6 @@ struct dmc520_edac {
int masks[NUMBER_OF_IRQS];
};
-static int dmc520_mc_idx;
-
static u32 dmc520_read_reg(struct dmc520_edac *pvt, u32 offset)
{
return readl(pvt->reg_base + offset);
@@ -515,7 +513,7 @@ static int dmc520_edac_probe(struct platform_device *pdev)
layers[0].size = dmc520_get_rank_count(reg_base);
layers[0].is_virt_csrow = true;
- mci = edac_mc_alloc(dmc520_mc_idx++, ARRAY_SIZE(layers), layers, sizeof(*pvt));
+ mci = edac_mc_alloc(EDAC_AUTO_MC_NUM, ARRAY_SIZE(layers), layers, sizeof(*pvt));
if (!mci) {
edac_printk(KERN_ERR, EDAC_MOD_NAME,
"Failed to allocate memory for mc instance\n");
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 11d66333d93b4..f6b822c35ac80 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -29,6 +29,9 @@
#include <linux/edac.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
+#include <linux/idr.h>
+#include <linux/of.h>
+
#include <asm/page.h>
#include "edac_mc.h"
#include "edac_module.h"
@@ -46,6 +49,7 @@ EXPORT_SYMBOL_GPL(edac_op_state);
/* lock to memory controller's control array */
static DEFINE_MUTEX(mem_ctls_mutex);
static LIST_HEAD(mc_devices);
+static DEFINE_IDR(mc_idr);
/*
* Used to lock EDAC MC to just one module, avoiding two drivers e. g.
@@ -147,10 +151,12 @@ const char * const edac_mem_types[] = {
[MEM_RDR] = "Registered-SDR",
[MEM_DDR] = "Unbuffered-DDR",
[MEM_RDDR] = "Registered-DDR",
+ [MEM_LPDDR] = "Low-Power-(m)DDR-RAM",
[MEM_RMBS] = "RMBS",
[MEM_DDR2] = "Unbuffered-DDR2",
[MEM_FB_DDR2] = "FullyBuffered-DDR2",
[MEM_RDDR2] = "Registered-DDR2",
+ [MEM_LPDDR2] = "Low-Power-DDR2-RAM",
[MEM_XDR] = "XDR",
[MEM_DDR3] = "Unbuffered-DDR3",
[MEM_RDDR3] = "Registered-DDR3",
@@ -256,7 +262,6 @@ static int edac_mc_alloc_dimms(struct mem_ctl_info *mci)
unsigned int pos[EDAC_MAX_LAYERS];
unsigned int row, chn, idx;
int layer;
- void *p;
/*
* Allocate and fill the dimm structs
@@ -271,7 +276,6 @@ static int edac_mc_alloc_dimms(struct mem_ctl_info *mci)
for (idx = 0; idx < mci->tot_dimms; idx++) {
struct dimm_info *dimm;
struct rank_info *chan;
- int n, len;
chan = mci->csrows[row]->channels[chn];
@@ -282,22 +286,9 @@ static int edac_mc_alloc_dimms(struct mem_ctl_info *mci)
dimm->mci = mci;
dimm->idx = idx;
- /*
- * Copy DIMM location and initialize it.
- */
- len = sizeof(dimm->label);
- p = dimm->label;
- n = scnprintf(p, len, "mc#%u", mci->mc_idx);
- p += n;
- len -= n;
- for (layer = 0; layer < mci->n_layers; layer++) {
- n = scnprintf(p, len, "%s#%u",
- edac_layer_name[mci->layers[layer].type],
- pos[layer]);
- p += n;
- len -= n;
+ /* Copy DIMM location */
+ for (layer = 0; layer < mci->n_layers; layer++)
dimm->location[layer] = pos[layer];
- }
/* Link it to the csrows old API data */
chan->dimm = dimm;
@@ -508,7 +499,91 @@ void edac_mc_reset_delay_period(unsigned long value)
mutex_unlock(&mem_ctls_mutex);
}
+/**
+ * edac_mc_alloc_id() - Allocate unique Memory Controller identifier
+ *
+ * @mci: pointer to the mci structure to allocate ID for
+ *
+ * Use edac_mc_free_id() to coherently free the MC identifier.
+ *
+ * .. note::
+ * locking model: must be called with the mem_ctls_mutex lock held
+ *
+ * Returns:
+ * 0 on Success, or an error code on failure
+ */
+static int edac_mc_alloc_id(struct mem_ctl_info *mci)
+{
+ struct device_node *np = dev_of_node(mci->pdev);
+ int ret, min, max;
+
+ if (mci->mc_idx == EDAC_AUTO_MC_NUM) {
+ ret = of_alias_get_id(np, "mc");
+ if (ret >= 0) {
+ min = ret;
+ max = ret + 1;
+ } else {
+ min = of_alias_get_highest_id("mc");
+ if (min >= 0)
+ min++;
+ else
+ min = 0;
+
+ max = 0;
+ }
+ } else {
+ min = mci->mc_idx;
+ max = mci->mc_idx + 1;
+ }
+
+ ret = idr_alloc(&mc_idr, mci, min, max, GFP_KERNEL);
+ if (ret < 0)
+ return ret == -ENOSPC ? -EBUSY : ret;
+
+ mci->mc_idx = ret;
+
+ return 0;
+}
+
+/**
+ * edac_mc_free_id() - Free Memory Controller identifier
+ *
+ * @mci: pointer to the mci structure to free ID from
+ *
+ * .. note::
+ * locking model: must be called with the mem_ctls_mutex lock held
+ */
+static void edac_mc_free_id(struct mem_ctl_info *mci)
+{
+ idr_remove(&mc_idr, mci->mc_idx);
+}
+
+/**
+ * edac_mc_init_labels() - Initialize DIMM labels
+ *
+ * @mci: pointer to the mci structure which DIMM labels need to be initialized
+ *
+ * .. note::
+ * locking model: must be called with the mem_ctls_mutex lock held
+ */
+static void edac_mc_init_labels(struct mem_ctl_info *mci)
+{
+ int n, len, layer;
+ unsigned int idx;
+ char *p;
+ for (idx = 0; idx < mci->tot_dimms; idx++) {
+ len = sizeof(mci->dimms[idx]->label);
+ p = mci->dimms[idx]->label;
+
+ n = scnprintf(p, len, "mc#%u", mci->mc_idx);
+ for (layer = 0; layer < mci->n_layers; layer++) {
+ n += scnprintf(p + n, len - n, "%s#%u",
+ edac_layer_name[mci->layers[layer].type],
+ mci->dimms[idx]->location[layer]);
+ }
+ }
+}
/* Return 0 on success, 1 on failure.
* Before calling this function, caller must
@@ -600,7 +675,8 @@ EXPORT_SYMBOL_GPL(edac_get_owner);
int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci,
const struct attribute_group **groups)
{
- int ret = -EINVAL;
+ int ret;
+
edac_dbg(0, "\n");
#ifdef CONFIG_EDAC_DEBUG
@@ -637,18 +713,30 @@ int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci,
goto fail0;
}
- if (add_mc_to_global_list(mci))
+ ret = edac_mc_alloc_id(mci);
+ if (ret) {
+ edac_printk(KERN_ERR, EDAC_MC, "failed to allocate MC idx %u\n",
+ mci->mc_idx);
goto fail0;
+ }
+
+ edac_mc_init_labels(mci);
+
+ if (add_mc_to_global_list(mci)) {
+ ret = -EINVAL;
+ goto fail1;
+ }
/* set load time so that error rate can be tracked */
mci->start_time = jiffies;
mci->bus = edac_get_sysfs_subsys();
- if (edac_create_sysfs_mci_device(mci, groups)) {
+ ret = edac_create_sysfs_mci_device(mci, groups);
+ if (ret) {
edac_mc_printk(mci, KERN_WARNING,
"failed to create sysfs device\n");
- goto fail1;
+ goto fail2;
}
if (mci->edac_check) {
@@ -672,9 +760,12 @@ int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci,
mutex_unlock(&mem_ctls_mutex);
return 0;
-fail1:
+fail2:
del_mc_from_global_list(mci);
+fail1:
+ edac_mc_free_id(mci);
+
fail0:
mutex_unlock(&mem_ctls_mutex);
return ret;
@@ -702,6 +793,8 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev)
if (del_mc_from_global_list(mci))
edac_mc_owner = NULL;
+ edac_mc_free_id(mci);
+
mutex_unlock(&mem_ctls_mutex);
if (mci->edac_check)
diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h
index 881b00eadf7a5..4b6676235b1b4 100644
--- a/drivers/edac/edac_mc.h
+++ b/drivers/edac/edac_mc.h
@@ -23,6 +23,7 @@
#define _EDAC_MC_H_
#include <linux/kernel.h>
+#include <linux/limits.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/spinlock.h>
@@ -37,6 +38,9 @@
#include <linux/workqueue.h>
#include <linux/edac.h>
+/* Generate MC identifier automatically */
+#define EDAC_AUTO_MC_NUM UINT_MAX
+
#if PAGE_SHIFT < 20
#define PAGES_TO_MiB(pages) ((pages) >> (20 - PAGE_SHIFT))
#define MiB_TO_PAGES(mb) ((mb) << (20 - PAGE_SHIFT))
diff --git a/drivers/edac/pasemi_edac.c b/drivers/edac/pasemi_edac.c
index 1a1c3296ccc83..afebfbda1ea00 100644
--- a/drivers/edac/pasemi_edac.c
+++ b/drivers/edac/pasemi_edac.c
@@ -57,8 +57,6 @@
#define PASEMI_EDAC_ERROR_GRAIN 64
static int last_page_in_mmc;
-static int system_mmc_id;
-
static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci)
{
@@ -203,8 +201,7 @@ static int pasemi_edac_probe(struct pci_dev *pdev,
layers[1].type = EDAC_MC_LAYER_CHANNEL;
layers[1].size = PASEMI_EDAC_NR_CHANS;
layers[1].is_virt_csrow = false;
- mci = edac_mc_alloc(system_mmc_id++, ARRAY_SIZE(layers), layers,
- 0);
+ mci = edac_mc_alloc(EDAC_AUTO_MC_NUM, ARRAY_SIZE(layers), layers, 0);
if (mci == NULL)
return -ENOMEM;
diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c
index 51143b3257de2..6e98bd89eb3b6 100644
--- a/drivers/edac/synopsys_edac.c
+++ b/drivers/edac/synopsys_edac.c
@@ -1,97 +1,49 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Synopsys DDR ECC Driver
+ * Synopsys DW uMCTL2 DDR ECC Driver
* This driver is based on ppc4xx_edac.c drivers
*
* Copyright (C) 2012 - 2014 Xilinx, Inc.
*/
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
#include <linux/edac.h>
+#include <linux/fs.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
#include <linux/module.h>
+#include <linux/pfn.h>
#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/sizes.h>
#include <linux/spinlock.h>
#include <linux/sizes.h>
+#include <linux/units.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include "edac_module.h"
-/* Number of cs_rows needed per memory controller */
-#define SYNPS_EDAC_NR_CSROWS 1
-
/* Number of channels per memory controller */
-#define SYNPS_EDAC_NR_CHANS 1
-
-/* Granularity of reported error in bytes */
-#define SYNPS_EDAC_ERR_GRAIN 1
-
-#define SYNPS_EDAC_MSG_SIZE 256
-
-#define SYNPS_EDAC_MOD_STRING "synps_edac"
-#define SYNPS_EDAC_MOD_VER "1"
-
-/* Synopsys DDR memory controller registers that are relevant to ECC */
-#define CTRL_OFST 0x0
-#define T_ZQ_OFST 0xA4
-
-/* ECC control register */
-#define ECC_CTRL_OFST 0xC4
-/* ECC log register */
-#define CE_LOG_OFST 0xC8
-/* ECC address register */
-#define CE_ADDR_OFST 0xCC
-/* ECC data[31:0] register */
-#define CE_DATA_31_0_OFST 0xD0
-
-/* Uncorrectable error info registers */
-#define UE_LOG_OFST 0xDC
-#define UE_ADDR_OFST 0xE0
-#define UE_DATA_31_0_OFST 0xE4
-
-#define STAT_OFST 0xF0
-#define SCRUB_OFST 0xF4
-
-/* Control register bit field definitions */
-#define CTRL_BW_MASK 0xC
-#define CTRL_BW_SHIFT 2
-
-#define DDRCTL_WDTH_16 1
-#define DDRCTL_WDTH_32 0
-
-/* ZQ register bit field definitions */
-#define T_ZQ_DDRMODE_MASK 0x2
-
-/* ECC control register bit field definitions */
-#define ECC_CTRL_CLR_CE_ERR 0x2
-#define ECC_CTRL_CLR_UE_ERR 0x1
-
-/* ECC correctable/uncorrectable error log register definitions */
-#define LOG_VALID 0x1
-#define CE_LOG_BITPOS_MASK 0xFE
-#define CE_LOG_BITPOS_SHIFT 1
-
-/* ECC correctable/uncorrectable error address register definitions */
-#define ADDR_COL_MASK 0xFFF
-#define ADDR_ROW_MASK 0xFFFF000
-#define ADDR_ROW_SHIFT 12
-#define ADDR_BANK_MASK 0x70000000
-#define ADDR_BANK_SHIFT 28
-
-/* ECC statistic register definitions */
-#define STAT_UECNT_MASK 0xFF
-#define STAT_CECNT_MASK 0xFF00
-#define STAT_CECNT_SHIFT 8
-
-/* ECC scrub register definitions */
-#define SCRUB_MODE_MASK 0x7
-#define SCRUB_MODE_SECDED 0x4
-
-/* DDR ECC Quirks */
-#define DDR_ECC_INTR_SUPPORT BIT(0)
-#define DDR_ECC_DATA_POISON_SUPPORT BIT(1)
-#define DDR_ECC_INTR_SELF_CLEAR BIT(2)
-
-/* ZynqMP Enhanced DDR memory controller registers that are relevant to ECC */
+#define SNPS_EDAC_NR_CHANS 1
+
+#define SNPS_EDAC_MSG_SIZE 256
+
+#define SNPS_EDAC_MOD_STRING "snps_edac"
+#define SNPS_EDAC_MOD_VER "1"
+
+/* DDR capabilities */
+#define SNPS_CAP_ECC_SCRUB BIT(0)
+#define SNPS_CAP_ECC_SCRUBBER BIT(1)
+#define SNPS_CAP_ZYNQMP BIT(31)
+
+/* Synopsys uMCTL2 DDR controller registers that are relevant to ECC */
+
+/* DDRC Master 0 Register */
+#define DDR_MSTR_OFST 0x0
+
/* ECC Configuration Registers */
#define ECC_CFG0_OFST 0x70
#define ECC_CFG1_OFST 0x74
@@ -132,96 +84,126 @@
#define ECC_POISON0_OFST 0xB8
#define ECC_POISON1_OFST 0xBC
-#define ECC_ADDRMAP0_OFFSET 0x200
+/* DDR CRC/Parity Registers */
+#define DDR_CRCPARCTL0_OFST 0xC0
+#define DDR_CRCPARCTL1_OFST 0xC4
+#define DDR_CRCPARCTL2_OFST 0xC8
+#define DDR_CRCPARSTAT_OFST 0xCC
+
+/* DDR Address Map Registers */
+#define DDR_ADDRMAP0_OFST 0x200
+
+/* DDR Software Control Register */
+#define DDR_SWCTL 0x320
+
+/* ECC Poison Pattern Registers */
+#define ECC_POISONPAT0_OFST 0x37C
+#define ECC_POISONPAT1_OFST 0x380
+#define ECC_POISONPAT2_OFST 0x384
+
+/* DDR SAR Registers */
+#define DDR_SARBASE0_OFST 0xF04
+#define DDR_SARSIZE0_OFST 0xF08
+
+/* ECC Scrubber Registers */
+#define ECC_SBRCTL_OFST 0xF24
+#define ECC_SBRSTAT_OFST 0xF28
+#define ECC_SBRWDATA0_OFST 0xF2C
+#define ECC_SBRWDATA1_OFST 0xF30
+
+/* ZynqMP DDR QOS Registers */
+#define ZYNQMP_DDR_QOS_IRQ_STAT_OFST 0x20200
+#define ZYNQMP_DDR_QOS_IRQ_EN_OFST 0x20208
+#define ZYNQMP_DDR_QOS_IRQ_DB_OFST 0x2020C
+
+/* DDR Master register definitions */
+#define DDR_MSTR_DEV_CFG_MASK GENMASK(31, 30)
+#define DDR_MSTR_DEV_X4 0
+#define DDR_MSTR_DEV_X8 1
+#define DDR_MSTR_DEV_X16 2
+#define DDR_MSTR_DEV_X32 3
+#define DDR_MSTR_ACT_RANKS_MASK GENMASK(27, 24)
+#define DDR_MSTR_FREQ_RATIO11 BIT(22)
+#define DDR_MSTR_BURST_RDWR GENMASK(19, 16)
+#define DDR_MSTR_BUSWIDTH_MASK GENMASK(13, 12)
+#define DDR_MSTR_MEM_MASK GENMASK(5, 0)
+#define DDR_MSTR_MEM_LPDDR4 BIT(5)
+#define DDR_MSTR_MEM_DDR4 BIT(4)
+#define DDR_MSTR_MEM_LPDDR3 BIT(3)
+#define DDR_MSTR_MEM_LPDDR2 BIT(2)
+#define DDR_MSTR_MEM_LPDDR BIT(1)
+#define DDR_MSTR_MEM_DDR3 BIT(0)
+#define DDR_MSTR_MEM_DDR2 0
+
+/* ECC CFG0 register definitions */
+#define ECC_CFG0_DIS_SCRUB BIT(4)
+#define ECC_CFG0_MODE_MASK GENMASK(2, 0)
+
+/* ECC CFG1 register definitions */
+#define ECC_CFG1_POISON_BIT BIT(1)
+#define ECC_CFG1_POISON_EN BIT(0)
+
+/* ECC status register definitions */
+#define ECC_STAT_UE_MASK GENMASK(23, 16)
+#define ECC_STAT_CE_MASK GENMASK(15, 8)
+#define ECC_STAT_BITNUM_MASK GENMASK(6, 0)
-/* Control register bitfield definitions */
-#define ECC_CTRL_BUSWIDTH_MASK 0x3000
-#define ECC_CTRL_BUSWIDTH_SHIFT 12
+/* ECC control/clear register definitions */
+#define ECC_CTRL_CLR_CE_ERR BIT(0)
+#define ECC_CTRL_CLR_UE_ERR BIT(1)
#define ECC_CTRL_CLR_CE_ERRCNT BIT(2)
#define ECC_CTRL_CLR_UE_ERRCNT BIT(3)
-
-/* DDR Control Register width definitions */
-#define DDRCTL_EWDTH_16 2
-#define DDRCTL_EWDTH_32 1
-#define DDRCTL_EWDTH_64 0
-
-/* ECC status register definitions */
-#define ECC_STAT_UECNT_MASK 0xF0000
-#define ECC_STAT_UECNT_SHIFT 16
-#define ECC_STAT_CECNT_MASK 0xF00
-#define ECC_STAT_CECNT_SHIFT 8
-#define ECC_STAT_BITNUM_MASK 0x7F
+#define ECC_CTRL_EN_CE_IRQ BIT(8)
+#define ECC_CTRL_EN_UE_IRQ BIT(9)
/* ECC error count register definitions */
-#define ECC_ERRCNT_UECNT_MASK 0xFFFF0000
-#define ECC_ERRCNT_UECNT_SHIFT 16
-#define ECC_ERRCNT_CECNT_MASK 0xFFFF
-
-/* DDR QOS Interrupt register definitions */
-#define DDR_QOS_IRQ_STAT_OFST 0x20200
-#define DDR_QOSUE_MASK 0x4
-#define DDR_QOSCE_MASK 0x2
-#define ECC_CE_UE_INTR_MASK 0x6
-#define DDR_QOS_IRQ_EN_OFST 0x20208
-#define DDR_QOS_IRQ_DB_OFST 0x2020C
-
-/* DDR QOS Interrupt register definitions */
-#define DDR_UE_MASK BIT(9)
-#define DDR_CE_MASK BIT(8)
-
-/* ECC Corrected Error Register Mask and Shifts*/
-#define ECC_CEADDR0_RW_MASK 0x3FFFF
-#define ECC_CEADDR0_RNK_MASK BIT(24)
-#define ECC_CEADDR1_BNKGRP_MASK 0x3000000
-#define ECC_CEADDR1_BNKNR_MASK 0x70000
-#define ECC_CEADDR1_BLKNR_MASK 0xFFF
-#define ECC_CEADDR1_BNKGRP_SHIFT 24
-#define ECC_CEADDR1_BNKNR_SHIFT 16
-
-/* ECC Poison register shifts */
-#define ECC_POISON0_RANK_SHIFT 24
-#define ECC_POISON0_RANK_MASK BIT(24)
-#define ECC_POISON0_COLUMN_SHIFT 0
-#define ECC_POISON0_COLUMN_MASK 0xFFF
-#define ECC_POISON1_BG_SHIFT 28
-#define ECC_POISON1_BG_MASK 0x30000000
-#define ECC_POISON1_BANKNR_SHIFT 24
-#define ECC_POISON1_BANKNR_MASK 0x7000000
-#define ECC_POISON1_ROW_SHIFT 0
-#define ECC_POISON1_ROW_MASK 0x3FFFF
-
-/* DDR Memory type defines */
-#define MEM_TYPE_DDR3 0x1
-#define MEM_TYPE_LPDDR3 0x8
-#define MEM_TYPE_DDR2 0x4
-#define MEM_TYPE_DDR4 0x10
-#define MEM_TYPE_LPDDR4 0x20
-
-/* DDRC Software control register */
-#define DDRC_SWCTL 0x320
-
-/* DDRC ECC CE & UE poison mask */
-#define ECC_CEPOISON_MASK 0x3
-#define ECC_UEPOISON_MASK 0x1
-
-/* DDRC Device config masks */
-#define DDRC_MSTR_CFG_MASK 0xC0000000
-#define DDRC_MSTR_CFG_SHIFT 30
-#define DDRC_MSTR_CFG_X4_MASK 0x0
-#define DDRC_MSTR_CFG_X8_MASK 0x1
-#define DDRC_MSTR_CFG_X16_MASK 0x2
-#define DDRC_MSTR_CFG_X32_MASK 0x3
-
-#define DDR_MAX_ROW_SHIFT 18
-#define DDR_MAX_COL_SHIFT 14
-#define DDR_MAX_BANK_SHIFT 3
-#define DDR_MAX_BANKGRP_SHIFT 2
-
-#define ROW_MAX_VAL_MASK 0xF
-#define COL_MAX_VAL_MASK 0xF
-#define BANK_MAX_VAL_MASK 0x1F
-#define BANKGRP_MAX_VAL_MASK 0x1F
-#define RANK_MAX_VAL_MASK 0x1F
+#define ECC_ERRCNT_UECNT_MASK GENMASK(31, 16)
+#define ECC_ERRCNT_CECNT_MASK GENMASK(15, 0)
+
+/* ECC Corrected Error register definitions */
+#define ECC_CEADDR0_RANK_MASK GENMASK(27, 24)
+#define ECC_CEADDR0_ROW_MASK GENMASK(17, 0)
+#define ECC_CEADDR1_BANKGRP_MASK GENMASK(25, 24)
+#define ECC_CEADDR1_BANK_MASK GENMASK(23, 16)
+#define ECC_CEADDR1_COL_MASK GENMASK(11, 0)
+
+/* DDR CRC/Parity register definitions */
+#define DDR_CRCPARCTL0_CLR_ALRT_ERRCNT BIT(2)
+#define DDR_CRCPARCTL0_CLR_ALRT_ERR BIT(1)
+#define DDR_CRCPARCTL0_EN_ALRT_IRQ BIT(0)
+#define DDR_CRCPARSTAT_ALRT_ERR BIT(16)
+#define DDR_CRCPARSTAT_ALRT_CNT_MASK GENMASK(15, 0)
+
+/* ECC Poison register definitions */
+#define ECC_POISON0_RANK_MASK GENMASK(27, 24)
+#define ECC_POISON0_COL_MASK GENMASK(11, 0)
+#define ECC_POISON1_BANKGRP_MASK GENMASK(29, 28)
+#define ECC_POISON1_BANK_MASK GENMASK(26, 24)
+#define ECC_POISON1_ROW_MASK GENMASK(17, 0)
+
+/* DDRC address mapping parameters */
+#define DDR_ADDRMAP_NREGS 12
+
+#define DDR_MAX_HIF_WIDTH 60
+#define DDR_MAX_ROW_WIDTH 18
+#define DDR_MAX_COL_WIDTH 14
+#define DDR_MAX_BANK_WIDTH 3
+#define DDR_MAX_BANKGRP_WIDTH 2
+#define DDR_MAX_RANK_WIDTH 2
+
+#define DDR_ADDRMAP_B0_M15 GENMASK(3, 0)
+#define DDR_ADDRMAP_B8_M15 GENMASK(11, 8)
+#define DDR_ADDRMAP_B16_M15 GENMASK(19, 16)
+#define DDR_ADDRMAP_B24_M15 GENMASK(27, 24)
+
+#define DDR_ADDRMAP_B0_M31 GENMASK(4, 0)
+#define DDR_ADDRMAP_B8_M31 GENMASK(12, 8)
+#define DDR_ADDRMAP_B16_M31 GENMASK(20, 16)
+#define DDR_ADDRMAP_B24_M31 GENMASK(28, 24)
+
+#define DDR_ADDRMAP_UNUSED ((u8)-1)
+#define DDR_ADDRMAP_MAX_15 DDR_ADDRMAP_B0_M15
+#define DDR_ADDRMAP_MAX_31 DDR_ADDRMAP_B0_M31
#define ROW_B0_BASE 6
#define ROW_B1_BASE 7
@@ -263,322 +245,803 @@
#define BANKGRP_B1_BASE 3
#define RANK_B0_BASE 6
+#define RANK_B1_BASE 7
+
+/* DDRC System Address parameters */
+#define DDR_MAX_NSAR 4
+#define DDR_MIN_SARSIZE SZ_256M
+
+/* ECC Scrubber registers definitions */
+#define ECC_SBRCTL_SCRUB_INTERVAL GENMASK(20, 8)
+#define ECC_SBRCTL_INTERVAL_STEP 512
+#define ECC_SBRCTL_INTERVAL_MIN 0
+#define ECC_SBRCTL_INTERVAL_SAFE 1
+#define ECC_SBRCTL_INTERVAL_MAX FIELD_MAX(ECC_SBRCTL_SCRUB_INTERVAL)
+#define ECC_SBRCTL_SCRUB_BURST GENMASK(6, 4)
+#define ECC_SBRCTL_SCRUB_MODE_WR BIT(2)
+#define ECC_SBRCTL_SCRUB_EN BIT(0)
+#define ECC_SBRSTAT_SCRUB_DONE BIT(1)
+#define ECC_SBRSTAT_SCRUB_BUSY BIT(0)
+
+/* ZynqMP DDR QOS Interrupt register definitions */
+#define ZYNQMP_DDR_QOS_UE_MASK BIT(2)
+#define ZYNQMP_DDR_QOS_CE_MASK BIT(1)
+
+/**
+ * enum snps_dq_width - SDRAM DQ bus width (ECC capable).
+ * @SNPS_DQ_32: 32-bit memory data width.
+ * @SNPS_DQ_64: 64-bit memory data width.
+ */
+enum snps_dq_width {
+ SNPS_DQ_32 = 2,
+ SNPS_DQ_64 = 3,
+};
+
+/**
+ * enum snps_dq_mode - SDRAM DQ bus mode.
+ * @SNPS_DQ_FULL: Full DQ bus width.
+ * @SNPS_DQ_HALF: Half DQ bus width.
+ * @SNPS_DQ_QRTR: Quarter DQ bus width.
+ */
+enum snps_dq_mode {
+ SNPS_DQ_FULL = 0,
+ SNPS_DQ_HALF = 1,
+ SNPS_DQ_QRTR = 2,
+};
+
+/**
+ * enum snps_burst_length - HIF/SDRAM burst transactions length.
+ * @SNPS_DDR_BL2: Burst length 2xSDRAM-words.
+ * @SNPS_DDR_BL4: Burst length 4xSDRAM-words.
+ * @SNPS_DDR_BL8: Burst length 8xSDRAM-words.
+ * @SNPS_DDR_BL16: Burst length 16xSDRAM-words.
+ */
+enum snps_burst_length {
+ SNPS_DDR_BL2 = 2,
+ SNPS_DDR_BL4 = 4,
+ SNPS_DDR_BL8 = 8,
+ SNPS_DDR_BL16 = 16,
+};
+
+/**
+ * enum snps_freq_ratio - HIF:SDRAM frequency ratio mode.
+ * @SNPS_FREQ_RATIO11: 1:1 frequency mode.
+ * @SNPS_FREQ_RATIO12: 1:2 frequency mode.
+ */
+enum snps_freq_ratio {
+ SNPS_FREQ_RATIO11 = 1,
+ SNPS_FREQ_RATIO12 = 2,
+};
+
+/**
+ * enum snps_ecc_mode - ECC mode.
+ * @SNPS_ECC_DISABLED: ECC is disabled/unavailable.
+ * @SNPS_ECC_SECDED: SEC/DED over 1 beat ECC (SideBand/Inline).
+ * @SNPS_ECC_ADVX4X8: Advanced ECC X4/X8 (SideBand).
+ */
+enum snps_ecc_mode {
+ SNPS_ECC_DISABLED = 0,
+ SNPS_ECC_SECDED = 4,
+ SNPS_ECC_ADVX4X8 = 5,
+};
+
+/**
+ * enum snps_ref_clk - DW uMCTL2 DDR controller clocks.
+ * @SNPS_CSR_CLK: CSR/APB interface clock.
+ * @SNPS_AXI_CLK: AXI (AHB) Port reference clock.
+ * @SNPS_CORE_CLK: DDR controller (including DFI) clock. SDRAM clock
+ * matches runs with this freq in 1:1 ratio mode and
+ * with twice of this freq in case of 1:2 ratio mode.
+ * @SNPS_SBR_CLK: Scrubber port reference clock (synchronous to
+ * the core clock).
+ * @SNPS_MAX_NCLK: Total number of clocks.
+ */
+enum snps_ref_clk {
+ SNPS_CSR_CLK,
+ SNPS_AXI_CLK,
+ SNPS_CORE_CLK,
+ SNPS_SBR_CLK,
+ SNPS_MAX_NCLK
+};
+
+/**
+ * struct snps_ddrc_info - DDR controller platform parameters.
+ * @caps: DDR controller capabilities.
+ * @sdram_mode: Current SDRAM mode selected.
+ * @dev_cfg: Current memory device config (if applicable).
+ * @dq_width: Memory data bus width (width of the DQ signals
+ * connected to SDRAM chips).
+ * @dq_mode: Proportion of the DQ bus utilized to access SDRAM.
+ * @sdram_burst_len: SDRAM burst transaction length.
+ * @hif_burst_len: HIF burst transaction length (Host Interface).
+ * @freq_ratio: HIF/SDRAM frequency ratio mode.
+ * @ecc_mode: ECC mode enabled for the DDR controller (SEC/DED, etc).
+ * @ranks: Number of ranks enabled to access DIMM (1, 2 or 4).
+ */
+struct snps_ddrc_info {
+ unsigned int caps;
+ enum mem_type sdram_mode;
+ enum dev_type dev_cfg;
+ enum snps_dq_width dq_width;
+ enum snps_dq_mode dq_mode;
+ enum snps_burst_length sdram_burst_len;
+ enum snps_burst_length hif_burst_len;
+ enum snps_freq_ratio freq_ratio;
+ enum snps_ecc_mode ecc_mode;
+ unsigned int ranks;
+};
+
+/**
+ * struct snps_sys_app_map - System/Application mapping table.
+ * @nsar: Number of SARs enabled on the controller (max 4).
+ * @minsize: Minimal block size (from 256MB to 32GB).
+ * @sar.base: SAR base address aligned to minsize.
+ * @sar.size: SAR size aligned to minsize.
+ * @sar.ofst: SAR address offset.
+ */
+struct snps_sys_app_map {
+ u8 nsar;
+ u64 minsize;
+ struct {
+ u64 base;
+ u64 size;
+ u64 ofst;
+ } sar[DDR_MAX_NSAR];
+};
+
+/**
+ * struct snps_hif_sdram_map - HIF/SDRAM mapping table.
+ * @row: HIF bit offsets used as row address bits.
+ * @col: HIF bit offsets used as column address bits.
+ * @bank: HIF bit offsets used as bank address bits.
+ * @bankgrp: HIF bit offsets used as bank group address bits.
+ * @rank: HIF bit offsets used as rank address bits.
+ *
+ * For example, row[0] = 6 means row bit #0 is encoded by the HIF
+ * address bit #6 and vice-versa.
+ */
+struct snps_hif_sdram_map {
+ u8 row[DDR_MAX_ROW_WIDTH];
+ u8 col[DDR_MAX_COL_WIDTH];
+ u8 bank[DDR_MAX_BANK_WIDTH];
+ u8 bankgrp[DDR_MAX_BANKGRP_WIDTH];
+ u8 rank[DDR_MAX_RANK_WIDTH];
+};
/**
- * struct ecc_error_info - ECC error log information.
+ * struct snps_sdram_addr - SDRAM address.
* @row: Row number.
* @col: Column number.
* @bank: Bank number.
- * @bitpos: Bit position.
- * @data: Data causing the error.
- * @bankgrpnr: Bank group number.
- * @blknr: Block number.
+ * @bankgrp: Bank group number.
+ * @rank: Rank number.
*/
-struct ecc_error_info {
- u32 row;
- u32 col;
- u32 bank;
- u32 bitpos;
- u32 data;
- u32 bankgrpnr;
- u32 blknr;
+struct snps_sdram_addr {
+ u16 row;
+ u16 col;
+ u8 bank;
+ u8 bankgrp;
+ u8 rank;
};
/**
- * struct synps_ecc_status - ECC status information to report.
- * @ce_cnt: Correctable error count.
- * @ue_cnt: Uncorrectable error count.
- * @ceinfo: Correctable error log information.
- * @ueinfo: Uncorrectable error log information.
+ * struct snps_ecc_error_info - ECC error log information.
+ * @ecnt: Number of detected errors.
+ * @syndrome: Error syndrome.
+ * @sdram: SDRAM address.
+ * @syndrome: Error syndrome.
+ * @bitpos: Bit position.
+ * @data: Data causing the error.
+ * @ecc: Data ECC.
*/
-struct synps_ecc_status {
- u32 ce_cnt;
- u32 ue_cnt;
- struct ecc_error_info ceinfo;
- struct ecc_error_info ueinfo;
+struct snps_ecc_error_info {
+ u16 ecnt;
+ struct snps_sdram_addr sdram;
+ u32 syndrome;
+ u32 bitpos;
+ u64 data;
+ u32 ecc;
};
/**
- * struct synps_edac_priv - DDR memory controller private instance data.
+ * struct snps_edac_priv - DDR memory controller private data.
+ * @info: DDR controller config info.
+ * @sys_app_map: Sys/App mapping table.
+ * @hif_sdram_map: HIF/SDRAM mapping table.
+ * @pdev: Platform device.
* @baseaddr: Base address of the DDR controller.
* @reglock: Concurrent CSRs access lock.
- * @message: Buffer for framing the event specific info.
- * @stat: ECC status information.
- * @p_data: Platform data.
- * @ce_cnt: Correctable Error count.
- * @ue_cnt: Uncorrectable Error count.
- * @poison_addr: Data poison address.
- * @row_shift: Bit shifts for row bit.
- * @col_shift: Bit shifts for column bit.
- * @bank_shift: Bit shifts for bank bit.
- * @bankgrp_shift: Bit shifts for bank group bit.
- * @rank_shift: Bit shifts for rank bit.
+ * @clks: Controller reference clocks.
+ * @msg_ecc_ce: Buffer for framing the ECC CE specific info.
+ * @msg_ecc_ue: Buffer for framing the ECC UE specific info.
+ * @msg_dfi_e: Buffer for framing the DFI E specific info.
*/
-struct synps_edac_priv {
+struct snps_edac_priv {
+ struct snps_ddrc_info info;
+ struct snps_sys_app_map sys_app_map;
+ struct snps_hif_sdram_map hif_sdram_map;
+ struct platform_device *pdev;
void __iomem *baseaddr;
spinlock_t reglock;
- char message[SYNPS_EDAC_MSG_SIZE];
- struct synps_ecc_status stat;
- const struct synps_platform_data *p_data;
- u32 ce_cnt;
- u32 ue_cnt;
-#ifdef CONFIG_EDAC_DEBUG
- ulong poison_addr;
- u32 row_shift[18];
- u32 col_shift[14];
- u32 bank_shift[3];
- u32 bankgrp_shift[2];
- u32 rank_shift[1];
-#endif
+ struct clk_bulk_data clks[SNPS_MAX_NCLK];
+ char msg_ecc_ce[SNPS_EDAC_MSG_SIZE];
+ char msg_ecc_ue[SNPS_EDAC_MSG_SIZE];
+ char msg_dfi_e[SNPS_EDAC_MSG_SIZE];
};
-enum synps_platform_type {
- ZYNQ,
- ZYNQMP,
- SYNPS,
-};
+/**
+ * snps_map_sys_to_app - Map System address to Application address.
+ * @priv: DDR memory controller private instance data.
+ * @sys: System address (source).
+ * @app: Application address (destination).
+ *
+ * System address space is used to define disjoint memory regions
+ * mapped then to the contiguous application memory space:
+ *
+ * System Address Space (SAR) <-> Application Address Space
+ * +------+ +------+
+ * | SAR0 |----------------------->| Reg0 |
+ * +------+ -offset +------+
+ * | ... | +----------->| Reg1 |
+ * +------+ | +------+
+ * | SAR1 |-----------+ | ... |
+ * +------+
+ * | ... |
+ *
+ * The translation is done by applying the corresponding SAR offset
+ * to the inbound system address. Note according to the hardware reference
+ * manual the same mapping is applied to the addresses up to the next
+ * SAR base address irrespective to the region size.
+ */
+static void snps_map_sys_to_app(struct snps_edac_priv *priv,
+ dma_addr_t sys, u64 *app)
+{
+ struct snps_sys_app_map *map = &priv->sys_app_map;
+ u64 ofst;
+ int i;
+
+ ofst = 0;
+ for (i = 0; i < map->nsar; i++) {
+ if (sys < map->sar[i].base)
+ break;
+
+ ofst = map->sar[i].ofst;
+ }
+
+ *app = sys - ofst;
+}
/**
- * struct synps_platform_data - synps platform data structure.
- * @platform: Identifies the target hardware platform
- * @get_error_info: Get EDAC error info.
- * @get_mtype: Get mtype.
- * @get_dtype: Get dtype.
- * @get_mem_info: Get EDAC memory info
- * @quirks: To differentiate IPs.
+ * snps_map_app_to_sys - Map Application address to System address.
+ * @priv: DDR memory controller private instance data.
+ * @app: Application address (source).
+ * @sys: System address (destination).
+ *
+ * Backward App-to-sys translation is easier because the application address
+ * space is contiguous. So we just need to add the offset corresponding
+ * to the region the passed address belongs to. Note the later offset is applied
+ * to all the addresses above the last available region.
*/
-struct synps_platform_data {
- enum synps_platform_type platform;
- int (*get_error_info)(struct synps_edac_priv *priv);
- enum mem_type (*get_mtype)(const void __iomem *base);
- enum dev_type (*get_dtype)(const void __iomem *base);
-#ifdef CONFIG_EDAC_DEBUG
- u64 (*get_mem_info)(struct synps_edac_priv *priv);
-#endif
- int quirks;
-};
+static void snps_map_app_to_sys(struct snps_edac_priv *priv,
+ u64 app, dma_addr_t *sys)
+{
+ struct snps_sys_app_map *map = &priv->sys_app_map;
+ u64 ofst, size;
+ int i;
+
+ ofst = 0;
+ for (i = 0, size = 0; i < map->nsar; i++) {
+ ofst = map->sar[i].ofst;
+ size += map->sar[i].size;
+ if (app < size)
+ break;
+ }
+
+ *sys = app + ofst;
+}
/**
- * zynq_get_error_info - Get the current ECC error info.
+ * snps_map_app_to_hif - Map Application address to HIF address.
* @priv: DDR memory controller private instance data.
+ * @app: Application address (source).
+ * @hif: HIF address (destination).
*
- * Return: one if there is no error, otherwise zero.
+ * HIF address is used to perform the DQ bus width aligned burst transactions.
+ * So in order to perform the Application-to-HIF address translation we just
+ * need to discard the SDRAM-word bits of the Application address.
*/
-static int zynq_get_error_info(struct synps_edac_priv *priv)
+static void snps_map_app_to_hif(struct snps_edac_priv *priv,
+ u64 app, u64 *hif)
{
- struct synps_ecc_status *p;
- u32 regval, clearval = 0;
- void __iomem *base;
+ *hif = app >> priv->info.dq_width;
+}
- base = priv->baseaddr;
- p = &priv->stat;
+/**
+ * snps_map_hif_to_app - Map HIF address to Application address.
+ * @priv: DDR memory controller private instance data.
+ * @hif: HIF address (source).
+ * @app: Application address (destination).
+ *
+ * Backward HIF-to-App translation is just the opposite DQ-width-based
+ * shift operation.
+ */
+static void snps_map_hif_to_app(struct snps_edac_priv *priv,
+ u64 hif, u64 *app)
+{
+ *app = hif << priv->info.dq_width;
+}
- regval = readl(base + STAT_OFST);
- if (!regval)
- return 1;
+/**
+ * snps_map_hif_to_sdram - Map HIF address to SDRAM address.
+ * @priv: DDR memory controller private instance data.
+ * @hif: HIF address (source).
+ * @sdram: SDRAM address (destination).
+ *
+ * HIF-SDRAM address mapping is configured with the ADDRMAPx registers, Based
+ * on the CSRs value the HIF address bits are mapped to the corresponding bits
+ * in the SDRAM rank/bank/column/row. If an SDRAM address bit is unused (there
+ * is no any HIF address bit corresponding to it) it will be set to zero. Using
+ * this fact we can freely set the output SDRAM address with zeros and walk
+ * over the set HIF address bits only. Similarly the unmapped HIF address bits
+ * are just ignored.
+ */
+static void snps_map_hif_to_sdram(struct snps_edac_priv *priv,
+ u64 hif, struct snps_sdram_addr *sdram)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
- p->ce_cnt = (regval & STAT_CECNT_MASK) >> STAT_CECNT_SHIFT;
- p->ue_cnt = regval & STAT_UECNT_MASK;
-
- regval = readl(base + CE_LOG_OFST);
- if (!(p->ce_cnt && (regval & LOG_VALID)))
- goto ue_err;
-
- p->ceinfo.bitpos = (regval & CE_LOG_BITPOS_MASK) >> CE_LOG_BITPOS_SHIFT;
- regval = readl(base + CE_ADDR_OFST);
- p->ceinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT;
- p->ceinfo.col = regval & ADDR_COL_MASK;
- p->ceinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT;
- p->ceinfo.data = readl(base + CE_DATA_31_0_OFST);
- edac_dbg(3, "CE bit position: %d data: %d\n", p->ceinfo.bitpos,
- p->ceinfo.data);
- clearval = ECC_CTRL_CLR_CE_ERR;
-
-ue_err:
- regval = readl(base + UE_LOG_OFST);
- if (!(p->ue_cnt && (regval & LOG_VALID)))
- goto out;
-
- regval = readl(base + UE_ADDR_OFST);
- p->ueinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT;
- p->ueinfo.col = regval & ADDR_COL_MASK;
- p->ueinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT;
- p->ueinfo.data = readl(base + UE_DATA_31_0_OFST);
- clearval |= ECC_CTRL_CLR_UE_ERR;
-
-out:
- writel(clearval, base + ECC_CTRL_OFST);
- writel(0x0, base + ECC_CTRL_OFST);
+ sdram->row = 0;
+ for (i = 0; i < DDR_MAX_ROW_WIDTH; i++) {
+ if (map->row[i] != DDR_ADDRMAP_UNUSED && hif & BIT(map->row[i]))
+ sdram->row |= BIT(i);
+ }
- return 0;
+ sdram->col = 0;
+ for (i = 0; i < DDR_MAX_COL_WIDTH; i++) {
+ if (map->col[i] != DDR_ADDRMAP_UNUSED && hif & BIT(map->col[i]))
+ sdram->col |= BIT(i);
+ }
+
+ sdram->bank = 0;
+ for (i = 0; i < DDR_MAX_BANK_WIDTH; i++) {
+ if (map->bank[i] != DDR_ADDRMAP_UNUSED && hif & BIT(map->bank[i]))
+ sdram->bank |= BIT(i);
+ }
+
+ sdram->bankgrp = 0;
+ for (i = 0; i < DDR_MAX_BANKGRP_WIDTH; i++) {
+ if (map->bankgrp[i] != DDR_ADDRMAP_UNUSED && hif & BIT(map->bankgrp[i]))
+ sdram->bankgrp |= BIT(i);
+ }
+
+ sdram->rank = 0;
+ for (i = 0; i < DDR_MAX_RANK_WIDTH; i++) {
+ if (map->rank[i] != DDR_ADDRMAP_UNUSED && hif & BIT(map->rank[i]))
+ sdram->rank |= BIT(i);
+ }
+}
+
+/**
+ * snps_map_sdram_to_hif - Map SDRAM address to HIF address.
+ * @priv: DDR memory controller private instance data.
+ * @sdram: SDRAM address (source).
+ * @hif: HIF address (destination).
+ *
+ * SDRAM-HIF address mapping is similar to the HIF-SDRAM mapping procedure, but
+ * we'll traverse each SDRAM rank/bank/column/row bit.
+ *
+ * Note the unmapped bits of the SDRAM address components will be just
+ * ignored. So make sure the source address is valid.
+ */
+static void snps_map_sdram_to_hif(struct snps_edac_priv *priv,
+ struct snps_sdram_addr *sdram, u64 *hif)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ unsigned long addr;
+ int i;
+
+ *hif = 0;
+
+ addr = sdram->row;
+ for_each_set_bit(i, &addr, DDR_MAX_ROW_WIDTH) {
+ if (map->row[i] != DDR_ADDRMAP_UNUSED)
+ *hif |= BIT_ULL(map->row[i]);
+ }
+
+ addr = sdram->col;
+ for_each_set_bit(i, &addr, DDR_MAX_COL_WIDTH) {
+ if (map->col[i] != DDR_ADDRMAP_UNUSED)
+ *hif |= BIT_ULL(map->col[i]);
+ }
+
+ addr = sdram->bank;
+ for_each_set_bit(i, &addr, DDR_MAX_BANK_WIDTH) {
+ if (map->bank[i] != DDR_ADDRMAP_UNUSED)
+ *hif |= BIT_ULL(map->bank[i]);
+ }
+
+ addr = sdram->bankgrp;
+ for_each_set_bit(i, &addr, DDR_MAX_BANKGRP_WIDTH) {
+ if (map->bankgrp[i] != DDR_ADDRMAP_UNUSED)
+ *hif |= BIT_ULL(map->bankgrp[i]);
+ }
+
+ addr = sdram->rank;
+ for_each_set_bit(i, &addr, DDR_MAX_RANK_WIDTH) {
+ if (map->rank[i] != DDR_ADDRMAP_UNUSED)
+ *hif |= BIT_ULL(map->rank[i]);
+ }
}
-#ifdef CONFIG_EDAC_DEBUG
/**
- * zynqmp_get_mem_info - Get the current memory info.
+ * snps_map_sys_to_sdram - Map System address to SDRAM address.
* @priv: DDR memory controller private instance data.
+ * @sys: System address (source).
+ * @sdram: SDRAM address (destination).
*
- * Return: host interface address.
+ * Perform a full mapping of the system address (detected on the controller
+ * ports) to the SDRAM address tuple row/column/bank/etc.
*/
-static u64 zynqmp_get_mem_info(struct synps_edac_priv *priv)
+static void snps_map_sys_to_sdram(struct snps_edac_priv *priv,
+ dma_addr_t sys, struct snps_sdram_addr *sdram)
{
- u64 hif_addr = 0, linear_addr;
+ u64 app, hif;
+
+ snps_map_sys_to_app(priv, sys, &app);
+
+ snps_map_app_to_hif(priv, app, &hif);
- linear_addr = priv->poison_addr;
- if (linear_addr >= SZ_32G)
- linear_addr = linear_addr - SZ_32G + SZ_2G;
- hif_addr = linear_addr >> 3;
- return hif_addr;
+ snps_map_hif_to_sdram(priv, hif, sdram);
}
-#endif
/**
- * zynqmp_get_error_info - Get the current ECC error info.
+ * snps_map_sdram_to_sys - Map SDRAM address to System address.
* @priv: DDR memory controller private instance data.
+ * @sdram: SDRAM address (source).
+ * @sys: System address (destination).
*
- * Return: one if there is no error otherwise returns zero.
+ * Perform a full mapping of the SDRAM address (row/column/bank/etc) to
+ * the System address specific to the controller system bus ports.
*/
-static int zynqmp_get_error_info(struct synps_edac_priv *priv)
+static void snps_map_sdram_to_sys(struct snps_edac_priv *priv,
+ struct snps_sdram_addr *sdram, dma_addr_t *sys)
{
- struct synps_ecc_status *p;
- u32 regval, clearval;
+ u64 app, hif;
+
+ snps_map_sdram_to_hif(priv, sdram, &hif);
+
+ snps_map_hif_to_app(priv, hif, &app);
+
+ snps_map_app_to_sys(priv, app, sys);
+}
+
+/**
+ * snps_get_bitpos - Get DQ-bus corrected bit position.
+ * @syndrome: Error syndrome.
+ * @dq_width: Controller DQ-bus width.
+ *
+ * Return: actual corrected DQ-bus bit position starting from 0.
+ */
+static inline u32 snps_get_bitpos(u32 syndrome, enum snps_dq_width dq_width)
+{
+ /* ecc[0] bit */
+ if (syndrome == 0)
+ return BITS_PER_BYTE << dq_width;
+
+ /* ecc[1:x] bit */
+ if (is_power_of_2(syndrome))
+ return (BITS_PER_BYTE << dq_width) + ilog2(syndrome) + 1;
+
+ /* data[0:y] bit */
+ return syndrome - ilog2(syndrome) - 2;
+}
+
+/**
+ * snps_ce_irq_handler - Corrected error interrupt handler.
+ * @irq: IRQ number.
+ * @dev_id: Device ID.
+ *
+ * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise.
+ */
+static irqreturn_t snps_ce_irq_handler(int irq, void *dev_id)
+{
+ struct mem_ctl_info *mci = dev_id;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ struct snps_ecc_error_info einfo;
unsigned long flags;
- void __iomem *base;
+ u32 qosval, regval;
+ dma_addr_t sys;
+
+ /* Make sure IRQ is caused by a corrected ECC error */
+ if (priv->info.caps & SNPS_CAP_ZYNQMP) {
+ qosval = readl(priv->baseaddr + ZYNQMP_DDR_QOS_IRQ_STAT_OFST);
+ if (!(qosval & ZYNQMP_DDR_QOS_CE_MASK))
+ return IRQ_NONE;
- base = priv->baseaddr;
- p = &priv->stat;
+ qosval &= ZYNQMP_DDR_QOS_CE_MASK;
+ }
- regval = readl(base + ECC_ERRCNT_OFST);
- p->ce_cnt = regval & ECC_ERRCNT_CECNT_MASK;
- p->ue_cnt = (regval & ECC_ERRCNT_UECNT_MASK) >> ECC_ERRCNT_UECNT_SHIFT;
- if (!p->ce_cnt)
- goto ue_err;
+ regval = readl(priv->baseaddr + ECC_STAT_OFST);
+ if (!FIELD_GET(ECC_STAT_CE_MASK, regval))
+ return IRQ_NONE;
- regval = readl(base + ECC_STAT_OFST);
- if (!regval)
- return 1;
+ /* Read error info like syndrome, bit position, SDRAM address, data */
+ einfo.syndrome = FIELD_GET(ECC_STAT_BITNUM_MASK, regval);
- p->ceinfo.bitpos = (regval & ECC_STAT_BITNUM_MASK);
-
- regval = readl(base + ECC_CEADDR0_OFST);
- p->ceinfo.row = (regval & ECC_CEADDR0_RW_MASK);
- regval = readl(base + ECC_CEADDR1_OFST);
- p->ceinfo.bank = (regval & ECC_CEADDR1_BNKNR_MASK) >>
- ECC_CEADDR1_BNKNR_SHIFT;
- p->ceinfo.bankgrpnr = (regval & ECC_CEADDR1_BNKGRP_MASK) >>
- ECC_CEADDR1_BNKGRP_SHIFT;
- p->ceinfo.blknr = (regval & ECC_CEADDR1_BLKNR_MASK);
- p->ceinfo.data = readl(base + ECC_CSYND0_OFST);
- edac_dbg(2, "ECCCSYN0: 0x%08X ECCCSYN1: 0x%08X ECCCSYN2: 0x%08X\n",
- readl(base + ECC_CSYND0_OFST), readl(base + ECC_CSYND1_OFST),
- readl(base + ECC_CSYND2_OFST));
-ue_err:
- if (!p->ue_cnt)
- goto out;
-
- regval = readl(base + ECC_UEADDR0_OFST);
- p->ueinfo.row = (regval & ECC_CEADDR0_RW_MASK);
- regval = readl(base + ECC_UEADDR1_OFST);
- p->ueinfo.bankgrpnr = (regval & ECC_CEADDR1_BNKGRP_MASK) >>
- ECC_CEADDR1_BNKGRP_SHIFT;
- p->ueinfo.bank = (regval & ECC_CEADDR1_BNKNR_MASK) >>
- ECC_CEADDR1_BNKNR_SHIFT;
- p->ueinfo.blknr = (regval & ECC_CEADDR1_BLKNR_MASK);
- p->ueinfo.data = readl(base + ECC_UESYND0_OFST);
-out:
+ einfo.bitpos = snps_get_bitpos(einfo.syndrome, priv->info.dq_width);
+
+ regval = readl(priv->baseaddr + ECC_ERRCNT_OFST);
+ einfo.ecnt = FIELD_GET(ECC_ERRCNT_CECNT_MASK, regval);
+
+ regval = readl(priv->baseaddr + ECC_CEADDR0_OFST);
+ einfo.sdram.rank = FIELD_GET(ECC_CEADDR0_RANK_MASK, regval);
+ einfo.sdram.row = FIELD_GET(ECC_CEADDR0_ROW_MASK, regval);
+
+ regval = readl(priv->baseaddr + ECC_CEADDR1_OFST);
+ einfo.sdram.bankgrp = FIELD_GET(ECC_CEADDR1_BANKGRP_MASK, regval);
+ einfo.sdram.bank = FIELD_GET(ECC_CEADDR1_BANK_MASK, regval);
+ einfo.sdram.col = FIELD_GET(ECC_CEADDR1_COL_MASK, regval);
+
+ einfo.data = readl(priv->baseaddr + ECC_CSYND0_OFST);
+ if (priv->info.dq_width == SNPS_DQ_64)
+ einfo.data |= (u64)readl(priv->baseaddr + ECC_CSYND1_OFST) << 32;
+
+ einfo.ecc = readl(priv->baseaddr + ECC_CSYND2_OFST);
+
+ /* Report the detected errors with the corresponding sys address */
+ snps_map_sdram_to_sys(priv, &einfo.sdram, &sys);
+
+ snprintf(priv->msg_ecc_ce, SNPS_EDAC_MSG_SIZE,
+ "Row %hu Col %hu Bank %hhu Bank Group %hhu Rank %hhu Bit %d Data 0x%08llx:0x%02x",
+ einfo.sdram.row, einfo.sdram.col, einfo.sdram.bank,
+ einfo.sdram.bankgrp, einfo.sdram.rank,
+ einfo.bitpos, einfo.data, einfo.ecc);
+
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, einfo.ecnt,
+ PHYS_PFN(sys), offset_in_page(sys),
+ einfo.syndrome, einfo.sdram.rank, 0, -1,
+ priv->msg_ecc_ce, "");
+
+ /* Make sure the CE IRQ status is cleared */
spin_lock_irqsave(&priv->reglock, flags);
- clearval = readl(base + ECC_CLR_OFST) |
- ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT |
- ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT;
- writel(clearval, base + ECC_CLR_OFST);
+ regval = readl(priv->baseaddr + ECC_CLR_OFST) |
+ ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT;
+ writel(regval, priv->baseaddr + ECC_CLR_OFST);
spin_unlock_irqrestore(&priv->reglock, flags);
- return 0;
+ if (priv->info.caps & SNPS_CAP_ZYNQMP)
+ writel(qosval, priv->baseaddr + ZYNQMP_DDR_QOS_IRQ_STAT_OFST);
+
+ return IRQ_HANDLED;
}
/**
- * handle_error - Handle Correctable and Uncorrectable errors.
- * @mci: EDAC memory controller instance.
- * @p: Synopsys ECC status structure.
+ * snps_ue_irq_handler - Uncorrected error interrupt handler.
+ * @irq: IRQ number.
+ * @dev_id: Device ID.
*
- * Handles ECC correctable and uncorrectable errors.
+ * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise.
*/
-static void handle_error(struct mem_ctl_info *mci, struct synps_ecc_status *p)
+static irqreturn_t snps_ue_irq_handler(int irq, void *dev_id)
{
- struct synps_edac_priv *priv = mci->pvt_info;
- struct ecc_error_info *pinf;
-
- if (p->ce_cnt) {
- pinf = &p->ceinfo;
- if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) {
- snprintf(priv->message, SYNPS_EDAC_MSG_SIZE,
- "DDR ECC error type:%s Row %d Bank %d BankGroup Number %d Block Number %d Bit Position: %d Data: 0x%08x",
- "CE", pinf->row, pinf->bank,
- pinf->bankgrpnr, pinf->blknr,
- pinf->bitpos, pinf->data);
- } else {
- snprintf(priv->message, SYNPS_EDAC_MSG_SIZE,
- "DDR ECC error type:%s Row %d Bank %d Col %d Bit Position: %d Data: 0x%08x",
- "CE", pinf->row, pinf->bank, pinf->col,
- pinf->bitpos, pinf->data);
- }
+ struct mem_ctl_info *mci = dev_id;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ struct snps_ecc_error_info einfo;
+ unsigned long flags;
+ u32 qosval, regval;
+ dma_addr_t sys;
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
- p->ce_cnt, 0, 0, 0, 0, 0, -1,
- priv->message, "");
+ /* Make sure IRQ is caused by an uncorrected ECC error */
+ if (priv->info.caps & SNPS_CAP_ZYNQMP) {
+ qosval = readl(priv->baseaddr + ZYNQMP_DDR_QOS_IRQ_STAT_OFST);
+ if (!(regval & ZYNQMP_DDR_QOS_UE_MASK))
+ return IRQ_NONE;
+
+ qosval &= ZYNQMP_DDR_QOS_UE_MASK;
}
- if (p->ue_cnt) {
- pinf = &p->ueinfo;
- if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) {
- snprintf(priv->message, SYNPS_EDAC_MSG_SIZE,
- "DDR ECC error type :%s Row %d Bank %d BankGroup Number %d Block Number %d",
- "UE", pinf->row, pinf->bank,
- pinf->bankgrpnr, pinf->blknr);
- } else {
- snprintf(priv->message, SYNPS_EDAC_MSG_SIZE,
- "DDR ECC error type :%s Row %d Bank %d Col %d ",
- "UE", pinf->row, pinf->bank, pinf->col);
- }
+ regval = readl(priv->baseaddr + ECC_STAT_OFST);
+ if (!FIELD_GET(ECC_STAT_UE_MASK, regval))
+ return IRQ_NONE;
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
- p->ue_cnt, 0, 0, 0, 0, 0, -1,
- priv->message, "");
- }
+ /* Read error info like SDRAM address, data and syndrome */
+ regval = readl(priv->baseaddr + ECC_ERRCNT_OFST);
+ einfo.ecnt = FIELD_GET(ECC_ERRCNT_UECNT_MASK, regval);
+
+ regval = readl(priv->baseaddr + ECC_UEADDR0_OFST);
+ einfo.sdram.rank = FIELD_GET(ECC_CEADDR0_RANK_MASK, regval);
+ einfo.sdram.row = FIELD_GET(ECC_CEADDR0_ROW_MASK, regval);
+
+ regval = readl(priv->baseaddr + ECC_UEADDR1_OFST);
+ einfo.sdram.bankgrp = FIELD_GET(ECC_CEADDR1_BANKGRP_MASK, regval);
+ einfo.sdram.bank = FIELD_GET(ECC_CEADDR1_BANK_MASK, regval);
+ einfo.sdram.col = FIELD_GET(ECC_CEADDR1_COL_MASK, regval);
+
+ einfo.data = readl(priv->baseaddr + ECC_UESYND0_OFST);
+ if (priv->info.dq_width == SNPS_DQ_64)
+ einfo.data |= (u64)readl(priv->baseaddr + ECC_UESYND1_OFST) << 32;
+
+ einfo.ecc = readl(priv->baseaddr + ECC_UESYND2_OFST);
+
+ /* Report the detected errors with the corresponding sys address */
+ snps_map_sdram_to_sys(priv, &einfo.sdram, &sys);
+
+ snprintf(priv->msg_ecc_ue, SNPS_EDAC_MSG_SIZE,
+ "Row %hu Col %hu Bank %hhu Bank Group %hhu Rank %hhu Data 0x%08llx:0x%02x",
+ einfo.sdram.row, einfo.sdram.col, einfo.sdram.bank,
+ einfo.sdram.bankgrp, einfo.sdram.rank,
+ einfo.data, einfo.ecc);
+
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, einfo.ecnt,
+ PHYS_PFN(sys), offset_in_page(sys),
+ 0, einfo.sdram.rank, 0, -1,
+ priv->msg_ecc_ue, "");
+
+ /* Make sure the UE IRQ status is cleared */
+ spin_lock_irqsave(&priv->reglock, flags);
- memset(p, 0, sizeof(*p));
+ regval = readl(priv->baseaddr + ECC_CLR_OFST) |
+ ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT;
+ writel(regval, priv->baseaddr + ECC_CLR_OFST);
+
+ spin_unlock_irqrestore(&priv->reglock, flags);
+
+ if (priv->info.caps & SNPS_CAP_ZYNQMP)
+ writel(qosval, priv->baseaddr + ZYNQMP_DDR_QOS_IRQ_STAT_OFST);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * snps_dfi_irq_handler - DFI CRC/Parity error interrupt handler.
+ * @irq: IRQ number.
+ * @dev_id: Device ID.
+ *
+ * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise.
+ */
+static irqreturn_t snps_dfi_irq_handler(int irq, void *dev_id)
+{
+ struct mem_ctl_info *mci = dev_id;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ unsigned long flags;
+ u32 regval;
+ u16 ecnt;
+
+ /* Make sure IRQ is caused by an DFI alert error */
+ regval = readl(priv->baseaddr + DDR_CRCPARSTAT_OFST);
+ if (!(regval & DDR_CRCPARSTAT_ALRT_ERR))
+ return IRQ_NONE;
+
+ /* Just a number of CRC/Parity errors is available */
+ ecnt = FIELD_GET(DDR_CRCPARSTAT_ALRT_CNT_MASK, regval);
+
+ /* Report the detected errors with just the custom message */
+ snprintf(priv->msg_dfi_e, SNPS_EDAC_MSG_SIZE,
+ "DFI CRC/Parity error detected on dfi_alert_n");
+
+ edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, ecnt,
+ 0, 0, 0, 0, 0, -1, priv->msg_dfi_e, "");
+
+ /* Make sure the DFI alert IRQ status is cleared */
+ spin_lock_irqsave(&priv->reglock, flags);
+
+ regval = readl(priv->baseaddr + DDR_CRCPARCTL0_OFST) |
+ DDR_CRCPARCTL0_CLR_ALRT_ERR | DDR_CRCPARCTL0_CLR_ALRT_ERRCNT;
+ writel(regval, priv->baseaddr + DDR_CRCPARCTL0_OFST);
+
+ spin_unlock_irqrestore(&priv->reglock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * snps_sbr_irq_handler - Scrubber Done interrupt handler.
+ * @irq: IRQ number.
+ * @dev_id: Device ID.
+ *
+ * It just checks whether the IRQ has been caused by the Scrubber Done event
+ * and disables the back-to-back scrubbing by falling back to the smallest
+ * delay between the Scrubber read commands.
+ *
+ * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise.
+ */
+static irqreturn_t snps_sbr_irq_handler(int irq, void *dev_id)
+{
+ struct mem_ctl_info *mci = dev_id;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ unsigned long flags;
+ u32 regval, en;
+
+ /* Make sure IRQ is caused by the Scrubber Done event */
+ regval = readl(priv->baseaddr + ECC_SBRSTAT_OFST);
+ if (!(regval & ECC_SBRSTAT_SCRUB_DONE))
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&priv->reglock, flags);
+
+ regval = readl(priv->baseaddr + ECC_SBRCTL_OFST);
+ en = regval & ECC_SBRCTL_SCRUB_EN;
+ writel(regval & ~en, priv->baseaddr + ECC_SBRCTL_OFST);
+
+ regval = FIELD_PREP(ECC_SBRCTL_SCRUB_INTERVAL, ECC_SBRCTL_INTERVAL_SAFE);
+ writel(regval, priv->baseaddr + ECC_SBRCTL_OFST);
+
+ writel(regval | en, priv->baseaddr + ECC_SBRCTL_OFST);
+
+ spin_unlock_irqrestore(&priv->reglock, flags);
+
+ edac_mc_printk(mci, KERN_WARNING, "Back-to-back scrubbing disabled\n");
+
+ return IRQ_HANDLED;
}
-static void enable_intr(struct synps_edac_priv *priv)
+/**
+ * snps_com_irq_handler - Interrupt IRQ signal handler.
+ * @irq: IRQ number.
+ * @dev_id: Device ID.
+ *
+ * Return: IRQ_NONE, if interrupts not set or IRQ_HANDLED otherwise.
+ */
+static irqreturn_t snps_com_irq_handler(int irq, void *dev_id)
+{
+ struct mem_ctl_info *mci = dev_id;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ irqreturn_t rc = IRQ_NONE;
+
+ rc |= snps_ce_irq_handler(irq, dev_id);
+
+ rc |= snps_ue_irq_handler(irq, dev_id);
+
+ rc |= snps_dfi_irq_handler(irq, dev_id);
+
+ if (priv->info.caps & SNPS_CAP_ECC_SCRUBBER)
+ rc |= snps_sbr_irq_handler(irq, dev_id);
+
+ return rc;
+}
+
+static void snps_enable_irq(struct snps_edac_priv *priv)
{
unsigned long flags;
/* Enable UE/CE Interrupts */
- if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)) {
- writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK,
- priv->baseaddr + DDR_QOS_IRQ_EN_OFST);
+ if (priv->info.caps & SNPS_CAP_ZYNQMP) {
+ writel(ZYNQMP_DDR_QOS_UE_MASK | ZYNQMP_DDR_QOS_CE_MASK,
+ priv->baseaddr + ZYNQMP_DDR_QOS_IRQ_EN_OFST);
return;
}
spin_lock_irqsave(&priv->reglock, flags);
- writel(DDR_UE_MASK | DDR_CE_MASK,
+ /*
+ * IRQs Enable/Disable flags have been available since v3.10a.
+ * This is noop for the older controllers.
+ */
+ writel(ECC_CTRL_EN_CE_IRQ | ECC_CTRL_EN_UE_IRQ,
priv->baseaddr + ECC_CLR_OFST);
+ /*
+ * CRC/Parity interrupts control has been available since v2.10a.
+ * This is noop for the older controllers.
+ */
+ writel(DDR_CRCPARCTL0_EN_ALRT_IRQ,
+ priv->baseaddr + DDR_CRCPARCTL0_OFST);
+
spin_unlock_irqrestore(&priv->reglock, flags);
}
-static void disable_intr(struct synps_edac_priv *priv)
+static void snps_disable_irq(struct snps_edac_priv *priv)
{
unsigned long flags;
/* Disable UE/CE Interrupts */
- if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)) {
- writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK,
- priv->baseaddr + DDR_QOS_IRQ_DB_OFST);
+ if (priv->info.caps & SNPS_CAP_ZYNQMP) {
+ writel(ZYNQMP_DDR_QOS_UE_MASK | ZYNQMP_DDR_QOS_CE_MASK,
+ priv->baseaddr + ZYNQMP_DDR_QOS_IRQ_DB_OFST);
return;
}
@@ -586,779 +1049,1388 @@ static void disable_intr(struct synps_edac_priv *priv)
spin_lock_irqsave(&priv->reglock, flags);
writel(0, priv->baseaddr + ECC_CLR_OFST);
+ writel(0, priv->baseaddr + DDR_CRCPARCTL0_OFST);
spin_unlock_irqrestore(&priv->reglock, flags);
}
/**
- * intr_handler - Interrupt Handler for ECC interrupts.
- * @irq: IRQ number.
- * @dev_id: Device ID.
+ * snps_get_sdram_bw - Get SDRAM bandwidth.
+ * @priv: DDR memory controller private instance data.
*
- * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise.
+ * The SDRAM interface bandwidth is calculated based on the DDRC Core clock rate
+ * and the DW uMCTL2 IP-core parameters like DQ-bus width and mode and
+ * Core/SDRAM clocks frequency ratio. Note it returns the theoretical bandwidth
+ * which in reality is hardly possible to reach.
+ *
+ * Return: SDRAM bandwidth or zero if no Core clock specified.
*/
-static irqreturn_t intr_handler(int irq, void *dev_id)
+static u64 snps_get_sdram_bw(struct snps_edac_priv *priv)
{
- const struct synps_platform_data *p_data;
- struct mem_ctl_info *mci = dev_id;
- struct synps_edac_priv *priv;
- int status, regval;
+ unsigned long rate;
- priv = mci->pvt_info;
- p_data = priv->p_data;
+ /*
+ * Depending on the ratio mode the SDRAM clock either matches the Core
+ * clock or runs with the twice its frequency.
+ */
+ rate = clk_get_rate(priv->clks[SNPS_CORE_CLK].clk);
+ rate *= priv->info.freq_ratio;
/*
- * v3.0 of the controller has the ce/ue bits cleared automatically,
- * so this condition does not apply.
+ * Scale up by 2 since it's DDR (Double Data Rate) and subtract the
+ * DQ-mode since in non-Full mode only a part of the DQ-bus is utilised
+ * on each SDRAM clock edge.
*/
- if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)) {
- regval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST);
- regval &= (DDR_QOSCE_MASK | DDR_QOSUE_MASK);
- if (!(regval & ECC_CE_UE_INTR_MASK))
- return IRQ_NONE;
+ return (2U << (priv->info.dq_width - priv->info.dq_mode)) * (u64)rate;
+}
+
+/**
+ * snps_get_scrub_bw - Get Scrubber bandwidth.
+ * @priv: DDR memory controller private instance data.
+ * @interval: Scrub interval.
+ *
+ * DW uMCTL2 DDRC Scrubber performs periodical progressive burst reads (RMW if
+ * ECC CE is detected) commands from the whole memory space. The read commands
+ * can be delayed by means of the SBRCTL.scrub_interval field. The Scrubber
+ * cycles look as follows:
+ *
+ * |-HIF-burst-read-|-------delay-------|-HIF-burst-read-|------- etc
+ *
+ * Tb = Bl*[DQ]/Bw[RAM], Td = 512*interval/Fc - periods of the HIF-burst-read
+ * and delay stages, where
+ * Bl - HIF burst length, [DQ] - Full DQ-bus width, Bw[RAM] - SDRAM bandwidth,
+ * Fc - Core clock frequency (Scrubber and Core clocks are synchronous).
+ *
+ * After some simple calculations the expressions above can be used to get the
+ * next Scrubber bandwidth formulae:
+ *
+ * Bw[Sbr] = Bw[RAM] / (1 + F * interval), where
+ * F = 2 * 512 * Fr * Fc * [DQ]e - interval scale factor with
+ * Fr - HIF/SDRAM clock frequency ratio (1 or 2), [DQ]e - DQ-bus width mode.
+ *
+ * Return: Scrubber bandwidth or zero if no Core clock specified.
+ */
+static u64 snps_get_scrub_bw(struct snps_edac_priv *priv, u32 interval)
+{
+ unsigned long fac;
+ u64 bw_ram;
+
+ fac = (2 * ECC_SBRCTL_INTERVAL_STEP * priv->info.freq_ratio) /
+ (priv->info.hif_burst_len * (1UL << priv->info.dq_mode));
+
+ bw_ram = snps_get_sdram_bw(priv);
+
+ return div_u64(bw_ram, 1 + fac * interval);
+}
+
+/**
+ * snps_get_scrub_interval - Get Scrubber delay interval.
+ * @priv: DDR memory controller private instance data.
+ * @bw: Scrubber bandwidth.
+ *
+ * Similarly to the Scrubber bandwidth the interval formulae can be inferred
+ * from the same expressions:
+ *
+ * interval = (Bw[RAM] - Bw[Sbr]) / (F * Bw[Sbr])
+ *
+ * Return: Scrubber delay interval or zero if no Core clock specified.
+ */
+static u32 snps_get_scrub_interval(struct snps_edac_priv *priv, u32 bw)
+{
+ unsigned long fac;
+ u64 bw_ram;
+
+ fac = (2 * priv->info.freq_ratio * ECC_SBRCTL_INTERVAL_STEP) /
+ (priv->info.hif_burst_len * (1UL << priv->info.dq_mode));
+
+ bw_ram = snps_get_sdram_bw(priv);
+
+ /* Divide twice so not to cause the integer overflow in (fac * bw) */
+ return div_u64(div_u64(bw_ram - bw, bw), fac);
+}
+
+/**
+ * snps_set_sdram_scrub_rate - Set the Scrubber bandwidth.
+ * @mci: EDAC memory controller instance.
+ * @bw: Bandwidth.
+ *
+ * It calculates the delay between the Scrubber read commands based on the
+ * specified bandwidth and the Core clock rate. If the Core clock is unavailable
+ * the passed bandwidth will be directly used as the interval value.
+ *
+ * Note the method warns about the back-to-back scrubbing since it may
+ * significantly degrade the system performance. This mode is supposed to be
+ * used for a single SDRAM scrubbing pass only. So it will be turned off in the
+ * Scrubber Done IRQ handler.
+ *
+ * Return: Actually set bandwidth (interval-based approximated bandwidth if the
+ * Core clock is unavailable) or zero if the Scrubber was disabled.
+ */
+static int snps_set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 bw)
+{
+ struct snps_edac_priv *priv = mci->pvt_info;
+ u32 regval, interval;
+ unsigned long flags;
+ u64 bw_min, bw_max;
+
+ /* Don't bother with the calculations just disable and return. */
+ if (!bw) {
+ spin_lock_irqsave(&priv->reglock, flags);
+
+ regval = readl(priv->baseaddr + ECC_SBRCTL_OFST);
+ regval &= ~ECC_SBRCTL_SCRUB_EN;
+ writel(regval, priv->baseaddr + ECC_SBRCTL_OFST);
+
+ spin_unlock_irqrestore(&priv->reglock, flags);
+
+ return 0;
}
- status = p_data->get_error_info(priv);
- if (status)
- return IRQ_NONE;
+ /* If no Core clock specified fallback to the direct interval setup. */
+ bw_max = snps_get_scrub_bw(priv, ECC_SBRCTL_INTERVAL_MIN);
+ if (bw_max) {
+ bw_min = snps_get_scrub_bw(priv, ECC_SBRCTL_INTERVAL_MAX);
+ bw = clamp_t(u64, bw, bw_min, bw_max);
- priv->ce_cnt += priv->stat.ce_cnt;
- priv->ue_cnt += priv->stat.ue_cnt;
- handle_error(mci, &priv->stat);
+ interval = snps_get_scrub_interval(priv, bw);
+ } else {
+ bw = clamp_val(bw, ECC_SBRCTL_INTERVAL_MIN, ECC_SBRCTL_INTERVAL_MAX);
- edac_dbg(3, "Total error count CE %d UE %d\n",
- priv->ce_cnt, priv->ue_cnt);
- /* v3.0 of the controller does not have this register */
- if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR))
- writel(regval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST);
+ interval = ECC_SBRCTL_INTERVAL_MAX - bw;
+ }
- return IRQ_HANDLED;
+ /*
+ * SBRCTL.scrub_en bitfield must be accessed separately from the other
+ * CSR bitfields. It means the flag must be set/cleared with no updates
+ * to the rest of the fields.
+ */
+ spin_lock_irqsave(&priv->reglock, flags);
+
+ regval = FIELD_PREP(ECC_SBRCTL_SCRUB_INTERVAL, interval);
+ writel(regval, priv->baseaddr + ECC_SBRCTL_OFST);
+
+ writel(regval | ECC_SBRCTL_SCRUB_EN, priv->baseaddr + ECC_SBRCTL_OFST);
+
+ spin_unlock_irqrestore(&priv->reglock, flags);
+
+ if (!interval)
+ edac_mc_printk(mci, KERN_WARNING, "Back-to-back scrubbing enabled\n");
+
+ if (!bw_max)
+ return interval ? bw : INT_MAX;
+
+ return snps_get_scrub_bw(priv, interval);
}
/**
- * check_errors - Check controller for ECC errors.
+ * snps_get_sdram_scrub_rate - Get the Scrubber bandwidth.
* @mci: EDAC memory controller instance.
*
- * Check and post ECC errors. Called by the polling thread.
+ * Return: Scrubber bandwidth (interval-based approximated bandwidth if the
+ * Core clock is unavailable) or zero if the Scrubber was disabled.
*/
-static void check_errors(struct mem_ctl_info *mci)
+static int snps_get_sdram_scrub_rate(struct mem_ctl_info *mci)
{
- const struct synps_platform_data *p_data;
- struct synps_edac_priv *priv;
- int status;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ u32 regval;
+ u64 bw;
- priv = mci->pvt_info;
- p_data = priv->p_data;
+ regval = readl(priv->baseaddr + ECC_SBRCTL_OFST);
+ if (!(regval & ECC_SBRCTL_SCRUB_EN))
+ return 0;
- status = p_data->get_error_info(priv);
- if (status)
- return;
+ regval = FIELD_GET(ECC_SBRCTL_SCRUB_INTERVAL, regval);
+
+ bw = snps_get_scrub_bw(priv, regval);
+ if (!bw)
+ return regval ? ECC_SBRCTL_INTERVAL_MAX - regval : INT_MAX;
+
+ return bw;
+}
+
+/**
+ * snps_create_data - Create private data.
+ * @pdev: platform device.
+ *
+ * Return: Private data instance or negative errno.
+ */
+static struct snps_edac_priv *snps_create_data(struct platform_device *pdev)
+{
+ struct snps_edac_priv *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return ERR_PTR(-ENOMEM);
+
+ priv->baseaddr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->baseaddr))
+ return ERR_CAST(priv->baseaddr);
+
+ priv->pdev = pdev;
+ spin_lock_init(&priv->reglock);
+
+ return priv;
+}
+
+/**
+ * snps_get_res - Get platform device resources.
+ * @priv: DDR memory controller private instance data.
+ *
+ * It's supposed to request all the controller resources available for the
+ * particular platform and enable all the required for the driver normal
+ * work. Note only the CSR and Scrubber clocks are supposed to be switched
+ * on/off by the driver.
+ *
+ * Return: negative errno if failed to get the resources, otherwise - zero.
+ */
+static int snps_get_res(struct snps_edac_priv *priv)
+{
+ const char * const ids[] = {
+ [SNPS_CSR_CLK] = "pclk",
+ [SNPS_AXI_CLK] = "aclk",
+ [SNPS_CORE_CLK] = "core",
+ [SNPS_SBR_CLK] = "sbr",
+ };
+ int i, rc;
+
+ for (i = 0; i < SNPS_MAX_NCLK; i++)
+ priv->clks[i].id = ids[i];
+
+ rc = devm_clk_bulk_get_optional(&priv->pdev->dev, SNPS_MAX_NCLK,
+ priv->clks);
+ if (rc) {
+ edac_printk(KERN_INFO, EDAC_MC, "Failed to get ref clocks\n");
+ return rc;
+ }
+
+ /*
+ * Don't touch the Core and AXI clocks since they are critical for the
+ * stable system functioning and are supposed to have been enabled
+ * anyway.
+ */
+ rc = clk_prepare_enable(priv->clks[SNPS_CSR_CLK].clk);
+ if (rc) {
+ edac_printk(KERN_INFO, EDAC_MC, "Couldn't enable CSR clock\n");
+ return rc;
+ }
+
+ rc = clk_prepare_enable(priv->clks[SNPS_SBR_CLK].clk);
+ if (rc) {
+ edac_printk(KERN_INFO, EDAC_MC, "Couldn't enable Scrubber clock\n");
+ goto err_disable_pclk;
+ }
+
+ return 0;
+
+err_disable_pclk:
+ clk_disable_unprepare(priv->clks[SNPS_CSR_CLK].clk);
+
+ return rc;
+}
+
+/**
+ * snps_put_res - Put platform device resources.
+ * @priv: DDR memory controller private instance data.
+ */
+static void snps_put_res(struct snps_edac_priv *priv)
+{
+ clk_disable_unprepare(priv->clks[SNPS_SBR_CLK].clk);
+
+ clk_disable_unprepare(priv->clks[SNPS_CSR_CLK].clk);
+}
+
+/*
+ * zynqmp_init_plat - ZynqMP-specific platform initialization.
+ * @priv: DDR memory controller private data.
+ *
+ * Return: always zero.
+ */
+static int zynqmp_init_plat(struct snps_edac_priv *priv)
+{
+ priv->info.caps |= SNPS_CAP_ZYNQMP;
+ priv->info.dq_width = SNPS_DQ_64;
- priv->ce_cnt += priv->stat.ce_cnt;
- priv->ue_cnt += priv->stat.ue_cnt;
- handle_error(mci, &priv->stat);
+ /*
+ * It's known that the Zynq UltraScale+ MPSoC DDR has a disjoint memory
+ * from 2GB to 32GB. It means that the first region has 0 base address
+ * and a size of 2GB. It can be utilized to calculate the block size
+ * based on the value written to the SARSIZE0 register.
+ *
+ * TODO convert this to a constant once the size is found.
+ */
+ priv->sys_app_map.minsize =
+ SZ_2G / (readl(priv->baseaddr + DDR_SARSIZE0_OFST) + 1);
- edac_dbg(3, "Total error count CE %d UE %d\n",
- priv->ce_cnt, priv->ue_cnt);
+ return 0;
+}
+
+/*
+ * bt1_init_plat - Baikal-T1-specific platform initialization.
+ * @priv: DDR memory controller private data.
+ *
+ * Return: always zero.
+ */
+static int bt1_init_plat(struct snps_edac_priv *priv)
+{
+ priv->info.dq_width = SNPS_DQ_32;
+ priv->info.hif_burst_len = SNPS_DDR_BL8;
+ priv->sys_app_map.minsize = SZ_256M;
+
+ return 0;
}
/**
- * zynq_get_dtype - Return the controller memory width.
- * @base: DDR memory controller base address.
+ * snps_get_dtype - Return the DDR memory chips type.
+ * @mstr: Master CSR value.
*
- * Get the EDAC device type width appropriate for the current controller
+ * Get the attached DDR chips type based on the current controller
* configuration.
*
- * Return: a device type width enumeration.
+ * Return: type of the memory DRAM chips.
*/
-static enum dev_type zynq_get_dtype(const void __iomem *base)
+static inline enum dev_type snps_get_dtype(u32 mstr)
{
- enum dev_type dt;
- u32 width;
-
- width = readl(base + CTRL_OFST);
- width = (width & CTRL_BW_MASK) >> CTRL_BW_SHIFT;
-
- switch (width) {
- case DDRCTL_WDTH_16:
- dt = DEV_X2;
- break;
- case DDRCTL_WDTH_32:
- dt = DEV_X4;
- break;
- default:
- dt = DEV_UNKNOWN;
+ if (!(mstr & DDR_MSTR_MEM_DDR4))
+ return DEV_UNKNOWN;
+
+ switch (FIELD_GET(DDR_MSTR_DEV_CFG_MASK, mstr)) {
+ case DDR_MSTR_DEV_X4:
+ return DEV_X4;
+ case DDR_MSTR_DEV_X8:
+ return DEV_X8;
+ case DDR_MSTR_DEV_X16:
+ return DEV_X16;
+ case DDR_MSTR_DEV_X32:
+ return DEV_X32;
}
- return dt;
+ return DEV_UNKNOWN;
}
/**
- * zynqmp_get_dtype - Return the controller memory width.
- * @base: DDR memory controller base address.
+ * snps_get_mtype - Returns controller memory type.
+ * @mstr: Master CSR value.
*
- * Get the EDAC device type width appropriate for the current controller
+ * Get the EDAC memory type appropriate for the current controller
* configuration.
*
- * Return: a device type width enumeration.
+ * Return: a memory type enumeration.
*/
-static enum dev_type zynqmp_get_dtype(const void __iomem *base)
+static inline enum mem_type snps_get_mtype(u32 mstr)
{
- enum dev_type dt;
- u32 width;
-
- width = readl(base + CTRL_OFST);
- width = (width & ECC_CTRL_BUSWIDTH_MASK) >> ECC_CTRL_BUSWIDTH_SHIFT;
- switch (width) {
- case DDRCTL_EWDTH_16:
- dt = DEV_X2;
- break;
- case DDRCTL_EWDTH_32:
- dt = DEV_X4;
- break;
- case DDRCTL_EWDTH_64:
- dt = DEV_X8;
- break;
- default:
- dt = DEV_UNKNOWN;
+ switch (FIELD_GET(DDR_MSTR_MEM_MASK, mstr)) {
+ case DDR_MSTR_MEM_DDR2:
+ return MEM_DDR2;
+ case DDR_MSTR_MEM_DDR3:
+ return MEM_DDR3;
+ case DDR_MSTR_MEM_LPDDR:
+ return MEM_LPDDR;
+ case DDR_MSTR_MEM_LPDDR2:
+ return MEM_LPDDR2;
+ case DDR_MSTR_MEM_LPDDR3:
+ return MEM_LPDDR3;
+ case DDR_MSTR_MEM_DDR4:
+ return MEM_DDR4;
+ case DDR_MSTR_MEM_LPDDR4:
+ return MEM_LPDDR4;
}
- return dt;
+ return MEM_RESERVED;
}
-static bool get_ecc_state(struct synps_edac_priv *priv)
+/**
+ * snps_get_ddrc_info - Get the DDR controller config data.
+ * @priv: DDR memory controller private data.
+ *
+ * Return: negative errno if no ECC detected, otherwise - zero.
+ */
+static int snps_get_ddrc_info(struct snps_edac_priv *priv)
{
- u32 ecctype, clearval;
- enum dev_type dt;
-
- if (priv->p_data->platform == ZYNQ) {
- dt = zynq_get_dtype(priv->baseaddr);
- if (dt == DEV_UNKNOWN)
- return false;
-
- ecctype = readl(priv->baseaddr + SCRUB_OFST) & SCRUB_MODE_MASK;
- if (ecctype == SCRUB_MODE_SECDED && dt == DEV_X2) {
- clearval = ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_UE_ERR;
- writel(clearval, priv->baseaddr + ECC_CTRL_OFST);
- writel(0x0, priv->baseaddr + ECC_CTRL_OFST);
- return true;
- }
+ int (*init_plat)(struct snps_edac_priv *priv);
+ u32 regval;
+
+ /* Before getting the DDRC parameters make sure ECC is enabled */
+ regval = readl(priv->baseaddr + ECC_CFG0_OFST);
+
+ priv->info.ecc_mode = FIELD_GET(ECC_CFG0_MODE_MASK, regval);
+ if (priv->info.ecc_mode != SNPS_ECC_SECDED) {
+ edac_printk(KERN_INFO, EDAC_MC, "SEC/DED ECC not enabled\n");
+ return -ENODEV;
+ }
+
+ /* Assume HW-src scrub is always available if it isn't disabled */
+ if (!(regval & ECC_CFG0_DIS_SCRUB))
+ priv->info.caps |= SNPS_CAP_ECC_SCRUB;
+
+ /* Auto-detect the scrubber by writing to the SBRWDATA0 CSR */
+ regval = readl(priv->baseaddr + ECC_SBRWDATA0_OFST);
+ writel(~regval, priv->baseaddr + ECC_SBRWDATA0_OFST);
+ if (regval != readl(priv->baseaddr + ECC_SBRWDATA0_OFST)) {
+ priv->info.caps |= SNPS_CAP_ECC_SCRUBBER;
+ writel(regval, priv->baseaddr + ECC_SBRWDATA0_OFST);
+ }
+
+ /* Auto-detect the basic HIF/SDRAM bus parameters */
+ regval = readl(priv->baseaddr + DDR_MSTR_OFST);
+
+ priv->info.sdram_mode = snps_get_mtype(regval);
+ priv->info.dev_cfg = snps_get_dtype(regval);
+
+ priv->info.dq_mode = FIELD_GET(DDR_MSTR_BUSWIDTH_MASK, regval);
+
+ /*
+ * Assume HIF burst length matches the SDRAM burst length since it's
+ * not auto-detectable
+ */
+ priv->info.sdram_burst_len = FIELD_GET(DDR_MSTR_BURST_RDWR, regval) << 1;
+ priv->info.hif_burst_len = priv->info.sdram_burst_len;
+
+ /* Retrieve the current HIF/SDRAM frequency ratio: 1:1 vs 1:2 */
+ priv->info.freq_ratio = !(regval & DDR_MSTR_FREQ_RATIO11) + 1;
+
+ /* Activated ranks field: set bit corresponds to populated rank */
+ priv->info.ranks = FIELD_GET(DDR_MSTR_ACT_RANKS_MASK, regval);
+ priv->info.ranks = hweight_long(priv->info.ranks);
+
+ /* Auto-detect the DQ bus width by using the ECC-poison pattern CSR */
+ writel(0, priv->baseaddr + DDR_SWCTL);
+
+ /*
+ * If poison pattern [32:64] is changeable then DQ is 64-bit wide.
+ * Note the feature has been available since IP-core v2.51a.
+ */
+ regval = readl(priv->baseaddr + ECC_POISONPAT1_OFST);
+ writel(~regval, priv->baseaddr + ECC_POISONPAT1_OFST);
+ if (regval != readl(priv->baseaddr + ECC_POISONPAT1_OFST)) {
+ priv->info.dq_width = SNPS_DQ_64;
+ writel(regval, priv->baseaddr + ECC_POISONPAT1_OFST);
+ } else {
+ priv->info.dq_width = SNPS_DQ_32;
+ }
+
+ writel(1, priv->baseaddr + DDR_SWCTL);
+
+ /* Clear out the ECC counters to start from scratch */
+ regval = readl(priv->baseaddr + ECC_CLR_OFST) |
+ ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT |
+ ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT;
+ writel(regval, priv->baseaddr + ECC_CLR_OFST);
+
+ /* Apply platform setups after all the configs auto-detection */
+ init_plat = device_get_match_data(&priv->pdev->dev);
+
+ return init_plat ? init_plat(priv) : 0;
+}
+
+/**
+ * snps_get_sys_app_map - Get System/Application address map.
+ * @priv: DDR memory controller private instance data.
+ * @sarregs: Array with SAR registers value.
+ *
+ * System address regions are defined by the SARBASEn and SARSIZEn registers.
+ * Controller reference manual requires the base addresses and sizes creating
+ * a set of ascending non-overlapped regions in order to have a linear
+ * application address space. Doing otherwise causes unpredictable results.
+ */
+static void snps_get_sys_app_map(struct snps_edac_priv *priv, u32 *sarregs)
+{
+ struct snps_sys_app_map *map = &priv->sys_app_map;
+ int i, ofst;
+
+ /*
+ * SARs are supposed to be initialized in the ascending non-overlapped
+ * order: base[i - 1] < base[i] < etc. If that rule is broken for a SAR
+ * it's considered as no more SARs have been enabled, so the detection
+ * procedure will halt. Having the very first SAR with zero base
+ * address only makes sense if there is a consequent SAR.
+ */
+ for (i = 0, ofst = 0; i < DDR_MAX_NSAR; i++) {
+ map->sar[i].base = sarregs[2 * i] * map->minsize;
+ if (map->sar[i].base)
+ map->nsar = i + 1;
+ else if (i && map->sar[i].base <= map->sar[i - 1].base)
+ break;
+
+ map->sar[i].size = (sarregs[2 * i + 1] + 1) * map->minsize;
+ map->sar[i].ofst = map->sar[i].base - ofst;
+ ofst += map->sar[i].size;
+ }
+
+ /*
+ * SAR block size isn't auto-detectable. If one isn't specified for the
+ * platform there is a good chance to have invalid mapping of the
+ * detected SARs. So proceed with 1:1 mapping then.
+ */
+ if (!map->minsize && map->nsar) {
+ edac_printk(KERN_WARNING, EDAC_MC,
+ "No block size specified. Discard SARs mapping\n");
+ map->nsar = 0;
+ }
+}
+
+/**
+ * snps_get_hif_row_map - Get HIF/SDRAM-row address map.
+ * @priv: DDR memory controller private instance data.
+ * @addrmap: Array with ADDRMAP registers value.
+ *
+ * SDRAM-row address is defined by the fields in the ADDRMAP[5-7,9-11]
+ * registers. Those fields value indicate the HIF address bits used to encode
+ * the DDR row address.
+ */
+static void snps_get_hif_row_map(struct snps_edac_priv *priv, u32 *addrmap)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ u8 map_row_b2_10;
+ int i;
+
+ for (i = 0; i < DDR_MAX_ROW_WIDTH; i++)
+ map->row[i] = DDR_ADDRMAP_UNUSED;
+
+ map->row[0] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[5]) + ROW_B0_BASE;
+ map->row[1] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[5]) + ROW_B1_BASE;
+
+ map_row_b2_10 = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[5]);
+ if (map_row_b2_10 != DDR_ADDRMAP_MAX_15) {
+ for (i = 2; i < 11; i++)
+ map->row[i] = map_row_b2_10 + i + ROW_B0_BASE;
} else {
- dt = zynqmp_get_dtype(priv->baseaddr);
- if (dt == DEV_UNKNOWN)
- return false;
-
- ecctype = readl(priv->baseaddr + ECC_CFG0_OFST) & SCRUB_MODE_MASK;
- if (ecctype == SCRUB_MODE_SECDED &&
- (dt == DEV_X2 || dt == DEV_X4 || dt == DEV_X8)) {
- clearval = readl(priv->baseaddr + ECC_CLR_OFST) |
- ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT |
- ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT;
- writel(clearval, priv->baseaddr + ECC_CLR_OFST);
- return true;
+ map->row[2] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[9]) + ROW_B2_BASE;
+ map->row[3] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[9]) + ROW_B3_BASE;
+ map->row[4] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[9]) + ROW_B4_BASE;
+ map->row[5] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[9]) + ROW_B5_BASE;
+ map->row[6] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[10]) + ROW_B6_BASE;
+ map->row[7] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[10]) + ROW_B7_BASE;
+ map->row[8] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[10]) + ROW_B8_BASE;
+ map->row[9] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[10]) + ROW_B9_BASE;
+ map->row[10] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[11]) + ROW_B10_BASE;
+ }
+
+ map->row[11] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[5]);
+ map->row[11] = map->row[11] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[11] + ROW_B11_BASE;
+
+ map->row[12] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[6]);
+ map->row[12] = map->row[12] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[12] + ROW_B12_BASE;
+
+ map->row[13] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[6]);
+ map->row[13] = map->row[13] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[13] + ROW_B13_BASE;
+
+ map->row[14] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[6]);
+ map->row[14] = map->row[14] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[14] + ROW_B14_BASE;
+
+ map->row[15] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[6]);
+ map->row[15] = map->row[15] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[15] + ROW_B15_BASE;
+
+ if (priv->info.sdram_mode == MEM_DDR4 || priv->info.sdram_mode == MEM_LPDDR4) {
+ map->row[16] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[7]);
+ map->row[16] = map->row[16] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[16] + ROW_B16_BASE;
+
+ map->row[17] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[7]);
+ map->row[17] = map->row[17] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[17] + ROW_B17_BASE;
+ }
+}
+
+/**
+ * snps_get_hif_col_map - Get HIF/SDRAM-column address map.
+ * @priv: DDR memory controller private instance data.
+ * @addrmap: Array with ADDRMAP registers value.
+ *
+ * SDRAM-column address is defined by the fields in the ADDRMAP[2-4]
+ * registers. Those fields value indicate the HIF address bits used to encode
+ * the DDR row address.
+ */
+static void snps_get_hif_col_map(struct snps_edac_priv *priv, u32 *addrmap)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
+
+ for (i = 0; i < DDR_MAX_COL_WIDTH; i++)
+ map->col[i] = DDR_ADDRMAP_UNUSED;
+
+ map->col[0] = 0;
+ map->col[1] = 1;
+ map->col[2] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[2]) + COL_B2_BASE;
+ map->col[3] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[2]) + COL_B3_BASE;
+
+ map->col[4] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[2]);
+ map->col[4] = map->col[4] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[4] + COL_B4_BASE;
+
+ map->col[5] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[2]);
+ map->col[5] = map->col[5] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[5] + COL_B5_BASE;
+
+ map->col[6] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[3]);
+ map->col[6] = map->col[6] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[6] + COL_B6_BASE;
+
+ map->col[7] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[3]);
+ map->col[7] = map->col[7] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[7] + COL_B7_BASE;
+
+ map->col[8] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[3]);
+ map->col[8] = map->col[8] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[8] + COL_B8_BASE;
+
+ map->col[9] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[3]);
+ map->col[9] = map->col[9] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[9] + COL_B9_BASE;
+
+ map->col[10] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[4]);
+ map->col[10] = map->col[10] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[10] + COL_B10_BASE;
+
+ map->col[11] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[4]);
+ map->col[11] = map->col[11] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[11] + COL_B11_BASE;
+
+ /*
+ * In case of the non-Full DQ bus mode the lowest columns are
+ * unmapped and used by the controller to read the full DQ word
+ * in multiple cycles (col[0] for the Half bus mode, col[0:1] for
+ * the Quarter bus mode).
+ */
+ if (priv->info.dq_mode) {
+ for (i = 11 + priv->info.dq_mode; i >= priv->info.dq_mode; i--) {
+ map->col[i] = map->col[i - priv->info.dq_mode];
+ map->col[i - priv->info.dq_mode] = DDR_ADDRMAP_UNUSED;
}
}
- return false;
+ /*
+ * Per JEDEC DDR2/3/4/mDDR specification, column address bit 10 is
+ * reserved for indicating auto-precharge, and hence no source
+ * address bit can be mapped to col[10].
+ */
+ if (priv->info.sdram_mode == MEM_LPDDR || priv->info.sdram_mode == MEM_DDR2 ||
+ priv->info.sdram_mode == MEM_DDR3 || priv->info.sdram_mode == MEM_DDR4) {
+ for (i = 12 + priv->info.dq_mode; i > 10; i--) {
+ map->col[i] = map->col[i - 1];
+ map->col[i - 1] = DDR_ADDRMAP_UNUSED;
+ }
+ }
+
+ /*
+ * Per JEDEC specification, column address bit 12 is reserved
+ * for the Burst-chop status, so no source address bit mapping
+ * for col[12] either.
+ */
+ map->col[13] = map->col[12];
+ map->col[12] = DDR_ADDRMAP_UNUSED;
}
/**
- * get_memsize - Read the size of the attached memory device.
+ * snps_get_hif_bank_map - Get HIF/SDRAM-bank address map.
+ * @priv: DDR memory controller private instance data.
+ * @addrmap: Array with ADDRMAP registers value.
*
- * Return: the memory size in bytes.
+ * SDRAM-bank address is defined by the fields in the ADDRMAP[1]
+ * register. Those fields value indicate the HIF address bits used to encode
+ * the DDR bank address.
*/
-static u32 get_memsize(void)
+static void snps_get_hif_bank_map(struct snps_edac_priv *priv, u32 *addrmap)
{
- struct sysinfo inf;
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
- si_meminfo(&inf);
+ for (i = 0; i < DDR_MAX_BANK_WIDTH; i++)
+ map->bank[i] = DDR_ADDRMAP_UNUSED;
- return inf.totalram * inf.mem_unit;
+ map->bank[0] = FIELD_GET(DDR_ADDRMAP_B0_M31, addrmap[1]) + BANK_B0_BASE;
+ map->bank[1] = FIELD_GET(DDR_ADDRMAP_B8_M31, addrmap[1]) + BANK_B1_BASE;
+
+ map->bank[2] = FIELD_GET(DDR_ADDRMAP_B16_M31, addrmap[1]);
+ map->bank[2] = map->bank[2] == DDR_ADDRMAP_MAX_31 ?
+ DDR_ADDRMAP_UNUSED : map->bank[2] + BANK_B2_BASE;
}
/**
- * zynq_get_mtype - Return the controller memory type.
- * @base: Synopsys ECC status structure.
+ * snps_get_hif_bankgrp_map - Get HIF/SDRAM-bank group address map.
+ * @priv: DDR memory controller private instance data.
+ * @addrmap: Array with ADDRMAP registers value.
*
- * Get the EDAC memory type appropriate for the current controller
- * configuration.
+ * SDRAM-bank group address is defined by the fields in the ADDRMAP[8]
+ * register. Those fields value indicate the HIF address bits used to encode
+ * the DDR bank group address.
+ */
+static void snps_get_hif_bankgrp_map(struct snps_edac_priv *priv, u32 *addrmap)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
+
+ for (i = 0; i < DDR_MAX_BANKGRP_WIDTH; i++)
+ map->bankgrp[i] = DDR_ADDRMAP_UNUSED;
+
+ /* Bank group signals are available on the DDR4 memory only */
+ if (priv->info.sdram_mode != MEM_DDR4)
+ return;
+
+ map->bankgrp[0] = FIELD_GET(DDR_ADDRMAP_B0_M31, addrmap[8]) + BANKGRP_B0_BASE;
+
+ map->bankgrp[1] = FIELD_GET(DDR_ADDRMAP_B8_M31, addrmap[8]);
+ map->bankgrp[1] = map->bankgrp[1] == DDR_ADDRMAP_MAX_31 ?
+ DDR_ADDRMAP_UNUSED : map->bankgrp[1] + BANKGRP_B1_BASE;
+}
+
+/**
+ * snps_get_hif_rank_map - Get HIF/SDRAM-rank address map.
+ * @priv: DDR memory controller private instance data.
+ * @addrmap: Array with ADDRMAP registers value.
*
- * Return: a memory type enumeration.
+ * SDRAM-rank address is defined by the fields in the ADDRMAP[0]
+ * register. Those fields value indicate the HIF address bits used to encode
+ * the DDR rank address.
*/
-static enum mem_type zynq_get_mtype(const void __iomem *base)
+static void snps_get_hif_rank_map(struct snps_edac_priv *priv, u32 *addrmap)
{
- enum mem_type mt;
- u32 memtype;
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
- memtype = readl(base + T_ZQ_OFST);
+ for (i = 0; i < DDR_MAX_RANK_WIDTH; i++)
+ map->rank[i] = DDR_ADDRMAP_UNUSED;
- if (memtype & T_ZQ_DDRMODE_MASK)
- mt = MEM_DDR3;
- else
- mt = MEM_DDR2;
+ if (priv->info.ranks > 1) {
+ map->rank[0] = FIELD_GET(DDR_ADDRMAP_B0_M31, addrmap[0]);
+ map->rank[0] = map->rank[0] == DDR_ADDRMAP_MAX_31 ?
+ DDR_ADDRMAP_UNUSED : map->rank[0] + RANK_B0_BASE;
+ }
- return mt;
+ if (priv->info.ranks > 2) {
+ map->rank[1] = FIELD_GET(DDR_ADDRMAP_B8_M31, addrmap[0]);
+ map->rank[1] = map->rank[1] == DDR_ADDRMAP_MAX_31 ?
+ DDR_ADDRMAP_UNUSED : map->rank[1] + RANK_B1_BASE;
+ }
}
/**
- * zynqmp_get_mtype - Returns controller memory type.
- * @base: Synopsys ECC status structure.
+ * snps_get_addr_map - Get HIF/SDRAM/etc address map from CSRs.
+ * @priv: DDR memory controller private instance data.
*
- * Get the EDAC memory type appropriate for the current controller
- * configuration.
+ * Parse the controller registers content creating the addresses mapping tables.
+ * They will be used for the erroneous and poison addresses encode/decode.
+ */
+static void snps_get_addr_map(struct snps_edac_priv *priv)
+{
+ u32 regval[MAX(DDR_ADDRMAP_NREGS, 2 * DDR_MAX_NSAR)];
+ int i;
+
+ for (i = 0; i < 2 * DDR_MAX_NSAR; i++)
+ regval[i] = readl(priv->baseaddr + DDR_SARBASE0_OFST + i * 4);
+
+ snps_get_sys_app_map(priv, regval);
+
+ for (i = 0; i < DDR_ADDRMAP_NREGS; i++)
+ regval[i] = readl(priv->baseaddr + DDR_ADDRMAP0_OFST + i * 4);
+
+ snps_get_hif_row_map(priv, regval);
+
+ snps_get_hif_col_map(priv, regval);
+
+ snps_get_hif_bank_map(priv, regval);
+
+ snps_get_hif_bankgrp_map(priv, regval);
+
+ snps_get_hif_rank_map(priv, regval);
+}
+
+/**
+ * snps_get_sdram_size - Calculate SDRAM size.
+ * @priv: DDR memory controller private data.
*
- * Return: a memory type enumeration.
+ * The total size of the attached memory is calculated based on the HIF/SDRAM
+ * mapping table. It can be done since the hardware reference manual demands
+ * that none two SDRAM bits should be mapped to the same HIF bit and that the
+ * unused SDRAM address bits mapping must be disabled.
+ *
+ * Return: the memory size in bytes.
*/
-static enum mem_type zynqmp_get_mtype(const void __iomem *base)
+static u64 snps_get_sdram_size(struct snps_edac_priv *priv)
{
- enum mem_type mt;
- u32 memtype;
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ u64 size = 0;
+ int i;
- memtype = readl(base + CTRL_OFST);
+ for (i = 0; i < DDR_MAX_ROW_WIDTH; i++) {
+ if (map->row[i] != DDR_ADDRMAP_UNUSED)
+ size++;
+ }
- if ((memtype & MEM_TYPE_DDR3) || (memtype & MEM_TYPE_LPDDR3))
- mt = MEM_DDR3;
- else if (memtype & MEM_TYPE_DDR2)
- mt = MEM_RDDR2;
- else if ((memtype & MEM_TYPE_LPDDR4) || (memtype & MEM_TYPE_DDR4))
- mt = MEM_DDR4;
- else
- mt = MEM_EMPTY;
+ for (i = 0; i < DDR_MAX_COL_WIDTH; i++) {
+ if (map->col[i] != DDR_ADDRMAP_UNUSED)
+ size++;
+ }
+
+ for (i = 0; i < DDR_MAX_BANK_WIDTH; i++) {
+ if (map->bank[i] != DDR_ADDRMAP_UNUSED)
+ size++;
+ }
- return mt;
+ for (i = 0; i < DDR_MAX_BANKGRP_WIDTH; i++) {
+ if (map->bankgrp[i] != DDR_ADDRMAP_UNUSED)
+ size++;
+ }
+
+ /* Skip the ranks since the multi-rankness is determined by layer[0] */
+
+ return 1ULL << (size + priv->info.dq_width);
}
/**
- * init_csrows - Initialize the csrow data.
+ * snps_init_csrows - Initialize the csrow data.
* @mci: EDAC memory controller instance.
*
* Initialize the chip select rows associated with the EDAC memory
* controller instance.
*/
-static void init_csrows(struct mem_ctl_info *mci)
+static void snps_init_csrows(struct mem_ctl_info *mci)
{
- struct synps_edac_priv *priv = mci->pvt_info;
- const struct synps_platform_data *p_data;
+ struct snps_edac_priv *priv = mci->pvt_info;
struct csrow_info *csi;
struct dimm_info *dimm;
- u32 size, row;
+ u32 row, width;
+ u64 size;
int j;
- p_data = priv->p_data;
+ /* Actual SDRAM-word width for which ECC is calculated */
+ width = 1U << (priv->info.dq_width - priv->info.dq_mode);
for (row = 0; row < mci->nr_csrows; row++) {
csi = mci->csrows[row];
- size = get_memsize();
+ size = snps_get_sdram_size(priv);
for (j = 0; j < csi->nr_channels; j++) {
dimm = csi->channels[j]->dimm;
dimm->edac_mode = EDAC_SECDED;
- dimm->mtype = p_data->get_mtype(priv->baseaddr);
- dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels;
- dimm->grain = SYNPS_EDAC_ERR_GRAIN;
- dimm->dtype = p_data->get_dtype(priv->baseaddr);
+ dimm->mtype = priv->info.sdram_mode;
+ dimm->nr_pages = PHYS_PFN(size) / csi->nr_channels;
+ dimm->grain = width;
+ dimm->dtype = priv->info.dev_cfg;
}
}
}
/**
- * mc_init - Initialize one driver instance.
- * @mci: EDAC memory controller instance.
- * @pdev: platform device.
+ * snps_mc_create - Create and initialize MC instance.
+ * @priv: DDR memory controller private data.
*
- * Perform initialization of the EDAC memory controller instance and
- * related driver-private data associated with the memory controller the
- * instance is bound to.
+ * Allocate the EDAC memory controller descriptor and initialize it
+ * using the private data info.
+ *
+ * Return: MC data instance or negative errno.
*/
-static void mc_init(struct mem_ctl_info *mci, struct platform_device *pdev)
+static struct mem_ctl_info *snps_mc_create(struct snps_edac_priv *priv)
{
- struct synps_edac_priv *priv;
+ struct edac_mc_layer layers[2];
+ struct mem_ctl_info *mci;
+
+ layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+ layers[0].size = priv->info.ranks;
+ layers[0].is_virt_csrow = true;
+ layers[1].type = EDAC_MC_LAYER_CHANNEL;
+ layers[1].size = SNPS_EDAC_NR_CHANS;
+ layers[1].is_virt_csrow = false;
+
+ mci = edac_mc_alloc(EDAC_AUTO_MC_NUM, ARRAY_SIZE(layers), layers, 0);
+ if (!mci) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Failed memory allocation for mc instance\n");
+ return ERR_PTR(-ENOMEM);
+ }
- mci->pdev = &pdev->dev;
- priv = mci->pvt_info;
- platform_set_drvdata(pdev, mci);
+ mci->pvt_info = priv;
+ mci->pdev = &priv->pdev->dev;
+ platform_set_drvdata(priv->pdev, mci);
/* Initialize controller capabilities and configuration */
- mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR2;
- mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
- mci->scrub_cap = SCRUB_HW_SRC;
- mci->scrub_mode = SCRUB_NONE;
-
- mci->edac_cap = EDAC_FLAG_SECDED;
- mci->ctl_name = "synps_ddr_controller";
- mci->dev_name = SYNPS_EDAC_MOD_STRING;
- mci->mod_name = SYNPS_EDAC_MOD_VER;
-
- if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) {
- edac_op_state = EDAC_OPSTATE_INT;
+ mci->mtype_cap = MEM_FLAG_LPDDR | MEM_FLAG_DDR2 | MEM_FLAG_LPDDR2 |
+ MEM_FLAG_DDR3 | MEM_FLAG_LPDDR3 |
+ MEM_FLAG_DDR4 | MEM_FLAG_LPDDR4;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED | EDAC_FLAG_PARITY;
+ mci->edac_cap = mci->edac_ctl_cap;
+
+ if (priv->info.caps & SNPS_CAP_ECC_SCRUB) {
+ mci->scrub_mode = SCRUB_HW_SRC;
+ mci->scrub_cap = SCRUB_FLAG_HW_SRC;
} else {
- edac_op_state = EDAC_OPSTATE_POLL;
- mci->edac_check = check_errors;
+ mci->scrub_mode = SCRUB_SW_SRC;
+ mci->scrub_cap = SCRUB_FLAG_SW_SRC;
+ }
+
+ if (priv->info.caps & SNPS_CAP_ECC_SCRUBBER) {
+ mci->scrub_cap |= SCRUB_FLAG_HW_PROG | SCRUB_FLAG_HW_TUN;
+ mci->set_sdram_scrub_rate = snps_set_sdram_scrub_rate;
+ mci->get_sdram_scrub_rate = snps_get_sdram_scrub_rate;
}
+ mci->ctl_name = "snps_umctl2_ddrc";
+ mci->dev_name = SNPS_EDAC_MOD_STRING;
+ mci->mod_name = SNPS_EDAC_MOD_VER;
+
+ edac_op_state = EDAC_OPSTATE_INT;
+
mci->ctl_page_to_phys = NULL;
- init_csrows(mci);
+ snps_init_csrows(mci);
+
+ return mci;
+}
+
+/**
+ * snps_mc_free - Free MC instance.
+ * @mci: EDAC memory controller instance.
+ *
+ * Just revert what was done in the framework of the snps_mc_create().
+ *
+ * Return: MC data instance or negative errno.
+ */
+static void snps_mc_free(struct mem_ctl_info *mci)
+{
+ struct snps_edac_priv *priv = mci->pvt_info;
+
+ platform_set_drvdata(priv->pdev, NULL);
+
+ edac_mc_free(mci);
}
-static int setup_irq(struct mem_ctl_info *mci,
- struct platform_device *pdev)
+/**
+ * snps_request_ind_irq - Request individual DDRC IRQs.
+ * @mci: EDAC memory controller instance.
+ *
+ * Return: 0 if the IRQs were successfully requested, 1 if the individual IRQs
+ * are unavailable, otherwise negative errno.
+ */
+static int snps_request_ind_irq(struct mem_ctl_info *mci)
{
- struct synps_edac_priv *priv = mci->pvt_info;
- int ret, irq;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ struct device *dev = &priv->pdev->dev;
+ int rc, irq;
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- edac_printk(KERN_ERR, EDAC_MC,
- "No IRQ %d in DT\n", irq);
+ irq = platform_get_irq_byname_optional(priv->pdev, "ecc_ce");
+ if (irq == -ENXIO)
+ return 1;
+ if (irq < 0)
return irq;
- }
- ret = devm_request_irq(&pdev->dev, irq, intr_handler,
- 0, dev_name(&pdev->dev), mci);
- if (ret < 0) {
- edac_printk(KERN_ERR, EDAC_MC, "Failed to request IRQ\n");
- return ret;
+ rc = devm_request_irq(dev, irq, snps_ce_irq_handler, 0, "ecc_ce", mci);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_MC, "Failed to request ECC CE IRQ\n");
+ return rc;
}
- enable_intr(priv);
+ irq = platform_get_irq_byname(priv->pdev, "ecc_ue");
+ if (irq < 0)
+ return irq;
- return 0;
-}
+ rc = devm_request_irq(dev, irq, snps_ue_irq_handler, 0, "ecc_ue", mci);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_MC, "Failed to request ECC UE IRQ\n");
+ return rc;
+ }
-static const struct synps_platform_data zynq_edac_def = {
- .platform = ZYNQ,
- .get_error_info = zynq_get_error_info,
- .get_mtype = zynq_get_mtype,
- .get_dtype = zynq_get_dtype,
- .quirks = 0,
-};
+ irq = platform_get_irq_byname_optional(priv->pdev, "dfi_e");
+ if (irq > 0) {
+ rc = devm_request_irq(dev, irq, snps_dfi_irq_handler, 0, "dfi_e", mci);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_MC, "Failed to request DFI IRQ\n");
+ return rc;
+ }
+ }
-static const struct synps_platform_data zynqmp_edac_def = {
- .platform = ZYNQMP,
- .get_error_info = zynqmp_get_error_info,
- .get_mtype = zynqmp_get_mtype,
- .get_dtype = zynqmp_get_dtype,
-#ifdef CONFIG_EDAC_DEBUG
- .get_mem_info = zynqmp_get_mem_info,
-#endif
- .quirks = (DDR_ECC_INTR_SUPPORT
-#ifdef CONFIG_EDAC_DEBUG
- | DDR_ECC_DATA_POISON_SUPPORT
-#endif
- ),
-};
+ irq = platform_get_irq_byname_optional(priv->pdev, "ecc_sbr");
+ if (irq > 0) {
+ rc = devm_request_irq(dev, irq, snps_sbr_irq_handler, 0, "ecc_sbr", mci);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_MC, "Failed to request Sbr IRQ\n");
+ return rc;
+ }
+ }
-static const struct synps_platform_data synopsys_edac_def = {
- .platform = SYNPS,
- .get_error_info = zynqmp_get_error_info,
- .get_mtype = zynqmp_get_mtype,
- .get_dtype = zynqmp_get_dtype,
- .quirks = (DDR_ECC_INTR_SUPPORT | DDR_ECC_INTR_SELF_CLEAR
-#ifdef CONFIG_EDAC_DEBUG
- | DDR_ECC_DATA_POISON_SUPPORT
-#endif
- ),
-};
+ return 0;
+}
+/**
+ * snps_request_com_irq - Request common DDRC IRQ.
+ * @mci: EDAC memory controller instance.
+ *
+ * It first attempts to get the named IRQ. If failed the method fallbacks
+ * to first available one.
+ *
+ * Return: 0 if the IRQ was successfully requested otherwise negative errno.
+ */
+static int snps_request_com_irq(struct mem_ctl_info *mci)
+{
+ struct snps_edac_priv *priv = mci->pvt_info;
+ struct device *dev = &priv->pdev->dev;
+ int rc, irq;
-static const struct of_device_id synps_edac_match[] = {
- {
- .compatible = "xlnx,zynq-ddrc-a05",
- .data = (void *)&zynq_edac_def
- },
- {
- .compatible = "xlnx,zynqmp-ddrc-2.40a",
- .data = (void *)&zynqmp_edac_def
- },
- {
- .compatible = "snps,ddrc-3.80a",
- .data = (void *)&synopsys_edac_def
- },
- {
- /* end of table */
+ irq = platform_get_irq_byname_optional(priv->pdev, "ecc");
+ if (irq < 0) {
+ irq = platform_get_irq(priv->pdev, 0);
+ if (irq < 0)
+ return irq;
}
-};
-MODULE_DEVICE_TABLE(of, synps_edac_match);
+ rc = devm_request_irq(dev, irq, snps_com_irq_handler, 0, "ecc", mci);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_MC, "Failed to request IRQ\n");
+ return rc;
+ }
-#ifdef CONFIG_EDAC_DEBUG
-#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
+ return 0;
+}
/**
- * ddr_poison_setup - Update poison registers.
- * @priv: DDR memory controller private instance data.
+ * snps_setup_irq - Request and enable DDRC IRQs.
+ * @mci: EDAC memory controller instance.
*
- * Update poison registers as per DDR mapping.
- * Return: none.
+ * It first tries to get and request individual IRQs. If failed the method
+ * fallbacks to the common IRQ line case. The IRQs will be enabled only if
+ * some of these requests have been successful.
+ *
+ * Return: 0 if IRQs were successfully setup otherwise negative errno.
*/
-static void ddr_poison_setup(struct synps_edac_priv *priv)
+static int snps_setup_irq(struct mem_ctl_info *mci)
{
- int col = 0, row = 0, bank = 0, bankgrp = 0, rank = 0, regval;
- const struct synps_platform_data *p_data;
- int index;
- ulong hif_addr = 0;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ int rc;
- p_data = priv->p_data;
+ rc = snps_request_ind_irq(mci);
+ if (rc > 0)
+ rc = snps_request_com_irq(mci);
+ if (rc)
+ return rc;
- if (p_data->get_mem_info)
- hif_addr = p_data->get_mem_info(priv);
- else
- hif_addr = priv->poison_addr >> 3;
+ snps_enable_irq(priv);
- for (index = 0; index < DDR_MAX_ROW_SHIFT; index++) {
- if (priv->row_shift[index])
- row |= (((hif_addr >> priv->row_shift[index]) &
- BIT(0)) << index);
- else
- break;
- }
+ return 0;
+}
- for (index = 0; index < DDR_MAX_COL_SHIFT; index++) {
- if (priv->col_shift[index] || index < 3)
- col |= (((hif_addr >> priv->col_shift[index]) &
- BIT(0)) << index);
- else
- break;
- }
+#ifdef CONFIG_EDAC_DEBUG
- for (index = 0; index < DDR_MAX_BANK_SHIFT; index++) {
- if (priv->bank_shift[index])
- bank |= (((hif_addr >> priv->bank_shift[index]) &
- BIT(0)) << index);
- else
- break;
+#define SNPS_DEBUGFS_FOPS(__name, __read, __write) \
+ static const struct file_operations __name = { \
+ .owner = THIS_MODULE, \
+ .open = simple_open, \
+ .read = __read, \
+ .write = __write, \
}
- for (index = 0; index < DDR_MAX_BANKGRP_SHIFT; index++) {
- if (priv->bankgrp_shift[index])
- bankgrp |= (((hif_addr >> priv->bankgrp_shift[index])
- & BIT(0)) << index);
- else
- break;
- }
+#define SNPS_DBGFS_BUF_LEN 128
- if (priv->rank_shift[0])
- rank = (hif_addr >> priv->rank_shift[0]) & BIT(0);
+static int snps_ddrc_info_show(struct seq_file *s, void *data)
+{
+ struct mem_ctl_info *mci = s->private;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ unsigned long rate;
- regval = (rank << ECC_POISON0_RANK_SHIFT) & ECC_POISON0_RANK_MASK;
- regval |= (col << ECC_POISON0_COLUMN_SHIFT) & ECC_POISON0_COLUMN_MASK;
- writel(regval, priv->baseaddr + ECC_POISON0_OFST);
+ seq_printf(s, "SDRAM: %s\n", edac_mem_types[priv->info.sdram_mode]);
- regval = (bankgrp << ECC_POISON1_BG_SHIFT) & ECC_POISON1_BG_MASK;
- regval |= (bank << ECC_POISON1_BANKNR_SHIFT) & ECC_POISON1_BANKNR_MASK;
- regval |= (row << ECC_POISON1_ROW_SHIFT) & ECC_POISON1_ROW_MASK;
- writel(regval, priv->baseaddr + ECC_POISON1_OFST);
+ rate = clk_get_rate(priv->clks[SNPS_CORE_CLK].clk);
+ if (rate) {
+ rate = rate / HZ_PER_MHZ;
+ seq_printf(s, "Clock: Core %luMHz SDRAM %luMHz\n",
+ rate, priv->info.freq_ratio * rate);
+ }
+
+ seq_printf(s, "DQ bus: %u/%s\n", (BITS_PER_BYTE << priv->info.dq_width),
+ priv->info.dq_mode == SNPS_DQ_FULL ? "Full" :
+ priv->info.dq_mode == SNPS_DQ_HALF ? "Half" :
+ priv->info.dq_mode == SNPS_DQ_QRTR ? "Quarter" :
+ "Unknown");
+ seq_printf(s, "Burst: SDRAM %u HIF %u\n", priv->info.sdram_burst_len,
+ priv->info.hif_burst_len);
+
+ seq_printf(s, "Ranks: %u\n", priv->info.ranks);
+
+ seq_printf(s, "ECC: %s\n",
+ priv->info.ecc_mode == SNPS_ECC_SECDED ? "SEC/DED" :
+ priv->info.ecc_mode == SNPS_ECC_ADVX4X8 ? "Advanced X4/X8" :
+ "Unknown");
+
+ seq_puts(s, "Caps:");
+ if (priv->info.caps) {
+ if (priv->info.caps & SNPS_CAP_ECC_SCRUB)
+ seq_puts(s, " +Scrub");
+ if (priv->info.caps & SNPS_CAP_ECC_SCRUBBER)
+ seq_puts(s, " +Scrubber");
+ if (priv->info.caps & SNPS_CAP_ZYNQMP)
+ seq_puts(s, " +ZynqMP");
+ } else {
+ seq_puts(s, " -");
+ }
+ seq_putc(s, '\n');
+
+ return 0;
}
-static ssize_t inject_data_error_show(struct device *dev,
- struct device_attribute *mattr,
- char *data)
+DEFINE_SHOW_ATTRIBUTE(snps_ddrc_info);
+
+static int snps_sys_app_map_show(struct seq_file *s, void *data)
{
- struct mem_ctl_info *mci = to_mci(dev);
- struct synps_edac_priv *priv = mci->pvt_info;
-
- return sprintf(data, "Poison0 Addr: 0x%08x\n\rPoison1 Addr: 0x%08x\n\r"
- "Error injection Address: 0x%lx\n\r",
- readl(priv->baseaddr + ECC_POISON0_OFST),
- readl(priv->baseaddr + ECC_POISON1_OFST),
- priv->poison_addr);
+ struct mem_ctl_info *mci = s->private;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ struct snps_sys_app_map *map = &priv->sys_app_map;
+ u64 size;
+ int i;
+
+ if (!map->nsar) {
+ seq_puts(s, "No SARs detected\n");
+ return 0;
+ }
+
+ seq_printf(s, "%9s %-37s %-18s %-37s\n",
+ "", "System address", "Offset", "App address");
+
+ for (i = 0, size = 0; i < map->nsar; i++) {
+ seq_printf(s, "Region %d: ", i);
+ seq_printf(s, "0x%016llx-0x%016llx ", map->sar[i].base,
+ map->sar[i].base + map->sar[i].size - 1);
+ seq_printf(s, "0x%016llx ", map->sar[i].ofst);
+ seq_printf(s, "0x%016llx-0x%016llx\n", size,
+ size + map->sar[i].size - 1);
+ size += map->sar[i].size;
+ }
+
+ return 0;
}
-static ssize_t inject_data_error_store(struct device *dev,
- struct device_attribute *mattr,
- const char *data, size_t count)
+DEFINE_SHOW_ATTRIBUTE(snps_sys_app_map);
+
+static u8 snps_find_sdram_dim(struct snps_edac_priv *priv, u8 hif, char *dim)
{
- struct mem_ctl_info *mci = to_mci(dev);
- struct synps_edac_priv *priv = mci->pvt_info;
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
- if (kstrtoul(data, 0, &priv->poison_addr))
- return -EINVAL;
+ for (i = 0; i < DDR_MAX_ROW_WIDTH; i++) {
+ if (map->row[i] == hif) {
+ *dim = 'r';
+ return i;
+ }
+ }
- ddr_poison_setup(priv);
+ for (i = 0; i < DDR_MAX_COL_WIDTH; i++) {
+ if (map->col[i] == hif) {
+ *dim = 'c';
+ return i;
+ }
+ }
- return count;
-}
+ for (i = 0; i < DDR_MAX_BANK_WIDTH; i++) {
+ if (map->bank[i] == hif) {
+ *dim = 'b';
+ return i;
+ }
+ }
-static ssize_t inject_data_poison_show(struct device *dev,
- struct device_attribute *mattr,
- char *data)
-{
- struct mem_ctl_info *mci = to_mci(dev);
- struct synps_edac_priv *priv = mci->pvt_info;
+ for (i = 0; i < DDR_MAX_BANKGRP_WIDTH; i++) {
+ if (map->bankgrp[i] == hif) {
+ *dim = 'g';
+ return i;
+ }
+ }
+
+ for (i = 0; i < DDR_MAX_RANK_WIDTH; i++) {
+ if (map->rank[i] == hif) {
+ *dim = 'a';
+ return i;
+ }
+ }
- return sprintf(data, "Data Poisoning: %s\n\r",
- (((readl(priv->baseaddr + ECC_CFG1_OFST)) & 0x3) == 0x3)
- ? ("Correctable Error") : ("UnCorrectable Error"));
+ return DDR_ADDRMAP_UNUSED;
}
-static ssize_t inject_data_poison_store(struct device *dev,
- struct device_attribute *mattr,
- const char *data, size_t count)
+static int snps_hif_sdram_map_show(struct seq_file *s, void *data)
{
- struct mem_ctl_info *mci = to_mci(dev);
- struct synps_edac_priv *priv = mci->pvt_info;
+ struct mem_ctl_info *mci = s->private;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ char dim, buf[SNPS_DBGFS_BUF_LEN];
+ const int line_len = 10;
+ u8 bit;
+ int i;
- writel(0, priv->baseaddr + DDRC_SWCTL);
- if (strncmp(data, "CE", 2) == 0)
- writel(ECC_CEPOISON_MASK, priv->baseaddr + ECC_CFG1_OFST);
- else
- writel(ECC_UEPOISON_MASK, priv->baseaddr + ECC_CFG1_OFST);
- writel(1, priv->baseaddr + DDRC_SWCTL);
+ seq_printf(s, "%3s", "");
+ for (i = 0; i < line_len; i++)
+ seq_printf(s, " %02d ", i);
- return count;
-}
+ for (i = 0; i < DDR_MAX_HIF_WIDTH; i++) {
+ if (i % line_len == 0)
+ seq_printf(s, "\n%02d ", i);
-static DEVICE_ATTR_RW(inject_data_error);
-static DEVICE_ATTR_RW(inject_data_poison);
+ bit = snps_find_sdram_dim(priv, i, &dim);
-static int edac_create_sysfs_attributes(struct mem_ctl_info *mci)
-{
- int rc;
+ if (bit != DDR_ADDRMAP_UNUSED)
+ scnprintf(buf, SNPS_DBGFS_BUF_LEN, "%c%hhu", dim, bit);
+ else
+ scnprintf(buf, SNPS_DBGFS_BUF_LEN, "--");
+
+ seq_printf(s, "%3s ", buf);
+ }
+ seq_putc(s, '\n');
+
+ seq_puts(s, "r - row, c - column, b - bank, g - bank group, a - rank\n");
- rc = device_create_file(&mci->dev, &dev_attr_inject_data_error);
- if (rc < 0)
- return rc;
- rc = device_create_file(&mci->dev, &dev_attr_inject_data_poison);
- if (rc < 0)
- return rc;
return 0;
}
-static void edac_remove_sysfs_attributes(struct mem_ctl_info *mci)
+DEFINE_SHOW_ATTRIBUTE(snps_hif_sdram_map);
+
+static ssize_t snps_inject_data_error_read(struct file *filep, char __user *ubuf,
+ size_t size, loff_t *offp)
{
- device_remove_file(&mci->dev, &dev_attr_inject_data_error);
- device_remove_file(&mci->dev, &dev_attr_inject_data_poison);
+ struct mem_ctl_info *mci = filep->private_data;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ struct snps_sdram_addr sdram;
+ char buf[SNPS_DBGFS_BUF_LEN];
+ dma_addr_t sys;
+ u32 regval;
+ int pos;
+
+ regval = readl(priv->baseaddr + ECC_POISON0_OFST);
+ sdram.rank = FIELD_GET(ECC_POISON0_RANK_MASK, regval);
+ sdram.col = FIELD_GET(ECC_POISON0_COL_MASK, regval);
+
+ regval = readl(priv->baseaddr + ECC_POISON1_OFST);
+ sdram.bankgrp = FIELD_PREP(ECC_POISON1_BANKGRP_MASK, regval);
+ sdram.bank = FIELD_PREP(ECC_POISON1_BANK_MASK, regval);
+ sdram.row = FIELD_PREP(ECC_POISON1_ROW_MASK, regval);
+
+ snps_map_sdram_to_sys(priv, &sdram, &sys);
+
+ pos = scnprintf(buf, sizeof(buf),
+ "%pad: Row %hu Col %hu Bank %hhu Bank Group %hhu Rank %hhu\n",
+ &sys, sdram.row, sdram.col, sdram.bank, sdram.bankgrp,
+ sdram.rank);
+
+ return simple_read_from_buffer(ubuf, size, offp, buf, pos);
}
-static void setup_row_address_map(struct synps_edac_priv *priv, u32 *addrmap)
+static ssize_t snps_inject_data_error_write(struct file *filep, const char __user *ubuf,
+ size_t size, loff_t *offp)
{
- u32 addrmap_row_b2_10;
- int index;
+ struct mem_ctl_info *mci = filep->private_data;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ struct snps_sdram_addr sdram;
+ u32 regval;
+ u64 sys;
+ int rc;
- priv->row_shift[0] = (addrmap[5] & ROW_MAX_VAL_MASK) + ROW_B0_BASE;
- priv->row_shift[1] = ((addrmap[5] >> 8) &
- ROW_MAX_VAL_MASK) + ROW_B1_BASE;
+ rc = kstrtou64_from_user(ubuf, size, 0, &sys);
+ if (rc)
+ return rc;
- addrmap_row_b2_10 = (addrmap[5] >> 16) & ROW_MAX_VAL_MASK;
- if (addrmap_row_b2_10 != ROW_MAX_VAL_MASK) {
- for (index = 2; index < 11; index++)
- priv->row_shift[index] = addrmap_row_b2_10 +
- index + ROW_B0_BASE;
+ snps_map_sys_to_sdram(priv, sys, &sdram);
- } else {
- priv->row_shift[2] = (addrmap[9] &
- ROW_MAX_VAL_MASK) + ROW_B2_BASE;
- priv->row_shift[3] = ((addrmap[9] >> 8) &
- ROW_MAX_VAL_MASK) + ROW_B3_BASE;
- priv->row_shift[4] = ((addrmap[9] >> 16) &
- ROW_MAX_VAL_MASK) + ROW_B4_BASE;
- priv->row_shift[5] = ((addrmap[9] >> 24) &
- ROW_MAX_VAL_MASK) + ROW_B5_BASE;
- priv->row_shift[6] = (addrmap[10] &
- ROW_MAX_VAL_MASK) + ROW_B6_BASE;
- priv->row_shift[7] = ((addrmap[10] >> 8) &
- ROW_MAX_VAL_MASK) + ROW_B7_BASE;
- priv->row_shift[8] = ((addrmap[10] >> 16) &
- ROW_MAX_VAL_MASK) + ROW_B8_BASE;
- priv->row_shift[9] = ((addrmap[10] >> 24) &
- ROW_MAX_VAL_MASK) + ROW_B9_BASE;
- priv->row_shift[10] = (addrmap[11] &
- ROW_MAX_VAL_MASK) + ROW_B10_BASE;
- }
+ regval = FIELD_PREP(ECC_POISON0_RANK_MASK, sdram.rank) |
+ FIELD_PREP(ECC_POISON0_COL_MASK, sdram.col);
+ writel(regval, priv->baseaddr + ECC_POISON0_OFST);
+
+ regval = FIELD_PREP(ECC_POISON1_BANKGRP_MASK, sdram.bankgrp) |
+ FIELD_PREP(ECC_POISON1_BANK_MASK, sdram.bank) |
+ FIELD_PREP(ECC_POISON1_ROW_MASK, sdram.row);
+ writel(regval, priv->baseaddr + ECC_POISON1_OFST);
- priv->row_shift[11] = (((addrmap[5] >> 24) & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : (((addrmap[5] >> 24) &
- ROW_MAX_VAL_MASK) + ROW_B11_BASE);
- priv->row_shift[12] = ((addrmap[6] & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : ((addrmap[6] &
- ROW_MAX_VAL_MASK) + ROW_B12_BASE);
- priv->row_shift[13] = (((addrmap[6] >> 8) & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 8) &
- ROW_MAX_VAL_MASK) + ROW_B13_BASE);
- priv->row_shift[14] = (((addrmap[6] >> 16) & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 16) &
- ROW_MAX_VAL_MASK) + ROW_B14_BASE);
- priv->row_shift[15] = (((addrmap[6] >> 24) & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 24) &
- ROW_MAX_VAL_MASK) + ROW_B15_BASE);
- priv->row_shift[16] = ((addrmap[7] & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : ((addrmap[7] &
- ROW_MAX_VAL_MASK) + ROW_B16_BASE);
- priv->row_shift[17] = (((addrmap[7] >> 8) & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : (((addrmap[7] >> 8) &
- ROW_MAX_VAL_MASK) + ROW_B17_BASE);
+ return size;
}
-static void setup_column_address_map(struct synps_edac_priv *priv, u32 *addrmap)
+SNPS_DEBUGFS_FOPS(snps_inject_data_error, snps_inject_data_error_read,
+ snps_inject_data_error_write);
+
+static ssize_t snps_inject_data_poison_read(struct file *filep, char __user *ubuf,
+ size_t size, loff_t *offp)
{
- u32 width, memtype;
- int index;
-
- memtype = readl(priv->baseaddr + CTRL_OFST);
- width = (memtype & ECC_CTRL_BUSWIDTH_MASK) >> ECC_CTRL_BUSWIDTH_SHIFT;
-
- priv->col_shift[0] = 0;
- priv->col_shift[1] = 1;
- priv->col_shift[2] = (addrmap[2] & COL_MAX_VAL_MASK) + COL_B2_BASE;
- priv->col_shift[3] = ((addrmap[2] >> 8) &
- COL_MAX_VAL_MASK) + COL_B3_BASE;
- priv->col_shift[4] = (((addrmap[2] >> 16) & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : (((addrmap[2] >> 16) &
- COL_MAX_VAL_MASK) + COL_B4_BASE);
- priv->col_shift[5] = (((addrmap[2] >> 24) & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : (((addrmap[2] >> 24) &
- COL_MAX_VAL_MASK) + COL_B5_BASE);
- priv->col_shift[6] = ((addrmap[3] & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : ((addrmap[3] &
- COL_MAX_VAL_MASK) + COL_B6_BASE);
- priv->col_shift[7] = (((addrmap[3] >> 8) & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 8) &
- COL_MAX_VAL_MASK) + COL_B7_BASE);
- priv->col_shift[8] = (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 16) &
- COL_MAX_VAL_MASK) + COL_B8_BASE);
- priv->col_shift[9] = (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 24) &
- COL_MAX_VAL_MASK) + COL_B9_BASE);
- if (width == DDRCTL_EWDTH_64) {
- if (memtype & MEM_TYPE_LPDDR3) {
- priv->col_shift[10] = ((addrmap[4] &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- ((addrmap[4] & COL_MAX_VAL_MASK) +
- COL_B10_BASE);
- priv->col_shift[11] = (((addrmap[4] >> 8) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[4] >> 8) & COL_MAX_VAL_MASK) +
- COL_B11_BASE);
- } else {
- priv->col_shift[11] = ((addrmap[4] &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- ((addrmap[4] & COL_MAX_VAL_MASK) +
- COL_B10_BASE);
- priv->col_shift[13] = (((addrmap[4] >> 8) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[4] >> 8) & COL_MAX_VAL_MASK) +
- COL_B11_BASE);
- }
- } else if (width == DDRCTL_EWDTH_32) {
- if (memtype & MEM_TYPE_LPDDR3) {
- priv->col_shift[10] = (((addrmap[3] >> 24) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) +
- COL_B9_BASE);
- priv->col_shift[11] = ((addrmap[4] &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- ((addrmap[4] & COL_MAX_VAL_MASK) +
- COL_B10_BASE);
- } else {
- priv->col_shift[11] = (((addrmap[3] >> 24) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) +
- COL_B9_BASE);
- priv->col_shift[13] = ((addrmap[4] &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- ((addrmap[4] & COL_MAX_VAL_MASK) +
- COL_B10_BASE);
- }
- } else {
- if (memtype & MEM_TYPE_LPDDR3) {
- priv->col_shift[10] = (((addrmap[3] >> 16) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) +
- COL_B8_BASE);
- priv->col_shift[11] = (((addrmap[3] >> 24) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) +
- COL_B9_BASE);
- priv->col_shift[13] = ((addrmap[4] &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- ((addrmap[4] & COL_MAX_VAL_MASK) +
- COL_B10_BASE);
- } else {
- priv->col_shift[11] = (((addrmap[3] >> 16) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) +
- COL_B8_BASE);
- priv->col_shift[13] = (((addrmap[3] >> 24) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) +
- COL_B9_BASE);
- }
- }
+ struct mem_ctl_info *mci = filep->private_data;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ char buf[SNPS_DBGFS_BUF_LEN];
+ const char *errstr;
+ u32 regval;
+ int pos;
+
+ regval = readl(priv->baseaddr + ECC_CFG1_OFST);
+ if (!(regval & ECC_CFG1_POISON_EN))
+ errstr = "Off";
+ else if (regval & ECC_CFG1_POISON_BIT)
+ errstr = "CE";
+ else
+ errstr = "UE";
- if (width) {
- for (index = 9; index > width; index--) {
- priv->col_shift[index] = priv->col_shift[index - width];
- priv->col_shift[index - width] = 0;
- }
- }
+ pos = scnprintf(buf, sizeof(buf), "%s\n", errstr);
+ return simple_read_from_buffer(ubuf, size, offp, buf, pos);
}
-static void setup_bank_address_map(struct synps_edac_priv *priv, u32 *addrmap)
+static ssize_t snps_inject_data_poison_write(struct file *filep, const char __user *ubuf,
+ size_t size, loff_t *offp)
{
- priv->bank_shift[0] = (addrmap[1] & BANK_MAX_VAL_MASK) + BANK_B0_BASE;
- priv->bank_shift[1] = ((addrmap[1] >> 8) &
- BANK_MAX_VAL_MASK) + BANK_B1_BASE;
- priv->bank_shift[2] = (((addrmap[1] >> 16) &
- BANK_MAX_VAL_MASK) == BANK_MAX_VAL_MASK) ? 0 :
- (((addrmap[1] >> 16) & BANK_MAX_VAL_MASK) +
- BANK_B2_BASE);
+ struct mem_ctl_info *mci = filep->private_data;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ char buf[SNPS_DBGFS_BUF_LEN];
+ u32 regval;
+ int rc;
-}
+ rc = simple_write_to_buffer(buf, sizeof(buf), offp, ubuf, size);
+ if (rc < 0)
+ return rc;
-static void setup_bg_address_map(struct synps_edac_priv *priv, u32 *addrmap)
-{
- priv->bankgrp_shift[0] = (addrmap[8] &
- BANKGRP_MAX_VAL_MASK) + BANKGRP_B0_BASE;
- priv->bankgrp_shift[1] = (((addrmap[8] >> 8) & BANKGRP_MAX_VAL_MASK) ==
- BANKGRP_MAX_VAL_MASK) ? 0 : (((addrmap[8] >> 8)
- & BANKGRP_MAX_VAL_MASK) + BANKGRP_B1_BASE);
+ writel(0, priv->baseaddr + DDR_SWCTL);
-}
+ regval = readl(priv->baseaddr + ECC_CFG1_OFST);
+ if (strncmp(buf, "CE", 2) == 0)
+ regval |= ECC_CFG1_POISON_BIT | ECC_CFG1_POISON_EN;
+ else if (strncmp(buf, "UE", 2) == 0)
+ regval = (regval & ~ECC_CFG1_POISON_BIT) | ECC_CFG1_POISON_EN;
+ else
+ regval &= ~ECC_CFG1_POISON_EN;
+ writel(regval, priv->baseaddr + ECC_CFG1_OFST);
-static void setup_rank_address_map(struct synps_edac_priv *priv, u32 *addrmap)
-{
- priv->rank_shift[0] = ((addrmap[0] & RANK_MAX_VAL_MASK) ==
- RANK_MAX_VAL_MASK) ? 0 : ((addrmap[0] &
- RANK_MAX_VAL_MASK) + RANK_B0_BASE);
+ writel(1, priv->baseaddr + DDR_SWCTL);
+
+ return size;
}
+SNPS_DEBUGFS_FOPS(snps_inject_data_poison, snps_inject_data_poison_read,
+ snps_inject_data_poison_write);
+
/**
- * setup_address_map - Set Address Map by querying ADDRMAP registers.
- * @priv: DDR memory controller private instance data.
+ * snps_create_debugfs_nodes - Create DebugFS nodes.
+ * @mci: EDAC memory controller instance.
*
- * Set Address Map by querying ADDRMAP registers.
+ * Create DW uMCTL2 EDAC driver DebugFS nodes in the device private
+ * DebugFS directory.
*
* Return: none.
*/
-static void setup_address_map(struct synps_edac_priv *priv)
+static void snps_create_debugfs_nodes(struct mem_ctl_info *mci)
{
- u32 addrmap[12];
- int index;
+ edac_debugfs_create_file("ddrc_info", 0400, mci->debugfs, mci,
+ &snps_ddrc_info_fops);
- for (index = 0; index < 12; index++) {
- u32 addrmap_offset;
+ edac_debugfs_create_file("sys_app_map", 0400, mci->debugfs, mci,
+ &snps_sys_app_map_fops);
- addrmap_offset = ECC_ADDRMAP0_OFFSET + (index * 4);
- addrmap[index] = readl(priv->baseaddr + addrmap_offset);
- }
+ edac_debugfs_create_file("hif_sdram_map", 0400, mci->debugfs, mci,
+ &snps_hif_sdram_map_fops);
- setup_row_address_map(priv, addrmap);
+ edac_debugfs_create_file("inject_data_error", 0600, mci->debugfs, mci,
+ &snps_inject_data_error);
- setup_column_address_map(priv, addrmap);
+ edac_debugfs_create_file("inject_data_poison", 0600, mci->debugfs, mci,
+ &snps_inject_data_poison);
+}
- setup_bank_address_map(priv, addrmap);
+#else /* !CONFIG_EDAC_DEBUG */
- setup_bg_address_map(priv, addrmap);
+static inline void snps_create_debugfs_nodes(struct mem_ctl_info *mci) {}
- setup_rank_address_map(priv, addrmap);
-}
-#endif /* CONFIG_EDAC_DEBUG */
+#endif /* !CONFIG_EDAC_DEBUG */
/**
- * mc_probe - Check controller and bind driver.
+ * snps_mc_probe - Check controller and bind driver.
* @pdev: platform device.
*
* Probe a specific controller instance for binding with the driver.
@@ -1366,57 +2438,35 @@ static void setup_address_map(struct synps_edac_priv *priv)
* Return: 0 if the controller instance was successfully bound to the
* driver; otherwise, < 0 on error.
*/
-static int mc_probe(struct platform_device *pdev)
+static int snps_mc_probe(struct platform_device *pdev)
{
- const struct synps_platform_data *p_data;
- struct edac_mc_layer layers[2];
- struct synps_edac_priv *priv;
+ struct snps_edac_priv *priv;
struct mem_ctl_info *mci;
- void __iomem *baseaddr;
int rc;
- baseaddr = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(baseaddr))
- return PTR_ERR(baseaddr);
+ priv = snps_create_data(pdev);
+ if (IS_ERR(priv))
+ return PTR_ERR(priv);
- p_data = of_device_get_match_data(&pdev->dev);
- if (!p_data)
- return -ENODEV;
+ rc = snps_get_res(priv);
+ if (rc)
+ return rc;
+ rc = snps_get_ddrc_info(priv);
+ if (rc)
+ goto put_res;
- layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
- layers[0].size = SYNPS_EDAC_NR_CSROWS;
- layers[0].is_virt_csrow = true;
- layers[1].type = EDAC_MC_LAYER_CHANNEL;
- layers[1].size = SYNPS_EDAC_NR_CHANS;
- layers[1].is_virt_csrow = false;
+ snps_get_addr_map(priv);
- mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
- sizeof(struct synps_edac_priv));
- if (!mci) {
- edac_printk(KERN_ERR, EDAC_MC,
- "Failed memory allocation for mc instance\n");
- return -ENOMEM;
+ mci = snps_mc_create(priv);
+ if (IS_ERR(mci)) {
+ rc = PTR_ERR(mci);
+ goto put_res;
}
- priv = mci->pvt_info;
- priv->baseaddr = baseaddr;
- priv->p_data = p_data;
- if (!get_ecc_state(priv)) {
- edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n");
- rc = -ENODEV;
+ rc = snps_setup_irq(mci);
+ if (rc)
goto free_edac_mc;
- }
-
- spin_lock_init(&priv->reglock);
-
- mc_init(mci, pdev);
-
- if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) {
- rc = setup_irq(mci, pdev);
- if (rc)
- goto free_edac_mc;
- }
rc = edac_mc_add_mc(mci);
if (rc) {
@@ -1425,69 +2475,58 @@ static int mc_probe(struct platform_device *pdev)
goto free_edac_mc;
}
-#ifdef CONFIG_EDAC_DEBUG
- if (priv->p_data->quirks & DDR_ECC_DATA_POISON_SUPPORT) {
- rc = edac_create_sysfs_attributes(mci);
- if (rc) {
- edac_printk(KERN_ERR, EDAC_MC,
- "Failed to create sysfs entries\n");
- goto free_edac_mc;
- }
- }
-
- if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT)
- setup_address_map(priv);
-#endif
+ snps_create_debugfs_nodes(mci);
- /*
- * Start capturing the correctable and uncorrectable errors. A write of
- * 0 starts the counters.
- */
- if (!(priv->p_data->quirks & DDR_ECC_INTR_SUPPORT))
- writel(0x0, baseaddr + ECC_CTRL_OFST);
-
- return rc;
+ return 0;
free_edac_mc:
- edac_mc_free(mci);
+ snps_mc_free(mci);
+
+put_res:
+ snps_put_res(priv);
return rc;
}
/**
- * mc_remove - Unbind driver from controller.
+ * snps_mc_remove - Unbind driver from device.
* @pdev: Platform device.
*
* Return: Unconditionally 0
*/
-static void mc_remove(struct platform_device *pdev)
+
+static void snps_mc_remove(struct platform_device *pdev)
{
struct mem_ctl_info *mci = platform_get_drvdata(pdev);
- struct synps_edac_priv *priv = mci->pvt_info;
+ struct snps_edac_priv *priv = mci->pvt_info;
- if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT)
- disable_intr(priv);
-
-#ifdef CONFIG_EDAC_DEBUG
- if (priv->p_data->quirks & DDR_ECC_DATA_POISON_SUPPORT)
- edac_remove_sysfs_attributes(mci);
-#endif
+ snps_disable_irq(priv);
edac_mc_del_mc(&pdev->dev);
- edac_mc_free(mci);
+
+ snps_mc_free(mci);
+
+ snps_put_res(priv);
}
-static struct platform_driver synps_edac_mc_driver = {
+static const struct of_device_id snps_edac_match[] = {
+ { .compatible = "xlnx,zynqmp-ddrc-2.40a", .data = zynqmp_init_plat },
+ { .compatible = "baikal,bt1-ddrc", .data = bt1_init_plat },
+ { .compatible = "snps,ddrc-3.80a" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, snps_edac_match);
+
+static struct platform_driver snps_edac_mc_driver = {
.driver = {
- .name = "synopsys-edac",
- .of_match_table = synps_edac_match,
+ .name = "snps-edac",
+ .of_match_table = snps_edac_match,
},
- .probe = mc_probe,
- .remove = mc_remove,
+ .probe = snps_mc_probe,
+ .remove = snps_mc_remove,
};
-
-module_platform_driver(synps_edac_mc_driver);
+module_platform_driver(snps_edac_mc_driver);
MODULE_AUTHOR("Xilinx Inc");
-MODULE_DESCRIPTION("Synopsys DDR ECC driver");
+MODULE_DESCRIPTION("Synopsys uMCTL2 DDR ECC driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/edac/zynq_edac.c b/drivers/edac/zynq_edac.c
new file mode 100644
index 0000000000000..f1b5771bbee17
--- /dev/null
+++ b/drivers/edac/zynq_edac.c
@@ -0,0 +1,506 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Zynq DDR ECC Driver
+ * This driver is based on ppc4xx_edac.c drivers
+ *
+ * Copyright (C) 2012 - 2014 Xilinx, Inc.
+ */
+
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "edac_module.h"
+
+/* Number of cs_rows needed per memory controller */
+#define ZYNQ_EDAC_NR_CSROWS 1
+
+/* Number of channels per memory controller */
+#define ZYNQ_EDAC_NR_CHANS 1
+
+/* Granularity of reported error in bytes */
+#define ZYNQ_EDAC_ERR_GRAIN 1
+
+#define ZYNQ_EDAC_MSG_SIZE 256
+
+#define ZYNQ_EDAC_MOD_STRING "zynq_edac"
+#define ZYNQ_EDAC_MOD_VER "1"
+
+/* Zynq DDR memory controller ECC registers */
+#define ZYNQ_CTRL_OFST 0x0
+#define ZYNQ_T_ZQ_OFST 0xA4
+
+/* ECC control register */
+#define ZYNQ_ECC_CTRL_OFST 0xC4
+/* ECC log register */
+#define ZYNQ_CE_LOG_OFST 0xC8
+/* ECC address register */
+#define ZYNQ_CE_ADDR_OFST 0xCC
+/* ECC data[31:0] register */
+#define ZYNQ_CE_DATA_31_0_OFST 0xD0
+
+/* Uncorrectable error info registers */
+#define ZYNQ_UE_LOG_OFST 0xDC
+#define ZYNQ_UE_ADDR_OFST 0xE0
+#define ZYNQ_UE_DATA_31_0_OFST 0xE4
+
+#define ZYNQ_STAT_OFST 0xF0
+#define ZYNQ_SCRUB_OFST 0xF4
+
+/* Control register bit field definitions */
+#define ZYNQ_CTRL_BW_MASK 0xC
+#define ZYNQ_CTRL_BW_SHIFT 2
+
+#define ZYNQ_DDRCTL_WDTH_16 1
+#define ZYNQ_DDRCTL_WDTH_32 0
+
+/* ZQ register bit field definitions */
+#define ZYNQ_T_ZQ_DDRMODE_MASK 0x2
+
+/* ECC control register bit field definitions */
+#define ZYNQ_ECC_CTRL_CLR_CE_ERR 0x2
+#define ZYNQ_ECC_CTRL_CLR_UE_ERR 0x1
+
+/* ECC correctable/uncorrectable error log register definitions */
+#define ZYNQ_LOG_VALID 0x1
+#define ZYNQ_CE_LOG_BITPOS_MASK 0xFE
+#define ZYNQ_CE_LOG_BITPOS_SHIFT 1
+
+/* ECC correctable/uncorrectable error address register definitions */
+#define ZYNQ_ADDR_COL_MASK 0xFFF
+#define ZYNQ_ADDR_ROW_MASK 0xFFFF000
+#define ZYNQ_ADDR_ROW_SHIFT 12
+#define ZYNQ_ADDR_BANK_MASK 0x70000000
+#define ZYNQ_ADDR_BANK_SHIFT 28
+
+/* ECC statistic register definitions */
+#define ZYNQ_STAT_UECNT_MASK 0xFF
+#define ZYNQ_STAT_CECNT_MASK 0xFF00
+#define ZYNQ_STAT_CECNT_SHIFT 8
+
+/* ECC scrub register definitions */
+#define ZYNQ_SCRUB_MODE_MASK 0x7
+#define ZYNQ_SCRUB_MODE_SECDED 0x4
+
+/**
+ * struct zynq_ecc_error_info - ECC error log information.
+ * @row: Row number.
+ * @col: Column number.
+ * @bank: Bank number.
+ * @bitpos: Bit position.
+ * @data: Data causing the error.
+ */
+struct zynq_ecc_error_info {
+ u32 row;
+ u32 col;
+ u32 bank;
+ u32 bitpos;
+ u32 data;
+};
+
+/**
+ * struct zynq_ecc_status - ECC status information to report.
+ * @ce_cnt: Correctable error count.
+ * @ue_cnt: Uncorrectable error count.
+ * @ceinfo: Correctable error log information.
+ * @ueinfo: Uncorrectable error log information.
+ */
+struct zynq_ecc_status {
+ u32 ce_cnt;
+ u32 ue_cnt;
+ struct zynq_ecc_error_info ceinfo;
+ struct zynq_ecc_error_info ueinfo;
+};
+
+/**
+ * struct zynq_edac_priv - DDR memory controller private instance data.
+ * @baseaddr: Base address of the DDR controller.
+ * @message: Buffer for framing the event specific info.
+ * @stat: ECC status information.
+ */
+struct zynq_edac_priv {
+ void __iomem *baseaddr;
+ char message[ZYNQ_EDAC_MSG_SIZE];
+ struct zynq_ecc_status stat;
+};
+
+/**
+ * zynq_get_error_info - Get the current ECC error info.
+ * @priv: DDR memory controller private instance data.
+ *
+ * Return: one if there is no error, otherwise zero.
+ */
+static int zynq_get_error_info(struct zynq_edac_priv *priv)
+{
+ struct zynq_ecc_status *p;
+ u32 regval, clearval = 0;
+ void __iomem *base;
+
+ base = priv->baseaddr;
+ p = &priv->stat;
+
+ regval = readl(base + ZYNQ_STAT_OFST);
+ if (!regval)
+ return 1;
+
+ p->ce_cnt = (regval & ZYNQ_STAT_CECNT_MASK) >> ZYNQ_STAT_CECNT_SHIFT;
+ p->ue_cnt = regval & ZYNQ_STAT_UECNT_MASK;
+
+ regval = readl(base + ZYNQ_CE_LOG_OFST);
+ if (!(p->ce_cnt && (regval & ZYNQ_LOG_VALID)))
+ goto ue_err;
+
+ p->ceinfo.bitpos = (regval & ZYNQ_CE_LOG_BITPOS_MASK) >> ZYNQ_CE_LOG_BITPOS_SHIFT;
+ regval = readl(base + ZYNQ_CE_ADDR_OFST);
+ p->ceinfo.row = (regval & ZYNQ_ADDR_ROW_MASK) >> ZYNQ_ADDR_ROW_SHIFT;
+ p->ceinfo.col = regval & ZYNQ_ADDR_COL_MASK;
+ p->ceinfo.bank = (regval & ZYNQ_ADDR_BANK_MASK) >> ZYNQ_ADDR_BANK_SHIFT;
+ p->ceinfo.data = readl(base + ZYNQ_CE_DATA_31_0_OFST);
+ edac_dbg(3, "CE bit position: %d data: %d\n", p->ceinfo.bitpos,
+ p->ceinfo.data);
+ clearval = ZYNQ_ECC_CTRL_CLR_CE_ERR;
+
+ue_err:
+ regval = readl(base + ZYNQ_UE_LOG_OFST);
+ if (!(p->ue_cnt && (regval & ZYNQ_LOG_VALID)))
+ goto out;
+
+ regval = readl(base + ZYNQ_UE_ADDR_OFST);
+ p->ueinfo.row = (regval & ZYNQ_ADDR_ROW_MASK) >> ZYNQ_ADDR_ROW_SHIFT;
+ p->ueinfo.col = regval & ZYNQ_ADDR_COL_MASK;
+ p->ueinfo.bank = (regval & ZYNQ_ADDR_BANK_MASK) >> ZYNQ_ADDR_BANK_SHIFT;
+ p->ueinfo.data = readl(base + ZYNQ_UE_DATA_31_0_OFST);
+ clearval |= ZYNQ_ECC_CTRL_CLR_UE_ERR;
+
+out:
+ writel(clearval, base + ZYNQ_ECC_CTRL_OFST);
+ writel(0x0, base + ZYNQ_ECC_CTRL_OFST);
+
+ return 0;
+}
+
+/**
+ * zynq_handle_error - Handle Correctable and Uncorrectable errors.
+ * @mci: EDAC memory controller instance.
+ * @p: Zynq ECC status structure.
+ *
+ * Handles ECC correctable and uncorrectable errors.
+ */
+static void zynq_handle_error(struct mem_ctl_info *mci, struct zynq_ecc_status *p)
+{
+ struct zynq_edac_priv *priv = mci->pvt_info;
+ struct zynq_ecc_error_info *pinf;
+
+ if (p->ce_cnt) {
+ pinf = &p->ceinfo;
+
+ snprintf(priv->message, ZYNQ_EDAC_MSG_SIZE,
+ "Row %d Bank %d Col %d Bit %d Data 0x%08x",
+ pinf->row, pinf->bank, pinf->col,
+ pinf->bitpos, pinf->data);
+
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ p->ce_cnt, 0, 0, 0, 0, 0, -1,
+ priv->message, "");
+ }
+
+ if (p->ue_cnt) {
+ pinf = &p->ueinfo;
+
+ snprintf(priv->message, ZYNQ_EDAC_MSG_SIZE,
+ "Row %d Bank %d Col %d",
+ pinf->row, pinf->bank, pinf->col);
+
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ p->ue_cnt, 0, 0, 0, 0, 0, -1,
+ priv->message, "");
+ }
+
+ memset(p, 0, sizeof(*p));
+}
+
+/**
+ * zynq_check_errors - Check controller for ECC errors.
+ * @mci: EDAC memory controller instance.
+ *
+ * Check and post ECC errors. Called by the polling thread.
+ */
+static void zynq_check_errors(struct mem_ctl_info *mci)
+{
+ struct zynq_edac_priv *priv = mci->pvt_info;
+ int status;
+
+ status = zynq_get_error_info(priv);
+ if (status)
+ return;
+
+ zynq_handle_error(mci, &priv->stat);
+}
+
+/**
+ * zynq_get_dtype - Return the controller memory width.
+ * @base: DDR memory controller base address.
+ *
+ * Get the EDAC device type width appropriate for the current controller
+ * configuration.
+ *
+ * Return: a device type width enumeration.
+ */
+static enum dev_type zynq_get_dtype(const void __iomem *base)
+{
+ enum dev_type dt;
+ u32 width;
+
+ width = readl(base + ZYNQ_CTRL_OFST);
+ width = (width & ZYNQ_CTRL_BW_MASK) >> ZYNQ_CTRL_BW_SHIFT;
+
+ switch (width) {
+ case ZYNQ_DDRCTL_WDTH_16:
+ dt = DEV_X2;
+ break;
+ case ZYNQ_DDRCTL_WDTH_32:
+ dt = DEV_X4;
+ break;
+ default:
+ dt = DEV_UNKNOWN;
+ }
+
+ return dt;
+}
+
+/**
+ * zynq_get_ecc_state - Return the controller ECC enable/disable status.
+ * @base: DDR memory controller base address.
+ *
+ * Get the ECC enable/disable status for the controller and clear out
+ * the ECC counters if applicatble.
+ *
+ * Return: true if enabled, otherwise false.
+ */
+static bool zynq_get_ecc_state(void __iomem *base)
+{
+ u32 ecctype, clearval;
+ enum dev_type dt;
+
+ dt = zynq_get_dtype(base);
+ if (dt == DEV_UNKNOWN)
+ return false;
+
+ ecctype = readl(base + ZYNQ_SCRUB_OFST) & ZYNQ_SCRUB_MODE_MASK;
+ if ((ecctype == ZYNQ_SCRUB_MODE_SECDED) && (dt == DEV_X2)) {
+ clearval = ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_UE_ERR;
+ writel(clearval, base + ECC_CTRL_OFST);
+ writel(0x0, base + ECC_CTRL_OFST);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * zynq_get_memsize - Read the size of the attached memory device.
+ *
+ * Return: the memory size in bytes.
+ */
+static u32 zynq_get_memsize(void)
+{
+ struct sysinfo inf;
+
+ si_meminfo(&inf);
+
+ return inf.totalram * inf.mem_unit;
+}
+
+/**
+ * zynq_get_mtype - Return the controller memory type.
+ * @base: Zynq ECC status structure.
+ *
+ * Get the EDAC memory type appropriate for the current controller
+ * configuration.
+ *
+ * Return: a memory type enumeration.
+ */
+static enum mem_type zynq_get_mtype(const void __iomem *base)
+{
+ enum mem_type mt;
+ u32 memtype;
+
+ memtype = readl(base + ZYNQ_T_ZQ_OFST);
+
+ if (memtype & ZYNQ_T_ZQ_DDRMODE_MASK)
+ mt = MEM_DDR3;
+ else
+ mt = MEM_DDR2;
+
+ return mt;
+}
+
+/**
+ * zynq_init_csrows - Initialize the csrow data.
+ * @mci: EDAC memory controller instance.
+ *
+ * Initialize the chip select rows associated with the EDAC memory
+ * controller instance.
+ */
+static void zynq_init_csrows(struct mem_ctl_info *mci)
+{
+ struct zynq_edac_priv *priv = mci->pvt_info;
+ struct csrow_info *csi;
+ struct dimm_info *dimm;
+ u32 size, row;
+ int j;
+
+ for (row = 0; row < mci->nr_csrows; row++) {
+ csi = mci->csrows[row];
+ size = zynq_get_memsize();
+
+ for (j = 0; j < csi->nr_channels; j++) {
+ dimm = csi->channels[j]->dimm;
+ dimm->edac_mode = EDAC_SECDED;
+ dimm->mtype = zynq_get_mtype(priv->baseaddr);
+ dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels;
+ dimm->grain = ZYNQ_EDAC_ERR_GRAIN;
+ dimm->dtype = zynq_get_dtype(priv->baseaddr);
+ }
+ }
+}
+
+/**
+ * zynq_mc_init - Initialize one driver instance.
+ * @mci: EDAC memory controller instance.
+ * @pdev: platform device.
+ *
+ * Perform initialization of the EDAC memory controller instance and
+ * related driver-private data associated with the memory controller the
+ * instance is bound to.
+ */
+static void zynq_mc_init(struct mem_ctl_info *mci, struct platform_device *pdev)
+{
+ mci->pdev = &pdev->dev;
+ platform_set_drvdata(pdev, mci);
+
+ /* Initialize controller capabilities and configuration */
+ mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR2;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
+ mci->scrub_cap = SCRUB_FLAG_HW_SRC;
+ mci->scrub_mode = SCRUB_NONE;
+
+ mci->edac_cap = EDAC_FLAG_SECDED;
+ mci->ctl_name = "zynq_ddr_controller";
+ mci->dev_name = ZYNQ_EDAC_MOD_STRING;
+ mci->mod_name = ZYNQ_EDAC_MOD_VER;
+
+ edac_op_state = EDAC_OPSTATE_POLL;
+ mci->edac_check = zynq_check_errors;
+
+ mci->ctl_page_to_phys = NULL;
+
+ zynq_init_csrows(mci);
+}
+
+/**
+ * zynq_mc_probe - Check controller and bind driver.
+ * @pdev: platform device.
+ *
+ * Probe a specific controller instance for binding with the driver.
+ *
+ * Return: 0 if the controller instance was successfully bound to the
+ * driver; otherwise, < 0 on error.
+ */
+static int zynq_mc_probe(struct platform_device *pdev)
+{
+ struct edac_mc_layer layers[2];
+ struct zynq_edac_priv *priv;
+ struct mem_ctl_info *mci;
+ void __iomem *baseaddr;
+ int rc;
+
+ baseaddr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(baseaddr))
+ return PTR_ERR(baseaddr);
+
+ if (!zynq_get_ecc_state(baseaddr)) {
+ edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n");
+ return -ENXIO;
+ }
+
+ layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+ layers[0].size = ZYNQ_EDAC_NR_CSROWS;
+ layers[0].is_virt_csrow = true;
+ layers[1].type = EDAC_MC_LAYER_CHANNEL;
+ layers[1].size = ZYNQ_EDAC_NR_CHANS;
+ layers[1].is_virt_csrow = false;
+
+ mci = edac_mc_alloc(EDAC_AUTO_MC_NUM, ARRAY_SIZE(layers), layers,
+ sizeof(struct zynq_edac_priv));
+ if (!mci) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Failed memory allocation for mc instance\n");
+ return -ENOMEM;
+ }
+
+ priv = mci->pvt_info;
+ priv->baseaddr = baseaddr;
+
+ zynq_mc_init(mci, pdev);
+
+ rc = edac_mc_add_mc(mci);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Failed to register with EDAC core\n");
+ goto free_edac_mc;
+ }
+
+ /*
+ * Start capturing the correctable and uncorrectable errors. A write of
+ * 0 starts the counters.
+ */
+ writel(0x0, baseaddr + ZYNQ_ECC_CTRL_OFST);
+
+ return 0;
+
+free_edac_mc:
+ edac_mc_free(mci);
+
+ return rc;
+}
+
+/**
+ * zynq_mc_remove - Unbind driver from controller.
+ * @pdev: Platform device.
+ *
+ * Return: Unconditionally 0
+ */
+static int zynq_mc_remove(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci = platform_get_drvdata(pdev);
+
+ edac_mc_del_mc(&pdev->dev);
+ edac_mc_free(mci);
+
+ return 0;
+}
+
+static const struct of_device_id zynq_edac_match[] = {
+ { .compatible = "xlnx,zynq-ddrc-a05" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, zynq_edac_match);
+
+static struct platform_driver zynq_edac_mc_driver = {
+ .driver = {
+ .name = "zynq-edac",
+ .of_match_table = zynq_edac_match,
+ },
+ .probe = zynq_mc_probe,
+ .remove = zynq_mc_remove,
+};
+module_platform_driver(zynq_edac_mc_driver);
+
+MODULE_AUTHOR("Xilinx Inc");
+MODULE_DESCRIPTION("Zynq DDR ECC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/bt1-pvt.h b/drivers/hwmon/bt1-pvt.h
index 93b8dd5e7c944..64acc79e6af49 100644
--- a/drivers/hwmon/bt1-pvt.h
+++ b/drivers/hwmon/bt1-pvt.h
@@ -166,8 +166,8 @@ struct pvt_sensor_info {
.thres_sts_lo = PVT_INTR_ ##_thres## _LO, \
.thres_sts_hi = PVT_INTR_ ##_thres## _HI, \
.type = _type, \
- .attr_min_alarm = _type## _min, \
- .attr_max_alarm = _type## _max, \
+ .attr_min_alarm = _type## _min_alarm, \
+ .attr_max_alarm = _type## _max_alarm, \
}
/*
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 7cb6b9b864a74..72fc592e1b015 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -580,6 +580,7 @@ if I2C_DESIGNWARE_CORE
config I2C_DESIGNWARE_PLATFORM
tristate "Synopsys DesignWare Platform driver"
depends on (ACPI && COMMON_CLK) || !ACPI
+ select MFD_SYSCON if MIPS_BAIKAL_T1
default I2C_DESIGNWARE_CORE
help
If you say yes to this option, support will be included for the
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 426ffec06e221..23ce3b7faace0 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -37,6 +37,70 @@ static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
return clk_get_rate(dev->clk) / HZ_PER_KHZ;
}
+#ifdef CONFIG_OF
+#define BT1_I2C_CTL 0x100
+#define BT1_I2C_CTL_ADDR_MASK GENMASK(7, 0)
+#define BT1_I2C_CTL_WR BIT(8)
+#define BT1_I2C_CTL_GO BIT(31)
+#define BT1_I2C_DI 0x104
+#define BT1_I2C_DO 0x108
+
+static int bt1_i2c_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct dw_i2c_dev *dev = context;
+ int ret;
+
+ /*
+ * Note these methods shouldn't ever fail because the system controller
+ * registers are memory mapped. We check the return value just in case.
+ */
+ ret = regmap_write(dev->sysmap, BT1_I2C_CTL,
+ BT1_I2C_CTL_GO | (reg & BT1_I2C_CTL_ADDR_MASK));
+ if (ret)
+ return ret;
+
+ return regmap_read(dev->sysmap, BT1_I2C_DO, val);
+}
+
+static int bt1_i2c_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct dw_i2c_dev *dev = context;
+ int ret;
+
+ ret = regmap_write(dev->sysmap, BT1_I2C_DI, val);
+ if (ret)
+ return ret;
+
+ return regmap_write(dev->sysmap, BT1_I2C_CTL,
+ BT1_I2C_CTL_GO | BT1_I2C_CTL_WR | (reg & BT1_I2C_CTL_ADDR_MASK));
+}
+
+static const struct regmap_config bt1_i2c_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+ .reg_read = bt1_i2c_read,
+ .reg_write = bt1_i2c_write,
+ .max_register = DW_IC_COMP_TYPE,
+};
+
+static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
+{
+ dev->sysmap = syscon_node_to_regmap(dev->dev->of_node->parent);
+ if (IS_ERR(dev->sysmap))
+ return PTR_ERR(dev->sysmap);
+
+ dev->map = devm_regmap_init(dev->dev, NULL, dev, &bt1_i2c_cfg);
+ return PTR_ERR_OR_ZERO(dev->map);
+}
+#else
+static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
+{
+ return -ENODEV;
+}
+#endif
+
static int dw_i2c_get_parent_regmap(struct dw_i2c_dev *dev)
{
dev->map = dev_get_regmap(dev->dev->parent, NULL);
@@ -61,6 +125,8 @@ static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev)
if (device_is_compatible(dev->dev, "intel,xe-i2c"))
return dw_i2c_get_parent_regmap(dev);
+ else if (device_is_compatible(dev->dev, "baikal,bt1-sys-i2c"))
+ return bt1_i2c_request_regs(dev);
switch (dev->flags & MODEL_MASK) {
case MODEL_WANGXUN_SP:
@@ -252,6 +318,7 @@ static void dw_i2c_plat_remove(struct platform_device *pdev)
}
static const struct of_device_id dw_i2c_of_match[] = {
+ { .compatible = "baikal,bt1-sys-i2c" },
{ .compatible = "mobileye,eyeq6lplus-i2c" },
{ .compatible = "mscc,ocelot-i2c" },
{ .compatible = "snps,designware-i2c" },
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 07088d03dbab5..9d8c9347893f8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -10,6 +10,8 @@ config STMMAC_ETH
select PHYLINK
select CRC32
select RESET_CONTROLLER
+ select GPIOLIB
+ select GPIOLIB_IRQCHIP
select NET_DEVLINK
help
This is the driver for the Ethernet IPs built around a
@@ -68,6 +70,15 @@ config DWMAC_ANARION
This selects the Anarion SoC glue layer support for the stmmac driver.
+config DWMAC_BT1
+ tristate "Baikal-T1 GMAC/xGMAC support"
+ depends on OF && (MIPS_BAIKAL_T1 || COMPILE_TEST)
+ help
+ Support for Baikal-T1 (x)GMAC Ethernet controller.
+
+ This selects the Baikal-T1 platform specific glue layer of the
+ STMMAC driver.
+
config DWMAC_EIC7700
tristate "Support for Eswin eic7700 ethernet driver"
depends on OF && HAS_DMA && ARCH_ESWIN || COMPILE_TEST
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index c9263987ef8d2..a63522ee0b385 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -1,19 +1,20 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_STMMAC_ETH) += stmmac.o
-stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
- chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
+stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o \
+ dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o dwmac4_descs.o \
dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5.o hwif.o \
stmmac_tc.o dwxgmac2_core.o dwxgmac2_dma.o dwxgmac2_descs.o \
stmmac_xdp.o stmmac_est.o stmmac_fpe.o stmmac_vlan.o \
- stmmac_pcs.o $(stmmac-y)
+ stmmac_pcs.o stmmac_gpio.o $(stmmac-y)
stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o
# Ordering matters. Generic driver must be last.
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o
+obj-$(CONFIG_DWMAC_BT1) += dwmac-bt1.o
obj-$(CONFIG_DWMAC_EIC7700) += dwmac-eic7700.o
obj-$(CONFIG_DWMAC_INGENIC) += dwmac-ingenic.o
obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
deleted file mode 100644
index 37f9417c7c0e0..0000000000000
--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
+++ /dev/null
@@ -1,166 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*******************************************************************************
- Specialised functions for managing Chained mode
-
- Copyright(C) 2011 STMicroelectronics Ltd
-
- It defines all the functions used to handle the normal/enhanced
- descriptors in case of the DMA is configured to work in chained or
- in ring mode.
-
-
- Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
-*******************************************************************************/
-
-#include "stmmac.h"
-
-static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
- int csum)
-{
- unsigned int nopaged_len = skb_headlen(skb);
- struct stmmac_priv *priv = tx_q->priv_data;
- unsigned int entry = tx_q->cur_tx;
- unsigned int bmax, buf_len, des2;
- unsigned int i = 1, len;
- struct dma_desc *desc;
-
- desc = tx_q->dma_tx + entry;
-
- if (priv->plat->enh_desc)
- bmax = BUF_SIZE_8KiB;
- else
- bmax = BUF_SIZE_2KiB;
-
- buf_len = min_t(unsigned int, nopaged_len, bmax);
- len = nopaged_len - buf_len;
-
- des2 = dma_map_single(priv->device, skb->data,
- buf_len, DMA_TO_DEVICE);
- desc->des2 = cpu_to_le32(des2);
- if (dma_mapping_error(priv->device, des2))
- return -1;
- tx_q->tx_skbuff_dma[entry].buf = des2;
- tx_q->tx_skbuff_dma[entry].len = buf_len;
- /* do not close the descriptor and do not set own bit */
- stmmac_prepare_tx_desc(priv, desc, 1, buf_len, csum, STMMAC_CHAIN_MODE,
- 0, false, skb->len);
-
- while (len != 0) {
- tx_q->tx_skbuff[entry] = NULL;
- entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
- desc = tx_q->dma_tx + entry;
-
- if (len > bmax) {
- des2 = dma_map_single(priv->device,
- (skb->data + bmax * i),
- bmax, DMA_TO_DEVICE);
- desc->des2 = cpu_to_le32(des2);
- if (dma_mapping_error(priv->device, des2))
- return -1;
- tx_q->tx_skbuff_dma[entry].buf = des2;
- tx_q->tx_skbuff_dma[entry].len = bmax;
- stmmac_prepare_tx_desc(priv, desc, 0, bmax, csum,
- STMMAC_CHAIN_MODE, 1, false, skb->len);
- len -= bmax;
- i++;
- } else {
- des2 = dma_map_single(priv->device,
- (skb->data + bmax * i), len,
- DMA_TO_DEVICE);
- desc->des2 = cpu_to_le32(des2);
- if (dma_mapping_error(priv->device, des2))
- return -1;
- tx_q->tx_skbuff_dma[entry].buf = des2;
- tx_q->tx_skbuff_dma[entry].len = len;
- /* last descriptor can be set now */
- stmmac_prepare_tx_desc(priv, desc, 0, len, csum,
- STMMAC_CHAIN_MODE, 1, true, skb->len);
- len = 0;
- }
- }
-
- tx_q->cur_tx = entry;
-
- return entry;
-}
-
-static bool is_jumbo_frm(unsigned int len, bool enh_desc)
-{
- bool ret = false;
-
- if ((enh_desc && (len > BUF_SIZE_8KiB)) ||
- (!enh_desc && (len > BUF_SIZE_2KiB)))
- ret = true;
-
- return ret;
-}
-
-static void init_dma_chain(void *des, dma_addr_t phy_addr,
- unsigned int size, unsigned int extend_desc)
-{
- /*
- * In chained mode the des3 points to the next element in the ring.
- * The latest element has to point to the head.
- */
- int i;
- dma_addr_t dma_phy = phy_addr;
-
- if (extend_desc) {
- struct dma_extended_desc *p = (struct dma_extended_desc *)des;
- for (i = 0; i < (size - 1); i++) {
- dma_phy += sizeof(struct dma_extended_desc);
- p->basic.des3 = cpu_to_le32((unsigned int)dma_phy);
- p++;
- }
- p->basic.des3 = cpu_to_le32((unsigned int)phy_addr);
-
- } else {
- struct dma_desc *p = (struct dma_desc *)des;
- for (i = 0; i < (size - 1); i++) {
- dma_phy += sizeof(struct dma_desc);
- p->des3 = cpu_to_le32((unsigned int)dma_phy);
- p++;
- }
- p->des3 = cpu_to_le32((unsigned int)phy_addr);
- }
-}
-
-static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p)
-{
- struct stmmac_priv *priv = rx_q->priv_data;
-
- if (priv->hwts_rx_en && !priv->extend_desc)
- /* NOTE: Device will overwrite des3 with timestamp value if
- * 1588-2002 time stamping is enabled, hence reinitialize it
- * to keep explicit chaining in the descriptor.
- */
- p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy +
- (((rx_q->dirty_rx) + 1) %
- priv->dma_conf.dma_rx_size) *
- sizeof(struct dma_desc)));
-}
-
-static void clean_desc3(struct stmmac_tx_queue *tx_q, struct dma_desc *p)
-{
- struct stmmac_priv *priv = tx_q->priv_data;
- unsigned int entry = tx_q->dirty_tx;
-
- if (tx_q->tx_skbuff_dma[entry].last_segment && !priv->extend_desc &&
- priv->hwts_tx_en)
- /* NOTE: Device will overwrite des3 with timestamp value if
- * 1588-2002 time stamping is enabled, hence reinitialize it
- * to keep explicit chaining in the descriptor.
- */
- p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy +
- ((tx_q->dirty_tx + 1) %
- priv->dma_conf.dma_tx_size))
- * sizeof(struct dma_desc)));
-}
-
-const struct stmmac_mode_ops chain_mode_ops = {
- .init = init_dma_chain,
- .is_jumbo_frm = is_jumbo_frm,
- .jumbo_frm = jumbo_frm,
- .refill_desc3 = refill_desc3,
- .clean_desc3 = clean_desc3,
-};
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index d26e8a0630224..a549dd82eafa5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -158,7 +158,6 @@ struct stmmac_extra_stats {
unsigned long fatal_bus_error_irq;
/* Tx/Rx IRQ Events */
unsigned long rx_early_irq;
- unsigned long threshold;
unsigned long irq_receive_pmt_irq_n;
/* MMC info */
unsigned long mmc_tx_irq_n;
@@ -190,6 +189,7 @@ struct stmmac_extra_stats {
unsigned long ptp_frame_type;
unsigned long ptp_ver;
unsigned long timestamp_dropped;
+ unsigned long arp_pkt_ofld;
unsigned long av_pkt_rcvd;
unsigned long av_tagged_pkt_rcvd;
unsigned long vlan_tag_priority_val;
@@ -278,6 +278,7 @@ struct stmmac_safety_stats {
#define FLOW_AUTO (FLOW_TX | FLOW_RX)
/* PCS defines */
+#define STMMAC_PCS_RGMII (1 << 0)
#define STMMAC_PCS_SGMII (1 << 1)
#define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */
@@ -323,9 +324,10 @@ struct stmmac_safety_stats {
#define PHY_INTF_SEL_SMII 6
#define PHY_INTF_SEL_REVMII 7
-/* XGMAC uses a different encoding - from the AgileX5 documentation */
-#define PHY_INTF_GMII 0
+/* XGMAC uses a different encoding */
+#define PHY_INTF_XGMII 0
#define PHY_INTF_RGMII 1
+#define PHY_INTF_RMII 2
/* MSI defines */
#define STMMAC_MSI_VEC_MAX 32
@@ -361,8 +363,14 @@ enum rx_frame_status {
discard_frame = 0x1,
csum_none = 0x2,
llc_snap = 0x4,
- dma_own = 0x8,
- rx_not_ls = 0x10,
+ arp_done = 0x8,
+ dma_own = 0x10,
+ rx_not_ls = 0x20,
+ len_err = 0x40, /* Under/Oversized frames */
+ csum_err = 0x80, /* Control sums check failure */
+ proto_err = 0x100, /* L1 protocol failures */
+ cutoff_err = 0x200, /* Frame' cut off due to HW failures */
+ rx_err = len_err | csum_err | proto_err | cutoff_err,
};
/* Tx status */
@@ -371,14 +379,17 @@ enum tx_frame_status {
tx_not_ls = 0x1,
tx_err = 0x2,
tx_dma_own = 0x4,
- tx_err_bump_tc = 0x8,
};
enum dma_irq_status {
- tx_hard_error = 0x1,
- tx_hard_error_bump_tc = 0x2,
- handle_rx = 0x4,
- handle_tx = 0x8,
+ handle_rx = 0x1,
+ handle_tx = 0x2,
+ io_fatal_error = 0x4,
+ rx_hard_error = 0x8,
+ tx_hard_error = 0x10,
+ rx_ovf_error = 0x20,
+ tx_unf_error = 0x40,
+ tx_soft_stop = 0x80,
};
enum dma_irq_dir {
@@ -387,6 +398,15 @@ enum dma_irq_dir {
DMA_DIR_RXTX = 0x3,
};
+enum mtl_irq_status {
+ CORE_IRQ_TX_PATH_IN_LPI_MODE = 0x1,
+ CORE_IRQ_TX_PATH_EXIT_LPI_MODE = 0x2,
+ CORE_IRQ_RX_PATH_IN_LPI_MODE = 0x4,
+ CORE_IRQ_RX_PATH_EXIT_LPI_MODE = 0x8,
+ CORE_IRQ_MTL_RX_OVERFLOW = 0x100,
+ CORE_IRQ_MTL_TX_UNDERFLOW = 0x200,
+};
+
enum request_irq_err {
REQ_IRQ_ERR_ALL,
REQ_IRQ_ERR_TX,
@@ -399,12 +419,6 @@ enum request_irq_err {
REQ_IRQ_ERR_NO,
};
-/* EEE and LPI defines */
-#define CORE_IRQ_TX_PATH_IN_LPI_MODE (1 << 0)
-#define CORE_IRQ_TX_PATH_EXIT_LPI_MODE (1 << 1)
-#define CORE_IRQ_RX_PATH_IN_LPI_MODE (1 << 2)
-#define CORE_IRQ_RX_PATH_EXIT_LPI_MODE (1 << 3)
-
/* FPE defines */
#define FPE_EVENT_UNKNOWN 0
#define FPE_EVENT_TRSP BIT(0)
@@ -412,8 +426,6 @@ enum request_irq_err {
#define FPE_EVENT_RRSP BIT(2)
#define FPE_EVENT_RVER BIT(3)
-#define CORE_IRQ_MTL_RX_OVERFLOW BIT(8)
-
/* DMA HW capabilities */
struct dma_features {
unsigned int mbps_10_100;
@@ -519,11 +531,9 @@ struct dma_features {
u8 actphyif;
};
-/* RX Buffer size must be multiple of 4/8/16 bytes */
-#define BUF_SIZE_16KiB 16368
-#define BUF_SIZE_8KiB 8188
-#define BUF_SIZE_4KiB 4096
-#define BUF_SIZE_2KiB 2048
+/* RX Buffer size alignment is of 4/8/16 bytes */
+#define STMMAC_RX_BUF_ALIGN(x) ALIGN_DOWN(x, 16)
+#define STMMAC_RX_BUF_ADJUST(x) ALIGN(x, 16)
/* Power Down and WOL */
#define PMT_NOT_SUPPORTED 0
@@ -569,7 +579,12 @@ void stmmac_axi_blen_to_mask(u32 *regval, const u32 *blen, size_t len);
#define STMMAC_CHAIN_MODE 0x1
#define STMMAC_RING_MODE 0x2
-#define JUMBO_LEN 9000
+/* MAC MTU constraint */
+#define STMMAC_MTU_GIANT (SZ_16K - ETH_HLEN - 2*VLAN_HLEN - ETH_FCS_LEN - 1)
+#define STMMAC_MTU_JUMBO 9000
+#define STMMAC_MTU_NORMAL ETH_DATA_LEN
+#define XGMAC_JUMBO_LEN STMMAC_MTU_GIANT
+#define JUMBO_LEN STMMAC_MTU_JUMBO
/* Receive Side Scaling */
#define STMMAC_RSS_HASH_KEY_SIZE 40
@@ -581,6 +596,16 @@ void stmmac_axi_blen_to_mask(u32 *regval, const u32 *blen, size_t len);
#define STMMAC_VLAN_INSERT 0x2
#define STMMAC_VLAN_REPLACE 0x3
+/* AXI ACE */
+#define STMMAC_AXI_ACE_AR_WbNa 0xb
+#define STMMAC_AXI_ACE_AW_WbNa 0x7
+#define STMMAC_AXI_ACE_D_NONS 0x0
+#define STMMAC_AXI_ACE_D_INNS 0x1
+#define STMMAC_AXI_ACE_D_OUTS 0x2
+#define STMMAC_AXI_ACE_D_SYS 0x3
+#define STMMAC_AXI_ACE(_dev, _reg, _val) \
+ (FIELD_PREP(_dev ## _AXI_ ## _reg, STMMAC_AXI_ACE_ ## _val))
+
struct mac_device_info;
struct mac_link {
@@ -619,12 +644,12 @@ struct mac_device_info {
const struct stmmac_ops *mac;
const struct stmmac_desc_ops *desc;
const struct stmmac_dma_ops *dma;
- const struct stmmac_mode_ops *mode;
const struct stmmac_hwtimestamp *ptp;
const struct stmmac_tc_ops *tc;
const struct stmmac_mmc_ops *mmc;
const struct stmmac_est_ops *est;
const struct stmmac_vlan_ops *vlan;
+ const struct stmmac_gpio_ops *gpio;
struct dw_xpcs *xpcs;
struct phylink_pcs *phylink_pcs;
struct mii_regs mii; /* MII register Addresses */
@@ -633,14 +658,15 @@ struct mac_device_info {
unsigned int multicast_filter_bins;
unsigned int unicast_filter_entries;
unsigned int mcast_bits_log2;
- unsigned int rx_csum;
unsigned int pcs;
unsigned int xlgmac;
unsigned int num_vlan;
+ u16 vlan_hash;
+ int vlan_ctags;
+ int vlan_stags;
u32 vlan_filter[32];
bool vlan_fail_q_en;
u8 vlan_fail_q;
- bool hw_vlan_en;
bool reverse_sgmii_enable;
/* This spinlock protects read-modify-write of the interrupt
@@ -672,6 +698,4 @@ void stmmac_dwmac4_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int high, unsigned int low);
void stmmac_dwmac4_set_mac(void __iomem *ioaddr, bool enable);
-void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);
-
#endif /* __COMMON_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h
index e62e2ebcf2733..48fb1cee663f5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/descs.h
+++ b/drivers/net/ethernet/stmicro/stmmac/descs.h
@@ -23,6 +23,7 @@
#define RDES0_FRAME_TYPE BIT(5)
#define RDES0_COLLISION BIT(6)
#define RDES0_IPC_CSUM_ERROR BIT(7)
+#define RDES0_GIANT_FRAME_ERROR RDES0_IPC_CSUM_ERROR
#define RDES0_LAST_DESCRIPTOR BIT(8)
#define RDES0_FIRST_DESCRIPTOR BIT(9)
#define RDES0_VLAN_TAG BIT(10)
@@ -101,6 +102,7 @@
#define ETDES0_ERROR_SUMMARY BIT(15)
#define ETDES0_IP_HEADER_ERROR BIT(16)
#define ETDES0_TIME_STAMP_STATUS BIT(17)
+#define ETDES0_VLIC_MASK GENMASK(19, 18)
#define ETDES0_SECOND_ADDRESS_CHAINED BIT(20)
#define ETDES0_END_RING BIT(21)
#define ETDES0_CHECKSUM_INSERTION_MASK GENMASK(23, 22)
@@ -163,6 +165,8 @@ struct dma_extended_desc {
__le32 des6; /* Tx/Rx Timestamp Low */
__le32 des7; /* Tx/Rx Timestamp High */
};
+#define to_dma_extended_desc(_basic) \
+ container_of(_basic, struct dma_extended_desc, basic)
/* Enhanced descriptor for TBS */
struct dma_edesc {
diff --git a/drivers/net/ethernet/stmicro/stmmac/descs_com.h b/drivers/net/ethernet/stmicro/stmmac/descs_com.h
index 9d1a94a4fa49c..5eba3182b7a95 100644
--- a/drivers/net/ethernet/stmicro/stmmac/descs_com.h
+++ b/drivers/net/ethernet/stmicro/stmmac/descs_com.h
@@ -19,17 +19,41 @@
/* Specific functions used for Ring mode */
/* Enhanced descriptors */
-static inline void ehn_desc_rx_set_on_ring(struct dma_desc *p, int end,
+static inline void enh_desc_rx_set_on_ring(struct dma_desc *p, int end,
int bfsize)
{
- if (bfsize == BUF_SIZE_16KiB)
- p->des1 |= cpu_to_le32(FIELD_PREP(ERDES1_BUFFER2_SIZE_MASK,
- BUF_SIZE_8KiB));
+ if (bfsize > STMMAC_RX_BUF_ALIGN(ERDES1_BUFFER1_SIZE_MASK)) {
+ u32 buf1_len = STMMAC_RX_BUF_ALIGN(ERDES1_BUFFER1_SIZE_MASK);
+ u32 buf2_len = FIELD_PREP(ERDES1_BUFFER2_SIZE_MASK,
+ bfsize - buf1_len);
+
+ p->des1 |= cpu_to_le32(buf1_len | buf2_len);
+ } else {
+ p->des1 |= cpu_to_le32(FIELD_PREP(ERDES1_BUFFER1_SIZE_MASK, bfsize));
+ }
if (end)
p->des1 |= cpu_to_le32(ERDES1_END_RING);
}
+static inline void enh_desc_rx_set_buf_on_ring(struct dma_desc *p, int bfsize)
+{
+ if (bfsize > STMMAC_RX_BUF_ALIGN(ERDES1_BUFFER1_SIZE_MASK)) {
+ u32 buf1_len = STMMAC_RX_BUF_ALIGN(ERDES1_BUFFER1_SIZE_MASK);
+
+ /* Note DMA will ignore the data-bus unaligned offset of the
+ * non-first Rx buffers.
+ */
+ p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + buf1_len);
+ }
+}
+
+static inline unsigned int enh_desc_rx_desc_len_on_ring(void)
+{
+ return STMMAC_RX_BUF_ALIGN(FIELD_MAX(ERDES1_BUFFER1_SIZE_MASK)) +
+ STMMAC_RX_BUF_ALIGN(FIELD_MAX(ERDES1_BUFFER2_SIZE_MASK));
+}
+
static inline void enh_desc_end_tx_desc_on_ring(struct dma_desc *p, int end)
{
if (end)
@@ -38,37 +62,59 @@ static inline void enh_desc_end_tx_desc_on_ring(struct dma_desc *p, int end)
p->des0 &= cpu_to_le32(~ETDES0_END_RING);
}
-/* The maximum buffer 1 size is 8KiB - 1. However, we limit to 4KiB. */
static inline void enh_set_tx_desc_len_on_ring(struct dma_desc *p, int len)
{
- unsigned int buffer1_max_length = BUF_SIZE_4KiB;
+ if (unlikely(len > ETDES1_BUFFER1_SIZE_MASK)) {
+ u32 buf2_len = FIELD_PREP(ETDES1_BUFFER2_SIZE_MASK,
+ len - ETDES1_BUFFER1_SIZE_MASK);
- if (unlikely(len > buffer1_max_length)) {
- p->des1 |= cpu_to_le32(FIELD_PREP(ETDES1_BUFFER2_SIZE_MASK,
- len - buffer1_max_length) |
- FIELD_PREP(ETDES1_BUFFER1_SIZE_MASK,
- buffer1_max_length));
+ p->des1 |= cpu_to_le32(ETDES1_BUFFER1_SIZE_MASK | buf2_len);
+ p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + ETDES1_BUFFER1_SIZE_MASK);
} else {
- p->des1 |= cpu_to_le32(FIELD_PREP(ETDES1_BUFFER1_SIZE_MASK,
- len));
+ p->des1 |= cpu_to_le32(FIELD_PREP(ETDES1_BUFFER1_SIZE_MASK, len));
}
}
+static inline unsigned int enh_desc_tx_desc_len_on_ring(void)
+{
+ return FIELD_MAX(ETDES1_BUFFER1_SIZE_MASK) +
+ FIELD_MAX(ETDES1_BUFFER2_SIZE_MASK);
+}
+
/* Normal descriptors */
static inline void ndesc_rx_set_on_ring(struct dma_desc *p, int end, int bfsize)
{
- if (bfsize >= BUF_SIZE_2KiB) {
- int bfsize2;
+ if (bfsize > STMMAC_RX_BUF_ALIGN(RDES1_BUFFER1_SIZE_MASK)) {
+ u32 buf1_len = STMMAC_RX_BUF_ALIGN(RDES1_BUFFER1_SIZE_MASK);
+ u32 buf2_len = FIELD_PREP(RDES1_BUFFER2_SIZE_MASK, bfsize - buf1_len);
- bfsize2 = min(bfsize - BUF_SIZE_2KiB + 1, BUF_SIZE_2KiB - 1);
- p->des1 |= cpu_to_le32(FIELD_PREP(RDES1_BUFFER2_SIZE_MASK,
- bfsize2));
+ p->des1 |= cpu_to_le32(buf1_len | buf2_len);
+ } else {
+ p->des1 |= cpu_to_le32(FIELD_PREP(RDES1_BUFFER1_SIZE_MASK, bfsize));
}
if (end)
p->des1 |= cpu_to_le32(RDES1_END_RING);
}
+static inline void ndesc_rx_set_buf_on_ring(struct dma_desc *p, int bfsize)
+{
+ if (bfsize > STMMAC_RX_BUF_ALIGN(RDES1_BUFFER1_SIZE_MASK)) {
+ u32 buf1_len = STMMAC_RX_BUF_ALIGN(RDES1_BUFFER1_SIZE_MASK);
+
+ /* Note DMA will ignore the data-bus unaligned offset of the
+ * non-first Rx buffers.
+ */
+ p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + buf1_len);
+ }
+}
+
+static inline unsigned int ndesc_rx_desc_len_on_ring(void)
+{
+ return STMMAC_RX_BUF_ALIGN(FIELD_MAX(RDES1_BUFFER1_SIZE_MASK)) +
+ STMMAC_RX_BUF_ALIGN(FIELD_MAX(RDES1_BUFFER2_SIZE_MASK));
+}
+
static inline void ndesc_end_tx_desc_on_ring(struct dma_desc *p, int end)
{
if (end)
@@ -77,33 +123,55 @@ static inline void ndesc_end_tx_desc_on_ring(struct dma_desc *p, int end)
p->des1 &= cpu_to_le32(~TDES1_END_RING);
}
-/* The maximum buffer 1 size is 2KiB - 1, limited by the mask width */
static inline void norm_set_tx_desc_len_on_ring(struct dma_desc *p, int len)
{
- unsigned int buffer1_max_length = BUF_SIZE_2KiB - 1;
+ if (unlikely(len > TDES1_BUFFER1_SIZE_MASK)) {
+ u32 buf2_len = FIELD_PREP(TDES1_BUFFER2_SIZE_MASK,
+ len - TDES1_BUFFER1_SIZE_MASK);
- if (unlikely(len > buffer1_max_length)) {
- p->des1 |= cpu_to_le32(FIELD_PREP(TDES1_BUFFER2_SIZE_MASK,
- len - buffer1_max_length) |
- FIELD_PREP(TDES1_BUFFER1_SIZE_MASK,
- buffer1_max_length));
+ p->des1 |= cpu_to_le32(TDES1_BUFFER1_SIZE_MASK | buf2_len);
+ p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + TDES1_BUFFER1_SIZE_MASK);
} else {
- p->des1 |= cpu_to_le32(FIELD_PREP(TDES1_BUFFER1_SIZE_MASK,
- len));
+ p->des1 |= cpu_to_le32(FIELD_PREP(TDES1_BUFFER1_SIZE_MASK, len));
}
}
+static inline unsigned int ndesc_tx_desc_len_on_ring(void)
+{
+ return FIELD_MAX(TDES1_BUFFER1_SIZE_MASK) +
+ FIELD_MAX(TDES1_BUFFER2_SIZE_MASK);
+}
+
/* Specific functions used for Chain mode */
/* Enhanced descriptors */
-static inline void ehn_desc_rx_set_on_chain(struct dma_desc *p)
+static inline void enh_desc_rx_set_on_chain(struct dma_desc *p, int bfsize)
{
- p->des1 |= cpu_to_le32(ERDES1_SECOND_ADDRESS_CHAINED);
+ bfsize = umin(bfsize, STMMAC_RX_BUF_ALIGN(ERDES1_BUFFER1_SIZE_MASK));
+ p->des1 |= cpu_to_le32(FIELD_PREP(ERDES1_BUFFER1_SIZE_MASK, bfsize) |
+ ERDES1_SECOND_ADDRESS_CHAINED);
}
-static inline void enh_desc_end_tx_desc_on_chain(struct dma_desc *p)
+static inline void enh_desc_rx_set_buf_on_chain(struct dma_desc *p, dma_addr_t np,
+ bool hwts_rx_en)
+{
+ /* des3 is overwritten with timestamp in case of IEEE 1588-2002 */
+ if (hwts_rx_en)
+ p->des3 = cpu_to_le32(lower_32_bits(np));
+}
+
+static inline unsigned int enh_desc_rx_desc_len_on_chain(void)
+{
+ return STMMAC_RX_BUF_ALIGN(FIELD_MAX(ERDES1_BUFFER1_SIZE_MASK));
+}
+
+static inline void enh_desc_end_tx_desc_on_chain(struct dma_desc *p, dma_addr_t np,
+ bool hwts_tx_en)
{
p->des0 |= cpu_to_le32(ETDES0_SECOND_ADDRESS_CHAINED);
+ /* des3 is overwritten with timestamp in case of IEEE 1588-2002 */
+ if (hwts_tx_en)
+ p->des3 = cpu_to_le32(lower_32_bits(np));
}
static inline void enh_set_tx_desc_len_on_chain(struct dma_desc *p, int len)
@@ -111,19 +179,86 @@ static inline void enh_set_tx_desc_len_on_chain(struct dma_desc *p, int len)
p->des1 |= cpu_to_le32(len & ETDES1_BUFFER1_SIZE_MASK);
}
+static inline unsigned int enh_desc_tx_desc_len_on_chain(void)
+{
+ return FIELD_MAX(ETDES1_BUFFER1_SIZE_MASK);
+}
+
/* Normal descriptors */
-static inline void ndesc_rx_set_on_chain(struct dma_desc *p, int end)
+static inline void ndesc_rx_set_on_chain(struct dma_desc *p, int bfsize)
+{
+ bfsize = umin(bfsize, STMMAC_RX_BUF_ALIGN(RDES1_BUFFER1_SIZE_MASK));
+ p->des1 |= cpu_to_le32(FIELD_PREP(RDES1_BUFFER1_SIZE_MASK, bfsize) |
+ RDES1_SECOND_ADDRESS_CHAINED);
+}
+
+static inline void ndesc_rx_set_buf_on_chain(struct dma_desc *p, dma_addr_t np,
+ bool hwts_rx_en)
{
- p->des1 |= cpu_to_le32(RDES1_SECOND_ADDRESS_CHAINED);
+ /* des3 is overwritten with timestamp in case of IEEE 1588-2002 */
+ if (hwts_rx_en)
+ p->des3 = cpu_to_le32(lower_32_bits(np));
}
-static inline void ndesc_tx_set_on_chain(struct dma_desc *p)
+static inline unsigned int ndesc_rx_desc_len_on_chain(void)
+{
+ return STMMAC_RX_BUF_ALIGN(FIELD_MAX(RDES1_BUFFER1_SIZE_MASK));
+}
+
+static inline void ndesc_end_tx_desc_on_chain(struct dma_desc *p, dma_addr_t np,
+ bool hwts_tx_en)
{
p->des1 |= cpu_to_le32(TDES1_SECOND_ADDRESS_CHAINED);
+ /* des3 is overwritten with timestamp in case of IEEE 1588-2002 */
+ if (hwts_tx_en)
+ p->des3 = cpu_to_le32(lower_32_bits(np));
}
static inline void norm_set_tx_desc_len_on_chain(struct dma_desc *p, int len)
{
p->des1 |= cpu_to_le32(len & TDES1_BUFFER1_SIZE_MASK);
}
+
+static inline unsigned int ndesc_tx_desc_len_on_chain(void)
+{
+ return FIELD_MAX(TDES1_BUFFER1_SIZE_MASK);
+}
+
+/* Functions used for all ring/chain modes, enhanced/normal descriptors */
+
+static inline int com_desc_rx_coe_rdes0(int ipc_err, int type, int payload_err)
+{
+ u32 status = (type << 2 | ipc_err << 1 | payload_err) & 0x7;
+
+ /* bits 5 7 0 | Frame status
+ * ----------------------------------------------------------
+ * 0 0 0 | IEEE 802.3 Type frame (length < 1536 octets)
+ * 1 0 0 | IPv4/6 No CSUM errorS.
+ * 1 0 1 | IPv4/6 CSUM PAYLOAD error
+ * 1 1 0 | IPv4/6 CSUM IP HR error
+ * 1 1 1 | IPv4/6 IP PAYLOAD AND HEADER errorS
+ * 0 0 1 | IPv4/6 unsupported IP PAYLOAD
+ * 0 1 1 | COE bypassed.. no IPv4/6 frame
+ * 0 1 0 | Reserved.
+ */
+ switch (status) {
+ case 0x0:
+ return llc_snap;
+ case 0x1:
+ return csum_none;
+ case 0x3:
+ return csum_none;
+ case 0x4:
+ return good_frame;
+ case 0x5:
+ return csum_err;
+ case 0x6:
+ return csum_err;
+ case 0x7:
+ return csum_err;
+ default:
+ return csum_none;
+ }
+}
+
#endif /* __DESC_COM_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-bt1.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-bt1.c
new file mode 100644
index 0000000000000..d14b233d13c2c
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-bt1.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Baikal-T1 GMAC driver
+ *
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ */
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pcs/pcs-xpcs.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+#include "stmmac.h"
+#include "stmmac_platform.h"
+
+struct bt1_xgmac {
+ struct device *dev;
+};
+
+typedef int (*bt1_xgmac_plat_init)(struct bt1_xgmac *, struct plat_stmmacenet_data *);
+
+static void bt1_gmac_get_interfaces(struct stmmac_priv *priv, void *bsp_priv,
+ unsigned long *interfaces)
+{
+ phy_interface_set_rgmii(interfaces);
+}
+
+static int bt1_gmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
+ phy_interface_t interface, int speed)
+{
+ long rate;
+ int ret;
+
+ if (!phy_interface_mode_is_rgmii(interface))
+ return -EINVAL;
+
+ rate = rgmii_clock(speed);
+ if (rate < 0)
+ return -EINVAL;
+
+ /* Tx clock must be gated to successfully update the rate */
+ clk_disable_unprepare(clk_tx_i);
+
+ /* Clock is running with double rate (5 MHz, 50 MHz and 250 MHz) */
+ ret = clk_set_rate(clk_tx_i, 2 * rate);
+ if (ret)
+ goto err_en_clk;
+
+ return clk_prepare_enable(clk_tx_i);
+
+err_en_clk:
+ clk_prepare_enable(clk_tx_i);
+
+ return ret;
+}
+
+static int bt1_gmac_plat_data_init(struct bt1_xgmac *btxg,
+ struct plat_stmmacenet_data *plat)
+{
+ phy_interface_t interface;
+ int phyif;
+
+ plat->core_type = DWMAC_CORE_GMAC;
+ plat->host_dma_width = 32;
+ plat->tx_fifo_size = SZ_16K;
+ plat->rx_fifo_size = SZ_16K;
+ plat->enh_desc = 1;
+ plat->tx_coe = 1;
+ plat->rx_coe = 1;
+ plat->pmt = 1;
+ plat->unicast_filter_entries = 8;
+ plat->multicast_filter_bins = 0;
+ plat->set_clk_tx_rate = bt1_gmac_set_clk_tx_rate;
+ plat->get_interfaces = bt1_gmac_get_interfaces;
+ plat->mdio_bus_data->needs_reset = true;
+ plat->ngpios = 2;
+
+ phyif = stmmac_get_phy_intf_sel(plat->phy_interface);
+ if (phyif != PHY_INTF_SEL_RGMII) {
+ dev_err(btxg->dev, "Unsupported PHY interface specified\n");
+ return -EINVAL;
+ }
+
+ interface = phy_fix_phy_mode_for_mac_delays(plat->phy_interface, true, false);
+ if (interface == PHY_INTERFACE_MODE_NA) {
+ dev_err(btxg->dev, "Unsupported RGMII mode specified\n");
+ return -EINVAL;
+ }
+
+ plat->phy_interface = interface;
+
+ return 0;
+}
+
+static void bt1_xgmac_get_interfaces(struct stmmac_priv *priv, void *bsp_priv,
+ unsigned long *interfaces)
+{
+ __set_bit(PHY_INTERFACE_MODE_XGMII, interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASEX, interfaces);
+ __set_bit(PHY_INTERFACE_MODE_XAUI, interfaces);
+}
+
+static struct phylink_pcs *bt1_xgmac_select_pcs(struct stmmac_priv *priv,
+ phy_interface_t interface)
+{
+ return xpcs_to_phylink_pcs(priv->hw->xpcs);
+}
+
+static int bt1_xgmac_plat_data_init(struct bt1_xgmac *btxg,
+ struct plat_stmmacenet_data *plat)
+{
+ /* Note Disabling SPH improves the network performance for about
+ * 300Mbit/s. Might be due to the DMA-syncing for two buffers.
+ * Also SPH consumes twice the normal memory especially if there
+ * is a single page-per-buffer allocated.
+ */
+ plat->core_type = DWMAC_CORE_XGMAC;
+ plat->host_dma_width = 40;
+ plat->tx_fifo_size = SZ_32K;
+ plat->rx_fifo_size = SZ_32K;
+ plat->tx_coe = 1;
+ plat->rx_coe = 1;
+ plat->rss_en = 1; /* & cap.rssen */
+ plat->unicast_filter_entries = 8;
+ plat->multicast_filter_bins = 64;
+ plat->select_pcs = bt1_xgmac_select_pcs;
+ plat->get_interfaces = bt1_xgmac_get_interfaces;
+ plat->flags |= STMMAC_FLAG_TSO_EN | /* & cap.tsoen */
+ STMMAC_FLAG_TSO_FULL | /* all channels TSO-capable */
+ STMMAC_FLAG_SPH_DISABLE | /* & cap.sph */
+ STMMAC_FLAG_MULTI_MSI_EN;
+
+ /* Attached XPCS supports 10GBase-KR/KX4 modes */
+ switch (plat->phy_interface) {
+ case PHY_INTERFACE_MODE_XGMII:
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_10GBASEX:
+ case PHY_INTERFACE_MODE_XAUI:
+ break;
+ default:
+ dev_err(btxg->dev, "Unsupported PHY interface specified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bt1_xgmac_data_init(struct platform_device *pdev,
+ struct plat_stmmacenet_data *plat)
+{
+ bt1_xgmac_plat_init plat_init;
+ struct bt1_xgmac *btxg;
+ int ret;
+
+ btxg = devm_kzalloc(&pdev->dev, sizeof(*btxg), GFP_KERNEL);
+ if (!btxg)
+ return -ENOMEM;
+
+ btxg->dev = &pdev->dev;
+
+ plat->bsp_priv = btxg;
+ plat->clk_tx_i = devm_clk_get_enabled(&pdev->dev, "tx");
+ if (IS_ERR(plat->clk_tx_i))
+ return dev_err_probe(&pdev->dev, PTR_ERR(plat->clk_tx_i),
+ "Failed to get tx clock\n");
+
+ plat_init = device_get_match_data(&pdev->dev);
+ if (plat_init) {
+ ret = plat_init(btxg, plat);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int bt1_xgmac_probe(struct platform_device *pdev)
+{
+ struct stmmac_resources stmmac_res;
+ struct plat_stmmacenet_data *plat;
+ int ret;
+
+ ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+ if (ret)
+ return ret;
+
+ plat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat))
+ return dev_err_probe(&pdev->dev, PTR_ERR(plat), "DT config failed\n");
+
+ ret = bt1_xgmac_data_init(pdev, plat);
+ if (ret)
+ return ret;
+
+ ret = devm_stmmac_pltfr_probe(pdev, plat, &stmmac_res);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id bt1_xgmac_match[] = {
+ { .compatible = "baikal,bt1-gmac", .data = (void *)bt1_gmac_plat_data_init },
+ { .compatible = "baikal,bt1-xgmac", .data = (void *)bt1_xgmac_plat_data_init },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bt1_xgmac_match);
+
+static struct platform_driver bt1_xgmac_driver = {
+ .probe = bt1_xgmac_probe,
+ .driver = {
+ .name = "bt1-xgmac",
+ .pm = &stmmac_pltfr_pm_ops,
+ .of_match_table = of_match_ptr(bt1_xgmac_match),
+ },
+};
+module_platform_driver(bt1_xgmac_driver);
+
+MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 GMAC/XGMAC glue driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
index 8152132235834..075a4bdb34437 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
@@ -30,7 +30,9 @@
#define DMA_INTR_ABNORMAL_LOONGSON (DMA_INTR_ENA_AIE_TX_LOONGSON | \
DMA_INTR_ENA_AIE_RX_LOONGSON | \
- DMA_INTR_ENA_FBE | DMA_INTR_ENA_UNE)
+ DMA_INTR_ENA_FBE | DMA_INTR_ENA_RUE | \
+ DMA_INTR_ENA_UNE | DMA_INTR_ENA_OVE | \
+ DMA_INTR_ENA_TJE)
#define DMA_INTR_DEFAULT_MASK_LOONGSON (DMA_INTR_NORMAL_LOONGSON | \
DMA_INTR_ABNORMAL_LOONGSON)
@@ -246,28 +248,37 @@ static int loongson_dwmac_dma_interrupt(struct stmmac_priv *priv,
/* ABNORMAL interrupts */
if (unlikely(abnor_intr_status)) {
if (unlikely(intr_status & DMA_STATUS_UNF)) {
- ret = tx_hard_error_bump_tc;
x->tx_undeflow_irq++;
+ ret = tx_unf_error;
}
- if (unlikely(intr_status & DMA_STATUS_TJT))
+
+ if (unlikely(intr_status & DMA_STATUS_TJT)) {
x->tx_jabber_irq++;
- if (unlikely(intr_status & DMA_STATUS_OVF))
+ ret = tx_soft_stop;
+ }
+
+ if (unlikely(intr_status & DMA_STATUS_OVF)) {
x->rx_overflow_irq++;
- if (unlikely(intr_status & DMA_STATUS_RU))
+ ret = rx_ovf_error;
+ }
+
+ if (unlikely(intr_status & DMA_STATUS_RU)) {
x->rx_buf_unav_irq++;
+ ret = handle_rx;
+ }
+
if (unlikely(intr_status & DMA_STATUS_RPS))
x->rx_process_stopped_irq++;
if (unlikely(intr_status & DMA_STATUS_RWT))
x->rx_watchdog_irq++;
if (unlikely(intr_status & DMA_STATUS_ETI))
x->tx_early_irq++;
- if (unlikely(intr_status & DMA_STATUS_TPS)) {
+ if (unlikely(intr_status & DMA_STATUS_TPS))
x->tx_process_stopped_irq++;
- ret = tx_hard_error;
- }
+
if (unlikely(fb_intr_status)) {
x->fatal_bus_error_irq++;
- ret = tx_hard_error;
+ ret = io_fatal_error;
}
}
/* TX/RX NORMAL interrupts */
@@ -429,14 +440,14 @@ static int loongson_dwmac_dt_config(struct pci_dev *pdev,
plat->bus_id = ret;
res->irq = of_irq_get_byname(np, "macirq");
- if (res->irq < 0) {
+ if (res->irq <= 0) {
dev_err(&pdev->dev, "IRQ macirq not found\n");
ret = -ENODEV;
goto err_put_node;
}
res->wol_irq = of_irq_get_byname(np, "eth_wake_irq");
- if (res->wol_irq < 0) {
+ if (res->wol_irq <= 0) {
dev_info(&pdev->dev,
"IRQ eth_wake_irq not found, using macirq\n");
res->wol_irq = res->irq;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
index b0441a368cb13..382c02d25d8c6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
@@ -1140,7 +1140,8 @@ static int rk_gmac_clk_init(struct plat_stmmacenet_data *plat)
}
if (plat->phy_node && bsp_priv->integrated_phy) {
- bsp_priv->clk_phy = of_clk_get(plat->phy_node, 0);
+ bsp_priv->clk_phy =
+ devm_get_clk_from_child(dev, plat->phy_node, NULL);
ret = PTR_ERR_OR_ZERO(bsp_priv->clk_phy);
if (ret)
return dev_err_probe(dev, ret, "Cannot get PHY clock\n");
@@ -1312,6 +1313,13 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
dev_err(&pdev->dev, "No PHY reset control found.\n");
bsp_priv->phy_reset = NULL;
}
+
+ ret = devm_add_action_or_reset(dev, reset_control_put,
+ bsp_priv->phy_reset);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to add rst put action\n");
+ return ERR_PTR(ret);
+ }
}
}
dev_info(dev, "integrated PHY? (%s).\n",
@@ -1334,7 +1342,6 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
if (ops->init) {
ret = ops->init(bsp_priv);
if (ret) {
- reset_control_put(bsp_priv->phy_reset);
dev_err_probe(dev, ret, "failed to init BSP\n");
return ERR_PTR(ret);
}
@@ -1558,11 +1565,6 @@ static void rk_gmac_exit(struct device *dev, void *bsp_priv_)
struct rk_priv_data *bsp_priv = bsp_priv_;
rk_gmac_powerdown(bsp_priv);
-
- if (priv->plat->phy_node && bsp_priv->integrated_phy)
- clk_put(bsp_priv->clk_phy);
-
- reset_control_put(bsp_priv->phy_reset);
}
static int rk_gmac_probe(struct platform_device *pdev)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index c01b86fd64da1..4e82954920980 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -663,13 +663,18 @@ static void sun8i_dwmac_get_umac_addr(struct mac_device_info *hw,
}
/* caution this function must return non 0 to work */
-static int sun8i_dwmac_rx_ipc_enable(struct mac_device_info *hw)
+static int sun8i_dwmac_rx_ipc_enable(struct mac_device_info *hw, bool enable)
{
void __iomem *ioaddr = hw->pcsr;
u32 v;
v = readl(ioaddr + EMAC_RX_CTL0);
- v |= EMAC_RX_DO_CRC;
+
+ if (enable)
+ v |= EMAC_RX_DO_CRC;
+ else
+ v &= ~EMAC_RX_DO_CRC;
+
writel(v, ioaddr + EMAC_RX_CTL0);
return 1;
@@ -1181,7 +1186,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
/* platform data specifying hardware features and callbacks.
* hardware features were copied from Allwinner drivers.
*/
- plat_dat->rx_coe = STMMAC_RX_COE_TYPE2;
+ plat_dat->rx_coe = 1;
plat_dat->tx_coe = 1;
plat_dat->flags |= STMMAC_FLAG_HAS_SUN8I;
plat_dat->bsp_priv = gmac;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
index 9497b13a57539..a68abd4d0ec35 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
@@ -195,11 +195,8 @@ static int visconti_eth_clock_probe(struct platform_device *pdev,
static void visconti_eth_clock_remove(struct platform_device *pdev)
{
struct visconti_eth *dwmac = get_stmmac_bsp_priv(&pdev->dev);
- struct net_device *ndev = platform_get_drvdata(pdev);
- struct stmmac_priv *priv = netdev_priv(ndev);
clk_disable_unprepare(dwmac->phy_ref_clk);
- clk_disable_unprepare(priv->plat->stmmac_clk);
}
static int visconti_eth_dwmac_probe(struct platform_device *pdev)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100.h b/drivers/net/ethernet/stmicro/stmmac/dwmac100.h
index 547863cb982fd..576cbf766cc1d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100.h
@@ -68,12 +68,6 @@ enum ttc_control {
DMA_CONTROL_OSF = 0x00000004, /* Operate On 2nd Frame */
};
-/* STMAC110 DMA Missed Frame Counter register defines */
-#define DMA_MISSED_FRAME_OVE 0x10000000 /* FIFO Overflow Overflow */
-#define DMA_MISSED_FRAME_OVE_CNTR 0x0ffe0000 /* Overflow Frame Counter */
-#define DMA_MISSED_FRAME_OVE_M 0x00010000 /* Missed Frame Overflow */
-#define DMA_MISSED_FRAME_M_CNTR 0x0000ffff /* Missed Frame Couinter */
-
extern const struct stmmac_dma_ops dwmac100_dma_ops;
#endif /* __DWMAC100_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index 9fe639fb06bb3..2688895b4e7e5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
@@ -19,6 +19,7 @@
#define GMAC_MII_DATA 0x00000014 /* MII Data */
#define GMAC_FLOW_CTRL 0x00000018 /* Flow Control */
#define GMAC_VLAN_TAG 0x0000001c /* VLAN Tag */
+#define GMAC_VERSION 0x00000020 /* GMAC version register */
#define GMAC_DEBUG 0x00000024 /* GMAC debug register */
#define GMAC_INT_STATUS 0x00000038 /* interrupt status register */
@@ -26,6 +27,7 @@
#define GMAC_INT_STATUS_MMCTIS BIT(6)
#define GMAC_INT_STATUS_MMCCSUM BIT(7)
#define GMAC_INT_STATUS_LPIIS BIT(10)
+#define GMAC_INT_STATUS_GPIIS BIT(11)
/* interrupt mask register */
#define GMAC_INT_MASK 0x0000003c
@@ -34,10 +36,12 @@
#define GMAC_INT_DISABLE_PCSAN BIT(2)
#define GMAC_INT_DISABLE_PMT BIT(3)
#define GMAC_INT_DISABLE_TIMESTAMP BIT(9)
+#define GMAC_INT_DISABLE_LPI BIT(10)
#define GMAC_INT_DEFAULT_MASK (GMAC_INT_DISABLE_RGMII | \
GMAC_INT_DISABLE_PCSLINK | \
GMAC_INT_DISABLE_PCSAN | \
- GMAC_INT_DISABLE_TIMESTAMP)
+ GMAC_INT_DISABLE_TIMESTAMP | \
+ GMAC_INT_DISABLE_LPI)
/* PMT Control and Status */
#define GMAC_PMT 0x0000002c
@@ -69,22 +73,31 @@ enum power_event {
#define GMAC_PCS_BASE 0x000000c0 /* PCS register base */
#define GMAC_RGSMIIIS 0x000000d8 /* RGMII/SMII status */
+/* GMAC Watchdog timeout for received frames register */
+#define GMAC_WDT 0x000000dc
+
+/* General Purpose IO register */
+#define GMAC_GPIO 0x000000e0 /* General Purpose IO */
+
+/* MMC registers */
+#define GMAC_MMC_CTRL 0x00000100
+#define GMAC_MMC_RX_INTR 0x00000104
+#define GMAC_MMC_TX_INTR 0x00000108
+
+/* VLAN Tag Inclusion or Replacement register */
+#define GMAC_VLAN_INCL 0x00000584
+
+/* VLAN Hash register */
+#define GMAC_VLAN_HASH_TABLE 0x00000588
+
/* SGMII/RGMII status register */
-#define GMAC_RGSMIIIS_LNKMODE BIT(0)
-#define GMAC_RGSMIIIS_SPEED GENMASK(2, 1)
-#define GMAC_RGSMIIIS_LNKSTS BIT(3)
-#define GMAC_RGSMIIIS_JABTO BIT(4)
-#define GMAC_RGSMIIIS_FALSECARDET BIT(5)
+#define GMAC_RSGMIIIS_MASK GENMASK(15, 0)
#define GMAC_RGSMIIIS_SMIDRXS BIT(16)
-/* LNKMOD */
-#define GMAC_RGSMIIIS_LNKMOD_MASK 0x1
-/* LNKSPEED */
-#define GMAC_RGSMIIIS_SPEED_125 0x2
-#define GMAC_RGSMIIIS_SPEED_25 0x1
-#define GMAC_RGSMIIIS_SPEED_2_5 0x0
/* GMAC Configuration defines */
#define GMAC_CONTROL_2K 0x08000000 /* IEEE 802.3as 2K packets */
+#define GMAC_CONTROL_CST 0x02000000 /* CRC Stripping for Type Frames */
+#define GMAC_CONTROL_WD 0x00800000 /* Disable Watchdog on receive */
#define GMAC_CONTROL_JD 0x00400000 /* Jabber disable */
#define GMAC_CONTROL_BE 0x00200000 /* Frame Burst Enable */
#define GMAC_CONTROL_JE 0x00100000 /* Jumbo frame */
@@ -100,8 +113,7 @@ enum inter_frame_gap {
#define GMAC_CONTROL_DM 0x00000800 /* Duplex Mode */
#define GMAC_CONTROL_IPC 0x00000400 /* Checksum Offload */
-#define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | \
- GMAC_CONTROL_BE | GMAC_CONTROL_DCRS)
+#define GMAC_CORE_INIT (GMAC_CONTROL_PS | GMAC_CONTROL_BE | GMAC_CONTROL_DCRS)
/* GMAC Frame Filter defines */
#define GMAC_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */
@@ -109,12 +121,30 @@ enum inter_frame_gap {
#define GMAC_FRAME_FILTER_PM 0x00000010 /* Pass all multicast */
#define GMAC_FRAME_FILTER_PCF 0x00000080 /* Pass Control frames */
#define GMAC_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */
+#define GMAC_FRAME_FILTER_VTFE 0x00010000 /* VLAN Tag Filter */
#define GMAC_FRAME_FILTER_RA 0x80000000 /* Receive all mode */
/* GMAC FLOW CTRL defines */
#define GMAC_FLOW_CTRL_PT_MASK GENMASK(31, 16) /* Pause Time Mask */
#define GMAC_FLOW_CTRL_UP 0x00000008 /* Unicast pause frame enable */
#define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */
#define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */
+/* VLAN Tag defines */
+#define GMAC_VLAN_VTHM BIT(19)
+#define GMAC_VLAN_ESVL BIT(18)
+#define GMAC_VLAN_ETV BIT(16)
+#define GMAC_VLAN_VID GENMASK(15, 0)
+/* Watchdog timeout for received frames defines */
+#define GMAC_WDT_PWE BIT(16)
+#define GMAC_WDT_WTO GENMASK(13, 0)
+/* General Purpose IO defines */
+#define GMAC_GPIO_GPIS GENMASK(3, 0) /* General Purpose Input Sts */
+#define GMAC_GPIO_GPO GENMASK(11, 8) /* General Purpose Output */
+#define GMAC_GPIO_GPIE GENMASK(19, 16) /* GPI Interrupt Enable */
+#define GMAC_GPIO_GPIT GENMASK(27, 24) /* GPI Type */
+/* VLAN Tag inclusing defines */
+#define GMAC_VLAN_CSVL BIT(19)
+#define GMAC_VLAN_VLC GENMASK(17, 16)
+#define GMAC_VLAN_VLT GENMASK(15, 0)
/* DEBUG Register defines */
/* MTL TxStatus FIFO */
@@ -172,7 +202,7 @@ enum rx_tx_priority_ratio {
/* DMA CRS Control and Status Register Mapping */
/* DMA operation mode defines (start/stop tx/rx are placed in common header)*/
-/* Disable Drop TCP/IP csum error */
+#define DMA_CONTROL_DT 0x04000000 /* Disable Drop TCP/IP csum error */
#define DMA_CONTROL_RSF 0x02000000 /* Receive Store and Forward */
#define DMA_CONTROL_DFF 0x01000000 /* Disaable flushing */
/* Threshold for Activating the FC */
@@ -204,6 +234,11 @@ enum ttc_control {
#define DMA_CONTROL_TC_TX_MASK 0xfffe3fff
#define DMA_CONTROL_EFC 0x00000100
+#define DMA_CONTROL_FEF 0x00000080
+#define DMA_CONTROL_FUF 0x00000040
+#define DMA_CONTROL_DGF 0x00000020
+#define DMA_CONTROL_PEF_MASK (DMA_CONTROL_DT | DMA_CONTROL_FEF | \
+ DMA_CONTROL_FUF | DMA_CONTROL_DGF)
/* Receive flow control activation field
* RFA field in DMA control register, bits 23,10:9
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index af566636fad90..323f4c9588461 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -12,29 +12,36 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#include <linux/bitfield.h>
#include <linux/crc32.h>
#include <linux/slab.h>
#include <linux/ethtool.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/string_choices.h>
#include "stmmac.h"
#include "stmmac_pcs.h"
#include "stmmac_ptp.h"
#include "dwmac1000.h"
+#include "dwmac_dma.h"
+
+static const struct stmmac_pcs_info dwmac1000_pcs_info = {
+ .pcs_offset = GMAC_PCS_BASE,
+ .rgsmii_offset = GMAC_RGSMIIIS,
+ .rgsmii_status_mask = GMAC_RSGMIIIS_MASK,
+ .int_mask = GMAC_INT_DISABLE_RGMII | GMAC_INT_DISABLE_PCSLINK |
+ GMAC_INT_DISABLE_PCSAN,
+};
static int dwmac1000_pcs_init(struct stmmac_priv *priv)
{
- if (!priv->dma_cap.pcs)
- return 0;
-
- return stmmac_integrated_pcs_init(priv, GMAC_PCS_BASE,
- GMAC_INT_DISABLE_PCSLINK |
- GMAC_INT_DISABLE_PCSAN);
+ return stmmac_integrated_pcs_init(priv, &dwmac1000_pcs_info);
}
static void dwmac1000_core_init(struct mac_device_info *hw,
struct net_device *dev)
{
+ struct stmmac_priv *priv = netdev_priv(dev);
void __iomem *ioaddr = hw->pcsr;
int mtu = dev->mtu;
u32 value;
@@ -42,20 +49,32 @@ static void dwmac1000_core_init(struct mac_device_info *hw,
/* Configure GMAC core */
value = readl(ioaddr + GMAC_CONTROL);
- if (mtu > 1500)
- value |= GMAC_CONTROL_2K;
- if (mtu > 2000)
+ /* Frame length limits (giant status(+VLAN):truncated by watchdog) */
+ if (mtu > 9000) /* Rx <= 9018(9022):16383 && Tx <= 16383 */
+ value |= GMAC_CONTROL_JE | GMAC_CONTROL_WD | GMAC_CONTROL_JD;
+ else if (mtu > 1978) /* Rx <= 9018(9022):10240 && Tx <= 10240 */
+ value |= GMAC_CONTROL_JE;
+ else if (priv->synopsys_id < DWMAC_CORE_3_70 && mtu > 1500)
value |= GMAC_CONTROL_JE;
+ else if (mtu > 1500) /* Rx <= 1996(2000):2048 && Tx <= 2048 */
+ value |= GMAC_CONTROL_2K;
+ /* else 64 <= Rx <= 1518(1522):2048 && Tx <= 2048 */
writel(value | GMAC_CORE_INIT, ioaddr + GMAC_CONTROL);
+ /* Over 2K, 3K, ..., 9K (10K and 16K-1) frames will be truncated on Rx */
+ if (mtu > 1500 && mtu <= 9000) {
+ value = ALIGN(mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, SZ_1K);
+ value = FIELD_PREP(GMAC_WDT_WTO, value) | GMAC_WDT_PWE;
+ } else {
+ value = 0;
+ }
+
+ /* Over giant frame watchdog fine-tuning (available since v3.70a) */
+ writel(value, ioaddr + GMAC_WDT);
+
/* Mask GMAC interrupts */
writel(GMAC_INT_DEFAULT_MASK, ioaddr + GMAC_INT_MASK);
-
-#ifdef STMMAC_VLAN_TAG_USED
- /* Tag detection without filtering */
- writel(0x0, ioaddr + GMAC_VLAN_TAG);
-#endif
}
static void dwmac1000_irq_modify(struct mac_device_info *hw, u32 disable,
@@ -72,12 +91,156 @@ static void dwmac1000_irq_modify(struct mac_device_info *hw, u32 disable,
spin_unlock_irqrestore(&hw->irq_ctrl_lock, flags);
}
-static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw)
+/* Stop the DMA and MAC engines (as described in the Stopping and
+ * Starting Transmission chapter of the databook)
+ */
+static int dwmac1000_core_stop(struct mac_device_info *hw)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ const unsigned int maxch = 8;
+ int ret, ch;
+ u32 value;
+
+ /* Disable and clear DMA IRQ */
+ for (ch = 0; ch < maxch; ch++) {
+ writel(0, ioaddr + DMA_CHAN_INTR_ENA(ch));
+ writel(0x0001FFFF, ioaddr + DMA_CHAN_STATUS(ch));
+ }
+
+ /* Disable MAC interrupts. Note the MAC interrupts are enabled by
+ * default after reset, but mask them out anyway so to avoid any
+ * spurious IRQ in the clean up procedure
+ */
+ writel(0x7FF, ioaddr + GMAC_INT_MASK);
+
+ /* Disable DMA transmitter */
+ for (ch = 0; ch < maxch; ch++) {
+ value = readl(ioaddr + DMA_CHAN_CONTROL(ch));
+ value &= ~DMA_CONTROL_ST;
+ writel(value, ioaddr + DMA_CHAN_CONTROL(ch));
+ }
+
+ /* Wait for MAC to stop transmitting the data */
+ ret = readl_poll_timeout(ioaddr + GMAC_DEBUG, value,
+ !(value & GMAC_DEBUG_TPESTS),
+ 1000, 20000);
+ if (ret)
+ return ret;
+
+ /* Disable MAC transmitter and receiver */
+ writel(0, ioaddr + GMAC_CONTROL);
+
+ /* Make sure Tx FIFOs is empty */
+ ret = readl_poll_timeout(ioaddr + GMAC_DEBUG, value,
+ !(value & GMAC_DEBUG_TXFSTS),
+ 1000, 20000);
+ if (ret)
+ return ret;
+
+ /* Wait for MAC to stop receiving the data */
+ ret = readl_poll_timeout(ioaddr + GMAC_DEBUG, value,
+ !(value & GMAC_DEBUG_RPESTS),
+ 1000, 20000);
+ if (ret)
+ return ret;
+
+ /* Make sure Rx FIFOs is empty */
+ ret = readl_poll_timeout(ioaddr + GMAC_DEBUG, value,
+ !(value & GMAC_DEBUG_RXFSTS_MASK),
+ 1000, 20000);
+ if (ret)
+ return ret;
+
+ /* Disable DMA receiver after making sure no data in Rx FIFO */
+ for (ch = 0; ch < maxch; ch++)
+ writel(0, ioaddr + DMA_CHAN_CONTROL(ch));
+
+ return 0;
+}
+
+/* Clean the basic MAC registers up */
+static void dwmac1000_core_clean(struct mac_device_info *hw)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ int i;
+
+ /* Restore default CSRs state */
+ writel(0, ioaddr + GMAC_FRAME_FILTER);
+ writel(0, ioaddr + GMAC_HASH_HIGH);
+ writel(0, ioaddr + GMAC_HASH_LOW);
+ for (i = 0; i < 8; i++)
+ writel(0, ioaddr + GMAC_EXTHASH_BASE + 4 * i);
+ writel(0, ioaddr + GMAC_FLOW_CTRL);
+ writel(0, ioaddr + GMAC_VLAN_TAG);
+ writel(0x80000000, ioaddr + GMAC_PMT);
+ writel(0, ioaddr + LPI_CTRL_STATUS);
+ writel(0x03e80000, ioaddr + LPI_TIMER_CTRL);
+ for (i = 0; i < 128; i++) {
+ writel(0x0000ffff, ioaddr + GMAC_ADDR_HIGH(i));
+ writel(0xffffffff, ioaddr + GMAC_ADDR_LOW(i));
+ }
+ writel(0, ioaddr + GMAC_PCS_BASE);
+ writel(0, ioaddr + GMAC_RGSMIIIS);
+ writel(0, ioaddr + GMAC_WDT);
+ writel(0x1, ioaddr + GMAC_MMC_CTRL);
+ writel(0, ioaddr + GMAC_VLAN_INCL);
+ writel(0, ioaddr + GMAC_VLAN_HASH_TABLE);
+
+ /* IEEE 1588-2002/2008 Timestamps */
+ writel(0, ioaddr + 0xb00);
+ writel(0, ioaddr + 0xb04);
+ writel(0, ioaddr + 0xb10);
+ writel(0, ioaddr + 0xb14);
+ writel(0, ioaddr + 0xb18);
+
+ /* L3/L4 filters (not supported by the driver yet) */
+ writel(0, ioaddr + 0x400);
+ writel(0, ioaddr + 0x430);
+ writel(0, ioaddr + 0x404);
+ writel(0, ioaddr + 0x434);
+ for (i = 0; i < 4; i++)
+ writel(0, ioaddr + 0x410 + 4 * i);
+
+ /* Clear the pending statuses */
+ readl(ioaddr + GMAC_INT_STATUS);
+ readl(ioaddr + GMAC_PMT);
+ readl(ioaddr + LPI_CTRL_STATUS);
+ readl(ioaddr + GMAC_RGSMIIIS);
+ readl(ioaddr + GMAC_MMC_RX_INTR);
+ readl(ioaddr + GMAC_MMC_TX_INTR);
+}
+
+static void dwmac1000_rx_fcs_enable(struct mac_device_info *hw, bool enable)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ u32 value;
+
+ value = readl(ioaddr + GMAC_CONTROL);
+
+ if (enable)
+ value &= ~GMAC_CONTROL_CST;
+ else
+ value |= GMAC_CONTROL_CST;
+
+ writel(value, ioaddr + GMAC_CONTROL);
+}
+
+static int dwmac1000_rx_fcs_status(struct mac_device_info *hw, int snps_id,
+ int status)
+{
+ /* CRC Stripping for Type Frames has been supported since v3.50a */
+ if (snps_id < DWMAC_CORE_3_50 || unlikely(status & llc_snap))
+ return -ENOTSUPP;
+
+ return 0;
+}
+
+static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw, bool enable)
{
void __iomem *ioaddr = hw->pcsr;
u32 value = readl(ioaddr + GMAC_CONTROL);
- if (hw->rx_csum)
+ if (enable)
value |= GMAC_CONTROL_IPC;
else
value &= ~GMAC_CONTROL_IPC;
@@ -141,32 +304,216 @@ static void dwmac1000_set_mchash(void __iomem *ioaddr, u32 *mcfilterbits,
ioaddr + GMAC_EXTHASH_BASE + regs * 4);
}
+static int dwmac1000_vlan_set_tag(struct mac_device_info *hw, u32 queue, u16 vid)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ bool ready;
+ u32 value;
+ int ret;
+
+ /* Wait for any transfer being finished so not to corrupt the pending
+ * outbound frames.
+ */
+ ret = read_poll_timeout_atomic(dwmac_dma_suspended, ready, ready,
+ 100, 50000, false,
+ ioaddr, queue, DMA_DIR_TX);
+ if (ret)
+ return ret;
+
+ value = readl(ioaddr + GMAC_VLAN_INCL) & ~GMAC_VLAN_VLT;
+ value |= FIELD_PREP(GMAC_VLAN_VLT, vid);
+ writel(value, ioaddr + GMAC_VLAN_INCL);
+
+ return 0;
+}
+
+static void dwmac1000_vlan_write_single(struct net_device *dev, u32 data)
+{
+ void __iomem *ioaddr = (void __iomem *)dev->base_addr;
+ u32 val;
+
+ if (!(data & GMAC_VLAN_VID))
+ data |= GMAC_VLAN_VID;
+
+ val = readl(ioaddr + GMAC_VLAN_TAG) & ~GMAC_VLAN_VID;
+ val |= GMAC_VLAN_ETV | (data & GMAC_VLAN_VID);
+
+ writel(val, ioaddr + GMAC_VLAN_TAG);
+}
+
+static int dwmac1000_vlan_add_hw_rx_fltr(struct net_device *dev,
+ struct mac_device_info *hw,
+ __be16 proto, u16 vid)
+{
+ bool is_stag;
+ u32 val;
+
+ if (vid > 4095)
+ return -EINVAL;
+
+ is_stag = proto == htons(ETH_P_8021AD);
+
+ /* For single VLAN filter, VID 0 means VLAN promiscuous */
+ if (vid == 0)
+ return -EINVAL;
+
+ /* Note ESVL is utilized to virtually distinguish the C- and S-Tags
+ * only. The bit is set/cleared in the set_hw_vlan_mode() depending
+ * on the HW-offloaded features requested.
+ */
+ val = vid;
+ if (is_stag)
+ val |= GMAC_VLAN_ESVL;
+
+ if (hw->vlan_filter[0] == val)
+ return 0;
+
+ if (hw->vlan_filter[0] & GMAC_VLAN_VID)
+ return -ENOSPC;
+
+ if (netif_running(dev))
+ dwmac1000_vlan_write_single(dev, val);
+
+ hw->vlan_filter[0] = val;
+
+ return 0;
+}
+
+static int dwmac1000_vlan_del_hw_rx_fltr(struct net_device *dev,
+ struct mac_device_info *hw,
+ __be16 proto, u16 vid)
+{
+ bool is_stag;
+
+ is_stag = proto == htons(ETH_P_8021AD);
+
+ /* Single Rx VLAN Filter */
+ if ((hw->vlan_filter[0] & GMAC_VLAN_VID) != vid)
+ return -ENOENT;
+
+ if (is_stag != !!(hw->vlan_filter[0] & GMAC_VLAN_ESVL))
+ return -ENOENT;
+
+ if (netif_running(dev))
+ dwmac1000_vlan_write_single(dev, 0);
+
+ hw->vlan_filter[0] = 0;
+
+ return 0;
+}
+
+static void dwmac1000_vlan_update_hash(struct mac_device_info *hw, u32 hash,
+ int add_ctags, int add_stags)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ u32 value;
+
+ hw->vlan_hash = hash;
+ writel(hash, ioaddr + GMAC_VLAN_HASH_TABLE);
+
+ value = readl(ioaddr + GMAC_VLAN_TAG) | GMAC_VLAN_ETV;
+
+ if (hash)
+ value |= GMAC_VLAN_VTHM;
+ else
+ value &= ~GMAC_VLAN_VTHM;
+
+ /* Note DW GMAC VLAN filter doesn't have a filter-specific flag to
+ * toggle the S-Tags support. Instead if ESVL flag is set then both
+ * tag types will be considered as valid. But since the bit also
+ * affects the MAC transmitter it is handled in set_hw_vlan_mode().
+ */
+
+ writel(value, ioaddr + GMAC_VLAN_TAG);
+}
+
+static void dwmac1000_vlan_restore_hw_rx_fltr(struct net_device *dev,
+ struct mac_device_info *hw)
+{
+ /* Hash-based Rx VLAN Filter */
+ dwmac1000_vlan_update_hash(hw, hw->vlan_hash, 0, 0);
+
+ /* Single Rx VLAN Filter */
+ dwmac1000_vlan_write_single(dev, hw->vlan_filter[0]);
+}
+
+static void dwmac1000_vlan_set_hw_mode(struct mac_device_info *hw, bool rx_strip,
+ bool rx_ctag, bool rx_stag, bool tx_stag)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ u32 value;
+
+ /* Activate VLAN Tag Rx filters */
+ value = readl(ioaddr + GMAC_FRAME_FILTER);
+ if (rx_ctag || rx_stag)
+ value |= GMAC_FRAME_FILTER_VTFE;
+ else
+ value &= ~GMAC_FRAME_FILTER_VTFE;
+ writel(value, ioaddr + GMAC_FRAME_FILTER);
+
+ /* Activate S-VLAN feature on MAC Tx and Rx */
+ value = readl(ioaddr + GMAC_VLAN_TAG);
+ if (rx_stag || tx_stag)
+ value |= GMAC_VLAN_ESVL;
+ else
+ value &= ~GMAC_VLAN_ESVL;
+ writel(value, ioaddr + GMAC_VLAN_TAG);
+
+ /* Set Tx VLAN Insertion feature (might be unavailable) */
+ value = readl(ioaddr + GMAC_VLAN_INCL);
+ if (tx_stag)
+ value |= GMAC_VLAN_CSVL;
+ else
+ value &= ~GMAC_VLAN_CSVL;
+ writel(value, ioaddr + GMAC_VLAN_INCL);
+}
+
+static u32 dwmac1000_get_num_vlan(void __iomem *ioaddr)
+{
+ u32 val;
+
+ val = readl(ioaddr + GMAC_VERSION);
+ if ((val & GENMASK(7, 0)) >= DWMAC_CORE_3_70)
+ return 1;
+
+ return 0;
+}
+
static void dwmac1000_set_filter(struct mac_device_info *hw,
struct net_device *dev)
{
void __iomem *ioaddr = (void __iomem *)dev->base_addr;
unsigned int value = 0;
- unsigned int perfect_addr_number = hw->unicast_filter_entries;
u32 mc_filter[8];
int mcbitslog2 = hw->mcast_bits_log2;
pr_debug("%s: # mcasts %d, # unicast %d\n", __func__,
netdev_mc_count(dev), netdev_uc_count(dev));
+ value = readl(ioaddr + GMAC_FRAME_FILTER);
+ value &= ~GMAC_FRAME_FILTER_RA;
+ value &= ~GMAC_FRAME_FILTER_HPF;
+ value &= ~GMAC_FRAME_FILTER_PCF;
+ value &= ~GMAC_FRAME_FILTER_PM;
+ value &= ~GMAC_FRAME_FILTER_HMC;
+ value &= ~GMAC_FRAME_FILTER_PR;
+
memset(mc_filter, 0, sizeof(mc_filter));
if (dev->flags & IFF_PROMISC) {
- value = GMAC_FRAME_FILTER_PR | GMAC_FRAME_FILTER_PCF;
+ value |= GMAC_FRAME_FILTER_RA;
+ value |= GMAC_FRAME_FILTER_PR;
+ value |= GMAC_FRAME_FILTER_PCF;
} else if (dev->flags & IFF_ALLMULTI) {
value = GMAC_FRAME_FILTER_PM; /* pass all multi */
} else if (!netdev_mc_empty(dev) && (mcbitslog2 == 0)) {
/* Fall back to all multicast if we've no filter */
- value = GMAC_FRAME_FILTER_PM;
- } else if (!netdev_mc_empty(dev)) {
+ value |= GMAC_FRAME_FILTER_PM;
+ } else if (!netdev_mc_empty(dev) && dev->flags & IFF_MULTICAST) {
struct netdev_hw_addr *ha;
/* Hash filter for multicast */
- value = GMAC_FRAME_FILTER_HMC;
+ value |= GMAC_FRAME_FILTER_HMC;
netdev_for_each_mc_addr(ha, dev) {
/* The upper n bits of the calculated CRC are used to
@@ -189,7 +536,7 @@ static void dwmac1000_set_filter(struct mac_device_info *hw,
dwmac1000_set_mchash(ioaddr, mc_filter, mcbitslog2);
/* Handle multiple unicast addresses (perfect filtering) */
- if (netdev_uc_count(dev) > perfect_addr_number)
+ if (netdev_uc_count(dev) > hw->unicast_filter_entries - 1)
/* Switch to promiscuous mode if more than unicast
* addresses are requested than supported by hardware.
*/
@@ -205,10 +552,9 @@ static void dwmac1000_set_filter(struct mac_device_info *hw,
reg++;
}
- while (reg < perfect_addr_number) {
+ for (; reg < hw->unicast_filter_entries; reg++) {
writel(0, ioaddr + GMAC_ADDR_HIGH(reg));
writel(0, ioaddr + GMAC_ADDR_LOW(reg));
- reg++;
}
}
@@ -216,10 +562,14 @@ static void dwmac1000_set_filter(struct mac_device_info *hw,
/* Enable Receive all mode (to debug filtering_fail errors) */
value |= GMAC_FRAME_FILTER_RA;
#endif
+
+ /* VLAN filtering */
+ if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ dwmac1000_vlan_restore_hw_rx_fltr(dev, hw);
+
writel(value, ioaddr + GMAC_FRAME_FILTER);
}
-
static void dwmac1000_flow_ctrl(struct mac_device_info *hw, unsigned int duplex,
unsigned int fc, unsigned int pause_time,
u32 tx_cnt)
@@ -304,7 +654,7 @@ static int dwmac1000_irq_status(struct stmmac_priv *priv,
x->irq_rx_path_exit_lpi_mode_n++;
}
- if (intr_status & (PCS_ANE_IRQ | PCS_LINK_IRQ))
+ if (intr_status & (PCS_ANE_IRQ | PCS_LINK_IRQ | PCS_RGSMIIIS_IRQ))
stmmac_integrated_pcs_irq(priv, intr_status, x);
return ret;
@@ -333,15 +683,18 @@ static int dwmac1000_set_lpi_mode(struct mac_device_info *hw,
static void dwmac1000_set_eee_pls(struct mac_device_info *hw, int link)
{
void __iomem *ioaddr = hw->pcsr;
- u32 value;
+ u32 value, intr_mask;
value = readl(ioaddr + LPI_CTRL_STATUS);
-
- if (link)
+ intr_mask = readl(ioaddr + GMAC_INT_MASK);
+ if (link) {
value |= LPI_CTRL_STATUS_PLS;
- else
+ intr_mask &= ~GMAC_INT_DISABLE_LPI;
+ } else {
+ intr_mask |= GMAC_INT_DISABLE_LPI;
value &= ~LPI_CTRL_STATUS_PLS;
-
+ }
+ writel(intr_mask, ioaddr + GMAC_INT_MASK);
writel(value, ioaddr + LPI_CTRL_STATUS);
}
@@ -451,11 +804,48 @@ static void dwmac1000_set_mac_loopback(void __iomem *ioaddr, bool enable)
writel(value, ioaddr + GMAC_CONTROL);
}
+static void dwmac1000_gpio_set_ctrl(struct stmmac_priv *priv, u32 gpie,
+ u32 gpit, u32 gpo)
+{
+ u32 value;
+
+ value = FIELD_PREP(GMAC_GPIO_GPO, gpo) |
+ FIELD_PREP(GMAC_GPIO_GPIE, gpie) |
+ FIELD_PREP(GMAC_GPIO_GPIT, gpit);
+
+ writel(value, priv->ioaddr + GMAC_GPIO);
+}
+
+static void dwmac1000_gpio_get_ctrl(struct stmmac_priv *priv, u32 *gpie,
+ u32 *gpit, u32 *gpo)
+{
+ u32 value;
+
+ value = readl(priv->ioaddr + GMAC_GPIO);
+
+ *gpie = FIELD_GET(GMAC_GPIO_GPIE, value);
+ *gpit = FIELD_GET(GMAC_GPIO_GPIT, value);
+ *gpo = FIELD_GET(GMAC_GPIO_GPO, value);
+}
+
+static int dwmac1000_gpio_get_gpis(struct stmmac_priv *priv)
+{
+ u32 value;
+
+ value = readl(priv->ioaddr + GMAC_GPIO);
+
+ return FIELD_GET(GMAC_GPIO_GPIS, value);
+}
+
const struct stmmac_ops dwmac1000_ops = {
.pcs_init = dwmac1000_pcs_init,
.core_init = dwmac1000_core_init,
+ .core_stop = dwmac1000_core_stop,
+ .core_clean = dwmac1000_core_clean,
.irq_modify = dwmac1000_irq_modify,
.set_mac = stmmac_set_mac,
+ .rx_fcs = dwmac1000_rx_fcs_enable,
+ .rx_fcs_status = dwmac1000_rx_fcs_status,
.rx_ipc = dwmac1000_rx_ipc_enable,
.dump_regs = dwmac1000_dump_regs,
.host_irq_status = dwmac1000_irq_status,
@@ -472,6 +862,21 @@ const struct stmmac_ops dwmac1000_ops = {
.set_mac_loopback = dwmac1000_set_mac_loopback,
};
+const struct stmmac_vlan_ops dwmac1000_vlan_ops = {
+ .set_vlan_tag = dwmac1000_vlan_set_tag,
+ .update_vlan_hash = dwmac1000_vlan_update_hash,
+ .add_hw_vlan_rx_fltr = dwmac1000_vlan_add_hw_rx_fltr,
+ .del_hw_vlan_rx_fltr = dwmac1000_vlan_del_hw_rx_fltr,
+ .restore_hw_vlan_rx_fltr = dwmac1000_vlan_restore_hw_rx_fltr,
+ .set_hw_vlan_mode = dwmac1000_vlan_set_hw_mode,
+};
+
+const struct stmmac_gpio_ops dwmac1000_gpio_ops = {
+ .set_ctrl = dwmac1000_gpio_set_ctrl,
+ .get_ctrl = dwmac1000_gpio_get_ctrl,
+ .get_gpis = dwmac1000_gpio_get_gpis,
+};
+
int dwmac1000_setup(struct stmmac_priv *priv)
{
struct mac_device_info *mac = priv->hw;
@@ -502,6 +907,7 @@ int dwmac1000_setup(struct stmmac_priv *priv)
mac->mii.reg_mask = 0x000007C0;
mac->mii.clk_csr_shift = 2;
mac->mii.clk_csr_mask = GENMASK(5, 2);
+ mac->num_vlan = dwmac1000_get_num_vlan(priv->ioaddr);
return 0;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index 3ac7a7949529c..ddceb33dcd40b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -102,6 +102,29 @@ static void dwmac1000_dma_init_tx(struct stmmac_priv *priv,
writel(lower_32_bits(dma_tx_phy), ioaddr + DMA_CHAN_TX_BASE_ADDR(chan));
}
+static void dwmac1000_dma_clean(struct stmmac_priv *priv, void __iomem *ioaddr)
+{
+ const unsigned int maxch = 8;
+ int ch;
+
+ for (ch = 0; ch < maxch; ch++) {
+ /* Restore default CSRs state */
+ writel(0x00020100, ioaddr + DMA_CHAN_BUS_MODE(ch));
+ writel(0, ioaddr + DMA_CHAN_RCV_BASE_ADDR(ch));
+ writel(0, ioaddr + DMA_CHAN_TX_BASE_ADDR(ch));
+ writel(0, ioaddr + DMA_CHAN_CONTROL(ch));
+ writel(0, ioaddr + DMA_CHAN_INTR_ENA(ch));
+ writel(0, ioaddr + DMA_CHAN_RX_WATCHDOG(ch));
+
+ /* Clear the pending statuses */
+ readl(ioaddr + DMA_CHAN_MISSED_FRAME_CTR(ch));
+ writel(0x0007FFFF, ioaddr + DMA_CHAN_STATUS(ch));
+ }
+
+ /* AXI Bus Mode CSR is shared for all channel */
+ writel(0x00110001, ioaddr + DMA_AXI_BUS_MODE);
+}
+
static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
{
csr6 &= ~DMA_CONTROL_RFA_MASK;
@@ -125,7 +148,8 @@ static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
static void dwmac1000_dma_operation_mode_rx(struct stmmac_priv *priv,
void __iomem *ioaddr, int mode,
- u32 channel, int fifosz, u8 qmode)
+ u32 channel, int fifosz, u8 qmode,
+ bool rxall)
{
u32 csr6 = readl(ioaddr + DMA_CHAN_CONTROL(channel));
@@ -146,6 +170,17 @@ static void dwmac1000_dma_operation_mode_rx(struct stmmac_priv *priv,
csr6 |= DMA_CONTROL_RTC_128;
}
+ /* Permit errorneous frames (IP/TCP/UDP csum, overflow, giant, etc) */
+ csr6 &= ~DMA_CONTROL_PEF_MASK;
+ if (rxall) {
+ csr6 |= DMA_CONTROL_DT | DMA_CONTROL_FEF | DMA_CONTROL_FUF;
+ } else {
+ /* Drop giant frames to unify the GMACs semantics (available
+ * since v3.70a if no giant frame status reported via Rx descs).
+ */
+ csr6 |= DMA_CONTROL_DGF;
+ }
+
/* Configure flow control based on rx fifo size */
csr6 = dwmac1000_configure_fc(csr6, fifosz);
@@ -197,6 +232,20 @@ static void dwmac1000_dump_dma_regs(struct stmmac_priv *priv,
readl(ioaddr + DMA_BUS_MODE + i * 4);
}
+static bool dwmac1000_det_hw_vlhash(void __iomem *ioaddr)
+{
+ u32 tmp;
+
+ writel(0xffff, ioaddr + GMAC_VLAN_HASH_TABLE);
+ tmp = readl(ioaddr + GMAC_VLAN_HASH_TABLE);
+ if (tmp == 0xffff) {
+ writel(0x0, ioaddr + GMAC_VLAN_HASH_TABLE);
+ return true;
+ }
+
+ return false;
+}
+
static int dwmac1000_get_hw_feature(void __iomem *ioaddr,
struct dma_features *dma_cap)
{
@@ -238,9 +287,14 @@ static int dwmac1000_get_hw_feature(void __iomem *ioaddr,
dma_cap->number_tx_channel = (hw_cap & DMA_HW_FEAT_TXCHCNT) >> 22;
/* Alternate (enhanced) DESC mode */
dma_cap->enh_desc = (hw_cap & DMA_HW_FEAT_ENHDESSEL) >> 24;
+ /* Source address/VLAN Insertion/Replacement/Deletion Engine */
+ dma_cap->vlins = (hw_cap & DMA_HW_FEAT_SAVLANINS) >> 27;
dma_cap->actphyif = FIELD_GET(DMA_HW_FEAT_ACTPHYIF, hw_cap);
+ /* 16-bit VLAN Hash Filter (flag is unavailable in the CSR) */
+ dma_cap->vlhash = dwmac1000_det_hw_vlhash(ioaddr);
+
return 0;
}
@@ -255,10 +309,12 @@ const struct stmmac_dma_ops dwmac1000_dma_ops = {
.init_chan = dwmac1000_dma_init_channel,
.init_rx_chan = dwmac1000_dma_init_rx,
.init_tx_chan = dwmac1000_dma_init_tx,
+ .clean = dwmac1000_dma_clean,
.axi = dwmac1000_dma_axi,
.dump_regs = dwmac1000_dump_dma_regs,
.dma_rx_mode = dwmac1000_dma_operation_mode_rx,
.dma_tx_mode = dwmac1000_dma_operation_mode_tx,
+ .dma_diagnostic_fr = dwmac_dma_diagnostic_fr,
.enable_dma_transmission = dwmac_enable_dma_transmission,
.enable_dma_reception = dwmac_enable_dma_reception,
.enable_dma_irq = dwmac_enable_dma_irq,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
index db4fbe64a38af..9c3e1ab992dc5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
@@ -48,11 +48,6 @@ static void dwmac100_dump_mac_regs(struct mac_device_info *hw, u32 *reg_space)
reg_space[MAC_VLAN2 / 4] = readl(ioaddr + MAC_VLAN2);
}
-static int dwmac100_rx_ipc_enable(struct mac_device_info *hw)
-{
- return 0;
-}
-
static int dwmac100_irq_status(struct stmmac_priv *priv,
struct stmmac_extra_stats *x)
{
@@ -157,7 +152,6 @@ static void dwmac100_set_mac_loopback(void __iomem *ioaddr, bool enable)
const struct stmmac_ops dwmac100_ops = {
.core_init = dwmac100_core_init,
.set_mac = stmmac_set_mac,
- .rx_ipc = dwmac100_rx_ipc_enable,
.dump_regs = dwmac100_dump_mac_regs,
.host_irq_status = dwmac100_irq_status,
.set_filter = dwmac100_set_filter,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
index 12b2bf2d739ab..093d09267e38b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
@@ -82,30 +82,6 @@ static void dwmac100_dump_dma_regs(struct stmmac_priv *priv,
readl(ioaddr + DMA_CUR_RX_BUF_ADDR);
}
-/* DMA controller has two counters to track the number of the missed frames. */
-static void dwmac100_dma_diagnostic_fr(struct stmmac_extra_stats *x,
- void __iomem *ioaddr)
-{
- u32 csr8 = readl(ioaddr + DMA_MISSED_FRAME_CTR);
-
- if (unlikely(csr8)) {
- if (csr8 & DMA_MISSED_FRAME_OVE) {
- x->rx_overflow_cntr += 0x800;
- } else {
- unsigned int ove_cntr;
- ove_cntr = ((csr8 & DMA_MISSED_FRAME_OVE_CNTR) >> 17);
- x->rx_overflow_cntr += ove_cntr;
- }
-
- if (csr8 & DMA_MISSED_FRAME_OVE_M) {
- x->rx_missed_cntr += 0xffff;
- } else {
- unsigned int miss_f = (csr8 & DMA_MISSED_FRAME_M_CNTR);
- x->rx_missed_cntr += miss_f;
- }
- }
-}
-
const struct stmmac_dma_ops dwmac100_dma_ops = {
.reset = dwmac_dma_reset,
.init = dwmac100_dma_init,
@@ -113,7 +89,7 @@ const struct stmmac_dma_ops dwmac100_dma_ops = {
.init_tx_chan = dwmac100_dma_init_tx,
.dump_regs = dwmac100_dump_dma_regs,
.dma_tx_mode = dwmac100_dma_operation_mode_tx,
- .dma_diagnostic_fr = dwmac100_dma_diagnostic_fr,
+ .dma_diagnostic_fr = dwmac_dma_diagnostic_fr,
.enable_dma_transmission = dwmac_enable_dma_transmission,
.enable_dma_irq = dwmac_enable_dma_irq,
.disable_dma_irq = dwmac_disable_dma_irq,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
index d797d936aee16..0fed45ba26cb3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
@@ -16,6 +16,7 @@
#define GMAC_CONFIG 0x00000000
#define GMAC_EXT_CONFIG 0x00000004
#define GMAC_PACKET_FILTER 0x00000008
+#define GMAC_WDT 0x0000000c
#define GMAC_HASH_TAB(x) (0x10 + (x) * 4)
#define GMAC_RX_FLOW_CTRL 0x00000090
#define GMAC_QX_TX_FLOW_CTRL(x) (0x70 + x * 4)
@@ -77,6 +78,10 @@
#define GMAC_MAX_PERFECT_ADDRESSES 128
+/* MAC Watchdog timeout for received frames */
+#define GMAC_WDT_PWE BIT(8)
+#define GMAC_WDT_WTO GENMASK(3, 0)
+
/* MAC RX Queue Enable */
#define GMAC_RX_QUEUE_CLEAR(queue) ~(GENMASK(1, 0) << ((queue) * 2))
#define GMAC_RX_AV_QUEUE_ENABLE(queue) BIT((queue) * 2)
@@ -106,8 +111,7 @@
#define GMAC_INT_LPI_EN BIT(5)
#define GMAC_INT_TSIE BIT(12)
-#define GMAC_INT_DEFAULT_ENABLE (GMAC_INT_PMT_EN | GMAC_INT_LPI_EN | \
- GMAC_INT_TSIE)
+#define GMAC_INT_DEFAULT_ENABLE (GMAC_INT_PMT_EN | GMAC_INT_TSIE)
enum dwmac4_irq_status {
time_stamp_irq = 0x00001000,
@@ -155,8 +159,11 @@ enum power_event {
#define GMAC_CONFIG_SARC GENMASK(30, 28)
#define GMAC_CONFIG_IPC BIT(27)
#define GMAC_CONFIG_IPG GENMASK(26, 24)
+#define GMAC_CONFIG_GPSLCE BIT(23)
#define GMAC_CONFIG_2K BIT(22)
+#define GMAC_CONFIG_CST BIT(21)
#define GMAC_CONFIG_ACS BIT(20)
+#define GMAC_CONFIG_WD BIT(19)
#define GMAC_CONFIG_BE BIT(18)
#define GMAC_CONFIG_JD BIT(17)
#define GMAC_CONFIG_JE BIT(16)
@@ -173,6 +180,7 @@ enum power_event {
#define GMAC_CONFIG_EIPG_EN BIT(24)
#define GMAC_CONFIG_HDSMS GENMASK(22, 20)
#define GMAC_CONFIG_HDSMS_256 FIELD_PREP_CONST(GMAC_CONFIG_HDSMS, 0x2)
+#define GMAC_CONFIG_GPSL GENMASK(13, 0)
/* MAC HW features0 bitmap */
#define GMAC_HW_FEAT_SAVLANINS BIT(27)
@@ -296,6 +304,7 @@ static inline u32 mtl_chanx_base_addr(const struct dwmac4_addrs *addrs,
#define MTL_CHAN_TX_DEBUG(addrs, x) (mtl_chanx_base_addr(addrs, x) + 0x8)
#define MTL_CHAN_INT_CTRL(addrs, x) (mtl_chanx_base_addr(addrs, x) + 0x2c)
#define MTL_CHAN_RX_OP_MODE(addrs, x) (mtl_chanx_base_addr(addrs, x) + 0x30)
+#define MTL_CHAN_RX_MISSED_PKT_CTR(addrs, x) (mtl_chanx_base_addr(addrs, x) + 0x34)
#define MTL_CHAN_RX_DEBUG(addrs, x) (mtl_chanx_base_addr(addrs, x) + 0x38)
#define MTL_OP_MODE_RSF BIT(5)
@@ -324,6 +333,8 @@ static inline u32 mtl_chanx_base_addr(const struct dwmac4_addrs *addrs,
#define MTL_OP_MODE_EHFC BIT(7)
#define MTL_OP_MODE_DIS_TCP_EF BIT(6)
+#define MTL_OP_MODE_FEP BIT(4)
+#define MTL_OP_MODE_FUP BIT(3)
#define MTL_OP_MODE_RTC_MASK GENMASK(1, 0)
@@ -428,6 +439,12 @@ static inline u32 mtl_low_credx_base_addr(const struct dwmac4_addrs *addrs,
#define MTL_HIGH_CRED_LC_MASK GENMASK(28, 0)
+/* MTL Rx Queue Missed Packet and Overflow Counter */
+#define MTL_MISSED_PKT_MISCNTOVF BIT(27)
+#define MTL_MISSED_PKT_MISPKTCNT GENMASK(26, 16)
+#define MTL_MISSED_PKT_OVFCNTOVF BIT(11)
+#define MTL_MISSED_PKT_OVFPKTCNT GENMASK(10, 0)
+
/* MTL debug */
#define MTL_DEBUG_TXSTSFSTS BIT(5)
#define MTL_DEBUG_TXFSTS BIT(4)
@@ -457,11 +474,12 @@ static inline u32 mtl_low_credx_base_addr(const struct dwmac4_addrs *addrs,
/* MTL interrupt */
#define MTL_RX_OVERFLOW_INT_EN BIT(24)
#define MTL_RX_OVERFLOW_INT BIT(16)
+#define MTL_TX_UNDERFLOW_INT_EN BIT(8)
+#define MTL_TX_UNDERFLOW_INT BIT(0)
+#define MTL_INT_DEFAULT_ENABLE (MTL_RX_OVERFLOW_INT | MTL_TX_UNDERFLOW_INT)
/* Default operating mode of the MAC */
-#define GMAC_CORE_INIT (GMAC_CONFIG_JD | GMAC_CONFIG_PS | \
- GMAC_CONFIG_BE | GMAC_CONFIG_DCRS | \
- GMAC_CONFIG_JE)
+#define GMAC_CORE_INIT (GMAC_CONFIG_PS | GMAC_CONFIG_BE | GMAC_CONFIG_DCRS)
/* To dump the core regs excluding the Address Registers */
#define GMAC_REG_NUM 132
@@ -470,15 +488,7 @@ static inline u32 mtl_low_credx_base_addr(const struct dwmac4_addrs *addrs,
#define GMAC_PHYIF_CTRLSTATUS_TC BIT(0)
#define GMAC_PHYIF_CTRLSTATUS_LUD BIT(1)
#define GMAC_PHYIF_CTRLSTATUS_SMIDRXS BIT(4)
-#define GMAC_PHYIF_CTRLSTATUS_LNKMOD BIT(16)
-#define GMAC_PHYIF_CTRLSTATUS_SPEED GENMASK(18, 17)
-#define GMAC_PHYIF_CTRLSTATUS_LNKSTS BIT(19)
-#define GMAC_PHYIF_CTRLSTATUS_JABTO BIT(20)
-#define GMAC_PHYIF_CTRLSTATUS_FALSECARDET BIT(21)
-/* LNKSPEED */
-#define GMAC_PHYIF_CTRLSTATUS_SPEED_125 0x2
-#define GMAC_PHYIF_CTRLSTATUS_SPEED_25 0x1
-#define GMAC_PHYIF_CTRLSTATUS_SPEED_2_5 0x0
+#define GMAC_PHYIF_CTRLSTATUS_RSGMII_MASK GENMASK(31, 16)
extern const struct stmmac_dma_ops dwmac4_dma_ops;
extern const struct stmmac_dma_ops dwmac410_dma_ops;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index 623868afe93d2..9e1a116a17ee1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -22,13 +22,16 @@
#include "dwmac4.h"
#include "dwmac5.h"
+static const struct stmmac_pcs_info dwmac4_pcs_info = {
+ .pcs_offset = GMAC_PCS_BASE,
+ .rgsmii_offset = GMAC_PHYIF_CONTROL_STATUS,
+ .rgsmii_status_mask = GMAC_PHYIF_CTRLSTATUS_RSGMII_MASK,
+ .int_mask = GMAC_INT_RGSMIIS | GMAC_INT_PCS_LINK | GMAC_INT_PCS_ANE,
+};
+
static int dwmac4_pcs_init(struct stmmac_priv *priv)
{
- if (!priv->dma_cap.pcs)
- return 0;
-
- return stmmac_integrated_pcs_init(priv, GMAC_PCS_BASE,
- GMAC_INT_PCS_LINK | GMAC_INT_PCS_ANE);
+ return stmmac_integrated_pcs_init(priv, &dwmac4_pcs_info);
}
static void dwmac4_core_init(struct mac_device_info *hw,
@@ -37,10 +40,43 @@ static void dwmac4_core_init(struct mac_device_info *hw,
struct stmmac_priv *priv = netdev_priv(dev);
void __iomem *ioaddr = hw->pcsr;
unsigned long clk_rate;
- u32 value;
+ int mtu = dev->mtu;
+ u32 value, gps;
value = readl(ioaddr + GMAC_CONFIG);
- writel(value | GMAC_CORE_INIT, ioaddr + GMAC_CONFIG);
+ value |= GMAC_CORE_INIT;
+
+ /* Frame length limits (giant status reported or dropped) */
+ gps = ETH_HLEN + ETH_FCS_LEN;
+ if (mtu > 16357) { /* Rx <= 16375 (+C/SVLAN headers) && Tx <= 16383 */
+ gps += 16357;
+ value |= GMAC_CONFIG_GPSLCE | GMAC_CONFIG_JD;
+ } else if (mtu > 2022) { /* Rx <= MTU + Eth (+C/SVLAN headers) && Tx <= 16383 */
+ gps += mtu;
+ value |= GMAC_CONFIG_GPSLCE | GMAC_CONFIG_JD;
+ } else if (mtu > 1500) { /* Rx <= MTU + Eth (+C/SVLAN headers) && Tx <= 2048 */
+ gps += mtu;
+ value |= GMAC_CONFIG_GPSLCE;
+ } else { /* Rx <= 1500 + Eth (+C/SVLAN headers) && Tx <= 2048 */
+ gps = 1500 + ETH_HLEN + ETH_FCS_LEN;
+ }
+
+ writel(value, ioaddr + GMAC_CONFIG);
+
+ value = readl(ioaddr + GMAC_EXT_CONFIG);
+ value |= FIELD_PREP(GMAC_CONFIG_GPSL, gps);
+ writel(value, ioaddr + GMAC_EXT_CONFIG);
+
+ /* Over 2K, 3K, ..., 16K-1 frames will be truncated on Rx */
+ gps = ALIGN(gps + 2 * VLAN_HLEN, SZ_1K);
+ if (gps >= SZ_2K)
+ gps = gps / SZ_1K - 2;
+ else
+ gps = 0;
+
+ /* Over giant frame watchdog fine-tuning */
+ value = FIELD_PREP(GMAC_WDT_WTO, gps) | GMAC_WDT_PWE;
+ writel(value, ioaddr + GMAC_WDT);
/* Configure LPI 1us counter to number of CSR clock ticks in 1us - 1 */
clk_rate = clk_get_rate(priv->plat->stmmac_clk);
@@ -318,12 +354,36 @@ static void dwmac4_dump_regs(struct mac_device_info *hw, u32 *reg_space)
reg_space[i] = readl(ioaddr + i * 4);
}
-static int dwmac4_rx_ipc_enable(struct mac_device_info *hw)
+static void dwmac4_rx_fcs_enable(struct mac_device_info *hw, bool enable)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ u32 value;
+
+ value = readl(ioaddr + GMAC_CONFIG);
+
+ if (enable)
+ value &= ~GMAC_CONFIG_CST;
+ else
+ value |= GMAC_CONFIG_CST;
+
+ writel(value, ioaddr + GMAC_CONFIG);
+}
+
+static int dwmac4_rx_fcs_status(struct mac_device_info *hw, int snps_id,
+ int status)
+{
+ if (unlikely(status & llc_snap))
+ return -ENOTSUPP;
+
+ return 0;
+}
+
+static int dwmac4_rx_ipc_enable(struct mac_device_info *hw, bool enable)
{
void __iomem *ioaddr = hw->pcsr;
u32 value = readl(ioaddr + GMAC_CONFIG);
- if (hw->rx_csum)
+ if (enable)
value |= GMAC_CONFIG_IPC;
else
value &= ~GMAC_CONFIG_IPC;
@@ -422,15 +482,18 @@ static int dwmac4_set_lpi_mode(struct mac_device_info *hw,
static void dwmac4_set_eee_pls(struct mac_device_info *hw, int link)
{
void __iomem *ioaddr = hw->pcsr;
- u32 value;
+ u32 value, intr_enable;
value = readl(ioaddr + GMAC4_LPI_CTRL_STATUS);
-
- if (link)
+ intr_enable = readl(ioaddr + GMAC_INT_EN);
+ if (link) {
value |= LPI_CTRL_STATUS_PLS;
- else
+ intr_enable |= GMAC_INT_LPI_EN;
+ } else {
+ intr_enable &= ~GMAC_INT_LPI_EN;
value &= ~LPI_CTRL_STATUS_PLS;
-
+ }
+ writel(intr_enable, ioaddr + GMAC_INT_EN);
writel(value, ioaddr + GMAC4_LPI_CTRL_STATUS);
}
@@ -454,6 +517,7 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
{
void __iomem *ioaddr = (void __iomem *)dev->base_addr;
int numhashregs = (hw->multicast_filter_bins >> 5);
+ struct stmmac_priv *priv = netdev_priv(dev);
int mcbitslog2 = hw->mcast_bits_log2;
unsigned int value;
u32 mc_filter[8];
@@ -476,11 +540,10 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
value |= GMAC_RXQCTRL_VFFQE |
(hw->vlan_fail_q << GMAC_RXQCTRL_VFFQ_SHIFT);
writel(value, ioaddr + GMAC_RXQ_CTRL4);
- value = GMAC_PACKET_FILTER_PR | GMAC_PACKET_FILTER_RA;
- } else {
- value = GMAC_PACKET_FILTER_PR | GMAC_PACKET_FILTER_PCF;
}
-
+ value |= GMAC_PACKET_FILTER_RA;
+ value |= GMAC_PACKET_FILTER_PR;
+ value |= GMAC_PACKET_FILTER_PCF;
} else if ((dev->flags & IFF_ALLMULTI) ||
(netdev_mc_count(dev) > hw->multicast_filter_bins)) {
/* Pass all multi */
@@ -515,7 +578,7 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
value |= GMAC_PACKET_FILTER_HPF;
/* Handle multiple unicast addresses */
- if (netdev_uc_count(dev) > hw->unicast_filter_entries) {
+ if (netdev_uc_count(dev) > hw->unicast_filter_entries - 1) {
/* Switch to promiscuous mode if more than 128 addrs
* are required
*/
@@ -529,18 +592,15 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
reg++;
}
- while (reg < GMAC_MAX_PERFECT_ADDRESSES) {
+ for (; reg < GMAC_MAX_PERFECT_ADDRESSES; reg++) {
writel(0, ioaddr + GMAC_ADDR_HIGH(reg));
writel(0, ioaddr + GMAC_ADDR_LOW(reg));
- reg++;
}
}
/* VLAN filtering */
- if (dev->flags & IFF_PROMISC && !hw->vlan_fail_q_en)
- value &= ~GMAC_PACKET_FILTER_VTFE;
- else if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
- value |= GMAC_PACKET_FILTER_VTFE;
+ if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ stmmac_restore_hw_vlan_rx_fltr(priv, dev, hw);
writel(value, ioaddr + GMAC_PACKET_FILTER);
}
@@ -604,12 +664,12 @@ static int dwmac4_irq_mtl_status(struct stmmac_priv *priv,
u32 status = readl(ioaddr + MTL_CHAN_INT_CTRL(dwmac4_addrs,
chan));
- if (status & MTL_RX_OVERFLOW_INT) {
- /* clear Interrupt */
- writel(status | MTL_RX_OVERFLOW_INT,
- ioaddr + MTL_CHAN_INT_CTRL(dwmac4_addrs, chan));
- ret = CORE_IRQ_MTL_RX_OVERFLOW;
- }
+ if (unlikely(status & MTL_RX_OVERFLOW_INT))
+ ret |= CORE_IRQ_MTL_RX_OVERFLOW;
+ if (unlikely(status & MTL_TX_UNDERFLOW_INT))
+ ret |= CORE_IRQ_MTL_TX_UNDERFLOW;
+
+ writel(status, ioaddr + MTL_CHAN_INT_CTRL(dwmac4_addrs, chan));
}
return ret;
@@ -658,7 +718,7 @@ static int dwmac4_irq_status(struct stmmac_priv *priv,
x->irq_rx_path_exit_lpi_mode_n++;
}
- if (intr_status & (PCS_ANE_IRQ | PCS_LINK_IRQ))
+ if (intr_status & (PCS_ANE_IRQ | PCS_LINK_IRQ | PCS_RGSMIIIS_IRQ))
stmmac_integrated_pcs_irq(priv, intr_status, x);
return ret;
@@ -895,6 +955,8 @@ const struct stmmac_ops dwmac4_ops = {
.irq_modify = dwmac4_irq_modify,
.update_caps = dwmac4_update_caps,
.set_mac = stmmac_set_mac,
+ .rx_fcs = dwmac4_rx_fcs_enable,
+ .rx_fcs_status = dwmac4_rx_fcs_status,
.rx_ipc = dwmac4_rx_ipc_enable,
.rx_queue_enable = dwmac4_rx_queue_enable,
.rx_queue_prio = dwmac4_rx_queue_priority,
@@ -931,6 +993,8 @@ const struct stmmac_ops dwmac410_ops = {
.irq_modify = dwmac4_irq_modify,
.update_caps = dwmac4_update_caps,
.set_mac = stmmac_dwmac4_set_mac,
+ .rx_fcs = dwmac4_rx_fcs_enable,
+ .rx_fcs_status = dwmac4_rx_fcs_status,
.rx_ipc = dwmac4_rx_ipc_enable,
.rx_queue_enable = dwmac4_rx_queue_enable,
.rx_queue_prio = dwmac4_rx_queue_priority,
@@ -969,6 +1033,8 @@ const struct stmmac_ops dwmac510_ops = {
.irq_modify = dwmac4_irq_modify,
.update_caps = dwmac4_update_caps,
.set_mac = stmmac_dwmac4_set_mac,
+ .rx_fcs = dwmac4_rx_fcs_enable,
+ .rx_fcs_status = dwmac4_rx_fcs_status,
.rx_ipc = dwmac4_rx_ipc_enable,
.rx_queue_enable = dwmac4_rx_queue_enable,
.rx_queue_prio = dwmac4_rx_queue_priority,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
index e226dc6a1b17d..59c46d1b16a7d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
@@ -11,11 +11,11 @@
#include <linux/stmmac.h>
#include "common.h"
#include "dwmac4.h"
+#include "dwmac4_dma.h"
#include "dwmac4_descs.h"
static int dwmac4_wrback_get_tx_status(struct stmmac_extra_stats *x,
- struct dma_desc *p,
- void __iomem *ioaddr)
+ struct dma_desc *p)
{
u32 tdes3 = le32_to_cpu(p->des3);
int ret = tx_done;
@@ -49,10 +49,8 @@ static int dwmac4_wrback_get_tx_status(struct stmmac_extra_stats *x,
if (unlikely(tdes3 & TDES3_EXCESSIVE_DEFERRAL))
x->tx_deferred++;
- if (unlikely(tdes3 & TDES3_UNDERFLOW_ERROR)) {
+ if (unlikely(tdes3 & TDES3_UNDERFLOW_ERROR))
x->tx_underflow++;
- ret |= tx_err_bump_tc;
- }
if (unlikely(tdes3 & TDES3_IP_HDR_ERROR))
x->tx_ip_header_error++;
@@ -85,42 +83,74 @@ static int dwmac4_wrback_get_rx_status(struct stmmac_extra_stats *x,
return rx_not_ls;
if (unlikely(rdes3 & RDES3_ERROR_SUMMARY)) {
- if (unlikely(rdes3 & RDES3_GIANT_PACKET))
+ if (unlikely(rdes3 & RDES3_GIANT_PACKET)) {
x->rx_length++;
- if (unlikely(rdes3 & RDES3_OVERFLOW_ERROR))
+ ret |= len_err;
+ }
+
+ if (unlikely(rdes3 & RDES3_OVERFLOW_ERROR)) {
x->rx_gmac_overflow++;
+ ret |= cutoff_err;
+ }
- if (unlikely(rdes3 & RDES3_RECEIVE_WATCHDOG))
+ if (unlikely(rdes3 & RDES3_RECEIVE_WATCHDOG)) {
x->rx_watchdog++;
+ ret |= cutoff_err;
+ }
- if (unlikely(rdes3 & RDES3_RECEIVE_ERROR))
+ if (unlikely(rdes3 & RDES3_RECEIVE_ERROR)) {
x->rx_mii++;
+ ret |= proto_err;
+ }
- if (unlikely(rdes3 & RDES3_CRC_ERROR))
+ if (unlikely(rdes3 & RDES3_CRC_ERROR)) {
x->rx_crc_errors++;
+ ret |= csum_err;
+ }
- if (unlikely(rdes3 & RDES3_DRIBBLE_ERROR))
+ if (unlikely(rdes3 & RDES3_DRIBBLE_ERROR)) {
x->dribbling_bit++;
-
- ret = discard_frame;
+ ret |= proto_err;
+ }
+ } else {
+ int l2t = FIELD_GET(RDES3_PACKET_LEN_TYPE_MASK, rdes3);
+
+ switch (l2t) {
+ case RDES3_PACKET_LENGTH:
+ ret |= llc_snap;
+ break;
+ case RDES3_PACKET_ARP:
+ x->arp_pkt_ofld++;
+ if (likely(!(rdes2 & RDES2_RPNG)))
+ ret |= arp_done;
+ break;
+ case RDES3_PACKET_VLAN:
+ case RDES3_PACKET_DVLAN:
+ x->rx_vlan++;
+ break;
+ }
}
message_type = FIELD_GET(RDES1_PTP_MSG_TYPE_MASK, rdes1);
if (rdes1 & RDES1_IP_HDR_ERROR) {
x->ip_hdr_err++;
- ret |= csum_none;
+ ret |= csum_err;
}
- if (rdes1 & RDES1_IP_CSUM_BYPASSED)
+ if (rdes1 & RDES1_IP_PAYLOAD_ERROR) {
+ x->ip_payload_err++;
+ ret |= csum_err;
+ }
+ if (rdes1 & RDES1_IP_CSUM_BYPASSED) {
x->ip_csum_bypassed++;
+ ret |= csum_none;
+ }
if (rdes1 & RDES1_IPV4_HEADER)
x->ipv4_pkt_rcvd++;
- if (rdes1 & RDES1_IPV6_HEADER)
+ else if (rdes1 & RDES1_IPV6_HEADER)
x->ipv6_pkt_rcvd++;
- if (rdes1 & RDES1_IP_PAYLOAD_ERROR) {
- x->ip_payload_err++;
+ else /* Not an IP packet or COE is disabled */
ret |= csum_none;
- }
if (message_type == RDES_EXT_NO_PTP)
x->no_ptp_rx_msg_type_ext++;
@@ -152,14 +182,10 @@ static int dwmac4_wrback_get_rx_status(struct stmmac_extra_stats *x,
if (rdes1 & RDES1_TIMESTAMP_DROPPED)
x->timestamp_dropped++;
- if (unlikely(rdes2 & RDES2_SA_FILTER_FAIL)) {
+ if (unlikely(rdes2 & RDES2_SA_FILTER_FAIL))
x->sa_rx_filter_fail++;
- ret = discard_frame;
- }
- if (unlikely(rdes2 & RDES2_DA_FILTER_FAIL)) {
+ if (unlikely(rdes2 & RDES2_DA_FILTER_FAIL))
x->da_rx_filter_fail++;
- ret = discard_frame;
- }
if (rdes2 & RDES2_L3_FILTER_MATCH)
x->l3_filter_match++;
@@ -171,11 +197,6 @@ static int dwmac4_wrback_get_rx_status(struct stmmac_extra_stats *x,
return ret;
}
-static int dwmac4_rd_get_tx_len(struct dma_desc *p)
-{
- return (le32_to_cpu(p->des2) & TDES2_BUFFER1_SIZE_MASK);
-}
-
static int dwmac4_get_tx_owner(struct dma_desc *p)
{
return (le32_to_cpu(p->des3) & TDES3_OWN) >> TDES3_OWN_SHIFT;
@@ -202,6 +223,12 @@ static int dwmac4_get_tx_ls(struct dma_desc *p)
>> TDES3_LAST_DESCRIPTOR_SHIFT;
}
+static u16 dwmac4_wrback_get_rx_vlan_tpid(struct dma_desc *p)
+{
+ /* Alas at least up to v5.20 there is no TPID in wrback desc */
+ return ETH_P_8021Q;
+}
+
static u16 dwmac4_wrback_get_rx_vlan_tci(struct dma_desc *p)
{
return (le32_to_cpu(p->des0) & RDES0_VLAN_TAG_MASK);
@@ -213,7 +240,7 @@ static bool dwmac4_wrback_get_rx_vlan_valid(struct dma_desc *p)
(le32_to_cpu(p->des3) & RDES3_RDES0_VALID));
}
-static int dwmac4_wrback_get_rx_frame_len(struct dma_desc *p, int rx_coe)
+static int dwmac4_wrback_get_rx_frame_len(struct dma_desc *p)
{
return (le32_to_cpu(p->des3) & RDES3_PACKET_SIZE_MASK);
}
@@ -307,7 +334,12 @@ exit:
static void dwmac4_rd_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
int mode, int end, int bfsize)
{
- dwmac4_set_rx_owner(p, disable_rx_ic);
+ u32 flags = (RDES3_OWN | RDES3_BUFFER1_VALID_ADDR);
+
+ if (!disable_rx_ic)
+ flags |= RDES3_INT_ON_COMPLETION_EN;
+
+ p->des3 |= cpu_to_le32(flags);
}
static void dwmac4_rd_init_tx_desc(struct dma_desc *p, int mode, int end)
@@ -318,6 +350,40 @@ static void dwmac4_rd_init_tx_desc(struct dma_desc *p, int mode, int end)
p->des3 = 0;
}
+static unsigned int dwmac4_rd_get_rx_len(int mode)
+{
+ return STMMAC_RX_BUF_ALIGN(FIELD_MAX(DMA_RBSZ_MASK));
+}
+
+static void dwmac4_release_rx_desc(struct dma_desc *p, int disable_rx_ic,
+ int is_fs, bool rx_own)
+{
+ unsigned int rdes3 = le32_to_cpu(p->des3);
+
+ rdes3 |= RDES3_BUFFER1_VALID_ADDR;
+
+ if (disable_rx_ic)
+ rdes3 &= cpu_to_le32(~RDES3_INT_ON_COMPLETION_EN);
+ else
+ rdes3 |= cpu_to_le32(RDES3_INT_ON_COMPLETION_EN);
+
+ if (rx_own)
+ rdes3 |= cpu_to_le32(RDES3_OWN);
+
+ /* Before releasing the initial descriptor make sure that all
+ * the previous writes are visible to the controller.
+ */
+ if (is_fs && rx_own)
+ dma_wmb();
+
+ p->des3 = cpu_to_le32(rdes3);
+}
+
+static unsigned int dwmac4_rd_get_tx_len(int mode)
+{
+ return FIELD_MAX(TDES2_BUFFER1_SIZE_MASK);
+}
+
static void dwmac4_rd_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
bool csum_flag, int mode, bool tx_own,
bool ls, unsigned int tot_pkt_len)
@@ -397,7 +463,8 @@ static void dwmac4_rd_prepare_tso_tx_desc(struct dma_desc *p, int is_fs,
p->des3 = cpu_to_le32(tdes3);
}
-static void dwmac4_release_tx_desc(struct dma_desc *p, int mode)
+static void dwmac4_release_tx_desc(struct dma_desc *p, int mode,
+ dma_addr_t np, bool hwts_tx)
{
p->des0 = 0;
p->des1 = 0;
@@ -488,15 +555,6 @@ static void dwmac4_set_sarc(struct dma_desc *p, u32 sarc_type)
sarc_type));
}
-static int set_16kib_bfsize(int mtu)
-{
- int ret = 0;
-
- if (unlikely(mtu >= BUF_SIZE_8KiB))
- ret = BUF_SIZE_16KiB;
- return ret;
-}
-
static void dwmac4_set_vlan_tag(struct dma_desc *p, u16 tag, u16 inner_tag,
u32 inner_type)
{
@@ -551,11 +609,13 @@ static void dwmac4_set_tbs(struct dma_edesc *p, u32 sec, u32 nsec)
const struct stmmac_desc_ops dwmac4_desc_ops = {
.tx_status = dwmac4_wrback_get_tx_status,
.rx_status = dwmac4_wrback_get_rx_status,
+ .get_rx_len = dwmac4_rd_get_rx_len,
.get_tx_len = dwmac4_rd_get_tx_len,
.get_tx_owner = dwmac4_get_tx_owner,
.set_tx_owner = dwmac4_set_tx_owner,
.set_rx_owner = dwmac4_set_rx_owner,
.get_tx_ls = dwmac4_get_tx_ls,
+ .get_rx_vlan_tpid = dwmac4_wrback_get_rx_vlan_tpid,
.get_rx_vlan_tci = dwmac4_wrback_get_rx_vlan_tci,
.get_rx_vlan_valid = dwmac4_wrback_get_rx_vlan_valid,
.get_rx_frame_len = dwmac4_wrback_get_rx_frame_len,
@@ -564,6 +624,7 @@ const struct stmmac_desc_ops dwmac4_desc_ops = {
.get_rx_timestamp_status = dwmac4_wrback_get_rx_timestamp_status,
.get_timestamp = dwmac4_get_timestamp,
.set_tx_ic = dwmac4_rd_set_tx_ic,
+ .release_rx_desc = dwmac4_release_rx_desc,
.prepare_tx_desc = dwmac4_rd_prepare_tx_desc,
.prepare_tso_tx_desc = dwmac4_rd_prepare_tso_tx_desc,
.release_tx_desc = dwmac4_release_tx_desc,
@@ -580,7 +641,3 @@ const struct stmmac_desc_ops dwmac4_desc_ops = {
.set_sec_addr = dwmac4_set_sec_addr,
.set_tbs = dwmac4_set_tbs,
};
-
-const struct stmmac_mode_ops dwmac4_ring_mode_ops = {
- .set_16kib_bfsize = set_16kib_bfsize,
-};
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h
index fb1fea5b0e6ed..4f9e2601d8572 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h
@@ -108,12 +108,20 @@
#define RDES2_L4_FILTER_MATCH BIT(28)
#define RDES2_L3_L4_FILT_NB_MATCH_MASK GENMASK(27, 26)
#define RDES2_L3_L4_FILT_NB_MATCH_SHIFT 26
+#define RDES2_RPNG BIT(10)
#define RDES2_HL GENMASK(9, 0)
/* RDES3 (write back format) */
#define RDES3_PACKET_SIZE_MASK GENMASK(14, 0)
#define RDES3_ERROR_SUMMARY BIT(15)
#define RDES3_PACKET_LEN_TYPE_MASK GENMASK(18, 16)
+#define RDES3_PACKET_LENGTH 0
+#define RDES3_PACKET_TYPE 1
+#define RDES3_PACKET_ARP 3
+#define RDES3_PACKET_VLAN 4
+#define RDES3_PACKET_DVLAN 5
+#define RDES3_PACKET_MAC_CONTROL 6
+#define RDES3_PACKET_OAM_PACKET 7
#define RDES3_DRIBBLE_ERROR BIT(19)
#define RDES3_RECEIVE_ERROR BIT(20)
#define RDES3_OVERFLOW_ERROR BIT(21)
@@ -136,7 +144,6 @@
/* TDS3 use for both format (read and write back) */
#define RDES3_OWN BIT(31)
-extern const struct stmmac_mode_ops dwmac4_ring_mode_ops;
extern const struct stmmac_desc_ops dwmac4_desc_ops;
#endif /* __DWMAC4_DESCS_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index 60b880cdd9da2..a75206682c413 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -40,6 +40,30 @@ static void dwmac4_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
value = (value & ~DMA_AXI_BLEN_MASK) | axi->axi_blen_regval;
writel(value, ioaddr + DMA_SYS_BUS_MODE);
+
+ if (axi->axi_cc) {
+ writel(STMMAC_AXI_ACE(DMA, TX_AR_THD, D_OUTS) |
+ STMMAC_AXI_ACE(DMA, TX_AR_THC, AR_WbNa) |
+ STMMAC_AXI_ACE(DMA, TX_AR_TED, D_OUTS) |
+ STMMAC_AXI_ACE(DMA, TX_AR_TEC, AR_WbNa) |
+ STMMAC_AXI_ACE(DMA, TX_AR_TDRD, D_OUTS) |
+ STMMAC_AXI_ACE(DMA, TX_AR_TDRC, AR_WbNa),
+ ioaddr + DMA_AXI_TX_AR_ACE_CTRL);
+ writel(STMMAC_AXI_ACE(DMA, RX_AW_RDD, D_OUTS) |
+ STMMAC_AXI_ACE(DMA, RX_AW_RDC, AW_WbNa) |
+ STMMAC_AXI_ACE(DMA, RX_AW_RHD, D_OUTS) |
+ STMMAC_AXI_ACE(DMA, RX_AW_RHC, AW_WbNa) |
+ STMMAC_AXI_ACE(DMA, RX_AW_RPD, D_OUTS) |
+ STMMAC_AXI_ACE(DMA, RX_AW_RPC, AW_WbNa) |
+ STMMAC_AXI_ACE(DMA, RX_AW_RDWD, D_OUTS) |
+ STMMAC_AXI_ACE(DMA, RX_AW_RDWC, AW_WbNa),
+ ioaddr + DMA_AXI_RX_AW_ACE_CTRL);
+ writel(STMMAC_AXI_ACE(DMA, TXRX_AWAR_RDRD, D_OUTS) |
+ STMMAC_AXI_ACE(DMA, TXRX_AWAR_RDRC, AR_WbNa) |
+ STMMAC_AXI_ACE(DMA, TXRX_AWAR_TDWD, D_OUTS) |
+ STMMAC_AXI_ACE(DMA, TXRX_AWAR_TDWC, AW_WbNa),
+ ioaddr + DMA_AXI_TXRX_AWAR_ACE_CTRL);
+ }
}
static void dwmac4_dma_init_rx_chan(struct stmmac_priv *priv,
@@ -104,6 +128,10 @@ static void dwmac4_dma_init_channel(struct stmmac_priv *priv,
/* Mask interrupts by writing to CSR7 */
writel(DMA_CHAN_INTR_DEFAULT_MASK,
ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+
+ /* Enable interrupts by writing to MTL INT CSR */
+ writel(MTL_INT_DEFAULT_ENABLE,
+ ioaddr + MTL_CHAN_INT_CTRL(dwmac4_addrs, chan));
}
static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
@@ -123,6 +151,10 @@ static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
/* Mask interrupts by writing to CSR7 */
writel(DMA_CHAN_INTR_DEFAULT_MASK_4_10,
ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+
+ /* Enable interrupts by writing to MTL INT CSR */
+ writel(MTL_INT_DEFAULT_ENABLE,
+ ioaddr + MTL_CHAN_INT_CTRL(dwmac4_addrs, chan));
}
static void dwmac4_dma_init(void __iomem *ioaddr,
@@ -232,7 +264,8 @@ static void dwmac4_rx_watchdog(struct stmmac_priv *priv, void __iomem *ioaddr,
static void dwmac4_dma_rx_chan_op_mode(struct stmmac_priv *priv,
void __iomem *ioaddr, int mode,
- u32 channel, int fifosz, u8 qmode)
+ u32 channel, int fifosz, u8 qmode,
+ bool rxall)
{
const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
unsigned int rqs = fifosz / 256 - 1;
@@ -240,8 +273,6 @@ static void dwmac4_dma_rx_chan_op_mode(struct stmmac_priv *priv,
mtl_rx_op = readl(ioaddr + MTL_CHAN_RX_OP_MODE(dwmac4_addrs, channel));
- mtl_rx_op |= MTL_OP_MODE_DIS_TCP_EF;
-
if (mode == SF_DMA_MODE) {
pr_debug("GMAC: enable RX store and forward mode\n");
mtl_rx_op |= MTL_OP_MODE_RSF;
@@ -259,6 +290,12 @@ static void dwmac4_dma_rx_chan_op_mode(struct stmmac_priv *priv,
mtl_rx_op |= MTL_OP_MODE_RTC_128;
}
+ /* Permit errorneous frames (IP/TCP/UDP csum, overflow, giant, etc) */
+ if (rxall)
+ mtl_rx_op |= MTL_OP_MODE_DIS_TCP_EF | MTL_OP_MODE_FEP | MTL_OP_MODE_FUP;
+ else
+ mtl_rx_op &= ~(MTL_OP_MODE_DIS_TCP_EF | MTL_OP_MODE_FEP | MTL_OP_MODE_FUP);
+
mtl_rx_op &= ~MTL_OP_MODE_RQS_MASK;
mtl_rx_op |= FIELD_PREP(MTL_OP_MODE_RQS_MASK, rqs);
@@ -448,6 +485,23 @@ static int dwmac4_get_hw_feature(void __iomem *ioaddr,
dma_cap->frpsel = (hw_cap & GMAC_HW_FEAT_FRPSEL) >> 10;
dma_cap->dvlan = (hw_cap & GMAC_HW_FEAT_DVLAN) >> 5;
+ /* Number of Extended VLAN Tag Filters */
+ dma_cap->nrvf_num = (hw_cap & GMAC_HW_FEAT_NRVF) >> 0;
+ switch (dma_cap->nrvf_num) {
+ case 1 ... 3:
+ dma_cap->nrvf_num = 1 << (dma_cap->nrvf_num + 1);
+ break;
+ case 4:
+ dma_cap->nrvf_num = 24;
+ break;
+ case 5:
+ dma_cap->nrvf_num = 32;
+ break;
+ default:
+ dma_cap->nrvf_num = 0;
+ break;
+ }
+
return 0;
}
@@ -543,6 +597,26 @@ static int dwmac4_enable_tbs(struct stmmac_priv *priv, void __iomem *ioaddr,
return 0;
}
+static void dwmac4_dma_diagnostic_fr(struct stmmac_priv *priv, void __iomem *ioaddr,
+ struct stmmac_extra_stats *x, u32 chan)
+{
+ const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
+ u32 value = readl(ioaddr + MTL_CHAN_RX_MISSED_PKT_CTR(dwmac4_addrs, chan));
+ unsigned long cntr;
+
+ cntr = FIELD_GET(MTL_MISSED_PKT_MISPKTCNT, value);
+ if (value & MTL_MISSED_PKT_MISCNTOVF)
+ cntr += FIELD_MAX(MTL_MISSED_PKT_MISPKTCNT) + 1;
+
+ x->rx_missed_cntr += cntr;
+
+ cntr = FIELD_GET(MTL_MISSED_PKT_OVFPKTCNT, value);
+ if (value & MTL_MISSED_PKT_OVFCNTOVF)
+ cntr += FIELD_MAX(MTL_MISSED_PKT_OVFPKTCNT) + 1;
+
+ x->rx_overflow_cntr += cntr;
+}
+
const struct stmmac_dma_ops dwmac4_dma_ops = {
.reset = dwmac4_dma_reset,
.init = dwmac4_dma_init,
@@ -553,6 +627,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = {
.dump_regs = dwmac4_dump_dma_regs,
.dma_rx_mode = dwmac4_dma_rx_chan_op_mode,
.dma_tx_mode = dwmac4_dma_tx_chan_op_mode,
+ .dma_diagnostic_fr = dwmac4_dma_diagnostic_fr,
.enable_dma_irq = dwmac4_enable_dma_irq,
.disable_dma_irq = dwmac4_disable_dma_irq,
.start_tx = dwmac4_dma_start_tx,
@@ -582,8 +657,9 @@ const struct stmmac_dma_ops dwmac410_dma_ops = {
.dump_regs = dwmac4_dump_dma_regs,
.dma_rx_mode = dwmac4_dma_rx_chan_op_mode,
.dma_tx_mode = dwmac4_dma_tx_chan_op_mode,
+ .dma_diagnostic_fr = dwmac4_dma_diagnostic_fr,
.enable_dma_irq = dwmac410_enable_dma_irq,
- .disable_dma_irq = dwmac4_disable_dma_irq,
+ .disable_dma_irq = dwmac410_disable_dma_irq,
.start_tx = dwmac4_dma_start_tx,
.stop_tx = dwmac4_dma_stop_tx,
.start_rx = dwmac4_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
index 9d9077a4ac9f1..677a606db5bea 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
@@ -24,13 +24,6 @@
#define DMA_SYS_BUS_MODE 0x00001004
-#define DMA_BUS_MODE_MB BIT(14)
-#define DMA_BUS_MODE_FB BIT(0)
-
-#define DMA_STATUS 0x00001008
-
-#define DMA_AXI_BUS_MODE 0x00001028
-
#define DMA_AXI_EN_LPI BIT(31)
#define DMA_AXI_LPI_XIT_FRM BIT(30)
#define DMA_AXI_WR_OSR_LMT GENMASK(27, 24)
@@ -41,6 +34,37 @@
#define DMA_SYS_BUS_EAME BIT(11)
#define DMA_SYS_BUS_FB BIT(0)
+#define DMA_STATUS 0x00001008
+
+#define DMA_AXI_TX_AR_ACE_CTRL 0x00001020
+
+#define DMA_AXI_TX_AR_THD GENMASK(21, 20)
+#define DMA_AXI_TX_AR_THC GENMASK(19, 16)
+#define DMA_AXI_TX_AR_TED GENMASK(13, 12)
+#define DMA_AXI_TX_AR_TEC GENMASK(11, 8)
+#define DMA_AXI_TX_AR_TDRD GENMASK(5, 4)
+#define DMA_AXI_TX_AR_TDRC GENMASK(3, 0)
+
+#define DMA_AXI_RX_AW_ACE_CTRL 0x00001024
+
+#define DMA_AXI_RX_AW_RDD GENMASK(29, 28)
+#define DMA_AXI_RX_AW_RDC GENMASK(27, 24)
+#define DMA_AXI_RX_AW_RHD GENMASK(21, 20)
+#define DMA_AXI_RX_AW_RHC GENMASK(19, 16)
+#define DMA_AXI_RX_AW_RPD GENMASK(13, 12)
+#define DMA_AXI_RX_AW_RPC GENMASK(11, 8)
+#define DMA_AXI_RX_AW_RDWD GENMASK(5, 4)
+#define DMA_AXI_RX_AW_RDWC GENMASK(3, 0)
+
+#define DMA_AXI_TXRX_AWAR_ACE_CTRL 0x00001028
+
+#define DMA_AXI_TXRX_AWAR_WRP GENMASK(22, 20)
+#define DMA_AXI_TXRX_AWAR_RDP GENMASK(18, 16)
+#define DMA_AXI_TXRX_AWAR_RDRD GENMASK(13, 12)
+#define DMA_AXI_TXRX_AWAR_RDRC GENMASK(11, 8)
+#define DMA_AXI_TXRX_AWAR_TDWD GENMASK(5, 4)
+#define DMA_AXI_TXRX_AWAR_TDWC GENMASK(3, 0)
+
#define DMA_TBS_CTRL 0x00001050
#define DMA_TBS_FTOS GENMASK(31, 8)
@@ -99,6 +123,7 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs,
#define DMA_CHAN_INTR_ENA_NIE_4_10 BIT(15)
#define DMA_CHAN_INTR_ENA_AIE_4_10 BIT(14)
#define DMA_CHAN_INTR_ENA_FBE BIT(12)
+#define DMA_CHAN_INTR_ENA_RBUE BIT(7)
#define DMA_CHAN_INTR_ENA_RIE BIT(6)
#define DMA_CHAN_INTR_ENA_TIE BIT(0)
@@ -107,11 +132,13 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs,
DMA_CHAN_INTR_ENA_TIE)
#define DMA_CHAN_INTR_ABNORMAL (DMA_CHAN_INTR_ENA_AIE | \
- DMA_CHAN_INTR_ENA_FBE)
+ DMA_CHAN_INTR_ENA_FBE | \
+ DMA_CHAN_INTR_ENA_RBUE)
/* DMA default interrupt mask for 4.00 */
#define DMA_CHAN_INTR_DEFAULT_MASK (DMA_CHAN_INTR_NORMAL | \
DMA_CHAN_INTR_ABNORMAL)
-#define DMA_CHAN_INTR_DEFAULT_RX (DMA_CHAN_INTR_ENA_RIE)
+#define DMA_CHAN_INTR_DEFAULT_RX (DMA_CHAN_INTR_ENA_RBUE | \
+ DMA_CHAN_INTR_ENA_RIE)
#define DMA_CHAN_INTR_DEFAULT_TX (DMA_CHAN_INTR_ENA_TIE)
#define DMA_CHAN_INTR_NORMAL_4_10 (DMA_CHAN_INTR_ENA_NIE_4_10 | \
@@ -119,11 +146,13 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs,
DMA_CHAN_INTR_ENA_TIE)
#define DMA_CHAN_INTR_ABNORMAL_4_10 (DMA_CHAN_INTR_ENA_AIE_4_10 | \
- DMA_CHAN_INTR_ENA_FBE)
+ DMA_CHAN_INTR_ENA_FBE | \
+ DMA_CHAN_INTR_ENA_RBUE)
/* DMA default interrupt mask for 4.10a */
#define DMA_CHAN_INTR_DEFAULT_MASK_4_10 (DMA_CHAN_INTR_NORMAL_4_10 | \
DMA_CHAN_INTR_ABNORMAL_4_10)
-#define DMA_CHAN_INTR_DEFAULT_RX_4_10 (DMA_CHAN_INTR_ENA_RIE)
+#define DMA_CHAN_INTR_DEFAULT_RX_4_10 (DMA_CHAN_INTR_ENA_RBUE | \
+ DMA_CHAN_INTR_ENA_RIE)
#define DMA_CHAN_INTR_DEFAULT_TX_4_10 (DMA_CHAN_INTR_ENA_TIE)
#define DMA_CHAN_RX_WATCHDOG(addrs, x) (dma_chanx_base_addr(addrs, x) + 0x38)
@@ -138,6 +167,7 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs,
/* Interrupt status per channel */
#define DMA_CHAN_STATUS_REB GENMASK(21, 19)
+#define DMA_CHAN_STATUS_TEB GENMASK(18, 16)
#define DMA_CHAN_STATUS_NIS BIT(15)
#define DMA_CHAN_STATUS_AIS BIT(14)
#define DMA_CHAN_STATUS_CDE BIT(13)
@@ -165,7 +195,8 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs,
DMA_CHAN_STATUS_RI | \
DMA_CHAN_STATUS_MSK_COMMON)
-#define DMA_CHAN_STATUS_MSK_TX (DMA_CHAN_STATUS_ETI | \
+#define DMA_CHAN_STATUS_MSK_TX (DMA_CHAN_STATUS_TEB | \
+ DMA_CHAN_STATUS_ETI | \
DMA_CHAN_STATUS_TBU | \
DMA_CHAN_STATUS_TPS | \
DMA_CHAN_STATUS_TI | \
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
index c098047a3bff8..851bfbac67874 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
@@ -181,19 +181,24 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
/* ABNORMAL interrupts */
if (unlikely(intr_status & DMA_CHAN_STATUS_AIS)) {
- if (unlikely(intr_status & DMA_CHAN_STATUS_RBU))
+ if (unlikely(intr_status & DMA_CHAN_STATUS_RBU)) {
x->rx_buf_unav_irq++;
+ ret = handle_rx;
+ }
+
if (unlikely(intr_status & DMA_CHAN_STATUS_RPS))
x->rx_process_stopped_irq++;
if (unlikely(intr_status & DMA_CHAN_STATUS_ETI))
x->tx_early_irq++;
- if (unlikely(intr_status & DMA_CHAN_STATUS_TPS)) {
+ if (unlikely(intr_status & DMA_CHAN_STATUS_TPS))
x->tx_process_stopped_irq++;
- ret = tx_hard_error;
- }
+
if (unlikely(intr_status & DMA_CHAN_STATUS_FBE)) {
x->fatal_bus_error_irq++;
- ret = tx_hard_error;
+ if (intr_status & DMA_CHAN_STATUS_REB)
+ ret = rx_hard_error;
+ if (intr_status & DMA_CHAN_STATUS_TEB)
+ ret = tx_hard_error;
}
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
index e1c37ac2c99d2..c34e4ccb3b68c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
@@ -83,14 +83,18 @@
/* DMA Abnormal interrupt */
#define DMA_INTR_ENA_AIE 0x00008000 /* Abnormal Summary */
#define DMA_INTR_ENA_FBE 0x00002000 /* Fatal Bus Error */
+#define DMA_INTR_ENA_RUE 0x00000080 /* Receive Buffer Unavailable */
#define DMA_INTR_ENA_UNE 0x00000020 /* Tx Underflow */
+#define DMA_INTR_ENA_OVE 0x00000010 /* Receive Overflow */
+#define DMA_INTR_ENA_TJE 0x00000008 /* Transmit Jabber */
#define DMA_INTR_ABNORMAL (DMA_INTR_ENA_AIE | DMA_INTR_ENA_FBE | \
- DMA_INTR_ENA_UNE)
+ DMA_INTR_ENA_RUE | DMA_INTR_ENA_UNE | \
+ DMA_INTR_ENA_OVE | DMA_INTR_ENA_TJE)
/* DMA default interrupt mask */
#define DMA_INTR_DEFAULT_MASK (DMA_INTR_NORMAL | DMA_INTR_ABNORMAL)
-#define DMA_INTR_DEFAULT_RX (DMA_INTR_ENA_RIE)
+#define DMA_INTR_DEFAULT_RX (DMA_INTR_ENA_RUE | DMA_INTR_ENA_RIE)
#define DMA_INTR_DEFAULT_TX (DMA_INTR_ENA_TIE)
#define DMA_MISSED_FRAME_CTR 0x00001020 /* Missed Frame Counter */
@@ -115,9 +119,16 @@ static inline u32 dma_chan_base_addr(u32 base, u32 chan)
#define DMA_CHAN_STATUS(chan) dma_chan_base_addr(DMA_STATUS, chan)
#define DMA_CHAN_CONTROL(chan) dma_chan_base_addr(DMA_CONTROL, chan)
#define DMA_CHAN_INTR_ENA(chan) dma_chan_base_addr(DMA_INTR_ENA, chan)
+#define DMA_CHAN_MISSED_FRAME_CTR(chan) \
+ dma_chan_base_addr(DMA_MISSED_FRAME_CTR, chan)
#define DMA_CHAN_RX_WATCHDOG(chan) \
dma_chan_base_addr(DMA_RX_WATCHDOG, chan)
+/* DMA Missed Frame Counter */
+#define DMA_MISSED_FRAME_OVFCNTOVF BIT(28)
+#define DMA_MISSED_FRAME_OVFFRMCNT GENMASK(27, 17)
+#define DMA_MISSED_FRAME_MISCNTOVF BIT(16)
+#define DMA_MISSED_FRAME_MISFRMCNT GENMASK(15, 0)
/* Rx watchdog register */
#define DMA_RX_WATCHDOG 0x00001024
@@ -156,8 +167,11 @@ void dwmac_dma_start_rx(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan);
void dwmac_dma_stop_rx(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan);
+bool dwmac_dma_suspended(void __iomem *ioaddr, u32 chan, u32 dir);
int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
struct stmmac_extra_stats *x, u32 chan, u32 dir);
+void dwmac_dma_diagnostic_fr(struct stmmac_priv *priv, void __iomem *ioaddr,
+ struct stmmac_extra_stats *x, u32 chan);
int dwmac_dma_reset(void __iomem *ioaddr);
#endif /* __DWMAC_DMA_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
index a0383f9486c2d..031deffde9d9e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
@@ -158,6 +158,24 @@ static void show_rx_process_state(unsigned int status)
}
#endif
+bool dwmac_dma_suspended(void __iomem *ioaddr, u32 chan, u32 dir)
+{
+ u32 status = readl(ioaddr + DMA_CHAN_STATUS(chan));
+ u32 ts, rs;
+
+ if (dir == DMA_DIR_TX) {
+ ts = FIELD_GET(DMA_STATUS_TS_MASK, status);
+ return ts == 6;
+ } else if (dir == DMA_DIR_RX) {
+ rs = FIELD_GET(DMA_STATUS_RS_MASK, status);
+ return rs == 4;
+ }
+
+ ts = FIELD_GET(DMA_STATUS_TS_MASK, status);
+ rs = FIELD_GET(DMA_STATUS_RS_MASK, status);
+ return ts == 6 && rs == 4;
+}
+
int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
struct stmmac_extra_stats *x, u32 chan, u32 dir)
{
@@ -181,30 +199,37 @@ int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
/* ABNORMAL interrupts */
if (unlikely(intr_status & DMA_STATUS_AIS)) {
if (unlikely(intr_status & DMA_STATUS_UNF)) {
- ret = tx_hard_error_bump_tc;
x->tx_undeflow_irq++;
+ ret = tx_unf_error;
}
- if (unlikely(intr_status & DMA_STATUS_TJT))
+
+ if (unlikely(intr_status & DMA_STATUS_TJT)) {
x->tx_jabber_irq++;
+ ret = tx_soft_stop;
+ }
- if (unlikely(intr_status & DMA_STATUS_OVF))
+ if (unlikely(intr_status & DMA_STATUS_OVF)) {
x->rx_overflow_irq++;
+ ret = rx_ovf_error;
+ }
- if (unlikely(intr_status & DMA_STATUS_RU))
+ if (unlikely(intr_status & DMA_STATUS_RU)) {
x->rx_buf_unav_irq++;
+ ret = handle_rx;
+ }
+
if (unlikely(intr_status & DMA_STATUS_RPS))
x->rx_process_stopped_irq++;
if (unlikely(intr_status & DMA_STATUS_RWT))
x->rx_watchdog_irq++;
if (unlikely(intr_status & DMA_STATUS_ETI))
x->tx_early_irq++;
- if (unlikely(intr_status & DMA_STATUS_TPS)) {
+ if (unlikely(intr_status & DMA_STATUS_TPS))
x->tx_process_stopped_irq++;
- ret = tx_hard_error;
- }
+
if (unlikely(intr_status & DMA_STATUS_FBI)) {
x->fatal_bus_error_irq++;
- ret = tx_hard_error;
+ ret = io_fatal_error;
}
}
/* TX/RX NORMAL interrupts */
@@ -229,8 +254,7 @@ int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
x->rx_early_irq++;
}
/* Optional hardware blocks, interrupts should be disabled */
- if (unlikely(intr_status &
- (DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI)))
+ if (unlikely(intr_status & (DMA_STATUS_GPI | DMA_STATUS_GMI)))
pr_warn("%s: unexpected status %08x\n", __func__, intr_status);
/* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
@@ -239,12 +263,23 @@ int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
return ret;
}
-void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr)
+void dwmac_dma_diagnostic_fr(struct stmmac_priv *priv, void __iomem *ioaddr,
+ struct stmmac_extra_stats *x, u32 chan)
{
- u32 csr6 = readl(ioaddr + DMA_CONTROL);
- writel((csr6 | DMA_CONTROL_FTF), ioaddr + DMA_CONTROL);
+ u32 csr8 = readl(ioaddr + DMA_CHAN_MISSED_FRAME_CTR(chan));
+ unsigned long cntr;
+
+ cntr = FIELD_GET(DMA_MISSED_FRAME_OVFFRMCNT, csr8);
+ if (csr8 & DMA_MISSED_FRAME_OVFCNTOVF)
+ cntr += FIELD_MAX(DMA_MISSED_FRAME_OVFFRMCNT) + 1;
+
+ x->rx_overflow_cntr += cntr;
+
+ cntr = FIELD_GET(DMA_MISSED_FRAME_MISFRMCNT, csr8);
+ if (csr8 & DMA_MISSED_FRAME_MISCNTOVF)
+ cntr += FIELD_MAX(DMA_MISSED_FRAME_MISFRMCNT) + 1;
- do {} while ((readl(ioaddr + DMA_CONTROL) & DMA_CONTROL_FTF));
+ x->rx_missed_cntr += cntr;
}
void stmmac_set_mac_addr(void __iomem *ioaddr, const u8 addr[6],
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
index 51943705a2b03..c40c59ed2626a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
@@ -9,9 +9,6 @@
#include "common.h"
-/* Misc */
-#define XGMAC_JUMBO_LEN 16368
-
/* MAC Registers */
#define XGMAC_TX_CONFIG 0x00000000
#define XGMAC_CONFIG_SS_OFF 29
@@ -26,7 +23,6 @@
#define XGMAC_CONFIG_SARC GENMASK(22, 20)
#define XGMAC_CONFIG_JD BIT(16)
#define XGMAC_CONFIG_TE BIT(0)
-#define XGMAC_CORE_INIT_TX (XGMAC_CONFIG_JD)
#define XGMAC_RX_CONFIG 0x00000004
#define XGMAC_CONFIG_ARPEN BIT(31)
#define XGMAC_CONFIG_GPSL GENMASK(29, 16)
@@ -42,10 +38,6 @@
#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_GPSLCE | \
- XGMAC_CONFIG_WD | \
- FIELD_PREP(XGMAC_CONFIG_GPSL, \
- XGMAC_JUMBO_LEN))
#define XGMAC_PACKET_FILTER 0x00000008
#define XGMAC_FILTER_RA BIT(31)
#define XGMAC_FILTER_IPFE BIT(20)
@@ -55,6 +47,9 @@
#define XGMAC_FILTER_PM BIT(4)
#define XGMAC_FILTER_HMC BIT(2)
#define XGMAC_FILTER_PR BIT(0)
+#define XGMAC_WDT 0x0000000c
+#define XGMAC_WDT_PWE BIT(8)
+#define XGMAC_WDT_WTO GENMASK(3, 0)
#define XGMAC_HASH_TABLE(x) (0x00000010 + (x) * 4)
#define XGMAC_MAX_HASH_TABLE 8
#define XGMAC_RXQ_CTRL0 0x000000a0
@@ -87,7 +82,7 @@
#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_INT_DEFAULT_EN (XGMAC_PMTIE)
#define XGMAC_Qx_TX_FLOW_CTRL(x) (0x00000070 + (x) * 4)
#define XGMAC_PT GENMASK(31, 16)
#define XGMAC_TFE BIT(1)
@@ -101,6 +96,15 @@
#define XGMAC_LPI_CTRL 0x000000d0
/* For definitions, see LPI_CTRL_STATUS_xxx in common.h */
#define XGMAC_LPI_TIMER_CTRL 0x000000d4
+#define XGMAC_DEBUG 0x00000114
+#define XGMAC_TFCSTS_MASK GENMASK(18, 17)
+#define XGMAC_TFCSTS_IDLE 0
+#define XGMAC_TFCSTS_WAIT 1
+#define XGMAC_TFCSTS_GEN_PAUSE 2
+#define XGMAC_TFCSTS_XFER 3
+#define XGMAC_TPESTS BIT(16)
+#define XGMAC_RFCFCSTS_MASK GENMASK(2, 1)
+#define XGMAC_RPESTS BIT(0)
#define XGMAC_HW_FEATURE0 0x0000011c
#define XGMAC_HWFEAT_EDMA BIT(31)
#define XGMAC_HWFEAT_EDIFFC BIT(30)
@@ -257,7 +261,7 @@
#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(x) GENMASK((x) * 8 + 6, (x) * 8)
#define XGMAC_QxMDMACH_SHIFT(x) ((x) * 8)
#define XGMAC_QDDMACH BIT(7)
#define XGMAC_TC_PRTY_MAP0 0x00001040
@@ -291,6 +295,16 @@
#define XGMAC_TTC GENMASK(6, 4)
#define XGMAC_TXQEN GENMASK(3, 2)
#define XGMAC_TSF BIT(1)
+#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_MASK GENMASK(2, 1)
+#define XGMAC_TRCSTS_IDLE 0
+#define XGMAC_TRCSTS_READ 1
+#define XGMAC_TRCSTS_TXW 2
+#define XGMAC_TRCSTS_WRITE 3
+#define XGMAC_TXPAUSED 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)))
@@ -304,13 +318,35 @@
#define XGMAC_MTL_RXQ_OPMODE(x) (0x00001140 + (0x80 * (x)))
#define XGMAC_RQS GENMASK(25, 16)
#define XGMAC_EHFC BIT(7)
+#define XGMAC_DIS_TCP_EF BIT(6)
#define XGMAC_RSF BIT(5)
+#define XGMAC_FEF BIT(4)
+#define XGMAC_FUP BIT(3)
#define XGMAC_RTC GENMASK(1, 0)
+#define XGMAC_MTL_RXQ_MISSED_PKT_CTR(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_RXQSTS_MASK GENMASK(5, 4)
+#define XGMAC_RXQSTS_EMPTY 0
+#define XGMAC_RXQSTS_BT 1
+#define XGMAC_RXQSTS_AT 2
+#define XGMAC_RXQSTS_FULL 3
+#define XGMAC_RRCSTS_MASK GENMASK(2, 1)
+#define XGMAC_RRCSTS_IDLE 0
+#define XGMAC_RRCSTS_RDATA 1
+#define XGMAC_RRCSTS_RSTAT 2
+#define XGMAC_RRCSTS_FLUSH 3
+#define XGMAC_RWCSTS BIT(0)
#define XGMAC_MTL_RXQ_FLOW_CONTROL(x) (0x00001150 + (0x80 * (x)))
#define XGMAC_RFD GENMASK(31, 17)
#define XGMAC_RFA GENMASK(15, 1)
#define XGMAC_MTL_QINTEN(x) (0x00001170 + (0x80 * (x)))
#define XGMAC_RXOIE BIT(16)
+#define XGMAC_TXUIE BIT(0)
+#define XGMAC_MTL_INT_DEFAULT_EN (XGMAC_RXOIE | XGMAC_TXUIE)
#define XGMAC_MTL_QINT_STATUS(x) (0x00001174 + (0x80 * (x)))
#define XGMAC_RXOVFIS BIT(16)
#define XGMAC_ABPSIS BIT(1)
@@ -319,6 +355,8 @@
/* DMA Registers */
#define XGMAC_DMA_MODE 0x00003000
+#define XGMAC_INTM GENMASK(13, 12)
+#define XGMAC_INTM_MODE1 0x1
#define XGMAC_SWR BIT(0)
#define XGMAC_DMA_SYSBUS_MODE 0x00003004
#define XGMAC_WR_OSR_LMT GENMASK(29, 24)
@@ -329,6 +367,29 @@
#define XGMAC_EAME BIT(11)
/* XGMAC_BLEN* are now defined as DMA_AXI_BLEN* in common.h */
#define XGMAC_UNDEF BIT(0)
+#define XGMAC_AXI_TX_AR_ACE_CTRL 0x00003010
+#define XGMAC_AXI_TX_AR_THD GENMASK(21, 20)
+#define XGMAC_AXI_TX_AR_THC GENMASK(19, 16)
+#define XGMAC_AXI_TX_AR_TED GENMASK(13, 12)
+#define XGMAC_AXI_TX_AR_TEC GENMASK(11, 8)
+#define XGMAC_AXI_TX_AR_TDRD GENMASK(5, 4)
+#define XGMAC_AXI_TX_AR_TDRC GENMASK(3, 0)
+#define XGMAC_AXI_RX_AW_ACE_CTRL 0x00003018
+#define XGMAC_AXI_RX_AW_RDD GENMASK(29, 28)
+#define XGMAC_AXI_RX_AW_RDC GENMASK(27, 24)
+#define XGMAC_AXI_RX_AW_RHD GENMASK(21, 20)
+#define XGMAC_AXI_RX_AW_RHC GENMASK(19, 16)
+#define XGMAC_AXI_RX_AW_RPD GENMASK(13, 12)
+#define XGMAC_AXI_RX_AW_RPC GENMASK(11, 8)
+#define XGMAC_AXI_RX_AW_RDWD GENMASK(5, 4)
+#define XGMAC_AXI_RX_AW_RDWC GENMASK(3, 0)
+#define XGMAC_AXI_TXRX_AWAR_ACE_CTRL 0x0000301c
+#define XGMAC_AXI_TXRX_AWAR_WRP GENMASK(22, 20)
+#define XGMAC_AXI_TXRX_AWAR_RDP GENMASK(18, 16)
+#define XGMAC_AXI_TXRX_AWAR_RDRD GENMASK(13, 12)
+#define XGMAC_AXI_TXRX_AWAR_RDRC GENMASK(11, 8)
+#define XGMAC_AXI_TXRX_AWAR_TDWD GENMASK(5, 4)
+#define XGMAC_AXI_TXRX_AWAR_TDWC GENMASK(3, 0)
#define XGMAC_TX_EDMA_CTRL 0x00003040
#define XGMAC_TDPS GENMASK(29, 0)
#define XGMAC_RX_EDMA_CTRL 0x00003044
@@ -375,20 +436,24 @@
#define XGMAC_DMA_CH_INT_EN(x) (0x00003138 + (0x80 * (x)))
#define XGMAC_NIE BIT(15)
#define XGMAC_AIE BIT(14)
+#define XGMAC_FBEE BIT(12)
#define XGMAC_RBUE BIT(7)
#define XGMAC_RIE BIT(6)
#define XGMAC_TBUE BIT(2)
#define XGMAC_TIE BIT(0)
-#define XGMAC_DMA_INT_DEFAULT_EN (XGMAC_NIE | XGMAC_AIE | XGMAC_RBUE | \
- XGMAC_RIE | XGMAC_TIE)
+#define XGMAC_DMA_INT_DEFAULT_EN (XGMAC_NIE | XGMAC_AIE | XGMAC_FBEE | \
+ XGMAC_RBUE | XGMAC_RIE | XGMAC_TIE)
#define XGMAC_DMA_INT_DEFAULT_RX (XGMAC_RBUE | XGMAC_RIE)
#define XGMAC_DMA_INT_DEFAULT_TX (XGMAC_TIE)
#define XGMAC_DMA_CH_Rx_WATCHDOG(x) (0x0000313c + (0x80 * (x)))
#define XGMAC_RWT GENMASK(7, 0)
#define XGMAC_DMA_CH_STATUS(x) (0x00003160 + (0x80 * (x)))
+#define XGMAC_REB GENMASK(21, 19)
+#define XGMAC_TEB GENMASK(18, 16)
#define XGMAC_NIS BIT(15)
#define XGMAC_AIS BIT(14)
#define XGMAC_FBE BIT(12)
+#define XGMAC_RPS BIT(8)
#define XGMAC_RBU BIT(7)
#define XGMAC_RI BIT(6)
#define XGMAC_TBU BIT(2)
@@ -397,9 +462,11 @@
#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 | \
+#define XGMAC_DMA_STATUS_MSK_RX (XGMAC_REB | XGMAC_RBU | \
+ XGMAC_RPS | XGMAC_RI | \
XGMAC_DMA_STATUS_MSK_COMMON)
-#define XGMAC_DMA_STATUS_MSK_TX (XGMAC_TBU | XGMAC_TPS | XGMAC_TI | \
+#define XGMAC_DMA_STATUS_MSK_TX (XGMAC_TEB | XGMAC_TBU | \
+ XGMAC_TPS | XGMAC_TI | \
XGMAC_DMA_STATUS_MSK_COMMON)
/* Descriptors */
@@ -429,6 +496,14 @@
#define XGMAC_TDES3_VLTV BIT(16)
#define XGMAC_TDES3_VT GENMASK(15, 0)
#define XGMAC_TDES3_FL GENMASK(14, 0)
+#define XGMAC_RDES2_L4FM BIT(28)
+#define XGMAC_RDES2_L3FM BIT(27)
+#define XGMAC_RDES2_MADRM GENMASK(26, 19)
+#define XGMAC_RDES2_HF BIT(18)
+#define XGMAC_RDES2_DAF BIT(17)
+#define XGMAC_RDES2_SAF BIT(16)
+#define XGMAC_RDES2_VF BIT(15)
+#define XGMAC_RDES2_RPNG BIT(14)
#define XGMAC_RDES2_HL GENMASK(9, 0)
#define XGMAC_RDES3_OWN BIT(31)
#define XGMAC_RDES3_CTXT BIT(30)
@@ -437,11 +512,39 @@
#define XGMAC_RDES3_CDA BIT(27)
#define XGMAC_RDES3_RSV BIT(26)
#define XGMAC_RDES3_L34T GENMASK(23, 20)
-#define XGMAC_RDES3_ET_LT GENMASK(19, 16)
+#define XGMAC_L34T_NOTIP 0x0
#define XGMAC_L34T_IP4TCP 0x1
#define XGMAC_L34T_IP4UDP 0x2
+#define XGMAC_L34T_IP4ICMP 0x3
+#define XGMAC_L34T_IP4IGMP 0x4
+#define XGMAC_L34T_IP4UNKNOWN 0x7
#define XGMAC_L34T_IP6TCP 0x9
#define XGMAC_L34T_IP6UDP 0xA
+#define XGMAC_L34T_IP6ICMP 0xB
+#define XGMAC_L34T_IP6UNKNOWN 0xF
+#define XGMAC_RDES3_ET_LT GENMASK(19, 16)
+#define XGMAC_ET_RECEIVE_WATCHDOG 1
+#define XGMAC_ET_XGMII_ERROR 2
+#define XGMAC_ET_CRC_ERROR 3
+#define XGMAC_ET_GIANT_PACKET 4
+#define XGMAC_ET_IP_HDR_ERROR 5
+#define XGMAC_ET_IP_PAYLOAD_ERROR 6
+#define XGMAC_ET_OVERFLOW_ERROR 7
+#define XGMAC_ET_DESC_ERROR 8
+#define XGMAC_L2T_LENGTH_PACKET 0
+#define XGMAC_L2T_MAC_CONTROL 1
+#define XGMAC_L2T_DCB_CONTROL 2
+#define XGMAC_L2T_ARP_REQUEST 3
+#define XGMAC_L2T_OAM_PACKET 4
+#define XGMAC_L2T_CUSTOM_TYPE 5
+#define XGMAC_L2T_AV_CONTROL 6
+#define XGMAC_L2T_OTHER_TYPE 7
+#define XGMAC_L2T_VLAN_STAG 8
+#define XGMAC_L2T_VLAN_CTAG 9
+#define XGMAC_L2T_DVLAN_CCTAG 10
+#define XGMAC_L2T_DVLAN_SSTAG 11
+#define XGMAC_L2T_DVLAN_SCTAG 12
+#define XGMAC_L2T_DVLAN_CSTAG 13
#define XGMAC_RDES3_ES BIT(15)
#define XGMAC_RDES3_PL GENMASK(13, 0)
#define XGMAC_RDES3_TSD BIT(6)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index 49893b9fb88c4..7e4457bfa86f2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -18,13 +18,43 @@ static void dwxgmac2_core_init(struct mac_device_info *hw,
struct net_device *dev)
{
void __iomem *ioaddr = hw->pcsr;
- u32 tx, rx;
+ int mtu = dev->mtu;
+ u32 tx, rx, gps;
tx = readl(ioaddr + XGMAC_TX_CONFIG);
rx = readl(ioaddr + XGMAC_RX_CONFIG);
- writel(tx | XGMAC_CORE_INIT_TX, ioaddr + XGMAC_TX_CONFIG);
- writel(rx | XGMAC_CORE_INIT_RX, ioaddr + XGMAC_RX_CONFIG);
+ /* Frame length limits (giant status reported or dropped) */
+ gps = ETH_HLEN + ETH_FCS_LEN;
+ if (mtu > 16357) { /* Rx <= 16375 (+C/SVLAN headers) && Tx <= 16383 */
+ tx |= XGMAC_CONFIG_JD;
+ gps += 16357;
+ rx |= XGMAC_CONFIG_GPSLCE | FIELD_PREP(XGMAC_CONFIG_GPSL, gps);
+ } else if (mtu > 2022) { /* Rx <= MTU + Eth (+C/SVLAN headers) && Tx <= 16383 */
+ tx |= XGMAC_CONFIG_JD;
+ gps += mtu;
+ rx |= XGMAC_CONFIG_GPSLCE | FIELD_PREP(XGMAC_CONFIG_GPSL, gps);
+ } else if (mtu > 1500) { /* Rx <= MTU + Eth (+C/SVLAN headers) && Tx <= 2048 */
+ gps += mtu;
+ rx |= XGMAC_CONFIG_GPSLCE | FIELD_PREP(XGMAC_CONFIG_GPSL, gps);
+ } else { /* Rx <= 1500 + Eth (+C/SVLAN headers) && Tx <= 2048 */
+ gps += 1500;
+ }
+
+ writel(tx, ioaddr + XGMAC_TX_CONFIG);
+ writel(rx, ioaddr + XGMAC_RX_CONFIG);
+
+ /* Over 2K, 3K, ..., 16K-1 frames will be truncated on Rx */
+ gps = ALIGN(gps + 2 * VLAN_HLEN, SZ_1K);
+ if (gps >= SZ_2K)
+ gps = gps / SZ_1K - 2;
+ else
+ gps = 0;
+
+ /* Over giant frame watchdog fine-tuning (available since v2.00a) */
+ rx = FIELD_PREP(XGMAC_WDT_WTO, gps) | XGMAC_WDT_PWE;
+ writel(rx, ioaddr + XGMAC_WDT);
+
writel(XGMAC_INT_DEFAULT_EN, ioaddr + XGMAC_INT_EN);
}
@@ -65,13 +95,37 @@ static void dwxgmac2_set_mac(void __iomem *ioaddr, bool enable)
writel(rx, ioaddr + XGMAC_RX_CONFIG);
}
-static int dwxgmac2_rx_ipc(struct mac_device_info *hw)
+static void dwxgmac2_rx_fcs(struct mac_device_info *hw, bool enable)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ u32 value;
+
+ value = readl(ioaddr + XGMAC_RX_CONFIG);
+
+ if (enable)
+ value &= ~XGMAC_CONFIG_CST;
+ else
+ value |= XGMAC_CONFIG_CST;
+
+ writel(value, ioaddr + XGMAC_RX_CONFIG);
+}
+
+static int dwxgmac2_rx_fcs_status(struct mac_device_info *hw, int snps_id,
+ int status)
+{
+ if (unlikely(status & llc_snap))
+ return -ENOTSUPP;
+
+ return 0;
+}
+
+static int dwxgmac2_rx_ipc(struct mac_device_info *hw, bool enable)
{
void __iomem *ioaddr = hw->pcsr;
u32 value;
value = readl(ioaddr + XGMAC_RX_CONFIG);
- if (hw->rx_csum)
+ if (enable)
value |= XGMAC_CONFIG_IPC;
else
value &= ~XGMAC_CONFIG_IPC;
@@ -251,6 +305,24 @@ static void dwxgmac2_set_mtl_tx_queue_weight(struct stmmac_priv *priv,
writel(weight, ioaddr + XGMAC_MTL_TCx_QUANTUM_WEIGHT(queue));
}
+static void dwxgmac2_map_mtl_to_dyn(struct mac_device_info *hw, u32 queue,
+ bool enable)
+{
+ void __iomem *ioaddr = hw->pcsr;
+ u32 value, reg;
+
+ reg = (queue < 4) ? XGMAC_MTL_RXQ_DMA_MAP0 : XGMAC_MTL_RXQ_DMA_MAP1;
+ if (queue >= 4)
+ queue -= 4;
+
+ value = readl(ioaddr + reg);
+ if (enable)
+ value |= (XGMAC_QDDMACH << XGMAC_QxMDMACH_SHIFT(queue));
+ else
+ value &= ~(XGMAC_QDDMACH << XGMAC_QxMDMACH_SHIFT(queue));
+ writel(value, ioaddr + reg);
+}
+
static void dwxgmac2_map_mtl_to_dma(struct mac_device_info *hw, u32 queue,
u32 chan)
{
@@ -346,8 +418,10 @@ static int dwxgmac2_host_mtl_irq_status(struct stmmac_priv *priv,
if (chan_status & XGMAC_RXOVFIS)
ret |= CORE_IRQ_MTL_RX_OVERFLOW;
+ if (chan_status & XGMAC_TXUNFIS)
+ ret |= CORE_IRQ_MTL_TX_UNDERFLOW;
- writel(~0x0, ioaddr + XGMAC_MTL_QINT_STATUS(chan));
+ writel(chan_status, ioaddr + XGMAC_MTL_QINT_STATUS(chan));
}
return ret;
@@ -452,13 +526,18 @@ static int dwxgmac2_set_lpi_mode(struct mac_device_info *hw,
static void dwxgmac2_set_eee_pls(struct mac_device_info *hw, int link)
{
void __iomem *ioaddr = hw->pcsr;
- u32 value;
+ u32 value, en;
value = readl(ioaddr + XGMAC_LPI_CTRL);
- if (link)
+ en = readl(ioaddr + XGMAC_INT_EN);
+ if (link) {
value |= LPI_CTRL_STATUS_PLS;
- else
+ en |= XGMAC_LPIIE;
+ } else {
+ en &= ~XGMAC_LPIIE;
value &= ~LPI_CTRL_STATUS_PLS;
+ }
+ writel(en, ioaddr + XGMAC_INT_EN);
writel(value, ioaddr + XGMAC_LPI_CTRL);
}
@@ -498,25 +577,29 @@ static void dwxgmac2_set_filter(struct mac_device_info *hw,
struct net_device *dev)
{
void __iomem *ioaddr = (void __iomem *)dev->base_addr;
- u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
+ struct stmmac_priv *priv = netdev_priv(dev);
int mcbitslog2 = hw->mcast_bits_log2;
- u32 mc_filter[8];
- int i;
+ u32 value, mc_filter[8];
+
+ value = readl(ioaddr + XGMAC_PACKET_FILTER);
+ value &= ~XGMAC_FILTER_RA;
+ value &= ~XGMAC_FILTER_PCF;
+ value &= ~XGMAC_FILTER_PM;
+ value &= ~XGMAC_FILTER_HMC;
+ value &= ~XGMAC_FILTER_PR;
- value &= ~(XGMAC_FILTER_PR | XGMAC_FILTER_HMC | XGMAC_FILTER_PM);
value |= XGMAC_FILTER_HPF;
memset(mc_filter, 0, sizeof(mc_filter));
if (dev->flags & IFF_PROMISC) {
+ value |= XGMAC_FILTER_RA;
value |= XGMAC_FILTER_PR;
value |= XGMAC_FILTER_PCF;
} else if ((dev->flags & IFF_ALLMULTI) ||
(netdev_mc_count(dev) > hw->multicast_filter_bins)) {
value |= XGMAC_FILTER_PM;
-
- for (i = 0; i < XGMAC_MAX_HASH_TABLE; i++)
- writel(~0x0, ioaddr + XGMAC_HASH_TABLE(i));
+ memset(mc_filter, 0xff, sizeof(mc_filter));
} else if (!netdev_mc_empty(dev) && (dev->flags & IFF_MULTICAST)) {
struct netdev_hw_addr *ha;
@@ -532,7 +615,7 @@ static void dwxgmac2_set_filter(struct mac_device_info *hw,
dwxgmac2_set_mchash(ioaddr, mc_filter, mcbitslog2);
/* Handle multiple unicast addresses */
- if (netdev_uc_count(dev) > hw->unicast_filter_entries) {
+ if (netdev_uc_count(dev) > hw->unicast_filter_entries - 1) {
value |= XGMAC_FILTER_PR;
} else {
struct netdev_hw_addr *ha;
@@ -549,9 +632,96 @@ static void dwxgmac2_set_filter(struct mac_device_info *hw,
}
}
+ /* VLAN filtering */
+ if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ stmmac_restore_hw_vlan_rx_fltr(priv, dev, hw);
+
writel(value, ioaddr + XGMAC_PACKET_FILTER);
}
+static void dwxgmac2_debug(struct stmmac_priv *priv, void __iomem *ioaddr,
+ struct stmmac_extra_stats *x,
+ u32 rx_queues, u32 tx_queues)
+{
+ u32 value, queue;
+
+ for (queue = 0; queue < tx_queues; queue++) {
+ value = readl(ioaddr + XGMAC_MTL_TXQ_DEBUG(queue));
+
+ if (value & XGMAC_TXQSTS)
+ x->mtl_tx_fifo_not_empty++;
+ if (value & XGMAC_TWCSTS)
+ x->mmtl_fifo_ctrl++;
+ if (value & XGMAC_TRCSTS_MASK) {
+ u32 trcsts = FIELD_GET(XGMAC_TRCSTS_MASK, value);
+
+ if (trcsts == XGMAC_TRCSTS_WRITE)
+ x->mtl_tx_fifo_read_ctrl_write++;
+ else if (trcsts == XGMAC_TRCSTS_TXW)
+ x->mtl_tx_fifo_read_ctrl_wait++;
+ else if (trcsts == XGMAC_TRCSTS_READ)
+ x->mtl_tx_fifo_read_ctrl_read++;
+ else
+ x->mtl_tx_fifo_read_ctrl_idle++;
+ }
+ if (value & XGMAC_TXPAUSED)
+ x->mac_tx_in_pause++;
+ }
+
+ for (queue = 0; queue < rx_queues; queue++) {
+ value = readl(ioaddr + XGMAC_MTL_RXQ_DEBUG(queue));
+
+ if (value & XGMAC_RXQSTS_MASK) {
+ u32 rxfsts = FIELD_GET(XGMAC_RXQSTS_MASK, value);
+
+ if (rxfsts == XGMAC_RXQSTS_FULL)
+ x->mtl_rx_fifo_fill_level_full++;
+ else if (rxfsts == XGMAC_RXQSTS_AT)
+ x->mtl_rx_fifo_fill_above_thresh++;
+ else if (rxfsts == XGMAC_RXQSTS_BT)
+ x->mtl_rx_fifo_fill_below_thresh++;
+ else
+ x->mtl_rx_fifo_fill_level_empty++;
+ }
+ if (value & XGMAC_RRCSTS_MASK) {
+ u32 rrcsts = FIELD_GET(XGMAC_RRCSTS_MASK, value);
+
+ if (rrcsts == XGMAC_RRCSTS_FLUSH)
+ x->mtl_rx_fifo_read_ctrl_flush++;
+ else if (rrcsts == XGMAC_RRCSTS_RSTAT)
+ x->mtl_rx_fifo_read_ctrl_read_data++;
+ else if (rrcsts == XGMAC_RRCSTS_RDATA)
+ x->mtl_rx_fifo_read_ctrl_status++;
+ else
+ x->mtl_rx_fifo_read_ctrl_idle++;
+ }
+ if (value & XGMAC_RWCSTS)
+ x->mtl_rx_fifo_ctrl_active++;
+ }
+
+ /* XGMAC debug */
+ value = readl(ioaddr + XGMAC_DEBUG);
+
+ if (value & XGMAC_TFCSTS_MASK) {
+ u32 tfcsts = FIELD_GET(XGMAC_TFCSTS_MASK, value);
+
+ if (tfcsts == XGMAC_TFCSTS_XFER)
+ x->mac_tx_frame_ctrl_xfer++;
+ else if (tfcsts == XGMAC_TFCSTS_GEN_PAUSE)
+ x->mac_tx_frame_ctrl_pause++;
+ else if (tfcsts == XGMAC_TFCSTS_WAIT)
+ x->mac_tx_frame_ctrl_wait++;
+ else
+ x->mac_tx_frame_ctrl_idle++;
+ }
+ if (value & XGMAC_TPESTS)
+ x->mac_gmii_tx_proto_engine++;
+ if (value & XGMAC_RFCFCSTS_MASK)
+ x->mac_rx_frame_ctrl_fifo = FIELD_GET(XGMAC_RFCFCSTS_MASK, value);
+ if (value & XGMAC_RPESTS)
+ x->mac_gmii_rx_proto_engine++;
+}
+
static void dwxgmac2_set_mac_loopback(void __iomem *ioaddr, bool enable)
{
u32 value = readl(ioaddr + XGMAC_RX_CONFIG);
@@ -580,16 +750,20 @@ static int dwxgmac2_rss_write_reg(void __iomem *ioaddr, bool is_key, int idx,
}
static int dwxgmac2_rss_configure(struct mac_device_info *hw,
- struct stmmac_rss *cfg, u32 num_rxq)
+ struct stmmac_rss *cfg, bool enable, u32 num_rxq)
{
void __iomem *ioaddr = hw->pcsr;
u32 value, *key;
int i, ret;
value = readl(ioaddr + XGMAC_RSS_CTRL);
- if (!cfg || !cfg->enable) {
+ if (!enable) {
value &= ~XGMAC_RSSE;
writel(value, ioaddr + XGMAC_RSS_CTRL);
+
+ for (i = 0; i < num_rxq; i++)
+ dwxgmac2_map_mtl_to_dyn(hw, i, false);
+
return 0;
}
@@ -607,7 +781,7 @@ static int dwxgmac2_rss_configure(struct mac_device_info *hw,
}
for (i = 0; i < num_rxq; i++)
- dwxgmac2_map_mtl_to_dma(hw, i, XGMAC_QDDMACH);
+ dwxgmac2_map_mtl_to_dyn(hw, i, true);
value |= XGMAC_UDP4TE | XGMAC_TCP4TE | XGMAC_IP2TE | XGMAC_RSSE;
writel(value, ioaddr + XGMAC_RSS_CTRL);
@@ -1429,6 +1603,8 @@ const struct stmmac_ops dwxgmac210_ops = {
.irq_modify = dwxgmac2_irq_modify,
.update_caps = dwxgmac2_update_caps,
.set_mac = dwxgmac2_set_mac,
+ .rx_fcs = dwxgmac2_rx_fcs,
+ .rx_fcs_status = dwxgmac2_rx_fcs_status,
.rx_ipc = dwxgmac2_rx_ipc,
.rx_queue_enable = dwxgmac2_rx_queue_enable,
.rx_queue_prio = dwxgmac2_rx_queue_prio,
@@ -1449,7 +1625,7 @@ const struct stmmac_ops dwxgmac210_ops = {
.set_lpi_mode = dwxgmac2_set_lpi_mode,
.set_eee_timer = dwxgmac2_set_eee_timer,
.set_eee_pls = dwxgmac2_set_eee_pls,
- .debug = NULL,
+ .debug = dwxgmac2_debug,
.set_filter = dwxgmac2_set_filter,
.safety_feat_config = dwxgmac3_safety_feat_config,
.safety_feat_irq_status = dwxgmac3_safety_feat_irq_status,
@@ -1484,6 +1660,8 @@ const struct stmmac_ops dwxlgmac2_ops = {
.core_init = dwxgmac2_core_init,
.irq_modify = dwxgmac2_irq_modify,
.set_mac = dwxgmac2_set_mac,
+ .rx_fcs = dwxgmac2_rx_fcs,
+ .rx_fcs_status = dwxgmac2_rx_fcs_status,
.rx_ipc = dwxgmac2_rx_ipc,
.rx_queue_enable = dwxlgmac2_rx_queue_enable,
.rx_queue_prio = dwxgmac2_rx_queue_prio,
@@ -1504,7 +1682,7 @@ const struct stmmac_ops dwxlgmac2_ops = {
.set_lpi_mode = dwxgmac2_set_lpi_mode,
.set_eee_timer = dwxgmac2_set_eee_timer,
.set_eee_pls = dwxgmac2_set_eee_pls,
- .debug = NULL,
+ .debug = dwxgmac2_debug,
.set_filter = dwxgmac2_set_filter,
.safety_feat_config = dwxgmac3_safety_feat_config,
.safety_feat_irq_status = dwxgmac3_safety_feat_irq_status,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c
index 41e5b420a2151..8a4b9a09310d4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c
@@ -10,7 +10,7 @@
#include "dwxgmac2.h"
static int dwxgmac2_get_tx_status(struct stmmac_extra_stats *x,
- struct dma_desc *p, void __iomem *ioaddr)
+ struct dma_desc *p)
{
u32 tdes3 = le32_to_cpu(p->des3);
int ret = tx_done;
@@ -26,7 +26,10 @@ static int dwxgmac2_get_tx_status(struct stmmac_extra_stats *x,
static int dwxgmac2_get_rx_status(struct stmmac_extra_stats *x,
struct dma_desc *p)
{
+ u32 rdes2 = le32_to_cpu(p->des2);
u32 rdes3 = le32_to_cpu(p->des3);
+ int ret = good_frame;
+ int l34t;
if (unlikely(rdes3 & XGMAC_RDES3_OWN))
return dma_own;
@@ -34,15 +37,101 @@ static int dwxgmac2_get_rx_status(struct stmmac_extra_stats *x,
return discard_frame;
if (likely(!(rdes3 & XGMAC_RDES3_LD)))
return rx_not_ls;
- if (unlikely((rdes3 & XGMAC_RDES3_ES) && (rdes3 & XGMAC_RDES3_LD)))
- return discard_frame;
- return good_frame;
-}
+ if (likely(!(rdes3 & XGMAC_RDES3_ES))) {
+ int l2t = FIELD_GET(XGMAC_RDES3_ET_LT, rdes3);
-static int dwxgmac2_get_tx_len(struct dma_desc *p)
-{
- return (le32_to_cpu(p->des2) & XGMAC_TDES2_B1L);
+ switch (l2t) {
+ case XGMAC_L2T_LENGTH_PACKET:
+ ret |= llc_snap;
+ break;
+ case XGMAC_L2T_ARP_REQUEST:
+ x->arp_pkt_ofld++;
+ if (likely(!(rdes2 & XGMAC_RDES2_RPNG)))
+ ret |= arp_done;
+ break;
+ case XGMAC_L2T_AV_CONTROL:
+ x->av_pkt_rcvd++;
+ break;
+ case XGMAC_L2T_VLAN_STAG ... XGMAC_L2T_DVLAN_CSTAG:
+ x->rx_vlan++;
+ break;
+ }
+ } else {
+ int et = FIELD_GET(XGMAC_RDES3_ET_LT, rdes3);
+
+ switch (et) {
+ case XGMAC_ET_RECEIVE_WATCHDOG:
+ x->rx_watchdog++;
+ ret |= cutoff_err;
+ break;
+ case XGMAC_ET_XGMII_ERROR:
+ x->rx_mii++;
+ ret |= proto_err;
+ break;
+ case XGMAC_ET_CRC_ERROR:
+ x->rx_crc_errors++;
+ ret |= csum_err;
+ break;
+ case XGMAC_ET_GIANT_PACKET:
+ x->rx_length++;
+ ret |= len_err;
+ break;
+ case XGMAC_ET_IP_HDR_ERROR:
+ x->ip_hdr_err++;
+ ret |= csum_err;
+ break;
+ case XGMAC_ET_IP_PAYLOAD_ERROR:
+ x->ip_payload_err++;
+ ret |= csum_err;
+ break;
+ case XGMAC_ET_OVERFLOW_ERROR:
+ x->rx_gmac_overflow++;
+ ret |= cutoff_err;
+ break;
+ case XGMAC_ET_DESC_ERROR:
+ x->rx_desc++;
+ ret |= cutoff_err;
+ break;
+ }
+ }
+
+ l34t = FIELD_GET(XGMAC_RDES3_L34T, rdes3);
+
+ switch (l34t) {
+ case XGMAC_L34T_IP4UNKNOWN:
+ x->ip_csum_bypassed++;
+ ret |= csum_none;
+ fallthrough;
+ case XGMAC_L34T_IP4TCP ... XGMAC_L34T_IP4IGMP:
+ x->ipv4_pkt_rcvd++;
+ break;
+ case XGMAC_L34T_IP6UNKNOWN:
+ x->ip_csum_bypassed++;
+ ret |= csum_none;
+ fallthrough;
+ case XGMAC_L34T_IP6TCP ... XGMAC_L34T_IP6ICMP:
+ x->ipv6_pkt_rcvd++;
+ break;
+ case XGMAC_L34T_NOTIP:
+ x->ip_csum_bypassed++;
+ ret |= csum_none;
+ break;
+ }
+
+ if (unlikely(rdes2 & XGMAC_RDES2_SAF))
+ x->sa_rx_filter_fail++;
+
+ if (unlikely(rdes2 & XGMAC_RDES2_DAF))
+ x->da_rx_filter_fail++;
+
+ if (unlikely(rdes2 & XGMAC_RDES2_L3FM))
+ x->l3_filter_match++;
+
+ if (unlikely(rdes2 & XGMAC_RDES2_L4FM))
+ x->l4_filter_match++;
+
+ return ret;
}
static int dwxgmac2_get_tx_owner(struct dma_desc *p)
@@ -70,6 +159,26 @@ static int dwxgmac2_get_tx_ls(struct dma_desc *p)
return (le32_to_cpu(p->des3) & XGMAC_RDES3_LD) > 0;
}
+static u16 dwxgmac2_wrback_get_rx_vlan_tpid(struct dma_desc *p)
+{
+ u32 l2t;
+
+ l2t = FIELD_GET(XGMAC_RDES3_ET_LT, le32_to_cpu(p->des3));
+
+ switch (l2t) {
+ case XGMAC_L2T_VLAN_STAG:
+ case XGMAC_L2T_DVLAN_SSTAG:
+ case XGMAC_L2T_DVLAN_SCTAG:
+ return ETH_P_8021AD;
+ case XGMAC_L2T_VLAN_CTAG:
+ case XGMAC_L2T_DVLAN_CCTAG:
+ case XGMAC_L2T_DVLAN_CSTAG:
+ return ETH_P_8021Q;
+ default:
+ return 0;
+ }
+}
+
static u16 dwxgmac2_wrback_get_rx_vlan_tci(struct dma_desc *p)
{
return le32_to_cpu(p->des0) & XGMAC_RDES0_VLAN_TAG_MASK;
@@ -77,15 +186,16 @@ static u16 dwxgmac2_wrback_get_rx_vlan_tci(struct dma_desc *p)
static bool dwxgmac2_wrback_get_rx_vlan_valid(struct dma_desc *p)
{
- u32 et_lt;
+ u32 des3 = le32_to_cpu(p->des3);
+ u32 l2t;
- et_lt = FIELD_GET(XGMAC_RDES3_ET_LT, le32_to_cpu(p->des3));
+ l2t = FIELD_GET(XGMAC_RDES3_ET_LT, des3);
- return et_lt >= XGMAC_ET_LT_VLAN_STAG &&
- et_lt <= XGMAC_ET_LT_DVLAN_STAG_CTAG;
+ return (des3 & XGMAC_RDES3_LD) && !(des3 & XGMAC_RDES3_ES) &&
+ (l2t >= XGMAC_L2T_VLAN_STAG) && (l2t <= XGMAC_L2T_DVLAN_CSTAG);
}
-static int dwxgmac2_get_rx_frame_len(struct dma_desc *p, int rx_coe)
+static int dwxgmac2_get_rx_frame_len(struct dma_desc *p)
{
return (le32_to_cpu(p->des3) & XGMAC_RDES3_PL);
}
@@ -147,7 +257,12 @@ static int dwxgmac2_get_rx_timestamp_status(void *desc, void *next_desc,
static void dwxgmac2_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
int mode, int end, int bfsize)
{
- dwxgmac2_set_rx_owner(p, disable_rx_ic);
+ u32 flags = XGMAC_RDES3_OWN;
+
+ if (!disable_rx_ic)
+ flags |= XGMAC_RDES3_IOC;
+
+ p->des3 |= cpu_to_le32(flags);
}
static void dwxgmac2_init_tx_desc(struct dma_desc *p, int mode, int end)
@@ -158,6 +273,38 @@ static void dwxgmac2_init_tx_desc(struct dma_desc *p, int mode, int end)
p->des3 = 0;
}
+static unsigned int dwxgmac2_get_rx_len(int mode)
+{
+ return STMMAC_RX_BUF_ALIGN(FIELD_MAX(XGMAC_RBSZ));
+}
+
+static void dwxgmac2_release_rx_desc(struct dma_desc *p, int disable_rx_ic,
+ int is_fs, bool rx_own)
+{
+ unsigned int rdes3 = le32_to_cpu(p->des3);
+
+ if (disable_rx_ic)
+ rdes3 &= cpu_to_le32(~XGMAC_RDES3_IOC);
+ else
+ rdes3 |= cpu_to_le32(XGMAC_RDES3_IOC);
+
+ if (rx_own)
+ rdes3 |= cpu_to_le32(XGMAC_RDES3_OWN);
+
+ /* Before releasing the initial descriptor make sure that all
+ * the previous writes are visible to the controller.
+ */
+ if (is_fs && rx_own)
+ dma_wmb();
+
+ p->des3 = cpu_to_le32(rdes3);
+}
+
+static unsigned int dwxgmac2_get_tx_len(int mode)
+{
+ return FIELD_MAX(XGMAC_TDES2_B1L);
+}
+
static void dwxgmac2_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
bool csum_flag, int mode, bool tx_own,
bool ls, unsigned int tot_pkt_len)
@@ -234,7 +381,8 @@ static void dwxgmac2_prepare_tso_tx_desc(struct dma_desc *p, int is_fs,
p->des3 = cpu_to_le32(tdes3);
}
-static void dwxgmac2_release_tx_desc(struct dma_desc *p, int mode)
+static void dwxgmac2_release_tx_desc(struct dma_desc *p, int mode,
+ dma_addr_t np, bool hwts_tx)
{
p->des0 = 0;
p->des1 = 0;
@@ -305,8 +453,13 @@ static void dwxgmac2_get_rx_header_len(struct dma_desc *p, unsigned int *len)
static void dwxgmac2_set_sec_addr(struct dma_desc *p, dma_addr_t addr, bool is_valid)
{
- p->des2 = cpu_to_le32(lower_32_bits(addr));
- p->des3 = cpu_to_le32(upper_32_bits(addr));
+ if (is_valid) {
+ p->des2 = cpu_to_le32(lower_32_bits(addr));
+ p->des3 = cpu_to_le32(upper_32_bits(addr));
+ } else {
+ p->des2 = 0;
+ p->des3 = 0;
+ }
}
static void dwxgmac2_set_sarc(struct dma_desc *p, u32 sarc_type)
@@ -352,14 +505,35 @@ static void dwxgmac2_set_tbs(struct dma_edesc *p, u32 sec, u32 nsec)
p->des7 = 0;
}
+static void dwxgmac2_display_ring(void *head, unsigned int size, bool rx,
+ dma_addr_t dma_rx_phy, unsigned int desc_size)
+{
+ struct dma_desc *p = (struct dma_desc *)head;
+ dma_addr_t dma_addr;
+ int i;
+
+ pr_info("%s descriptor ring:\n", rx ? "RX" : "TX");
+
+ for (i = 0; i < size; i++) {
+ dma_addr = dma_rx_phy + i * sizeof(*p);
+ pr_info("%03d [%pad]: 0x%x 0x%x 0x%x 0x%x\n",
+ i, &dma_addr,
+ le32_to_cpu(p->des0), le32_to_cpu(p->des1),
+ le32_to_cpu(p->des2), le32_to_cpu(p->des3));
+ p++;
+ }
+}
+
const struct stmmac_desc_ops dwxgmac210_desc_ops = {
.tx_status = dwxgmac2_get_tx_status,
.rx_status = dwxgmac2_get_rx_status,
+ .get_rx_len = dwxgmac2_get_rx_len,
.get_tx_len = dwxgmac2_get_tx_len,
.get_tx_owner = dwxgmac2_get_tx_owner,
.set_tx_owner = dwxgmac2_set_tx_owner,
.set_rx_owner = dwxgmac2_set_rx_owner,
.get_tx_ls = dwxgmac2_get_tx_ls,
+ .get_rx_vlan_tpid = dwxgmac2_wrback_get_rx_vlan_tpid,
.get_rx_vlan_tci = dwxgmac2_wrback_get_rx_vlan_tci,
.get_rx_vlan_valid = dwxgmac2_wrback_get_rx_vlan_valid,
.get_rx_frame_len = dwxgmac2_get_rx_frame_len,
@@ -368,6 +542,7 @@ const struct stmmac_desc_ops dwxgmac210_desc_ops = {
.get_rx_timestamp_status = dwxgmac2_get_rx_timestamp_status,
.get_timestamp = dwxgmac2_get_timestamp,
.set_tx_ic = dwxgmac2_set_tx_ic,
+ .release_rx_desc = dwxgmac2_release_rx_desc,
.prepare_tx_desc = dwxgmac2_prepare_tx_desc,
.prepare_tso_tx_desc = dwxgmac2_prepare_tso_tx_desc,
.release_tx_desc = dwxgmac2_release_tx_desc,
@@ -383,4 +558,5 @@ const struct stmmac_desc_ops dwxgmac210_desc_ops = {
.set_vlan_tag = dwxgmac2_set_vlan_tag,
.set_vlan = dwxgmac2_set_vlan,
.set_tbs = dwxgmac2_set_tbs,
+ .display_ring = dwxgmac2_display_ring,
};
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
index 03437f1cf3df3..dabca4e4b8e0d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
@@ -6,6 +6,7 @@
#include <linux/iopoll.h>
#include "stmmac.h"
+#include "stmmac_vlan.h"
#include "dwxgmac2.h"
static int dwxgmac2_dma_reset(void __iomem *ioaddr)
@@ -31,6 +32,12 @@ static void dwxgmac2_dma_init(void __iomem *ioaddr,
value |= XGMAC_EAME;
writel(value, ioaddr + XGMAC_DMA_SYSBUS_MODE);
+
+ if (dma_cfg->multi_msi_en) {
+ value = readl(ioaddr + XGMAC_DMA_MODE);
+ FIELD_MODIFY(XGMAC_INTM, &value, XGMAC_INTM_MODE1);
+ writel(value, ioaddr + XGMAC_DMA_MODE);
+ }
}
static void dwxgmac2_dma_init_chan(struct stmmac_priv *priv,
@@ -44,6 +51,8 @@ static void dwxgmac2_dma_init_chan(struct stmmac_priv *priv,
writel(value, ioaddr + XGMAC_DMA_CH_CONTROL(chan));
writel(XGMAC_DMA_INT_DEFAULT_EN, ioaddr + XGMAC_DMA_CH_INT_EN(chan));
+
+ writel(XGMAC_MTL_INT_DEFAULT_EN, ioaddr + XGMAC_MTL_QINTEN(chan));
}
static void dwxgmac2_dma_init_rx_chan(struct stmmac_priv *priv,
@@ -101,6 +110,31 @@ static void dwxgmac2_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
value = (value & ~DMA_AXI_BLEN_MASK) | axi->axi_blen_regval;
writel(value, ioaddr + XGMAC_DMA_SYSBUS_MODE);
+
+ if (axi->axi_cc) {
+ writel(STMMAC_AXI_ACE(XGMAC, TX_AR_THD, D_OUTS) |
+ STMMAC_AXI_ACE(XGMAC, TX_AR_THC, AR_WbNa) |
+ STMMAC_AXI_ACE(XGMAC, TX_AR_TED, D_OUTS) |
+ STMMAC_AXI_ACE(XGMAC, TX_AR_TEC, AR_WbNa) |
+ STMMAC_AXI_ACE(XGMAC, TX_AR_TDRD, D_OUTS) |
+ STMMAC_AXI_ACE(XGMAC, TX_AR_TDRC, AR_WbNa),
+ ioaddr + XGMAC_AXI_TX_AR_ACE_CTRL);
+ writel(STMMAC_AXI_ACE(XGMAC, RX_AW_RDD, D_OUTS) |
+ STMMAC_AXI_ACE(XGMAC, RX_AW_RDC, AW_WbNa) |
+ STMMAC_AXI_ACE(XGMAC, RX_AW_RHD, D_OUTS) |
+ STMMAC_AXI_ACE(XGMAC, RX_AW_RHC, AW_WbNa) |
+ STMMAC_AXI_ACE(XGMAC, RX_AW_RPD, D_OUTS) |
+ STMMAC_AXI_ACE(XGMAC, RX_AW_RPC, AW_WbNa) |
+ STMMAC_AXI_ACE(XGMAC, RX_AW_RDWD, D_OUTS) |
+ STMMAC_AXI_ACE(XGMAC, RX_AW_RDWC, AW_WbNa),
+ ioaddr + XGMAC_AXI_RX_AW_ACE_CTRL);
+ writel(STMMAC_AXI_ACE(XGMAC, TXRX_AWAR_RDRD, D_OUTS) |
+ STMMAC_AXI_ACE(XGMAC, TXRX_AWAR_RDRC, AR_WbNa) |
+ STMMAC_AXI_ACE(XGMAC, TXRX_AWAR_TDWD, D_OUTS) |
+ STMMAC_AXI_ACE(XGMAC, TXRX_AWAR_TDWC, AW_WbNa),
+ ioaddr + XGMAC_AXI_TXRX_AWAR_ACE_CTRL);
+ }
+
writel(XGMAC_TDPS, ioaddr + XGMAC_TX_EDMA_CTRL);
writel(XGMAC_RDPS, ioaddr + XGMAC_RX_EDMA_CTRL);
}
@@ -115,7 +149,8 @@ static void dwxgmac2_dma_dump_regs(struct stmmac_priv *priv,
}
static void dwxgmac2_dma_rx_mode(struct stmmac_priv *priv, void __iomem *ioaddr,
- int mode, u32 channel, int fifosz, u8 qmode)
+ int mode, u32 channel, int fifosz, u8 qmode,
+ bool rxall)
{
u32 value = readl(ioaddr + XGMAC_MTL_RXQ_OPMODE(channel));
unsigned int rqs = fifosz / 256 - 1;
@@ -136,6 +171,12 @@ static void dwxgmac2_dma_rx_mode(struct stmmac_priv *priv, void __iomem *ioaddr,
value = u32_replace_bits(value, rtc, XGMAC_RTC);
}
+ /* Permit errorneous frames (IP/TCP/UDP csum, overflow, giant, etc) */
+ if (rxall)
+ value |= XGMAC_DIS_TCP_EF | XGMAC_FEF | XGMAC_FUP;
+ else
+ value &= ~(XGMAC_DIS_TCP_EF | XGMAC_FEF | XGMAC_FUP);
+
value = u32_replace_bits(value, rqs, XGMAC_RQS);
if ((fifosz >= 4096) && (qmode != MTL_QUEUE_AVB)) {
@@ -319,13 +360,18 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv,
x->rx_buf_unav_irq++;
ret |= handle_rx;
}
- if (unlikely(intr_status & XGMAC_TPS)) {
+
+ if (unlikely(intr_status & XGMAC_RPS))
+ x->rx_process_stopped_irq++;
+ if (unlikely(intr_status & XGMAC_TPS))
x->tx_process_stopped_irq++;
- ret |= tx_hard_error;
- }
+
if (unlikely(intr_status & XGMAC_FBE)) {
x->fatal_bus_error_irq++;
- ret |= tx_hard_error;
+ if (intr_status & XGMAC_REB)
+ ret |= rx_hard_error;
+ if (intr_status & XGMAC_TEB)
+ ret |= tx_hard_error;
}
}
@@ -336,7 +382,7 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv,
u64_stats_update_end(&stats->syncp);
ret |= handle_rx;
}
- if (likely(intr_status & (XGMAC_TI | XGMAC_TBU))) {
+ if (likely(intr_status & XGMAC_TI)) {
u64_stats_update_begin(&stats->syncp);
u64_stats_inc(&stats->tx_normal_irq_n[chan]);
u64_stats_update_end(&stats->syncp);
@@ -349,6 +395,20 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv,
return ret;
}
+static bool dwxgmac2_det_hw_dvlan(void __iomem *ioaddr)
+{
+ u32 tmp;
+
+ tmp = readl(ioaddr + VLAN_TAG);
+ writel(tmp ^ VLAN_EIVLRXS, ioaddr + VLAN_TAG);
+ if (tmp != readl(ioaddr + VLAN_TAG)) {
+ writel(tmp, ioaddr + VLAN_TAG);
+ return true;
+ }
+
+ return false;
+}
+
static int dwxgmac2_get_hw_feature(void __iomem *ioaddr,
struct dma_features *dma_cap)
{
@@ -453,14 +513,35 @@ static int dwxgmac2_get_hw_feature(void __iomem *ioaddr,
dma_cap->estsel = (hw_cap & XGMAC_HWFEAT_ESTSEL) >> 19;
dma_cap->ttsfd = (hw_cap & XGMAC_HWFEAT_TTSFD) >> 16;
dma_cap->asp = (hw_cap & XGMAC_HWFEAT_ASP) >> 14;
+
+ /* Double VLAN Tagging (flag is unavailable prior DW XGMAC v3.00a) */
dma_cap->dvlan = (hw_cap & XGMAC_HWFEAT_DVLAN) >> 13;
+ if (!dma_cap->dvlan)
+ dma_cap->dvlan = dwxgmac2_det_hw_dvlan(ioaddr);
+
dma_cap->frpes = (hw_cap & XGMAC_HWFEAT_FRPES) >> 11;
dma_cap->frpbs = (hw_cap & XGMAC_HWFEAT_FRPPB) >> 9;
dma_cap->pou_ost_en = (hw_cap & XGMAC_HWFEAT_POUOST) >> 8;
dma_cap->frppipe_num = ((hw_cap & XGMAC_HWFEAT_FRPPIPE) >> 5) + 1;
dma_cap->cbtisel = (hw_cap & XGMAC_HWFEAT_CBTISEL) >> 4;
dma_cap->frpsel = (hw_cap & XGMAC_HWFEAT_FRPSEL) >> 3;
+
+ /* Number of Extended VLAN Tag Filters */
dma_cap->nrvf_num = (hw_cap & XGMAC_HWFEAT_NRVF) >> 0;
+ switch (dma_cap->nrvf_num) {
+ case 1 ... 3:
+ dma_cap->nrvf_num = 1 << (dma_cap->nrvf_num + 1);
+ break;
+ case 4:
+ dma_cap->nrvf_num = 24;
+ break;
+ case 5:
+ dma_cap->nrvf_num = 32;
+ break;
+ default:
+ dma_cap->nrvf_num = 0;
+ break;
+ }
/* MAC HW feature 4 */
hw_cap = readl(ioaddr + XGMAC_HW_FEATURE4);
@@ -582,6 +663,25 @@ static int dwxgmac2_enable_tbs(struct stmmac_priv *priv, void __iomem *ioaddr,
return 0;
}
+static void dwxgmac2_dma_diagnostic_fr(struct stmmac_priv *priv, void __iomem *ioaddr,
+ struct stmmac_extra_stats *x, u32 chan)
+{
+ u32 value = readl(ioaddr + XGMAC_MTL_RXQ_MISSED_PKT_CTR(chan));
+ unsigned long cntr;
+
+ cntr = FIELD_GET(XGMAC_MISPKTCNT, value);
+ if (value & XGMAC_MISCNTOVF)
+ cntr += FIELD_MAX(XGMAC_MISPKTCNT) + 1;
+
+ x->rx_missed_cntr += cntr;
+
+ cntr = FIELD_GET(XGMAC_OVFPKTCNT, value);
+ if (value & XGMAC_OVFCNTOVF)
+ cntr += FIELD_MAX(XGMAC_OVFPKTCNT) + 1;
+
+ x->rx_overflow_cntr += cntr;
+}
+
const struct stmmac_dma_ops dwxgmac210_dma_ops = {
.reset = dwxgmac2_dma_reset,
.init = dwxgmac2_dma_init,
@@ -592,6 +692,7 @@ const struct stmmac_dma_ops dwxgmac210_dma_ops = {
.dump_regs = dwxgmac2_dma_dump_regs,
.dma_rx_mode = dwxgmac2_dma_rx_mode,
.dma_tx_mode = dwxgmac2_dma_tx_mode,
+ .dma_diagnostic_fr = dwxgmac2_dma_diagnostic_fr,
.enable_dma_irq = dwxgmac2_enable_dma_irq,
.disable_dma_irq = dwxgmac2_disable_dma_irq,
.start_tx = dwxgmac2_dma_start_tx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
index 8f6993c8bcae5..01ac6cc72fa68 100644
--- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
@@ -13,7 +13,7 @@
#include "descs_com.h"
static int enh_desc_get_tx_status(struct stmmac_extra_stats *x,
- struct dma_desc *p, void __iomem *ioaddr)
+ struct dma_desc *p)
{
u32 tdes0 = le32_to_cpu(p->des0);
int ret = tx_done;
@@ -30,10 +30,8 @@ static int enh_desc_get_tx_status(struct stmmac_extra_stats *x,
if (unlikely(tdes0 & ETDES0_JABBER_TIMEOUT))
x->tx_jabber++;
- if (unlikely(tdes0 & ETDES0_FRAME_FLUSHED)) {
+ if (unlikely(tdes0 & ETDES0_FRAME_FLUSHED))
x->tx_frame_flushed++;
- dwmac_dma_flush_tx_fifo(ioaddr);
- }
if (unlikely(tdes0 & ETDES0_LOSS_CARRIER)) {
x->tx_losscarrier++;
@@ -49,18 +47,14 @@ static int enh_desc_get_tx_status(struct stmmac_extra_stats *x,
if (unlikely(tdes0 & ETDES0_EXCESSIVE_DEFERRAL))
x->tx_deferred++;
- if (unlikely(tdes0 & ETDES0_UNDERFLOW_ERROR)) {
- dwmac_dma_flush_tx_fifo(ioaddr);
+ if (unlikely(tdes0 & ETDES0_UNDERFLOW_ERROR))
x->tx_underflow++;
- }
if (unlikely(tdes0 & ETDES0_IP_HEADER_ERROR))
x->tx_ip_header_error++;
- if (unlikely(tdes0 & ETDES0_PAYLOAD_ERROR)) {
+ if (unlikely(tdes0 & ETDES0_PAYLOAD_ERROR))
x->tx_payload_error++;
- dwmac_dma_flush_tx_fifo(ioaddr);
- }
ret = tx_err;
}
@@ -76,171 +70,157 @@ static int enh_desc_get_tx_status(struct stmmac_extra_stats *x,
return ret;
}
-static int enh_desc_get_tx_len(struct dma_desc *p)
-{
- return (le32_to_cpu(p->des1) & ETDES1_BUFFER1_SIZE_MASK);
-}
-
-static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)
+static int enh_desc_rx_ext_status(unsigned int rdes4,
+ struct stmmac_extra_stats *x)
{
+ int message_type = FIELD_GET(ERDES4_MSG_TYPE_MASK, rdes4);
int ret = good_frame;
- u32 status = (type << 2 | ipc_err << 1 | payload_err) & 0x7;
-
- /* bits 5 7 0 | Frame status
- * ----------------------------------------------------------
- * 0 0 0 | IEEE 802.3 Type frame (length < 1536 octets)
- * 1 0 0 | IPv4/6 No CSUM errorS.
- * 1 0 1 | IPv4/6 CSUM PAYLOAD error
- * 1 1 0 | IPv4/6 CSUM IP HR error
- * 1 1 1 | IPv4/6 IP PAYLOAD AND HEADER errorS
- * 0 0 1 | IPv4/6 unsupported IP PAYLOAD
- * 0 1 1 | COE bypassed.. no IPv4/6 frame
- * 0 1 0 | Reserved.
- */
- if (status == 0x0)
- ret = llc_snap;
- else if (status == 0x4)
- ret = good_frame;
- else if (status == 0x5)
- ret = csum_none;
- else if (status == 0x6)
+
+ if (rdes4 & ERDES4_IP_HDR_ERR) {
+ x->ip_hdr_err++;
+ ret = csum_err;
+ }
+
+ if (rdes4 & ERDES4_IP_PAYLOAD_ERR) {
+ x->ip_payload_err++;
+ ret = csum_err;
+ }
+
+ if (rdes4 & ERDES4_IP_CSUM_BYPASSED) {
+ x->ip_csum_bypassed++;
ret = csum_none;
- else if (status == 0x7)
+ }
+
+ if (rdes4 & ERDES4_IPV4_PKT_RCVD)
+ x->ipv4_pkt_rcvd++;
+ else if (rdes4 & ERDES4_IPV6_PKT_RCVD)
+ x->ipv6_pkt_rcvd++;
+ else /* Not an IP packet or COE is disabled */
ret = csum_none;
- else if (status == 0x1)
- ret = discard_frame;
- else if (status == 0x3)
- ret = discard_frame;
- return ret;
-}
-static void enh_desc_get_ext_status(struct stmmac_extra_stats *x,
- struct dma_extended_desc *p)
-{
- u32 rdes0 = le32_to_cpu(p->basic.des0);
- u32 rdes4 = le32_to_cpu(p->des4);
-
- if (unlikely(rdes0 & ERDES0_RX_MAC_ADDR)) {
- int message_type = FIELD_GET(ERDES4_MSG_TYPE_MASK, rdes4);
-
- if (rdes4 & ERDES4_IP_HDR_ERR)
- x->ip_hdr_err++;
- if (rdes4 & ERDES4_IP_PAYLOAD_ERR)
- x->ip_payload_err++;
- if (rdes4 & ERDES4_IP_CSUM_BYPASSED)
- x->ip_csum_bypassed++;
- if (rdes4 & ERDES4_IPV4_PKT_RCVD)
- x->ipv4_pkt_rcvd++;
- if (rdes4 & ERDES4_IPV6_PKT_RCVD)
- x->ipv6_pkt_rcvd++;
-
- if (message_type == RDES_EXT_NO_PTP)
- x->no_ptp_rx_msg_type_ext++;
- else if (message_type == RDES_EXT_SYNC)
- x->ptp_rx_msg_type_sync++;
- else if (message_type == RDES_EXT_FOLLOW_UP)
- x->ptp_rx_msg_type_follow_up++;
- else if (message_type == RDES_EXT_DELAY_REQ)
- x->ptp_rx_msg_type_delay_req++;
- else if (message_type == RDES_EXT_DELAY_RESP)
- x->ptp_rx_msg_type_delay_resp++;
- else if (message_type == RDES_EXT_PDELAY_REQ)
- x->ptp_rx_msg_type_pdelay_req++;
- else if (message_type == RDES_EXT_PDELAY_RESP)
- x->ptp_rx_msg_type_pdelay_resp++;
- else if (message_type == RDES_EXT_PDELAY_FOLLOW_UP)
- x->ptp_rx_msg_type_pdelay_follow_up++;
- else if (message_type == RDES_PTP_ANNOUNCE)
- x->ptp_rx_msg_type_announce++;
- else if (message_type == RDES_PTP_MANAGEMENT)
- x->ptp_rx_msg_type_management++;
- else if (message_type == RDES_PTP_PKT_RESERVED_TYPE)
- x->ptp_rx_msg_pkt_reserved_type++;
-
- if (rdes4 & ERDES4_PTP_FRAME_TYPE)
- x->ptp_frame_type++;
- if (rdes4 & ERDES4_PTP_VER)
- x->ptp_ver++;
- if (rdes4 & ERDES4_TIMESTAMP_DROPPED)
- x->timestamp_dropped++;
- if (rdes4 & ERDES4_AV_PKT_RCVD)
- x->av_pkt_rcvd++;
- if (rdes4 & ERDES4_AV_TAGGED_PKT_RCVD)
- x->av_tagged_pkt_rcvd++;
- if (rdes4 & ERDES4_VLAN_TAG_PRI_VAL_MASK)
- x->vlan_tag_priority_val++;
- if (rdes4 & ERDES4_L3_FILTER_MATCH)
- x->l3_filter_match++;
- if (rdes4 & ERDES4_L4_FILTER_MATCH)
- x->l4_filter_match++;
- if (rdes4 & ERDES4_L3_L4_FILT_NO_MATCH_MASK)
- x->l3_l4_filter_no_match++;
+ switch (message_type) {
+ case RDES_EXT_NO_PTP:
+ x->no_ptp_rx_msg_type_ext++;
+ break;
+ case RDES_EXT_SYNC:
+ x->ptp_rx_msg_type_sync++;
+ break;
+ case RDES_EXT_FOLLOW_UP:
+ x->ptp_rx_msg_type_follow_up++;
+ break;
+ case RDES_EXT_DELAY_REQ:
+ x->ptp_rx_msg_type_delay_req++;
+ break;
+ case RDES_EXT_DELAY_RESP:
+ x->ptp_rx_msg_type_delay_resp++;
+ break;
+ case RDES_EXT_PDELAY_REQ:
+ x->ptp_rx_msg_type_pdelay_req++;
+ break;
+ case RDES_EXT_PDELAY_RESP:
+ x->ptp_rx_msg_type_pdelay_resp++;
+ break;
+ case RDES_EXT_PDELAY_FOLLOW_UP:
+ x->ptp_rx_msg_type_pdelay_follow_up++;
+ break;
+ case RDES_PTP_ANNOUNCE:
+ x->ptp_rx_msg_type_announce++;
+ break;
+ case RDES_PTP_MANAGEMENT:
+ x->ptp_rx_msg_type_management++;
+ break;
+ case RDES_PTP_PKT_RESERVED_TYPE:
+ x->ptp_rx_msg_pkt_reserved_type++;
+ break;
+ default:
+ break;
}
+
+ if (rdes4 & ERDES4_PTP_FRAME_TYPE)
+ x->ptp_frame_type++;
+ if (rdes4 & ERDES4_PTP_VER)
+ x->ptp_ver++;
+ if (rdes4 & ERDES4_TIMESTAMP_DROPPED)
+ x->timestamp_dropped++;
+ if (rdes4 & ERDES4_AV_PKT_RCVD)
+ x->av_pkt_rcvd++;
+ if (rdes4 & ERDES4_AV_TAGGED_PKT_RCVD)
+ x->av_tagged_pkt_rcvd++;
+ if (rdes4 & ERDES4_VLAN_TAG_PRI_VAL_MASK)
+ x->vlan_tag_priority_val++;
+ if (rdes4 & ERDES4_L3_FILTER_MATCH)
+ x->l3_filter_match++;
+ if (rdes4 & ERDES4_L4_FILTER_MATCH)
+ x->l4_filter_match++;
+ if (rdes4 & ERDES4_L3_L4_FILT_NO_MATCH_MASK)
+ x->l3_l4_filter_no_match++;
+
+ return ret;
}
-static int enh_desc_get_rx_status(struct stmmac_extra_stats *x,
- struct dma_desc *p)
+static int enh_desc_get_rx_basic_status(unsigned int rdes0,
+ struct stmmac_extra_stats *x)
{
- u32 rdes0 = le32_to_cpu(p->des0);
int ret = good_frame;
if (unlikely(rdes0 & RDES0_OWN))
return dma_own;
- if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) {
- x->rx_length++;
- return discard_frame;
- }
+ if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR)))
+ return rx_not_ls;
+
+ if (unlikely(!(rdes0 & RDES0_FRAME_TYPE)))
+ ret = llc_snap;
if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) {
if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) {
x->rx_desc++;
- x->rx_length++;
+ ret |= cutoff_err;
}
- if (unlikely(rdes0 & RDES0_OVERFLOW_ERROR))
+
+ if (unlikely(rdes0 & RDES0_OVERFLOW_ERROR)) {
x->rx_gmac_overflow++;
+ ret |= cutoff_err;
+ }
- if (unlikely(rdes0 & RDES0_IPC_CSUM_ERROR))
- pr_err("\tIPC Csum Error/Giant frame\n");
+ if (unlikely(rdes0 & RDES0_GIANT_FRAME_ERROR)) {
+ x->rx_length++;
+ ret |= len_err;
+ }
- if (unlikely(rdes0 & RDES0_COLLISION))
+ if (unlikely(rdes0 & RDES0_COLLISION)) {
x->rx_collision++;
- if (unlikely(rdes0 & RDES0_RECEIVE_WATCHDOG))
+ ret |= proto_err;
+ }
+
+ if (unlikely(rdes0 & RDES0_RECEIVE_WATCHDOG)) {
x->rx_watchdog++;
+ ret |= cutoff_err;
+ }
- if (unlikely(rdes0 & RDES0_MII_ERROR)) /* GMII */
+ if (unlikely(rdes0 & RDES0_MII_ERROR)) {
x->rx_mii++;
+ ret |= proto_err;
+ }
if (unlikely(rdes0 & RDES0_CRC_ERROR)) {
x->rx_crc_errors++;
+ ret |= csum_err;
}
- ret = discard_frame;
}
- /* After a payload csum error, the ES bit is set.
- * It doesn't match with the information reported into the databook.
- * At any rate, we need to understand if the CSUM hw computation is ok
- * and report this info to the upper layers. */
- if (likely(ret == good_frame))
- ret = enh_desc_coe_rdes0(!!(rdes0 & RDES0_IPC_CSUM_ERROR),
- !!(rdes0 & RDES0_FRAME_TYPE),
- !!(rdes0 & ERDES0_RX_MAC_ADDR));
-
if (unlikely(rdes0 & RDES0_DRIBBLING))
x->dribbling_bit++;
- if (unlikely(rdes0 & RDES0_SA_FILTER_FAIL)) {
+ if (unlikely(rdes0 & RDES0_SA_FILTER_FAIL))
x->sa_rx_filter_fail++;
- ret = discard_frame;
- }
- if (unlikely(rdes0 & RDES0_DA_FILTER_FAIL)) {
+
+ if (unlikely(rdes0 & RDES0_DA_FILTER_FAIL))
x->da_rx_filter_fail++;
- ret = discard_frame;
- }
- if (unlikely(rdes0 & RDES0_LENGTH_ERROR)) {
+
+ if (unlikely(rdes0 & RDES0_LENGTH_ERROR))
x->rx_length++;
- ret = discard_frame;
- }
+
#ifdef STMMAC_VLAN_TAG_USED
if (rdes0 & RDES0_VLAN_TAG)
x->rx_vlan++;
@@ -249,20 +229,65 @@ static int enh_desc_get_rx_status(struct stmmac_extra_stats *x,
return ret;
}
+static int enh_desc_get_rx_status_nocoe(struct stmmac_extra_stats *x,
+ struct dma_desc *p)
+{
+ return enh_desc_get_rx_basic_status(le32_to_cpu(p->des0), x);
+}
+
+static int enh_desc_get_rx_status_noext(struct stmmac_extra_stats *x,
+ struct dma_desc *p)
+{
+ unsigned int rdes0 = le32_to_cpu(p->des0);
+ int ret;
+
+ ret = enh_desc_get_rx_basic_status(rdes0, x);
+ if (ret & (dma_own | rx_not_ls))
+ return ret;
+
+ /* Rx COE type 2 has been available since v3.30a. If it's synthesized
+ * into the GMAC the Bits 5, 7, and 0 state reflects the Rx COE
+ * outcome. The bits permutation had been available up to the DW GMAC
+ * v3.50a IP-core release which initially introduced the extended
+ * enhanced descriptors.
+ */
+ return ret | com_desc_rx_coe_rdes0(!!(rdes0 & RDES0_IPC_CSUM_ERROR),
+ !!(rdes0 & RDES0_FRAME_TYPE),
+ !!(rdes0 & RDES0_PAYLOAD_CSUM_ERR));
+}
+
+static int enh_desc_get_rx_status_ext(struct stmmac_extra_stats *x,
+ struct dma_desc *p)
+{
+ unsigned int rdes4 = le32_to_cpu(to_dma_extended_desc(p)->des4);
+ unsigned int rdes0 = le32_to_cpu(p->des0);
+ int ret;
+
+ ret = enh_desc_get_rx_basic_status(rdes0, x);
+ if (ret & (dma_own | rx_not_ls))
+ return ret;
+
+ /* In DW GMAC v3.50a IP-core the Rx COE type 2 status was moved to
+ * the extended part of the enhanced descriptor (RDES4). The RDES0
+ * 5, 7, and 0 bits permutation has been no longer relevant.
+ */
+ if (rdes0 & ERDES0_RX_MAC_ADDR)
+ ret |= enh_desc_rx_ext_status(rdes4, x);
+ else /* No extended part so no CSUM/Timestamp processed */
+ ret |= csum_none;
+
+ return ret;
+}
+
static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
int mode, int end, int bfsize)
{
- int bfsize1;
-
p->des0 |= cpu_to_le32(RDES0_OWN);
- bfsize1 = min(bfsize, BUF_SIZE_8KiB);
- p->des1 |= cpu_to_le32(bfsize1 & ERDES1_BUFFER1_SIZE_MASK);
-
if (mode == STMMAC_CHAIN_MODE)
- ehn_desc_rx_set_on_chain(p);
+ enh_desc_rx_set_on_chain(p, bfsize);
else
- ehn_desc_rx_set_on_ring(p, end, bfsize);
+ enh_desc_rx_set_on_ring(p, end, bfsize);
if (disable_rx_ic)
p->des1 |= cpu_to_le32(ERDES1_DISABLE_IC);
@@ -270,9 +295,9 @@ static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
static void enh_desc_init_tx_desc(struct dma_desc *p, int mode, int end)
{
- p->des0 &= cpu_to_le32(~ETDES0_OWN);
+ memset(p, 0, offsetof(struct dma_desc, des2));
if (mode == STMMAC_CHAIN_MODE)
- enh_desc_end_tx_desc_on_chain(p);
+ enh_desc_end_tx_desc_on_chain(p, 0, false);
else
enh_desc_end_tx_desc_on_ring(p, end);
}
@@ -292,18 +317,66 @@ static void enh_desc_set_rx_owner(struct dma_desc *p, int disable_rx_ic)
p->des0 |= cpu_to_le32(RDES0_OWN);
}
+static unsigned int enh_desc_get_rx_len(int mode)
+{
+ if (mode == STMMAC_CHAIN_MODE)
+ return enh_desc_rx_desc_len_on_chain();
+ else
+ return enh_desc_rx_desc_len_on_ring();
+}
+
+static void enh_desc_release_rx_desc(struct dma_desc *p, int disable_rx_ic,
+ int is_fs, bool rx_own)
+{
+ unsigned int rdes1 = le32_to_cpu(p->des1);
+
+ if (disable_rx_ic)
+ rdes1 |= cpu_to_le32(ERDES1_DISABLE_IC);
+ else
+ rdes1 &= cpu_to_le32(~ERDES1_DISABLE_IC);
+
+ p->des1 = cpu_to_le32(rdes1);
+
+ /* Before releasing the initial descriptor make sure that all
+ * the previous writes are visible to the controller.
+ */
+ if (is_fs && rx_own)
+ dma_wmb();
+
+ if (rx_own)
+ p->des0 = cpu_to_le32(RDES0_OWN);
+}
+
+static void enh_desc_prepare_rx_desc(struct dma_desc *p, int mode,
+ dma_addr_t np, bool hwts_rx, int bfsize)
+{
+ if (mode == STMMAC_CHAIN_MODE)
+ enh_desc_rx_set_buf_on_chain(p, np, hwts_rx);
+ else
+ enh_desc_rx_set_buf_on_ring(p, bfsize);
+}
+
static int enh_desc_get_tx_ls(struct dma_desc *p)
{
return (le32_to_cpu(p->des0) & ETDES0_LAST_SEGMENT) >> 29;
}
-static void enh_desc_release_tx_desc(struct dma_desc *p, int mode)
+static unsigned int enh_desc_get_tx_len(int mode)
+{
+ if (mode == STMMAC_CHAIN_MODE)
+ return enh_desc_tx_desc_len_on_chain();
+ else
+ return enh_desc_tx_desc_len_on_ring();
+}
+
+static void enh_desc_release_tx_desc(struct dma_desc *p, int mode,
+ dma_addr_t np, bool hwts_tx)
{
int ter = (le32_to_cpu(p->des0) & ETDES0_END_RING) >> 21;
memset(p, 0, offsetof(struct dma_desc, des2));
if (mode == STMMAC_CHAIN_MODE)
- enh_desc_end_tx_desc_on_chain(p);
+ enh_desc_end_tx_desc_on_chain(p, np, hwts_tx);
else
enh_desc_end_tx_desc_on_ring(p, ter);
}
@@ -349,19 +422,9 @@ static void enh_desc_set_tx_ic(struct dma_desc *p)
p->des0 |= cpu_to_le32(ETDES0_INTERRUPT);
}
-static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
+static int enh_desc_get_rx_frame_len(struct dma_desc *p)
{
- unsigned int csum = 0;
- /* The type-1 checksum offload engines append the checksum at
- * the end of frame and the two bytes of checksum are added in
- * the length.
- * Adjust for that in the framelen for type-1 checksum offload
- * engines.
- */
- if (rx_coe_type == STMMAC_RX_COE_TYPE1)
- csum = 2;
-
- return FIELD_GET(RDES0_FRAME_LEN_MASK, le32_to_cpu(p->des0)) - csum;
+ return FIELD_GET(RDES0_FRAME_LEN_MASK, le32_to_cpu(p->des0));
}
static void enh_desc_enable_tx_timestamp(struct dma_desc *p)
@@ -442,13 +505,75 @@ static void enh_desc_clear(struct dma_desc *p)
p->des2 = 0;
}
+static void enh_desc_set_vlan(struct dma_desc *p, u32 type)
+{
+ p->des0 |= cpu_to_le32(FIELD_PREP(ETDES0_VLIC_MASK, type));
+}
+
const struct stmmac_desc_ops enh_desc_ops = {
.tx_status = enh_desc_get_tx_status,
- .rx_status = enh_desc_get_rx_status,
+ .rx_status = enh_desc_get_rx_status_nocoe,
+ .get_rx_len = enh_desc_get_rx_len,
+ .get_tx_len = enh_desc_get_tx_len,
+ .init_rx_desc = enh_desc_init_rx_desc,
+ .init_tx_desc = enh_desc_init_tx_desc,
+ .get_tx_owner = enh_desc_get_tx_owner,
+ .release_rx_desc = enh_desc_release_rx_desc,
+ .prepare_rx_desc = enh_desc_prepare_rx_desc,
+ .release_tx_desc = enh_desc_release_tx_desc,
+ .prepare_tx_desc = enh_desc_prepare_tx_desc,
+ .set_tx_ic = enh_desc_set_tx_ic,
+ .get_tx_ls = enh_desc_get_tx_ls,
+ .set_tx_owner = enh_desc_set_tx_owner,
+ .set_rx_owner = enh_desc_set_rx_owner,
+ .get_rx_frame_len = enh_desc_get_rx_frame_len,
+ .enable_tx_timestamp = enh_desc_enable_tx_timestamp,
+ .get_tx_timestamp_status = enh_desc_get_tx_timestamp_status,
+ .get_timestamp = enh_desc_get_timestamp,
+ .get_rx_timestamp_status = enh_desc_get_rx_timestamp_status,
+ .display_ring = enh_desc_display_ring,
+ .set_addr = enh_desc_set_addr,
+ .clear = enh_desc_clear,
+ .set_vlan = enh_desc_set_vlan,
+};
+
+const struct stmmac_desc_ops enh_desc_noext_ops = {
+ .tx_status = enh_desc_get_tx_status,
+ .rx_status = enh_desc_get_rx_status_noext,
+ .get_rx_len = enh_desc_get_rx_len,
+ .get_tx_len = enh_desc_get_tx_len,
+ .init_rx_desc = enh_desc_init_rx_desc,
+ .init_tx_desc = enh_desc_init_tx_desc,
+ .get_tx_owner = enh_desc_get_tx_owner,
+ .release_rx_desc = enh_desc_release_rx_desc,
+ .prepare_rx_desc = enh_desc_prepare_rx_desc,
+ .release_tx_desc = enh_desc_release_tx_desc,
+ .prepare_tx_desc = enh_desc_prepare_tx_desc,
+ .set_tx_ic = enh_desc_set_tx_ic,
+ .get_tx_ls = enh_desc_get_tx_ls,
+ .set_tx_owner = enh_desc_set_tx_owner,
+ .set_rx_owner = enh_desc_set_rx_owner,
+ .get_rx_frame_len = enh_desc_get_rx_frame_len,
+ .enable_tx_timestamp = enh_desc_enable_tx_timestamp,
+ .get_tx_timestamp_status = enh_desc_get_tx_timestamp_status,
+ .get_timestamp = enh_desc_get_timestamp,
+ .get_rx_timestamp_status = enh_desc_get_rx_timestamp_status,
+ .display_ring = enh_desc_display_ring,
+ .set_addr = enh_desc_set_addr,
+ .clear = enh_desc_clear,
+ .set_vlan = enh_desc_set_vlan,
+};
+
+const struct stmmac_desc_ops enh_desc_ext_ops = {
+ .tx_status = enh_desc_get_tx_status,
+ .rx_status = enh_desc_get_rx_status_ext,
+ .get_rx_len = enh_desc_get_rx_len,
.get_tx_len = enh_desc_get_tx_len,
.init_rx_desc = enh_desc_init_rx_desc,
.init_tx_desc = enh_desc_init_tx_desc,
.get_tx_owner = enh_desc_get_tx_owner,
+ .release_rx_desc = enh_desc_release_rx_desc,
+ .prepare_rx_desc = enh_desc_prepare_rx_desc,
.release_tx_desc = enh_desc_release_tx_desc,
.prepare_tx_desc = enh_desc_prepare_tx_desc,
.set_tx_ic = enh_desc_set_tx_ic,
@@ -456,7 +581,6 @@ const struct stmmac_desc_ops enh_desc_ops = {
.set_tx_owner = enh_desc_set_tx_owner,
.set_rx_owner = enh_desc_set_rx_owner,
.get_rx_frame_len = enh_desc_get_rx_frame_len,
- .rx_extended_status = enh_desc_get_ext_status,
.enable_tx_timestamp = enh_desc_enable_tx_timestamp,
.get_tx_timestamp_status = enh_desc_get_tx_timestamp_status,
.get_timestamp = enh_desc_get_timestamp,
@@ -464,4 +588,5 @@ const struct stmmac_desc_ops enh_desc_ops = {
.display_ring = enh_desc_display_ring,
.set_addr = enh_desc_set_addr,
.clear = enh_desc_clear,
+ .set_vlan = enh_desc_set_vlan,
};
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.c b/drivers/net/ethernet/stmicro/stmmac/hwif.c
index 7e69ff4b9a98d..6ed6be48323cb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.c
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.c
@@ -7,6 +7,7 @@
#include "common.h"
#include "stmmac.h"
#include "stmmac_fpe.h"
+#include "stmmac_gpio.h"
#include "stmmac_ptp.h"
#include "stmmac_est.h"
#include "stmmac_vlan.h"
@@ -51,21 +52,6 @@ static void stmmac_get_version(struct stmmac_priv *priv,
ver->dev_id = FIELD_GET(DWMAC_USERVER, version);
}
-static void stmmac_dwmac_mode_quirk(struct stmmac_priv *priv)
-{
- struct mac_device_info *mac = priv->hw;
-
- if (priv->chain_mode) {
- dev_info(priv->device, "Chain mode enabled\n");
- priv->mode = STMMAC_CHAIN_MODE;
- mac->mode = &chain_mode_ops;
- } else {
- dev_info(priv->device, "Ring mode enabled\n");
- priv->mode = STMMAC_RING_MODE;
- mac->mode = &ring_mode_ops;
- }
-}
-
static int stmmac_dwmac1_quirks(struct stmmac_priv *priv)
{
struct mac_device_info *mac = priv->hw;
@@ -74,26 +60,24 @@ static int stmmac_dwmac1_quirks(struct stmmac_priv *priv)
dev_info(priv->device, "Enhanced/Alternate descriptors\n");
/* GMAC older than 3.50 has no extended descriptors */
- if (priv->synopsys_id >= DWMAC_CORE_3_50) {
- dev_info(priv->device, "Enabled extended descriptors\n");
+ if (priv->synopsys_id < DWMAC_CORE_3_50) {
+ dev_warn(priv->device, "Extended descriptors not supported\n");
+ mac->desc = priv->plat->rx_coe ?
+ &enh_desc_noext_ops : &enh_desc_ops;
+ } else if (priv->dma_cap.atime_stamp || priv->plat->rx_coe) {
+ dev_info(priv->device, "Extended descriptors enabled\n");
priv->extend_desc = 1;
+ mac->desc = &enh_desc_ext_ops;
} else {
- dev_warn(priv->device, "Extended descriptors not supported\n");
+ dev_info(priv->device, "Extended descriptors disabled\n");
+ priv->extend_desc = 0;
+ mac->desc = &enh_desc_ops;
}
-
- mac->desc = &enh_desc_ops;
} else {
dev_info(priv->device, "Normal descriptors\n");
- mac->desc = &ndesc_ops;
+ mac->desc = priv->plat->rx_coe ? &ndesc_rxcoe2_ops : &ndesc_ops;
}
- stmmac_dwmac_mode_quirk(priv);
- return 0;
-}
-
-static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
-{
- stmmac_dwmac_mode_quirk(priv);
return 0;
}
@@ -107,10 +91,23 @@ int stmmac_reset(struct stmmac_priv *priv)
{
struct plat_stmmacenet_data *plat = priv->plat;
void __iomem *ioaddr = priv->ioaddr;
+ int ret;
if (plat && plat->fix_soc_reset)
return plat->fix_soc_reset(priv);
+ if (stmmac_gpio_is_available(priv)) {
+ ret = stmmac_core_stop(priv, priv->hw);
+ if (ret)
+ return ret;
+
+ ret = stmmac_core_clean(priv, priv->hw);
+ if (ret)
+ return ret;
+
+ return stmmac_dma_clean(priv, ioaddr);
+ }
+
return stmmac_do_callback(priv, dma, reset, ioaddr);
}
@@ -124,11 +121,11 @@ static const struct stmmac_hwif_entry {
const void *mac;
const void *hwtimestamp;
const void *ptp;
- const void *mode;
const void *tc;
const void *mmc;
const void *est;
const void *vlan;
+ const void *gpio;
int (*setup)(struct stmmac_priv *priv);
int (*quirks)(struct stmmac_priv *priv);
} stmmac_hw[] = {
@@ -145,7 +142,6 @@ static const struct stmmac_hwif_entry {
.mac = &dwmac100_ops,
.hwtimestamp = &dwmac1000_ptp,
.ptp = &dwmac1000_ptp_clock_ops,
- .mode = NULL,
.tc = NULL,
.mmc = &dwmac_mmc_ops,
.setup = dwmac100_setup,
@@ -160,11 +156,12 @@ static const struct stmmac_hwif_entry {
.desc = NULL,
.dma = &dwmac1000_dma_ops,
.mac = &dwmac1000_ops,
+ .vlan = &dwmac1000_vlan_ops,
.hwtimestamp = &dwmac1000_ptp,
.ptp = &dwmac1000_ptp_clock_ops,
- .mode = NULL,
.tc = NULL,
.mmc = &dwmac_mmc_ops,
+ .gpio = &dwmac1000_gpio_ops,
.setup = dwmac1000_setup,
.quirks = stmmac_dwmac1_quirks,
}, {
@@ -178,15 +175,13 @@ static const struct stmmac_hwif_entry {
.desc = &dwmac4_desc_ops,
.dma = &dwmac4_dma_ops,
.mac = &dwmac4_ops,
- .vlan = &dwmac_vlan_ops,
+ .vlan = &dwmac4_vlan_ops,
.hwtimestamp = &stmmac_ptp,
.ptp = &stmmac_ptp_clock_ops,
- .mode = NULL,
.tc = &dwmac4_tc_ops,
.mmc = &dwmac_mmc_ops,
.est = &dwmac510_est_ops,
.setup = dwmac4_setup,
- .quirks = stmmac_dwmac4_quirks,
}, {
.core_type = DWMAC_CORE_GMAC4,
.min_id = DWMAC_CORE_4_00,
@@ -199,10 +194,9 @@ static const struct stmmac_hwif_entry {
.desc = &dwmac4_desc_ops,
.dma = &dwmac4_dma_ops,
.mac = &dwmac410_ops,
- .vlan = &dwmac_vlan_ops,
+ .vlan = &dwmac4_vlan_ops,
.hwtimestamp = &stmmac_ptp,
.ptp = &stmmac_ptp_clock_ops,
- .mode = &dwmac4_ring_mode_ops,
.tc = &dwmac510_tc_ops,
.mmc = &dwmac_mmc_ops,
.est = &dwmac510_est_ops,
@@ -220,10 +214,9 @@ static const struct stmmac_hwif_entry {
.desc = &dwmac4_desc_ops,
.dma = &dwmac410_dma_ops,
.mac = &dwmac410_ops,
- .vlan = &dwmac_vlan_ops,
+ .vlan = &dwmac4_vlan_ops,
.hwtimestamp = &stmmac_ptp,
.ptp = &stmmac_ptp_clock_ops,
- .mode = &dwmac4_ring_mode_ops,
.tc = &dwmac510_tc_ops,
.mmc = &dwmac_mmc_ops,
.est = &dwmac510_est_ops,
@@ -241,10 +234,9 @@ static const struct stmmac_hwif_entry {
.desc = &dwmac4_desc_ops,
.dma = &dwmac410_dma_ops,
.mac = &dwmac510_ops,
- .vlan = &dwmac_vlan_ops,
+ .vlan = &dwmac4_vlan_ops,
.hwtimestamp = &stmmac_ptp,
.ptp = &stmmac_ptp_clock_ops,
- .mode = &dwmac4_ring_mode_ops,
.tc = &dwmac510_tc_ops,
.mmc = &dwmac_mmc_ops,
.est = &dwmac510_est_ops,
@@ -263,10 +255,9 @@ static const struct stmmac_hwif_entry {
.desc = &dwxgmac210_desc_ops,
.dma = &dwxgmac210_dma_ops,
.mac = &dwxgmac210_ops,
- .vlan = &dwxgmac210_vlan_ops,
+ .vlan = &dwmac4_vlan_ops,
.hwtimestamp = &stmmac_ptp,
.ptp = &stmmac_ptp_clock_ops,
- .mode = NULL,
.tc = &dwmac510_tc_ops,
.mmc = &dwxgmac_mmc_ops,
.est = &dwmac510_est_ops,
@@ -288,7 +279,6 @@ static const struct stmmac_hwif_entry {
.vlan = &dwxlgmac2_vlan_ops,
.hwtimestamp = &stmmac_ptp,
.ptp = &stmmac_ptp_clock_ops,
- .mode = NULL,
.tc = &dwmac510_tc_ops,
.mmc = &dwxgmac_mmc_ops,
.est = &dwmac510_est_ops,
@@ -380,11 +370,11 @@ int stmmac_hwif_init(struct stmmac_priv *priv)
mac->dma = mac->dma ? : entry->dma;
mac->mac = mac->mac ? : entry->mac;
mac->ptp = mac->ptp ? : entry->hwtimestamp;
- mac->mode = mac->mode ? : entry->mode;
mac->tc = mac->tc ? : entry->tc;
mac->mmc = mac->mmc ? : entry->mmc;
mac->est = mac->est ? : entry->est;
mac->vlan = mac->vlan ? : entry->vlan;
+ mac->gpio = mac->gpio ? : entry->gpio;
priv->hw = mac;
priv->fpe_cfg.reg = entry->regs.fpe_reg;
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 0db96a3872592..c19782bf5e20c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -42,6 +42,9 @@ struct stmmac_desc_ops {
int end, int bfsize);
/* DMA TX descriptor ring initialization */
void (*init_tx_desc)(struct dma_desc *p, int mode, int end);
+ /* Invoked by the refill function to prepare the rx descriptor */
+ void (*prepare_rx_desc)(struct dma_desc *p, int mode,
+ dma_addr_t np, bool hwts_rx, int bfsize);
/* Invoked by the xmit function to prepare the tx descriptor */
void (*prepare_tx_desc)(struct dma_desc *p, int is_fs, int len,
bool csum_flag, int mode, bool tx_own, bool ls,
@@ -52,31 +55,35 @@ struct stmmac_desc_ops {
/* Set/get the owner of the descriptor */
void (*set_tx_owner)(struct dma_desc *p);
int (*get_tx_owner)(struct dma_desc *p);
+ /* Clean the rx descriptor as soon as the rx flow is handled */
+ void (*release_rx_desc)(struct dma_desc *p, int disable_rx_ic,
+ int is_fs, bool rx_own);
/* Clean the tx descriptor as soon as the tx irq is received */
- void (*release_tx_desc)(struct dma_desc *p, int mode);
+ void (*release_tx_desc)(struct dma_desc *p, int mode,
+ dma_addr_t np, bool hwts_tx);
/* Clear interrupt on tx frame completion. When this bit is
* set an interrupt happens as soon as the frame is transmitted */
void (*set_tx_ic)(struct dma_desc *p);
/* Last tx segment reports the transmit status */
int (*get_tx_ls)(struct dma_desc *p);
+ /* Get the protocol of the descriptor */
+ u16 (*get_rx_vlan_tpid)(struct dma_desc *p);
/* Get the tag of the descriptor */
u16 (*get_rx_vlan_tci)(struct dma_desc *p);
/* Get the valid status of descriptor */
bool (*get_rx_vlan_valid)(struct dma_desc *p);
/* Return the transmit status looking at the TDES1 */
- int (*tx_status)(struct stmmac_extra_stats *x,
- struct dma_desc *p, void __iomem *ioaddr);
- /* Get the buffer size from the descriptor */
- int (*get_tx_len)(struct dma_desc *p);
+ int (*tx_status)(struct stmmac_extra_stats *x, struct dma_desc *p);
+ /* Get the buffer(s) size for a single descriptor */
+ unsigned int (*get_rx_len)(int mode);
+ unsigned int (*get_tx_len)(int mode);
/* Handle extra events on specific interrupts hw dependent */
void (*set_rx_owner)(struct dma_desc *p, int disable_rx_ic);
/* Get the receive frame size */
- int (*get_rx_frame_len)(struct dma_desc *p, int rx_coe_type);
+ int (*get_rx_frame_len)(struct dma_desc *p);
/* Return the reception status looking at the RDES1 */
int (*rx_status)(struct stmmac_extra_stats *x,
struct dma_desc *p);
- void (*rx_extended_status)(struct stmmac_extra_stats *x,
- struct dma_extended_desc *p);
/* Set tx timestamp enable bit */
void (*enable_tx_timestamp) (struct dma_desc *p);
/* get tx timestamp status */
@@ -110,6 +117,8 @@ struct stmmac_desc_ops {
stmmac_do_void_callback(__priv, desc, init_rx_desc, __args)
#define stmmac_init_tx_desc(__priv, __args...) \
stmmac_do_void_callback(__priv, desc, init_tx_desc, __args)
+#define stmmac_prepare_rx_desc(__priv, __args...) \
+ stmmac_do_void_callback(__priv, desc, prepare_rx_desc, __args)
#define stmmac_prepare_tx_desc(__priv, __args...) \
stmmac_do_void_callback(__priv, desc, prepare_tx_desc, __args)
#define stmmac_prepare_tso_tx_desc(__priv, __args...) \
@@ -118,6 +127,8 @@ struct stmmac_desc_ops {
stmmac_do_void_callback(__priv, desc, set_tx_owner, __args)
#define stmmac_get_tx_owner(__priv, __args...) \
stmmac_do_callback(__priv, desc, get_tx_owner, __args)
+#define stmmac_release_rx_desc(__priv, __args...) \
+ stmmac_do_void_callback(__priv, desc, release_rx_desc, __args)
#define stmmac_release_tx_desc(__priv, __args...) \
stmmac_do_void_callback(__priv, desc, release_tx_desc, __args)
#define stmmac_set_tx_ic(__priv, __args...) \
@@ -130,6 +141,8 @@ struct stmmac_desc_ops {
stmmac_do_callback(__priv, desc, get_rx_vlan_valid, __args)
#define stmmac_tx_status(__priv, __args...) \
stmmac_do_callback(__priv, desc, tx_status, __args)
+#define stmmac_get_rx_len(__priv, __args...) \
+ stmmac_do_callback(__priv, desc, get_rx_len, __args)
#define stmmac_get_tx_len(__priv, __args...) \
stmmac_do_callback(__priv, desc, get_tx_len, __args)
#define stmmac_set_rx_owner(__priv, __args...) \
@@ -138,8 +151,6 @@ struct stmmac_desc_ops {
stmmac_do_callback(__priv, desc, get_rx_frame_len, __args)
#define stmmac_rx_status(__priv, __args...) \
stmmac_do_callback(__priv, desc, rx_status, __args)
-#define stmmac_rx_extended_status(__priv, __args...) \
- stmmac_do_void_callback(__priv, desc, rx_extended_status, __args)
#define stmmac_enable_tx_timestamp(__priv, __args...) \
stmmac_do_void_callback(__priv, desc, enable_tx_timestamp, __args)
#define stmmac_get_tx_timestamp_status(__priv, __args...) \
@@ -187,19 +198,22 @@ struct stmmac_dma_ops {
void (*init_tx_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
struct stmmac_dma_cfg *dma_cfg,
dma_addr_t phy, u32 chan);
+ /* DMA core cleanup */
+ void (*clean)(struct stmmac_priv *priv, void __iomem *ioaddr);
/* Configure the AXI Bus Mode Register */
void (*axi)(void __iomem *ioaddr, struct stmmac_axi *axi);
/* Dump DMA registers */
void (*dump_regs)(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 *reg_space);
+ /* Setup DMA Rx/Tx channels */
void (*dma_rx_mode)(struct stmmac_priv *priv, void __iomem *ioaddr,
- int mode, u32 channel,
- int fifosz, u8 qmode);
+ int mode, u32 channel, int fifosz, u8 qmode,
+ bool rxall);
void (*dma_tx_mode)(struct stmmac_priv *priv, void __iomem *ioaddr,
int mode, u32 channel, int fifosz, u8 qmode);
/* To track extra statistic (if supported) */
- void (*dma_diagnostic_fr)(struct stmmac_extra_stats *x,
- void __iomem *ioaddr);
+ void (*dma_diagnostic_fr)(struct stmmac_priv *priv, void __iomem *ioaddr,
+ struct stmmac_extra_stats *x, u32 chan);
void (*enable_dma_transmission)(void __iomem *ioaddr, u32 chan);
void (*enable_dma_reception)(void __iomem *ioaddr, u32 chan);
void (*enable_dma_irq)(struct stmmac_priv *priv, void __iomem *ioaddr,
@@ -250,6 +264,8 @@ struct stmmac_dma_ops {
stmmac_do_void_callback(__priv, dma, init_rx_chan, __priv, __args)
#define stmmac_init_tx_chan(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, init_tx_chan, __priv, __args)
+#define stmmac_dma_clean(__priv, __args...) \
+ stmmac_do_void_callback(__priv, dma, clean, __priv, __args)
#define stmmac_axi(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, axi, __args)
#define stmmac_dump_dma_regs(__priv, __args...) \
@@ -259,7 +275,7 @@ struct stmmac_dma_ops {
#define stmmac_dma_tx_mode(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, dma_tx_mode, __priv, __args)
#define stmmac_dma_diagnostic_fr(__priv, __args...) \
- stmmac_do_void_callback(__priv, dma, dma_diagnostic_fr, __args)
+ stmmac_do_void_callback(__priv, dma, dma_diagnostic_fr, __priv, __args)
#define stmmac_enable_dma_transmission(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, enable_dma_transmission, __args)
#define stmmac_enable_dma_reception(__priv, __args...) \
@@ -320,14 +336,21 @@ struct stmmac_ops {
int (*pcs_init)(struct stmmac_priv *priv);
/* MAC core initialization */
void (*core_init)(struct mac_device_info *hw, struct net_device *dev);
+ /* MAC core stop */
+ int (*core_stop)(struct mac_device_info *hw);
+ /* MAC core cleanup */
+ void (*core_clean)(struct mac_device_info *hw);
/* Update MAC capabilities */
void (*update_caps)(struct stmmac_priv *priv);
/* Change the interrupt enable setting. Enable takes precedence. */
void (*irq_modify)(struct mac_device_info *hw, u32 disable, u32 enable);
/* Enable the MAC RX/TX */
void (*set_mac)(void __iomem *ioaddr, bool enable);
+ /* Enable and verify the Auto Pad/FCS stripping */
+ void (*rx_fcs)(struct mac_device_info *hw, bool enable);
+ int (*rx_fcs_status)(struct mac_device_info *hw, int snps_id, int status);
/* Enable and verify that the IPC module is supported */
- int (*rx_ipc)(struct mac_device_info *hw);
+ int (*rx_ipc)(struct mac_device_info *hw, bool enable);
/* Enable RX Queues */
void (*rx_queue_enable)(struct mac_device_info *hw, u8 mode, u32 queue);
/* RX Queues Priority */
@@ -402,7 +425,7 @@ struct stmmac_ops {
void (*set_mac_loopback)(void __iomem *ioaddr, bool enable);
/* RSS */
int (*rss_configure)(struct mac_device_info *hw,
- struct stmmac_rss *cfg, u32 num_rxq);
+ struct stmmac_rss *cfg, bool enable, u32 num_rxq);
/* TX Timestamp */
int (*get_mac_tx_timestamp)(struct mac_device_info *hw, u64 *ts);
/* Source Address Insertion / Replacement */
@@ -424,12 +447,20 @@ struct stmmac_ops {
stmmac_do_callback(__priv, mac, pcs_init, __priv)
#define stmmac_core_init(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, core_init, __args)
+#define stmmac_core_stop(__priv, __args...) \
+ stmmac_do_callback(__priv, mac, core_stop, __args)
+#define stmmac_core_clean(__priv, __args...) \
+ stmmac_do_void_callback(__priv, mac, core_clean, __args)
#define stmmac_mac_update_caps(__priv) \
stmmac_do_void_callback(__priv, mac, update_caps, __priv)
#define stmmac_mac_irq_modify(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, irq_modify, (__priv)->hw, __args)
#define stmmac_mac_set(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, set_mac, __args)
+#define stmmac_rx_fcs(__priv, __args...) \
+ stmmac_do_void_callback(__priv, mac, rx_fcs, __args)
+#define stmmac_rx_fcs_status(__priv, __args...) \
+ stmmac_do_callback(__priv, mac, rx_fcs_status, __args)
#define stmmac_rx_ipc(__priv, __args...) \
stmmac_do_callback(__priv, mac, rx_ipc, __args)
#define stmmac_rx_queue_enable(__priv, __args...) \
@@ -540,34 +571,6 @@ struct stmmac_hwtimestamp {
struct stmmac_tx_queue;
struct stmmac_rx_queue;
-/* Helpers to manage the descriptors for chain and ring modes */
-struct stmmac_mode_ops {
- void (*init) (void *des, dma_addr_t phy_addr, unsigned int size,
- unsigned int extend_desc);
- bool (*is_jumbo_frm)(unsigned int len, bool enh_desc);
- int (*jumbo_frm)(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
- int csum);
- int (*set_16kib_bfsize)(int mtu);
- void (*init_desc3)(struct dma_desc *p);
- void (*refill_desc3)(struct stmmac_rx_queue *rx_q, struct dma_desc *p);
- void (*clean_desc3)(struct stmmac_tx_queue *tx_q, struct dma_desc *p);
-};
-
-#define stmmac_mode_init(__priv, __args...) \
- stmmac_do_void_callback(__priv, mode, init, __args)
-#define stmmac_is_jumbo_frm(__priv, __args...) \
- stmmac_do_callback(__priv, mode, is_jumbo_frm, __args)
-#define stmmac_jumbo_frm(__priv, __args...) \
- stmmac_do_callback(__priv, mode, jumbo_frm, __args)
-#define stmmac_set_16kib_bfsize(__priv, __args...) \
- stmmac_do_callback(__priv, mode, set_16kib_bfsize, __args)
-#define stmmac_init_desc3(__priv, __args...) \
- stmmac_do_void_callback(__priv, mode, init_desc3, __args)
-#define stmmac_refill_desc3(__priv, __args...) \
- stmmac_do_void_callback(__priv, mode, refill_desc3, __args)
-#define stmmac_clean_desc3(__priv, __args...) \
- stmmac_do_void_callback(__priv, mode, clean_desc3, __args)
-
struct tc_cls_u32_offload;
struct tc_cbs_qopt_offload;
struct flow_cls_offload;
@@ -639,12 +642,13 @@ struct stmmac_est_ops {
struct stmmac_vlan_ops {
/* VLAN */
+ int (*set_vlan_tag)(struct mac_device_info *hw, u32 queue, u16 vid);
void (*update_vlan_hash)(struct mac_device_info *hw, u32 hash,
- u16 perfect_match, bool is_double);
- void (*enable_vlan)(struct mac_device_info *hw, u32 type);
+ int add_ctags, int add_stags);
void (*rx_hw_vlan)(struct mac_device_info *hw, struct dma_desc *rx_desc,
struct sk_buff *skb);
- void (*set_hw_vlan_mode)(struct mac_device_info *hw);
+ void (*set_hw_vlan_mode)(struct mac_device_info *hw, bool rx_strip,
+ bool rx_ctag, bool rx_stag, bool tx_stag);
int (*add_hw_vlan_rx_fltr)(struct net_device *dev,
struct mac_device_info *hw,
__be16 proto, u16 vid);
@@ -655,10 +659,10 @@ struct stmmac_vlan_ops {
struct mac_device_info *hw);
};
+#define stmmac_set_core_vlan_tag(__priv, __args...) \
+ stmmac_do_callback(__priv, vlan, set_vlan_tag, __args)
#define stmmac_update_vlan_hash(__priv, __args...) \
stmmac_do_void_callback(__priv, vlan, update_vlan_hash, __args)
-#define stmmac_enable_vlan(__priv, __args...) \
- stmmac_do_void_callback(__priv, vlan, enable_vlan, __args)
#define stmmac_rx_hw_vlan(__priv, __args...) \
stmmac_do_void_callback(__priv, vlan, rx_hw_vlan, __args)
#define stmmac_set_hw_vlan_mode(__priv, __args...) \
@@ -670,6 +674,19 @@ struct stmmac_vlan_ops {
#define stmmac_restore_hw_vlan_rx_fltr(__priv, __args...) \
stmmac_do_void_callback(__priv, vlan, restore_hw_vlan_rx_fltr, __args)
+struct stmmac_gpio_ops {
+ void (*set_ctrl)(struct stmmac_priv *priv, u32 gpie, u32 gpit, u32 gpo);
+ void (*get_ctrl)(struct stmmac_priv *priv, u32 *gpie, u32 *gpit, u32 *gpo);
+ int (*get_gpis)(struct stmmac_priv *priv);
+};
+
+#define stmmac_gpio_set_ctrl(__priv, __args...) \
+ stmmac_do_void_callback(__priv, gpio, set_ctrl, __priv, __args)
+#define stmmac_gpio_get_ctrl(__priv, __args...) \
+ stmmac_do_void_callback(__priv, gpio, get_ctrl, __priv, __args)
+#define stmmac_gpio_get_gpis(__priv) \
+ stmmac_do_callback(__priv, gpio, get_gpis, __priv)
+
struct stmmac_regs_off {
const struct stmmac_fpe_reg *fpe_reg;
u32 ptp_off;
@@ -678,18 +695,20 @@ struct stmmac_regs_off {
};
extern const struct stmmac_desc_ops enh_desc_ops;
+extern const struct stmmac_desc_ops enh_desc_noext_ops;
+extern const struct stmmac_desc_ops enh_desc_ext_ops;
extern const struct stmmac_desc_ops ndesc_ops;
+extern const struct stmmac_desc_ops ndesc_rxcoe2_ops;
extern const struct stmmac_hwtimestamp stmmac_ptp;
extern const struct stmmac_hwtimestamp dwmac1000_ptp;
-extern const struct stmmac_mode_ops ring_mode_ops;
-extern const struct stmmac_mode_ops chain_mode_ops;
-
extern const struct stmmac_ops dwmac100_ops;
extern const struct stmmac_dma_ops dwmac100_dma_ops;
extern const struct stmmac_ops dwmac1000_ops;
extern const struct stmmac_dma_ops dwmac1000_dma_ops;
+extern const struct stmmac_vlan_ops dwmac1000_vlan_ops;
+extern const struct stmmac_gpio_ops dwmac1000_gpio_ops;
extern const struct stmmac_ops dwmac4_ops;
extern const struct stmmac_dma_ops dwmac4_dma_ops;
extern const struct stmmac_ops dwmac410_ops;
diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
index 859cb9242a523..389e07f35c6af 100644
--- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
@@ -13,7 +13,7 @@
#include "descs_com.h"
static int ndesc_get_tx_status(struct stmmac_extra_stats *x,
- struct dma_desc *p, void __iomem *ioaddr)
+ struct dma_desc *p)
{
u32 tdes0 = le32_to_cpu(p->des0);
u32 tdes1 = le32_to_cpu(p->des1);
@@ -55,57 +55,72 @@ static int ndesc_get_tx_status(struct stmmac_extra_stats *x,
return ret;
}
-static int ndesc_get_tx_len(struct dma_desc *p)
-{
- return (le32_to_cpu(p->des1) & RDES1_BUFFER1_SIZE_MASK);
-}
-
/* This function verifies if each incoming frame has some errors
* and, if required, updates the multicast statistics.
* In case of success, it returns good_frame because the GMAC device
* is supposed to be able to compute the csum in HW. */
-static int ndesc_get_rx_status(struct stmmac_extra_stats *x,
- struct dma_desc *p)
+static int ndesc_get_rx_basic_status(unsigned int rdes0,
+ struct stmmac_extra_stats *x)
{
- u32 rdes0 = le32_to_cpu(p->des0);
int ret = good_frame;
if (unlikely(rdes0 & RDES0_OWN))
return dma_own;
- if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) {
- x->rx_length++;
- return discard_frame;
- }
+ if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR)))
+ return rx_not_ls;
+
+ if (unlikely(!(rdes0 & RDES0_FRAME_TYPE)))
+ ret = llc_snap;
if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) {
- if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR))
+ if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) {
x->rx_desc++;
- if (unlikely(rdes0 & RDES0_SA_FILTER_FAIL))
- x->sa_filter_fail++;
- if (unlikely(rdes0 & RDES0_OVERFLOW_ERROR))
+ ret |= cutoff_err;
+ }
+
+ if (unlikely(rdes0 & RDES0_OVERFLOW_ERROR)) {
x->overflow_error++;
- if (unlikely(rdes0 & RDES0_IPC_CSUM_ERROR))
- x->ipc_csum_error++;
+ ret |= cutoff_err;
+ }
+
+ if (unlikely(rdes0 & RDES0_GIANT_FRAME_ERROR)) {
+ x->rx_length++;
+ ret |= len_err;
+ }
+
if (unlikely(rdes0 & RDES0_COLLISION)) {
x->rx_collision++;
+ ret |= proto_err;
}
+
+ if (unlikely(rdes0 & RDES0_RECEIVE_WATCHDOG)) {
+ x->rx_watchdog++;
+ ret |= cutoff_err;
+ }
+
+ if (unlikely(rdes0 & RDES0_MII_ERROR)) {
+ x->rx_mii++;
+ ret |= proto_err;
+ }
+
if (unlikely(rdes0 & RDES0_CRC_ERROR)) {
x->rx_crc_errors++;
+ ret |= csum_err;
}
- ret = discard_frame;
}
if (unlikely(rdes0 & RDES0_DRIBBLING))
x->dribbling_bit++;
- if (unlikely(rdes0 & RDES0_LENGTH_ERROR)) {
+ if (unlikely(rdes0 & RDES0_SA_FILTER_FAIL))
+ x->sa_filter_fail++;
+
+ if (unlikely(rdes0 & RDES0_DA_FILTER_FAIL))
+ x->da_rx_filter_fail++;
+
+ if (unlikely(rdes0 & RDES0_LENGTH_ERROR))
x->rx_length++;
- ret = discard_frame;
- }
- if (unlikely(rdes0 & RDES0_MII_ERROR)) {
- x->rx_mii++;
- ret = discard_frame;
- }
+
#ifdef STMMAC_VLAN_TAG_USED
if (rdes0 & RDES0_VLAN_TAG)
x->vlan_tag++;
@@ -113,18 +128,38 @@ static int ndesc_get_rx_status(struct stmmac_extra_stats *x,
return ret;
}
+static int ndesc_get_rx_status_nocoe(struct stmmac_extra_stats *x,
+ struct dma_desc *p)
+{
+ return ndesc_get_rx_basic_status(le32_to_cpu(p->des0), x);
+}
+
+static int ndesc_get_rx_status_coe2(struct stmmac_extra_stats *x,
+ struct dma_desc *p)
+{
+ unsigned int rdes0 = le32_to_cpu(p->des0);
+ int ret;
+
+ ret = ndesc_get_rx_basic_status(rdes0, x);
+ if (ret & (dma_own | rx_not_ls))
+ return ret;
+
+ /* Rx COE type 2 has been available since v3.30a. If it's synthesized
+ * into the GMAC the Bits 5, 7, and 0 state reflects the Rx COE
+ * outcome.
+ */
+ return ret | com_desc_rx_coe_rdes0(!!(rdes0 & RDES0_IPC_CSUM_ERROR),
+ !!(rdes0 & RDES0_FRAME_TYPE),
+ !!(rdes0 & RDES0_PAYLOAD_CSUM_ERR));
+}
+
static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode,
int end, int bfsize)
{
- int bfsize1;
-
p->des0 |= cpu_to_le32(RDES0_OWN);
- bfsize1 = min(bfsize, BUF_SIZE_2KiB - 1);
- p->des1 |= cpu_to_le32(bfsize1 & RDES1_BUFFER1_SIZE_MASK);
-
if (mode == STMMAC_CHAIN_MODE)
- ndesc_rx_set_on_chain(p, end);
+ ndesc_rx_set_on_chain(p, bfsize);
else
ndesc_rx_set_on_ring(p, end, bfsize);
@@ -134,9 +169,9 @@ static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode,
static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end)
{
- p->des0 &= cpu_to_le32(~TDES0_OWN);
+ memset(p, 0, offsetof(struct dma_desc, des2));
if (mode == STMMAC_CHAIN_MODE)
- ndesc_tx_set_on_chain(p);
+ ndesc_end_tx_desc_on_chain(p, 0, false);
else
ndesc_end_tx_desc_on_ring(p, end);
}
@@ -156,18 +191,66 @@ static void ndesc_set_rx_owner(struct dma_desc *p, int disable_rx_ic)
p->des0 |= cpu_to_le32(RDES0_OWN);
}
+static unsigned int ndesc_get_rx_len(int mode)
+{
+ if (mode == STMMAC_CHAIN_MODE)
+ return ndesc_rx_desc_len_on_chain();
+ else
+ return ndesc_rx_desc_len_on_ring();
+}
+
+static void ndesc_release_rx_desc(struct dma_desc *p, int disable_rx_ic,
+ int is_fs, bool rx_own)
+{
+ unsigned int rdes1 = le32_to_cpu(p->des1);
+
+ if (disable_rx_ic)
+ rdes1 |= cpu_to_le32(RDES1_DISABLE_IC);
+ else
+ rdes1 &= cpu_to_le32(~RDES1_DISABLE_IC);
+
+ p->des1 = cpu_to_le32(rdes1);
+
+ /* Before releasing the initial descriptor make sure that all
+ * the previous writes are visible to the controller.
+ */
+ if (is_fs && rx_own)
+ dma_wmb();
+
+ if (rx_own)
+ p->des0 = cpu_to_le32(RDES0_OWN);
+}
+
+static void ndesc_prepare_rx_desc(struct dma_desc *p, int mode,
+ dma_addr_t np, bool hwts_rx, int bfsize)
+{
+ if (mode == STMMAC_CHAIN_MODE)
+ ndesc_rx_set_buf_on_chain(p, np, hwts_rx);
+ else
+ ndesc_rx_set_buf_on_ring(p, bfsize);
+}
+
static int ndesc_get_tx_ls(struct dma_desc *p)
{
return (le32_to_cpu(p->des1) & TDES1_LAST_SEGMENT) >> 30;
}
-static void ndesc_release_tx_desc(struct dma_desc *p, int mode)
+static unsigned int ndesc_get_tx_len(int mode)
+{
+ if (mode == STMMAC_CHAIN_MODE)
+ return ndesc_tx_desc_len_on_chain();
+ else
+ return ndesc_tx_desc_len_on_ring();
+}
+
+static void ndesc_release_tx_desc(struct dma_desc *p, int mode,
+ dma_addr_t np, bool hwts_tx)
{
int ter = (le32_to_cpu(p->des1) & TDES1_END_RING) >> 25;
memset(p, 0, offsetof(struct dma_desc, des2));
if (mode == STMMAC_CHAIN_MODE)
- ndesc_tx_set_on_chain(p);
+ ndesc_end_tx_desc_on_chain(p, np, hwts_tx);
else
ndesc_end_tx_desc_on_ring(p, ter);
}
@@ -205,20 +288,9 @@ static void ndesc_set_tx_ic(struct dma_desc *p)
p->des1 |= cpu_to_le32(TDES1_INTERRUPT);
}
-static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
+static int ndesc_get_rx_frame_len(struct dma_desc *p)
{
- unsigned int csum = 0;
-
- /* The type-1 checksum offload engines append the checksum at
- * the end of frame and the two bytes of checksum are added in
- * the length.
- * Adjust for that in the framelen for type-1 checksum offload
- * engines
- */
- if (rx_coe_type == STMMAC_RX_COE_TYPE1)
- csum = 2;
-
- return FIELD_GET(RDES0_FRAME_LEN_MASK, le32_to_cpu(p->des0)) - csum;
+ return FIELD_GET(RDES0_FRAME_LEN_MASK, le32_to_cpu(p->des0));
}
static void ndesc_enable_tx_timestamp(struct dma_desc *p)
@@ -290,11 +362,40 @@ static void ndesc_clear(struct dma_desc *p)
const struct stmmac_desc_ops ndesc_ops = {
.tx_status = ndesc_get_tx_status,
- .rx_status = ndesc_get_rx_status,
+ .rx_status = ndesc_get_rx_status_nocoe,
+ .get_tx_len = ndesc_get_tx_len,
+ .get_rx_len = ndesc_get_rx_len,
+ .init_rx_desc = ndesc_init_rx_desc,
+ .init_tx_desc = ndesc_init_tx_desc,
+ .get_tx_owner = ndesc_get_tx_owner,
+ .release_rx_desc = ndesc_release_rx_desc,
+ .prepare_rx_desc = ndesc_prepare_rx_desc,
+ .release_tx_desc = ndesc_release_tx_desc,
+ .prepare_tx_desc = ndesc_prepare_tx_desc,
+ .set_tx_ic = ndesc_set_tx_ic,
+ .get_tx_ls = ndesc_get_tx_ls,
+ .set_tx_owner = ndesc_set_tx_owner,
+ .set_rx_owner = ndesc_set_rx_owner,
+ .get_rx_frame_len = ndesc_get_rx_frame_len,
+ .enable_tx_timestamp = ndesc_enable_tx_timestamp,
+ .get_tx_timestamp_status = ndesc_get_tx_timestamp_status,
+ .get_timestamp = ndesc_get_timestamp,
+ .get_rx_timestamp_status = ndesc_get_rx_timestamp_status,
+ .display_ring = ndesc_display_ring,
+ .set_addr = ndesc_set_addr,
+ .clear = ndesc_clear,
+};
+
+const struct stmmac_desc_ops ndesc_rxcoe2_ops = {
+ .tx_status = ndesc_get_tx_status,
+ .rx_status = ndesc_get_rx_status_coe2,
+ .get_rx_len = ndesc_get_rx_len,
.get_tx_len = ndesc_get_tx_len,
.init_rx_desc = ndesc_init_rx_desc,
.init_tx_desc = ndesc_init_tx_desc,
.get_tx_owner = ndesc_get_tx_owner,
+ .release_rx_desc = ndesc_release_rx_desc,
+ .prepare_rx_desc = ndesc_prepare_rx_desc,
.release_tx_desc = ndesc_release_tx_desc,
.prepare_tx_desc = ndesc_prepare_tx_desc,
.set_tx_ic = ndesc_set_tx_ic,
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
deleted file mode 100644
index 382d94a3b9720..0000000000000
--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
+++ /dev/null
@@ -1,141 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*******************************************************************************
- Specialised functions for managing Ring mode
-
- Copyright(C) 2011 STMicroelectronics Ltd
-
- It defines all the functions used to handle the normal/enhanced
- descriptors in case of the DMA is configured to work in chained or
- in ring mode.
-
-
- Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
-*******************************************************************************/
-
-#include "stmmac.h"
-
-static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
- int csum)
-{
- unsigned int nopaged_len = skb_headlen(skb);
- struct stmmac_priv *priv = tx_q->priv_data;
- unsigned int entry = tx_q->cur_tx;
- unsigned int bmax, len, des2;
- struct dma_desc *desc;
-
- if (priv->extend_desc)
- desc = (struct dma_desc *)(tx_q->dma_etx + entry);
- else
- desc = tx_q->dma_tx + entry;
-
- if (priv->plat->enh_desc)
- bmax = BUF_SIZE_8KiB;
- else
- bmax = BUF_SIZE_2KiB;
-
- len = nopaged_len - bmax;
-
- if (nopaged_len > BUF_SIZE_8KiB) {
-
- des2 = dma_map_single(priv->device, skb->data, bmax,
- DMA_TO_DEVICE);
- desc->des2 = cpu_to_le32(des2);
- if (dma_mapping_error(priv->device, des2))
- return -1;
-
- tx_q->tx_skbuff_dma[entry].buf = des2;
- tx_q->tx_skbuff_dma[entry].len = bmax;
- tx_q->tx_skbuff_dma[entry].is_jumbo = true;
-
- desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
- stmmac_prepare_tx_desc(priv, desc, 1, bmax, csum,
- STMMAC_RING_MODE, 0, false, skb->len);
- tx_q->tx_skbuff[entry] = NULL;
- entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
-
- if (priv->extend_desc)
- desc = (struct dma_desc *)(tx_q->dma_etx + entry);
- else
- desc = tx_q->dma_tx + entry;
-
- des2 = dma_map_single(priv->device, skb->data + bmax, len,
- DMA_TO_DEVICE);
- desc->des2 = cpu_to_le32(des2);
- if (dma_mapping_error(priv->device, des2))
- return -1;
- tx_q->tx_skbuff_dma[entry].buf = des2;
- tx_q->tx_skbuff_dma[entry].len = len;
- tx_q->tx_skbuff_dma[entry].is_jumbo = true;
-
- desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
- stmmac_prepare_tx_desc(priv, desc, 0, len, csum,
- STMMAC_RING_MODE, 1, !skb_is_nonlinear(skb),
- skb->len);
- } else {
- des2 = dma_map_single(priv->device, skb->data,
- nopaged_len, DMA_TO_DEVICE);
- desc->des2 = cpu_to_le32(des2);
- if (dma_mapping_error(priv->device, des2))
- return -1;
- tx_q->tx_skbuff_dma[entry].buf = des2;
- tx_q->tx_skbuff_dma[entry].len = nopaged_len;
- tx_q->tx_skbuff_dma[entry].is_jumbo = true;
- desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
- stmmac_prepare_tx_desc(priv, desc, 1, nopaged_len, csum,
- STMMAC_RING_MODE, 0, !skb_is_nonlinear(skb),
- skb->len);
- }
-
- tx_q->cur_tx = entry;
-
- return entry;
-}
-
-static bool is_jumbo_frm(unsigned int len, bool enh_desc)
-{
- return len >= BUF_SIZE_4KiB;
-}
-
-static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p)
-{
- struct stmmac_priv *priv = rx_q->priv_data;
-
- /* Fill DES3 in case of RING mode */
- if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB)
- p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB);
-}
-
-/* In ring mode we need to fill the desc3 because it is used as buffer */
-static void init_desc3(struct dma_desc *p)
-{
- p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB);
-}
-
-static void clean_desc3(struct stmmac_tx_queue *tx_q, struct dma_desc *p)
-{
- struct stmmac_priv *priv = tx_q->priv_data;
- unsigned int entry = tx_q->dirty_tx;
-
- /* des3 is only used for jumbo frames tx or time stamping */
- if (unlikely(tx_q->tx_skbuff_dma[entry].is_jumbo ||
- (tx_q->tx_skbuff_dma[entry].last_segment &&
- !priv->extend_desc && priv->hwts_tx_en)))
- p->des3 = 0;
-}
-
-static int set_16kib_bfsize(int mtu)
-{
- int ret = 0;
- if (unlikely(mtu > BUF_SIZE_8KiB))
- ret = BUF_SIZE_16KiB;
- return ret;
-}
-
-const struct stmmac_mode_ops ring_mode_ops = {
- .is_jumbo_frm = is_jumbo_frm,
- .jumbo_frm = jumbo_frm,
- .refill_desc3 = refill_desc3,
- .init_desc3 = init_desc3,
- .clean_desc3 = clean_desc3,
- .set_16kib_bfsize = set_16kib_bfsize,
-};
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 33667a26708c8..d1aabf3321691 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -11,6 +11,7 @@
#define STMMAC_RESOURCE_NAME "stmmaceth"
+#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/hrtimer.h>
#include <linux/if_vlan.h>
@@ -21,10 +22,13 @@
#include <linux/ptp_clock_kernel.h>
#include <linux/net_tstamp.h>
#include <linux/reset.h>
+#include <linux/workqueue.h>
#include <net/page_pool/types.h>
#include <net/xdp.h>
#include <uapi/linux/bpf.h>
+#include "stmmac_gpio.h"
+
struct stmmac_pcs;
struct stmmac_resources {
@@ -39,6 +43,43 @@ struct stmmac_resources {
int tx_irq[MTL_MAX_TX_QUEUES];
};
+enum stmmac_state {
+ STMMAC_STATE_RESET,
+ STMMAC_STATE_RESET_RX,
+ STMMAC_STATE_RESET_TX,
+ STMMAC_STATE_DROP_RTC,
+ STMMAC_STATE_BUMP_TTC,
+ STMMAC_STATE_RESUME_TX,
+ STMMAC_STATE_COUNT
+};
+
+#define stmmac_set_state(_priv, _state) \
+ test_and_set_bit(STMMAC_STATE_ ## _state, (_priv)->state)
+
+#define stmmac_get_state(_priv, _state) \
+ test_bit(STMMAC_STATE_ ## _state, (_priv)->state)
+
+#define stmmac_clr_state(_priv, _state) \
+ test_and_clear_bit(STMMAC_STATE_ ## _state, (_priv)->state)
+
+#define stmmac_srv_state(_priv, _state) \
+ ({ \
+ if (!stmmac_set_state(_priv, _state)) \
+ queue_work((_priv)->state_wq, &(_priv)->state_work); \
+ })
+
+#define stmmac_sch_state(_priv, _ch, _state) \
+ ({ \
+ if (!stmmac_set_state(&(_priv)->channel[(_ch)], _state)) \
+ queue_work((_priv)->state_wq, &(_priv)->state_work); \
+ })
+
+#define stmmac_fls_state(_priv, _state) \
+ ({ \
+ if (stmmac_clr_state(_priv, _state)) \
+ flush_work(&(_priv)->state_work); \
+ })
+
enum stmmac_txbuf_type {
STMMAC_TXBUF_T_SKB,
STMMAC_TXBUF_T_XDP_TX,
@@ -51,7 +92,6 @@ struct stmmac_tx_info {
bool map_as_page;
unsigned len;
bool last_segment;
- bool is_jumbo;
enum stmmac_txbuf_type buf_type;
struct xsk_tx_metadata_compl xsk_meta;
};
@@ -81,6 +121,8 @@ struct stmmac_tx_queue {
dma_addr_t dma_tx_phy;
dma_addr_t tx_tail_addr;
u32 mss;
+ u16 tci;
+ u16 ttc;
};
struct stmmac_rx_buffer {
@@ -89,11 +131,14 @@ struct stmmac_rx_buffer {
struct page *page;
dma_addr_t addr;
__u32 page_offset;
+ __u32 frame_offset;
+ __u32 frag_offset;
};
struct xdp_buff *xdp;
};
struct page *sec_page;
dma_addr_t sec_addr;
+ __u32 sec_page_offset;
};
struct stmmac_xdp_buff {
@@ -122,6 +167,7 @@ struct stmmac_rx_queue {
struct xdp_rxq_info xdp_rxq;
struct xsk_buff_pool *xsk_pool;
struct page_pool *page_pool;
+ struct page_pool *sec_page_pool;
struct stmmac_rx_buffer *buf_pool;
struct stmmac_priv *priv_data;
struct dma_extended_desc *dma_erx;
@@ -132,6 +178,7 @@ struct stmmac_rx_queue {
unsigned int napi_skb_frag_size;
dma_addr_t dma_rx_phy;
u32 rx_tail_addr;
+ u16 rtc;
unsigned int state_saved;
struct {
struct sk_buff *skb;
@@ -141,6 +188,7 @@ struct stmmac_rx_queue {
};
struct stmmac_channel {
+ unsigned long state[BITS_TO_LONGS(STMMAC_STATE_COUNT)];
struct napi_struct rx_napi ____cacheline_aligned_in_smp;
struct napi_struct tx_napi ____cacheline_aligned_in_smp;
struct napi_struct rxtx_napi ____cacheline_aligned_in_smp;
@@ -187,7 +235,6 @@ struct stmmac_pps_cfg {
};
struct stmmac_rss {
- int enable;
u8 key[STMMAC_RSS_HASH_KEY_SIZE];
u32 table[STMMAC_RSS_MAX_TABLE_SIZE];
};
@@ -222,10 +269,12 @@ struct stmmac_dma_conf {
unsigned int dma_buf_sz;
/* RX Queue */
+ unsigned int rx_queue_max;
struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES];
unsigned int dma_rx_size;
/* TX Queue */
+ unsigned int tx_queue_max;
struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES];
unsigned int dma_tx_size;
};
@@ -246,16 +295,22 @@ struct stmmac_est {
struct stmmac_priv {
/* Frequently used values are kept adjacent for cache effect */
+
+ /* Device state-flags and work-task handling them */
+ unsigned long state[BITS_TO_LONGS(STMMAC_STATE_COUNT)];
+ struct workqueue_struct *state_wq;
+ struct work_struct state_work;
+
u32 tx_coal_frames[MTL_MAX_TX_QUEUES];
u32 tx_coal_timer[MTL_MAX_TX_QUEUES];
u32 rx_coal_frames[MTL_MAX_RX_QUEUES];
int hwts_tx_en;
bool tx_path_in_lpi_mode;
- bool tso;
bool sph_active;
- bool sph_capable;
u32 sarc_type;
+
+ unsigned int rx_copybreak;
u32 rx_riwt[MTL_MAX_RX_QUEUES];
int hwts_rx_en;
bool tsfupdt_coarse;
@@ -322,8 +377,10 @@ struct stmmac_priv {
void __iomem *mmcaddr;
void __iomem *ptpaddr;
void __iomem *estaddr;
- unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
- unsigned int num_double_vlans;
+
+ DECLARE_BITMAP(active_cvlans, VLAN_N_VID);
+ DECLARE_BITMAP(active_svlans, VLAN_N_VID);
+
int sfty_irq;
int sfty_ce_irq;
int sfty_ue_irq;
@@ -343,10 +400,6 @@ struct stmmac_priv {
struct dentry *dbgfs_dir;
#endif
- unsigned long state;
- struct workqueue_struct *wq;
- struct work_struct service_task;
-
/* Frame Preemption feature (FPE) */
struct stmmac_fpe_cfg fpe_cfg;
@@ -367,6 +420,13 @@ struct stmmac_priv {
/* Receive Side Scaling */
struct stmmac_rss rss;
+ /* ARP offload */
+ __be32 arp_addr;
+ struct notifier_block arp_cb;
+
+ /* General Purpose IO */
+ struct stmmac_gpio gpio;
+
/* XDP BPF Program */
unsigned long *af_xdp_zc_qps;
struct bpf_prog *xdp_prog;
@@ -374,13 +434,6 @@ struct stmmac_priv {
struct devlink *devlink;
};
-enum stmmac_state {
- STMMAC_DOWN,
- STMMAC_RESET_REQUESTED,
- STMMAC_RESETING,
- STMMAC_SERVICE_SCHED,
-};
-
extern const struct dev_pm_ops stmmac_simple_pm_ops;
int stmmac_mdio_unregister(struct net_device *ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index c1e26965d9b5d..6e30a7fbf49dd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -16,6 +16,8 @@
#include <linux/phylink.h>
#include <linux/net_tstamp.h>
+#include <net/page_pool/helpers.h>
+
#include "stmmac.h"
#include "stmmac_fpe.h"
#include "dwmac_dma.h"
@@ -89,7 +91,6 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(fatal_bus_error_irq),
/* Tx/Rx IRQ Events */
STMMAC_STAT(rx_early_irq),
- STMMAC_STAT(threshold),
STMMAC_STAT(irq_receive_pmt_irq_n),
/* MMC info */
STMMAC_STAT(mmc_tx_irq_n),
@@ -121,6 +122,7 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(ptp_frame_type),
STMMAC_STAT(ptp_ver),
STMMAC_STAT(timestamp_dropped),
+ STMMAC_STAT(arp_pkt_ofld),
STMMAC_STAT(av_pkt_rcvd),
STMMAC_STAT(av_tagged_pkt_rcvd),
STMMAC_STAT(vlan_tag_priority_val),
@@ -483,6 +485,9 @@ static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data)
{
u32 tx_cnt = priv->plat->tx_queues_to_use;
u32 rx_cnt = priv->plat->rx_queues_to_use;
+#ifdef CONFIG_PAGE_POOL_STATS
+ struct page_pool_stats stats = {};
+#endif
unsigned int start;
int q;
@@ -511,6 +516,19 @@ static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data)
*data++ = pkt_n;
*data++ = stmmac_get_rx_normal_irq_n(priv, q);
}
+
+#ifdef CONFIG_PAGE_POOL_STATS
+ for (q = 0; q < rx_cnt; q++) {
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[q];
+
+ if (rx_q->page_pool)
+ page_pool_get_stats(rx_q->page_pool, &stats);
+ if (rx_q->sec_page_pool)
+ page_pool_get_stats(rx_q->sec_page_pool, &stats);
+ }
+
+ page_pool_ethtool_stats_get(data, &stats);
+#endif
}
static void stmmac_get_ethtool_stats(struct net_device *dev,
@@ -520,10 +538,11 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
u32 rx_queues_count = priv->plat->rx_queues_to_use;
u32 tx_queues_count = priv->plat->tx_queues_to_use;
u64 napi_poll = 0, normal_irq_n = 0;
- int i, j = 0, pos, ret;
unsigned long count;
unsigned int start;
+ int i, j = 0, pos;
+ /* Update the statistics by reading from the HW counters */
if (priv->dma_cap.asp) {
for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
if (!stmmac_safety_feat_dump(priv, &priv->sstats, i,
@@ -532,33 +551,31 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
}
}
- /* Update the DMA HW counters for dwmac10/100 */
- ret = stmmac_dma_diagnostic_fr(priv, &priv->xstats, priv->ioaddr);
- if (ret) {
- /* If supported, for new GMAC chips expose the MMC counters */
- if (priv->dma_cap.rmon) {
- stmmac_mmc_read(priv, priv->mmcaddr, &priv->mmc);
+ if (priv->dma_cap.rmon) {
+ stmmac_mmc_read(priv, priv->mmcaddr, &priv->mmc);
- for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) {
- char *p;
- p = (char *)priv + stmmac_mmc[i].stat_offset;
+ for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) {
+ char *p;
+ p = (char *)priv + stmmac_mmc[i].stat_offset;
- data[j++] = (stmmac_mmc[i].sizeof_stat ==
- sizeof(u64)) ? (*(u64 *)p) :
- (*(u32 *)p);
- }
- }
- if (priv->dma_cap.eee) {
- int val = phylink_get_eee_err(priv->phylink);
- if (val)
- priv->xstats.phy_eee_wakeup_error_n = val;
+ data[j++] = (stmmac_mmc[i].sizeof_stat == sizeof(u64)) ?
+ (*(u64 *)p) : (*(u32 *)p);
}
+ }
- if (priv->synopsys_id >= DWMAC_CORE_3_50)
- stmmac_mac_debug(priv, priv->ioaddr,
- (void *)&priv->xstats,
- rx_queues_count, tx_queues_count);
+ if (priv->dma_cap.eee) {
+ int val = phylink_get_eee_err(priv->phylink);
+ if (val)
+ priv->xstats.phy_eee_wakeup_error_n = val;
}
+
+ stmmac_mac_debug(priv, priv->ioaddr,
+ (void *)&priv->xstats,
+ rx_queues_count, tx_queues_count);
+
+ for (i = 0; i < rx_queues_count; i++)
+ stmmac_dma_diagnostic_fr(priv, priv->ioaddr, &priv->xstats, i);
+
for (i = 0; i < STMMAC_STATS_LEN; i++) {
char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset;
data[j++] = (stmmac_gstrings_stats[i].sizeof_stat ==
@@ -630,7 +647,8 @@ static int stmmac_get_sset_count(struct net_device *netdev, int sset)
case ETH_SS_STATS:
len = STMMAC_STATS_LEN + STMMAC_QSTATS +
STMMAC_TXQ_STATS * tx_cnt +
- STMMAC_RXQ_STATS * rx_cnt;
+ STMMAC_RXQ_STATS * rx_cnt +
+ page_pool_ethtool_stats_get_count();
if (priv->dma_cap.rmon)
len += STMMAC_MMC_STATS_LEN;
@@ -673,6 +691,8 @@ static void stmmac_get_qstats_string(struct stmmac_priv *priv, u8 *data)
data += ETH_GSTRING_LEN;
}
}
+
+ page_pool_ethtool_stats_get_strings(data);
}
static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
@@ -973,6 +993,7 @@ static int stmmac_set_rxfh(struct net_device *dev,
memcpy(priv->rss.key, rxfh->key, sizeof(priv->rss.key));
return stmmac_rss_configure(priv, priv->hw, &priv->rss,
+ priv->dev->features & NETIF_F_RXHASH,
priv->plat->rx_queues_to_use);
}
@@ -983,18 +1004,14 @@ static void stmmac_get_channels(struct net_device *dev,
chan->rx_count = priv->plat->rx_queues_to_use;
chan->tx_count = priv->plat->tx_queues_to_use;
- chan->max_rx = priv->dma_cap.number_rx_queues;
- chan->max_tx = priv->dma_cap.number_tx_queues;
+ chan->max_rx = priv->dma_conf.rx_queue_max;
+ chan->max_tx = priv->dma_conf.tx_queue_max;
}
static int stmmac_set_channels(struct net_device *dev,
struct ethtool_channels *chan)
{
- struct stmmac_priv *priv = netdev_priv(dev);
-
- if (chan->rx_count > priv->dma_cap.number_rx_queues ||
- chan->tx_count > priv->dma_cap.number_tx_queues ||
- !chan->rx_count || !chan->tx_count)
+ if (!chan->rx_count || !chan->tx_count)
return -EINVAL;
return stmmac_reinit_queues(dev, chan->rx_count, chan->tx_count);
@@ -1035,6 +1052,43 @@ static int stmmac_get_ts_info(struct net_device *dev,
return ethtool_op_get_ts_info(dev, info);
}
+static int stmmac_get_tunable(struct net_device *dev,
+ const struct ethtool_tunable *tuna, void *data)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ int ret = 0;
+
+ switch (tuna->id) {
+ case ETHTOOL_RX_COPYBREAK:
+ *(u32 *)data = priv->rx_copybreak;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int stmmac_set_tunable(struct net_device *dev,
+ const struct ethtool_tunable *tuna,
+ const void *data)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ int ret = 0;
+
+ switch (tuna->id) {
+ case ETHTOOL_RX_COPYBREAK:
+ priv->rx_copybreak = *(u32 *)data;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
static int stmmac_get_mm(struct net_device *ndev,
struct ethtool_mm_state *state)
{
@@ -1124,6 +1178,8 @@ static const struct ethtool_ops stmmac_ethtool_ops = {
.set_per_queue_coalesce = stmmac_set_per_queue_coalesce,
.get_channels = stmmac_get_channels,
.set_channels = stmmac_set_channels,
+ .get_tunable = stmmac_get_tunable,
+ .set_tunable = stmmac_set_tunable,
.get_link_ksettings = stmmac_ethtool_get_link_ksettings,
.set_link_ksettings = stmmac_ethtool_set_link_ksettings,
.get_mm = stmmac_get_mm,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_gpio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_gpio.c
new file mode 100644
index 0000000000000..80df614a2662c
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_gpio.c
@@ -0,0 +1,519 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ * stmmac GPIO handling
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+
+#include "common.h"
+#include "hwif.h"
+#include "stmmac.h"
+#include "stmmac_gpio.h"
+#include "stmmac_platform.h"
+
+/* Synopsys DesignWare Ethernet controllers can be synthesized with
+ * General-Purpose IOs support. GPIOs can work either as inputs or as
+ * outputs depending on whether they belong to the gpi_i and gpo_o ports
+ * respectively. The ports width (number of possible inputs/outputs) and
+ * the configuration registers layout depend on the IP-core version and
+ * the GIW/GOW synthesize parameters. For instance, DW GMAC can have from
+ * 0 to 4 GPIs and from 0 to 4 GPOs, while DW QoS/xGMAC have a wider ports
+ * width up to 16 pins of each one. Here are the DW MAC GPIO block
+ * implementation specifics:
+ *
+ * Note 1. The GPIs state is multiplexed with the edge-triggered interrupt
+ * status in the GPIO control/status register. That is in case of the falling
+ * or rising edge event the GPI status register field preserves the latched
+ * state of the input pin until the next read from the register. So in order to
+ * read the actual state of the input pin we'd need to read the GPIO status
+ * register twice. But that also causes the race condition between the ordinary
+ * GPI state reads and the GPI IRQ handler. In order to fix that the
+ * IRQ-pending value cache is implemented.
+ *
+ * Note 2. The GPIs state configuration fields are mapped either to the
+ * GPIO control or the GPIO status register, which other than that may
+ * also provide the settings like GPOs state, GPIs IRQ type/mask/unmask.
+ * Due to that we have no choice but to cache the registers state and use
+ * the cached values to update the denoted settings, so to prevent the
+ * racy reads of the GPIs.
+ *
+ * Note 3. Due to the multiplexed GPIs state and interrupt status, the
+ * driver may experience false repeated IRQs detection. That is if after
+ * reading the GPI status register and calling the interrupt handler a client
+ * device doesn't revert the IRQ lane state and some other GPI raises an
+ * interrupt, the driver will get to detect the previous IRQ again. So the
+ * GPIO client device must return the GPI state back to idling after the
+ * IRQ is handled.
+ *
+ * Note 4. The GPIOs state is cleared together with the DW *MAC controller
+ * reset. So if IP-core isn't fixed to prevent that behavior the core
+ * resets mustn't be performed while the GPIO-chip is being registered in
+ * the system to have a stable GPIs/GPOs interface.
+ */
+
+/* IRQs are acked in the parental ISR, but the irq_ack() callback is still
+ * required for the edge-triggered IRQ handling by the IRQ-chip core.
+ */
+static void stmmac_gpio_irq_ack(struct irq_data *data)
+{
+}
+
+static void stmmac_gpio_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct stmmac_priv *priv = gpiochip_get_data(gc);
+ unsigned long flags;
+ u32 mask;
+
+ mask = ~BIT(irqd_to_hwirq(d));
+
+ spin_lock_irqsave(&priv->gpio.lock, flags);
+
+ priv->gpio.cache.gpie &= mask;
+ stmmac_gpio_set_ctrl(priv, priv->gpio.cache.gpie,
+ priv->gpio.cache.gpit, priv->gpio.cache.gpo);
+
+ priv->gpio.cache.gpip &= mask;
+
+ spin_unlock_irqrestore(&priv->gpio.lock, flags);
+}
+
+static void stmmac_gpio_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct stmmac_priv *priv = gpiochip_get_data(gc);
+ unsigned long flags;
+ u32 mask;
+
+ mask = BIT(irqd_to_hwirq(d));
+
+ spin_lock_irqsave(&priv->gpio.lock, flags);
+
+ priv->gpio.cache.gpie |= mask;
+ stmmac_gpio_set_ctrl(priv, priv->gpio.cache.gpie,
+ priv->gpio.cache.gpit, priv->gpio.cache.gpo);
+
+ spin_unlock_irqrestore(&priv->gpio.lock, flags);
+}
+
+static int stmmac_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct stmmac_priv *priv = gpiochip_get_data(gc);
+
+ return irq_set_irq_wake(priv->dev->irq, on);
+}
+
+static int stmmac_gpio_irq_set_type(struct irq_data *d, u32 type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct stmmac_priv *priv = gpiochip_get_data(gc);
+ irq_hw_number_t offset = irqd_to_hwirq(d);
+ unsigned long flags;
+ int ret = 0;
+ u32 mask;
+
+ if (offset >= priv->gpio.ngpis)
+ return -EINVAL;
+
+ mask = BIT(offset);
+
+ spin_lock_irqsave(&priv->gpio.lock, flags);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ priv->gpio.cache.gpit &= ~mask;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ priv->gpio.cache.gpit |= mask;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+
+ stmmac_gpio_set_ctrl(priv, priv->gpio.cache.gpie,
+ priv->gpio.cache.gpit, priv->gpio.cache.gpo);
+
+ priv->gpio.cache.gpip &= ~mask;
+
+err_unlock:
+ spin_unlock_irqrestore(&priv->gpio.lock, flags);
+
+ return ret;
+}
+
+static void stmmac_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+ seq_printf(p, dev_name(gc->parent));
+}
+
+static irqreturn_t stmmac_gpio_interrupt(int irq, void *dev_id)
+{
+ struct stmmac_gpio *gpio = dev_id;
+ struct gpio_chip *gc = &gpio->gc;
+ struct stmmac_priv *priv;
+ irq_hw_number_t hwirq;
+ unsigned long status;
+
+ priv = container_of(gpio, struct stmmac_priv, gpio);
+
+ /* Make sure we get to handle only the GPIs with unmasked
+ * latched-low/-high events and the GPI is latched in accordance with
+ * the type of the event. If for some reason the IRQ cause hasn't been
+ * cleared on the previous IRQ handler execution or the client device
+ * hasn't got the GPI state back, here we'll get a repeated false IRQ.
+ */
+ spin_lock(&gpio->lock);
+
+ status = stmmac_gpio_get_gpis(priv);
+ status = (status ^ gpio->cache.gpit) | gpio->cache.gpip;
+ status &= gpio->cache.gpie;
+
+ gpio->cache.gpip = 0;
+
+ spin_unlock(&gpio->lock);
+
+ for_each_set_bit(hwirq, &status, gpio->ngpis)
+ generic_handle_irq(irq_find_mapping(gc->irq.domain, hwirq));
+
+ return IRQ_RETVAL(status);
+}
+
+static const struct irq_chip stmmac_gpio_irq_chip = {
+ .name = "dwmac",
+ .irq_ack = stmmac_gpio_irq_ack,
+ .irq_mask = stmmac_gpio_irq_mask,
+ .irq_unmask = stmmac_gpio_irq_unmask,
+ .irq_set_wake = stmmac_gpio_irq_set_wake,
+ .irq_set_type = stmmac_gpio_irq_set_type,
+ .irq_print_chip = stmmac_gpio_irq_print_chip,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static int stmmac_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ struct stmmac_priv *priv = gpiochip_get_data(gc);
+
+ if (offset < priv->gpio.ngpis)
+ return GPIO_LINE_DIRECTION_IN;
+ else if (offset < priv->plat->ngpios)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return -EINVAL;
+}
+
+static int stmmac_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct stmmac_priv *priv = gpiochip_get_data(gc);
+ unsigned long flags;
+ int ret;
+
+ if (offset >= priv->plat->ngpios)
+ return -EINVAL;
+
+ spin_lock_irqsave(&priv->gpio.lock, flags);
+
+ if (offset < priv->gpio.ngpis) {
+ /* Read twice to clear the latched state out and get the
+ * current input pin state.
+ */
+ ret = stmmac_gpio_get_gpis(priv);
+ priv->gpio.cache.gpip |= (ret ^ priv->gpio.cache.gpit) &
+ priv->gpio.cache.gpie;
+
+ ret = stmmac_gpio_get_gpis(priv);
+ priv->gpio.cache.gpip |= (ret ^ priv->gpio.cache.gpit) &
+ priv->gpio.cache.gpie;
+ } else {
+ ret = priv->gpio.cache.gpo << priv->gpio.ngpis;
+ }
+
+ spin_unlock_irqrestore(&priv->gpio.lock, flags);
+
+ return !!(ret & BIT(offset));
+}
+
+static int stmmac_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct stmmac_priv *priv = gpiochip_get_data(gc);
+ unsigned long flags, fs, val = 0;
+
+ fs = __ffs(*mask);
+
+ spin_lock_irqsave(&priv->gpio.lock, flags);
+
+ if (fs <= priv->gpio.ngpis) {
+ /* Read twice to clear the latched state out and get the
+ * current input pin state.
+ */
+ val = stmmac_gpio_get_gpis(priv);
+ priv->gpio.cache.gpip |= (val ^ priv->gpio.cache.gpit) &
+ priv->gpio.cache.gpie;
+
+ val = stmmac_gpio_get_gpis(priv);
+ priv->gpio.cache.gpip |= (val ^ priv->gpio.cache.gpit) &
+ priv->gpio.cache.gpie;
+ }
+
+ val |= (priv->gpio.cache.gpo << priv->gpio.ngpis);
+
+ spin_unlock_irqrestore(&priv->gpio.lock, flags);
+
+ bitmap_replace(bits, bits, &val, mask, priv->plat->ngpios);
+
+ return 0;
+}
+
+static int stmmac_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ struct stmmac_priv *priv = gpiochip_get_data(gc);
+ unsigned long flags;
+
+ if (offset < priv->gpio.ngpis || offset >= priv->plat->ngpios)
+ return -EINVAL;
+
+ offset -= priv->gpio.ngpis;
+
+ spin_lock_irqsave(&priv->gpio.lock, flags);
+
+ if (value)
+ priv->gpio.cache.gpo |= BIT(offset);
+ else
+ priv->gpio.cache.gpo &= ~BIT(offset);
+
+ stmmac_gpio_set_ctrl(priv, priv->gpio.cache.gpie,
+ priv->gpio.cache.gpit, priv->gpio.cache.gpo);
+
+ spin_unlock_irqrestore(&priv->gpio.lock, flags);
+
+ return 0;
+}
+
+static int stmmac_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct stmmac_priv *priv = gpiochip_get_data(gc);
+ unsigned long flags, gpom, gpob;
+
+ gpom = *mask >> priv->gpio.ngpis;
+ gpob = *bits >> priv->gpio.ngpis;
+
+ spin_lock_irqsave(&priv->gpio.lock, flags);
+
+ priv->gpio.cache.gpo = (priv->gpio.cache.gpo & ~gpom) | gpob;
+
+ stmmac_gpio_set_ctrl(priv, priv->gpio.cache.gpie,
+ priv->gpio.cache.gpit, priv->gpio.cache.gpo);
+
+ spin_unlock_irqrestore(&priv->gpio.lock, flags);
+
+ return 0;
+}
+
+static int stmmac_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
+ unsigned long cfg)
+{
+ enum pin_config_param mode = pinconf_to_config_param(cfg);
+
+ /* All output pins operate as open source */
+ if (mode != PIN_CONFIG_DRIVE_OPEN_SOURCE)
+ return -ENOTSUPP;
+
+ return gpiochip_generic_config(gc, offset, cfg);
+}
+
+static int stmmac_gpio_data_init(struct stmmac_priv *priv)
+{
+ u32 tmp;
+
+ if (priv->plat->ngpios > STMMAC_GPI_MAX) {
+ dev_err(priv->device, "Invalid ngpios specified\n");
+ return -EINVAL;
+ }
+
+ /* GPIs auto-detection: save GPO state, set as many GPI type bits
+ * as possible, then read the written value back. The number of set
+ * bits will be the actual number of GPIs. Leave the GPI type field
+ * being initialized with ones (i.e. the inputs having latched-low
+ * type) to have the falling-edge IRQs by default.
+ */
+ priv->gpio.cache.gpit = ~0;
+ stmmac_gpio_get_ctrl(priv, &tmp, &tmp, &priv->gpio.cache.gpo);
+ stmmac_gpio_set_ctrl(priv, priv->gpio.cache.gpie,
+ priv->gpio.cache.gpit, priv->gpio.cache.gpo);
+ stmmac_gpio_get_ctrl(priv, &priv->gpio.cache.gpie,
+ &priv->gpio.cache.gpit, &priv->gpio.cache.gpo);
+
+ priv->gpio.ngpis = hweight_long(priv->gpio.cache.gpit);
+
+ if (priv->gpio.ngpis > priv->plat->ngpios) {
+ dev_err(priv->device, "ngpios less than detected\n");
+ return -EINVAL;
+ }
+
+ priv->gpio.ngpos = priv->plat->ngpios - priv->gpio.ngpis;
+
+ spin_lock_init(&priv->gpio.lock);
+
+ dev_info(priv->device, "GPI: %u, GPO: %u\n", priv->gpio.ngpis,
+ priv->gpio.ngpos);
+
+ return 0;
+}
+
+static void stmmac_gpio_chip_init(struct stmmac_priv *priv)
+{
+ struct gpio_chip *gc = &priv->gpio.gc;
+
+ gc->label = dev_name(priv->device);
+ gc->parent = priv->device;
+ gc->owner = THIS_MODULE;
+ gc->request = gpiochip_generic_request,
+ gc->free = gpiochip_generic_free,
+ gc->get_direction = stmmac_gpio_get_direction;
+ gc->get = stmmac_gpio_get;
+ gc->get_multiple = stmmac_gpio_get_multiple;
+ gc->set = stmmac_gpio_set;
+ gc->set_multiple = stmmac_gpio_set_multiple;
+ gc->set_config = stmmac_gpio_set_config;
+ gc->base = -1;
+ gc->ngpio = priv->plat->ngpios;
+ gc->can_sleep = false;
+}
+
+static int stmmac_gpio_irq_init(struct stmmac_priv *priv)
+{
+ struct gpio_chip *gc = &priv->gpio.gc;
+ struct gpio_irq_chip *girq = &gc->irq;
+ int ret;
+
+ /* Skip GPIO IRQ-chip initialization if no GPI detected */
+ if (!priv->gpio.ngpis)
+ return 0;
+
+ /* Make sure no MAC activity enabled before requesting IRQ so not
+ * to end up with spurious IRQs flood left pending after boot up.
+ */
+ ret = stmmac_core_stop(priv, priv->hw);
+ if (ret) {
+ dev_err(priv->device, "Failed to stop MAC before GPIO init\n");
+ return ret;
+ }
+
+ girq->handler = handle_edge_irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->parent_handler = NULL;
+
+ gpio_irq_chip_set_chip(girq, &stmmac_gpio_irq_chip);
+
+ ret = request_irq(priv->dev->irq, stmmac_gpio_interrupt,
+ IRQF_SHARED, priv->dev->name, &priv->gpio);
+ if (ret) {
+ dev_err(priv->device, "Failed to request GPIO IRQ\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void stmmac_gpio_irq_free(struct stmmac_priv *priv)
+{
+ if (!priv->gpio.ngpis)
+ return;
+
+ free_irq(priv->dev->irq, &priv->gpio);
+}
+
+static int stmmac_gpio_chip_register(struct stmmac_priv *priv)
+{
+ struct gpio_chip *gc = &priv->gpio.gc;
+ int ret;
+
+ ret = gpiochip_add_data(gc, priv);
+ if (ret)
+ dev_err(priv->device, "Failed to register GPIO-chip\n");
+
+ return ret;
+}
+
+static void stmmac_gpio_chip_remove(struct stmmac_priv *priv)
+{
+ gpiochip_remove(&priv->gpio.gc);
+}
+
+/**
+ * stmmac_gpio_is_available - test whether DW MAC GPIO is detected
+ * @priv: driver private structure
+ * Description: check whether the DW MAC has been synthesized with some
+ * GPIOs enabled.
+ * Return:
+ * true if the DW MAC has GPIO capability, otherwise - false
+ */
+bool stmmac_gpio_is_available(struct stmmac_priv *priv)
+{
+ return !!priv->plat->ngpios;
+}
+
+/**
+ * stmmac_gpio_add - add DW MAC GPIO chip
+ * @priv: driver private structure
+ * Description: register GPIO-chip handling the DW *MAC GPIs and GPOs
+ * Return:
+ * returns 0 on success, otherwise negative errno.
+ */
+int stmmac_gpio_setup(struct stmmac_priv *priv)
+{
+ int ret;
+
+ if (!priv->plat->ngpios)
+ return 0;
+
+ ret = stmmac_gpio_data_init(priv);
+ if (ret)
+ return ret;
+
+ stmmac_gpio_chip_init(priv);
+
+ ret = stmmac_gpio_irq_init(priv);
+ if (ret)
+ return ret;
+
+ ret = stmmac_gpio_chip_register(priv);
+ if (ret)
+ goto err_irq_free;
+
+ return 0;
+
+err_irq_free:
+ stmmac_gpio_irq_free(priv);
+
+ return ret;
+}
+
+/**
+ * stmmac_gpio_clean - remove DW MAC GPIO-chip
+ * @priv: driver private structure
+ * Description: remove GPIO-chip registered for the DW *MAC GPIs and GPOs
+ */
+void stmmac_gpio_clean(struct stmmac_priv *priv)
+{
+ if (!priv->plat->ngpios)
+ return;
+
+ stmmac_gpio_chip_remove(priv);
+
+ stmmac_gpio_irq_free(priv);
+}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_gpio.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_gpio.h
new file mode 100644
index 0000000000000..17888e0b64d56
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_gpio.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ * stmmac GPIO handling
+ */
+
+#ifndef __STMMAC_GPIO_H__
+#define __STMMAC_GPIO_H__
+
+#include <linux/container_of.h>
+#include <linux/gpio/driver.h>
+#include <linux/spinlock.h>
+
+#include "stmmac.h"
+
+#define STMMAC_GPI_MAX 16
+#define STMMAC_GPO_MAX 16
+#define STMMAC_GPIO_MAX (STMMAC_GPI_MAX + STMMAC_GPO_MAX)
+
+struct stmmac_gpio {
+ struct gpio_chip gc;
+
+ u32 ngpis;
+ u32 ngpos;
+
+ struct {
+ u32 gpie;
+ u32 gpit;
+ u32 gpip;
+ u32 gpo;
+ } cache;
+ spinlock_t lock;
+};
+
+bool stmmac_gpio_is_available(struct stmmac_priv *priv);
+int stmmac_gpio_setup(struct stmmac_priv *priv);
+void stmmac_gpio_clean(struct stmmac_priv *priv);
+
+#endif /* __STMMAC_GPIO_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 13d3cac056bea..a4cbdac59c4b1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -16,8 +16,10 @@
#include <linux/clk.h>
#include <linux/kernel.h>
+#include <linux/inetdevice.h>
#include <linux/interrupt.h>
#include <linux/ip.h>
+#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
@@ -41,12 +43,14 @@
#include <linux/udp.h>
#include <linux/bpf_trace.h>
#include <net/devlink.h>
+#include <net/ipv6.h>
#include <net/page_pool/helpers.h>
#include <net/pkt_cls.h>
#include <net/xdp_sock_drv.h>
#include "stmmac_ptp.h"
#include "stmmac_fpe.h"
#include "stmmac.h"
+#include "stmmac_gpio.h"
#include "stmmac_pcs.h"
#include "stmmac_xdp.h"
#include <linux/reset.h>
@@ -61,7 +65,7 @@
*/
#define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCTRLSSR)
-#define STMMAC_ALIGN(x) ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16)
+#define TSO_MAX_LOAD_SIZE SZ_256K
#define TSO_MAX_BUFF_SIZE (SZ_16K - 1)
/* Module parameters */
@@ -99,16 +103,20 @@ static int pause = PAUSE_TIME;
module_param(pause, int, 0644);
MODULE_PARM_DESC(pause, "Flow Control Pause Time (units of 512 bit times)");
-#define TC_DEFAULT 64
-static int tc = TC_DEFAULT;
-module_param(tc, int, 0644);
-MODULE_PARM_DESC(tc, "DMA threshold control value");
+static int rtc = 128;
+module_param(rtc, int, 0644);
+MODULE_PARM_DESC(rtc, "DMA Rx threshold control value");
-/* This is unused */
-#define DEFAULT_BUFSIZE 1536
-static int buf_sz = DEFAULT_BUFSIZE;
-module_param(buf_sz, int, 0644);
-MODULE_PARM_DESC(buf_sz, "DMA buffer size");
+static int ttc = 32;
+module_param(ttc, int, 0644);
+MODULE_PARM_DESC(tc, "DMA Tx threshold control value");
+
+#define DEFAULT_BUFSIZE 1536U
+static unsigned int buf_sz = DEFAULT_BUFSIZE;
+module_param(buf_sz, uint, 0644);
+MODULE_PARM_DESC(buf_sz, "Rx DMA buffer size");
+
+#define STMMAC_RX_COPYBREAK 256
static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
NETIF_MSG_LINK | NETIF_MSG_IFUP |
@@ -139,8 +147,9 @@ static const char *stmmac_dwmac_actphyif[8] = {
};
static const char *stmmac_dwxgmac_phyif[4] = {
- [PHY_INTF_GMII] = "GMII",
+ [PHY_INTF_XGMII] = "(XG)MII",
[PHY_INTF_RGMII] = "RGMII",
+ [PHY_INTF_RMII] = "RMII",
};
static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
@@ -154,9 +163,6 @@ static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue);
static void stmmac_reset_queues_param(struct stmmac_priv *priv);
static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue);
static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue);
-static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
- u32 rxmode, u32 chan);
-static void stmmac_vlan_restore(struct stmmac_priv *priv);
#ifdef CONFIG_DEBUG_FS
static const struct net_device_ops stmmac_netdev_ops;
@@ -254,6 +260,10 @@ static void stmmac_verify_args(void)
{
if (unlikely(watchdog < 0))
watchdog = TX_TIMEO;
+
+ if (unlikely(buf_sz < DEFAULT_BUFSIZE || buf_sz > SZ_16K))
+ buf_sz = DEFAULT_BUFSIZE;
+
if (unlikely((pause < 0) || (pause > 0xffff)))
pause = PAUSE_TIME;
@@ -333,18 +343,85 @@ static void stmmac_enable_all_queues(struct stmmac_priv *priv)
}
}
-static void stmmac_service_event_schedule(struct stmmac_priv *priv)
+/**
+ * stmmac_handle_fatal_err - Handle controller fatal error
+ * @priv: driver private structure
+ * @queue: TX queue index
+ * Description:
+ * Handle unrecoverable error by submitting the controller reset and
+ * invoking the service task.
+ */
+static void stmmac_handle_fatal_err(struct stmmac_priv *priv)
{
- if (!test_bit(STMMAC_DOWN, &priv->state) &&
- !test_and_set_bit(STMMAC_SERVICE_SCHED, &priv->state))
- queue_work(priv->wq, &priv->service_task);
+ netif_carrier_off(priv->dev);
+
+ stmmac_srv_state(priv, RESET);
}
-static void stmmac_global_err(struct stmmac_priv *priv)
+/**
+ * stmmac_handle_rx_err - Handle Rx queue error
+ * @priv: driver private structure
+ * @queue: RX queue index
+ * Description:
+ * Handle recoverable Rx queue error by updating the channel state and
+ * invoking the service task.
+ */
+static void stmmac_handle_rx_err(struct stmmac_priv *priv, u32 queue)
{
- netif_carrier_off(priv->dev);
- set_bit(STMMAC_RESET_REQUESTED, &priv->state);
- stmmac_service_event_schedule(priv);
+ stmmac_sch_state(priv, queue, RESET_RX);
+}
+
+/**
+ * stmmac_handle_tx_err - Handle Tx queue error
+ * @priv: driver private structure
+ * @queue: TX queue index
+ * Description:
+ * Handle recoverable Tx queue error by updating the channel state and
+ * invoking the service task.
+ */
+static void stmmac_handle_tx_err(struct stmmac_priv *priv, u32 queue)
+{
+ stmmac_sch_state(priv, queue, RESET_TX);
+}
+
+/**
+ * stmmac_handle_rx_ovf_error - Handle Rx FIFO overflow error
+ * @priv: driver private structure
+ * @queue: RX queue index
+ * Description:
+ * Handle Rx overflow event raised on the IP-cores for instance due
+ * to too slow DMA-interface so it doesn't keep up with the upcoming
+ * frame.
+ */
+static void stmmac_handle_rx_ovf_error(struct stmmac_priv *priv, u32 queue)
+{
+ stmmac_sch_state(priv, queue, DROP_RTC);
+}
+
+/**
+ * stmmac_handle_tx_unf_error - Handle Tx FIFO underflow error
+ * @priv: driver private structure
+ * @queue: TX queue index
+ * Description:
+ * Handle Tx underrun event raised on some IP-cores for instance due
+ * to too slow DMA-interface comparing to the link speed.
+ */
+static void stmmac_handle_tx_unf_error(struct stmmac_priv *priv, u32 queue)
+{
+ stmmac_sch_state(priv, queue, BUMP_TTC);
+}
+
+/**
+ * stmmac_handle_tx_stop - Handle Tx soft stop
+ * @priv: driver private structure
+ * @queue: TX queue index
+ * Description:
+ * Handle Tx queue stop event raised on some IP-cores for instance due
+ * to minor inconsistencies, like Jabber timeout.
+ */
+static void stmmac_handle_tx_stop(struct stmmac_priv *priv, u32 queue)
+{
+ stmmac_sch_state(priv, queue, RESUME_TX);
}
static void print_pkt(unsigned char *buf, int len)
@@ -683,8 +760,7 @@ static int stmmac_hwtstamp_set(struct net_device *dev,
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
ptp_v2 = PTP_TCR_TSVER2ENA;
snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
- if (priv->synopsys_id < DWMAC_CORE_4_10)
- ts_event_en = PTP_TCR_TSEVNTENA;
+
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
ptp_over_ethernet = PTP_TCR_TSIPENA;
@@ -955,11 +1031,13 @@ static struct phylink_pcs *stmmac_mac_select_pcs(struct phylink_config *config,
return pcs;
}
- /* The PCS control register is only relevant for SGMII, TBI and RTBI
- * modes. We no longer support TBI or RTBI, so only configure this
- * register when operating in SGMII mode with the integrated PCS.
+ /* The PCS AN CSRs are only relevant for SGMII, TBI and RTBI modes.
+ * The in-band link status is available for RGMII and SGMII interfaces.
+ * We no longer support TBI or RTBI, so use the feature when operating
+ * in RGMII or SGMII mode with the integrated PCS.
*/
- if (priv->hw->pcs & STMMAC_PCS_SGMII && priv->integrated_pcs)
+ if (priv->hw->pcs & (STMMAC_PCS_SGMII | STMMAC_PCS_RGMII) &&
+ priv->integrated_pcs)
return &priv->integrated_pcs->pcs;
return NULL;
@@ -1013,7 +1091,18 @@ static void stmmac_mac_link_up(struct phylink_config *config,
old_ctrl = readl(priv->ioaddr + MAC_CTRL_REG);
ctrl = old_ctrl & ~priv->hw->link.speed_mask;
- if (interface == PHY_INTERFACE_MODE_USXGMII) {
+ if (interface == PHY_INTERFACE_MODE_XGMII ||
+ interface == PHY_INTERFACE_MODE_10GBASER ||
+ interface == PHY_INTERFACE_MODE_10GBASEX ||
+ interface == PHY_INTERFACE_MODE_XAUI) {
+ switch (speed) {
+ case SPEED_10000:
+ ctrl |= priv->hw->link.xgmii.speed10000;
+ break;
+ default:
+ return;
+ }
+ } else if (interface == PHY_INTERFACE_MODE_USXGMII) {
switch (speed) {
case SPEED_10000:
ctrl |= priv->hw->link.xgmii.speed10000;
@@ -1024,6 +1113,9 @@ static void stmmac_mac_link_up(struct phylink_config *config,
case SPEED_2500:
ctrl |= priv->hw->link.xgmii.speed2500;
break;
+ case SPEED_1000:
+ ctrl |= priv->hw->link.speed1000;
+ break;
default:
return;
}
@@ -1232,6 +1324,9 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv)
priv->hw->reverse_sgmii_enable = false;
break;
}
+ } else if (phy_interface_mode_is_rgmii(interface)) {
+ netdev_dbg(priv->dev, "PCS RGMII support enabled\n");
+ priv->hw->pcs = STMMAC_PCS_RGMII;
}
}
@@ -1477,30 +1572,194 @@ static void stmmac_display_rings(struct stmmac_priv *priv,
stmmac_display_tx_rings(priv, dma_conf);
}
+/**
+ * stmmac_get_txpbl - retrieve Tx DMA PBL value
+ * @priv: driver private structure
+ * Description: calculate the Tx DMA PBL in accordance with the
+ * DW controller specifics.
+ * Return: Tx DMA PBL in beats.
+ */
+static unsigned int stmmac_get_txpbl(struct stmmac_priv *priv)
+{
+ struct stmmac_dma_cfg *dma_cfg = priv->plat->dma_cfg;
+ unsigned int factor;
+
+ /* DMA PBL multiplier had been x4 prior DW GMAC v3.50a */
+ if ((priv->plat->core_type == DWMAC_CORE_GMAC &&
+ priv->synopsys_id >= DWMAC_CORE_3_50) ||
+ dwmac_is_xmac(priv->plat->core_type)) {
+ factor = dma_cfg->pblx8 ? 8 : 1;
+ } else {
+ factor = dma_cfg->pblx8 ? 4 : 1;
+ }
+
+ return (dma_cfg->txpbl ?: dma_cfg->pbl) * factor;
+}
+
+/**
+ * stmmac_get_txfifosz - retrieve Tx MTL FIFO size
+ * @priv: driver private structure
+ * Description: calculate the Tx MTL FIFO depth in accordance with
+ * the amount of the activated queues.
+ * Return: current Tx FIFO depth.
+ */
+static unsigned int stmmac_get_txfifosz(struct stmmac_priv *priv)
+{
+
+ unsigned int txfifosz = priv->plat->tx_fifo_size;
+
+ /* Tx FIFO is shared between queues on the modern devices */
+ if (dwmac_is_xmac(priv->plat->core_type))
+ txfifosz /= priv->plat->tx_queues_to_use;
+
+ return txfifosz;
+}
+
+/**
+ * stmmac_get_rxfifosz - retrieve Rx MTL FIFO size
+ * @priv: driver private structure
+ * Description: calculate the Rx MTL FIFO depth in accordance with
+ * the amount of the activated queues.
+ * Return: current Rx FIFO depth.
+ */
+static unsigned int stmmac_get_rxfifosz(struct stmmac_priv *priv)
+{
+
+ unsigned int rxfifosz = priv->plat->rx_fifo_size;
+
+ /* Rx FIFO is shared between queues on the modern devices */
+ if (dwmac_is_xmac(priv->plat->core_type))
+ rxfifosz /= priv->plat->rx_queues_to_use;
+
+ return rxfifosz;
+}
+
+/**
+ * stmmac_get_min_mtu - retrieve minimal supported MTU
+ * @priv: driver private structure
+ * Return: minimal MTU.
+ */
+static unsigned int stmmac_get_min_mtu(struct stmmac_priv *priv)
+{
+ return ETH_ZLEN - ETH_HLEN;
+}
+
+/**
+ * stmmac_get_max_mtu - retrieve maximal supported MTU
+ * @priv: driver private structure
+ * @tx_coe: Tx COE feature state
+ * @tx_cnt: Number of active Tx queues
+ * @rx_cnt: Number of active Rx queues
+ * Description: calculate the maximum MTU supported by DW controller in
+ * accordance with the device active features.
+ * Return: maximum MTU.
+ */
+static unsigned int stmmac_get_max_mtu(struct stmmac_priv *priv, bool tx_coe,
+ u32 tx_cnt, u32 rx_cnt)
+{
+ unsigned int max_mtu, tx_mtu, rx_mtu, txfifosz, rxfifosz, addend;
+
+ /* MTU constraint caused by the MAC implementation */
+ if (dwmac_is_xmac(priv->plat->core_type))
+ max_mtu = STMMAC_MTU_GIANT;
+ else if (priv->plat->core_type == DWMAC_CORE_GMAC)
+ max_mtu = STMMAC_MTU_JUMBO;
+ else
+ max_mtu = STMMAC_MTU_NORMAL;
+
+ /* Override only with valid platform-specific max MTU constraint */
+ if (priv->plat->maxmtu >= stmmac_get_min_mtu(priv) &&
+ priv->plat->maxmtu < max_mtu)
+ max_mtu = priv->plat->maxmtu;
+
+ /* MTU constraint caused by DMA SF/CT mode and active Tx COE */
+ if (dwmac_is_xmac(priv->plat->core_type)) {
+ addend = priv->plat->data_width > 4 ? 5 : 7;
+ txfifosz = priv->plat->tx_fifo_size / tx_cnt;
+ rxfifosz = priv->plat->rx_fifo_size / rx_cnt;
+ } else {
+ addend = 3;
+ txfifosz = priv->plat->tx_fifo_size;
+ rxfifosz = priv->plat->rx_fifo_size;
+ }
+
+ /* DMA Cut-through mode doesn't apply any Rx FIFO-depth constraint */
+ if (priv->plat->force_thresh_dma_mode)
+ rx_mtu = max_mtu;
+ else
+ rx_mtu = rxfifosz - ETH_HLEN - 2 * VLAN_HLEN - ETH_FCS_LEN;
+
+ /* DMA Store-and-Forward mode requires frames to be <= FIFO-depth */
+ if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe)
+ tx_mtu = txfifosz - ETH_HLEN - 2 * VLAN_HLEN - ETH_FCS_LEN;
+ else
+ tx_mtu = max_mtu;
+
+ /* Tx COE and TSO further reduce the frame length to avoid dead-lock */
+ if (tx_coe) {
+ tx_mtu -= (stmmac_get_txpbl(priv) + addend) * priv->plat->data_width;
+ /* Subtract two more bytes since MTU must be strictly lesser */
+ tx_mtu -= 2;
+ }
+
+ /* Zero Tx/Rx FIFO size lead to standard max MTU due to subtractions */
+ return min3(max_mtu, tx_mtu, rx_mtu);
+}
+
+/**
+ * stmmac_fs_offset - find out platform-specific frame offset
+ * Description: retrieve Rx DMA-buffer offset to align up the incoming
+ * frames within the IP-header.
+ */
+static unsigned int stmmac_fs_offset(void)
+{
+#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ return 0;
+#else
+ return NET_IP_ALIGN;
+#endif
+}
+
+/**
+ * stmmac_rx_offset - find out Rx DMA-buffer offset
+ * @priv: driver private structure
+ * Description: retrieve Rx DMA-buffer offset specific to the current
+ * interface mode: XDP-less and XDP.
+ */
static unsigned int stmmac_rx_offset(struct stmmac_priv *priv)
{
if (stmmac_xdp_is_enabled(priv))
- return XDP_PACKET_HEADROOM;
+ return XDP_PACKET_HEADROOM + stmmac_fs_offset();
- return NET_SKB_PAD;
+ return NET_SKB_PAD + stmmac_fs_offset();
}
-static int stmmac_set_bfsize(int mtu)
+/**
+ * stmmac_get_bfsize- determine Rx DMA-buffer size
+ * @priv: driver private structure
+ * @mtu : requested MTU
+ * Description: calculate the controller Rx DMA-buffer size suitable
+ * for the passed MTU value.
+ *
+ * Return: the Rx DMA-buffer size.
+ */
+static unsigned int stmmac_get_bfsize(struct stmmac_priv *priv, int mtu)
{
- int ret;
+ unsigned int buff_size, buff_max;
- if (mtu >= BUF_SIZE_8KiB)
- ret = BUF_SIZE_16KiB;
- else if (mtu >= BUF_SIZE_4KiB)
- ret = BUF_SIZE_8KiB;
- else if (mtu >= BUF_SIZE_2KiB)
- ret = BUF_SIZE_4KiB;
- else if (mtu > DEFAULT_BUFSIZE)
- ret = BUF_SIZE_2KiB;
- else
- ret = DEFAULT_BUFSIZE;
+ /* Make sure there is a place for the entire frame including
+ * the Ethernet header plus possible C/S-VLAN headers.
+ */
+ buff_size = umax(mtu + ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN, buf_sz);
- return ret;
+ /* Make sure the buffer is bus-width aligned up to fit in all the DMA
+ * writes and to avoid undefined behaviour (see notes in the databooks).
+ */
+ buff_size = STMMAC_RX_BUF_ADJUST(buff_size);
+
+ /* Apply the max buf size constraint specific to the particular MAC */
+ buff_max = stmmac_get_rx_len(priv, priv->mode);
+ return umin(buff_size, buff_max);
}
/**
@@ -1599,40 +1858,49 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv,
*/
static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
struct stmmac_dma_conf *dma_conf,
- struct dma_desc *p,
+ struct dma_desc *p, dma_addr_t np,
int i, gfp_t flags, u32 queue)
{
struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
+ unsigned int size;
if (priv->dma_cap.host_dma_width <= 32)
gfp |= GFP_DMA32;
if (!buf->page) {
- buf->page = page_pool_alloc_pages(rx_q->page_pool, gfp);
+ size = rx_q->napi_skb_frag_size;
+ buf->page = page_pool_alloc(rx_q->page_pool,
+ &buf->page_offset, &size, gfp);
if (!buf->page)
return -ENOMEM;
- buf->page_offset = stmmac_rx_offset(priv);
+
+ buf->frame_offset = stmmac_rx_offset(priv);
+ buf->frag_offset = buf->frame_offset - stmmac_fs_offset();
+ buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset +
+ buf->frame_offset;
}
if (priv->sph_active && !buf->sec_page) {
- buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
+ size = dma_conf->dma_buf_sz;
+ buf->sec_page = page_pool_alloc(rx_q->sec_page_pool,
+ &buf->sec_page_offset, &size, gfp);
if (!buf->sec_page)
return -ENOMEM;
- buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
+ buf->sec_addr = page_pool_get_dma_addr(buf->sec_page) +
+ buf->sec_page_offset;
stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true);
} else {
buf->sec_page = NULL;
+ buf->sec_addr = 0;
stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false);
}
- buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
-
stmmac_set_desc_addr(priv, p, buf->addr);
- if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
- stmmac_init_desc3(priv, p);
+ stmmac_prepare_rx_desc(priv, p, priv->mode, np, true,
+ dma_conf->dma_buf_sz);
return 0;
}
@@ -1654,7 +1922,7 @@ static void stmmac_free_rx_buffer(struct stmmac_priv *priv,
buf->page = NULL;
if (buf->sec_page)
- page_pool_put_full_page(rx_q->page_pool, buf->sec_page, false);
+ page_pool_put_full_page(rx_q->sec_page_pool, buf->sec_page, false);
buf->sec_page = NULL;
}
@@ -1710,6 +1978,10 @@ static void stmmac_free_tx_buffer(struct stmmac_priv *priv,
* @priv: private structure
* @dma_conf: structure to take the dma data
* @queue: RX queue index
+ * Description: Revert what has been added to the descriptors in the
+ * framework of the stmmac_alloc_rx_buffers()//stmmac_refill() methods.
+ * Basically it means to unmap the respective buffers and get them back
+ * to the pages pool.
*/
static void dma_free_rx_skbufs(struct stmmac_priv *priv,
struct stmmac_dma_conf *dma_conf,
@@ -1720,6 +1992,12 @@ static void dma_free_rx_skbufs(struct stmmac_priv *priv,
for (i = 0; i < dma_conf->dma_rx_size; i++)
stmmac_free_rx_buffer(priv, rx_q, i);
+
+ rx_q->buf_alloc_num = 0;
+
+ if (rx_q->state_saved)
+ dev_kfree_skb(rx_q->state.skb);
+ rx_q->state_saved = false;
}
static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv,
@@ -1731,14 +2009,19 @@ static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv,
for (i = 0; i < dma_conf->dma_rx_size; i++) {
struct dma_desc *p;
+ dma_addr_t np;
int ret;
- if (priv->extend_desc)
- p = &((rx_q->dma_erx + i)->basic);
- else
- p = rx_q->dma_rx + i;
+ np = STMMAC_GET_ENTRY(i, dma_conf->dma_rx_size);
+ if (priv->extend_desc) {
+ p = &rx_q->dma_erx[i].basic;
+ np = rx_q->dma_rx_phy + np * sizeof(struct dma_extended_desc);
+ } else {
+ p = &rx_q->dma_rx[i];
+ np = rx_q->dma_rx_phy + np * sizeof(struct dma_desc);
+ }
- ret = stmmac_init_rx_buffers(priv, dma_conf, p, i, flags,
+ ret = stmmac_init_rx_buffers(priv, dma_conf, p, np, i, flags,
queue);
if (ret)
return ret;
@@ -1754,6 +2037,10 @@ static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv,
* @priv: private structure
* @dma_conf: structure to take the dma data
* @queue: RX queue index
+ * Description: Revert what has been added to the descriptors in the
+ * framework of the stmmac_alloc_rx_buffers_zc()/stmmac_refill_zc()
+ * methods. Basically it means to unmap the respective buffers and get
+ * them back to the XDP buff pool.
*/
static void dma_free_rx_xskbufs(struct stmmac_priv *priv,
struct stmmac_dma_conf *dma_conf,
@@ -1771,6 +2058,8 @@ static void dma_free_rx_xskbufs(struct stmmac_priv *priv,
xsk_buff_free(buf->xdp);
buf->xdp = NULL;
}
+
+ rx_q->buf_alloc_num = 0;
}
static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv,
@@ -1788,13 +2077,17 @@ static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv,
for (i = 0; i < dma_conf->dma_rx_size; i++) {
struct stmmac_rx_buffer *buf;
- dma_addr_t dma_addr;
+ dma_addr_t dma_addr, np;
struct dma_desc *p;
- if (priv->extend_desc)
- p = (struct dma_desc *)(rx_q->dma_erx + i);
- else
- p = rx_q->dma_rx + i;
+ np = STMMAC_GET_ENTRY(i, dma_conf->dma_rx_size);
+ if (priv->extend_desc) {
+ p = &rx_q->dma_erx[i].basic;
+ np = rx_q->dma_rx_phy + np * sizeof(struct dma_extended_desc);
+ } else {
+ p = &rx_q->dma_rx[i];
+ np = rx_q->dma_rx_phy + np * sizeof(struct dma_desc);
+ }
buf = &rx_q->buf_pool[i];
@@ -1804,6 +2097,9 @@ static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv,
dma_addr = xsk_buff_xdp_get_dma(buf->xdp);
stmmac_set_desc_addr(priv, p, dma_addr);
+ stmmac_prepare_rx_desc(priv, p, priv->mode, np, true,
+ dma_conf->dma_buf_sz);
+
rx_q->buf_alloc_num++;
}
@@ -1843,6 +2139,7 @@ static int __init_dma_rx_desc_rings(struct stmmac_priv *priv,
xdp_rxq_info_unreg_mem_model(&rx_q->xdp_rxq);
+ /* This must be performed on each SKB/XSK mode switch */
rx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue);
if (rx_q->xsk_pool) {
@@ -1873,18 +2170,6 @@ static int __init_dma_rx_desc_rings(struct stmmac_priv *priv,
return -ENOMEM;
}
- /* Setup the chained descriptor addresses */
- if (priv->mode == STMMAC_CHAIN_MODE) {
- if (priv->extend_desc)
- stmmac_mode_init(priv, rx_q->dma_erx,
- rx_q->dma_rx_phy,
- dma_conf->dma_rx_size, 1);
- else
- stmmac_mode_init(priv, rx_q->dma_rx,
- rx_q->dma_rx_phy,
- dma_conf->dma_rx_size, 0);
- }
-
return 0;
}
@@ -1918,9 +2203,6 @@ err_init_rx_buffers:
else
dma_free_rx_skbufs(priv, dma_conf, queue);
- rx_q->buf_alloc_num = 0;
- rx_q->xsk_pool = NULL;
-
queue--;
}
@@ -1947,37 +2229,32 @@ static int __init_dma_tx_desc_rings(struct stmmac_priv *priv,
"(%s) dma_tx_phy=0x%08x\n", __func__,
(u32)tx_q->dma_tx_phy);
- /* Setup the chained descriptor addresses */
- if (priv->mode == STMMAC_CHAIN_MODE) {
- if (priv->extend_desc)
- stmmac_mode_init(priv, tx_q->dma_etx,
- tx_q->dma_tx_phy,
- dma_conf->dma_tx_size, 1);
- else if (!(tx_q->tbs & STMMAC_TBS_AVAIL))
- stmmac_mode_init(priv, tx_q->dma_tx,
- tx_q->dma_tx_phy,
- dma_conf->dma_tx_size, 0);
- }
-
+ /* This must be performed on each SKB/XSK mode switch */
tx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue);
for (i = 0; i < dma_conf->dma_tx_size; i++) {
struct dma_desc *p;
+ dma_addr_t np;
- if (priv->extend_desc)
- p = &((tx_q->dma_etx + i)->basic);
- else if (tx_q->tbs & STMMAC_TBS_AVAIL)
- p = &((tx_q->dma_entx + i)->basic);
- else
- p = tx_q->dma_tx + i;
-
- stmmac_clear_desc(priv, p);
+ np = STMMAC_GET_ENTRY(i, dma_conf->dma_tx_size);
+ if (priv->extend_desc) {
+ p = &tx_q->dma_etx[i].basic;
+ np = tx_q->dma_tx_phy + np * sizeof(struct dma_extended_desc);
+ } else if (tx_q->tbs & STMMAC_TBS_AVAIL) {
+ p = &tx_q->dma_entx[i].basic;
+ np = tx_q->dma_tx_phy + np * sizeof(struct dma_edesc);
+ } else {
+ p = &tx_q->dma_tx[i];
+ np = tx_q->dma_tx_phy + np * sizeof(struct dma_desc);
+ }
tx_q->tx_skbuff_dma[i].buf = 0;
tx_q->tx_skbuff_dma[i].map_as_page = false;
tx_q->tx_skbuff_dma[i].len = 0;
tx_q->tx_skbuff_dma[i].last_segment = false;
tx_q->tx_skbuff[i] = NULL;
+
+ stmmac_release_tx_desc(priv, p, priv->mode, np, true);
}
return 0;
@@ -2033,6 +2310,9 @@ static int init_dma_desc_rings(struct net_device *dev,
* @priv: private structure
* @dma_conf: structure to take the dma data
* @queue: TX queue index
+ * Description: Revert what has been added to the descriptors in the
+ * framework of the stmmac_xmit()/stmmac_xmit_xdp_zc() methods work.
+ * Basically it means to unmap/free the respective buffers.
*/
static void dma_free_tx_skbufs(struct stmmac_priv *priv,
struct stmmac_dma_conf *dma_conf,
@@ -2046,11 +2326,10 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv,
for (i = 0; i < dma_conf->dma_tx_size; i++)
stmmac_free_tx_buffer(priv, dma_conf, queue, i);
- if (tx_q->xsk_pool && tx_q->xsk_frames_done) {
+ if (tx_q->xsk_pool && tx_q->xsk_frames_done)
xsk_tx_completed(tx_q->xsk_pool, tx_q->xsk_frames_done);
- tx_q->xsk_frames_done = 0;
- tx_q->xsk_pool = NULL;
- }
+
+ tx_q->xsk_frames_done = 0;
}
/**
@@ -2084,9 +2363,6 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
else
dma_free_rx_skbufs(priv, dma_conf, queue);
- rx_q->buf_alloc_num = 0;
- rx_q->xsk_pool = NULL;
-
/* Free DMA regions of consistent memory previously allocated */
if (!priv->extend_desc)
dma_free_coherent(priv->device, dma_conf->dma_rx_size *
@@ -2103,6 +2379,8 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
kfree(rx_q->buf_pool);
if (rx_q->page_pool)
page_pool_destroy(rx_q->page_pool);
+ if (rx_q->sec_page_pool)
+ page_pool_destroy(rx_q->sec_page_pool);
}
static void free_dma_rx_desc_resources(struct stmmac_priv *priv,
@@ -2185,26 +2463,31 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
unsigned int napi_id;
int ret;
- dma_buf_sz_pad = stmmac_rx_offset(priv) + dma_conf->dma_buf_sz +
- SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ dma_buf_sz_pad = SKB_HEAD_ALIGN(stmmac_rx_offset(priv) + dma_conf->dma_buf_sz);
num_pages = DIV_ROUND_UP(dma_buf_sz_pad, PAGE_SIZE);
rx_q->queue_index = queue;
rx_q->priv_data = priv;
- rx_q->napi_skb_frag_size = num_pages * PAGE_SIZE;
pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
pp_params.pool_size = dma_conf->dma_rx_size;
pp_params.order = order_base_2(num_pages);
pp_params.nid = dev_to_node(priv->device);
pp_params.dev = priv->device;
- pp_params.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
pp_params.offset = stmmac_rx_offset(priv);
- pp_params.max_len = dma_conf->dma_buf_sz;
-
- if (priv->sph_active) {
- pp_params.offset = 0;
- pp_params.max_len += stmmac_rx_offset(priv);
+ /* Allocate the entire page for a single frame in case of XDP and use
+ * a Headroom+DMA-buffer+Pad region per-frame otherwise to optimize the
+ * page memory usage. Note the later requires syncing the whole page
+ * after all the fragments released.
+ */
+ if (xdp_prog) {
+ rx_q->napi_skb_frag_size = num_pages * PAGE_SIZE;
+ pp_params.dma_dir = DMA_BIDIRECTIONAL;
+ pp_params.max_len = dma_conf->dma_buf_sz;
+ } else {
+ rx_q->napi_skb_frag_size = dma_buf_sz_pad;
+ pp_params.dma_dir = DMA_FROM_DEVICE;
+ pp_params.max_len = num_pages * PAGE_SIZE - pp_params.offset;
}
rx_q->page_pool = page_pool_create(&pp_params);
@@ -2214,6 +2497,20 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
return ret;
}
+ if (priv->sph_active) {
+ num_pages = DIV_ROUND_UP(dma_conf->dma_buf_sz, PAGE_SIZE);
+ pp_params.order = order_base_2(num_pages);
+ pp_params.offset = 0;
+ pp_params.max_len = PAGE_SIZE << pp_params.order;
+
+ rx_q->sec_page_pool = page_pool_create(&pp_params);
+ if (IS_ERR(rx_q->sec_page_pool)) {
+ ret = PTR_ERR(rx_q->sec_page_pool);
+ rx_q->sec_page_pool = NULL;
+ return ret;
+ }
+ }
+
rx_q->buf_pool = kzalloc_objs(*rx_q->buf_pool, dma_conf->dma_rx_size);
if (!rx_q->buf_pool)
return -ENOMEM;
@@ -2363,13 +2660,15 @@ err_dma:
static int alloc_dma_desc_resources(struct stmmac_priv *priv,
struct stmmac_dma_conf *dma_conf)
{
- /* RX Allocation */
- int ret = alloc_dma_rx_desc_resources(priv, dma_conf);
+ int ret;
+ ret = alloc_dma_rx_desc_resources(priv, dma_conf);
if (ret)
return ret;
ret = alloc_dma_tx_desc_resources(priv, dma_conf);
+ if (ret)
+ free_dma_rx_desc_resources(priv, dma_conf);
return ret;
}
@@ -2525,27 +2824,16 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
{
u32 rx_channels_count = priv->plat->rx_queues_to_use;
u32 tx_channels_count = priv->plat->tx_queues_to_use;
- int rxfifosz = priv->plat->rx_fifo_size;
- int txfifosz = priv->plat->tx_fifo_size;
+ int txfifosz = stmmac_get_txfifosz(priv);
+ int rxfifosz = stmmac_get_rxfifosz(priv);
u32 txmode = 0;
u32 rxmode = 0;
u32 chan = 0;
u8 qmode = 0;
- if (rxfifosz == 0)
- rxfifosz = priv->dma_cap.rx_fifo_size;
- if (txfifosz == 0)
- txfifosz = priv->dma_cap.tx_fifo_size;
-
- /* Split up the shared Tx/Rx FIFO memory on DW QoS Eth and DW XGMAC */
- if (dwmac_is_xmac(priv->plat->core_type)) {
- rxfifosz /= rx_channels_count;
- txfifosz /= tx_channels_count;
- }
-
if (priv->plat->force_thresh_dma_mode) {
- txmode = tc;
- rxmode = tc;
+ txmode = ttc;
+ rxmode = rtc;
} else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) {
/*
* In case of GMAC, SF mode can be enabled
@@ -2556,9 +2844,8 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
*/
txmode = SF_DMA_MODE;
rxmode = SF_DMA_MODE;
- priv->xstats.threshold = SF_DMA_MODE;
} else {
- txmode = tc;
+ txmode = ttc;
rxmode = SF_DMA_MODE;
}
@@ -2568,9 +2855,10 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
u32 buf_size;
qmode = priv->plat->rx_queues_cfg[chan].mode_to_use;
+ rx_q->rtc = rxmode;
- stmmac_dma_rx_mode(priv, priv->ioaddr, rxmode, chan,
- rxfifosz, qmode);
+ stmmac_dma_rx_mode(priv, priv->ioaddr, rxmode, chan, rxfifosz,
+ qmode, priv->dev->features & NETIF_F_RXALL);
if (rx_q->xsk_pool) {
buf_size = xsk_pool_get_rx_frame_size(rx_q->xsk_pool);
@@ -2585,7 +2873,10 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
}
for (chan = 0; chan < tx_channels_count; chan++) {
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+
qmode = priv->plat->tx_queues_cfg[chan].mode_to_use;
+ tx_q->ttc = txmode;
stmmac_dma_tx_mode(priv, priv->ioaddr, txmode, chan,
txfifosz, qmode);
@@ -2709,7 +3000,6 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
tx_q->tx_skbuff_dma[entry].map_as_page = false;
tx_q->tx_skbuff_dma[entry].len = xdp_desc.len;
tx_q->tx_skbuff_dma[entry].last_segment = true;
- tx_q->tx_skbuff_dma[entry].is_jumbo = false;
stmmac_set_desc_addr(priv, tx_desc, dma_addr);
@@ -2739,8 +3029,6 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
csum, priv->mode, true, true,
xdp_desc.len);
- stmmac_enable_dma_transmission(priv, priv->ioaddr, queue);
-
xsk_tx_metadata_to_compl(meta,
&tx_q->tx_skbuff_dma[entry].xsk_meta);
@@ -2764,21 +3052,6 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
return !!budget && work_done;
}
-static void stmmac_bump_dma_threshold(struct stmmac_priv *priv, u32 chan)
-{
- if (unlikely(priv->xstats.threshold != SF_DMA_MODE) && tc <= 256) {
- tc += 64;
-
- if (priv->plat->force_thresh_dma_mode)
- stmmac_set_dma_operation_mode(priv, tc, tc, chan);
- else
- stmmac_set_dma_operation_mode(priv, tc, SF_DMA_MODE,
- chan);
-
- priv->xstats.threshold = tc;
- }
-}
-
/**
* stmmac_tx_clean - to manage the transmission completion
* @priv: driver private structure
@@ -2797,8 +3070,11 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
unsigned int bytes_compl = 0, pkts_compl = 0;
unsigned int entry, xmits = 0, count = 0;
u32 tx_packets = 0, tx_errors = 0;
+ struct netdev_queue *nq;
+
+ nq = netdev_get_tx_queue(priv->dev, queue);
- __netif_tx_lock_bh(netdev_get_tx_queue(priv->dev, queue));
+ __netif_tx_lock_bh(nq);
tx_q->xsk_frames_done = 0;
@@ -2809,6 +3085,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
struct xdp_frame *xdpf;
struct sk_buff *skb;
struct dma_desc *p;
+ dma_addr_t np;
int status;
if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_XDP_TX ||
@@ -2823,14 +3100,19 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
skb = NULL;
}
- if (priv->extend_desc)
- p = (struct dma_desc *)(tx_q->dma_etx + entry);
- else if (tx_q->tbs & STMMAC_TBS_AVAIL)
+ np = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ if (priv->extend_desc) {
+ p = &tx_q->dma_etx[entry].basic;
+ np = tx_q->dma_tx_phy + np * sizeof(struct dma_extended_desc);
+ } else if (tx_q->tbs & STMMAC_TBS_AVAIL) {
p = &tx_q->dma_entx[entry].basic;
- else
- p = tx_q->dma_tx + entry;
+ np = tx_q->dma_tx_phy + np * sizeof(struct dma_edesc);
+ } else {
+ p = &tx_q->dma_tx[entry];
+ np = tx_q->dma_tx_phy + np * sizeof(struct dma_desc);
+ }
- status = stmmac_tx_status(priv, &priv->xstats, p, priv->ioaddr);
+ status = stmmac_tx_status(priv, &priv->xstats, p);
/* Check if the descriptor is owned by the DMA */
if (unlikely(status & tx_dma_own))
break;
@@ -2845,13 +3127,11 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
/* Just consider the last segment and ...*/
if (likely(!(status & tx_not_ls))) {
/* ... verify the status error condition */
- if (unlikely(status & tx_err)) {
+ if (unlikely(status & tx_err))
tx_errors++;
- if (unlikely(status & tx_err_bump_tc))
- stmmac_bump_dma_threshold(priv, queue);
- } else {
+ else
tx_packets++;
- }
+
if (skb) {
stmmac_get_tx_hwtstamp(priv, p, skb);
} else if (tx_q->xsk_pool &&
@@ -2884,10 +3164,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
tx_q->tx_skbuff_dma[entry].map_as_page = false;
}
- stmmac_clean_desc3(priv, tx_q, p);
-
tx_q->tx_skbuff_dma[entry].last_segment = false;
- tx_q->tx_skbuff_dma[entry].is_jumbo = false;
if (xdpf &&
tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_XDP_TX) {
@@ -2913,22 +3190,20 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
}
}
- stmmac_release_tx_desc(priv, p, priv->mode);
+ stmmac_release_tx_desc(priv, p, priv->mode, np,
+ priv->hwts_tx_en);
entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
}
tx_q->dirty_tx = entry;
- netdev_tx_completed_queue(netdev_get_tx_queue(priv->dev, queue),
- pkts_compl, bytes_compl);
+ netdev_tx_completed_queue(nq, pkts_compl, bytes_compl);
- if (unlikely(netif_tx_queue_stopped(netdev_get_tx_queue(priv->dev,
- queue))) &&
+ if (unlikely(netif_tx_queue_stopped(nq)) &&
stmmac_tx_avail(priv, queue) > STMMAC_TX_THRESH(priv)) {
-
netif_dbg(priv, tx_done, priv->dev,
"%s: restart transmit\n", __func__);
- netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, queue));
+ netif_tx_wake_queue(nq);
}
if (tx_q->xsk_pool) {
@@ -2968,70 +3243,12 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
priv->xstats.tx_errors += tx_errors;
- __netif_tx_unlock_bh(netdev_get_tx_queue(priv->dev, queue));
+ __netif_tx_unlock_bh(nq);
/* Combine decisions from TX clean and XSK TX */
return max(count, xmits);
}
-/**
- * stmmac_tx_err - to manage the tx error
- * @priv: driver private structure
- * @chan: channel index
- * Description: it cleans the descriptors and restarts the transmission
- * in case of transmission errors.
- */
-static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan)
-{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
-
- netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan));
-
- stmmac_stop_tx_dma(priv, chan);
- dma_free_tx_skbufs(priv, &priv->dma_conf, chan);
- stmmac_clear_tx_descriptors(priv, &priv->dma_conf, chan);
- stmmac_reset_tx_queue(priv, chan);
- stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
- tx_q->dma_tx_phy, chan);
- stmmac_start_tx_dma(priv, chan);
-
- priv->xstats.tx_errors++;
- netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, chan));
-}
-
-/**
- * stmmac_set_dma_operation_mode - Set DMA operation mode by channel
- * @priv: driver private structure
- * @txmode: TX operating mode
- * @rxmode: RX operating mode
- * @chan: channel index
- * Description: it is used for configuring of the DMA operation mode in
- * runtime in order to program the tx/rx DMA thresholds or Store-And-Forward
- * mode.
- */
-static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
- u32 rxmode, u32 chan)
-{
- u8 rxqmode = priv->plat->rx_queues_cfg[chan].mode_to_use;
- u8 txqmode = priv->plat->tx_queues_cfg[chan].mode_to_use;
- u32 rx_channels_count = priv->plat->rx_queues_to_use;
- u32 tx_channels_count = priv->plat->tx_queues_to_use;
- int rxfifosz = priv->plat->rx_fifo_size;
- int txfifosz = priv->plat->tx_fifo_size;
-
- if (rxfifosz == 0)
- rxfifosz = priv->dma_cap.rx_fifo_size;
- if (txfifosz == 0)
- txfifosz = priv->dma_cap.tx_fifo_size;
-
- /* Adjust for real per queue fifo size */
- rxfifosz /= rx_channels_count;
- txfifosz /= tx_channels_count;
-
- stmmac_dma_rx_mode(priv, priv->ioaddr, rxmode, chan, rxfifosz, rxqmode);
- stmmac_dma_tx_mode(priv, priv->ioaddr, txmode, chan, txfifosz, txqmode);
-}
-
static bool stmmac_safety_feat_interrupt(struct stmmac_priv *priv)
{
int ret;
@@ -3039,7 +3256,7 @@ static bool stmmac_safety_feat_interrupt(struct stmmac_priv *priv)
ret = stmmac_safety_feat_irq_status(priv, priv->dev,
priv->ioaddr, priv->dma_cap.asp, &priv->sstats);
if (ret && (ret != -EINVAL)) {
- stmmac_global_err(priv);
+ stmmac_handle_fatal_err(priv);
return true;
}
@@ -3092,12 +3309,12 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)
{
u32 tx_channel_count = priv->plat->tx_queues_to_use;
u32 rx_channel_count = priv->plat->rx_queues_to_use;
- u32 channels_to_check = tx_channel_count > rx_channel_count ?
- tx_channel_count : rx_channel_count;
- u32 chan;
int status[MAX_T(u32, MTL_MAX_TX_QUEUES, MTL_MAX_RX_QUEUES)];
+ u32 channels_to_check;
+ u32 chan;
/* Make sure we never check beyond our status buffer. */
+ channels_to_check = max(tx_channel_count, rx_channel_count);
if (WARN_ON_ONCE(channels_to_check > ARRAY_SIZE(status)))
channels_to_check = ARRAY_SIZE(status);
@@ -3105,13 +3322,19 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)
status[chan] = stmmac_napi_check(priv, chan,
DMA_DIR_RXTX);
- for (chan = 0; chan < tx_channel_count; chan++) {
- if (unlikely(status[chan] & tx_hard_error_bump_tc)) {
- /* Try to bump up the dma threshold on this failure */
- stmmac_bump_dma_threshold(priv, chan);
- } else if (unlikely(status[chan] == tx_hard_error)) {
- stmmac_tx_err(priv, chan);
- }
+ for (chan = 0; chan < channels_to_check; chan++) {
+ if (unlikely(status[chan] & io_fatal_error))
+ stmmac_handle_fatal_err(priv);
+ if (unlikely(status[chan] & rx_hard_error))
+ stmmac_handle_rx_err(priv, chan);
+ if (unlikely(status[chan] & tx_hard_error))
+ stmmac_handle_tx_err(priv, chan);
+ if (unlikely(status[chan] & rx_ovf_error))
+ stmmac_handle_rx_ovf_error(priv, chan);
+ if (unlikely(status[chan] & tx_unf_error))
+ stmmac_handle_tx_unf_error(priv, chan);
+ if (unlikely(status[chan] & tx_soft_stop))
+ stmmac_handle_tx_stop(priv, chan);
}
}
@@ -3247,14 +3470,6 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
u32 chan = 0;
int ret = 0;
- if (!priv->plat->dma_cfg || !priv->plat->dma_cfg->pbl) {
- netdev_err(priv->dev, "Invalid DMA configuration\n");
- return -EINVAL;
- }
-
- if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE))
- priv->plat->dma_cfg->atds = 1;
-
ret = stmmac_prereset_configure(priv);
if (ret)
return ret;
@@ -3526,17 +3741,8 @@ static void stmmac_mac_config_rx_queues_routing(struct stmmac_priv *priv)
static void stmmac_mac_config_rss(struct stmmac_priv *priv)
{
- if (!priv->dma_cap.rssen || !priv->plat->rss_en) {
- priv->rss.enable = false;
- return;
- }
-
- if (priv->dev->features & NETIF_F_RXHASH)
- priv->rss.enable = true;
- else
- priv->rss.enable = false;
-
stmmac_rss_configure(priv, priv->hw, &priv->rss,
+ priv->dev->features & NETIF_F_RXHASH,
priv->plat->rx_queues_to_use);
}
@@ -3618,7 +3824,6 @@ static int stmmac_hw_setup(struct net_device *dev)
struct stmmac_priv *priv = netdev_priv(dev);
u32 rx_cnt = priv->plat->rx_queues_to_use;
u32 tx_cnt = priv->plat->tx_queues_to_use;
- bool sph_en;
u32 chan;
int ret;
@@ -3656,12 +3861,15 @@ static int stmmac_hw_setup(struct net_device *dev)
/* Initialize Safety Features */
stmmac_safety_feat_configuration(priv);
- ret = stmmac_rx_ipc(priv, priv->hw);
- if (!ret) {
- netdev_warn(priv->dev, "RX IPC Checksum Offload disabled\n");
- priv->plat->rx_coe = STMMAC_RX_COE_NONE;
- priv->hw->rx_csum = 0;
- }
+ /* Initialize Auto Pad/FCS Stripping */
+ stmmac_rx_fcs(priv, priv->hw, dev->features & NETIF_F_RXFCS);
+
+ /* Initialize Rx Checksum Offload Engine */
+ stmmac_rx_ipc(priv, priv->hw, dev->features & NETIF_F_RXCSUM);
+
+ /* Initialize ARP offload */
+ stmmac_set_arp_offload(priv, priv->hw, priv->arp_addr != -1,
+ ntohl(priv->arp_addr));
/* Enable the MAC Rx/Tx */
stmmac_mac_set(priv, priv->ioaddr, true);
@@ -3686,35 +3894,19 @@ static int stmmac_hw_setup(struct net_device *dev)
/* set TX and RX rings length */
stmmac_set_rings_length(priv);
- /* Enable TSO */
- if (priv->tso) {
- for (chan = 0; chan < tx_cnt; chan++) {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
-
- /* TSO and TBS cannot co-exist */
- if (tx_q->tbs & STMMAC_TBS_AVAIL)
- continue;
-
- stmmac_enable_tso(priv, priv->ioaddr, 1, chan);
- }
- }
-
/* Enable Split Header */
- sph_en = (priv->hw->rx_csum > 0) && priv->sph_active;
for (chan = 0; chan < rx_cnt; chan++)
- stmmac_enable_sph(priv, priv->ioaddr, sph_en, chan);
+ stmmac_enable_sph(priv, priv->ioaddr, priv->sph_active, chan);
-
- /* VLAN Tag Insertion */
- if (priv->dma_cap.vlins)
- stmmac_enable_vlan(priv, priv->hw, STMMAC_VLAN_INSERT);
-
- /* TBS */
+ /* Enable TSO and/or TBS */
for (chan = 0; chan < tx_cnt; chan++) {
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
- int enable = tx_q->tbs & STMMAC_TBS_AVAIL;
- stmmac_enable_tbs(priv, priv->ioaddr, enable, chan);
+ /* TSO and TBS cannot co-exist */
+ if (tx_q->tbs & STMMAC_TBS_AVAIL)
+ stmmac_enable_tbs(priv, priv->ioaddr, true, chan);
+ else if (dev->features & (NETIF_F_TSO | NETIF_F_TSO6))
+ stmmac_enable_tso(priv, priv->ioaddr, true, chan);
}
/* Configure real RX and TX queues */
@@ -3725,7 +3917,12 @@ static int stmmac_hw_setup(struct net_device *dev)
stmmac_start_all_dma(priv);
phylink_rx_clk_stop_block(priv->phylink);
- stmmac_set_hw_vlan_mode(priv, priv->hw);
+ stmmac_set_hw_vlan_mode(priv, priv->hw,
+ dev->features & (NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_HW_VLAN_STAG_RX),
+ dev->features & NETIF_F_HW_VLAN_CTAG_FILTER,
+ dev->features & NETIF_F_HW_VLAN_STAG_FILTER,
+ dev->features & NETIF_F_HW_VLAN_STAG_TX);
phylink_rx_clk_stop_unblock(priv->phylink);
return 0;
@@ -3739,7 +3936,7 @@ static void stmmac_free_irq(struct net_device *dev,
switch (irq_err) {
case REQ_IRQ_ERR_ALL:
- irq_idx = priv->plat->tx_queues_to_use;
+ irq_idx = priv->plat->dma_cfg->multi_msi_en ? priv->plat->tx_queues_to_use : 0;
fallthrough;
case REQ_IRQ_ERR_TX:
for (j = irq_idx - 1; j >= 0; j--) {
@@ -3748,7 +3945,7 @@ static void stmmac_free_irq(struct net_device *dev,
free_irq(priv->tx_irq[j], &priv->dma_conf.tx_queue[j]);
}
}
- irq_idx = priv->plat->rx_queues_to_use;
+ irq_idx = priv->plat->dma_cfg->multi_msi_en ? priv->plat->rx_queues_to_use : 0;
fallthrough;
case REQ_IRQ_ERR_RX:
for (j = irq_idx - 1; j >= 0; j--) {
@@ -4011,7 +4208,7 @@ static struct stmmac_dma_conf *
stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
{
struct stmmac_dma_conf *dma_conf;
- int chan, bfsize, ret;
+ int chan, ret;
dma_conf = kzalloc_obj(*dma_conf);
if (!dma_conf) {
@@ -4020,26 +4217,12 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
return ERR_PTR(-ENOMEM);
}
- /* Returns 0 or BUF_SIZE_16KiB if mtu > 8KiB and dwmac4 or ring mode */
- bfsize = stmmac_set_16kib_bfsize(priv, mtu);
- if (bfsize < 0)
- bfsize = 0;
-
- if (bfsize < BUF_SIZE_16KiB)
- bfsize = stmmac_set_bfsize(mtu);
-
- dma_conf->dma_buf_sz = bfsize;
- /* Chose the tx/rx size from the already defined one in the
- * priv struct. (if defined)
- */
+ dma_conf->dma_buf_sz = stmmac_get_bfsize(priv, mtu);
+ dma_conf->tx_queue_max = priv->dma_conf.tx_queue_max;
dma_conf->dma_tx_size = priv->dma_conf.dma_tx_size;
+ dma_conf->rx_queue_max = priv->dma_conf.rx_queue_max;
dma_conf->dma_rx_size = priv->dma_conf.dma_rx_size;
- if (!dma_conf->dma_tx_size)
- dma_conf->dma_tx_size = DMA_DEFAULT_TX_SIZE;
- if (!dma_conf->dma_rx_size)
- dma_conf->dma_rx_size = DMA_DEFAULT_RX_SIZE;
-
/* Earlier check for TBS */
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) {
struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[chan];
@@ -4089,6 +4272,8 @@ static int __stmmac_open(struct net_device *dev,
u32 chan;
int ret;
+ priv->rx_copybreak = STMMAC_RX_COPYBREAK;
+
for (int i = 0; i < MTL_MAX_TX_QUEUES; i++)
if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_EN)
dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs;
@@ -4106,14 +4291,15 @@ static int __stmmac_open(struct net_device *dev,
stmmac_init_coalesce(priv);
- phylink_start(priv->phylink);
-
- stmmac_vlan_restore(priv);
-
+ /* Request IRQs before any PHY/DMA/etc IRQs are activated to prevent
+ * the spurious IRQs getting by the handlers of the shared MAC IRQ.
+ */
ret = stmmac_request_irq(dev);
if (ret)
goto irq_error;
+ phylink_start(priv->phylink);
+
stmmac_enable_all_queues(priv);
netif_tx_start_all_queues(priv->dev);
stmmac_enable_all_dma_irq(priv);
@@ -4121,8 +4307,6 @@ static int __stmmac_open(struct net_device *dev,
return 0;
irq_error:
- phylink_stop(priv->phylink);
-
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
@@ -4249,17 +4433,119 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
tag = skb_vlan_tag_get(skb);
+ /* The previously set outer VLAN Tag is stored in the DMA-engine.
+ * Skip setting it again if it is the same is in the previous xfers.
+ * Note TPID is selected by the activated Tx CTAG/STAG feature.
+ */
+ if (tx_q->tci == tag)
+ return true;
+
if (tx_q->tbs & STMMAC_TBS_AVAIL)
p = &tx_q->dma_entx[tx_q->cur_tx].basic;
else
p = &tx_q->dma_tx[tx_q->cur_tx];
- if (stmmac_set_desc_vlan_tag(priv, p, tag, 0x0, 0x0))
- return false;
+ if (!stmmac_set_desc_vlan_tag(priv, p, tag, 0x0, 0x0)) {
+ stmmac_set_tx_owner(priv, p);
+ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx,
+ priv->dma_conf.dma_tx_size);
+ tx_q->tci = tag;
+ return true;
+ } else if (!stmmac_set_core_vlan_tag(priv, priv->hw, tx_q->queue_index, tag)) {
+ tx_q->tci = tag;
+ return true;
+ }
- stmmac_set_tx_owner(priv, p);
- tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
- return true;
+ skb = __vlan_hwaccel_push_inside(skb);
+ if (IS_ERR(skb))
+ netdev_err(priv->dev, "failed to push VLAN inside, skipping\n");
+
+ return false;
+}
+
+/**
+ * stmmac_csum_insert - Insert CSUM into the frame
+ * @priv: driver private structure
+ * @skb : the socket buffer
+ * @queue: TX queue index
+ * Description :
+ * This function will either insert all the uninitialized checksum into the
+ * frame or return a request for the Tx COE to do that if it's supported.
+ *
+ * Return: zero if CSUM was inserted into the frame, one if Tx COE is supposed
+ * to do that, negative error otherwise.
+ */
+static int stmmac_csum_insert(struct stmmac_priv *priv, struct sk_buff *skb,
+ int queue)
+{
+ struct ipv6_opt_hdr *hp;
+ __be16 l3proto;
+ int depth = 0;
+ u8 l4proto;
+
+ /* No checksum required */
+ if (!(skb->ip_summed == CHECKSUM_PARTIAL))
+ return 0;
+
+ /* Tx COE might be available on a limited set of the queues */
+ if (priv->plat->tx_queues_cfg[queue].coe_unsupported)
+ goto sw_checksum;
+
+ /* Tx COE supports Ethernet II frames only (EtherType >= 0x0600) */
+ l3proto = eth_header_parse_protocol(skb);
+ if (!eth_proto_is_802_3(l3proto))
+ goto sw_checksum;
+
+ /* Tx COE supports C-/S-VLAN tagged IP-packets (excl. hwaccel tag?) */
+ l3proto = __vlan_get_protocol(skb, l3proto, &depth);
+ if (depth > (ETH_HLEN + VLAN_HLEN))
+ goto sw_checksum;
+
+ /* Tx COE supports IPv4/IPv6 types with TCP/UDP/ICMP protocols */
+ if ((priv->dev->features & NETIF_F_IP_CSUM) &&
+ (l3proto == htons(ETH_P_IP))) {
+ l4proto = ip_hdr(skb)->protocol;
+
+ switch(l4proto) {
+ case IPPROTO_ICMP:
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ return 1;
+ default:
+ goto sw_checksum;
+ }
+ } else if ((priv->dev->features & NETIF_F_IPV6_CSUM) &&
+ (l3proto == htons(ETH_P_IPV6))) {
+ l4proto = ipv6_hdr(skb)->nexthdr;
+ depth = sizeof(struct ipv6hdr);
+
+ /* Tx COE supports single Hop-by-Hop and multiple Dest headers */
+ do {
+ switch(l4proto) {
+ case NEXTHDR_TCP:
+ case NEXTHDR_UDP:
+ case NEXTHDR_ICMP:
+ return 1;
+ case NEXTHDR_HOP:
+ if (depth != sizeof(struct ipv6hdr))
+ goto sw_checksum;
+ break;
+ case NEXTHDR_DEST:
+ break;
+ default:
+ goto sw_checksum;
+ }
+
+ hp = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
+ depth);
+ l4proto = hp->nexthdr;
+ depth += ipv6_optlen(hp);
+ } while (l4proto != NEXTHDR_NONE);
+ }
+
+sw_checksum:
+ return unlikely(skb_csum_is_sctp(skb)) ?
+ skb_crc32c_csum_help(skb) : skb_checksum_help(skb);
}
/**
@@ -4327,8 +4613,16 @@ static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue)
*/
wmb();
+ /* Update Tx desc tail pointer and issue the Tx poll demand cmd
+ * (specific for the most modern IP-cores: DW QoS Ether, XGMAC, etc).
+ */
tx_q->tx_tail_addr = tx_q->dma_tx_phy + (tx_q->cur_tx * desc_size);
stmmac_set_tx_tail_ptr(priv, priv->ioaddr, tx_q->tx_tail_addr, queue);
+
+ /* Just issue the Tx poll demand cmd (specific to the older DW
+ * MAC100/GMAC IP-cores).
+ */
+ stmmac_enable_dma_transmission(priv, priv->ioaddr, queue);
}
/**
@@ -4370,33 +4664,23 @@ static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue)
*/
static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ bool has_vlan, set_ic, is_last_segment, drop = false;
struct dma_desc *desc, *first, *mss_desc = NULL;
struct stmmac_priv *priv = netdev_priv(dev);
unsigned int first_entry, tx_packets;
struct stmmac_txq_stats *txq_stats;
+ netdev_tx_t ret = NETDEV_TX_OK;
struct stmmac_tx_queue *tx_q;
- bool set_ic, is_last_segment;
+ struct netdev_queue *nq;
u32 pay_len, mss, queue;
int i, first_tx, nfrags;
u8 proto_hdr_len, hdr;
dma_addr_t des;
- /* Always insert VLAN tag to SKB payload for TSO frames.
- *
- * Never insert VLAN tag by HW, since segments split by
- * TSO engine will be un-tagged by mistake.
- */
- if (skb_vlan_tag_present(skb)) {
- skb = __vlan_hwaccel_push_inside(skb);
- if (unlikely(!skb)) {
- priv->xstats.tx_dropped++;
- return NETDEV_TX_OK;
- }
- }
-
nfrags = skb_shinfo(skb)->nr_frags;
queue = skb_get_queue_mapping(skb);
+ nq = netdev_get_tx_queue(dev, queue);
tx_q = &priv->dma_conf.tx_queue[queue];
txq_stats = &priv->xstats.txq_stats[queue];
first_tx = tx_q->cur_tx;
@@ -4413,15 +4697,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
/* Desc availability based on threshold should be enough safe */
if (unlikely(stmmac_tx_avail(priv, queue) <
(((skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1)))) {
- if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) {
- netif_tx_stop_queue(netdev_get_tx_queue(priv->dev,
- queue));
- /* This is a hard error, log it. */
- netdev_err(priv->dev,
- "%s: Tx Ring full when queue awake\n",
- __func__);
- }
- return NETDEV_TX_BUSY;
+ netif_tx_stop_queue(nq);
+ ret = NETDEV_TX_BUSY;
+ goto flush_ring;
}
pay_len = skb_headlen(skb) - proto_hdr_len; /* no frags */
@@ -4449,6 +4727,8 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
skb->data_len);
}
+ has_vlan = stmmac_vlan_insert(priv, skb, tx_q);
+
first_entry = tx_q->cur_tx;
WARN_ON(tx_q->tx_skbuff[first_entry]);
@@ -4458,11 +4738,17 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
desc = &tx_q->dma_tx[first_entry];
first = desc;
+ if (has_vlan)
+ stmmac_set_desc_vlan(priv, first, STMMAC_VLAN_INSERT);
+
/* first descriptor: fill Headers on Buf1 */
des = dma_map_single(priv->device, skb->data, skb_headlen(skb),
DMA_TO_DEVICE);
- if (dma_mapping_error(priv->device, des))
- goto dma_map_err;
+ if (dma_mapping_error(priv->device, des)) {
+ netdev_err(priv->dev, "First Tx DMA-desc map failed\n");
+ drop = true;
+ goto flush_ring;
+ }
stmmac_set_desc_addr(priv, first, des);
stmmac_tso_allocator(priv, des + proto_hdr_len, pay_len,
@@ -4492,8 +4778,11 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
des = skb_frag_dma_map(priv->device, frag, 0,
skb_frag_size(frag),
DMA_TO_DEVICE);
- if (dma_mapping_error(priv->device, des))
- goto dma_map_err;
+ if (dma_mapping_error(priv->device, des)) {
+ netdev_err(priv->dev, "Frag Tx DMA-desc map failed\n");
+ drop = true;
+ goto flush_ring;
+ }
stmmac_tso_allocator(priv, des, skb_frag_size(frag),
(i == nfrags - 1), queue);
@@ -4546,7 +4835,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) {
netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n",
__func__);
- netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue));
+ netif_tx_stop_queue(nq);
}
u64_stats_update_begin(&txq_stats->q_syncp);
@@ -4597,41 +4886,71 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
print_pkt(skb->data, skb_headlen(skb));
}
- netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len);
skb_tx_timestamp(skb);
- stmmac_flush_tx_descriptors(priv, queue);
- stmmac_tx_timer_arm(priv, queue);
-
- return NETDEV_TX_OK;
+ /* Push out the packets after the particular amount of SKBs handled */
+ if (__netdev_tx_sent_queue(nq, skb->len, netdev_xmit_more())) {
+flush_ring:
+ if (drop) {
+ dev_kfree_skb(skb);
+ priv->xstats.tx_dropped++;
+ }
+ stmmac_flush_tx_descriptors(priv, queue);
+ stmmac_tx_timer_arm(priv, queue);
+ }
-dma_map_err:
- dev_err(priv->device, "Tx dma map failed\n");
- dev_kfree_skb(skb);
- priv->xstats.tx_dropped++;
- return NETDEV_TX_OK;
+ return ret;
}
/**
- * stmmac_has_ip_ethertype() - Check if packet has IP ethertype
- * @skb: socket buffer to check
- *
- * Check if a packet has an ethertype that will trigger the IP header checks
- * and IP/TCP checksum engine of the stmmac core.
- *
- * Return: true if the ethertype can trigger the checksum engine, false
- * otherwise
+ * stmmac_tx_allocator - Tx DMA descriptors allocator for cross-descriptor frames
+ * @priv: driver private structure
+ * @des: buffer start address
+ * @total_len: total length to fill in descriptors
+ * @last_segment: condition for the last descriptor
+ * @queue: TX queue index
+ * Description:
+ * This function fills descriptor and request new descriptors according to
+ * buffer length to fill. The method specifically dedicated for Jumbo-frames
+ * which don't fit to a single descriptor.
*/
-static bool stmmac_has_ip_ethertype(struct sk_buff *skb)
+static void stmmac_tx_allocator(struct stmmac_priv *priv, dma_addr_t des,
+ int total_len, bool last_segment, u32 queue)
{
- int depth = 0;
- __be16 proto;
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ u32 buff_size, buff_max;
+ struct dma_desc *desc;
+ int tmp_len;
+
+ buff_max = stmmac_get_tx_len(priv, priv->mode);
+ tmp_len = total_len;
+
+ while (tmp_len > 0) {
+ dma_addr_t curr_addr;
+
+ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx,
+ priv->dma_conf.dma_tx_size);
+ WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
+
+ if (likely(priv->extend_desc))
+ desc = &tx_q->dma_etx[tx_q->cur_tx].basic;
+ else if (tx_q->tbs & STMMAC_TBS_AVAIL)
+ desc = &tx_q->dma_entx[tx_q->cur_tx].basic;
+ else
+ desc = &tx_q->dma_tx[tx_q->cur_tx];
+
+ curr_addr = des + (total_len - tmp_len);
+ stmmac_set_desc_addr(priv, desc, curr_addr);
- proto = __vlan_get_protocol(skb, eth_header_parse_protocol(skb),
- &depth);
+ buff_size = tmp_len >= buff_max ? buff_max : tmp_len;
- return (depth <= ETH_HLEN) &&
- (proto == htons(ETH_P_IP) || proto == htons(ETH_P_IPV6));
+ /* Prepare _not_ first descriptor and set it' own bit */
+ stmmac_prepare_tx_desc(priv, desc, false, buff_size, false,
+ priv->mode, true,
+ last_segment && (tmp_len <= buff_max), 0);
+
+ tmp_len -= buff_max;
+ }
}
/**
@@ -4644,22 +4963,23 @@ static bool stmmac_has_ip_ethertype(struct sk_buff *skb)
*/
static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
{
- bool enh_desc, has_vlan, set_ic, is_jumbo = false;
+ unsigned int first_entry, tx_packets, nopaged_len, descbuf_len;
struct stmmac_priv *priv = netdev_priv(dev);
- unsigned int nopaged_len = skb_headlen(skb);
u32 queue = skb_get_queue_mapping(skb);
int nfrags = skb_shinfo(skb)->nr_frags;
- unsigned int first_entry, tx_packets;
int gso = skb_shinfo(skb)->gso_type;
+ bool has_vlan, set_ic, drop = false;
struct stmmac_txq_stats *txq_stats;
struct dma_edesc *tbs_desc = NULL;
+ int i, first_tx, csum_insertion;
+ netdev_tx_t ret = NETDEV_TX_OK;
struct dma_desc *desc, *first;
struct stmmac_tx_queue *tx_q;
- int i, csum_insertion = 0;
- int entry, first_tx;
+ struct netdev_queue *nq;
dma_addr_t des;
u32 sdu_len;
+ nq = netdev_get_tx_queue(dev, queue);
tx_q = &priv->dma_conf.tx_queue[queue];
txq_stats = &priv->xstats.txq_stats[queue];
first_tx = tx_q->cur_tx;
@@ -4667,8 +4987,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en)
stmmac_stop_sw_lpi(priv);
- /* Manage oversized TCP frames for GMAC4 device */
- if (skb_is_gso(skb) && priv->tso) {
+ /* Manage oversized TCP frames for TSO-capable device */
+ if (skb_is_gso(skb)) {
if (gso & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
return stmmac_tso_xmit(skb, dev);
if (priv->plat->core_type == DWMAC_CORE_GMAC4 &&
@@ -4684,112 +5004,98 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
sdu_len += VLAN_HLEN;
if (sdu_len > priv->est->max_sdu[queue]) {
priv->xstats.max_sdu_txq_drop[queue]++;
- goto max_sdu_err;
+ drop = true;
+ goto flush_ring;
}
}
if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) {
- if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) {
- netif_tx_stop_queue(netdev_get_tx_queue(priv->dev,
- queue));
- /* This is a hard error, log it. */
- netdev_err(priv->dev,
- "%s: Tx Ring full when queue awake\n",
- __func__);
- }
- return NETDEV_TX_BUSY;
+ netif_tx_stop_queue(nq);
+ ret = NETDEV_TX_BUSY;
+ goto flush_ring;
}
/* Check if VLAN can be inserted by HW */
has_vlan = stmmac_vlan_insert(priv, skb, tx_q);
- entry = tx_q->cur_tx;
- first_entry = entry;
+ first_entry = tx_q->cur_tx;
WARN_ON(tx_q->tx_skbuff[first_entry]);
- csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);
- /* DWMAC IPs can be synthesized to support tx coe only for a few tx
- * queues. In that case, checksum offloading for those queues that don't
- * support tx coe needs to fallback to software checksum calculation.
- *
- * Packets that won't trigger the COE e.g. most DSA-tagged packets will
- * also have to be checksummed in software.
- */
- if (csum_insertion &&
- (priv->plat->tx_queues_cfg[queue].coe_unsupported ||
- !stmmac_has_ip_ethertype(skb))) {
- if (unlikely(skb_checksum_help(skb)))
- goto dma_map_err;
- csum_insertion = !csum_insertion;
+ /* Check if CSUM should be inserted by HW */
+ csum_insertion = stmmac_csum_insert(priv, skb, queue);
+ if (csum_insertion < 0) {
+ drop = true;
+ goto flush_ring;
}
if (likely(priv->extend_desc))
- desc = (struct dma_desc *)(tx_q->dma_etx + entry);
+ desc = &tx_q->dma_etx[first_entry].basic;
else if (tx_q->tbs & STMMAC_TBS_AVAIL)
- desc = &tx_q->dma_entx[entry].basic;
+ desc = &tx_q->dma_entx[first_entry].basic;
else
- desc = tx_q->dma_tx + entry;
+ desc = &tx_q->dma_tx[first_entry];
first = desc;
if (has_vlan)
stmmac_set_desc_vlan(priv, first, STMMAC_VLAN_INSERT);
- enh_desc = priv->plat->enh_desc;
- /* To program the descriptors according to the size of the frame */
- if (enh_desc)
- is_jumbo = stmmac_is_jumbo_frm(priv, skb->len, enh_desc);
+ des = dma_map_single(priv->device, skb->data, skb_headlen(skb),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->device, des)) {
+ drop = true;
+ goto flush_ring;
+ }
+
+ tx_q->tx_skbuff_dma[first_entry].buf = des;
+ tx_q->tx_skbuff_dma[first_entry].len = skb_headlen(skb);
+ tx_q->tx_skbuff_dma[first_entry].map_as_page = false;
+ tx_q->tx_skbuff_dma[first_entry].buf_type = STMMAC_TXBUF_T_SKB;
+
+ stmmac_set_desc_addr(priv, first, des);
- if (unlikely(is_jumbo)) {
- entry = stmmac_jumbo_frm(priv, tx_q, skb, csum_insertion);
- if (unlikely(entry < 0) && (entry != -EINVAL))
- goto dma_map_err;
+ nopaged_len = skb_headlen(skb);
+ descbuf_len = stmmac_get_tx_len(priv, priv->mode);
+ if (nopaged_len > descbuf_len) {
+ stmmac_tx_allocator(priv, des + descbuf_len,
+ nopaged_len - descbuf_len,
+ (nfrags == 0), queue);
+ nopaged_len = descbuf_len;
}
for (i = 0; i < nfrags; i++) {
const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- int len = skb_frag_size(frag);
- bool last_segment = (i == (nfrags - 1));
- entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
- WARN_ON(tx_q->tx_skbuff[entry]);
-
- if (likely(priv->extend_desc))
- desc = (struct dma_desc *)(tx_q->dma_etx + entry);
- else if (tx_q->tbs & STMMAC_TBS_AVAIL)
- desc = &tx_q->dma_entx[entry].basic;
- else
- desc = tx_q->dma_tx + entry;
-
- des = skb_frag_dma_map(priv->device, frag, 0, len,
+ des = skb_frag_dma_map(priv->device, frag, 0,
+ skb_frag_size(frag),
DMA_TO_DEVICE);
- if (dma_mapping_error(priv->device, des))
- goto dma_map_err; /* should reuse desc w/o issues */
-
- tx_q->tx_skbuff_dma[entry].buf = des;
-
- stmmac_set_desc_addr(priv, desc, des);
+ if (dma_mapping_error(priv->device, des)) {
+ netdev_err(priv->dev, "Frag Tx DMA map failed\n");
+ drop = true;
+ goto flush_ring; /* should reuse desc w/o issues */
+ }
- tx_q->tx_skbuff_dma[entry].map_as_page = true;
- tx_q->tx_skbuff_dma[entry].len = len;
- tx_q->tx_skbuff_dma[entry].last_segment = last_segment;
- tx_q->tx_skbuff_dma[entry].buf_type = STMMAC_TXBUF_T_SKB;
+ stmmac_tx_allocator(priv, des, skb_frag_size(frag),
+ (i == nfrags - 1), queue);
- /* Prepare the descriptor and set the own bit too */
- stmmac_prepare_tx_desc(priv, desc, 0, len, csum_insertion,
- priv->mode, 1, last_segment, skb->len);
+ tx_q->tx_skbuff_dma[tx_q->cur_tx].buf = des;
+ tx_q->tx_skbuff_dma[tx_q->cur_tx].len = skb_frag_size(frag);
+ tx_q->tx_skbuff_dma[tx_q->cur_tx].map_as_page = true;
+ tx_q->tx_skbuff_dma[tx_q->cur_tx].buf_type = STMMAC_TXBUF_T_SKB;
}
+ tx_q->tx_skbuff_dma[tx_q->cur_tx].last_segment = true;
+
/* Only the last descriptor gets to point to the skb. */
- tx_q->tx_skbuff[entry] = skb;
- tx_q->tx_skbuff_dma[entry].buf_type = STMMAC_TXBUF_T_SKB;
+ tx_q->tx_skbuff[tx_q->cur_tx] = skb;
+ tx_q->tx_skbuff_dma[tx_q->cur_tx].buf_type = STMMAC_TXBUF_T_SKB;
/* According to the coalesce parameter the IC bit for the latest
* segment is reset and the timer re-started to clean the tx status.
* This approach takes care about the fragments: desc is the first
* element in case of no SG.
*/
- tx_packets = (entry + 1) - first_tx;
+ tx_packets = (tx_q->cur_tx + 1) - first_tx;
tx_q->tx_count_frames += tx_packets;
if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)
@@ -4806,11 +5112,11 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (set_ic) {
if (likely(priv->extend_desc))
- desc = &tx_q->dma_etx[entry].basic;
+ desc = &tx_q->dma_etx[tx_q->cur_tx].basic;
else if (tx_q->tbs & STMMAC_TBS_AVAIL)
- desc = &tx_q->dma_entx[entry].basic;
+ desc = &tx_q->dma_entx[tx_q->cur_tx].basic;
else
- desc = &tx_q->dma_tx[entry];
+ desc = &tx_q->dma_tx[tx_q->cur_tx];
tx_q->tx_count_frames = 0;
stmmac_set_tx_ic(priv, desc);
@@ -4821,14 +5127,13 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
* ndo_start_xmit will fill this descriptor the next time it's
* called and stmmac_tx_clean may clean up to this descriptor.
*/
- entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
- tx_q->cur_tx = entry;
+ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
if (netif_msg_pktdata(priv)) {
netdev_dbg(priv->dev,
- "%s: curr=%d dirty=%d f=%d, e=%d, first=%p, nfrags=%d",
+ "%s: curr=%d dirty=%d entry=%d, first=%p, nfrags=%d",
__func__, tx_q->cur_tx, tx_q->dirty_tx, first_entry,
- entry, first, nfrags);
+ first, nfrags);
netdev_dbg(priv->dev, ">>> frame to be transmitted: ");
print_pkt(skb->data, skb->len);
@@ -4837,7 +5142,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) {
netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n",
__func__);
- netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue));
+ netif_tx_stop_queue(nq);
}
u64_stats_update_begin(&txq_stats->q_syncp);
@@ -4849,38 +5154,11 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (priv->sarc_type)
stmmac_set_desc_sarc(priv, first, priv->sarc_type);
- /* 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.
- */
- if (likely(!is_jumbo)) {
- bool last_segment = (nfrags == 0);
-
- des = dma_map_single(priv->device, skb->data,
- nopaged_len, DMA_TO_DEVICE);
- if (dma_mapping_error(priv->device, des))
- goto dma_map_err;
-
- tx_q->tx_skbuff_dma[first_entry].buf = des;
- tx_q->tx_skbuff_dma[first_entry].buf_type = STMMAC_TXBUF_T_SKB;
- tx_q->tx_skbuff_dma[first_entry].map_as_page = false;
-
- stmmac_set_desc_addr(priv, first, des);
-
- tx_q->tx_skbuff_dma[first_entry].len = nopaged_len;
- tx_q->tx_skbuff_dma[first_entry].last_segment = last_segment;
-
- if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
- priv->hwts_tx_en)) {
- /* declare that device is doing timestamping */
- skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
- stmmac_enable_tx_timestamp(priv, first);
- }
-
- /* Prepare the first descriptor setting the OWN bit too */
- stmmac_prepare_tx_desc(priv, first, 1, nopaged_len,
- csum_insertion, priv->mode, 0, last_segment,
- skb->len);
+ if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+ priv->hwts_tx_en)) {
+ /* declare that device is doing timestamping */
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ stmmac_enable_tx_timestamp(priv, first);
}
if (tx_q->tbs & STMMAC_TBS_EN) {
@@ -4890,41 +5168,123 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
stmmac_set_desc_tbs(priv, tbs_desc, ts.tv_sec, ts.tv_nsec);
}
- stmmac_set_tx_owner(priv, first);
-
- netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len);
+ /* Complete the first descriptor before granting the DMA */
+ stmmac_prepare_tx_desc(priv, first, 1, nopaged_len,
+ csum_insertion, priv->mode, true,
+ tx_q->tx_skbuff_dma[first_entry].last_segment,
+ skb->len);
- stmmac_enable_dma_transmission(priv, priv->ioaddr, queue);
skb_tx_timestamp(skb);
- stmmac_flush_tx_descriptors(priv, queue);
- stmmac_tx_timer_arm(priv, queue);
- return NETDEV_TX_OK;
+ /* Push out the packets after the particular amount of SKBs handled */
+ if (__netdev_tx_sent_queue(nq, skb->len, netdev_xmit_more())) {
+flush_ring:
+ if (drop) {
+ dev_kfree_skb(skb);
+ priv->xstats.tx_dropped++;
+ }
+ stmmac_flush_tx_descriptors(priv, queue);
+ stmmac_tx_timer_arm(priv, queue);
+ }
+
+ return ret;
+}
+
+/**
+ * stmmac_get_rx_status - Retrieve Rx DMA-descriptor status
+ * @priv: driver private structure
+ * @p : descriptor pointer
+ * Description :
+ * This function will parse the Rx DMA-descriptor status to get the generic
+ * status utilized by the Rx packets methods. It shall submit the descriptor
+ * for dropping if an error detected with no rx-all feature activated.
+ * Return: Rx DMA-descriptor status (rx_frame_status)
+ */
+static int stmmac_get_rx_status(struct stmmac_priv *priv, struct dma_desc *p)
+{
+ bool rxall = priv->dev->features & NETIF_F_RXALL;
+ int ret;
+
+ ret = stmmac_rx_status(priv, &priv->xstats, p);
+ if (likely(!(ret & (rx_err | arp_done)) || rxall))
+ return ret;
-dma_map_err:
- netdev_err(priv->dev, "Tx DMA map failed\n");
-max_sdu_err:
- dev_kfree_skb(skb);
- priv->xstats.tx_dropped++;
- return NETDEV_TX_OK;
+ return ret | discard_frame;
}
-static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
+/**
+ * stmmac_get_rx_vlan - Extract VLAN ID from the frame
+ * @priv: driver private structure
+ * @p : descriptor pointer
+ * @skb : the socket buffer
+ * Description :
+ * This function will either read the VLAN ID and TCI from the DMA-descriptor
+ * (if it's supported the VLAN stripping has been enabled) or just perform
+ * the VLAN header stripping directly from the payload.
+ */
+static void stmmac_get_rx_vlan(struct stmmac_priv *priv, struct dma_desc *p,
+ struct sk_buff *skb)
{
+#ifdef STMMAC_VLAN_TAG_USED
struct vlan_ethhdr *veth = skb_vlan_eth_hdr(skb);
- __be16 vlan_proto = veth->h_vlan_proto;
+ __be16 vlan_proto;
u16 vlanid;
+ int ret;
+
+ /* MAC level stripping */
+ ret = stmmac_rx_hw_vlan(priv, priv->hw, p, skb);
+ if (!ret)
+ return;
+ /* Driver level stripping */
+ vlan_proto = veth->h_vlan_proto;
if ((vlan_proto == htons(ETH_P_8021Q) &&
- dev->features & NETIF_F_HW_VLAN_CTAG_RX) ||
+ priv->dev->features & NETIF_F_HW_VLAN_CTAG_RX) ||
(vlan_proto == htons(ETH_P_8021AD) &&
- dev->features & NETIF_F_HW_VLAN_STAG_RX)) {
+ priv->dev->features & NETIF_F_HW_VLAN_STAG_RX)) {
/* pop the vlan tag */
vlanid = ntohs(veth->h_vlan_TCI);
memmove(skb->data + VLAN_HLEN, veth, ETH_ALEN * 2);
skb_pull(skb, VLAN_HLEN);
__vlan_hwaccel_put_tag(skb, vlan_proto, vlanid);
}
+#endif
+}
+
+/**
+ * stmmac_get_rx_csum - Retrieve Rx CSUM status
+ * @priv: driver private structure
+ * @status: frame Rx-status
+ * @skb : the socket buffer
+ * Description :
+ * This function will initialize the SKB IP summed based on the Rx COE
+ * outcomes.
+ */
+static void stmmac_get_rx_csum(struct stmmac_priv *priv, int status,
+ struct sk_buff *skb)
+{
+ bool coe = priv->dev->features & NETIF_F_RXCSUM;
+
+ /* If Rx COE wasn't synthesized into the IP-core the status may never
+ * have the csum_none flag. Skip the status check then to be on a safe
+ * side.
+ */
+ if (!coe || (status & csum_none)) {
+ skb_checksum_none_assert(skb);
+ return;
+ }
+
+ /* Rx COE feature is relatively similar on the late DW GMAC IP-cores
+ * (v3.30a and newer) and on the DW QoS Ether and DW XGMAC/XLGMAC/etc
+ * IP-cores except that the later ones support double tagged
+ * IP-packets:
+ * Rx COE type 2: C-(S-)VLAN + IPv4/IPv6 + TCP/UDP/ICMP
+ * Rx COE enh: 2xC-(S-)VLAN + IPv4/IPv6 + TCP/UDP/ICMP
+ *
+ * In any way the outcomes of the Rx COE work must be retrieved from
+ * the Rx DMA-descriptor.
+ */
+ __skb_incr_checksum_unnecessary(skb);
}
/**
@@ -4940,45 +5300,58 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
int dirty = stmmac_rx_dirty(priv, queue);
unsigned int entry = rx_q->dirty_rx;
gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
+ unsigned int size, first_rx;
if (priv->dma_cap.host_dma_width <= 32)
gfp |= GFP_DMA32;
+ first_rx = rx_q->dirty_rx;
+
while (dirty-- > 0) {
struct stmmac_rx_buffer *buf = &rx_q->buf_pool[entry];
struct dma_desc *p;
bool use_rx_wd;
+ dma_addr_t np;
- if (priv->extend_desc)
- p = (struct dma_desc *)(rx_q->dma_erx + entry);
- else
- p = rx_q->dma_rx + entry;
+ np = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_rx_size);
+ if (priv->extend_desc) {
+ p = &rx_q->dma_erx[entry].basic;
+ np = rx_q->dma_rx_phy + np * sizeof(struct dma_extended_desc);
+ } else {
+ p = &rx_q->dma_rx[entry];
+ np = rx_q->dma_rx_phy + np * sizeof(struct dma_desc);
+ }
if (!buf->page) {
- buf->page = page_pool_alloc_pages(rx_q->page_pool, gfp);
+ size = rx_q->napi_skb_frag_size;
+ buf->page = page_pool_alloc(rx_q->page_pool,
+ &buf->page_offset, &size, gfp);
if (!buf->page)
break;
+
+ buf->frame_offset = stmmac_rx_offset(priv);
+ buf->frag_offset = buf->frame_offset - stmmac_fs_offset();
+ buf->addr = page_pool_get_dma_addr(buf->page) +
+ buf->page_offset + buf->frame_offset;
}
if (priv->sph_active && !buf->sec_page) {
- buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
+ size = priv->dma_conf.dma_buf_sz;
+ buf->sec_page = page_pool_alloc(rx_q->sec_page_pool,
+ &buf->sec_page_offset, &size, gfp);
if (!buf->sec_page)
break;
- buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
+ buf->sec_addr = page_pool_get_dma_addr(buf->sec_page) +
+ buf->sec_page_offset;
}
- buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
-
stmmac_set_desc_addr(priv, p, buf->addr);
- if (priv->sph_active)
- stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true);
- else
- stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false);
- stmmac_refill_desc3(priv, rx_q, p);
+ stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, priv->sph_active);
+ stmmac_prepare_rx_desc(priv, p, priv->mode, np,
+ priv->hwts_rx_en, priv->dma_conf.dma_buf_sz);
rx_q->rx_count_frames++;
- rx_q->rx_count_frames += priv->rx_coal_frames[queue];
if (rx_q->rx_count_frames > priv->rx_coal_frames[queue])
rx_q->rx_count_frames = 0;
@@ -4987,8 +5360,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
if (!priv->use_riwt)
use_rx_wd = false;
- dma_wmb();
- stmmac_set_rx_owner(priv, p, use_rx_wd);
+ stmmac_release_rx_desc(priv, p, use_rx_wd,
+ entry == first_rx, true);
entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_rx_size);
}
@@ -5000,39 +5373,75 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
stmmac_enable_dma_reception(priv, priv->ioaddr, queue);
}
+/**
+ * stmmac_fcs_trimming - FCS field length
+ * @priv: driver private structure
+ * @status: frame Rx-status
+ * Description :
+ * Retrieve FCS length required to be trimmed of the Rx frame.
+ */
+static unsigned int stmmac_fcs_trimming(struct stmmac_priv *priv, int status)
+{
+ bool rxfcs = priv->dev->features & NETIF_F_RXFCS;
+
+ /* By default Pad/FCS is left attached to the frame */
+ if (rxfcs)
+ return 0;
+
+ /* Auto Pad/FCS stripping is unavailable on the old controller and it
+ * is deliberately disabled for Ethernet II frames to prevent the MAC
+ * confusing some DSA frames with IEEE 802.3 frames and truncating them
+ * up to the Ethertype-length (see "Receive Protocol Engine Module"
+ * chapter for details).
+ */
+ if (!stmmac_rx_fcs_status(priv, priv->hw, priv->synopsys_id, status))
+ return 0;
+
+ return ETH_FCS_LEN;
+}
+
static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv,
struct dma_desc *p,
int status, unsigned int len)
{
- unsigned int plen = 0, hlen = 0;
- int coe = priv->hw->rx_csum;
+ unsigned int plen = 0;
- /* Not first descriptor, buffer is always zero */
- if (priv->sph_active && len)
- return 0;
+ /* Split header enabled */
+ if (priv->sph_active) {
+ unsigned int hlen = 0;
+
+ /* Not first descriptor, primary buffer unused in any case */
+ if (len)
+ return 0;
+
+ /* First descriptor, get split header length */
+ stmmac_get_rx_header_len(priv, p, &hlen);
+ if (hlen) {
+ priv->xstats.rx_split_hdr_pkt_n++;
+ return hlen;
+ }
+ }
- /* First descriptor, get split header length */
- stmmac_get_rx_header_len(priv, p, &hlen);
- if (priv->sph_active && hlen) {
- priv->xstats.rx_split_hdr_pkt_n++;
- return hlen;
+ /* Not last descriptor and not split header */
+ if (status & rx_not_ls) {
+ /* First descriptor, buffer size reduced by offset */
+ plen = priv->dma_conf.dma_buf_sz;
+ return len ? plen : plen - stmmac_fs_offset();
}
- /* First descriptor, not last descriptor and not split header */
- if (status & rx_not_ls)
+ /* Last descriptor and not split header and secondary buffer used */
+ plen = stmmac_get_rx_frame_len(priv, p) - len;
+ if (plen > priv->dma_conf.dma_buf_sz)
return priv->dma_conf.dma_buf_sz;
- plen = stmmac_get_rx_frame_len(priv, p, coe);
-
- /* First descriptor and last descriptor and not split header */
- return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen);
+ /* Last descriptor and last buffer with FCS clipped of */
+ return plen - stmmac_fcs_trimming(priv, status);
}
static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
struct dma_desc *p,
int status, unsigned int len)
{
- int coe = priv->hw->rx_csum;
unsigned int plen = 0;
/* Not split header, buffer is not available */
@@ -5057,10 +5466,13 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
if (priv->plat->core_type != DWMAC_CORE_GMAC4 && (status & rx_not_ls))
return priv->dma_conf.dma_buf_sz;
- /* GMAC4 or last descriptor */
- plen = stmmac_get_rx_frame_len(priv, p, coe);
+ /* GMAC4 or last descriptor and secondary buffer unused */
+ plen = stmmac_get_rx_frame_len(priv, p) - len;
+ if (!plen)
+ return 0;
- return plen - len;
+ /* Last descriptor and last buffer with FCS clipped of */
+ return plen - stmmac_fcs_trimming(priv, status);
}
static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
@@ -5113,7 +5525,6 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
tx_q->tx_skbuff_dma[entry].map_as_page = false;
tx_q->tx_skbuff_dma[entry].len = xdpf->len;
tx_q->tx_skbuff_dma[entry].last_segment = true;
- tx_q->tx_skbuff_dma[entry].is_jumbo = false;
tx_q->xdpf[entry] = xdpf;
@@ -5138,8 +5549,6 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
u64_stats_update_end(&txq_stats->q_syncp);
}
- stmmac_enable_dma_transmission(priv, priv->ioaddr, queue);
-
entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
tx_q->cur_tx = entry;
@@ -5287,13 +5696,12 @@ static struct sk_buff *stmmac_construct_skb_zc(struct stmmac_channel *ch,
static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue,
struct dma_desc *p, struct dma_desc *np,
- struct xdp_buff *xdp)
+ int status, struct xdp_buff *xdp)
{
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue];
struct stmmac_channel *ch = &priv->channel[queue];
unsigned int len = xdp->data_end - xdp->data;
enum pkt_hash_types hash_type;
- int coe = priv->hw->rx_csum;
struct sk_buff *skb;
u32 hash;
@@ -5304,18 +5712,12 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue,
}
stmmac_get_rx_hwtstamp(priv, p, np, skb);
- if (priv->hw->hw_vlan_en)
- /* MAC level stripping. */
- stmmac_rx_hw_vlan(priv, priv->hw, p, skb);
- else
- /* Driver level stripping. */
- stmmac_rx_vlan(priv->dev, skb);
+
+ stmmac_get_rx_vlan(priv, p, skb);
+
skb->protocol = eth_type_trans(skb, priv->dev);
- if (unlikely(!coe) || !stmmac_has_ip_ethertype(skb))
- skb_checksum_none_assert(skb);
- else
- skb->ip_summed = CHECKSUM_UNNECESSARY;
+ stmmac_get_rx_csum(priv, status, skb);
if (!stmmac_get_rx_hash(priv, p, &hash, &hash_type))
skb_set_hash(skb, hash, hash_type);
@@ -5332,12 +5734,15 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue,
static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
{
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
- unsigned int entry = rx_q->dirty_rx;
+ unsigned int entry = rx_q->dirty_rx, first_rx;
struct dma_desc *rx_desc = NULL;
+ dma_addr_t rx_next;
bool ret = true;
budget = min(budget, stmmac_rx_dirty(priv, queue));
+ first_rx = rx_q->dirty_rx;
+
while (budget-- > 0 && entry != rx_q->cur_rx) {
struct stmmac_rx_buffer *buf = &rx_q->buf_pool[entry];
dma_addr_t dma_addr;
@@ -5351,18 +5756,24 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
}
}
- if (priv->extend_desc)
- rx_desc = (struct dma_desc *)(rx_q->dma_erx + entry);
- else
- rx_desc = rx_q->dma_rx + entry;
+ rx_next = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_rx_size);
+ if (priv->extend_desc) {
+ rx_desc = &rx_q->dma_erx[entry].basic;
+ rx_next = rx_q->dma_rx_phy +
+ rx_next * sizeof(struct dma_extended_desc);
+ } else {
+ rx_desc = &rx_q->dma_rx[entry];
+ rx_next = rx_q->dma_rx_phy +
+ rx_next * sizeof(struct dma_desc);
+ }
dma_addr = xsk_buff_xdp_get_dma(buf->xdp);
stmmac_set_desc_addr(priv, rx_desc, dma_addr);
stmmac_set_desc_sec_addr(priv, rx_desc, 0, false);
- stmmac_refill_desc3(priv, rx_q, rx_desc);
+ stmmac_prepare_rx_desc(priv, rx_desc, priv->mode, rx_next,
+ priv->hwts_rx_en, priv->dma_conf.dma_buf_sz);
rx_q->rx_count_frames++;
- rx_q->rx_count_frames += priv->rx_coal_frames[queue];
if (rx_q->rx_count_frames > priv->rx_coal_frames[queue])
rx_q->rx_count_frames = 0;
@@ -5371,8 +5782,8 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
if (!priv->use_riwt)
use_rx_wd = false;
- dma_wmb();
- stmmac_set_rx_owner(priv, rx_desc, use_rx_wd);
+ stmmac_release_rx_desc(priv, rx_desc, use_rx_wd,
+ entry == first_rx, true);
entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_rx_size);
}
@@ -5462,9 +5873,7 @@ read_again:
else
p = rx_q->dma_rx + entry;
- /* read the status of the incoming frame */
- status = stmmac_rx_status(priv, &priv->xstats, p);
- /* check if managed by the DMA otherwise go ahead */
+ status = stmmac_get_rx_status(priv, p);
if (unlikely(status & dma_own))
break;
@@ -5484,15 +5893,10 @@ read_again:
if (!buf->xdp)
break;
- if (priv->extend_desc)
- stmmac_rx_extended_status(priv, &priv->xstats,
- rx_q->dma_erx + entry);
- if (unlikely(status == discard_frame)) {
- xsk_buff_free(buf->xdp);
- buf->xdp = NULL;
+ if (unlikely(status & discard_frame)) {
dirty++;
error = 1;
- if (!priv->hwts_rx_en)
+ if (status & rx_err)
rx_errors++;
}
@@ -5505,8 +5909,6 @@ read_again:
/* XSK pool expects RX frame 1:1 mapped to XSK buffer */
if (likely(status & rx_not_ls)) {
- xsk_buff_free(buf->xdp);
- buf->xdp = NULL;
dirty++;
count++;
goto read_again;
@@ -5521,12 +5923,6 @@ read_again:
buf1_len = stmmac_rx_buf1_len(priv, p, status, len);
len += buf1_len;
- /* ACS is disabled; strip manually. */
- if (likely(!(status & rx_not_ls))) {
- buf1_len -= ETH_FCS_LEN;
- len -= ETH_FCS_LEN;
- }
-
/* RX buffer is good and fit into a XSK pool buffer */
buf->xdp->data_end = buf->xdp->data + buf1_len;
xsk_buff_dma_sync_for_cpu(buf->xdp);
@@ -5536,7 +5932,7 @@ read_again:
switch (res) {
case STMMAC_XDP_PASS:
- stmmac_dispatch_skb_zc(priv, queue, p, np, buf->xdp);
+ stmmac_dispatch_skb_zc(priv, queue, p, np, status, buf->xdp);
xsk_buff_free(buf->xdp);
break;
case STMMAC_XDP_CONSUMED:
@@ -5598,17 +5994,14 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
struct stmmac_channel *ch = &priv->channel[queue];
unsigned int count = 0, error = 0, len = 0;
- int status = 0, coe = priv->hw->rx_csum;
unsigned int next_entry = rx_q->cur_rx;
enum dma_data_direction dma_dir;
+ int status = 0, xdp_status = 0;
unsigned int desc_size;
struct sk_buff *skb = NULL;
struct stmmac_xdp_buff ctx;
- int xdp_status = 0;
- int bufsz;
dma_dir = page_pool_get_dma_dir(rx_q->page_pool);
- bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
limit = min(priv->dma_conf.dma_rx_size - 1, (unsigned int)limit);
if (netif_msg_rx_status(priv)) {
@@ -5659,9 +6052,7 @@ read_again:
else
p = rx_q->dma_rx + entry;
- /* read the status of the incoming frame */
- status = stmmac_rx_status(priv, &priv->xstats, p);
- /* check if managed by the DMA otherwise go ahead */
+ status = stmmac_get_rx_status(priv, p);
if (unlikely(status & dma_own))
break;
@@ -5676,13 +6067,9 @@ read_again:
prefetch(np);
- if (priv->extend_desc)
- stmmac_rx_extended_status(priv, &priv->xstats, rx_q->dma_erx + entry);
- if (unlikely(status == discard_frame)) {
- page_pool_put_page(rx_q->page_pool, buf->page, 0, true);
- buf->page = NULL;
+ if (unlikely(status & discard_frame)) {
error = 1;
- if (!priv->hwts_rx_en)
+ if (status & rx_err)
rx_errors++;
}
@@ -5702,31 +6089,21 @@ read_again:
buf2_len = stmmac_rx_buf2_len(priv, p, status, len);
len += buf2_len;
- /* ACS is disabled; strip manually. */
- if (likely(!(status & rx_not_ls))) {
- if (buf2_len) {
- buf2_len -= ETH_FCS_LEN;
- len -= ETH_FCS_LEN;
- } else if (buf1_len) {
- buf1_len -= ETH_FCS_LEN;
- len -= ETH_FCS_LEN;
- }
- }
-
if (!skb) {
unsigned int pre_len, sync_len;
dma_sync_single_for_cpu(priv->device, buf->addr,
buf1_len, dma_dir);
net_prefetch(page_address(buf->page) +
- buf->page_offset);
+ buf->page_offset + buf->frame_offset);
- xdp_init_buff(&ctx.xdp, bufsz, &rx_q->xdp_rxq);
- xdp_prepare_buff(&ctx.xdp, page_address(buf->page),
- buf->page_offset, buf1_len, true);
+ xdp_init_buff(&ctx.xdp, rx_q->napi_skb_frag_size,
+ &rx_q->xdp_rxq);
+ xdp_prepare_buff(&ctx.xdp, page_address(buf->page) + buf->page_offset,
+ buf->frame_offset, buf1_len, true);
pre_len = ctx.xdp.data_end - ctx.xdp.data_hard_start -
- buf->page_offset;
+ buf->frame_offset;
ctx.priv = priv;
ctx.desc = p;
@@ -5737,7 +6114,7 @@ read_again:
* cover max len CPU touch
*/
sync_len = ctx.xdp.data_end - ctx.xdp.data_hard_start -
- buf->page_offset;
+ buf->frame_offset;
sync_len = max(sync_len, pre_len);
/* For Not XDP_PASS verdict */
@@ -5745,9 +6122,7 @@ read_again:
unsigned int xdp_res = -PTR_ERR(skb);
if (xdp_res & STMMAC_XDP_CONSUMED) {
- page_pool_put_page(rx_q->page_pool,
- virt_to_head_page(ctx.xdp.data),
- sync_len, true);
+ page_pool_put_full_page(rx_q->page_pool, buf->page, true);
buf->page = NULL;
rx_dropped++;
@@ -5773,33 +6148,38 @@ read_again:
}
if (!skb) {
- unsigned int head_pad_len;
-
/* XDP program may expand or reduce tail */
buf1_len = ctx.xdp.data_end - ctx.xdp.data;
- skb = napi_build_skb(page_address(buf->page),
- rx_q->napi_skb_frag_size);
+ if (buf1_len < priv->rx_copybreak)
+ skb = napi_alloc_skb(&ch->rx_napi, buf1_len);
+ else
+ skb = napi_build_skb(page_address(buf->page) + buf->page_offset,
+ rx_q->napi_skb_frag_size);
if (!skb) {
- page_pool_recycle_direct(rx_q->page_pool,
- buf->page);
rx_dropped++;
count++;
goto drain_data;
}
/* XDP program may adjust header */
- head_pad_len = ctx.xdp.data - ctx.xdp.data_hard_start;
- skb_reserve(skb, head_pad_len);
+ if (buf1_len < priv->rx_copybreak) {
+ skb_copy_to_linear_data(skb, ctx.xdp.data, buf1_len);
+ page_pool_put_full_page(rx_q->page_pool, buf->page, true);
+ } else {
+ skb_reserve(skb, ctx.xdp.data - ctx.xdp.data_hard_start);
+ skb_mark_for_recycle(skb);
+ }
+
skb_put(skb, buf1_len);
- skb_mark_for_recycle(skb);
buf->page = NULL;
} else if (buf1_len) {
dma_sync_single_for_cpu(priv->device, buf->addr,
buf1_len, dma_dir);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
- buf->page, buf->page_offset, buf1_len,
- priv->dma_conf.dma_buf_sz);
+ buf->page, buf->page_offset + buf->frag_offset,
+ buf1_len, priv->dma_conf.dma_buf_sz);
+ skb_mark_for_recycle(skb);
buf->page = NULL;
}
@@ -5807,8 +6187,9 @@ read_again:
dma_sync_single_for_cpu(priv->device, buf->sec_addr,
buf2_len, dma_dir);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
- buf->sec_page, 0, buf2_len,
+ buf->sec_page, buf->sec_page_offset, buf2_len,
priv->dma_conf.dma_buf_sz);
+ skb_mark_for_recycle(skb);
buf->sec_page = NULL;
}
@@ -5822,20 +6203,11 @@ drain_data:
stmmac_get_rx_hwtstamp(priv, p, np, skb);
- if (priv->hw->hw_vlan_en)
- /* MAC level stripping. */
- stmmac_rx_hw_vlan(priv, priv->hw, p, skb);
- else
- /* Driver level stripping. */
- stmmac_rx_vlan(priv->dev, skb);
+ stmmac_get_rx_vlan(priv, p, skb);
skb->protocol = eth_type_trans(skb, priv->dev);
- if (unlikely(!coe) || !stmmac_has_ip_ethertype(skb) ||
- (status & csum_none))
- skb_checksum_none_assert(skb);
- else
- skb->ip_summed = CHECKSUM_UNNECESSARY;
+ stmmac_get_rx_csum(priv, status, skb);
if (!stmmac_get_rx_hash(priv, p, &hash, &hash_type))
skb_set_hash(skb, hash, hash_type);
@@ -5997,7 +6369,7 @@ static void stmmac_tx_timeout(struct net_device *dev, unsigned int txqueue)
{
struct stmmac_priv *priv = netdev_priv(dev);
- stmmac_global_err(priv);
+ stmmac_handle_fatal_err(priv);
}
/**
@@ -6020,6 +6392,47 @@ static void stmmac_set_rx_mode(struct net_device *dev)
}
/**
+ * stmmac_verify_max_mtu - verify net-device maximum MTU constraint
+ * @priv: driver private structure
+ * @tx_coe: Tx COE feature state
+ * @tx_cnt: Number of active Tx queues
+ * @rx_cnt: Number of active Rx queues
+ * Description: calculate the maximum MTU constraint corresponding to the
+ * device capability and passed Tx/Rx parameters, and check whether it can
+ * be applied so the current MTU wouldn't get out of bounds.
+ * Return: 0 on success or errno if current MTU gets out of the new max MTU.
+ */
+static int stmmac_verify_max_mtu(struct stmmac_priv *priv, bool tx_coe,
+ u32 tx_cnt, u32 rx_cnt)
+{
+ unsigned int mtu, max_mtu;
+
+ mtu = READ_ONCE(priv->dev->mtu);
+ max_mtu = stmmac_get_max_mtu(priv, tx_coe, tx_cnt, rx_cnt);
+
+ if (mtu > max_mtu)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * stmmac_set_max_mtu - set net-device maximum MTU constraint
+ * @priv: driver private structure
+ * @tx_coe: Tx COE feature state
+ * @tx_cnt: Number of active Tx queues
+ * @rx_cnt: Number of active Rx queues
+ * Description: calculate and apply the maximum MTU constraint corresponding to the
+ * device capability and passed Tx/Rx parameters.
+ * Return: 0 on success or errno if current MTU gets out of the new max MTU.
+ */
+static void stmmac_set_max_mtu(struct stmmac_priv *priv, bool tx_coe,
+ u32 tx_cnt, u32 rx_cnt)
+{
+ priv->dev->max_mtu = stmmac_get_max_mtu(priv, tx_coe, tx_cnt, rx_cnt);
+}
+
+/**
* stmmac_change_mtu - entry point to change MTU size for the device.
* @dev : device pointer.
* @new_mtu : the new MTU size for the device.
@@ -6033,39 +6446,28 @@ static void stmmac_set_rx_mode(struct net_device *dev)
static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
{
struct stmmac_priv *priv = netdev_priv(dev);
- int txfifosz = priv->plat->tx_fifo_size;
struct stmmac_dma_conf *dma_conf;
- const int mtu = new_mtu;
int ret;
- if (txfifosz == 0)
- txfifosz = priv->dma_cap.tx_fifo_size;
-
- txfifosz /= priv->plat->tx_queues_to_use;
-
if (stmmac_xdp_is_enabled(priv) && new_mtu > ETH_DATA_LEN) {
netdev_dbg(priv->dev, "Jumbo frames not supported for XDP\n");
return -EINVAL;
}
- new_mtu = STMMAC_ALIGN(new_mtu);
-
- /* If condition true, FIFO is too small or MTU too large */
- if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB))
- return -EINVAL;
-
if (netif_running(dev)) {
netdev_dbg(priv->dev, "restarting interface to change its MTU\n");
/* Try to allocate the new DMA conf with the new mtu */
- dma_conf = stmmac_setup_dma_desc(priv, mtu);
+ dma_conf = stmmac_setup_dma_desc(priv, new_mtu);
if (IS_ERR(dma_conf)) {
netdev_err(priv->dev, "failed allocating new dma conf for new MTU %d\n",
- mtu);
+ new_mtu);
return PTR_ERR(dma_conf);
}
__stmmac_release(dev);
+ WRITE_ONCE(dev->mtu, new_mtu);
+
ret = __stmmac_open(dev, dma_conf);
if (ret) {
free_dma_desc_resources(priv, dma_conf);
@@ -6077,9 +6479,10 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
kfree(dma_conf);
stmmac_set_rx_mode(dev);
+ } else {
+ WRITE_ONCE(dev->mtu, new_mtu);
}
- WRITE_ONCE(dev->mtu, mtu);
netdev_update_features(dev);
return 0;
@@ -6090,12 +6493,6 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,
{
struct stmmac_priv *priv = netdev_priv(dev);
- if (priv->plat->rx_coe == STMMAC_RX_COE_NONE)
- features &= ~NETIF_F_RXCSUM;
-
- if (!priv->plat->tx_coe)
- features &= ~NETIF_F_CSUM_MASK;
-
/* Some GMAC devices have a bugged Jumbo frame support that
* needs to have the Tx COE disabled for oversized frames
* (due to limited buffer sizes). In this case we disable
@@ -6104,14 +6501,40 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,
if (priv->plat->bugged_jumbo && (dev->mtu > ETH_DATA_LEN))
features &= ~NETIF_F_CSUM_MASK;
- /* Disable tso if asked by ethtool */
- if ((priv->plat->flags & STMMAC_FLAG_TSO_EN) && (priv->dma_cap.tsoen)) {
- if (features & NETIF_F_TSO)
- priv->tso = true;
- else
- priv->tso = false;
+ /* MTU constraint depends on Tx COE/TSO features */
+ if (stmmac_verify_max_mtu(priv, features & NETIF_F_CSUM_MASK,
+ priv->plat->tx_queues_to_use,
+ priv->plat->rx_queues_to_use))
+ features &= ~NETIF_F_CSUM_MASK;
+
+ /* SPH feature depends on Rx COE */
+ if (priv->sph_active)
+ features |= NETIF_F_RXCSUM;
+
+ /* RSS feature depends on Rx COE */
+ if (!(features & NETIF_F_RXCSUM))
+ features &= ~NETIF_F_RXHASH;
+
+ /* VLAN-filters work for both VLAN tag types */
+ if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ features |= NETIF_F_HW_VLAN_STAG_FILTER;
+ else if (features & NETIF_F_HW_VLAN_STAG_FILTER)
+ features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
+ /* VLAN-stripping implies removing any VLAN tag type: CTAG and STAG */
+ if (dwmac_is_xmac(priv->plat->core_type)) {
+ if (features & NETIF_F_HW_VLAN_CTAG_RX)
+ features |= NETIF_F_HW_VLAN_STAG_RX;
+ else if (features & NETIF_F_HW_VLAN_STAG_RX)
+ features |= NETIF_F_HW_VLAN_CTAG_RX;
}
+ /* VLAN type insertion is mutually exclusive: either CTAG or STAG */
+ if (features & NETIF_F_HW_VLAN_CTAG_TX)
+ features &= ~NETIF_F_HW_VLAN_STAG_TX;
+ else if (features & NETIF_F_HW_VLAN_STAG_TX)
+ features &= ~NETIF_F_HW_VLAN_CTAG_TX;
+
return features;
}
@@ -6119,32 +6542,44 @@ static int stmmac_set_features(struct net_device *netdev,
netdev_features_t features)
{
struct stmmac_priv *priv = netdev_priv(netdev);
+ u32 chan;
- /* Keep the COE Type in case of csum is supporting */
- if (features & NETIF_F_RXCSUM)
- priv->hw->rx_csum = priv->plat->rx_coe;
- else
- priv->hw->rx_csum = 0;
- /* No check needed because rx_coe has been set before and it will be
- * fixed in case of issue.
- */
- stmmac_rx_ipc(priv, priv->hw);
+ stmmac_set_max_mtu(priv, features & NETIF_F_CSUM_MASK,
+ priv->plat->tx_queues_to_use,
+ priv->plat->rx_queues_to_use);
+
+ stmmac_rx_fcs(priv, priv->hw, features & NETIF_F_RXFCS);
- if (priv->sph_capable) {
- bool sph_en = (priv->hw->rx_csum > 0) && priv->sph_active;
- u32 chan;
+ stmmac_rx_ipc(priv, priv->hw, features & NETIF_F_RXCSUM);
- for (chan = 0; chan < priv->plat->rx_queues_to_use; chan++)
- stmmac_enable_sph(priv, priv->ioaddr, sph_en, chan);
+ for (chan = 0; chan < priv->plat->rx_queues_to_use; chan++) {
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
+ u8 rxqmode = priv->plat->rx_queues_cfg[chan].mode_to_use;
+ int rxfifosz = stmmac_get_rxfifosz(priv);
+
+ stmmac_dma_rx_mode(priv, priv->ioaddr, rx_q->rtc, chan,
+ rxfifosz, rxqmode, features & NETIF_F_RXALL);
}
- if (features & NETIF_F_HW_VLAN_CTAG_RX)
- priv->hw->hw_vlan_en = true;
- else
- priv->hw->hw_vlan_en = false;
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) {
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ bool tso_en = !(tx_q->tbs & STMMAC_TBS_AVAIL) &&
+ features & (NETIF_F_TSO | NETIF_F_TSO6);
+
+ stmmac_enable_tso(priv, priv->ioaddr, tso_en, chan);
+ }
+
+ stmmac_rss_configure(priv, priv->hw, &priv->rss,
+ features & NETIF_F_RXHASH,
+ priv->plat->rx_queues_to_use);
phylink_rx_clk_stop_block(priv->phylink);
- stmmac_set_hw_vlan_mode(priv, priv->hw);
+ stmmac_set_hw_vlan_mode(priv, priv->hw,
+ features & (NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_HW_VLAN_STAG_RX),
+ features & NETIF_F_HW_VLAN_CTAG_FILTER,
+ features & NETIF_F_HW_VLAN_STAG_FILTER,
+ features & NETIF_F_HW_VLAN_STAG_TX);
phylink_rx_clk_stop_unblock(priv->phylink);
return 0;
@@ -6155,6 +6590,7 @@ static void stmmac_common_interrupt(struct stmmac_priv *priv)
u32 rx_cnt = priv->plat->rx_queues_to_use;
u32 tx_cnt = priv->plat->tx_queues_to_use;
u32 queues_count;
+ int status;
u32 queue;
bool xmac;
@@ -6173,7 +6609,7 @@ static void stmmac_common_interrupt(struct stmmac_priv *priv)
/* To handle GMAC own interrupts */
if (priv->plat->core_type == DWMAC_CORE_GMAC || xmac) {
- int status = stmmac_host_irq_status(priv, &priv->xstats);
+ status = stmmac_host_irq_status(priv, &priv->xstats);
if (unlikely(status)) {
/* For LPI we need to save the tx status */
@@ -6183,11 +6619,18 @@ static void stmmac_common_interrupt(struct stmmac_priv *priv)
priv->tx_path_in_lpi_mode = false;
}
- for (queue = 0; queue < queues_count; queue++)
- stmmac_host_mtl_irq_status(priv, priv->hw, queue);
-
stmmac_timestamp_interrupt(priv, priv);
}
+
+ if (xmac) {
+ for (queue = 0; queue < queues_count; queue++) {
+ status = stmmac_host_mtl_irq_status(priv, priv->hw, queue);
+ if (unlikely(status & CORE_IRQ_MTL_RX_OVERFLOW))
+ stmmac_handle_rx_ovf_error(priv, queue);
+ if (unlikely(status & CORE_IRQ_MTL_TX_UNDERFLOW))
+ stmmac_handle_tx_unf_error(priv, queue);
+ }
+ }
}
/**
@@ -6206,8 +6649,7 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
struct net_device *dev = (struct net_device *)dev_id;
struct stmmac_priv *priv = netdev_priv(dev);
- /* Check if adapter is up */
- if (test_bit(STMMAC_DOWN, &priv->state))
+ if (!netif_running(dev))
return IRQ_HANDLED;
/* Check ASP error if it isn't delivered via an individual IRQ */
@@ -6228,8 +6670,7 @@ static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id)
struct net_device *dev = (struct net_device *)dev_id;
struct stmmac_priv *priv = netdev_priv(dev);
- /* Check if adapter is up */
- if (test_bit(STMMAC_DOWN, &priv->state))
+ if (!netif_running(dev))
return IRQ_HANDLED;
/* To handle Common interrupts */
@@ -6243,8 +6684,7 @@ static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id)
struct net_device *dev = (struct net_device *)dev_id;
struct stmmac_priv *priv = netdev_priv(dev);
- /* Check if adapter is up */
- if (test_bit(STMMAC_DOWN, &priv->state))
+ if (!netif_running(dev))
return IRQ_HANDLED;
/* Check if a fatal error happened */
@@ -6264,18 +6704,18 @@ static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)
dma_conf = container_of(tx_q, struct stmmac_dma_conf, tx_queue[chan]);
priv = container_of(dma_conf, struct stmmac_priv, dma_conf);
- /* Check if adapter is up */
- if (test_bit(STMMAC_DOWN, &priv->state))
+ if (!netif_running(priv->dev))
return IRQ_HANDLED;
status = stmmac_napi_check(priv, chan, DMA_DIR_TX);
-
- if (unlikely(status & tx_hard_error_bump_tc)) {
- /* Try to bump up the dma threshold on this failure */
- stmmac_bump_dma_threshold(priv, chan);
- } else if (unlikely(status == tx_hard_error)) {
- stmmac_tx_err(priv, chan);
- }
+ if (unlikely(status & io_fatal_error))
+ stmmac_handle_fatal_err(priv);
+ if (unlikely(status & tx_hard_error))
+ stmmac_handle_tx_err(priv, chan);
+ if (unlikely(status & tx_unf_error))
+ stmmac_handle_tx_unf_error(priv, chan);
+ if (unlikely(status & tx_soft_stop))
+ stmmac_handle_tx_stop(priv, chan);
return IRQ_HANDLED;
}
@@ -6286,15 +6726,21 @@ static irqreturn_t stmmac_msi_intr_rx(int irq, void *data)
struct stmmac_dma_conf *dma_conf;
int chan = rx_q->queue_index;
struct stmmac_priv *priv;
+ int status;
dma_conf = container_of(rx_q, struct stmmac_dma_conf, rx_queue[chan]);
priv = container_of(dma_conf, struct stmmac_priv, dma_conf);
- /* Check if adapter is up */
- if (test_bit(STMMAC_DOWN, &priv->state))
+ if (!netif_running(priv->dev))
return IRQ_HANDLED;
- stmmac_napi_check(priv, chan, DMA_DIR_RX);
+ status = stmmac_napi_check(priv, chan, DMA_DIR_RX);
+ if (unlikely(status & io_fatal_error))
+ stmmac_handle_fatal_err(priv);
+ if (unlikely(status & rx_hard_error))
+ stmmac_handle_rx_err(priv, chan);
+ if (unlikely(status & rx_ovf_error))
+ stmmac_handle_rx_ovf_error(priv, chan);
return IRQ_HANDLED;
}
@@ -6386,9 +6832,11 @@ static int stmmac_setup_tc(struct net_device *ndev, enum tc_setup_type type,
static u16 stmmac_select_queue(struct net_device *dev, struct sk_buff *skb,
struct net_device *sb_dev)
{
+ struct stmmac_priv *priv = netdev_priv(dev);
int gso = skb_shinfo(skb)->gso_type;
- if (gso & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6 | SKB_GSO_UDP_L4)) {
+ if (gso & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6 | SKB_GSO_UDP_L4) &&
+ !(priv->plat->flags & STMMAC_FLAG_TSO_FULL)) {
/*
* There is no way to determine the number of TSO/USO
* capable Queues. Let's use always the Queue 0
@@ -6644,9 +7092,8 @@ static int stmmac_dma_cap_show(struct seq_file *seq, void *v)
BIT(priv->dma_cap.frpbs) << 6);
seq_printf(seq, "\tParallel Instruction Processor Engines: %d\n",
priv->dma_cap.frppipe_num);
- seq_printf(seq, "\tNumber of Extended VLAN Tag Filters: %lu\n",
- priv->dma_cap.nrvf_num ?
- (BIT(priv->dma_cap.nrvf_num) << 1) : 0);
+ seq_printf(seq, "\tNumber of Extended VLAN Tag Filters: %u\n",
+ priv->dma_cap.nrvf_num);
seq_printf(seq, "\tWidth of the Time Interval Field in GCL: %d\n",
priv->dma_cap.estwid ? 4 * priv->dma_cap.estwid + 12 : 0);
seq_printf(seq, "\tDepth of GCL: %lu\n",
@@ -6747,32 +7194,59 @@ static u32 stmmac_vid_crc32_le(__le16 vid_le)
return crc;
}
-static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
+static int stmmac_update_hw_vlan_rx_hash(struct stmmac_priv *priv, bool add,
+ __be16 proto, u16 vid)
{
+ int add_ctags = 0, add_stags = 0;
+ unsigned long *active_vlans;
u32 crc, hash = 0;
- u16 pmatch = 0;
- int count = 0;
- u16 vid = 0;
+ u16 vid_cpu;
+ int ret;
+
+ if (!priv->dma_cap.vlhash)
+ return -ENOSPC;
- for_each_set_bit(vid, priv->active_vlans, VLAN_N_VID) {
+ if (proto == htons(ETH_P_8021Q)) {
+ active_vlans = priv->active_cvlans;
+ add_ctags = add ? 1 : -1;
+ } else {
+ active_vlans = priv->active_svlans;
+ add_stags = add ? 1 : -1;
+ }
+
+ if (add)
+ ret = test_and_set_bit(vid, active_vlans);
+ else
+ ret = test_and_clear_bit(vid, active_vlans);
+
+ if (ret == add)
+ return 0;
+
+ for_each_set_bit(vid, priv->active_cvlans, VLAN_N_VID) {
__le16 vid_le = cpu_to_le16(vid);
crc = bitrev32(~stmmac_vid_crc32_le(vid_le)) >> 28;
hash |= (1 << crc);
- count++;
}
- if (!priv->dma_cap.vlhash) {
- if (count > 2) /* VID = 0 always passes filter */
- return -EOPNOTSUPP;
+ for_each_set_bit(vid_cpu, priv->active_svlans, VLAN_N_VID) {
+ __le16 vid_le = cpu_to_le16(vid_cpu);
+ crc = bitrev32(~stmmac_vid_crc32_le(vid_le)) >> 28;
+ hash |= (1 << crc);
+ }
+
+ ret = stmmac_update_vlan_hash(priv, priv->hw, hash, add_ctags, add_stags);
+ if (ret)
+ goto err_revert_bit;
- pmatch = vid;
- hash = 0;
- }
+ return 0;
- if (!netif_running(priv->dev))
- return 0;
+err_revert_bit:
+ if (add)
+ clear_bit(vid, active_vlans);
+ else
+ set_bit(vid, active_vlans);
- return stmmac_update_vlan_hash(priv, priv->hw, hash, pmatch, is_double);
+ return ret;
}
/* FIXME: This may need RXC to be running, but it may be called with BH
@@ -6781,37 +7255,19 @@ static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid)
{
struct stmmac_priv *priv = netdev_priv(ndev);
- unsigned int num_double_vlans;
- bool is_double = false;
int ret;
ret = pm_runtime_resume_and_get(priv->device);
if (ret < 0)
return ret;
- if (be16_to_cpu(proto) == ETH_P_8021AD)
- is_double = true;
+ ret = stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
+ if (!ret)
+ goto ok_pm_put;
- set_bit(vid, priv->active_vlans);
- num_double_vlans = priv->num_double_vlans + is_double;
- ret = stmmac_vlan_update(priv, num_double_vlans);
- if (ret) {
- clear_bit(vid, priv->active_vlans);
- goto err_pm_put;
- }
-
- if (priv->hw->num_vlan) {
- ret = stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
- if (ret) {
- clear_bit(vid, priv->active_vlans);
- stmmac_vlan_update(priv, priv->num_double_vlans);
- goto err_pm_put;
- }
- }
+ ret = stmmac_update_hw_vlan_rx_hash(priv, true, proto, vid);
- priv->num_double_vlans = num_double_vlans;
-
-err_pm_put:
+ok_pm_put:
pm_runtime_put(priv->device);
return ret;
@@ -6823,53 +7279,24 @@ err_pm_put:
static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid)
{
struct stmmac_priv *priv = netdev_priv(ndev);
- unsigned int num_double_vlans;
- bool is_double = false;
int ret;
ret = pm_runtime_resume_and_get(priv->device);
if (ret < 0)
return ret;
- if (be16_to_cpu(proto) == ETH_P_8021AD)
- is_double = true;
-
- clear_bit(vid, priv->active_vlans);
- num_double_vlans = priv->num_double_vlans - is_double;
- ret = stmmac_vlan_update(priv, num_double_vlans);
- if (ret) {
- set_bit(vid, priv->active_vlans);
- goto del_vlan_error;
- }
-
- if (priv->hw->num_vlan) {
- ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
- if (ret) {
- set_bit(vid, priv->active_vlans);
- stmmac_vlan_update(priv, priv->num_double_vlans);
- goto del_vlan_error;
- }
- }
+ ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
+ if (!ret)
+ goto ok_pm_put;
- priv->num_double_vlans = num_double_vlans;
+ ret = stmmac_update_hw_vlan_rx_hash(priv, false, proto, vid);
-del_vlan_error:
+ok_pm_put:
pm_runtime_put(priv->device);
return ret;
}
-static void stmmac_vlan_restore(struct stmmac_priv *priv)
-{
- if (!(priv->dev->features & NETIF_F_VLAN_FEATURES))
- return;
-
- if (priv->hw->num_vlan)
- stmmac_restore_hw_vlan_rx_fltr(priv, priv->dev, priv->hw);
-
- stmmac_vlan_update(priv, priv->num_double_vlans);
-}
-
static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf)
{
struct stmmac_priv *priv = netdev_priv(dev);
@@ -6894,7 +7321,7 @@ static int stmmac_xdp_xmit(struct net_device *dev, int num_frames,
int i, nxmit = 0;
int queue;
- if (unlikely(test_bit(STMMAC_DOWN, &priv->state)))
+ if (!netif_running(dev))
return -ENETDOWN;
if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
@@ -7085,7 +7512,6 @@ int stmmac_xdp_open(struct net_device *dev)
struct stmmac_rx_queue *rx_q;
struct stmmac_tx_queue *tx_q;
u32 buf_size;
- bool sph_en;
u32 chan;
int ret;
@@ -7111,9 +7537,6 @@ int stmmac_xdp_open(struct net_device *dev)
stmmac_disable_dma_irq(priv, priv->ioaddr, chan, 1, 1);
}
- /* Adjust Split header */
- sph_en = (priv->hw->rx_csum > 0) && priv->sph_active;
-
/* DMA RX Channel Configuration */
for (chan = 0; chan < rx_cnt; chan++) {
rx_q = &priv->dma_conf.rx_queue[chan];
@@ -7138,7 +7561,7 @@ int stmmac_xdp_open(struct net_device *dev)
rx_q->queue_index);
}
- stmmac_enable_sph(priv, priv->ioaddr, sph_en, chan);
+ stmmac_enable_sph(priv, priv->ioaddr, priv->sph_active, chan);
}
/* DMA TX Channel Configuration */
@@ -7190,8 +7613,7 @@ int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags)
struct stmmac_tx_queue *tx_q;
struct stmmac_channel *ch;
- if (test_bit(STMMAC_DOWN, &priv->state) ||
- !netif_carrier_ok(priv->dev))
+ if (!netif_carrier_ok(dev) || !netif_running(dev))
return -ENETDOWN;
if (!stmmac_xdp_is_enabled(priv))
@@ -7295,35 +7717,323 @@ static const struct net_device_ops stmmac_netdev_ops = {
.ndo_hwtstamp_set = stmmac_hwtstamp_set,
};
+/**
+ * stmmac_reset_subtask - Controller reset subtask
+ * @priv: driver private structure
+ * Description:
+ * Perform full controller reset due to some unrecoverable error. Normally
+ * it's caused by some out-side system problem: Tx timeout detected by the
+ * network core, fatal DMA-bus errors, etc.
+ */
static void stmmac_reset_subtask(struct stmmac_priv *priv)
{
- if (!test_and_clear_bit(STMMAC_RESET_REQUESTED, &priv->state))
- return;
- if (test_bit(STMMAC_DOWN, &priv->state))
+ /* Controller is in inactive state. Reset will be done on open */
+ if (!netif_running(priv->dev))
return;
- netdev_err(priv->dev, "Reset adapter.\n");
+ netdev_err(priv->dev, "Reset adapter\n");
- rtnl_lock();
netif_trans_update(priv->dev);
- while (test_and_set_bit(STMMAC_RESETING, &priv->state))
- usleep_range(1000, 2000);
- set_bit(STMMAC_DOWN, &priv->state);
dev_close(priv->dev);
dev_open(priv->dev, NULL);
- clear_bit(STMMAC_DOWN, &priv->state);
- clear_bit(STMMAC_RESETING, &priv->state);
- rtnl_unlock();
}
-static void stmmac_service_task(struct work_struct *work)
+/**
+ * stmmac_reset_rx_subtask - Rx DMA-engine reset subtask
+ * @priv: driver private structure
+ * @queue: RX queue index
+ * Description:
+ * Perform Rx-queue reset procedure. Normally it's caused by an upstream
+ * DMA-bus error response. In anyway the DMA is implied to be stopped
+ * due to that and the affected DMA-descriptor is closed with an error status.
+ */
+static void stmmac_reset_rx_subtask(struct stmmac_priv *priv, u32 queue)
+{
+ struct stmmac_rx_queue *rx_q;
+ struct napi_struct *napi;
+ int ret;
+
+ /* Skip if controller is inactive (queue will be reset on open anyway) */
+ if (!netif_running(priv->dev))
+ return;
+
+ netdev_err(priv->dev, "Reset Rx queue %u\n", queue);
+
+ rx_q = &priv->dma_conf.rx_queue[queue];
+ napi = rx_q->xsk_pool ? &priv->channel[queue].rxtx_napi :
+ &priv->channel[queue].rx_napi;
+
+ /* Make sure no receive procedure along the way */
+ napi_disable(napi);
+
+ /* Rx DMA must be stopped for sure to proceed */
+ stmmac_stop_rx_dma(priv, queue);
+
+ /* Free Rx SKBs and data buffers */
+ if (rx_q->xsk_pool)
+ dma_free_rx_xskbufs(priv, &priv->dma_conf, queue);
+ else
+ dma_free_rx_skbufs(priv, &priv->dma_conf, queue);
+
+ /* Clean up all the Tx descriptor and reallocate the buffers */
+ stmmac_clear_rx_descriptors(priv, &priv->dma_conf, queue);
+
+ if (rx_q->xsk_pool) {
+ stmmac_alloc_rx_buffers_zc(priv, &priv->dma_conf, queue);
+ } else {
+ ret = stmmac_alloc_rx_buffers(priv, &priv->dma_conf, queue, GFP_KERNEL);
+ if (ret) {
+ netdev_err(priv->dev, "Failed to alloc Rx queue %u buffers\n",
+ queue);
+ return;
+ }
+ }
+
+ /* Reset the desc ring head and tail addresses */
+ stmmac_reset_rx_queue(priv, queue);
+
+ stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+ rx_q->dma_rx_phy, queue);
+
+ rx_q->rx_tail_addr = rx_q->dma_rx_phy +
+ (rx_q->buf_alloc_num *
+ sizeof(struct dma_desc));
+ stmmac_set_rx_tail_ptr(priv, priv->ioaddr,
+ rx_q->rx_tail_addr, queue);
+
+ /* Start the Rx DMA back */
+ stmmac_start_rx_dma(priv, queue);
+
+ priv->xstats.rx_errors++;
+
+ napi_enable(napi);
+}
+
+/**
+ * stmmac_reset_tx_subtask - Tx DMA-engine reset subtask
+ * @priv: driver private structure
+ * @queue: TX queue index
+ * Description:
+ * Perform Tx-queue reset procedure. Normally it's caused by an upstream
+ * DMA-bus error response. In anyway the DMA is implied to be stopped
+ * due to that and the affected DMA-descriptor is closed with an error status.
+ */
+static void stmmac_reset_tx_subtask(struct stmmac_priv *priv, u32 queue)
+{
+ struct stmmac_tx_queue *tx_q;
+ struct netdev_queue *nq;
+
+ /* Skip if controller is inactive (queue will be reset on open anyway) */
+ if (!netif_running(priv->dev))
+ return;
+
+ netdev_err(priv->dev, "Reset Tx queue %u\n", queue);
+
+ tx_q = &priv->dma_conf.tx_queue[queue];
+
+ /* Make sure no transfer along the way */
+ nq = netdev_get_tx_queue(priv->dev, queue);
+ __netif_tx_lock_bh(nq);
+
+ /* Tx DMA must be stopped for sure to proceed */
+ stmmac_stop_tx_dma(priv, queue);
+
+ /* Free pending Tx SKBs and mapped buffers */
+ dma_free_tx_skbufs(priv, &priv->dma_conf, queue);
+
+ /* Clean up all the Tx descriptor for further transfers */
+ stmmac_clear_tx_descriptors(priv, &priv->dma_conf, queue);
+
+ /* Reset the desc ring head and tail pointers */
+ stmmac_reset_tx_queue(priv, queue);
+ stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+ tx_q->dma_tx_phy, queue);
+
+ /* Start the Tx DMA back */
+ stmmac_start_tx_dma(priv, queue);
+
+ priv->xstats.tx_errors++;
+
+ __netif_tx_unlock_bh(nq);
+}
+
+/**
+ * stmmac_drop_rtc_subtask - Rx DMA-threshold reduction subtask
+ * @priv: driver private structure
+ * @queue: TX queue index
+ * Description:
+ * Perform Rx-queue DMA-threshold decrease procedure. Normally it's caused
+ * by the Rx FIFO overflow due to too slow DMA-interface so one can't keep up
+ * with the upcoming traffic. It's applicable for the DMA threshold mode
+ * only (in opposite to the store-and-forward mode).
+ */
+static void stmmac_drop_rtc_subtask(struct stmmac_priv *priv, u32 queue)
+{
+ struct stmmac_rx_queue *rx_q;
+ int rxfifosz;
+ u8 rxqmode;
+
+ rx_q = &priv->dma_conf.rx_queue[queue];
+
+ if (rx_q->rtc == SF_DMA_MODE) {
+ netdev_err(priv->dev, "Unexpected drop RTC event on Q%u\n",
+ queue);
+ return;
+ }
+
+ rxfifosz = stmmac_get_rxfifosz(priv);
+
+ /* Lower limits: DW GMAC/GMAC4 - 32, DW XGMAC/XLGMAC - 64 */
+ if (rx_q->rtc <= 32) {
+ netdev_err_once(priv->dev, "Rx TC reached limit %u on Q%u\n",
+ rx_q->rtc, queue);
+ return;
+ } else if (rx_q->rtc > rxfifosz) {
+ rx_q->rtc = rxfifosz;
+ }
+
+ rx_q->rtc -= 32;
+
+ netdev_warn(priv->dev, "DMA Rx threshold reduced to %u on Q%u\n",
+ rx_q->rtc, queue);
+
+ rxqmode = priv->plat->rx_queues_cfg[queue].mode_to_use;
+
+ stmmac_dma_rx_mode(priv, priv->ioaddr, rx_q->rtc, queue, rxfifosz,
+ rxqmode, priv->dev->features & NETIF_F_RXALL);
+}
+
+/**
+ * stmmac_bump_ttc_subtask - Tx DMA-threshold increase subtask
+ * @priv: driver private structure
+ * @queue: TX queue index
+ * Description:
+ * Perform Tx-queue DMA-threshold increase procedure. Normally it's caused
+ * by the Tx FIFO underrun due to too slow DMA-interface so one can't keep up
+ * with the outgoing traffic. Obviously it's applicable for the DMA threshold
+ * mode only (in opposite to the store-and-forward mode).
+ */
+static void stmmac_bump_ttc_subtask(struct stmmac_priv *priv, u32 queue)
+{
+ struct stmmac_tx_queue *tx_q;
+ u8 txqmode, addend;
+ int txfifosz;
+
+ tx_q = &priv->dma_conf.tx_queue[queue];
+
+ if (tx_q->ttc == SF_DMA_MODE) {
+ netdev_err(priv->dev, "Unexpected bump TTC event on Q%u\n",
+ queue);
+ return;
+ }
+
+ txfifosz = stmmac_get_txfifosz(priv);
+
+ /* 64-bytes step threshold: DW GMAC - 64, DW QoS/XGMAC/etc - 128.
+ * 128-bytes step threshold: DW QoS/XGMAC/etc - 256.
+ */
+ addend = (tx_q->ttc < 128) ? 32 :
+ (tx_q->ttc < 256) ? 64 : 128;
+
+ /* Upper limits: DW GMAC/QoS - 256, DW XGMAC/XLGMAC - 512 or FIFO depth */
+ if (tx_q->ttc + addend > min(512, txfifosz)) {
+ netdev_err_once(priv->dev, "Tx TC reached limit %u on Q%u\n",
+ tx_q->ttc, queue);
+ return;
+ }
+
+ tx_q->ttc += addend;
+
+ netdev_warn(priv->dev, "DMA Tx threshold raised to %u on Q%u\n",
+ tx_q->ttc, queue);
+
+ txqmode = priv->plat->tx_queues_cfg[queue].mode_to_use;
+
+ stmmac_dma_tx_mode(priv, priv->ioaddr, tx_q->ttc, queue, txfifosz, txqmode);
+}
+
+/**
+ * stmmac_resume_tx_subtask - Tx DMA-engine resume subtask
+ * @priv: driver private structure
+ * Description:
+ * Resume Tx-queue. Normally it's caused by some minor internal errors
+ * like Jabber timeout. The respective frame might have been corrupted and
+ * dropped, but the Tx procedure can be safely resumed.
+ */
+static void stmmac_resume_tx_subtask(struct stmmac_priv *priv, u32 queue)
+{
+ /* Skip if controller is inactive (nothing to resume) */
+ if (!netif_running(priv->dev))
+ return;
+
+ /* Start the Tx DMA back */
+ stmmac_start_tx_dma(priv, queue);
+}
+
+/**
+ * stmmac_state_work - MAC state-change handler
+ * @work: worker structure
+ * Description: Execute the state-change tasks based on the state flags set.
+ */
+static void stmmac_state_work(struct work_struct *work)
{
struct stmmac_priv *priv = container_of(work, struct stmmac_priv,
- service_task);
+ state_work);
+ u32 maxq, queue;
- stmmac_reset_subtask(priv);
- clear_bit(STMMAC_SERVICE_SCHED, &priv->state);
+ /* Synchronize with possible on-going ndos */
+ rtnl_lock();
+
+ maxq = max(priv->plat->rx_queues_to_use, priv->plat->tx_queues_to_use);
+ for (queue = 0; queue < maxq; queue++) {
+ struct stmmac_channel *ch = &priv->channel[queue];
+
+ if (stmmac_clr_state(ch, RESET_RX))
+ stmmac_reset_rx_subtask(priv, queue);
+
+ if (stmmac_clr_state(ch, RESET_TX))
+ stmmac_reset_tx_subtask(priv, queue);
+
+ if (stmmac_clr_state(ch, DROP_RTC))
+ stmmac_drop_rtc_subtask(priv, queue);
+
+ if (stmmac_clr_state(ch, BUMP_TTC))
+ stmmac_bump_ttc_subtask(priv, queue);
+
+ if (stmmac_clr_state(ch, RESUME_TX))
+ stmmac_resume_tx_subtask(priv, queue);
+ }
+
+ if (stmmac_clr_state(priv, RESET))
+ stmmac_reset_subtask(priv);
+
+ rtnl_unlock();
+}
+
+/**
+ * stmmac_state_init - Init the MAC custom state interface
+ * @priv: driver private structure
+ * Description: initialize the dynamic MAC state interface. For now
+ * it just concerns the device-specific workqueue allocation and the states
+ * change handling work initialization.
+ */
+static int stmmac_state_init(struct stmmac_priv *priv)
+{
+ priv->state_wq = create_singlethread_workqueue("stmmac-wq");
+ if (!priv->state_wq) {
+ dev_err(priv->device, "failed to create workqueue\n");
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&priv->state_work, stmmac_state_work);
+
+ return 0;
+}
+
+static void stmmac_state_clear(struct stmmac_priv *priv)
+{
+ destroy_workqueue(priv->state_wq);
}
static void stmmac_print_actphyif(struct stmmac_priv *priv)
@@ -7374,8 +8084,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
/* dwmac-sun8i only work in chain mode */
if (priv->plat->flags & STMMAC_FLAG_HAS_SUN8I)
- chain_mode = 1;
- priv->chain_mode = chain_mode;
+ priv->chain_mode = 1;
+ else
+ priv->chain_mode = chain_mode;
/* Initialize HW Interface */
ret = stmmac_hwif_init(priv);
@@ -7402,31 +8113,39 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
ilog2(priv->hw->multicast_filter_bins);
}
+ if (!priv->plat->rx_fifo_size)
+ priv->plat->rx_fifo_size = priv->dma_cap.rx_fifo_size;
+ if (!priv->plat->tx_fifo_size)
+ priv->plat->tx_fifo_size = priv->dma_cap.tx_fifo_size;
+
/* TXCOE doesn't work in thresh DMA mode */
if (priv->plat->force_thresh_dma_mode)
priv->plat->tx_coe = 0;
else
priv->plat->tx_coe = priv->dma_cap.tx_coe;
- /* In case of GMAC4 rx_coe is from HW cap register. */
- priv->plat->rx_coe = priv->dma_cap.rx_coe;
-
- if (priv->dma_cap.rx_coe_type2)
- priv->plat->rx_coe = STMMAC_RX_COE_TYPE2;
- else if (priv->dma_cap.rx_coe_type1)
- priv->plat->rx_coe = STMMAC_RX_COE_TYPE1;
+ /* RXCOE runtime check (for backward compatibility) */
+ if (priv->dma_cap.rx_coe || priv->dma_cap.rx_coe_type2) {
+ priv->plat->rx_coe =
+ (stmmac_rx_ipc(priv, priv->hw, true) == 1);
+ }
stmmac_print_actphyif(priv);
} else {
dev_info(priv->device, "No HW DMA feature register supported\n");
}
- if (priv->plat->rx_coe) {
- priv->hw->rx_csum = priv->plat->rx_coe;
+ /* Set the data bus width of the system interface to 16 bytes by
+ * default. It will apply the strongest constraint on the Tx FIFO size
+ * and the Rx DMA buffers alignment, but at least we'll be on a safe
+ * side from possible HW UBs.
+ */
+ if (!priv->plat->data_width)
+ priv->plat->data_width = 16;
+
+ if (priv->plat->rx_coe)
dev_info(priv->device, "RX Checksum Offload Engine supported\n");
- if (priv->synopsys_id < DWMAC_CORE_4_00)
- dev_info(priv->device, "COE Type %d\n", priv->hw->rx_csum);
- }
+
if (priv->plat->tx_coe)
dev_info(priv->device, "TX Checksum insertion supported\n");
@@ -7503,12 +8222,154 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
return 0;
}
+/**
+ * stmmac_pbl_adjust - DMA PBL adjust and verify.
+ * @pbl: DMA PBL to verify
+ * @factor: DMA PBL x8/x4 factor
+ * @fifodp: MTL FIFO depth
+ * Description: adjust and verify the passed DMA PBL in accordance with the
+ * HW requirements.
+ * Return : 0 on success and errno if invalid PBL specified.
+ */
+static int stmmac_pbl_adjust(unsigned int *pbl, unsigned int factor,
+ unsigned int fifodp)
+{
+ const unsigned int min_pbl = 1, max_pbl = 32;
+
+ /* Make sure PBL is from the set [1, 2, 4, 8, 16, 32] otherwise UB */
+ *pbl = rounddown_pow_of_two(*pbl);
+ if (*pbl < min_pbl || *pbl > max_pbl)
+ return -EINVAL;
+
+ /* Skip legacy platforms with no Rx/Tx MTL FIFO depth data */
+ if (!fifodp)
+ return 0;
+
+ /* Make sure PBL is within the allowed range - less or equal to half
+ * the Queue depth (MTL FIFO size in terms of DMA beats).
+ */
+ if (*pbl * factor > fifodp / 2)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * stmmac_dma_verify - verify the DMA settings
+ * @priv: driver private structure
+ * Description: make sure the platform-specific DMA-config is valid. If it's
+ * not then either adjust it in some extent or return an error.
+ * Return: 0 on success or errno if invalid DMA-config detected.
+ */
+static int stmmac_dma_verify(struct stmmac_priv *priv)
+{
+ struct stmmac_dma_cfg *dma_cfg = priv->plat->dma_cfg;
+ unsigned int factor, rxfifodp, txfifodp;
+ unsigned int rxmaxq, txmaxq;
+ int ret;
+
+ /* It's mandatory to have the DMA-config specified */
+ if (!dma_cfg) {
+ dev_err(priv->device, "Missing DMA configuration\n");
+ return -EINVAL;
+ }
+
+ /* Make sure at least 1-beat PBL is setup */
+ if (!dma_cfg->pbl) {
+ dev_warn(priv->device, "No PBL specified, init with minimal 1\n");
+ dma_cfg->pbl = 1;
+ }
+
+ /* Activate extended DMA descriptors if requested */
+ if (priv->extend_desc)
+ priv->plat->dma_cfg->atds = 1;
+
+ /* Only DW QoS Eth v5.20 onwards IPs support DMA-desc prefetch */
+ if (priv->synopsys_id < DWMAC_CORE_5_20)
+ priv->plat->dma_cfg->dche = false;
+
+ /* DMA PBL multiplier had been x4 prior DW GMAC v3.50a */
+ if ((priv->plat->core_type == DWMAC_CORE_GMAC &&
+ priv->synopsys_id >= DWMAC_CORE_3_50) ||
+ dwmac_is_xmac(priv->plat->core_type)) {
+ factor = dma_cfg->pblx8 ? 8 : 1;
+ } else {
+ factor = dma_cfg->pblx8 ? 4 : 1;
+ }
+
+ /* Make sure DMA PBLs are valid and don't exceed HW constraints at
+ * least for the initially activated queues.
+ */
+ rxfifodp = stmmac_get_rxfifosz(priv) / priv->plat->data_width;
+ txfifodp = stmmac_get_txfifosz(priv) / priv->plat->data_width;
+ ret = stmmac_pbl_adjust(&dma_cfg->pbl, factor, min(rxfifodp, txfifodp));
+ if (ret) {
+ dev_err(priv->device, "Invalid DMA PBL specified\n");
+ return ret;
+ }
+
+ if (dma_cfg->txpbl) {
+ ret = stmmac_pbl_adjust(&dma_cfg->txpbl, factor, txfifodp);
+ if (ret) {
+ dev_err(priv->device, "Invalid Tx DMA PBL specified\n");
+ return ret;
+ }
+ }
+
+ if (dma_cfg->rxpbl) {
+ ret = stmmac_pbl_adjust(&dma_cfg->rxpbl, factor, rxfifodp);
+ if (ret) {
+ dev_err(priv->device, "Invalid Rx DMA PBL specified\n");
+ return ret;
+ }
+ }
+
+ /* Make sure the amount of the active queues never cause the PBL
+ * exceeding the half FIFO depth constraint.
+ */
+ priv->dma_conf.rx_queue_max = priv->dma_cap.number_rx_queues;
+ priv->dma_conf.tx_queue_max = priv->dma_cap.number_tx_queues;
+ if (dwmac_is_xmac(priv->plat->core_type)) {
+ rxfifodp = priv->plat->rx_fifo_size / priv->plat->data_width / 2;
+ rxmaxq = rxfifodp / ((dma_cfg->rxpbl ?: dma_cfg->pbl) * factor);
+ txfifodp = priv->plat->tx_fifo_size / priv->plat->data_width / 2;
+ txmaxq = txfifodp / ((dma_cfg->txpbl ?: dma_cfg->pbl) * factor);
+ } else {
+ rxmaxq = priv->plat->rx_queues_to_use;
+ txmaxq = priv->plat->tx_queues_to_use;
+ }
+
+ if (priv->dma_conf.rx_queue_max > rxmaxq) {
+ priv->dma_conf.rx_queue_max = rxmaxq;
+ dev_warn(priv->device, "Reduce Rx queues max to %u due to PBL\n",
+ rxmaxq);
+ } else if (!priv->dma_conf.rx_queue_max) {
+ priv->dma_conf.rx_queue_max = rxmaxq;
+ }
+
+ if (priv->dma_conf.tx_queue_max > txmaxq) {
+ priv->dma_conf.tx_queue_max = txmaxq;
+ dev_warn(priv->device, "Reduce Tx queues max to %u due to PBL\n",
+ txmaxq);
+ } else if (!priv->dma_conf.tx_queue_max) {
+ priv->dma_conf.tx_queue_max = txmaxq;
+ }
+
+ /* Initialize the default Tx/Rx DMA ring size */
+ priv->dma_conf.dma_tx_size = DMA_DEFAULT_TX_SIZE;
+ priv->dma_conf.dma_rx_size = DMA_DEFAULT_RX_SIZE;
+
+ return 0;
+}
+
static void stmmac_napi_add(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
u32 queue, maxq;
+ bool perch;
maxq = max(priv->plat->rx_queues_to_use, priv->plat->tx_queues_to_use);
+ perch = priv->plat->dma_cfg->multi_msi_en;
for (queue = 0; queue < maxq; queue++) {
struct stmmac_channel *ch = &priv->channel[queue];
@@ -7517,17 +8378,29 @@ static void stmmac_napi_add(struct net_device *dev)
ch->index = queue;
spin_lock_init(&ch->lock);
+ /* Non-XDP multi-queue setup for common and per-channel IRQs */
if (queue < priv->plat->rx_queues_to_use) {
netif_napi_add(dev, &ch->rx_napi, stmmac_napi_poll_rx);
+ netif_napi_set_irq(&ch->rx_napi,
+ perch ? priv->rx_irq[queue] : dev->irq);
+ netif_queue_set_napi(dev, queue, NETDEV_QUEUE_TYPE_RX,
+ &ch->rx_napi);
}
if (queue < priv->plat->tx_queues_to_use) {
- netif_napi_add_tx(dev, &ch->tx_napi,
- stmmac_napi_poll_tx);
+ netif_napi_add_tx(dev, &ch->tx_napi, stmmac_napi_poll_tx);
+ netif_napi_set_irq(&ch->tx_napi,
+ perch ? priv->tx_irq[queue] : dev->irq);
+ netif_queue_set_napi(dev, queue, NETDEV_QUEUE_TYPE_TX,
+ &ch->tx_napi);
}
+
+ /* XDP multi-queue setup. Set Rx-IRQ as most probable NAPI starter */
if (queue < priv->plat->rx_queues_to_use &&
queue < priv->plat->tx_queues_to_use) {
netif_napi_add(dev, &ch->rxtx_napi,
stmmac_napi_poll_rxtx);
+ netif_napi_set_irq(&ch->rxtx_napi,
+ perch ? priv->rx_irq[queue] : dev->irq);
}
}
}
@@ -7542,10 +8415,16 @@ static void stmmac_napi_del(struct net_device *dev)
for (queue = 0; queue < maxq; queue++) {
struct stmmac_channel *ch = &priv->channel[queue];
- if (queue < priv->plat->rx_queues_to_use)
+ if (queue < priv->plat->rx_queues_to_use) {
+ netif_queue_set_napi(dev, queue, NETDEV_QUEUE_TYPE_RX,
+ NULL);
netif_napi_del(&ch->rx_napi);
- if (queue < priv->plat->tx_queues_to_use)
+ }
+ if (queue < priv->plat->tx_queues_to_use) {
+ netif_queue_set_napi(dev, queue, NETDEV_QUEUE_TYPE_TX,
+ NULL);
netif_napi_del(&ch->tx_napi);
+ }
if (queue < priv->plat->rx_queues_to_use &&
queue < priv->plat->tx_queues_to_use) {
netif_napi_del(&ch->rxtx_napi);
@@ -7556,7 +8435,12 @@ static void stmmac_napi_del(struct net_device *dev)
int stmmac_reinit_queues(struct net_device *dev, u32 rx_cnt, u32 tx_cnt)
{
struct stmmac_priv *priv = netdev_priv(dev);
- int ret = 0, i;
+ int ret, i;
+
+ ret = stmmac_verify_max_mtu(priv, dev->features & NETIF_F_CSUM_MASK,
+ tx_cnt, rx_cnt);
+ if (ret)
+ return ret;
if (netif_running(dev))
stmmac_release(dev);
@@ -7565,6 +8449,10 @@ int stmmac_reinit_queues(struct net_device *dev, u32 rx_cnt, u32 tx_cnt)
priv->plat->rx_queues_to_use = rx_cnt;
priv->plat->tx_queues_to_use = tx_cnt;
+
+ stmmac_set_max_mtu(priv, dev->features & NETIF_F_CSUM_MASK,
+ tx_cnt, rx_cnt);
+
if (!netif_is_rxfh_configured(dev))
for (i = 0; i < ARRAY_SIZE(priv->rss.table); i++)
priv->rss.table[i] = ethtool_rxfh_indir_default(i,
@@ -7626,6 +8514,160 @@ static const struct xdp_metadata_ops stmmac_xdp_metadata_ops = {
.xmo_rx_timestamp = stmmac_xdp_rx_timestamp,
};
+#ifdef CONFIG_INET
+/**
+ * stmmac_update_arp_addr - add/remove IPv4 address to ARP-offload
+ * @priv: private structure
+ * @add: add/remove flag
+ * @ifa: interface address
+ * Description: initialize ARP-offload with new IPv4 address if it hasn't
+ * been initialized yet, replace being removed primary IPv4 address with
+ * a one from the inet-device list, disable ARP-offload engine if the last
+ * primary IPv4 address has been removed.
+ * Return: 0 on success and negative error on a failure
+ */
+static int stmmac_update_arp_addr(struct stmmac_priv *priv, bool add,
+ const struct in_ifaddr *ifa)
+{
+ const struct in_ifaddr *ifai;
+ struct in_device *in_dev;
+ __be32 new_addr = -1;
+ int ret;
+
+ if (add) {
+ if (priv->arp_addr != -1)
+ return 0;
+
+ /* New primary address with no ARP-offload initialized yet */
+ priv->arp_addr = ifa->ifa_address;
+ goto set_addr;
+ } else if (priv->arp_addr == -1 || priv->arp_addr != ifa->ifa_address) {
+ return 0;
+ }
+
+ in_dev = __in_dev_get_rtnl(priv->dev);
+ if (!in_dev) {
+ priv->arp_addr = -1;
+ goto set_addr;
+ }
+
+ /* Replace being removed primary address or disable ARP-offload */
+ in_dev_for_each_ifa_rtnl(ifai, in_dev) {
+ if (ifai->ifa_flags & IFA_F_SECONDARY)
+ continue;
+
+ if (ifai->ifa_address == priv->arp_addr) {
+ goto keep_addr;
+ } else if (new_addr == -1) {
+ add = true;
+ new_addr = ifai->ifa_address;
+ }
+ }
+
+ priv->arp_addr = new_addr;
+
+set_addr:
+ ret = stmmac_set_arp_offload(priv, priv->hw, add, ntohl(priv->arp_addr));
+ if (ret)
+ return ret;
+
+keep_addr:
+ return 0;
+}
+
+/**
+ * stmmac_set_arp_inetaddr_cb - setup ARP-offload on IP-address change event
+ * @nb: notifier block handler
+ * @event: event caused the notifier call
+ * @ptr: even-specific data (in_ifaddr)
+ * Description: make sure the event belongs to the STMMAC device, the subjected
+ * IPv4 address is a primary address and set/clear/replace one.
+ * Return: notify event status
+ */
+static int stmmac_set_arp_inetaddr_cb(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ const struct in_ifaddr *ifa = ptr;
+ struct stmmac_priv *priv;
+ struct net_device *ndev;
+ int ret = 0;
+
+ ndev = ifa->ifa_dev->dev;
+
+ if (is_vlan_dev(ndev))
+ ndev = vlan_dev_real_dev(ndev);
+
+ if (ndev->netdev_ops != &stmmac_netdev_ops)
+ return NOTIFY_DONE;
+
+ priv = netdev_priv(ndev);
+ if (!priv)
+ return NOTIFY_DONE;
+
+ if (ifa->ifa_flags & IFA_F_SECONDARY)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UP:
+ ret = stmmac_update_arp_addr(priv, true, ifa);
+ break;
+ case NETDEV_DOWN:
+ ret = stmmac_update_arp_addr(priv, false, ifa);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return notifier_from_errno(ret);
+}
+
+/**
+ * stmmac_arp_setup - initialize ARP infrastructure
+ * @priv: private structure
+ * Description: initialize and register inet-address change callback
+ * for the ARP offload feature.
+ * Return: 0 on success and negative error on a failure
+ */
+static int stmmac_arp_setup(struct stmmac_priv *priv)
+{
+ int ret;
+
+ if (!priv->dma_cap.arpoffsel)
+ return 0;
+
+ priv->arp_addr = -1;
+ priv->arp_cb.notifier_call = stmmac_set_arp_inetaddr_cb;
+ ret = register_inetaddr_notifier(&priv->arp_cb);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * stmmac_arp_clean - cleanup ARP infrastructure
+ * @priv: private structure
+ * Description: cleanup inet-address change callback responsible for
+ * the ARP offload IP-address setup.
+ */
+static void stmmac_arp_clean(struct stmmac_priv *priv)
+{
+ if (!priv->dma_cap.arpoffsel)
+ return;
+
+ unregister_inetaddr_notifier(&priv->arp_cb);
+}
+#else
+static int stmmac_arp_setup(struct stmmac_priv *priv)
+{
+ priv->arp_addr = -1;
+
+ return 0;
+}
+
+static void stmmac_arp_clean(struct stmmac_priv *priv) {}
+#endif
+
static int stmmac_dl_ts_coarse_set(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
@@ -7756,7 +8798,6 @@ static int __stmmac_dvr_probe(struct device *device,
{
struct net_device *ndev = NULL;
struct stmmac_priv *priv;
- u32 rxq;
int i, ret = 0;
ndev = devm_alloc_etherdev_mqs(device, sizeof(struct stmmac_priv),
@@ -7812,16 +8853,6 @@ static int __stmmac_dvr_probe(struct device *device,
if (!priv->af_xdp_zc_qps)
return -ENOMEM;
- /* Allocate workqueue */
- priv->wq = create_singlethread_workqueue("stmmac_wq");
- if (!priv->wq) {
- dev_err(priv->device, "failed to create workqueue\n");
- ret = -ENOMEM;
- goto error_wq_init;
- }
-
- INIT_WORK(&priv->service_task, stmmac_service_task);
-
timer_setup(&priv->eee_ctrl_timer, stmmac_eee_ctrl_timer, 0);
/* Override with kernel parameters if supplied XXX CRS XXX
@@ -7848,15 +8879,20 @@ static int __stmmac_dvr_probe(struct device *device,
/* Wait a bit for the reset to take effect */
udelay(10);
+ /* Init basic device/driver features state */
+ ret = stmmac_state_init(priv);
+ if (ret)
+ goto error_state_init;
+
/* Init MAC and get the capabilities */
ret = stmmac_hw_init(priv);
if (ret)
goto error_hw_init;
- /* Only DWMAC core version 5.20 onwards supports HW descriptor prefetch.
- */
- if (priv->synopsys_id < DWMAC_CORE_5_20)
- priv->plat->dma_cfg->dche = false;
+ /* Verify DMA config */
+ ret = stmmac_dma_verify(priv);
+ if (ret)
+ goto error_hw_init;
stmmac_check_ether_addr(priv);
@@ -7865,29 +8901,34 @@ static int __stmmac_dvr_probe(struct device *device,
ndev->xdp_metadata_ops = &stmmac_xdp_metadata_ops;
ndev->xsk_tx_metadata_ops = &stmmac_xsk_tx_metadata_ops;
- ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
- NETIF_F_RXCSUM;
ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
NETDEV_XDP_ACT_XSK_ZEROCOPY;
- ret = stmmac_tc_init(priv, priv);
- if (!ret) {
- ndev->hw_features |= NETIF_F_HW_TC;
+ if (priv->plat->tx_coe)
+ ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+
+ if (priv->plat->rx_coe)
+ ndev->hw_features |= NETIF_F_RXCSUM;
+
+ if (priv->dma_cap.frpsel || priv->dma_cap.av || priv->dma_cap.l3l4fnum ||
+ priv->dma_cap.tbssel || priv->dma_cap.estsel) {
+ ret = stmmac_tc_init(priv, priv);
+ if (!ret)
+ ndev->hw_features |= NETIF_F_HW_TC;
}
if ((priv->plat->flags & STMMAC_FLAG_TSO_EN) && (priv->dma_cap.tsoen)) {
ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
if (priv->plat->core_type == DWMAC_CORE_GMAC4)
ndev->hw_features |= NETIF_F_GSO_UDP_L4;
- priv->tso = true;
+ netif_set_tso_max_size(ndev, TSO_MAX_LOAD_SIZE);
dev_info(priv->device, "TSO feature enabled\n");
}
if (priv->dma_cap.sphen &&
!(priv->plat->flags & STMMAC_FLAG_SPH_DISABLE)) {
ndev->hw_features |= NETIF_F_GRO;
- priv->sph_capable = true;
- priv->sph_active = priv->sph_capable;
+ priv->sph_active = true;
dev_info(priv->device, "SPH feature enabled\n");
}
@@ -7925,56 +8966,57 @@ static int __stmmac_dvr_probe(struct device *device,
}
}
- ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA;
+ /* Initialize RSS */
+ netdev_rss_key_fill(priv->rss.key, sizeof(priv->rss.key));
+ for (i = 0; i < ARRAY_SIZE(priv->rss.table); i++) {
+ priv->rss.table[i] =
+ ethtool_rxfh_indir_default(i, priv->plat->rx_queues_to_use);
+ }
+
+ if (priv->dma_cap.rssen && priv->plat->rss_en)
+ ndev->hw_features |= NETIF_F_RXHASH;
+
+ ndev->features |= ndev->hw_features | NETIF_F_SG | NETIF_F_HIGHDMA;
+
+ /* Old DW MAC doesn't seem to provide the pass-error-frames setting */
+ if (priv->plat->core_type == DWMAC_CORE_GMAC ||
+ dwmac_is_xmac(priv->plat->core_type))
+ ndev->hw_features |= NETIF_F_RXALL;
+
+ /* Either hardware or software based FCS stripping supported */
+ ndev->hw_features |= NETIF_F_RXFCS;
+
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
#ifdef STMMAC_VLAN_TAG_USED
/* Both mac100 and gmac support receive VLAN tag detection */
ndev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX;
- if (dwmac_is_xmac(priv->plat->core_type)) {
- ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
- priv->hw->hw_vlan_en = true;
- }
- if (priv->dma_cap.vlhash) {
- ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
- ndev->features |= NETIF_F_HW_VLAN_STAG_FILTER;
+ ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX;
+
+ /* VLAN filtering is implemented for modern IP-cores only */
+ if ((priv->plat->core_type == DWMAC_CORE_GMAC &&
+ priv->synopsys_id >= DWMAC_CORE_3_70) ||
+ dwmac_is_xmac(priv->plat->core_type)) {
+ ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_FILTER;
+ ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_FILTER;
}
- if (priv->dma_cap.vlins)
+
+ /* VLAN Insertion feature supports only one type at a time */
+ if (priv->dma_cap.vlins) {
ndev->features |= NETIF_F_HW_VLAN_CTAG_TX;
+ ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_STAG_TX;
+ }
#endif
priv->msg_enable = netif_msg_init(debug, default_msg_level);
- priv->xstats.threshold = tc;
-
- /* Initialize RSS */
- rxq = priv->plat->rx_queues_to_use;
- netdev_rss_key_fill(priv->rss.key, sizeof(priv->rss.key));
- for (i = 0; i < ARRAY_SIZE(priv->rss.table); i++)
- priv->rss.table[i] = ethtool_rxfh_indir_default(i, rxq);
-
- if (priv->dma_cap.rssen && priv->plat->rss_en)
- ndev->features |= NETIF_F_RXHASH;
-
ndev->vlan_features |= ndev->features;
- /* MTU range: 46 - hw-specific max */
- ndev->min_mtu = ETH_ZLEN - ETH_HLEN;
-
- if (priv->plat->core_type == DWMAC_CORE_XGMAC)
- ndev->max_mtu = XGMAC_JUMBO_LEN;
- else if (priv->plat->enh_desc || priv->synopsys_id >= DWMAC_CORE_4_00)
- ndev->max_mtu = JUMBO_LEN;
- else
- ndev->max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN);
-
- /* Warn if the platform's maxmtu is smaller than the minimum MTU,
- * otherwise clamp the maximum MTU above to the platform's maxmtu.
- */
- if (priv->plat->maxmtu < ndev->min_mtu)
- dev_warn(priv->device,
- "%s: warning: maxmtu having invalid value (%d)\n",
- __func__, priv->plat->maxmtu);
- else if (priv->plat->maxmtu < ndev->max_mtu)
- ndev->max_mtu = priv->plat->maxmtu;
+ ndev->min_mtu = stmmac_get_min_mtu(priv);
+ ndev->max_mtu = stmmac_get_max_mtu(priv, ndev->features & NETIF_F_CSUM_MASK,
+ priv->plat->tx_queues_to_use,
+ priv->plat->rx_queues_to_use);
ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
@@ -7992,6 +9034,14 @@ static int __stmmac_dvr_probe(struct device *device,
if (!pm_runtime_enabled(device))
pm_runtime_enable(device);
+ ret = stmmac_arp_setup(priv);
+ if (ret)
+ goto error_arp_setup;
+
+ ret = stmmac_gpio_setup(priv);
+ if (ret)
+ goto error_gpio_setup;
+
ret = stmmac_mdio_register(ndev);
if (ret < 0) {
dev_err_probe(priv->device, ret,
@@ -8044,10 +9094,17 @@ error_phy_setup:
error_pcs_setup:
stmmac_mdio_unregister(ndev);
error_mdio_register:
+ stmmac_gpio_clean(priv);
+error_gpio_setup:
+ stmmac_arp_clean(priv);
+error_arp_setup:
+ pm_runtime_put_noidle(device);
+ pm_runtime_disable(device);
stmmac_napi_del(ndev);
error_hw_init:
- destroy_workqueue(priv->wq);
-error_wq_init:
+ stmmac_state_clear(priv);
+
+error_state_init:
bitmap_free(priv->af_xdp_zc_qps);
return ret;
@@ -8092,27 +9149,37 @@ void stmmac_dvr_remove(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
+ int ret;
netdev_info(priv->dev, "%s: removing driver", __func__);
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return;
unregister_netdev(ndev);
+ stmmac_arp_clean(priv);
+
#ifdef CONFIG_DEBUG_FS
stmmac_exit_fs(ndev);
#endif
stmmac_unregister_devlink(priv);
phylink_destroy(priv->phylink);
- if (priv->plat->stmmac_rst)
- reset_control_assert(priv->plat->stmmac_rst);
- reset_control_assert(priv->plat->stmmac_ahb_rst);
stmmac_pcs_clean(ndev);
stmmac_mdio_unregister(ndev);
- destroy_workqueue(priv->wq);
+ stmmac_gpio_clean(priv);
+
+ stmmac_state_clear(priv);
+
+ if (priv->plat->stmmac_rst)
+ reset_control_assert(priv->plat->stmmac_rst);
+
+ reset_control_assert(priv->plat->stmmac_ahb_rst);
+
mutex_destroy(&priv->lock);
bitmap_free(priv->af_xdp_zc_qps);
@@ -8200,6 +9267,7 @@ static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue)
tx_q->cur_tx = 0;
tx_q->dirty_tx = 0;
tx_q->mss = 0;
+ tx_q->tci = 0;
netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue));
}
@@ -8296,8 +9364,6 @@ int stmmac_resume(struct device *dev)
stmmac_set_rx_mode(ndev);
phylink_rx_clk_stop_unblock(priv->phylink);
- stmmac_vlan_restore(priv);
-
stmmac_enable_all_queues(priv);
stmmac_enable_all_dma_irq(priv);
@@ -8334,8 +9400,14 @@ static int __init stmmac_cmdline_opt(char *str)
} else if (!strncmp(opt, "phyaddr:", 8)) {
if (kstrtoint(opt + 8, 0, &phyaddr))
goto err;
- } else if (!strncmp(opt, "tc:", 3)) {
- if (kstrtoint(opt + 3, 0, &tc))
+ } else if (!strncmp(opt, "buf_sz:", 7)) {
+ if (kstrtoint(opt + 7, 0, &buf_sz))
+ goto err;
+ } else if (!strncmp(opt, "rtc:", 4)) {
+ if (kstrtoint(opt + 4, 0, &rtc))
+ goto err;
+ } else if (!strncmp(opt, "ttc:", 4)) {
+ if (kstrtoint(opt + 4, 0, &ttc))
goto err;
} else if (!strncmp(opt, "watchdog:", 9)) {
if (kstrtoint(opt + 9, 0, &watchdog))
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index a7c2496b39f28..6451435cc634b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -491,7 +491,12 @@ static u32 stmmac_clk_csr_set(struct stmmac_priv *priv)
unsigned long clk_rate;
u32 value = ~0;
- clk_rate = clk_get_rate(priv->plat->stmmac_clk);
+ /* If APB clock has been specified then it is supposed to be used
+ * to select the CSR mode. Otherwise the application clock is the
+ * source of the periodic signal for the CSR interface.
+ */
+ clk_rate = clk_get_rate(priv->plat->pclk) ?:
+ clk_get_rate(priv->plat->stmmac_clk);
/* Platform provided default clk_csr would be assumed valid
* for all other cases except for the below mentioned ones.
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c
index 88fa359ea7163..8fd6905a6831d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c
@@ -16,6 +16,16 @@
#define GMAC_ANE_LPA 0x0c /* ANE link partener ability */
#define GMAC_TBI 0x14 /* TBI extend status */
+/*
+ * RGSMII status bitfield definitions.
+ */
+#define GMAC_RGSMII_LNKMOD BIT(0)
+#define GMAC_RGSMII_SPEED_MASK GENMASK(2, 1)
+#define GMAC_RGSMII_SPEED_125 2
+#define GMAC_RGSMII_SPEED_25 1
+#define GMAC_RGSMII_SPEED_2_5 0
+#define GMAC_RGSMII_LNKSTS BIT(3)
+
static int dwmac_integrated_pcs_enable(struct phylink_pcs *pcs)
{
struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs);
@@ -36,7 +46,43 @@ static void dwmac_integrated_pcs_get_state(struct phylink_pcs *pcs,
unsigned int neg_mode,
struct phylink_link_state *state)
{
- state->link = false;
+ struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs);
+ u32 status, rgsmii;
+
+ status = readl(spcs->base + GMAC_AN_STATUS);
+
+ if (phy_interface_mode_is_8023z(state->interface)) {
+ state->link = false;
+ } else {
+ rgsmii = field_get(spcs->rgsmii_status_mask,
+ readl(spcs->rgsmii));
+
+ state->link = !!(rgsmii & GMAC_RGSMII_LNKSTS);
+ if (state->interface == PHY_INTERFACE_MODE_SGMII)
+ state->link = state->link && status & BMSR_LSTATUS;
+
+ if (state->link) {
+ state->duplex = rgsmii & GMAC_RGSMII_LNKMOD ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ switch (FIELD_GET(GMAC_RGSMII_SPEED_MASK, rgsmii)) {
+ case GMAC_RGSMII_SPEED_2_5:
+ state->speed = SPEED_10;
+ break;
+
+ case GMAC_RGSMII_SPEED_25:
+ state->speed = SPEED_100;
+ break;
+
+ case GMAC_RGSMII_SPEED_125:
+ state->speed = SPEED_1000;
+ break;
+
+ default:
+ state->link = false;
+ break;
+ }
+ }
+ }
}
static int dwmac_integrated_pcs_config(struct phylink_pcs *pcs,
@@ -47,6 +93,10 @@ static int dwmac_integrated_pcs_config(struct phylink_pcs *pcs,
{
struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs);
+ /* PCS AN capability isn't applicable for RGMII interface */
+ if (phy_interface_mode_is_rgmii(interface))
+ return 0;
+
dwmac_ctrl_ane(spcs->base, 0, 1, spcs->priv->hw->reverse_sgmii_enable);
return 0;
@@ -64,6 +114,7 @@ void stmmac_integrated_pcs_irq(struct stmmac_priv *priv, u32 status,
{
struct stmmac_pcs *spcs = priv->integrated_pcs;
u32 val = readl(spcs->base + GMAC_AN_STATUS);
+ bool link_changed = false;
if (status & PCS_ANE_IRQ) {
x->irq_pcs_ane_n++;
@@ -77,8 +128,22 @@ void stmmac_integrated_pcs_irq(struct stmmac_priv *priv, u32 status,
dev_info(priv->device, "PCS Link %s\n",
val & BMSR_LSTATUS ? "Up" : "Down");
- phylink_pcs_change(&spcs->pcs, val & BMSR_LSTATUS);
+ link_changed = true;
}
+
+ if (status & PCS_RGSMIIIS_IRQ) {
+ x->irq_rgmii_n++;
+
+ val = field_get(spcs->rgsmii_status_mask, readl(spcs->rgsmii));
+ dev_info(priv->device, "PHY Link %s\n",
+ val & GMAC_RGSMII_LNKSTS ? "Up" : "Down");
+
+ link_changed = true;
+ }
+
+ /* Let phylink re-read the state again for any supported mode */
+ if (link_changed)
+ phylink_pcs_change(&spcs->pcs, false);
}
int stmmac_integrated_pcs_get_phy_intf_sel(struct phylink_pcs *pcs,
@@ -86,25 +151,35 @@ int stmmac_integrated_pcs_get_phy_intf_sel(struct phylink_pcs *pcs,
{
if (interface == PHY_INTERFACE_MODE_SGMII)
return PHY_INTF_SEL_SGMII;
+ else if (phy_interface_mode_is_rgmii(interface))
+ return PHY_INTF_SEL_RGMII;
return -EINVAL;
}
-int stmmac_integrated_pcs_init(struct stmmac_priv *priv, unsigned int offset,
- u32 int_mask)
+int stmmac_integrated_pcs_init(struct stmmac_priv *priv,
+ const struct stmmac_pcs_info *pcs_info)
{
struct stmmac_pcs *spcs;
+ if (!priv->dma_cap.pcs && priv->dma_cap.actphyif != PHY_INTF_SEL_RGMII)
+ return 0;
+
spcs = devm_kzalloc(priv->device, sizeof(*spcs), GFP_KERNEL);
if (!spcs)
return -ENOMEM;
spcs->priv = priv;
- spcs->base = priv->ioaddr + offset;
- spcs->int_mask = int_mask;
+ spcs->base = priv->ioaddr + pcs_info->pcs_offset;
+ spcs->rgsmii = priv->ioaddr + pcs_info->rgsmii_offset;
+ spcs->rgsmii_status_mask = pcs_info->rgsmii_status_mask;
+ spcs->int_mask = pcs_info->int_mask;
spcs->pcs.ops = &dwmac_integrated_pcs_ops;
- __set_bit(PHY_INTERFACE_MODE_SGMII, spcs->pcs.supported_interfaces);
+ if (priv->dma_cap.pcs)
+ __set_bit(PHY_INTERFACE_MODE_SGMII, spcs->pcs.supported_interfaces);
+ if (priv->dma_cap.actphyif == PHY_INTF_SEL_RGMII)
+ phy_interface_set_rgmii(spcs->pcs.supported_interfaces);
priv->integrated_pcs = spcs;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h
index 23bbd4f10bf84..09e609f111b1b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h
@@ -27,9 +27,18 @@
struct stmmac_priv;
+struct stmmac_pcs_info {
+ unsigned int pcs_offset;
+ unsigned int rgsmii_offset;
+ u32 rgsmii_status_mask;
+ u32 int_mask;
+};
+
struct stmmac_pcs {
struct stmmac_priv *priv;
void __iomem *base;
+ void __iomem *rgsmii;
+ u32 rgsmii_status_mask;
u32 int_mask;
struct phylink_pcs pcs;
};
@@ -44,8 +53,8 @@ void stmmac_integrated_pcs_irq(struct stmmac_priv *priv, u32 status,
struct stmmac_extra_stats *x);
int stmmac_integrated_pcs_get_phy_intf_sel(struct phylink_pcs *pcs,
phy_interface_t interface);
-int stmmac_integrated_pcs_init(struct stmmac_priv *priv, unsigned int offset,
- u32 int_mask);
+int stmmac_integrated_pcs_init(struct stmmac_priv *priv,
+ const struct stmmac_pcs_info *pcs_info);
/**
* dwmac_ctrl_ane - To program the AN Control Register.
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 5c9fd91a1db9d..bc193b4d517b4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -40,6 +40,7 @@ static int dwmac1000_validate_mcast_bins(struct device *dev, int mcast_bins)
int x = mcast_bins;
switch (x) {
+ case 0:
case HASH_TABLE_SIZE:
case 128:
case 256:
@@ -87,24 +88,29 @@ static int dwmac1000_validate_ucast_entries(struct device *dev,
/**
* stmmac_axi_setup - parse DT parameters for programming the AXI register
* @pdev: platform device
+ * @plat: enet data
* Description:
* if required, from device-tree the AXI internal register can be tuned
* by using platform parameters.
+ * Return value:
+ * 0 on success and negative error otherwise.
*/
-static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
+static int stmmac_axi_setup(struct platform_device *pdev,
+ struct plat_stmmacenet_data *plat)
{
struct device_node *np;
struct stmmac_axi *axi;
u32 axi_blen[AXI_BLEN];
+ enum dev_dma_attr attr;
np = of_parse_phandle(pdev->dev.of_node, "snps,axi-config", 0);
if (!np)
- return NULL;
+ return 0;
axi = devm_kzalloc(&pdev->dev, sizeof(*axi), GFP_KERNEL);
if (!axi) {
of_node_put(np);
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
axi->axi_lpi_en = of_property_read_bool(np, "snps,lpi_en");
@@ -122,7 +128,15 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
stmmac_axi_blen_to_mask(&axi->axi_blen_regval, axi_blen, AXI_BLEN);
of_node_put(np);
- return axi;
+ attr = device_get_dma_attr(&pdev->dev);
+ if (attr == DEV_DMA_NOT_SUPPORTED)
+ return -ENODEV;
+
+ axi->axi_cc = (attr == DEV_DMA_COHERENT);
+
+ plat->axi = axi;
+
+ return 0;
}
/**
@@ -352,8 +366,10 @@ static int stmmac_mdio_setup(struct plat_stmmacenet_data *plat,
plat->mdio_bus_data = devm_kzalloc(dev,
sizeof(*plat->mdio_bus_data),
GFP_KERNEL);
- if (!plat->mdio_bus_data)
+ if (!plat->mdio_bus_data) {
+ of_node_put(plat->mdio_node);
return -ENOMEM;
+ }
plat->mdio_bus_data->needs_reset = true;
}
@@ -477,10 +493,30 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
goto error_put_phy;
}
+ /* Retrieve total number of supported GPIOs from the STMMAC DT-node.
+ * Amount of GPIs and GPOs will be auto-detected by the driver later.
+ */
+ of_property_read_u32(np, "ngpios", &plat->ngpios);
+
of_property_read_u32(np, "tx-fifo-depth", &plat->tx_fifo_size);
of_property_read_u32(np, "rx-fifo-depth", &plat->rx_fifo_size);
+ /* Default to 16 bytes data bus width to be on a safe side at the
+ * PBL upper limit and the Rx DMA buffer alignment calculation.
+ */
+ if (of_property_read_u32(np, "snps,data-width", &plat->data_width))
+ plat->data_width = 16;
+
+ /* Note that the max-frame-size parameter as defined in the
+ * ePAPR v1.1 spec is defined as max-frame-size, it's
+ * actually used as the IEEE definition of MAC Client
+ * data, or MTU. The ePAPR specification is confusing as
+ * the definition is max-frame-size, but usage examples
+ * are clearly MTUs
+ */
+ of_property_read_u32(np, "max-frame-size", &plat->maxmtu);
+
plat->force_sf_dma_mode =
of_property_read_bool(np, "snps,force_sf_dma_mode");
@@ -500,14 +536,6 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
of_device_is_compatible(np, "snps,dwmac-3.70a") ||
of_device_is_compatible(np, "snps,dwmac-3.72a") ||
of_device_is_compatible(np, "snps,dwmac")) {
- /* Note that the max-frame-size parameter as defined in the
- * ePAPR v1.1 spec is defined as max-frame-size, it's
- * actually used as the IEEE definition of MAC Client
- * data, or MTU. The ePAPR specification is confusing as
- * the definition is max-frame-size, but usage examples
- * are clearly MTUs
- */
- of_property_read_u32(np, "max-frame-size", &plat->maxmtu);
of_property_read_u32(np, "snps,multicast-filter-bins",
&plat->multicast_filter_bins);
of_property_read_u32(np, "snps,perfect-filter-entries",
@@ -579,7 +607,9 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
of_property_read_u32(np, "snps,ps-speed", &plat->mac_port_sel_speed);
- plat->axi = stmmac_axi_setup(pdev);
+ rc = stmmac_axi_setup(pdev, plat);
+ if (rc)
+ goto error_put_mdio;
rc = stmmac_mtl_setup(pdev, plat);
if (rc) {
@@ -700,6 +730,9 @@ EXPORT_SYMBOL_GPL(stmmac_pltfr_find_clk);
int stmmac_get_platform_resources(struct platform_device *pdev,
struct stmmac_resources *stmmac_res)
{
+ char irq_name[IFNAMSIZ];
+ int irq, i;
+
memset(stmmac_res, 0, sizeof(*stmmac_res));
/* Get IRQ information early to have an ability to ask for deferred
@@ -733,6 +766,27 @@ int stmmac_get_platform_resources(struct platform_device *pdev,
dev_info(&pdev->dev, "IRQ sfty not found\n");
}
+ /* Request optional MTL per-queue IRQs. Note in fact these are the
+ * DMA per-channel IRQs, the driver just maps them one-on-one.
+ */
+ for (i = 0; i < MTL_MAX_RX_QUEUES; i++) {
+ snprintf(irq_name, IFNAMSIZ, "rx-queue-%d", i);
+ irq = platform_get_irq_byname_optional(pdev, irq_name);
+ if (irq < 0)
+ break;
+
+ stmmac_res->rx_irq[i] = irq;
+ }
+
+ for (i = 0; i < MTL_MAX_TX_QUEUES; i++) {
+ snprintf(irq_name, IFNAMSIZ, "tx-queue-%d", i);
+ irq = platform_get_irq_byname_optional(pdev, irq_name);
+ if (irq < 0)
+ break;
+
+ stmmac_res->tx_irq[i] = irq;
+ }
+
stmmac_res->addr = devm_platform_ioremap_resource(pdev, 0);
return PTR_ERR_OR_ZERO(stmmac_res->addr);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
index a0c75886587c3..c7be1559fc3e2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
@@ -757,11 +757,7 @@ static int stmmac_test_flowctrl(struct stmmac_priv *priv)
dev_add_pack(&tpriv->pt);
/* Compute minimum number of packets to make FIFO full */
- pkt_count = priv->plat->rx_fifo_size;
- if (!pkt_count)
- pkt_count = priv->dma_cap.rx_fifo_size;
- pkt_count /= 1400;
- pkt_count *= 2;
+ pkt_count = priv->plat->rx_fifo_size / 1400 * 2;
for (i = 0; i < rx_cnt; i++)
stmmac_stop_rx(priv, priv->ioaddr, i);
@@ -821,7 +817,7 @@ static int stmmac_test_rss(struct stmmac_priv *priv)
{
struct stmmac_packet_attrs attr = { };
- if (!priv->dma_cap.rssen || !priv->rss.enable)
+ if (!(priv->dev->features & NETIF_F_RXHASH))
return -EOPNOTSUPP;
attr.dst = priv->dev->dev_addr;
@@ -1335,19 +1331,16 @@ static int __stmmac_test_l3filt(struct stmmac_priv *priv, u32 dst, u32 src,
struct stmmac_packet_attrs attr = { };
struct flow_dissector *dissector;
struct flow_cls_offload *cls;
- int ret, old_enable = 0;
struct flow_rule *rule;
+ int ret;
if (!tc_can_offload(priv->dev))
return -EOPNOTSUPP;
if (!priv->dma_cap.l3l4fnum)
return -EOPNOTSUPP;
- if (priv->rss.enable) {
- old_enable = priv->rss.enable;
- priv->rss.enable = false;
- stmmac_rss_configure(priv, priv->hw, NULL,
+ if (priv->dev->features & NETIF_F_RXHASH)
+ stmmac_rss_configure(priv, priv->hw, &priv->rss, false,
priv->plat->rx_queues_to_use);
- }
dissector = kzalloc_obj(*dissector);
if (!dissector) {
@@ -1415,11 +1408,9 @@ cleanup_cls:
cleanup_dissector:
kfree(dissector);
cleanup_rss:
- if (old_enable) {
- priv->rss.enable = old_enable;
- stmmac_rss_configure(priv, priv->hw, &priv->rss,
+ if (priv->dev->features & NETIF_F_RXHASH)
+ stmmac_rss_configure(priv, priv->hw, &priv->rss, true,
priv->plat->rx_queues_to_use);
- }
return ret;
}
@@ -1461,19 +1452,16 @@ static int __stmmac_test_l4filt(struct stmmac_priv *priv, u32 dst, u32 src,
struct stmmac_packet_attrs attr = { };
struct flow_dissector *dissector;
struct flow_cls_offload *cls;
- int ret, old_enable = 0;
struct flow_rule *rule;
+ int ret;
if (!tc_can_offload(priv->dev))
return -EOPNOTSUPP;
if (!priv->dma_cap.l3l4fnum)
return -EOPNOTSUPP;
- if (priv->rss.enable) {
- old_enable = priv->rss.enable;
- priv->rss.enable = false;
- stmmac_rss_configure(priv, priv->hw, NULL,
+ if (priv->dev->features & NETIF_F_RXHASH)
+ stmmac_rss_configure(priv, priv->hw, &priv->rss, false,
priv->plat->rx_queues_to_use);
- }
dissector = kzalloc_obj(*dissector);
if (!dissector) {
@@ -1546,11 +1534,9 @@ cleanup_cls:
cleanup_dissector:
kfree(dissector);
cleanup_rss:
- if (old_enable) {
- priv->rss.enable = old_enable;
- stmmac_rss_configure(priv, priv->hw, &priv->rss,
+ if (priv->dev->features & NETIF_F_RXHASH)
+ stmmac_rss_configure(priv, priv->hw, &priv->rss, true,
priv->plat->rx_queues_to_use);
- }
return ret;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index d786527185999..850fa179454f1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
@@ -219,6 +219,9 @@ static int tc_delete_knode(struct stmmac_priv *priv,
static int tc_setup_cls_u32(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
+ if (!priv->dma_cap.frpsel)
+ return -EOPNOTSUPP;
+
switch (cls->command) {
case TC_CLSU32_REPLACE_KNODE:
tc_unfill_entry(priv, cls);
@@ -340,11 +343,12 @@ static int tc_setup_cbs(struct stmmac_priv *priv,
u32 ptr;
int ret;
+ if (!priv->dma_cap.av)
+ return -EOPNOTSUPP;
+
/* Queue 0 is not AVB capable */
if (queue <= 0 || queue >= tx_queues_count)
return -EINVAL;
- if (!priv->dma_cap.av)
- return -EOPNOTSUPP;
port_transmit_rate_kbps = qopt->idleslope - qopt->sendslope;
@@ -866,8 +870,11 @@ static int tc_setup_cls(struct stmmac_priv *priv,
{
int ret = 0;
+ if (!priv->dma_cap.l3l4fnum)
+ return -EOPNOTSUPP;
+
/* When RSS is enabled, the filtering will be bypassed */
- if (priv->rss.enable)
+ if (priv->dev->features & NETIF_F_RXHASH)
return -EBUSY;
switch (cls->command) {
@@ -1160,6 +1167,7 @@ static int tc_setup_etf(struct stmmac_priv *priv,
{
if (!priv->dma_cap.tbssel)
return -EOPNOTSUPP;
+
if (qopt->queue >= priv->plat->tx_queues_to_use)
return -EINVAL;
if (!(priv->dma_conf.tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL))
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
index e24efe3bfedbe..4ce60c5a93ae8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
@@ -4,17 +4,23 @@
* stmmac VLAN (802.1Q) handling
*/
+#include "dwmac4.h"
#include "stmmac.h"
#include "stmmac_vlan.h"
-static void vlan_write_single(struct net_device *dev, u16 vid)
+static void vlan_update_hash(struct mac_device_info *hw, u32 hash,
+ int add_ctags, int add_stags);
+
+static void vlan_write_single(struct net_device *dev, u32 data)
{
void __iomem *ioaddr = (void __iomem *)dev->base_addr;
u32 val;
- val = readl(ioaddr + VLAN_TAG);
- val &= ~VLAN_TAG_VID;
- val |= VLAN_TAG_ETV | vid;
+ if (!(data & VLAN_TAG_VID))
+ data |= VLAN_TAG_VID;
+
+ val = readl(ioaddr + VLAN_TAG) & ~VLAN_TAG_VID;
+ val |= VLAN_TAG_ETV | (data & VLAN_TAG_VID);
writel(val, ioaddr + VLAN_TAG);
}
@@ -28,7 +34,15 @@ static int vlan_write_filter(struct net_device *dev,
u32 val;
if (index >= hw->num_vlan)
- return -EINVAL;
+ return -ENOSPC;
+
+ /* Indirect VLAN Tag filter access inteface requires PHY RXC clock to
+ * be running. Retry when the interface is up.
+ */
+ if (!netif_running(dev)) {
+ netdev_dbg(dev, "Skip writing Extended VLAN filter entry\n");
+ return -EAGAIN;
+ }
writel(data, ioaddr + VLAN_TAG_DATA);
@@ -40,9 +54,9 @@ static int vlan_write_filter(struct net_device *dev,
writel(val, ioaddr + VLAN_TAG);
- ret = readl_poll_timeout(ioaddr + VLAN_TAG, val,
- !(val & VLAN_TAG_CTRL_OB),
- 1000, 500000);
+ ret = readl_poll_timeout_atomic(ioaddr + VLAN_TAG, val,
+ !(val & VLAN_TAG_CTRL_OB),
+ 1000, 500000);
if (ret) {
netdev_err(dev, "Timeout accessing MAC_VLAN_Tag_Filter\n");
return -EBUSY;
@@ -56,35 +70,44 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
__be16 proto, u16 vid)
{
int index = -1;
+ bool is_stag;
u32 val = 0;
int i, ret;
if (vid > 4095)
- return -EINVAL;
+ return -ENXIO;
+
+ is_stag = proto == htons(ETH_P_8021AD);
/* Single Rx VLAN Filter */
if (hw->num_vlan == 1) {
/* For single VLAN filter, VID 0 means VLAN promiscuous */
- if (vid == 0) {
- netdev_warn(dev, "Adding VLAN ID 0 is not supported\n");
- return -EPERM;
- }
+ if (vid == 0)
+ return -EINVAL;
- if (hw->vlan_filter[0] & VLAN_TAG_VID) {
- netdev_err(dev, "Only single VLAN ID supported\n");
- return -EPERM;
- }
+ val = vid;
+ if (is_stag)
+ val |= VLAN_TAG_ERSVLM;
- hw->vlan_filter[0] = vid;
+ if (hw->vlan_filter[0] == val)
+ return 0;
- if (netif_running(dev))
- vlan_write_single(dev, vid);
+ if (hw->vlan_filter[0] & VLAN_TAG_VID)
+ return -ENOSPC;
+
+ vlan_update_hash(hw, hw->vlan_hash, !is_stag, is_stag);
+
+ vlan_write_single(dev, val);
+
+ hw->vlan_filter[0] = val;
return 0;
}
/* Extended Rx VLAN Filter Enable */
val |= VLAN_TAG_DATA_ETV | VLAN_TAG_DATA_VEN | vid;
+ if (is_stag)
+ val |= VLAN_TAG_DATA_ERSVLM;
for (i = 0; i < hw->num_vlan; i++) {
if (hw->vlan_filter[i] == val)
@@ -93,17 +116,12 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
index = i;
}
- if (index == -1) {
- netdev_err(dev, "MAC_VLAN_Tag_Filter full (size: %0u)\n",
- hw->num_vlan);
- return -EPERM;
- }
+ if (index == -1)
+ return -ENOSPC;
- if (netif_running(dev)) {
- ret = vlan_write_filter(dev, hw, index, val);
- if (ret)
- return ret;
- }
+ ret = vlan_write_filter(dev, hw, index, val);
+ if (ret && ret != -EAGAIN)
+ return ret;
hw->vlan_filter[index] = val;
@@ -114,215 +132,162 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev,
struct mac_device_info *hw,
__be16 proto, u16 vid)
{
- int i, ret = 0;
+ bool is_stag;
+ int i, ret;
+
+ is_stag = proto == htons(ETH_P_8021AD);
/* Single Rx VLAN Filter */
if (hw->num_vlan == 1) {
- if ((hw->vlan_filter[0] & VLAN_TAG_VID) == vid) {
- hw->vlan_filter[0] = 0;
+ if ((hw->vlan_filter[0] & VLAN_TAG_VID) != vid)
+ return -ENOENT;
+
+ if (is_stag != !!(hw->vlan_filter[0] & VLAN_TAG_ERSVLM))
+ return -ENOENT;
+
+ vlan_update_hash(hw, hw->vlan_hash, -!is_stag, -is_stag);
+
+ vlan_write_single(dev, 0);
+
+ hw->vlan_filter[0] = 0;
- if (netif_running(dev))
- vlan_write_single(dev, 0);
- }
return 0;
}
/* Extended Rx VLAN Filter Enable */
for (i = 0; i < hw->num_vlan; i++) {
- if ((hw->vlan_filter[i] & VLAN_TAG_DATA_VEN) &&
- ((hw->vlan_filter[i] & VLAN_TAG_DATA_VID) == vid)) {
+ if (!(hw->vlan_filter[i] & VLAN_TAG_DATA_VEN))
+ continue;
- if (netif_running(dev)) {
- ret = vlan_write_filter(dev, hw, i, 0);
- if (ret)
- return ret;
- }
+ if ((hw->vlan_filter[i] & VLAN_TAG_DATA_VID) != vid)
+ continue;
- hw->vlan_filter[i] = 0;
- }
- }
+ if (is_stag != !!(hw->vlan_filter[i] & VLAN_TAG_DATA_ERSVLM))
+ continue;
- return 0;
-}
+ ret = vlan_write_filter(dev, hw, i, 0);
+ if (ret && ret != -EAGAIN)
+ return ret;
-static void vlan_restore_hw_rx_fltr(struct net_device *dev,
- struct mac_device_info *hw)
-{
- int i;
+ hw->vlan_filter[i] = 0;
- /* Single Rx VLAN Filter */
- if (hw->num_vlan == 1) {
- vlan_write_single(dev, hw->vlan_filter[0]);
- return;
+ return 0;
}
- /* Extended Rx VLAN Filter Enable */
- for (i = 0; i < hw->num_vlan; i++)
- vlan_write_filter(dev, hw, i, hw->vlan_filter[i]);
+ return -ENOENT;
}
static void vlan_update_hash(struct mac_device_info *hw, u32 hash,
- u16 perfect_match, bool is_double)
+ int add_ctags, int add_stags)
{
void __iomem *ioaddr = hw->pcsr;
u32 value;
+ hw->vlan_hash = hash;
writel(hash, ioaddr + VLAN_HASH_TABLE);
- value = readl(ioaddr + VLAN_TAG);
+ hw->vlan_ctags += add_ctags;
+ hw->vlan_stags += add_stags;
+ value = readl(ioaddr + VLAN_TAG) | VLAN_ETV;
+
+ if (hash)
+ value |= VLAN_VTHM;
+ else
+ value &= ~VLAN_VTHM;
- if (hash) {
- value |= VLAN_VTHM | VLAN_ETV;
- if (is_double) {
- value |= VLAN_EDVLP;
- value |= VLAN_ESVL;
- value |= VLAN_DOVLTC;
- } else {
- value &= ~VLAN_EDVLP;
- value &= ~VLAN_ESVL;
- value &= ~VLAN_DOVLTC;
- }
-
- writel(value, ioaddr + VLAN_TAG);
- } else if (perfect_match) {
- u32 value = VLAN_ETV;
-
- if (is_double) {
- value |= VLAN_EDVLP;
- value |= VLAN_ESVL;
- value |= VLAN_DOVLTC;
- } else {
- value &= ~VLAN_EDVLP;
- value &= ~VLAN_ESVL;
- value &= ~VLAN_DOVLTC;
- }
-
- writel(value | perfect_match, ioaddr + VLAN_TAG);
+ if (hw->vlan_ctags && hw->vlan_stags) {
+ value |= VLAN_DOVLTC;
+ } else if (hw->vlan_stags) {
+ value &= ~VLAN_DOVLTC;
+ value |= VLAN_ERSVLM;
} else {
- value &= ~(VLAN_VTHM | VLAN_ETV);
- value &= ~(VLAN_EDVLP | VLAN_ESVL);
value &= ~VLAN_DOVLTC;
- value &= ~VLAN_VID;
-
- writel(value, ioaddr + VLAN_TAG);
+ value &= ~VLAN_ERSVLM;
}
+
+ writel(value, ioaddr + VLAN_TAG);
}
-static void vlan_enable(struct mac_device_info *hw, u32 type)
+static void vlan_restore_hw_rx_fltr(struct net_device *dev,
+ struct mac_device_info *hw)
{
- void __iomem *ioaddr = hw->pcsr;
- u32 value;
+ int i;
- value = readl(ioaddr + VLAN_INCL);
- value |= VLAN_VLTI;
- value &= ~VLAN_CSVL; /* Only use CVLAN */
- value &= ~VLAN_VLC;
- value |= (type << VLAN_VLC_SHIFT) & VLAN_VLC;
- writel(value, ioaddr + VLAN_INCL);
+ /* Hash-based Rx VLAN Filter */
+ vlan_update_hash(hw, hw->vlan_hash, 0, 0);
+
+ /* Single Rx VLAN Filter */
+ if (hw->num_vlan == 1) {
+ vlan_write_single(dev, hw->vlan_filter[0]);
+ return;
+ }
+
+ /* Extended Rx VLAN Filter Enable */
+ for (i = 0; i < hw->num_vlan; i++)
+ vlan_write_filter(dev, hw, i, hw->vlan_filter[i]);
}
static void vlan_rx_hw(struct mac_device_info *hw,
struct dma_desc *rx_desc, struct sk_buff *skb)
{
if (hw->desc->get_rx_vlan_valid(rx_desc)) {
+ u16 tpid = hw->desc->get_rx_vlan_tpid(rx_desc);
u16 vid = hw->desc->get_rx_vlan_tci(rx_desc);
- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
+ __vlan_hwaccel_put_tag(skb, htons(tpid), vid);
}
}
-static void vlan_set_hw_mode(struct mac_device_info *hw)
+static void vlan_set_hw_mode(struct mac_device_info *hw, bool rx_strip,
+ bool rx_ctag, bool rx_stag, bool tx_stag)
{
void __iomem *ioaddr = hw->pcsr;
- u32 value = readl(ioaddr + VLAN_TAG);
+ u32 value;
+ /* Activate VLAN Tag Rx filters */
+ value = readl(ioaddr + GMAC_PACKET_FILTER);
+ if (rx_ctag || rx_stag)
+ value |= GMAC_PACKET_FILTER_VTFE;
+ else
+ value &= ~GMAC_PACKET_FILTER_VTFE;
+ writel(value, ioaddr + GMAC_PACKET_FILTER);
+
+ /* Setup Rx VLAN Tag stripping */
+ value = readl(ioaddr + VLAN_TAG);
value &= ~VLAN_TAG_CTRL_EVLS_MASK;
- if (hw->hw_vlan_en)
+ if (rx_strip)
/* Always strip VLAN on Receive */
value |= VLAN_TAG_STRIP_ALL;
else
/* Do not strip VLAN on Receive */
value |= VLAN_TAG_STRIP_NONE;
+ /* Activate S-VLAN feature on MAC Tx and Rx */
+ if (rx_strip || rx_stag || tx_stag)
+ value |= VLAN_ESVL;
+ else
+ value &= ~VLAN_ESVL;
+
+ /* Activate Double VLAN for Rx COE */
+ value |= VLAN_EDVLP;
+
/* Enable outer VLAN Tag in Rx DMA descriptor */
value |= VLAN_TAG_CTRL_EVLRXS;
writel(value, ioaddr + VLAN_TAG);
-}
-
-static void dwxgmac2_update_vlan_hash(struct mac_device_info *hw, u32 hash,
- u16 perfect_match, bool is_double)
-{
- void __iomem *ioaddr = hw->pcsr;
-
- writel(hash, ioaddr + VLAN_HASH_TABLE);
-
- if (hash) {
- u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
-
- value |= XGMAC_FILTER_VTFE;
-
- writel(value, ioaddr + XGMAC_PACKET_FILTER);
-
- value = readl(ioaddr + VLAN_TAG);
- value |= VLAN_VTHM | VLAN_ETV;
- if (is_double) {
- value |= VLAN_EDVLP;
- value |= VLAN_ESVL;
- value |= VLAN_DOVLTC;
- } else {
- value &= ~VLAN_EDVLP;
- value &= ~VLAN_ESVL;
- value &= ~VLAN_DOVLTC;
- }
-
- value &= ~VLAN_VID;
- writel(value, ioaddr + VLAN_TAG);
- } else if (perfect_match) {
- u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
-
- value |= XGMAC_FILTER_VTFE;
-
- writel(value, ioaddr + XGMAC_PACKET_FILTER);
-
- value = readl(ioaddr + VLAN_TAG);
-
- value &= ~VLAN_VTHM;
- value |= VLAN_ETV;
- if (is_double) {
- value |= VLAN_EDVLP;
- value |= VLAN_ESVL;
- value |= VLAN_DOVLTC;
- } else {
- value &= ~VLAN_EDVLP;
- value &= ~VLAN_ESVL;
- value &= ~VLAN_DOVLTC;
- }
-
- value &= ~VLAN_VID;
- writel(value | perfect_match, ioaddr + VLAN_TAG);
- } else {
- u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
-
- value &= ~XGMAC_FILTER_VTFE;
-
- writel(value, ioaddr + XGMAC_PACKET_FILTER);
-
- value = readl(ioaddr + VLAN_TAG);
-
- value &= ~(VLAN_VTHM | VLAN_ETV);
- value &= ~(VLAN_EDVLP | VLAN_ESVL);
- value &= ~VLAN_DOVLTC;
- value &= ~VLAN_VID;
-
- writel(value, ioaddr + VLAN_TAG);
- }
+ /* Set Tx VLAN Insertion feature (might be unavailable) */
+ value = readl(ioaddr + VLAN_INCL);
+ value |= VLAN_VLTI;
+ if (tx_stag)
+ value |= VLAN_CSVL;
+ else
+ value &= ~VLAN_CSVL;
+ writel(value, ioaddr + VLAN_INCL);
}
-const struct stmmac_vlan_ops dwmac_vlan_ops = {
+const struct stmmac_vlan_ops dwmac4_vlan_ops = {
.update_vlan_hash = vlan_update_hash,
- .enable_vlan = vlan_enable,
.add_hw_vlan_rx_fltr = vlan_add_hw_rx_fltr,
.del_hw_vlan_rx_fltr = vlan_del_hw_rx_fltr,
.restore_hw_vlan_rx_fltr = vlan_restore_hw_rx_fltr,
@@ -331,17 +296,7 @@ const struct stmmac_vlan_ops dwmac_vlan_ops = {
};
const struct stmmac_vlan_ops dwxlgmac2_vlan_ops = {
- .update_vlan_hash = dwxgmac2_update_vlan_hash,
- .enable_vlan = vlan_enable,
-};
-
-const struct stmmac_vlan_ops dwxgmac210_vlan_ops = {
- .update_vlan_hash = dwxgmac2_update_vlan_hash,
- .enable_vlan = vlan_enable,
- .add_hw_vlan_rx_fltr = vlan_add_hw_rx_fltr,
- .del_hw_vlan_rx_fltr = vlan_del_hw_rx_fltr,
- .restore_hw_vlan_rx_fltr = vlan_restore_hw_rx_fltr,
- .rx_hw_vlan = vlan_rx_hw,
+ .update_vlan_hash = vlan_update_hash,
.set_hw_vlan_mode = vlan_set_hw_mode,
};
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h
index c24f89a9049b8..e38f570e3297e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h
@@ -8,7 +8,6 @@
#define __STMMAC_VLAN_H__
#include <linux/bitfield.h>
-#include "dwxgmac2.h"
#define VLAN_TAG 0x00000050
#define VLAN_TAG_DATA 0x00000054
@@ -16,9 +15,11 @@
#define VLAN_INCL 0x00000060
/* MAC VLAN */
+#define VLAN_EIVLRXS BIT(31)
#define VLAN_EDVLP BIT(26)
#define VLAN_VTHM BIT(25)
#define VLAN_DOVLTC BIT(20)
+#define VLAN_ERSVLM BIT(19)
#define VLAN_ESVL BIT(18)
#define VLAN_ETV BIT(16)
#define VLAN_VID GENMASK(15, 0)
@@ -31,6 +32,7 @@
/* MAC VLAN Tag */
#define VLAN_TAG_VID GENMASK(15, 0)
#define VLAN_TAG_ETV BIT(16)
+#define VLAN_TAG_ERSVLM BIT(19)
/* MAC VLAN Tag Control */
#define VLAN_TAG_CTRL_OB BIT(0)
@@ -50,13 +52,13 @@
#define VLAN_TAG_DATA_VID GENMASK(15, 0)
#define VLAN_TAG_DATA_VEN BIT(16)
#define VLAN_TAG_DATA_ETV BIT(17)
+#define VLAN_TAG_DATA_ERSVLM BIT(19)
/* MAC VLAN HW FEAT */
#define HW_FEATURE3 0x00000128
#define VLAN_HW_FEAT_NRVF GENMASK(2, 0)
-extern const struct stmmac_vlan_ops dwmac_vlan_ops;
-extern const struct stmmac_vlan_ops dwxgmac210_vlan_ops;
+extern const struct stmmac_vlan_ops dwmac4_vlan_ops;
extern const struct stmmac_vlan_ops dwxlgmac2_vlan_ops;
u32 stmmac_get_num_vlan(void __iomem *ioaddr);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
index d7e4db7224b0c..8627f887fec81 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
@@ -129,7 +129,10 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog,
bpf_prog_put(old_prog);
/* Disable RX SPH for XDP operation */
- priv->sph_active = priv->sph_capable && !stmmac_xdp_is_enabled(priv);
+ if (priv->dma_cap.sphen &&
+ !(priv->plat->flags & STMMAC_FLAG_SPH_DISABLE)) {
+ priv->sph_active = !stmmac_xdp_is_enabled(priv);
+ }
if (if_running && need_update)
stmmac_xdp_open(dev);
diff --git a/drivers/net/pcs/Makefile b/drivers/net/pcs/Makefile
index 4f7920618b900..95b91cebd8518 100644
--- a/drivers/net/pcs/Makefile
+++ b/drivers/net/pcs/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for Linux PCS drivers
-pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-plat.o \
+pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-plat.o pcs-xpcs-pma.o \
pcs-xpcs-nxp.o pcs-xpcs-wx.o
obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
diff --git a/drivers/net/pcs/pcs-xpcs-plat.c b/drivers/net/pcs/pcs-xpcs-plat.c
index b8c48f9effbf7..7ee57de7ec57e 100644
--- a/drivers/net/pcs/pcs-xpcs-plat.c
+++ b/drivers/net/pcs/pcs-xpcs-plat.c
@@ -285,7 +285,6 @@ static int xpcs_plat_init_clk(struct dw_xpcs_plat *pxpcs)
return dev_err_probe(dev, PTR_ERR(pxpcs->cclk),
"Failed to get CSR clock\n");
- pm_runtime_set_active(dev);
ret = devm_pm_runtime_enable(dev);
if (ret) {
dev_err(dev, "Failed to enable runtime-PM\n");
@@ -428,6 +427,7 @@ DW_XPCS_INFO_DECLARE(xpcs_pma_gen4_3g, DW_XPCS_ID_NATIVE, DW_XPCS_PMA_GEN4_3G_ID
DW_XPCS_INFO_DECLARE(xpcs_pma_gen4_6g, DW_XPCS_ID_NATIVE, DW_XPCS_PMA_GEN4_6G_ID);
DW_XPCS_INFO_DECLARE(xpcs_pma_gen5_10g, DW_XPCS_ID_NATIVE, DW_XPCS_PMA_GEN5_10G_ID);
DW_XPCS_INFO_DECLARE(xpcs_pma_gen5_12g, DW_XPCS_ID_NATIVE, DW_XPCS_PMA_GEN5_12G_ID);
+DW_XPCS_INFO_DECLARE(xpcs_bt1, BT1_XGMAC_XPCS_ID, DW_XPCS_PMA_GEN5_10G_ID);
static const struct of_device_id xpcs_of_ids[] = {
{ .compatible = "snps,dw-xpcs", .data = &xpcs_generic },
@@ -438,6 +438,7 @@ static const struct of_device_id xpcs_of_ids[] = {
{ .compatible = "snps,dw-xpcs-gen4-6g", .data = &xpcs_pma_gen4_6g },
{ .compatible = "snps,dw-xpcs-gen5-10g", .data = &xpcs_pma_gen5_10g },
{ .compatible = "snps,dw-xpcs-gen5-12g", .data = &xpcs_pma_gen5_12g },
+ { .compatible = "baikal,bt1-xpcs", .data = &xpcs_bt1 },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, xpcs_of_ids);
diff --git a/drivers/net/pcs/pcs-xpcs-pma.c b/drivers/net/pcs/pcs-xpcs-pma.c
new file mode 100644
index 0000000000000..8d9f2e1e85265
--- /dev/null
+++ b/drivers/net/pcs/pcs-xpcs-pma.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Synopsys DesignWare XPCS PMA driver
+ *
+ * Copyright (C) 2024 Serge Semin
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/mdio.h>
+#include <linux/pcs/pcs-xpcs.h>
+
+#include "pcs-xpcs.h"
+
+/* DesignWare Gen5 10G PHY can be clocked by an external clock source.
+ * It will be enabled instead of the internal one in case if it's specified.
+ */
+static int xpcs_gen5_10g_ref_clock_select(struct dw_xpcs *xpcs, bool reset)
+{
+ int ret;
+
+ ret = xpcs_read_vendor(xpcs, MDIO_MMD_PMAPMD,
+ DW_VR_XS_PMA_GEN5_10G_MPLL_CTRL);
+ if (ret < 0)
+ return ret;
+
+ if (xpcs->clks[DW_XPCS_PAD_CLK].clk)
+ ret &= ~DW_VR_XS_PMA_REF_CLK_SEL_CORE;
+ else if (xpcs->clks[DW_XPCS_CORE_CLK].clk)
+ ret |= DW_VR_XS_PMA_REF_CLK_SEL_CORE;
+ else if (reset)
+ goto out_vendor_reset;
+ else
+ return 0;
+
+ ret = xpcs_write_vendor(xpcs, MDIO_MMD_PMAPMD,
+ DW_VR_XS_PMA_GEN5_10G_MPLL_CTRL, ret);
+ if (ret < 0)
+ return ret;
+
+ /* Vendor reset must be immediately performed. Note it will workout
+ * only if Tx/Rx are stable (Power_Good state).
+ */
+out_vendor_reset:
+ return xpcs_vendor_reset(xpcs);
+}
+
+static int xpcs_10gbaser_gen5_10g_pma_config(struct dw_xpcs *xpcs)
+{
+ int ret;
+
+ ret = xpcs_read_vendor(xpcs, MDIO_MMD_PMAPMD,
+ DW_VR_XS_PMA_GEN5_10G_GEN_CTRL);
+ if (ret < 0)
+ return ret;
+
+ /* Select KR-mode of the PHY lanes */
+ ret &= ~DW_VR_XS_PMA_LANE_MODE;
+ ret |= FIELD_PREP(DW_VR_XS_PMA_LANE_MODE, DW_VR_XS_PMA_LANE_MODE_KR);
+
+ /* Activate 1 lane per link */
+ ret &= ~DW_VR_XS_PMA_LINK_WIDTH;
+ ret |= FIELD_PREP(DW_VR_XS_PMA_LINK_WIDTH, DW_VR_XS_PMA_LINK_WIDTH_1);
+
+ /* Disable unused 1-3 lanes */
+ ret &= ~DW_VR_XS_PMA_LANE_PWR_OFF;
+ ret |= FIELD_PREP(DW_VR_XS_PMA_LANE_PWR_OFF, 0xe);
+
+ ret = xpcs_write_vendor(xpcs, MDIO_MMD_PMAPMD,
+ DW_VR_XS_PMA_GEN5_10G_GEN_CTRL, ret);
+ if (ret < 0)
+ return ret;
+
+ /* Select PCS/PMA refclk source: Pad or Core. Vendor-reset the device
+ * to make sure the updates are perceived by the core
+ */
+ return xpcs_gen5_10g_ref_clock_select(xpcs, true);
+}
+
+int xpcs_10gbaser_pma_config(struct dw_xpcs *xpcs)
+{
+ switch (xpcs->info.pma) {
+ case DW_XPCS_PMA_GEN5_10G_ID:
+ return xpcs_10gbaser_gen5_10g_pma_config(xpcs);
+ default:
+ return 0;
+ }
+}
+
+static int xpcs_10gbasex_gen5_10g_pma_config(struct dw_xpcs *xpcs)
+{
+ int ret;
+
+ ret = xpcs_read_vendor(xpcs, MDIO_MMD_PMAPMD,
+ DW_VR_XS_PMA_GEN5_10G_GEN_CTRL);
+ if (ret < 0)
+ return ret;
+
+ /* Select KX4-mode of the PHY lanes */
+ ret &= ~DW_VR_XS_PMA_LANE_MODE;
+ ret |= FIELD_PREP(DW_VR_XS_PMA_LANE_MODE, DW_VR_XS_PMA_LANE_MODE_KX4);
+
+ /* Activate 4 lane per link */
+ ret &= ~DW_VR_XS_PMA_LINK_WIDTH;
+ ret |= FIELD_PREP(DW_VR_XS_PMA_LINK_WIDTH, DW_VR_XS_PMA_LINK_WIDTH_4);
+
+ /* Enable all 4 lanes since it's X4 */
+ ret &= ~DW_VR_XS_PMA_LANE_PWR_OFF;
+ ret |= FIELD_PREP(DW_VR_XS_PMA_LANE_PWR_OFF, 0x0);
+
+ ret = xpcs_write_vendor(xpcs, MDIO_MMD_PMAPMD,
+ DW_VR_XS_PMA_GEN5_10G_GEN_CTRL, ret);
+ if (ret < 0)
+ return ret;
+
+ /* Select PCS/PMA refclk source: Pad or Core. Vendor-reset the device
+ * to make sure the updates are perceived by the core
+ */
+ return xpcs_gen5_10g_ref_clock_select(xpcs, true);
+}
+
+int xpcs_10gbasex_pma_config(struct dw_xpcs *xpcs)
+{
+ switch (xpcs->info.pma) {
+ case DW_XPCS_PMA_GEN5_10G_ID:
+ return xpcs_10gbasex_gen5_10g_pma_config(xpcs);
+ default:
+ return 0;
+ }
+}
diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
index e69fa2f0a0e8d..cceae97673997 100644
--- a/drivers/net/pcs/pcs-xpcs.c
+++ b/drivers/net/pcs/pcs-xpcs.c
@@ -19,6 +19,15 @@
#define phylink_pcs_to_xpcs(pl_pcs) \
container_of((pl_pcs), struct dw_xpcs, pcs)
+static const int xpcs_xgmii_features[] = {
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_Autoneg_BIT,
+ ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ __ETHTOOL_LINK_MODE_MASK_NBITS,
+};
+
static const int xpcs_usxgmii_features[] = {
ETHTOOL_LINK_MODE_Pause_BIT,
ETHTOOL_LINK_MODE_Asym_Pause_BIT,
@@ -33,6 +42,7 @@ static const int xpcs_usxgmii_features[] = {
static const int xpcs_10gkr_features[] = {
ETHTOOL_LINK_MODE_Pause_BIT,
ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_Autoneg_BIT,
ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
__ETHTOOL_LINK_MODE_MASK_NBITS,
};
@@ -114,6 +124,28 @@ static const int xpcs_100gbasep_features[] = {
static const int xpcs_10gbaser_features[] = {
ETHTOOL_LINK_MODE_Pause_BIT,
ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
+ /* Speed-compatible modes for the link setup in the external PHYs */
+ ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ /* ETHTOOL_LINK_MODE_10000baseCX4_Full_BIT, */
+ /* ETHTOOL_LINK_MODE_10000baseLX4_Full_BIT, */
+ __ETHTOOL_LINK_MODE_MASK_NBITS,
+};
+
+static const int xpcs_10gbasex_features[] = {
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ /* ETHTOOL_LINK_MODE_10000baseCX4_Full_BIT, */
+ /* ETHTOOL_LINK_MODE_10000baseLX4_Full_BIT, */
+ /* Speed-compatible modes for the link setup in the external PHYs */
+ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
@@ -224,19 +256,17 @@ int xpcs_modify(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set)
return mdiodev_c45_modify(xpcs->mdiodev, dev, reg, mask, set);
}
-static int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg,
- u16 mask, u16 set)
+int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set)
{
return mdiodev_c45_modify_changed(xpcs->mdiodev, dev, reg, mask, set);
}
-static int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg)
+int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg)
{
return xpcs_read(xpcs, dev, DW_VENDOR | reg);
}
-static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg,
- u16 val)
+int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg, u16 val)
{
return xpcs_write(xpcs, dev, DW_VENDOR | reg, val);
}
@@ -262,43 +292,51 @@ static int xpcs_modify_vpcs(struct dw_xpcs *xpcs, int reg, u16 mask, u16 val)
return xpcs_modify_vendor(xpcs, MDIO_MMD_PCS, reg, mask, val);
}
-static int xpcs_poll_reset(struct dw_xpcs *xpcs, int dev)
+int xpcs_poll_val(struct dw_xpcs *xpcs, int dev, int reg, u16 mask, u16 goal)
{
int ret, val;
ret = read_poll_timeout(xpcs_read, val,
- val < 0 || !(val & BMCR_RESET),
- 50000, 600000, true, xpcs, dev, MII_BMCR);
+ val < 0 || (val & mask) == goal,
+ 50000, 600000, true, xpcs, dev, reg);
if (val < 0)
ret = val;
return ret;
}
-static int xpcs_soft_reset(struct dw_xpcs *xpcs,
- const struct dw_xpcs_compat *compat)
+static int __xpcs_soft_reset(struct dw_xpcs *xpcs, bool vendor)
{
- int ret, dev;
+ int ret, dev, reg;
- switch (compat->an_mode) {
- case DW_AN_C73:
- case DW_10GBASER:
- dev = MDIO_MMD_PCS;
- break;
- case DW_AN_C37_SGMII:
- case DW_2500BASEX:
- case DW_AN_C37_1000BASEX:
+ if (xpcs->mmd_ctrl & DW_SR_CTRL_MII_MMD_EN)
dev = MDIO_MMD_VEND2;
- break;
- default:
+ else if (xpcs->mmd_ctrl & DW_SR_CTRL_PCS_XS_MMD_EN)
+ dev = MDIO_MMD_PCS;
+ else if (xpcs->mmd_ctrl & DW_SR_CTRL_PMA_MMD_EN)
+ dev = MDIO_MMD_PMAPMD;
+ else
return -EINVAL;
- }
- ret = xpcs_write(xpcs, dev, MII_BMCR, BMCR_RESET);
+ reg = MII_BMCR;
+ if (vendor)
+ reg |= DW_VENDOR;
+
+ ret = xpcs_modify(xpcs, dev, reg, BMCR_RESET, BMCR_RESET);
if (ret < 0)
return ret;
- return xpcs_poll_reset(xpcs, dev);
+ return xpcs_poll_val(xpcs, dev, reg, BMCR_RESET, 0);
+}
+
+int xpcs_soft_reset(struct dw_xpcs *xpcs)
+{
+ return __xpcs_soft_reset(xpcs, false);
+}
+
+int xpcs_vendor_reset(struct dw_xpcs *xpcs)
+{
+ return __xpcs_soft_reset(xpcs, true);
}
#define xpcs_warn(__xpcs, __state, __args...) \
@@ -355,6 +393,11 @@ static int xpcs_read_fault_c73(struct dw_xpcs *xpcs,
return 0;
}
+static void xpcs_link_up_xgmii(struct dw_xpcs *xpcs, int speed)
+{
+ /* TODO add some XGMII-related configs ...*/
+}
+
static void xpcs_link_up_usxgmii(struct dw_xpcs *xpcs, int speed)
{
int ret, speed_sel;
@@ -618,13 +661,10 @@ static int xpcs_resolve_pma(struct dw_xpcs *xpcs,
}
}
- state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX;
+ state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX;
state->duplex = DUPLEX_FULL;
switch (state->interface) {
- case PHY_INTERFACE_MODE_10GKR:
- state->speed = SPEED_10000;
- break;
case PHY_INTERFACE_MODE_XLGMII:
state->speed = xpcs_get_max_xlgmii_speed(xpcs, state);
break;
@@ -686,6 +726,7 @@ static unsigned int xpcs_inband_caps(struct phylink_pcs *pcs,
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
case DW_10GBASER:
+ case DW_10GBASEX:
case DW_2500BASEX:
return LINK_INBAND_DISABLE;
@@ -721,7 +762,6 @@ static int xpcs_switch_interface_mode(struct dw_xpcs *xpcs,
static void xpcs_pre_config(struct phylink_pcs *pcs, phy_interface_t interface)
{
struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
- const struct dw_xpcs_compat *compat;
int ret;
ret = xpcs_switch_interface_mode(xpcs, interface);
@@ -732,14 +772,7 @@ static void xpcs_pre_config(struct phylink_pcs *pcs, phy_interface_t interface)
if (!xpcs->need_reset)
return;
- compat = xpcs_find_compat(xpcs, interface);
- if (!compat) {
- dev_err(&xpcs->mdiodev->dev, "unsupported interface %s\n",
- phy_modes(interface));
- return;
- }
-
- ret = xpcs_soft_reset(xpcs, compat);
+ ret = xpcs_soft_reset(xpcs);
if (ret)
dev_err(&xpcs->mdiodev->dev, "soft reset failed: %pe\n",
ERR_PTR(ret));
@@ -892,6 +925,76 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,
return changed;
}
+static int xpcs_config_10gbaser(struct dw_xpcs *xpcs)
+{
+ int ret;
+
+ /* Disable Clause 73 AN in anyway */
+ ret = xpcs_modify(xpcs, MDIO_MMD_AN, MDIO_CTRL1,
+ MDIO_AN_CTRL1_ENABLE, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Disable RXAUI if it's enabled by default */
+ ret = xpcs_read_vpcs(xpcs, DW_VR_XS_PCS_XAUI_MODE_CTRL);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~DW_VR_XS_PCS_RXAUI_MODE;
+ ret = xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_XAUI_MODE_CTRL, ret);
+ if (ret < 0)
+ return ret;
+
+ /* Set PCS to 10G speed and 10GBASE-R PCS */
+ ret = xpcs_modify(xpcs, MDIO_MMD_PCS, MDIO_CTRL1,
+ MDIO_CTRL1_SPEEDSEL, MDIO_CTRL1_SPEED10G);
+ if (ret < 0)
+ return ret;
+
+ ret = xpcs_modify(xpcs, MDIO_MMD_PCS, MDIO_CTRL2,
+ MDIO_PCS_CTRL2_TYPE, MDIO_PCS_CTRL2_10GBR);
+ if (ret < 0)
+ return ret;
+
+ /* Make sure the specified PCS type is perceived by the core */
+ return xpcs_vendor_reset(xpcs);
+}
+
+static int xpcs_config_10gbasex(struct dw_xpcs *xpcs)
+{
+ int ret;
+
+ /* Disable Clause 73 AN in anyway */
+ ret = xpcs_modify(xpcs, MDIO_MMD_AN, MDIO_CTRL1,
+ MDIO_AN_CTRL1_ENABLE, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Disable RXAUI if it's enabled by default */
+ ret = xpcs_read_vpcs(xpcs, DW_VR_XS_PCS_XAUI_MODE_CTRL);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~DW_VR_XS_PCS_RXAUI_MODE;
+ ret = xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_XAUI_MODE_CTRL, ret);
+ if (ret < 0)
+ return ret;
+
+ /* Set PCS to 10G speed and 10GBASE-X PCS */
+ ret = xpcs_modify(xpcs, MDIO_MMD_PCS, MDIO_CTRL1,
+ MDIO_CTRL1_SPEEDSEL, MDIO_CTRL1_SPEED10G);
+ if (ret < 0)
+ return ret;
+
+ ret = xpcs_modify(xpcs, MDIO_MMD_PCS, MDIO_CTRL2,
+ MDIO_PCS_CTRL2_TYPE, MDIO_PCS_CTRL2_10GBX);
+ if (ret < 0)
+ return ret;
+
+ /* Make sure the specified PCS type is perceived by the core */
+ return xpcs_vendor_reset(xpcs);
+}
+
static int xpcs_config_2500basex(struct dw_xpcs *xpcs)
{
int ret;
@@ -931,6 +1034,9 @@ static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
switch (compat->an_mode) {
case DW_10GBASER:
+ ret = xpcs_config_10gbaser(xpcs);
+ if (ret)
+ return ret;
break;
case DW_AN_C73:
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
@@ -950,6 +1056,11 @@ static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
if (ret)
return ret;
break;
+ case DW_10GBASEX:
+ ret = xpcs_config_10gbasex(xpcs);
+ if (ret)
+ return ret;
+ break;
case DW_2500BASEX:
ret = xpcs_config_2500basex(xpcs);
if (ret)
@@ -1003,7 +1114,7 @@ static int xpcs_get_state_c73(struct dw_xpcs *xpcs,
/* ... and then we check the faults. */
ret = xpcs_read_fault_c73(xpcs, state, pcs_stat1);
if (ret) {
- ret = xpcs_soft_reset(xpcs, compat);
+ ret = xpcs_soft_reset(xpcs);
if (ret)
return ret;
@@ -1060,7 +1171,6 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
state->link = false;
state->speed = SPEED_UNKNOWN;
state->duplex = DUPLEX_UNKNOWN;
- state->pause = 0;
/* For C37 SGMII mode, we check DW_VR_MII_AN_INTR_STS for link
* status, speed and duplex.
@@ -1189,6 +1299,7 @@ static void xpcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
switch (compat->an_mode) {
case DW_10GBASER:
+ case DW_10GBASEX:
phylink_mii_c45_pcs_get_state(xpcs->mdiodev, state);
break;
case DW_AN_C73:
@@ -1220,6 +1331,36 @@ static void xpcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
}
}
+static void xpcs_link_up_check(struct dw_xpcs *xpcs, int speed, int duplex)
+{
+ int ret;
+
+ ret = xpcs_poll_val(xpcs, MDIO_MMD_PCS, MDIO_STAT1,
+ MDIO_STAT1_LSTATUS, MDIO_STAT1_LSTATUS);
+ if (ret < 0) {
+ dev_err(&xpcs->mdiodev->dev, "link is malfunction\n");
+ return;
+ }
+
+ if (speed != SPEED_10000)
+ dev_warn(&xpcs->mdiodev->dev, "Incompatible speed\n");
+
+ if (duplex != DUPLEX_FULL)
+ dev_warn(&xpcs->mdiodev->dev, "Incompatible duplex\n");
+
+ ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT2);
+ if (ret < 0) {
+ dev_err(&xpcs->mdiodev->dev, "Failed to read PCS status\n");
+ return;
+ }
+
+ if (ret & MDIO_STAT2_RXFAULT)
+ dev_dbg(&xpcs->mdiodev->dev, "Receiver fault detected\n");
+
+ if (ret & MDIO_STAT2_TXFAULT)
+ dev_dbg(&xpcs->mdiodev->dev, "Transmitter fault detected\n");
+}
+
static void xpcs_link_up_sgmii_1000basex(struct dw_xpcs *xpcs,
unsigned int neg_mode,
phy_interface_t interface,
@@ -1257,10 +1398,20 @@ static void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
switch (interface) {
+ case PHY_INTERFACE_MODE_XGMII:
+ xpcs_link_up_xgmii(xpcs, speed);
+ break;
+
case PHY_INTERFACE_MODE_USXGMII:
xpcs_link_up_usxgmii(xpcs, speed);
break;
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_10GBASEX:
+ case PHY_INTERFACE_MODE_XAUI:
+ xpcs_link_up_check(xpcs, speed, duplex);
+ break;
+
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
xpcs_link_up_sgmii_1000basex(xpcs, neg_mode, interface, speed,
@@ -1404,6 +1555,10 @@ static int xpcs_read_ids(struct dw_xpcs *xpcs)
static const struct dw_xpcs_compat synopsys_xpcs_compat[] = {
{
+ .interface = PHY_INTERFACE_MODE_XGMII,
+ .supported = xpcs_xgmii_features,
+ .an_mode = DW_AN_C73,
+ }, {
.interface = PHY_INTERFACE_MODE_USXGMII,
.supported = xpcs_usxgmii_features,
.an_mode = DW_AN_C73,
@@ -1435,6 +1590,17 @@ static const struct dw_xpcs_compat synopsys_xpcs_compat[] = {
.interface = PHY_INTERFACE_MODE_10GBASER,
.supported = xpcs_10gbaser_features,
.an_mode = DW_10GBASER,
+ .pma_config = xpcs_10gbaser_pma_config,
+ }, {
+ .interface = PHY_INTERFACE_MODE_10GBASEX,
+ .supported = xpcs_10gbasex_features,
+ .an_mode = DW_10GBASEX,
+ .pma_config = xpcs_10gbasex_pma_config,
+ }, {
+ .interface = PHY_INTERFACE_MODE_XAUI,
+ .supported = xpcs_10gbasex_features,
+ .an_mode = DW_10GBASEX,
+ .pma_config = xpcs_10gbasex_pma_config,
}, {
.interface = PHY_INTERFACE_MODE_SGMII,
.supported = xpcs_sgmii_features,
@@ -1476,6 +1642,30 @@ static const struct dw_xpcs_compat nxp_sja1110_xpcs_compat[] = {
}
};
+static const struct dw_xpcs_compat bt1_xpcs_compat[] = {
+ {
+ .interface = PHY_INTERFACE_MODE_XGMII,
+ .supported = xpcs_xgmii_features,
+ .an_mode = DW_AN_C73,
+ }, {
+ .interface = PHY_INTERFACE_MODE_10GBASER,
+ .supported = xpcs_10gbaser_features,
+ .an_mode = DW_10GBASER,
+ .pma_config = xpcs_10gbaser_pma_config,
+ }, {
+ .interface = PHY_INTERFACE_MODE_10GBASEX,
+ .supported = xpcs_10gbasex_features,
+ .an_mode = DW_10GBASEX,
+ .pma_config = xpcs_10gbasex_pma_config,
+ }, {
+ .interface = PHY_INTERFACE_MODE_XAUI,
+ .supported = xpcs_10gbasex_features,
+ .an_mode = DW_10GBASEX,
+ .pma_config = xpcs_10gbasex_pma_config,
+ }, {
+ }
+};
+
static const struct dw_xpcs_desc xpcs_desc_list[] = {
{
.id = DW_XPCS_ID,
@@ -1489,6 +1679,10 @@ static const struct dw_xpcs_desc xpcs_desc_list[] = {
.id = NXP_SJA1110_XPCS_ID,
.mask = DW_XPCS_ID_MASK,
.compat = nxp_sja1110_xpcs_compat,
+ }, {
+ .id = BT1_XGMAC_XPCS_ID,
+ .mask = DW_XPCS_ID_MASK,
+ .compat = bt1_xpcs_compat,
},
};
@@ -1608,6 +1802,12 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev)
if (ret)
goto out_clear_clks;
+ ret = xpcs_read(xpcs, MDIO_MMD_VEND1, DW_SR_CTRL_MMD_CTRL);
+ if (ret < 0)
+ goto out_clear_clks;
+
+ xpcs->mmd_ctrl = ret;
+
xpcs_get_interfaces(xpcs, xpcs->pcs.supported_interfaces);
if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID ||
diff --git a/drivers/net/pcs/pcs-xpcs.h b/drivers/net/pcs/pcs-xpcs.h
index 929fa238445ed..0b3c972c9ee5d 100644
--- a/drivers/net/pcs/pcs-xpcs.h
+++ b/drivers/net/pcs/pcs-xpcs.h
@@ -12,6 +12,22 @@
/* Vendor regs access */
#define DW_VENDOR BIT(15)
+/* VR_XS_PMA */
+#define DW_VR_XS_PMA_GEN5_10G_MPLL_CTRL 0x007a
+#define DW_VR_XS_PMA_REF_CLK_SEL_CORE BIT(13)
+#define DW_VR_XS_PMA_GEN5_10G_GEN_CTRL 0x009c
+#define DW_VR_XS_PMA_LANE_MODE GENMASK(3, 0)
+#define DW_VR_XS_PMA_LANE_MODE_KX 0x3
+#define DW_VR_XS_PMA_LANE_MODE_KX4 0x4
+#define DW_VR_XS_PMA_LANE_MODE_KR 0x5
+#define DW_VR_XS_PMA_LANE_MODE_SGMII 0x6
+#define DW_VR_XS_PMA_LANE_MODE_RXAUI 0x8
+#define DW_VR_XS_PMA_LINK_WIDTH GENMASK(10, 8)
+#define DW_VR_XS_PMA_LINK_WIDTH_1 0x0
+#define DW_VR_XS_PMA_LINK_WIDTH_2 0x1
+#define DW_VR_XS_PMA_LINK_WIDTH_4 0x2
+#define DW_VR_XS_PMA_LANE_PWR_OFF GENMASK(15, 12)
+
/* VR_XS_PCS */
#define DW_USXGMII_RST BIT(10)
#define DW_USXGMII_EN BIT(9)
@@ -19,7 +35,12 @@
#define DW_VR_RST BIT(15)
#define DW_EN_VSMMD1 BIT(13)
#define DW_CL37_BP BIT(12)
+#define DW_VR_XS_PCS_XAUI_MODE_CTRL 0x0004
+#define DW_VR_XS_PCS_RXAUI_MODE BIT(0)
+#define DW_VR_XS_PCS_MRVL_RXAUI BIT(1)
#define DW_VR_XS_PCS_DIG_STS 0x0010
+#define DW_PSEQ_STATE GENMASK(4, 2)
+#define DW_PSEQ_TXRX_STABLE 0x100
#define DW_RXFIFO_ERR GENMASK(6, 5)
#define DW_PSEQ_ST GENMASK(4, 2)
#define DW_PSEQ_ST_GOOD FIELD_PREP(GENMASK(4, 2), 0x4)
@@ -52,6 +73,13 @@
#define DW_C73_2500KX BIT(0)
#define DW_C73_5000KR BIT(1)
+/* VR_CTRL_MMD */
+#define DW_SR_CTRL_MMD_CTRL 0x0009
+#define DW_SR_CTRL_AN_MMD_EN BIT(0)
+#define DW_SR_CTRL_PCS_XS_MMD_EN BIT(1)
+#define DW_SR_CTRL_MII_MMD_EN BIT(2)
+#define DW_SR_CTRL_PMA_MMD_EN BIT(3)
+
/* Clause 37 Defines */
/* VR MII MMD registers offsets */
#define DW_VR_MII_DIG_CTRL1 0x8000
@@ -110,6 +138,7 @@ struct dw_xpcs {
const struct dw_xpcs_desc *desc;
struct mdio_device *mdiodev;
struct clk_bulk_data clks[DW_XPCS_NUM_CLKS];
+ u16 mmd_ctrl;
struct phylink_pcs pcs;
phy_interface_t interface;
bool need_reset;
@@ -119,8 +148,17 @@ struct dw_xpcs {
int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg);
int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val);
int xpcs_modify(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set);
+int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg, u16 mask, u16 set);
+int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg);
+int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg, u16 val);
int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg);
int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val);
+int xpcs_poll_val(struct dw_xpcs *xpcs, int dev, int reg, u16 mask, u16 goal);
+int xpcs_soft_reset(struct dw_xpcs *xpcs);
+int xpcs_vendor_reset(struct dw_xpcs *xpcs);
+
+int xpcs_10gbaser_pma_config(struct dw_xpcs *xpcs);
+int xpcs_10gbasex_pma_config(struct dw_xpcs *xpcs);
int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs);
int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs);
int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs);
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 7b73332a13d95..a8272a7c869f2 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -277,6 +277,20 @@ config MARVELL_88X2222_PHY
Support for the Marvell 88X2222 Dual-port Multi-speed Ethernet
Transceiver.
+config MARVELL_88X2222_GPIO
+ bool "Marvell 88X2222 SFP+ GPIOs support"
+ depends on MARVELL_88X2222_PHY
+ depends on GPIOLIB && GPIOLIB_IRQCHIP
+ help
+ Support for the Marvell 88X2222 PHY SFP+/GPIO signals.
+
+config MARVELL_88X2222_I2C
+ bool "Marvell 88X2222 SFP+ I2C support"
+ depends on MARVELL_88X2222_PHY
+ depends on I2C
+ help
+ Support for the Marvell 88X2222 PHY SFP+/I2C signals.
+
config MAXLINEAR_GPHY
tristate "Maxlinear Ethernet PHYs"
select POLYNOMIAL if HWMON
diff --git a/drivers/net/phy/marvell-88x2222.c b/drivers/net/phy/marvell-88x2222.c
index ba1bbb6c63d64..f8fd5c71f4915 100644
--- a/drivers/net/phy/marvell-88x2222.c
+++ b/drivers/net/phy/marvell-88x2222.c
@@ -3,12 +3,19 @@
* Marvell 88x2222 dual-port multi-speed ethernet transceiver.
*
* Supports:
- * XAUI on the host side.
+ * XAUI or 10GBase-R on the host side.
* 1000Base-X or 10GBase-R on the line side.
* SGMII over 1000Base-X.
*/
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/phy.h>
+#include <linux/gpio/driver.h>
#include <linux/delay.h>
#include <linux/mdio.h>
#include <linux/marvell_phy.h>
@@ -16,12 +23,17 @@
#include <linux/phy_port.h>
#include <linux/netdevice.h>
+#include <dt-bindings/net/mv-phy-88x2222.h>
+
/* Port PCS Configuration */
#define MV_PCS_CONFIG 0xF002
+#define MV_PCS_HOST_PCS_SELECT GENMASK(6, 0)
+#define MV_PCS_HOST_10GBR 0x71
#define MV_PCS_HOST_XAUI 0x73
-#define MV_PCS_LINE_10GBR (0x71 << 8)
-#define MV_PCS_LINE_1GBX_AN (0x7B << 8)
-#define MV_PCS_LINE_SGMII_AN (0x7F << 8)
+#define MV_PCS_LINE_PCS_SELECT GENMASK(14, 8)
+#define MV_PCS_LINE_10GBR 0x71
+#define MV_PCS_LINE_1GBX_AN 0x7B
+#define MV_PCS_LINE_SGMII_AN 0x7F
/* Port Reset and Power Down */
#define MV_PORT_RST 0xF003
@@ -29,6 +41,74 @@
#define MV_HOST_RST_SW BIT(7)
#define MV_PORT_RST_SW (MV_LINE_RST_SW | MV_HOST_RST_SW)
+/* GPIO Interrupt Enable */
+#define MV_GPIO_INT_EN 0xF010
+
+/* GPIO Interrupt Status */
+#define MV_GPIO_INT_STAT 0xF011
+
+/* GPIO Input/Output data */
+#define MV_GPIO_DATA 0xF012
+
+/* GPIO Tristate control */
+#define MV_GPIO_TRISTATE_CTRL 0xF013
+
+/* GPIO Interrupt type 1 (GPIOs 0 - 3) */
+#define MV_GPIO_INT_TYPE1 0xF014
+#define MV_GPIO_INT_TYPE_PIN0 GENMASK(2, 0)
+#define MV_GPIO_INT_NO_IRQ 0
+#define MV_GPIO_INT_LEVEL_LOW 2
+#define MV_GPIO_INT_LEVEL_HIGH 3
+#define MV_GPIO_INT_EDGE_FALLING 4
+#define MV_GPIO_INT_EDGE_RISING 5
+#define MV_GPIO_INT_EDGE_BOTH 7
+#define MV_GPIO_INT_TYPE_PIN1 GENMASK(6, 4)
+#define MV_GPIO_FUNC_TX_FLT_PIN1 BIT(7)
+#define MV_GPIO_INT_TYPE_PIN2 GENMASK(10, 8)
+#define MV_GPIO_FUNC_RX_LOS_PIN2 BIT(11)
+#define MV_GPIO_INT_TYPE_PIN3 GENMASK(14, 12)
+
+/* GPIO Interrupt type 2 (GPIOs 4 - 7) */
+#define MV_GPIO_INT_TYPE2 0xF015
+#define MV_GPIO_INT_TYPE_PIN4 GENMASK(2, 0)
+#define MV_GPIO_FUNC_LED0_PIN4 BIT(3)
+#define MV_GPIO_INT_TYPE_PIN5 GENMASK(6, 4)
+#define MV_GPIO_FUNC_LED1_PIN5 BIT(7)
+#define MV_GPIO_INT_TYPE_PIN6 GENMASK(10, 8)
+#define MV_GPIO_FUNC_MPC_PIN6 BIT(11)
+#define MV_GPIO_INT_TYPE_PIN7 GENMASK(14, 12)
+#define MV_GPIO_FUNC_TOD_PIN7 BIT(15)
+
+/* GPIO Interrupt type 3 (GPIOs 8, 10, 11) */
+#define MV_GPIO_INT_TYPE3 0xF016
+#define MV_GPIO_INT_TYPE_PIN8 GENMASK(2, 0)
+#define MV_GPIO_FUNC_TX_DIS_PIN8 GENMASK(4, 3)
+#define MV_GPIO_FUNC_TX_DIS_LED 0
+#define MV_GPIO_FUNC_TX_DIS_PIN 1
+#define MV_GPIO_FUNC_TX_DIS_REG 2
+#define MV_GPIO_INT_TYPE_PIN10 GENMASK(10, 8)
+#define MV_GPIO_FUNC_SDA_PIN10 BIT(11)
+#define MV_GPIO_INT_TYPE_PIN11 GENMASK(14, 12)
+#define MV_GPIO_FUNC_SCL_PIN11 BIT(15)
+
+/* GPIO Interrupt type 1-3 helpers */
+#define MV_GPIO_INT_TYPE_REG(_pin) \
+ ((_pin) / 4)
+#define MV_GPIO_INT_TYPE_PREP(_pin, _val) \
+ ((_val) << ((_pin) % 4) * 4)
+#define MV_GPIO_INT_TYPE_MASK(_pin) \
+ MV_GPIO_INT_TYPE_PREP(_pin, GENMASK(2, 0))
+#define MV_GPIO_FUNC_PREP(_pin, _val) \
+ ((_val) << (3 + ((_pin) % 4) * 4))
+#define MV_GPIO_FUNC_MASK(_pin) \
+ MV_GPIO_FUNC_PREP(_pin, (_pin) != 8 ? BIT(0) : GENMASK(1, 0))
+
+/* Port Interrupt Status */
+#define MV_PORT_INT_STAT 0xF040
+#define MV_PORT_INT_PCS_LINE BIT(0)
+#define MV_PORT_INT_PCS_HOST BIT(2)
+#define MV_PORT_INT_GPIO BIT(3)
+
/* PMD Receive Signal Detect */
#define MV_RX_SIGNAL_DETECT 0x000A
#define MV_RX_SIGNAL_DETECT_GLOBAL BIT(0)
@@ -42,6 +122,111 @@
/* 1000Base-X Auto-Negotiation Advertisement Register */
#define MV_1GBX_ADVERTISE (0x2000 + MII_ADVERTISE)
+/* Two Wire Interface Caching Control/Status Register */
+#define MV_TWSI_CACHE_CTRL 0x8000
+#define MV_TWSI_CACHE_A0_CTRL GENMASK(1, 0)
+#define MV_TWSI_CACHE_NO_AUTO 0
+#define MV_TWSI_CACHE_AT_PLUGIN 1
+#define MV_TWSI_CACHE_AT_PLUGIN_POLL 2
+#define MV_TWSI_CACHE_MANUAL 3
+#define MV_TWSI_CACHE_A0_CMD_STAT GENMASK(3, 2)
+#define MV_TWSI_CACHE_NO_UPDATE 0
+#define MV_TWSI_CACHE_UPDATED_ONCE 1
+#define MV_TWSI_CACHE_IS_LOADING 2
+#define MV_TWSI_CACHE_FAILED 3
+#define MV_TWSI_CACHE_A0_VALID BIT(9)
+#define MV_TWSI_RESET BIT(10)
+#define MV_TWSI_CACHE_A2_CTRL GENMASK(12, 11)
+#define MV_TWSI_CACHE_A2_CMD_STAT GENMASK(14, 13)
+#define MV_TWSI_CACHE_A2_VALID BIT(15)
+
+/* Two Wire Interface Memory Address Register */
+#define MV_TWSI_MEM_ADDR 0x8001
+#define MV_TWSI_BYTE_ADDR GENMASK(7, 0)
+#define MV_TWSI_BYTE_READ BIT(8)
+#define MV_TWSI_SLV_ADDR GENMASK(15, 9)
+
+/* Two Wire Interface Memory Read Data and Status Register */
+#define MV_TWSI_MEM_READ_STAT 0x8002
+#define MV_TWSI_MEM_READ_DATA GENMASK(7, 0)
+#define MV_TWSI_STAT GENMASK(10, 8)
+#define MV_TWSI_READY 0
+#define MV_TWSI_CMD_DONE 1
+#define MV_TWSI_CMD_IN_PROG 2
+#define MV_TWSI_CMD_WDONE_RFAIL 3
+#define MV_TWSI_CMD_FAIL 5
+#define MV_TWSI_BUSY 7
+#define MV_TWSI_CACHE_ECC_UNCOR_ERR BIT(11)
+#define MV_TWSI_CACHE_ECC_COR_ERR BIT(12)
+
+/* Two Wire Interface Memory Write Data and Control Register */
+#define MV_TWSI_MEM_WRITE_CTRL 0x8003
+#define MV_TWSI_MEM_WRITE_DATA GENMASK(7, 0)
+#define MV_TWSI_MEM_AUTO_RBACK BIT(9)
+#define MV_TWSI_MEM_WRITE_TIME GENMASK(15, 12)
+
+/* Two Wire Interface Caching Delay Register */
+#define MV_TWSI_CACHE_DELAY 0x8004
+#define MV_TWSI_CACHE_A2_ADDR_MSB BIT(0)
+#define MV_TWSI_CACHE_A2_SLV_ADDR GENMASK(7, 1)
+#define MV_TWSI_CACHE_RELOAD_FREQ GENMASK(10, 9)
+#define MV_TWSI_CACHE_RELOAD_250MS 0
+#define MV_TWSI_CACHE_RELOAD_500MS 1
+#define MV_TWSI_CACHE_RELOAD_1S 2
+#define MV_TWSI_CACHE_RELOAD_2S 3
+#define MV_TWSI_CACHE_ECC_UNCOR_INT_EN BIT(11)
+#define MV_TWSI_CACHE_ECC_COR_INT_EN BIT(12)
+#define MV_TWSI_CACHE_AUTO_DELAY GENMASK(15, 13)
+#define MV_TWSI_CACHE_NO_DELAY 0
+#define MV_TWSI_CACHE_DELAY_250MS 1
+#define MV_TWSI_CACHE_DELAY_500MS 2
+#define MV_TWSI_CACHE_DELAY_1S 3
+#define MV_TWSI_CACHE_DELAY_2S 4
+#define MV_TWSI_CACHE_DELAY_4S 5
+#define MV_TWSI_CACHE_DELAY_8S 6
+#define MV_TWSI_CACHE_AUTO_DIS 7
+
+/* Two Wire Interface EEPROM Cache Page A0 Registers */
+#define MV_TWSI_CACHE_EEPROM_A0 0x8007
+
+/* Two Wire Interface EEPROM Cache Page A2 Registers */
+#define MV_TWSI_CACHE_EEPROM_A2 0x8087
+
+/* 10GBase-R Interrupt Enable Register */
+#define MV_10GBR_INT_EN 0x8000
+#define MV_10GBR_INT_BLOCK_LOCK BIT(0)
+#define MV_10GBR_INT_HIGH_BER BIT(1)
+#define MV_10GBR_INT_LINK_STAT BIT(2)
+#define MV_10GBR_INT_LOCAL_RXFAULT BIT(10)
+#define MV_10GBR_INT_LOCAL_TXFAULT BIT(11)
+#define MV_10GBR_INT_MASK (MV_10GBR_INT_LINK_STAT | \
+ MV_10GBR_INT_LOCAL_RXFAULT | \
+ MV_10GBR_INT_LOCAL_TXFAULT)
+
+/* 10GBase-R Interrupt Status Register */
+#define MV_10GBR_INT_STAT 0x8001
+
+/* 1000Base-X Interrupt Enable Register */
+#define MV_1GBX_INT_EN 0xA001
+#define MV_1GBX_INT_FALSE_CARRIER BIT(7)
+#define MV_1GBX_INT_SYMBOL_ERROR BIT(8)
+#define MV_1GBX_INT_LINK_UP BIT(9)
+#define MV_1GBX_INT_LINK_DOWN BIT(10)
+#define MV_1GBX_INT_AN_COMPLETED BIT(11)
+#define MV_1GBX_INT_PAGE_RECEIVED BIT(12)
+#define MV_1GBX_INT_DUPLEX_CHANGED BIT(13)
+#define MV_1GBX_INT_SPEED_CHANGED BIT(14)
+#define MV_1GBX_INT_MASK (MV_1GBX_INT_FALSE_CARRIER | \
+ MV_1GBX_INT_SYMBOL_ERROR | \
+ MV_1GBX_INT_LINK_UP | \
+ MV_1GBX_INT_LINK_DOWN | \
+ MV_1GBX_INT_AN_COMPLETED | \
+ MV_1GBX_INT_DUPLEX_CHANGED | \
+ MV_1GBX_INT_SPEED_CHANGED)
+
+/* 1000Base-X Interrupt Status Register */
+#define MV_1GBX_INT_STAT 0xA002
+
/* 1000Base-X PHY Specific Status Register */
#define MV_1GBX_PHY_STAT 0xA003
#define MV_1GBX_PHY_STAT_AN_RESOLVED BIT(11)
@@ -49,6 +234,62 @@
#define MV_1GBX_PHY_STAT_SPEED100 BIT(14)
#define MV_1GBX_PHY_STAT_SPEED1000 BIT(15)
+/* Host-side 10GBase-R PCS Status 1 Register */
+#define MV_HOST_10GBR_PCS_STAT1 (0x0 + MDIO_STAT1)
+
+/* Host-side XAUI PCS Status 1 Register */
+#define MV_HOST_XAUI_PCS_STAT1 (0x1000 + MDIO_STAT1)
+
+/* Host-side 10GBase-R Interrupt Enable Register */
+#define MV_HOST_10GBR_INT_EN 0x8000
+#define MV_HOST_10GBR_INT_BLOCK_LOCK BIT(0)
+#define MV_HOST_10GBR_INT_HIGH_BER BIT(1)
+#define MV_HOST_10GBR_INT_LINK_STAT BIT(2)
+#define MV_HOST_10GBR_INT_LOCAL_RXFAULT BIT(10)
+#define MV_HOST_10GBR_INT_LOCAL_TXFAULT BIT(11)
+#define MV_HOST_10GBR_INT_MASK (MV_HOST_10GBR_INT_LINK_STAT | \
+ MV_HOST_10GBR_INT_LOCAL_RXFAULT | \
+ MV_HOST_10GBR_INT_LOCAL_TXFAULT)
+
+/* Host-side 10GBase-R Interrupt Status Register */
+#define MV_HOST_10GBR_INT_STAT 0x8001
+
+/* Host-side XAUI Interrupt Enable 1 Register */
+#define MV_HOST_XAUI_INT_EN1 0x9001
+#define MV_HOST_XAUI_INT_LINK_UP BIT(2)
+#define MV_HOST_XAUI_INT_LINK_DOWN BIT(3)
+#define MV_HOST_XAUI_INT_MASK1 (MV_HOST_XAUI_INT_LINK_UP | \
+ MV_HOST_XAUI_INT_LINK_DOWN)
+
+/* Host-side XAUI Interrupt Enable 2 Register */
+#define MV_HOST_XAUI_INT_EN2 0x9002
+#define MV_HOST_XAUI_INT_LANE0_SYNC BIT(0)
+#define MV_HOST_XAUI_INT_LANE1_SYNC BIT(1)
+#define MV_HOST_XAUI_INT_LANE2_SYNC BIT(2)
+#define MV_HOST_XAUI_INT_LANE3_SYNC BIT(3)
+#define MV_HOST_XAUI_INT_LANE0_ENERGY BIT(4)
+#define MV_HOST_XAUI_INT_LANE1_ENERGY BIT(5)
+#define MV_HOST_XAUI_INT_LANE2_ENERGY BIT(6)
+#define MV_HOST_XAUI_INT_LANE3_ENERGY BIT(7)
+#define MV_HOST_XAUI_INT_TXFAULT BIT(8)
+#define MV_HOST_XAUI_INT_RXFAULT BIT(9)
+#define MV_HOST_XAUI_INT_MASK2 (MV_HOST_XAUI_INT_TXFAULT | \
+ MV_HOST_XAUI_INT_RXFAULT)
+
+/* Host-side XAUI Interrupt Status 1 Register */
+#define MV_HOST_XAUI_INT_STAT1 0x9003
+
+/* Host-side XAUI Interrupt Status 2 Register */
+#define MV_HOST_XAUI_INT_STAT2 0x9004
+
+/* GPIO controller settings (#9 always invalid, #0 and #3 always available) */
+#define MV_GPIO_NUM 12
+#define MV_GPIO_VAL_MASK 0xDFF
+#define MV_GPIO_INT_UPD_FLAG BIT(31)
+
+/* I2C controller settings */
+#define MV_I2C_POLL_NUM 3
+
#define AUTONEG_TIMEOUT 3
struct mv2222_data {
@@ -57,6 +298,27 @@ struct mv2222_data {
bool sfp_link;
};
+#ifdef CONFIG_MARVELL_88X2222_GPIO
+struct mv2222_gpio {
+ struct gpio_chip gc;
+ struct phy_device *phydev;
+ unsigned int irq;
+ u32 cache_val_mask;
+ u32 cache_data;
+ u32 cache_tri_ctrl;
+ u32 cache_int_en;
+ u32 cache_int_type[MV_GPIO_INT_TYPE_REG(MV_GPIO_NUM)];
+ struct mutex cache_lock;
+};
+#endif
+
+#ifdef CONFIG_MARVELL_88X2222_I2C
+struct mv2222_i2c {
+ struct i2c_adapter ia;
+ struct phy_device *phydev;
+};
+#endif
+
/* SFI PMA transmit enable */
static int mv2222_tx_enable(struct phy_device *phydev)
{
@@ -71,20 +333,34 @@ static int mv2222_tx_disable(struct phy_device *phydev)
MDIO_PMD_TXDIS_GLOBAL);
}
-static int mv2222_soft_reset(struct phy_device *phydev)
+static int __mv2222_soft_reset(struct phy_device *phydev, u16 mask)
{
int val, ret;
- ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PORT_RST,
- MV_PORT_RST_SW);
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PORT_RST, mask);
if (ret < 0)
return ret;
return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND2, MV_PORT_RST,
- val, !(val & MV_PORT_RST_SW),
+ val, !(val & mask),
5000, 1000000, true);
}
+static inline int mv2222_host_reset(struct phy_device *phydev)
+{
+ return __mv2222_soft_reset(phydev, MV_HOST_RST_SW);
+}
+
+static inline int mv2222_line_reset(struct phy_device *phydev)
+{
+ return __mv2222_soft_reset(phydev, MV_LINE_RST_SW);
+}
+
+static inline int mv2222_soft_reset(struct phy_device *phydev)
+{
+ return __mv2222_soft_reset(phydev, MV_PORT_RST_SW);
+}
+
static int mv2222_disable_aneg(struct phy_device *phydev)
{
int ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL,
@@ -92,7 +368,7 @@ static int mv2222_disable_aneg(struct phy_device *phydev)
if (ret < 0)
return ret;
- return mv2222_soft_reset(phydev);
+ return mv2222_line_reset(phydev);
}
static int mv2222_enable_aneg(struct phy_device *phydev)
@@ -102,7 +378,7 @@ static int mv2222_enable_aneg(struct phy_device *phydev)
if (ret < 0)
return ret;
- return mv2222_soft_reset(phydev);
+ return mv2222_line_reset(phydev);
}
static int mv2222_set_sgmii_speed(struct phy_device *phydev)
@@ -190,23 +466,46 @@ static bool mv2222_is_sgmii_capable(struct phy_device *phydev)
priv->supported));
}
+static int mv2222_config_host(struct phy_device *phydev)
+{
+ u16 val;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_XAUI:
+ val = FIELD_PREP(MV_PCS_HOST_PCS_SELECT, MV_PCS_HOST_XAUI);
+ break;
+ case PHY_INTERFACE_MODE_10GBASER:
+ val = FIELD_PREP(MV_PCS_HOST_PCS_SELECT, MV_PCS_HOST_10GBR);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG,
+ MV_PCS_HOST_PCS_SELECT, val);
+}
+
static int mv2222_config_line(struct phy_device *phydev)
{
struct mv2222_data *priv = phydev->priv;
+ u16 val;
switch (priv->line_interface) {
case PHY_INTERFACE_MODE_10GBASER:
- return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG,
- MV_PCS_HOST_XAUI | MV_PCS_LINE_10GBR);
+ val = FIELD_PREP(MV_PCS_LINE_PCS_SELECT, MV_PCS_LINE_10GBR);
+ break;
case PHY_INTERFACE_MODE_1000BASEX:
- return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG,
- MV_PCS_HOST_XAUI | MV_PCS_LINE_1GBX_AN);
+ val = FIELD_PREP(MV_PCS_LINE_PCS_SELECT, MV_PCS_LINE_1GBX_AN);
+ break;
case PHY_INTERFACE_MODE_SGMII:
- return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG,
- MV_PCS_HOST_XAUI | MV_PCS_LINE_SGMII_AN);
+ val = FIELD_PREP(MV_PCS_LINE_PCS_SELECT, MV_PCS_LINE_SGMII_AN);
+ break;
default:
return -EINVAL;
}
+
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG,
+ MV_PCS_LINE_PCS_SELECT, val);
}
/* Switch between 1G (1000Base-X/SGMII) and 10G (10GBase-R) modes */
@@ -299,12 +598,32 @@ static int mv2222_config_aneg(struct phy_device *phydev)
return mv2222_enable_aneg(phydev);
}
+/* The link state and some other fields in the status registers are latched
+ * low/high so that the momentary events could be detected. Do not double-read
+ * the status in polling mode to detect such a short flag changes except when
+ * it's forced to be required (i.e. when the link was already down).
+ */
+static int mv2222_read_mmd_latched(struct phy_device *phydev, int devad, u32 reg,
+ bool force)
+{
+ int ret;
+
+ if (!phy_polling_mode(phydev) || force) {
+ ret = phy_read_mmd(phydev, devad, reg);
+ if (ret < 0)
+ return ret;
+ }
+
+ return phy_read_mmd(phydev, devad, reg);
+}
+
static int mv2222_aneg_done(struct phy_device *phydev)
{
int ret;
if (mv2222_is_10g_capable(phydev)) {
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
+ ret = mv2222_read_mmd_latched(phydev, MDIO_MMD_PCS, MDIO_STAT1,
+ !phydev->link);
if (ret < 0)
return ret;
@@ -325,7 +644,8 @@ static int mv2222_read_status_10g(struct phy_device *phydev)
static int timeout;
int val, link = 0;
- val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
+ val = mv2222_read_mmd_latched(phydev, MDIO_MMD_PCS, MDIO_STAT1,
+ !phydev->link);
if (val < 0)
return val;
@@ -361,7 +681,8 @@ static int mv2222_read_status_1g(struct phy_device *phydev)
static int timeout;
int val, link = 0;
- val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT);
+ val = mv2222_read_mmd_latched(phydev, MDIO_MMD_PCS, MV_1GBX_STAT,
+ !phydev->link);
if (val < 0)
return val;
@@ -408,6 +729,29 @@ static int mv2222_read_status_1g(struct phy_device *phydev)
return link;
}
+static bool mv2222_iface_is_operational(struct phy_device *phydev)
+{
+ int reg, val;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_XAUI:
+ reg = MV_HOST_XAUI_PCS_STAT1;
+ break;
+ case PHY_INTERFACE_MODE_10GBASER:
+ reg = MV_HOST_10GBR_PCS_STAT1;
+ break;
+ default:
+ return false;
+ }
+
+ val = mv2222_read_mmd_latched(phydev, MDIO_MMD_PHYXS, reg,
+ !phydev->link);
+ if (val < 0 || !(val & MDIO_STAT1_LSTATUS))
+ return false;
+
+ return true;
+}
+
static bool mv2222_link_is_operational(struct phy_device *phydev)
{
struct mv2222_data *priv = phydev->priv;
@@ -432,8 +776,15 @@ static int mv2222_read_status(struct phy_device *phydev)
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
- if (!mv2222_link_is_operational(phydev))
+ if (!mv2222_iface_is_operational(phydev)) {
+ phydev_dbg(phydev, "Host interface isn't operational\n");
return 0;
+ }
+
+ if (!mv2222_link_is_operational(phydev)) {
+ phydev_dbg(phydev, "Line side isn't operational\n");
+ return 0;
+ }
if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER)
link = mv2222_read_status_10g(phydev);
@@ -448,6 +799,276 @@ static int mv2222_read_status(struct phy_device *phydev)
return 0;
}
+static int mv2222_config_intr_host_10gbr(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_10GBASER &&
+ phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_10GBR_INT_STAT);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_10GBR_INT_EN,
+ MV_HOST_10GBR_INT_MASK);
+ } else {
+ ret = phy_write_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_10GBR_INT_EN, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_10GBR_INT_STAT);
+ }
+
+ return ret;
+}
+
+static int mv2222_config_intr_host_xaui(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_XAUI &&
+ phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_XAUI_INT_STAT1);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_XAUI_INT_STAT2);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_XAUI_INT_EN1,
+ MV_HOST_XAUI_INT_MASK1);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_XAUI_INT_EN2,
+ MV_HOST_XAUI_INT_MASK2);
+ } else {
+ ret = phy_write_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_XAUI_INT_EN2, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_XAUI_INT_EN1, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_XAUI_INT_STAT2);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_XAUI_INT_STAT1);
+ }
+
+ return ret;
+}
+
+static int mv2222_config_intr_10g(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_10GBR_INT_STAT);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MV_10GBR_INT_EN,
+ MV_10GBR_INT_MASK);
+ } else {
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MV_10GBR_INT_EN, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_10GBR_INT_STAT);
+ }
+
+ return ret;
+}
+
+static int mv2222_config_intr_1g(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_INT_STAT);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_INT_EN,
+ MV_1GBX_INT_MASK);
+ } else {
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_INT_EN, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_INT_STAT);
+ }
+
+ return ret;
+}
+
+static int mv2222_config_intr(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = mv2222_config_intr_10g(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = mv2222_config_intr_1g(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = mv2222_config_intr_host_10gbr(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = mv2222_config_intr_host_xaui(phydev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mv2222_handle_interrupt_host_10gbr(struct phy_device *phydev)
+{
+ int val;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_10GBR_INT_STAT);
+ if (val < 0)
+ return val;
+
+ if (val & MV_HOST_10GBR_INT_LINK_STAT)
+ phydev_dbg(phydev, "Host link status changed\n");
+
+ if (val & MV_HOST_10GBR_INT_LOCAL_RXFAULT)
+ phydev_dbg(phydev, "Host Rx fault detected\n");
+
+ if (val & MV_HOST_10GBR_INT_LOCAL_TXFAULT)
+ phydev_dbg(phydev, "Host Tx fault detected\n");
+
+ return 0;
+}
+
+static int mv2222_handle_interrupt_host_xaui(struct phy_device *phydev)
+{
+ int val1, val2;
+
+ val1 = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_XAUI_INT_STAT1);
+ if (val1 < 0)
+ return val1;
+
+ val2 = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MV_HOST_XAUI_INT_STAT2);
+ if (val2 < 0)
+ return val2;
+
+ if (val1 & MV_HOST_XAUI_INT_LINK_UP)
+ phydev_dbg(phydev, "Host link is up\n");
+
+ if (val1 & MV_HOST_XAUI_INT_LINK_DOWN)
+ phydev_dbg(phydev, "Host link is down\n");
+
+ if (val2 & MV_HOST_XAUI_INT_TXFAULT)
+ phydev_dbg(phydev, "Host Tx fault detected\n");
+
+ if (val2 & MV_HOST_XAUI_INT_RXFAULT)
+ phydev_dbg(phydev, "Host Rx fault detected\n");
+
+ return 0;
+}
+
+static int mv2222_handle_interrupt_10g(struct phy_device *phydev)
+{
+ int val;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_10GBR_INT_STAT);
+ if (val < 0)
+ return val;
+
+ if (val & MV_10GBR_INT_LINK_STAT)
+ phydev_dbg(phydev, "Line link status changed\n");
+
+ if (val & MV_10GBR_INT_LOCAL_RXFAULT)
+ phydev_dbg(phydev, "Line Rx fault detected\n");
+
+ if (val & MV_10GBR_INT_LOCAL_TXFAULT)
+ phydev_dbg(phydev, "Line Tx fault detected\n");
+
+ return 0;
+}
+
+static int mv2222_handle_interrupt_1g(struct phy_device *phydev)
+{
+ int val;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_INT_STAT);
+ if (val < 0)
+ return val;
+
+ if (val & MV_1GBX_INT_FALSE_CARRIER)
+ phydev_dbg(phydev, "Line false carrier detected\n");
+
+ if (val & MV_1GBX_INT_SYMBOL_ERROR)
+ phydev_dbg(phydev, "Line symbol error detected\n");
+
+ if (val & MV_1GBX_INT_LINK_UP)
+ phydev_dbg(phydev, "Line link is up\n");
+
+ if (val & MV_1GBX_INT_LINK_DOWN)
+ phydev_dbg(phydev, "Line link is down\n");
+
+ if (val & MV_1GBX_INT_AN_COMPLETED)
+ phydev_dbg(phydev, "Line AN is completed\n");
+
+ if (val & MV_1GBX_INT_DUPLEX_CHANGED)
+ phydev_dbg(phydev, "Line link duplex changed\n");
+
+ if (val & MV_1GBX_INT_SPEED_CHANGED)
+ phydev_dbg(phydev, "Line link speed changed\n");
+
+ return 0;
+}
+
+static irqreturn_t mv2222_handle_interrupt(struct phy_device *phydev)
+{
+ int val, ret;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_PORT_INT_STAT);
+ if (val < 0)
+ goto err_set_state;
+
+ if (!(val & (MV_PORT_INT_PCS_HOST | MV_PORT_INT_PCS_LINE)))
+ return IRQ_NONE;
+
+ if (val & MV_PORT_INT_PCS_HOST) {
+ ret = mv2222_handle_interrupt_host_10gbr(phydev);
+ if (ret < 0)
+ goto err_set_state;
+
+ ret = mv2222_handle_interrupt_host_xaui(phydev);
+ if (ret < 0)
+ goto err_set_state;
+ }
+
+ if (val & MV_PORT_INT_PCS_LINE) {
+ ret = mv2222_handle_interrupt_10g(phydev);
+ if (ret < 0)
+ goto err_set_state;
+
+ ret = mv2222_handle_interrupt_1g(phydev);
+ if (ret < 0)
+ goto err_set_state;
+ }
+
+ phy_trigger_machine(phydev);
+
+ return IRQ_HANDLED;
+
+err_set_state:
+ phy_error(phydev);
+
+ return IRQ_NONE;
+}
+
static int mv2222_resume(struct phy_device *phydev)
{
return mv2222_tx_enable(phydev);
@@ -467,12 +1088,788 @@ static int mv2222_get_features(struct phy_device *phydev)
static int mv2222_config_init(struct phy_device *phydev)
{
- if (phydev->interface != PHY_INTERFACE_MODE_XAUI)
+ int ret;
+
+ ret = mv2222_config_host(phydev);
+ if (ret)
+ return ret;
+
+ return mv2222_host_reset(phydev);
+}
+
+#ifdef CONFIG_MARVELL_88X2222_GPIO
+/*
+ * Marvell 88x2222 GPIO control registers access is implemented by means of the
+ * private data caches. It has several reasons. First of all the IRQ chip
+ * implementation requires the IRQ-related settings applied in a non-atomic
+ * context for the slow asynchronously accessed bus. Thus caching the IRQ-masks
+ * and types is neccessary in order to flush the respective CSRs data in the
+ * may-sleep context. Second the implementation decreases a bus traffic which
+ * improves the accesses performance espcially in case of the bit-banged MDIO
+ * bus. Finally it prevents a race condition in the output value setup on the
+ * output GPIO-mode settings. The GPIO data register has a multiplexed input
+ * output data semantics. Any read from the CSRs returns either the input GPIO
+ * state or the output GPIO value. Meanwhile any write to the data register
+ * updates the output GPIO value. So the race condition may happen if the
+ * output value is updated before the output GPIO-mode is activated and if
+ * there is a parallel read-modify-write is performed to the GPIO data
+ * register. In that case the input GPIO state will be read and written back as
+ * the output GPIO value. Using the private data cache prevents that.
+ */
+
+static inline bool mv2222_gpio_offset_is_valid(unsigned int ofs)
+{
+ return ofs < MV_GPIO_NUM && ofs != 9;
+}
+
+static int mv2222_gpio_read_tri_ctrl(struct mv2222_gpio *gpio)
+{
+ int val;
+
+ mutex_lock(&gpio->cache_lock);
+
+ val = phy_read_mmd(gpio->phydev, MDIO_MMD_VEND2, MV_GPIO_TRISTATE_CTRL);
+ if (val < 0)
+ goto err_mutex_unlock;
+
+ gpio->cache_tri_ctrl = val;
+
+err_mutex_unlock:
+ mutex_unlock(&gpio->cache_lock);
+
+ return val;
+}
+
+static int mv2222_gpio_wthru_tri_ctrl(struct mv2222_gpio *gpio, u16 mask, u16 val)
+{
+ int ret;
+
+ mutex_lock(&gpio->cache_lock);
+
+ gpio->cache_tri_ctrl &= ~mask;
+ gpio->cache_tri_ctrl |= val;
+
+ ret = phy_write_mmd(gpio->phydev, MDIO_MMD_VEND2,
+ MV_GPIO_TRISTATE_CTRL, gpio->cache_tri_ctrl);
+
+ mutex_unlock(&gpio->cache_lock);
+
+ return ret;
+}
+
+static int mv2222_gpio_read_data(struct mv2222_gpio *gpio)
+{
+ int val;
+
+ mutex_lock(&gpio->cache_lock);
+
+ val = phy_read_mmd(gpio->phydev, MDIO_MMD_VEND2, MV_GPIO_DATA);
+ if (val < 0)
+ goto err_mutex_unlock;
+
+ gpio->cache_data = val;
+
+err_mutex_unlock:
+ mutex_unlock(&gpio->cache_lock);
+
+ return val;
+}
+
+static int mv2222_gpio_wthru_data(struct mv2222_gpio *gpio, u16 mask, u16 val)
+{
+ int ret;
+
+ mutex_lock(&gpio->cache_lock);
+
+ gpio->cache_data &= ~mask;
+ gpio->cache_data |= val;
+
+ ret = phy_write_mmd(gpio->phydev, MDIO_MMD_VEND2,
+ MV_GPIO_DATA, gpio->cache_data);
+
+ mutex_unlock(&gpio->cache_lock);
+
+ return ret;
+}
+
+static int mv2222_gpio_cache_int_en(struct mv2222_gpio *gpio,
+ unsigned int ofs, bool int_en)
+{
+ if (!mv2222_gpio_offset_is_valid(ofs))
return -EINVAL;
+ if (int_en)
+ gpio->cache_int_en |= BIT(ofs);
+ else
+ gpio->cache_int_en &= ~BIT(ofs);
+
+ gpio->cache_int_en |= MV_GPIO_INT_UPD_FLAG;
+
return 0;
}
+static int mv2222_gpio_cache_func(struct mv2222_gpio *gpio,
+ unsigned int ofs, bool gpio_en)
+{
+ u16 val;
+ int reg;
+
+ if (!mv2222_gpio_offset_is_valid(ofs))
+ return -EINVAL;
+
+ /* Pins #0 and #3 always work as GPIOs */
+ if (ofs == 0 || ofs == 3)
+ return !gpio_en ? -EINVAL : 0;
+
+ if (ofs == 8 && !gpio_en)
+ val = MV_GPIO_FUNC_TX_DIS_REG;
+ else
+ val = !!gpio_en;
+
+ reg = MV_GPIO_INT_TYPE_REG(ofs);
+ gpio->cache_int_type[reg] &= ~MV_GPIO_FUNC_MASK(ofs);
+ gpio->cache_int_type[reg] |= MV_GPIO_FUNC_PREP(ofs, val);
+
+ gpio->cache_int_type[reg] |= MV_GPIO_INT_UPD_FLAG;
+
+ return 0;
+}
+
+static int mv2222_gpio_cache_int_type(struct mv2222_gpio *gpio,
+ unsigned int ofs, unsigned int type)
+{
+ u16 val;
+ int reg;
+
+ if (!mv2222_gpio_offset_is_valid(ofs))
+ return -EINVAL;
+
+ switch (type) {
+ case IRQ_TYPE_NONE:
+ val = MV_GPIO_INT_NO_IRQ;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ val = MV_GPIO_INT_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ val = MV_GPIO_INT_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ val = MV_GPIO_INT_EDGE_BOTH;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ val = MV_GPIO_INT_LEVEL_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ val = MV_GPIO_INT_LEVEL_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ reg = MV_GPIO_INT_TYPE_REG(ofs);
+ gpio->cache_int_type[reg] &= ~MV_GPIO_INT_TYPE_MASK(ofs);
+ gpio->cache_int_type[reg] |= MV_GPIO_INT_TYPE_PREP(ofs, val);
+
+ gpio->cache_int_type[reg] |= MV_GPIO_INT_UPD_FLAG;
+
+ return 0;
+}
+
+static int mv2222_gpio_cache_int_flush(struct mv2222_gpio *gpio)
+{
+ int i, ret;
+
+ if (gpio->cache_int_en & MV_GPIO_INT_UPD_FLAG) {
+ ret = phy_write_mmd(gpio->phydev, MDIO_MMD_VEND2,
+ MV_GPIO_INT_EN, gpio->cache_int_en);
+ if (ret < 0)
+ return ret;
+
+ gpio->cache_int_en &= ~MV_GPIO_INT_UPD_FLAG;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(gpio->cache_int_type); ++i) {
+ if (!(gpio->cache_int_type[i] & MV_GPIO_INT_UPD_FLAG))
+ continue;
+
+ ret = phy_write_mmd(gpio->phydev, MDIO_MMD_VEND2,
+ MV_GPIO_INT_TYPE1 + i, gpio->cache_int_type[i]);
+ if (ret < 0)
+ return ret;
+
+ gpio->cache_int_type[i] &= ~MV_GPIO_INT_UPD_FLAG;
+ }
+
+ return 0;
+}
+
+static int mv2222_gpio_init(struct mv2222_gpio *gpio)
+{
+ int i, ret;
+
+ /* Setup GPIO function and default IRQs state for all valid GPIOs */
+ for (i = 0; i < MV_GPIO_NUM; ++i) {
+ mv2222_gpio_cache_int_en(gpio, i, false);
+
+ if (gpio->cache_val_mask & BIT(i))
+ mv2222_gpio_cache_func(gpio, i, true);
+ else
+ mv2222_gpio_cache_func(gpio, i, false);
+
+ mv2222_gpio_cache_int_type(gpio, i, IRQ_TYPE_NONE);
+ }
+
+ ret = mv2222_gpio_cache_int_flush(gpio);
+ if (ret < 0)
+ return ret;
+
+ ret = mv2222_gpio_read_tri_ctrl(gpio);
+ if (ret < 0)
+ return ret;
+
+ ret = mv2222_gpio_read_data(gpio);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mv2222_gpio_get_direction(struct gpio_chip *gc, unsigned int ofs)
+{
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ int ret;
+
+ mutex_lock(&gpio->cache_lock);
+ ret = !(gpio->cache_tri_ctrl & BIT(ofs));
+ mutex_unlock(&gpio->cache_lock);
+
+ return ret;
+}
+
+static int mv2222_gpio_direction_input(struct gpio_chip *gc, unsigned int ofs)
+{
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ u16 mask = BIT(ofs);
+
+ return mv2222_gpio_wthru_tri_ctrl(gpio, mask, 0);
+}
+
+static int mv2222_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int ofs, int val)
+{
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ u16 mask = BIT(ofs);
+ int ret;
+
+ ret = mv2222_gpio_wthru_data(gpio, mask, val ? mask : 0);
+ if (ret < 0)
+ return ret;
+
+ return mv2222_gpio_wthru_tri_ctrl(gpio, mask, mask);
+}
+
+static int mv2222_gpio_get(struct gpio_chip *gc, unsigned int ofs)
+{
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ u16 mask = BIT(ofs);
+ int val;
+
+ val = mv2222_gpio_read_data(gpio);
+ if (val < 0)
+ return val;
+
+ return !!(val & mask);
+}
+
+static int mv2222_gpio_get_multiple(struct gpio_chip *gc,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ int val;
+
+ val = mv2222_gpio_read_data(gpio);
+ if (val < 0)
+ return val;
+
+ *bits &= ~*mask;
+ *bits |= val & *mask;
+
+ return 0;
+}
+
+static int mv2222_gpio_set(struct gpio_chip *gc, unsigned ofs, int val)
+{
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ u16 mask = BIT(ofs);
+ int ret;
+
+ ret = mv2222_gpio_wthru_data(gpio, mask, val ? mask : 0);
+ if (ret < 0)
+ phydev_err(gpio->phydev, "Failed to set GPIO %d\n", ofs);
+
+ return ret;
+}
+
+static int mv2222_gpio_set_multiple(struct gpio_chip *gc,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ int ret;
+
+ ret = mv2222_gpio_wthru_data(gpio, *mask, *bits);
+ if (ret < 0)
+ phydev_err(gpio->phydev, "Failed to set GPIOs 0x%04lx\n", *bits);
+
+ return ret;
+}
+
+static int mv2222_gpio_set_config(struct gpio_chip *gc, unsigned int ofs,
+ unsigned long cfg)
+{
+ enum pin_config_param mode = pinconf_to_config_param(cfg);
+
+ /* All output pins operate as open drain */
+ if (mode != PIN_CONFIG_DRIVE_OPEN_DRAIN)
+ return -ENOTSUPP;
+
+ return 0;
+}
+
+static int mv2222_gpio_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ int ret;
+
+ if (ngpios > MV_GPIO_NUM)
+ return -EINVAL;
+
+ *valid_mask &= MV_GPIO_VAL_MASK;
+
+ gpio->cache_val_mask = *valid_mask;
+
+ ret = mv2222_gpio_init(gpio);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void mv2222_gpio_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ mv2222_gpio_cache_int_en(gpio, hwirq, false);
+ gpiochip_disable_irq(gc, hwirq);
+}
+
+static void mv2222_gpio_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ gpiochip_enable_irq(gc, hwirq);
+ mv2222_gpio_cache_int_en(gpio, hwirq, true);
+}
+
+static int mv2222_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ return mv2222_gpio_cache_int_type(gpio, hwirq, type);
+}
+
+static int mv2222_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+
+ return irq_set_irq_wake(gpio->irq, on);
+}
+
+static void mv2222_gpio_irq_bus_lock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+
+ mutex_lock(&gpio->cache_lock);
+}
+
+static void mv2222_gpio_irq_bus_sync_unlock(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct mv2222_gpio *gpio = gpiochip_get_data(gc);
+ int ret;
+
+ ret = mv2222_gpio_cache_int_flush(gpio);
+ if (ret < 0)
+ phydev_err(gpio->phydev, "Failed to flush GPIO IRQs state\n");
+
+ mutex_unlock(&gpio->cache_lock);
+}
+
+static void mv2222_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+ seq_printf(p, dev_name(gc->parent));
+}
+
+static const struct irq_chip mv2222_gpio_irq_chip = {
+ .name = "mv88x2222",
+ .irq_mask = mv2222_gpio_irq_mask,
+ .irq_unmask = mv2222_gpio_irq_unmask,
+ .irq_set_wake = mv2222_gpio_irq_set_wake,
+ .irq_set_type = mv2222_gpio_irq_set_type,
+ .irq_bus_lock = mv2222_gpio_irq_bus_lock,
+ .irq_bus_sync_unlock = mv2222_gpio_irq_bus_sync_unlock,
+ .irq_print_chip = mv2222_gpio_irq_print_chip,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static irqreturn_t mv2222_gpio_handle_interrupt(int irq, void *devid)
+{
+ struct mv2222_gpio *gpio = devid;
+ unsigned long pending;
+ int i, val;
+
+ val = phy_read_mmd(gpio->phydev, MDIO_MMD_VEND2, MV_GPIO_INT_STAT);
+ if (val < 0)
+ return IRQ_NONE;
+
+ /* The interrupt status register exports the raw IRQ status */
+ mutex_lock(&gpio->cache_lock);
+ pending = val & gpio->cache_int_en;
+ mutex_unlock(&gpio->cache_lock);
+
+ for_each_set_bit(i, &pending, gpio->gc.ngpio)
+ handle_nested_irq(irq_find_mapping(gpio->gc.irq.domain, i));
+
+ return IRQ_RETVAL(pending);
+}
+
+static const char * const mv2222_gpio_names[MV_GPIO_NUM] = {
+ [MV_88X2222_MOD_ABS] = "MOD_ABS",
+ [MV_88X2222_TX_FAULT] = "TX_FAULT",
+ [MV_88X2222_RX_LOS] = "RX_LOS",
+ [MV_88X2222_GPIO] = "GPIO",
+ [MV_88X2222_LED0] = "LED0",
+ [MV_88X2222_LED1] = "LED1",
+ [MV_88X2222_MPC] = "MPC",
+ [MV_88X2222_TOD] = "TOD",
+ [MV_88X2222_TX_DISABLE] = "TX_DISABLE",
+ [MV_88X2222_UNDEF] = NULL,
+ [MV_88X2222_SDA] = "SDA",
+ [MV_88X2222_SCL] = "SCL",
+};
+
+static int mv2222_gpio_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct mv2222_gpio *gpio;
+ struct gpio_chip *gc;
+ int ret;
+
+ /*
+ * No GPIO-chip registration if the PHY-device isn't marked as a
+ * GPIO-controller. This is another level of protection for the
+ * backward compatibility in case if the platforms rely on the
+ * default/pre-initialized pins functions.
+ */
+ if (!device_property_present(dev, "gpio-controller"))
+ return 0;
+
+ /*
+ * Marvell 88x2222 GPIO CSRs are tolerant to the soft-resets. They are
+ * marked as "Retain" in the "SW Rst" calumn of the registers
+ * description table defined in the HW-databook. On the contrary the
+ * hard-reset will reset all the GPIO CSRs to their default states which
+ * in its turn will break the driver GPIO functionality for sure.
+ */
+ if (phydev->mdio.reset_gpio || phydev->mdio.reset_ctrl) {
+ phydev_warn(phydev, "Hard-reset detected, GPIOs unsupported\n");
+ return 0;
+ }
+
+ gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ gpio->phydev = phydev;
+
+ mutex_init(&gpio->cache_lock);
+
+ gc = &gpio->gc;
+ gc->label = "mv88x2222";
+ gc->parent = dev;
+ gc->owner = THIS_MODULE;
+ gc->get_direction = mv2222_gpio_get_direction;
+ gc->direction_input = mv2222_gpio_direction_input;
+ gc->direction_output = mv2222_gpio_direction_output;
+ gc->get = mv2222_gpio_get;
+ gc->get_multiple = mv2222_gpio_get_multiple;
+ gc->set = mv2222_gpio_set;
+ gc->set_multiple = mv2222_gpio_set_multiple;
+ gc->set_config = mv2222_gpio_set_config;
+ gc->init_valid_mask = mv2222_gpio_init_valid_mask;
+ gc->base = -1;
+ gc->ngpio = MV_GPIO_NUM;
+ gc->names = mv2222_gpio_names;
+ gc->can_sleep = true;
+
+ if (phy_interrupt_is_valid(phydev)) {
+ struct gpio_irq_chip *girq = &gc->irq;
+
+ gpio->irq = phydev->irq;
+
+ girq->handler = handle_bad_irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->parent_handler = NULL;
+ girq->num_parents = 0;
+ girq->parents = NULL;
+ girq->threaded = true;
+
+ gpio_irq_chip_set_chip(girq, &mv2222_gpio_irq_chip);
+
+ ret = devm_request_threaded_irq(dev, gpio->irq, NULL,
+ mv2222_gpio_handle_interrupt,
+ IRQF_ONESHOT | IRQF_SHARED,
+ phydev_name(phydev), gpio);
+ if (ret) {
+ phydev_err(phydev, "Failed to request GPIO IRQ\n");
+ return ret;
+ }
+ } else {
+ gpio->irq = IRQ_NOTCONNECTED;
+ }
+
+ ret = devm_gpiochip_add_data(dev, gc, gpio);
+ if (ret)
+ phydev_err(phydev, "Failed to register GPIO chip\n");
+
+ return ret;
+}
+#else
+static int mv2222_gpio_probe(struct phy_device *phydev)
+{
+ return 0;
+}
+#endif /* !CONFIG_MARVELL_88X2222_GPIO */
+
+#ifdef CONFIG_MARVELL_88X2222_I2C
+static int mv2222_i2c_init(struct mv2222_i2c *i2c)
+{
+ struct i2c_timings t;
+ u16 val;
+ int ret;
+
+ /* The I2C bus is available if the SDA/SCL pins function isn't GPIO */
+ ret = phy_read_mmd(i2c->phydev, MDIO_MMD_VEND2, MV_GPIO_INT_TYPE3);
+ if (ret < 0)
+ return ret;
+
+ if (ret & MV_GPIO_FUNC_SDA_PIN10 || ret & MV_GPIO_FUNC_SCL_PIN11)
+ return -EBUSY;
+
+ /* Make sure the only supported bus speed is specified */
+ i2c_parse_fw_timings(&i2c->phydev->mdio.dev, &t, true);
+ if (t.bus_freq_hz != I2C_MAX_STANDARD_MODE_FREQ)
+ return -EINVAL;
+
+ /* Disable the EEPROM caching. It will interfere the I2C-adapter work */
+ val = FIELD_PREP(MV_TWSI_CACHE_A0_CTRL, MV_TWSI_CACHE_NO_AUTO) |
+ FIELD_PREP(MV_TWSI_CACHE_A2_CTRL, MV_TWSI_CACHE_NO_AUTO);
+
+ ret = phy_write_mmd(i2c->phydev, MDIO_MMD_PMAPMD, MV_TWSI_CACHE_CTRL, val);
+ if (ret < 0)
+ return ret;
+
+ /* Disable the Read-back-after-write functionality */
+ ret = phy_write_mmd(i2c->phydev, MDIO_MMD_PMAPMD, MV_TWSI_MEM_WRITE_CTRL, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Once again disable the auto-caching */
+ val = FIELD_PREP(MV_TWSI_CACHE_AUTO_DELAY, MV_TWSI_CACHE_AUTO_DIS);
+
+ ret = phy_write_mmd(i2c->phydev, MDIO_MMD_PMAPMD, MV_TWSI_CACHE_DELAY, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static inline unsigned long mv2222_i2c_get_udelay(char read_write)
+{
+ const unsigned long p = USEC_PER_SEC / I2C_MAX_STANDARD_MODE_FREQ;
+ unsigned long ud;
+
+ /* S, addr, Ack, cmd, Ack, data, Ack/Nack and P bits together */
+ ud = 29 * p;
+
+ /* Additional Sr, addr and Ack bits in case of the read op */
+ if (read_write == I2C_SMBUS_READ)
+ ud += 10 * p;
+
+ return ud;
+}
+
+static inline bool mv2222_i2c_ready(int val)
+{
+ if (FIELD_GET(MV_TWSI_STAT, val) == MV_TWSI_READY)
+ return true;
+
+ return false;
+}
+
+static inline bool mv2222_i2c_cmd_done(int val)
+{
+ if (FIELD_GET(MV_TWSI_STAT, val) != MV_TWSI_CMD_IN_PROG)
+ return true;
+
+ return false;
+}
+
+static int mv2222_i2c_xfer(struct i2c_adapter *ia,
+ u16 addr, unsigned short flags, char read_write,
+ u8 cmd, int size, union i2c_smbus_data *data)
+{
+ struct mv2222_i2c *i2c = ia->algo_data;
+ unsigned long ud;
+ int val, ret;
+
+ if (flags & I2C_CLIENT_TEN || size != I2C_SMBUS_BYTE_DATA)
+ return -EOPNOTSUPP;
+
+ /* Make sure the interface is ready to execute the command */
+ ud = mv2222_i2c_get_udelay(I2C_SMBUS_READ);
+
+ ret = phy_read_mmd_poll_timeout(i2c->phydev, MDIO_MMD_PMAPMD,
+ MV_TWSI_MEM_READ_STAT, val,
+ mv2222_i2c_ready(val),
+ ud, MV_I2C_POLL_NUM * ud, false);
+ if (ret < 0)
+ return ret;
+
+ /* Collect the command parameters */
+ if (read_write == I2C_SMBUS_WRITE) {
+ ret = phy_write_mmd(i2c->phydev, MDIO_MMD_PMAPMD,
+ MV_TWSI_MEM_WRITE_CTRL, data->byte);
+ if (ret < 0)
+ return ret;
+
+ val = 0;
+ } else {
+ val = MV_TWSI_BYTE_READ;
+ }
+
+ val |= FIELD_PREP(MV_TWSI_BYTE_ADDR, cmd) |
+ FIELD_PREP(MV_TWSI_SLV_ADDR, addr);
+
+ ret = phy_write_mmd(i2c->phydev, MDIO_MMD_PMAPMD, MV_TWSI_MEM_ADDR, val);
+ if (ret < 0)
+ return ret;
+
+ /* Issue the command and wait until the CMD in-progress status is set */
+ ud = mv2222_i2c_get_udelay(read_write);
+
+ ret = phy_read_mmd_poll_timeout(i2c->phydev, MDIO_MMD_PMAPMD,
+ MV_TWSI_MEM_READ_STAT, val,
+ mv2222_i2c_cmd_done(val),
+ ud, MV_I2C_POLL_NUM * ud, true);
+ if (ret < 0)
+ return ret;
+
+ /* The interface may respond with the READY status on the early stage
+ * of the SFP-module attachement. In that case ask to try again.
+ */
+ switch (FIELD_GET(MV_TWSI_STAT, val)) {
+ case MV_TWSI_CMD_FAIL:
+ return -EIO;
+ case MV_TWSI_BUSY:
+ return -ENXIO;
+ case MV_TWSI_READY:
+ return -EAGAIN;
+ }
+
+ if (read_write == I2C_SMBUS_READ)
+ data->byte = FIELD_GET(MV_TWSI_MEM_READ_DATA, val);
+
+ return 0;
+}
+
+static u32 mv2222_i2c_func(struct i2c_adapter *ia)
+{
+ return I2C_FUNC_SMBUS_BYTE_DATA;
+}
+
+static struct i2c_algorithm mv2222_i2c_algo = {
+ .smbus_xfer = mv2222_i2c_xfer,
+ .functionality = mv2222_i2c_func,
+};
+
+static int mv2222_i2c_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct mv2222_i2c *i2c;
+ struct i2c_adapter *ia;
+ int ret;
+
+ /*
+ * Marvell 88x2222 I2C CSRs are tolerant to the soft-resets. Alas that
+ * can be said about the hard-resets. It will halt any communications
+ * with possibly no error reported. So it isn't safe to use the
+ * interface if a sudden hard-reset might be performed.
+ */
+ if (phydev->mdio.reset_gpio || phydev->mdio.reset_ctrl) {
+ phydev_warn(phydev, "Hard-reset detected, I2C unsupported\n");
+ return 0;
+ }
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ i2c->phydev = phydev;
+
+ /* Make sure the I2C-interface is ready before to proceed */
+ ret = mv2222_i2c_init(i2c);
+ if (ret == -EBUSY) {
+ phydev_warn(phydev, "I2C interface unavailable\n");
+ return 0;
+ } else if (ret) {
+ phydev_err(phydev, "Failed to init I2C-interface\n");
+ return ret;
+ }
+
+ ia = &i2c->ia;
+ strscpy(ia->name, "mv88x2222", sizeof(ia->name));
+ ia->dev.parent = dev;
+ ia->owner = THIS_MODULE;
+ ia->algo = &mv2222_i2c_algo;
+ ia->algo_data = i2c;
+
+ ret = devm_i2c_add_adapter(dev, ia);
+ if (ret)
+ phydev_err(phydev, "Failed to register I2C adapter\n");
+
+ return ret;
+}
+#else
+static int mv2222_i2c_probe(struct phy_device *phydev)
+{
+ return 0;
+}
+#endif /* !CONFIG_MARVELL_88X2222_I2C */
+
static int mv2222_configure_serdes(struct phy_port *port, bool enable,
phy_interface_t interface)
{
@@ -541,6 +1938,7 @@ static int mv2222_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
struct mv2222_data *priv = NULL;
+ int ret;
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
@@ -565,6 +1963,10 @@ static int mv2222_probe(struct phy_device *phydev)
linkmode_copy(phydev->supported, supported);
+ /* In perspective the list can be extended with 1000BASE-X and SGMII */
+ __set_bit(PHY_INTERFACE_MODE_XAUI, phydev->possible_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, phydev->possible_interfaces);
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -572,6 +1974,14 @@ static int mv2222_probe(struct phy_device *phydev)
priv->line_interface = PHY_INTERFACE_MODE_NA;
phydev->priv = priv;
+ ret = mv2222_gpio_probe(phydev);
+ if (ret)
+ return ret;
+
+ ret = mv2222_i2c_probe(phydev);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -584,6 +1994,25 @@ static struct phy_driver mv2222_drivers[] = {
.soft_reset = mv2222_soft_reset,
.config_init = mv2222_config_init,
.config_aneg = mv2222_config_aneg,
+ .config_intr = mv2222_config_intr,
+ .handle_interrupt = mv2222_handle_interrupt,
+ .aneg_done = mv2222_aneg_done,
+ .probe = mv2222_probe,
+ .suspend = mv2222_suspend,
+ .resume = mv2222_resume,
+ .read_status = mv2222_read_status,
+ .attach_mii_port = mv2222_attach_mii_port,
+ },
+ {
+ .phy_id = MARVELL_PHY_ID_88X2222R,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .name = "Marvell 88X2222R",
+ .get_features = mv2222_get_features,
+ .soft_reset = mv2222_soft_reset,
+ .config_init = mv2222_config_init,
+ .config_aneg = mv2222_config_aneg,
+ .config_intr = mv2222_config_intr,
+ .handle_interrupt = mv2222_handle_interrupt,
.aneg_done = mv2222_aneg_done,
.probe = mv2222_probe,
.suspend = mv2222_suspend,
@@ -596,6 +2025,7 @@ module_phy_driver(mv2222_drivers);
static const struct mdio_device_id __maybe_unused mv2222_tbl[] = {
{ MARVELL_PHY_ID_88X2222, MARVELL_PHY_ID_MASK },
+ { MARVELL_PHY_ID_88X2222R, MARVELL_PHY_ID_MASK },
{ }
};
MODULE_DEVICE_TABLE(mdio, mv2222_tbl);
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index d7a4a977fc8ad..534abb0ab10a1 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -183,6 +183,7 @@ int phy_interface_num_ports(phy_interface_t interface)
case PHY_INTERFACE_MODE_SMII:
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_10GBASEX:
case PHY_INTERFACE_MODE_5GBASER:
case PHY_INTERFACE_MODE_10GBASER:
case PHY_INTERFACE_MODE_25GBASER:
diff --git a/drivers/net/phy/phy_caps.c b/drivers/net/phy/phy_caps.c
index 942d43191561e..ce79da3726308 100644
--- a/drivers/net/phy/phy_caps.c
+++ b/drivers/net/phy/phy_caps.c
@@ -352,6 +352,7 @@ unsigned long phy_caps_from_interface(phy_interface_t interface)
break;
case PHY_INTERFACE_MODE_XGMII:
+ case PHY_INTERFACE_MODE_10GBASEX:
case PHY_INTERFACE_MODE_RXAUI:
case PHY_INTERFACE_MODE_XAUI:
case PHY_INTERFACE_MODE_10GBASER:
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index aba1b35c7cd7e..e6cfdab4c5dc2 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -272,6 +272,7 @@ static int phylink_interface_max_speed(phy_interface_t interface)
return SPEED_5000;
case PHY_INTERFACE_MODE_XGMII:
+ case PHY_INTERFACE_MODE_10GBASEX:
case PHY_INTERFACE_MODE_RXAUI:
case PHY_INTERFACE_MODE_XAUI:
case PHY_INTERFACE_MODE_10GBASER:
@@ -818,8 +819,10 @@ static int phylink_parse_mode(struct phylink *pl,
case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_5GBASER:
case PHY_INTERFACE_MODE_25GBASER:
+ case PHY_INTERFACE_MODE_XGMII:
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10G_QXGMII:
+ case PHY_INTERFACE_MODE_10GBASEX:
case PHY_INTERFACE_MODE_10GKR:
case PHY_INTERFACE_MODE_10GBASER:
case PHY_INTERFACE_MODE_XLGMII:
@@ -4351,6 +4354,7 @@ void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs,
return;
switch (state->interface) {
+ case PHY_INTERFACE_MODE_10GBASEX:
case PHY_INTERFACE_MODE_10GBASER:
state->speed = SPEED_10000;
state->duplex = DUPLEX_FULL;
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 75565fbdbf6d4..3f2a54eacbbac 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -1465,6 +1465,42 @@ static int rtl822xb_get_rate_matching(struct phy_device *phydev,
return RATE_MATCH_NONE;
}
+static int rtl8211e_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
+ u16 val)
+{
+ int ret;
+
+ /* Write to the MMD registers by using the standard control/data pair.
+ * The only difference is that we need to perform a dummy read after
+ * the PC1R.CLKSTOP_EN bit is set. It's required to workaround an issue
+ * of a partial core freeze so LED2 stops blinking in EEE mode, PHY
+ * stops detecting the link change and raising IRQs until any read from
+ * its registers performed. That happens only if and right after the PHY
+ * is enabled to stop RXC in LPI mode.
+ */
+ ret = __phy_write(phydev, MII_MMD_CTRL, devnum);
+ if (ret)
+ return ret;
+
+ ret = __phy_write(phydev, MII_MMD_DATA, regnum);
+ if (ret)
+ return ret;
+
+ ret = __phy_write(phydev, MII_MMD_CTRL, devnum | MII_MMD_CTRL_NOINCR);
+ if (ret)
+ return ret;
+
+ ret = __phy_write(phydev, MII_MMD_DATA, val);
+ if (ret)
+ return ret;
+
+ if (devnum == MDIO_MMD_PCS && regnum == MDIO_CTRL1 &&
+ val & MDIO_PCS_CTRL1_CLKSTOP_EN)
+ ret = __phy_read(phydev, MII_MMD_DATA);
+
+ return ret < 0 ? ret : 0;
+}
+
static int rtl822x_get_features(struct phy_device *phydev)
{
int val;
@@ -2209,6 +2245,7 @@ static struct phy_driver realtek_drvs[] = {
.resume = genphy_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
+ .write_mmd = rtl8211e_write_mmd,
.led_hw_is_supported = rtl8211x_led_hw_is_supported,
.led_hw_control_get = rtl8211e_led_hw_control_get,
.led_hw_control_set = rtl8211e_led_hw_control_set,
diff --git a/drivers/pci/controller/dwc/pcie-bt1.c b/drivers/pci/controller/dwc/pcie-bt1.c
index 1340edc18d128..e99fd6620b8db 100644
--- a/drivers/pci/controller/dwc/pcie-bt1.c
+++ b/drivers/pci/controller/dwc/pcie-bt1.c
@@ -23,6 +23,7 @@
#include <linux/reset.h>
#include <linux/types.h>
+#include "../../pci.h"
#include "pcie-designware.h"
/* Baikal-T1 System CCU control registers */
@@ -134,7 +135,7 @@
#define BT1_PCIE_NUM_CORE_RSTS ARRAY_SIZE(bt1_pcie_core_rsts)
/* PCIe bus setup delays and timeouts */
-#define BT1_PCIE_RST_DELAY_MS 100
+#define BT1_PCIE_RST_DELAY_US 100
#define BT1_PCIE_RUN_DELAY_US 100
#define BT1_PCIE_REQ_DELAY_US 1
#define BT1_PCIE_REQ_TIMEOUT_US 1000
@@ -255,6 +256,18 @@ static void bt1_pcie_write_dbi2(struct dw_pcie *pci, void __iomem *base, u32 reg
BT1_CCU_PCIE_DBI2_MODE, 0);
}
+static int bt1_pcie_link_up(struct dw_pcie *pci)
+{
+ struct bt1_pcie *btpci = to_bt1_pcie(pci);
+ u32 val;
+
+ regmap_read(btpci->sys_regs, BT1_CCU_PCIE_PMSC, &val);
+ if ((val & BT1_CCU_PCIE_SMLH_LINKUP) && (val & BT1_CCU_PCIE_RDLH_LINKUP))
+ return true;
+
+ return false;
+}
+
static int bt1_pcie_start_link(struct dw_pcie *pci)
{
struct bt1_pcie *btpci = to_bt1_pcie(pci);
@@ -284,22 +297,44 @@ static int bt1_pcie_start_link(struct dw_pcie *pci)
return ret;
}
+ ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val,
+ BT1_CCU_PCIE_LTSSM_LINKUP(val),
+ BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US);
+ if (ret) {
+ dev_err(pci->dev, "LTSSM failed to get into L0 state\n");
+ return ret;
+ }
+
+ /*
+ * Wait for the speed change enabled by default and by the DW PCIe core
+ * driver to be finished.
+ */
+ ret = read_poll_timeout(dw_pcie_readl_dbi, val, !(val & PORT_LOGIC_SPEED_CHANGE),
+ BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US, false,
+ pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ if (ret) {
+ dev_err(pci->dev, "Initial speed change hanged up\n");
+ return ret;
+ }
+
/*
- * Activate direct speed change after the link is established in an
- * attempt to reach a higher bus performance (up to Gen.3 - 8.0 GT/s).
- * This is required at least to get 8.0 GT/s speed.
+ * Activate the direct speed change one more time after the link is
+ * established in order to reach a higher bus performance. This is
+ * required at least to get 8.0 GT/s speed.
*/
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
- ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val,
- BT1_CCU_PCIE_LTSSM_LINKUP(val),
- BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US);
- if (ret)
- dev_err(pci->dev, "LTSSM failed to get into L0 state\n");
+ ret = read_poll_timeout(dw_pcie_readl_dbi, val, !(val & PORT_LOGIC_SPEED_CHANGE),
+ BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US, false,
+ pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ if (ret) {
+ dev_err(pci->dev, "Speed change hanged up\n");
+ return ret;
+ }
- return ret;
+ return 0;
}
static void bt1_pcie_stop_link(struct dw_pcie *pci)
@@ -377,33 +412,60 @@ static void bt1_pcie_full_stop_bus(struct bt1_pcie *btpci, bool init)
struct dw_pcie *pci = &btpci->dw;
int ret;
- /* Disable LTSSM for sure */
+ /* Make sure LTSSM and DBI CS2 are disabled by default */
regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC,
- BT1_CCU_PCIE_LTSSM_EN, 0);
+ BT1_CCU_PCIE_DBI2_MODE | BT1_CCU_PCIE_LTSSM_EN, 0);
/*
- * Application reset controls are trigger-based so assert the core
- * resets only.
+ * In case if the link has already been attempted to be set up
+ * the PCIe M/S AXI clocks _must_ be enabled for the application
+ * resets to succeed.
*/
+ ret = clk_bulk_prepare_enable(DW_PCIE_NUM_APP_CLKS, pci->app_clks);
+ if (ret)
+ dev_err(dev, "Failed to enable APP clocks\n");
+
+ ret = reset_control_bulk_reset(DW_PCIE_NUM_APP_RSTS, pci->app_rsts);
+ if (ret)
+ dev_err(dev, "Failed to reset APP domains\n");
+
+ clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks);
+
+ /* The PCIe core resets can be now safely de-asserted */
ret = reset_control_bulk_assert(DW_PCIE_NUM_CORE_RSTS, pci->core_rsts);
if (ret)
dev_err(dev, "Failed to assert core resets\n");
+ /* Wait a bit for the resets to take actions */
+ udelay(BT1_PCIE_RST_DELAY_US);
+
/*
* Clocks are disabled by default at least in accordance with the clk
- * enable counter value on init stage.
+ * enable counter value on init stage. So skip disabling them in that
+ * case.
*/
- if (!init) {
+ if (!init)
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, pci->core_clks);
- clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks);
- }
+ /*
+ * The application domains _must_ be reset one more time so further
+ * initializations would not freeze and be performed from scratch.
+ */
+ ret = clk_bulk_prepare_enable(DW_PCIE_NUM_APP_CLKS, pci->app_clks);
+ if (ret)
+ dev_err(dev, "Failed to post-enable APP clocks\n");
+
+ ret = reset_control_bulk_reset(DW_PCIE_NUM_APP_RSTS, pci->app_rsts);
+ if (ret)
+ dev_err(dev, "Failed to post-reset APP domains\n");
+
+ clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks);
/* The peripheral devices are unavailable anyway so reset them too */
gpiod_set_value_cansleep(pci->pe_rst, 1);
/* Make sure all the resets are settled */
- msleep(BT1_PCIE_RST_DELAY_MS);
+ msleep(PCIE_T_PVPERL_MS);
}
/*
@@ -420,7 +482,7 @@ static int bt1_pcie_cold_start_bus(struct bt1_pcie *btpci)
/* First get out of the Power/Hot reset state */
ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PWR_RST].rstc);
if (ret) {
- dev_err(dev, "Failed to deassert PHY reset\n");
+ dev_err(dev, "Failed to deassert pwr reset\n");
return ret;
}
@@ -430,26 +492,11 @@ static int bt1_pcie_cold_start_bus(struct bt1_pcie *btpci)
goto err_assert_pwr_rst;
}
- /* Wait for the PM-core to stop requesting the PHY reset */
- ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val,
- !(val & BT1_CCU_PCIE_REQ_PHY_RST),
- BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US);
- if (ret) {
- dev_err(dev, "Timed out waiting for PM to stop PHY resetting\n");
- goto err_assert_hot_rst;
- }
-
- ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PHY_RST].rstc);
- if (ret) {
- dev_err(dev, "Failed to deassert PHY reset\n");
- goto err_assert_hot_rst;
- }
-
/* Clocks can be now enabled, but the ref one is crucial at this stage */
ret = clk_bulk_prepare_enable(DW_PCIE_NUM_APP_CLKS, pci->app_clks);
if (ret) {
dev_err(dev, "Failed to enable app clocks\n");
- goto err_assert_phy_rst;
+ goto err_assert_hot_rst;
}
ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, pci->core_clks);
@@ -458,32 +505,40 @@ static int bt1_pcie_cold_start_bus(struct bt1_pcie *btpci)
goto err_disable_app_clk;
}
- /* Wait for the PM to stop requesting the controller core reset */
+ /* Wait for the PM-core to stop requesting the PHY reset */
ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val,
- !(val & BT1_CCU_PCIE_REQ_CORE_RST),
+ !(val & BT1_CCU_PCIE_REQ_PHY_RST),
BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US);
if (ret) {
- dev_err(dev, "Timed out waiting for PM to stop core resetting\n");
+ dev_err(dev, "Timed out waiting for PM to stop PHY resetting\n");
goto err_disable_core_clk;
}
- /* PCS-PIPE interface and controller core can be now activated */
- ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc);
+ ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PHY_RST].rstc);
if (ret) {
- dev_err(dev, "Failed to deassert PIPE reset\n");
+ dev_err(dev, "Failed to deassert PHY reset\n");
goto err_disable_core_clk;
}
+ /* Wait for the PM to stop requesting the controller core reset */
+ ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val,
+ !(val & BT1_CCU_PCIE_REQ_CORE_RST),
+ BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "Timed out waiting for PM to stop core resetting\n");
+ goto err_assert_phy_rst;
+ }
+
+ /* Core and PCS-PIPE interface can be now activated */
ret = reset_control_deassert(pci->core_rsts[DW_PCIE_CORE_RST].rstc);
if (ret) {
dev_err(dev, "Failed to deassert core reset\n");
- goto err_assert_pipe_rst;
+ goto err_assert_phy_rst;
}
- /* It's recommended to reset the core and application logic together */
- ret = reset_control_bulk_reset(DW_PCIE_NUM_APP_RSTS, pci->app_rsts);
+ ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc);
if (ret) {
- dev_err(dev, "Failed to reset app domain\n");
+ dev_err(dev, "Failed to deassert PIPE reset\n");
goto err_assert_core_rst;
}
@@ -491,7 +546,7 @@ static int bt1_pcie_cold_start_bus(struct bt1_pcie *btpci)
ret = reset_control_deassert(pci->core_rsts[DW_PCIE_STICKY_RST].rstc);
if (ret) {
dev_err(dev, "Failed to deassert sticky reset\n");
- goto err_assert_core_rst;
+ goto err_assert_pipe_rst;
}
ret = reset_control_deassert(pci->core_rsts[DW_PCIE_NON_STICKY_RST].rstc);
@@ -511,11 +566,14 @@ static int bt1_pcie_cold_start_bus(struct bt1_pcie *btpci)
err_assert_sticky_rst:
reset_control_assert(pci->core_rsts[DW_PCIE_STICKY_RST].rstc);
+err_assert_pipe_rst:
+ reset_control_assert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc);
+
err_assert_core_rst:
reset_control_assert(pci->core_rsts[DW_PCIE_CORE_RST].rstc);
-err_assert_pipe_rst:
- reset_control_assert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc);
+err_assert_phy_rst:
+ reset_control_assert(pci->core_rsts[DW_PCIE_PHY_RST].rstc);
err_disable_core_clk:
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, pci->core_clks);
@@ -523,9 +581,6 @@ err_disable_core_clk:
err_disable_app_clk:
clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks);
-err_assert_phy_rst:
- reset_control_assert(pci->core_rsts[DW_PCIE_PHY_RST].rstc);
-
err_assert_hot_rst:
reset_control_assert(pci->core_rsts[DW_PCIE_HOT_RST].rstc);
@@ -545,8 +600,14 @@ static int bt1_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
return ret;
- bt1_pcie_full_stop_bus(btpci, true);
+ /* Don't stop bus if it has already been set up by firmware */
+ if (!bt1_pcie_link_up(pci))
+ bt1_pcie_full_stop_bus(btpci, true);
+ /*
+ * Perform bus start procedure in any case at least to re-initialize
+ * the kernel clock handlers.
+ */
return bt1_pcie_cold_start_bus(btpci);
}
@@ -597,8 +658,10 @@ static int bt1_pcie_add_port(struct bt1_pcie *btpci)
dw_pcie_cap_set(&btpci->dw, REQ_RES);
ret = dw_pcie_host_init(&btpci->dw.pp);
+ if (ret)
+ dev_err_probe(dev, ret, "Failed to initialize DWC PCIe host\n");
- return dev_err_probe(dev, ret, "Failed to initialize DWC PCIe host\n");
+ return ret;
}
static void bt1_pcie_del_port(struct bt1_pcie *btpci)
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 5741c09dde7f4..a61e4dbb99c2c 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -906,7 +906,6 @@ static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
/* Set the number of lanes */
plc = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
- plc &= ~PORT_LINK_FAST_LINK_MODE;
plc &= ~PORT_LINK_MODE_MASK;
/* Set link width speed control register */
@@ -1214,7 +1213,7 @@ int dw_pcie_edma_detect(struct dw_pcie *pci)
}
dev_info(pci->dev, "eDMA: unroll %s, %hu wr, %hu rd\n",
- pci->edma.mf == EDMA_MF_EDMA_UNROLL ? "T" : "F",
+ pci->edma.mf != EDMA_MF_EDMA_LEGACY ? "T" : "F",
pci->edma.ll_wr_cnt, pci->edma.ll_rd_cnt);
return 0;
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index ae6389dd9caa5..7fc9904c4313e 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -40,17 +40,20 @@
#define __dw_pcie_ver_cmp(_pci, _ver, _op) \
((_pci)->version _op DW_PCIE_VER_ ## _ver)
+#define __dw_pcie_ver_type_cmp(_pci, _type, _op) \
+ ((_pci)->type _op DW_PCIE_VER_TYPE_ ## _type)
+
#define dw_pcie_ver_is(_pci, _ver) __dw_pcie_ver_cmp(_pci, _ver, ==)
#define dw_pcie_ver_is_ge(_pci, _ver) __dw_pcie_ver_cmp(_pci, _ver, >=)
#define dw_pcie_ver_type_is(_pci, _ver, _type) \
(__dw_pcie_ver_cmp(_pci, _ver, ==) && \
- __dw_pcie_ver_cmp(_pci, TYPE_ ## _type, ==))
+ __dw_pcie_ver_type_cmp(_pci, _type, ==))
#define dw_pcie_ver_type_is_ge(_pci, _ver, _type) \
(__dw_pcie_ver_cmp(_pci, _ver, ==) && \
- __dw_pcie_ver_cmp(_pci, TYPE_ ## _type, >=))
+ __dw_pcie_ver_type_cmp(_pci, _type, >=))
/* DWC PCIe controller capabilities */
#define DW_PCIE_CAP_REQ_RES 0
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index c3b2f02f5912e..a5ffae9f56680 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -385,6 +385,33 @@ config SPI_DW_MMIO
tristate "Memory-mapped io interface driver for DW SPI core"
depends on HAS_IOMEM
+config SPI_DW_BT1
+ tristate "Baikal-T1 SPI driver for DW SPI core"
+ depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+ select MULTIPLEXER
+ help
+ Baikal-T1 SoC is equipped with three DW APB SSI-based MMIO SPI
+ controllers. Two of them are pretty much normal: with IRQ, DMA,
+ FIFOs of 64 words depth, 4x CSs, but the third one as being a
+ part of the Baikal-T1 System Boot Controller has got a very
+ limited resources: no IRQ, no DMA, only a single native
+ chip-select and Tx/Rx FIFO with just 8 words depth available.
+ The later one is normally connected to an external SPI-nor flash
+ of 128Mb (in general can be of bigger size).
+
+config SPI_DW_BT1_DIRMAP
+ bool "Directly mapped Baikal-T1 Boot SPI flash support"
+ depends on SPI_DW_BT1
+ help
+ Directly mapped SPI flash memory is an interface specific to the
+ Baikal-T1 System Boot Controller. It is a 16MB MMIO region, which
+ can be used to access a peripheral memory device just by
+ reading/writing data from/to it. Note that the system APB bus
+ will stall during each IO from/to the dirmap region until the
+ operation is finished. So try not to use it concurrently with
+ time-critical tasks (like the SPI memory operations implemented
+ in this driver).
+
endif
config SPI_DLN2
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9d36190a98848..9f44b508402b1 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_SPI_DLN2) += spi-dln2.o
obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o
spi-dw-y := spi-dw-core.o
spi-dw-$(CONFIG_SPI_DW_DMA) += spi-dw-dma.o
+obj-$(CONFIG_SPI_DW_BT1) += spi-dw-bt1.o
obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o
obj-$(CONFIG_SPI_DW_PCI) += spi-dw-pci.o
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c
new file mode 100644
index 0000000000000..91642e05ac607
--- /dev/null
+++ b/drivers/spi/spi-dw-bt1.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+//
+// Authors:
+// Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
+// Serge Semin <Sergey.Semin@baikalelectronics.ru>
+//
+// Baikal-T1 DW APB SPI and System Boot SPI driver
+//
+
+#include <linux/clk.h>
+#include <linux/cpumask.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mux/consumer.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/spi/spi-mem.h>
+#include <linux/spi/spi.h>
+
+#include "spi-dw.h"
+
+#define BT1_BOOT_DIRMAP 0
+#define BT1_BOOT_REGS 1
+
+struct dw_spi_bt1 {
+ struct dw_spi dws;
+ struct clk *clk;
+ struct mux_control *mux;
+
+#ifdef CONFIG_SPI_DW_BT1_DIRMAP
+ void __iomem *map;
+ resource_size_t map_len;
+#endif
+};
+#define to_dw_spi_bt1(_ctlr) \
+ container_of(spi_controller_get_devdata(_ctlr), struct dw_spi_bt1, dws)
+
+typedef int (*dw_spi_bt1_init_cb)(struct platform_device *pdev,
+ struct dw_spi_bt1 *dwsbt1);
+
+#ifdef CONFIG_SPI_DW_BT1_DIRMAP
+
+static int dw_spi_bt1_dirmap_create(struct spi_mem_dirmap_desc *desc)
+{
+ struct dw_spi_bt1 *dwsbt1 = to_dw_spi_bt1(desc->mem->spi->controller);
+
+ if (!dwsbt1->map ||
+ !dwsbt1->dws.mem_ops.supports_op(desc->mem, &desc->info.op_tmpl))
+ return -EOPNOTSUPP;
+
+ if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
+ return -EOPNOTSUPP;
+
+ /*
+ * Make sure the requested region doesn't go out of the physically
+ * mapped flash memory bounds.
+ */
+ if (desc->info.offset + desc->info.length > dwsbt1->map_len)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * Directly mapped SPI memory region is only accessible in the dword chunks.
+ * That's why we have to create a dedicated read-method to copy data from there
+ * to the passed buffer.
+ */
+static void dw_spi_bt1_dirmap_copy_from_map(void *to, void __iomem *from, size_t len)
+{
+ size_t shift, chunk;
+ u32 data;
+
+ /*
+ * We split the copying up into the next three stages: unaligned head,
+ * aligned body, unaligned tail.
+ */
+ shift = (size_t)from & 0x3;
+ if (shift) {
+ chunk = min_t(size_t, 4 - shift, len);
+ data = readl_relaxed(from - shift);
+ memcpy(to, (char *)&data + shift, chunk);
+ from += chunk;
+ to += chunk;
+ len -= chunk;
+ }
+
+ while (len >= 4) {
+ data = readl_relaxed(from);
+ memcpy(to, &data, 4);
+ from += 4;
+ to += 4;
+ len -= 4;
+ }
+
+ if (len) {
+ data = readl_relaxed(from);
+ memcpy(to, &data, len);
+ }
+}
+
+static ssize_t dw_spi_bt1_dirmap_read(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf)
+{
+ struct dw_spi_bt1 *dwsbt1 = to_dw_spi_bt1(desc->mem->spi->controller);
+ struct dw_spi *dws = &dwsbt1->dws;
+ struct spi_mem *mem = desc->mem;
+ struct dw_spi_cfg cfg;
+ int ret;
+
+ /*
+ * Make sure the requested operation length is valid. Truncate the
+ * length if it's greater than the length of the MMIO region.
+ */
+ if (offs >= dwsbt1->map_len || !len)
+ return 0;
+
+ len = min_t(size_t, len, dwsbt1->map_len - offs);
+
+ /* Collect the controller configuration required by the operation */
+ cfg.tmode = DW_SPI_CTRLR0_TMOD_EPROMREAD;
+ cfg.dfs = 8;
+ cfg.ndf = 4;
+ cfg.freq = mem->spi->max_speed_hz;
+
+ /* Make sure the corresponding CS is de-asserted on transmission */
+ dw_spi_set_cs(mem->spi, false);
+
+ dw_spi_enable_chip(dws, 0);
+
+ dw_spi_update_config(dws, mem->spi, &cfg);
+
+ dw_spi_umask_intr(dws, DW_SPI_INT_RXFI);
+
+ dw_spi_enable_chip(dws, 1);
+
+ /*
+ * Enable the transparent mode of the System Boot Controller.
+ * The SPI core IO should have been locked before calling this method
+ * so noone would be touching the controller' registers during the
+ * dirmap operation.
+ */
+ ret = mux_control_select(dwsbt1->mux, BT1_BOOT_DIRMAP);
+ if (ret)
+ return ret;
+
+ dw_spi_bt1_dirmap_copy_from_map(buf, dwsbt1->map + offs, len);
+
+ mux_control_deselect(dwsbt1->mux);
+
+ dw_spi_set_cs(mem->spi, true);
+
+ ret = dw_spi_check_status(dws, true);
+
+ return ret ?: len;
+}
+
+#endif /* CONFIG_SPI_DW_BT1_DIRMAP */
+
+static int dw_spi_bt1_std_init(struct platform_device *pdev,
+ struct dw_spi_bt1 *dwsbt1)
+{
+ struct dw_spi *dws = &dwsbt1->dws;
+
+ dws->irq = platform_get_irq(pdev, 0);
+ if (dws->irq < 0)
+ return dws->irq;
+
+ dws->num_cs = 4;
+
+ /*
+ * Baikal-T1 Normal SPI Controllers don't always keep up with full SPI
+ * bus speed especially when it comes to the concurrent access to the
+ * APB bus resources. Thus we have no choice but to set a constraint on
+ * the SPI bus frequency for the memory operations which require to
+ * read/write data as fast as possible.
+ */
+ dws->max_mem_freq = 20000000U;
+
+ dw_spi_dma_setup_generic(dws);
+
+ return 0;
+}
+
+static int dw_spi_bt1_sys_init(struct platform_device *pdev,
+ struct dw_spi_bt1 *dwsbt1)
+{
+ struct resource *mem __maybe_unused;
+ struct dw_spi *dws = &dwsbt1->dws;
+
+ /*
+ * Baikal-T1 System Boot Controller is equipped with a mux, which
+ * switches between the directly mapped SPI flash access mode and
+ * IO access to the DW APB SSI registers. Note the mux controller
+ * must be setup to preserve the registers being accessible by default
+ * (on idle-state).
+ */
+ dwsbt1->mux = devm_mux_control_get(&pdev->dev, NULL);
+ if (IS_ERR(dwsbt1->mux))
+ return PTR_ERR(dwsbt1->mux);
+
+ /*
+ * Directly mapped SPI flash memory is a 16MB MMIO region, which can be
+ * used to access a peripheral memory device just by reading/writing
+ * data from/to it. Note the system APB bus will stall during each IO
+ * from/to the dirmap region until the operation is finished. So don't
+ * use it concurrently with time-critical tasks (like the SPI memory
+ * operations implemented in the DW APB SSI driver).
+ */
+#ifdef CONFIG_SPI_DW_BT1_DIRMAP
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (mem) {
+ dwsbt1->map = devm_ioremap_resource(&pdev->dev, mem);
+ if (!IS_ERR(dwsbt1->map)) {
+ dwsbt1->map_len = resource_size(mem);
+ dws->mem_ops.dirmap_create = dw_spi_bt1_dirmap_create;
+ dws->mem_ops.dirmap_read = dw_spi_bt1_dirmap_read;
+ } else {
+ dwsbt1->map = NULL;
+ }
+ }
+#endif /* CONFIG_SPI_DW_BT1_DIRMAP */
+
+ /*
+ * There is no IRQ, no DMA and just one CS available on the System Boot
+ * SPI controller.
+ */
+ dws->irq = IRQ_NOTCONNECTED;
+ dws->num_cs = 1;
+
+ /*
+ * Baikal-T1 System Boot SPI Controller doesn't keep up with the full
+ * SPI bus speed due to relatively slow APB bus and races for it'
+ * resources from different CPUs. The situation is worsen by a small
+ * FIFOs depth (just 8 words). It works better in a single CPU mode
+ * though, but still tends to be not fast enough at low CPU
+ * frequencies.
+ */
+ if (num_possible_cpus() > 1)
+ dws->max_mem_freq = 10000000U;
+ else
+ dws->max_mem_freq = 20000000U;
+
+ return 0;
+}
+
+static int dw_spi_bt1_probe(struct platform_device *pdev)
+{
+ dw_spi_bt1_init_cb init_func;
+ struct dw_spi_bt1 *dwsbt1;
+ struct resource *mem;
+ struct dw_spi *dws;
+ int ret;
+
+ dwsbt1 = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_bt1), GFP_KERNEL);
+ if (!dwsbt1)
+ return -ENOMEM;
+
+ dws = &dwsbt1->dws;
+
+ dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(dws->regs))
+ return PTR_ERR(dws->regs);
+
+ dws->paddr = mem->start;
+
+ dwsbt1->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(dwsbt1->clk))
+ return PTR_ERR(dwsbt1->clk);
+
+ dws->bus_num = pdev->id;
+ dws->reg_io_width = 4;
+ dws->max_freq = clk_get_rate(dwsbt1->clk);
+ if (!dws->max_freq)
+ return -EINVAL;
+
+ init_func = device_get_match_data(&pdev->dev);
+ ret = init_func(pdev, dwsbt1);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = dw_spi_add_controller(&pdev->dev, dws);
+ if (ret) {
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, dwsbt1);
+
+ return 0;
+}
+
+static void dw_spi_bt1_remove(struct platform_device *pdev)
+{
+ struct dw_spi_bt1 *dwsbt1 = platform_get_drvdata(pdev);
+
+ dw_spi_remove_controller(&dwsbt1->dws);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static const struct of_device_id dw_spi_bt1_of_match[] = {
+ { .compatible = "baikal,bt1-ssi", .data = dw_spi_bt1_std_init},
+ { .compatible = "baikal,bt1-sys-ssi", .data = dw_spi_bt1_sys_init},
+ { }
+};
+MODULE_DEVICE_TABLE(of, dw_spi_bt1_of_match);
+
+static struct platform_driver dw_spi_bt1_driver = {
+ .probe = dw_spi_bt1_probe,
+ .remove = dw_spi_bt1_remove,
+ .driver = {
+ .name = "bt1-sys-ssi",
+ .of_match_table = dw_spi_bt1_of_match,
+ },
+};
+module_platform_driver(dw_spi_bt1_driver);
+
+MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 System Boot SPI Controller driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("SPI_DW_CORE");
diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index b47637888c5c6..fe94fa134866e 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -960,6 +960,7 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
ctlr->bus_num = dws->bus_num;
ctlr->num_chipselect = dws->num_cs;
+ ctlr->max_native_cs = dws->num_cs;
ctlr->setup = dw_spi_setup;
ctlr->cleanup = dw_spi_cleanup;
ctlr->transfer_one = dw_spi_transfer_one;
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 3b6452e759d5b..28cf61b182d83 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -283,7 +283,7 @@ int serial8250_request_dma(struct uart_8250_port *p)
/* RX buffer */
if (!dma->rx_size)
- dma->rx_size = PAGE_SIZE;
+ dma->rx_size = SZ_512;
dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size,
&dma->rx_addr, GFP_KERNEL);
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 94beadb4024df..22d809ebe75ff 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -75,6 +75,7 @@
struct dw8250_platform_data {
u8 usr_reg;
u32 cpr_value;
+ size_t rx_size;
unsigned int quirks;
};
@@ -647,6 +648,9 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
unsigned int quirks = data->pdata->quirks;
u32 cpr_value = data->pdata->cpr_value;
+ if (data->pdata && data->pdata->rx_size)
+ data->data.dma.rx_size = data->pdata->rx_size;
+
if (quirks & DW_UART_QUIRK_CPR_VALUE)
data->data.cpr_value = cpr_value;
@@ -936,6 +940,11 @@ static const struct dw8250_platform_data dw8250_dw_apb = {
.usr_reg = DW_UART_USR,
};
+static const struct dw8250_platform_data dw8250_baikal_bt1_data = {
+ .usr_reg = DW_UART_USR,
+ .rx_size = SZ_1K,
+};
+
static const struct dw8250_platform_data dw8250_octeon_3860_data = {
.usr_reg = OCTEON_UART_USR,
.quirks = DW_UART_QUIRK_OCTEON,
@@ -964,6 +973,7 @@ static const struct dw8250_platform_data dw8250_intc10ee = {
static const struct of_device_id dw8250_of_match[] = {
{ .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
+ { .compatible = "baikal,bt1-uart", .data = &dw8250_baikal_bt1_data },
{ .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data },
{ .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data },
{ .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data },
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index dc0371857ecbe..f8e7c5fa27ad5 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -174,6 +174,7 @@ EARLYCON_DECLARE(uart8250, early_serial8250_setup);
EARLYCON_DECLARE(uart, early_serial8250_setup);
OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup);
OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup);
+OF_EARLYCON_DECLARE(uart, "baikal,bt1-uart", early_serial8250_setup);
OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup);
OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup);
diff --git a/include/dt-bindings/net/mv-phy-88x2222.h b/include/dt-bindings/net/mv-phy-88x2222.h
new file mode 100644
index 0000000000000..fe21253bc669a
--- /dev/null
+++ b/include/dt-bindings/net/mv-phy-88x2222.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * Marvell 88x2222 PHY constants
+ *
+ * Copyright (C) 2023 BAIKAL ELECTRONICS, JSC
+ */
+
+#ifndef _DT_BINDINGS_MV_88X2222_H
+#define _DT_BINDINGS_MV_88X2222_H
+
+/* Marvell 88x2222 GPIOs mapping */
+#define MV_88X2222_MOD_ABS 0
+#define MV_88X2222_TX_FAULT 1
+#define MV_88X2222_RX_LOS 2
+#define MV_88X2222_GPIO 3
+#define MV_88X2222_LED0 4
+#define MV_88X2222_LED1 5
+#define MV_88X2222_MPC 6
+#define MV_88X2222_TOD 7
+#define MV_88X2222_TX_DISABLE 8
+#define MV_88X2222_UNDEF 9
+#define MV_88X2222_SDA 10
+#define MV_88X2222_SCL 11
+
+#endif /* _DT_BINDINGS_MV_88X2222_H */
diff --git a/include/dt-bindings/soc/bt1-boot-mode.h b/include/dt-bindings/soc/bt1-boot-mode.h
new file mode 100644
index 0000000000000..0e425805bc1d4
--- /dev/null
+++ b/include/dt-bindings/soc/bt1-boot-mode.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 Boot Modes
+ */
+#ifndef __DT_BINDINGS_SOC_BT1_BOOT_MODE_H
+#define __DT_BINDINGS_SOC_BT1_BOOT_MODE_H
+
+#define RCR_BOOT_NORMAL 0x1
+#define RCR_BOOT_LOADER 0x2
+#define RCR_BOOT_RECOVERY 0x3
+
+#endif /* __DT_BINDINGS_SOC_BT1_BOOT_MODE_H */
diff --git a/include/linux/edac.h b/include/linux/edac.h
index fa32f2aca22fd..f7c20693d23ff 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -157,6 +157,7 @@ static inline char *mc_event_error_type(const unsigned int err_type)
* This is a variant of the DDR memories.
* A registered memory has a buffer inside it, hiding
* part of the memory details to the memory controller.
+ * @MEM_LPDDR: Low-Power DDR memory (mDDR).
* @MEM_RMBS: Rambus DRAM, used on a few Pentium III/IV controllers.
* @MEM_DDR2: DDR2 RAM, as described at JEDEC JESD79-2F.
* Those memories are labeled as "PC2-" instead of "PC" to
@@ -167,6 +168,7 @@ static inline char *mc_event_error_type(const unsigned int err_type)
* a chip select signal.
* @MEM_RDDR2: Registered DDR2 RAM
* This is a variant of the DDR2 memories.
+ * @MEM_LPDDR2: Low-Power DDR2 memory.
* @MEM_XDR: Rambus XDR
* It is an evolution of the original RAMBUS memories,
* created to compete with DDR2. Weren't used on any
@@ -200,10 +202,12 @@ enum mem_type {
MEM_RDR,
MEM_DDR,
MEM_RDDR,
+ MEM_LPDDR,
MEM_RMBS,
MEM_DDR2,
MEM_FB_DDR2,
MEM_RDDR2,
+ MEM_LPDDR2,
MEM_XDR,
MEM_DDR3,
MEM_RDDR3,
@@ -232,10 +236,12 @@ enum mem_type {
#define MEM_FLAG_RDR BIT(MEM_RDR)
#define MEM_FLAG_DDR BIT(MEM_DDR)
#define MEM_FLAG_RDDR BIT(MEM_RDDR)
+#define MEM_FLAG_LPDDR BIT(MEM_LPDDR)
#define MEM_FLAG_RMBS BIT(MEM_RMBS)
#define MEM_FLAG_DDR2 BIT(MEM_DDR2)
#define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2)
#define MEM_FLAG_RDDR2 BIT(MEM_RDDR2)
+#define MEM_FLAG_LPDDR2 BIT(MEM_LPDDR2)
#define MEM_FLAG_XDR BIT(MEM_XDR)
#define MEM_FLAG_DDR3 BIT(MEM_DDR3)
#define MEM_FLAG_RDDR3 BIT(MEM_RDDR3)
diff --git a/include/linux/marvell_phy.h b/include/linux/marvell_phy.h
index b1fbe4118414a..a904fb2cc5be3 100644
--- a/include/linux/marvell_phy.h
+++ b/include/linux/marvell_phy.h
@@ -26,6 +26,7 @@
#define MARVELL_PHY_ID_88X3310 0x002b09a0
#define MARVELL_PHY_ID_88E2110 0x002b09b0
#define MARVELL_PHY_ID_88X2222 0x01410f10
+#define MARVELL_PHY_ID_88X2222R 0x014131b0
#define MARVELL_PHY_ID_88Q2110 0x002b0980
#define MARVELL_PHY_ID_88Q2220 0x002b0b20
diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h
index 36073f7b6bb40..e9f29dc33a463 100644
--- a/include/linux/pcs/pcs-xpcs.h
+++ b/include/linux/pcs/pcs-xpcs.h
@@ -20,9 +20,11 @@
#define DW_2500BASEX 3
#define DW_AN_C37_1000BASEX 4
#define DW_10GBASER 5
+#define DW_10GBASEX 6
enum dw_xpcs_pcs_id {
DW_XPCS_ID_NATIVE = 0,
+ BT1_XGMAC_XPCS_ID,
NXP_SJA1105_XPCS_ID = 0x00000010,
NXP_SJA1110_XPCS_ID = 0x00000020,
DW_XPCS_ID = 0x7996ced0,
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 6f9979a26892d..ac225cdfe33a9 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -93,6 +93,7 @@ extern const int phy_basic_ports_array[3];
* @PHY_INTERFACE_MODE_100BASEX: 100 BaseX
* @PHY_INTERFACE_MODE_1000BASEX: 1000 BaseX
* @PHY_INTERFACE_MODE_2500BASEX: 2500 BaseX
+ * @PHY_INTERFACE_MODE_10GBASEX: 10G BaseX
* @PHY_INTERFACE_MODE_5GBASER: 5G BaseR
* @PHY_INTERFACE_MODE_RXAUI: Reduced XAUI
* @PHY_INTERFACE_MODE_XAUI: 10 Gigabit Attachment Unit Interface
@@ -136,6 +137,7 @@ typedef enum {
PHY_INTERFACE_MODE_100BASEX,
PHY_INTERFACE_MODE_1000BASEX,
PHY_INTERFACE_MODE_2500BASEX,
+ PHY_INTERFACE_MODE_10GBASEX,
PHY_INTERFACE_MODE_5GBASER,
PHY_INTERFACE_MODE_RXAUI,
PHY_INTERFACE_MODE_XAUI,
@@ -258,6 +260,8 @@ static inline const char *phy_modes(phy_interface_t interface)
return "1000base-kx";
case PHY_INTERFACE_MODE_2500BASEX:
return "2500base-x";
+ case PHY_INTERFACE_MODE_10GBASEX:
+ return "10gbase-x";
case PHY_INTERFACE_MODE_5GBASER:
return "5gbase-r";
case PHY_INTERFACE_MODE_RXAUI:
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index 32352a216567e..d1bb1209fe655 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -19,10 +19,6 @@
#define MTL_MAX_TX_QUEUES 8
#define STMMAC_CH_MAX 8
-#define STMMAC_RX_COE_NONE 0
-#define STMMAC_RX_COE_TYPE1 1
-#define STMMAC_RX_COE_TYPE2 2
-
/* Define the macros for CSR clock range parameters to be passed by
* platform code.
* This could also be configured at run time using CPU freq framework. */
@@ -117,6 +113,7 @@ struct stmmac_axi {
bool axi_fb;
bool axi_mb;
bool axi_rb;
+ bool axi_cc;
};
struct stmmac_rxq_cfg {
@@ -192,6 +189,7 @@ enum dwmac_core_type {
#define STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP BIT(12)
#define STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY BIT(13)
#define STMMAC_FLAG_KEEP_PREAMBLE_BEFORE_SFD BIT(14)
+#define STMMAC_FLAG_TSO_FULL BIT(15)
struct mac_device_info;
@@ -229,6 +227,7 @@ struct plat_stmmacenet_data {
struct device_node *mdio_node;
struct stmmac_dma_cfg *dma_cfg;
struct stmmac_safety_feature_cfg *safety_feat_cfg;
+ u32 ngpios;
int clk_csr;
int enh_desc;
int tx_coe;
@@ -244,6 +243,7 @@ struct plat_stmmacenet_data {
int unicast_filter_entries;
int tx_fifo_size;
int rx_fifo_size;
+ u32 data_width;
u32 host_dma_width;
u32 rx_queues_to_use;
u32 tx_queues_to_use;