diff options
174 files changed, 19687 insertions, 3586 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 8d0533281bf09..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 @@ -25635,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/clk/Kconfig b/drivers/clk/Kconfig index 3d803b4cf5c13..b2e963f2225a5 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -502,7 +502,7 @@ config COMMON_CLK_RPMI source "drivers/clk/actions/Kconfig" source "drivers/clk/analogbits/Kconfig" source "drivers/clk/aspeed/Kconfig" -source "drivers/clk/baikal-t1/Kconfig" +source "drivers/clk/bt1/Kconfig" source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/imgtec/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f7bce3951a30d..641cb17e23e24 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -116,7 +116,7 @@ obj-y += aspeed/ obj-$(CONFIG_COMMON_CLK_AT91) += at91/ obj-$(CONFIG_ARCH_ARTPEC) += axis/ obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/ -obj-$(CONFIG_CLK_BAIKAL_T1) += baikal-t1/ +obj-$(CONFIG_CLK_BAIKAL_T1) += bt1/ obj-y += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ diff --git a/drivers/clk/baikal-t1/Kconfig b/drivers/clk/bt1/Kconfig index f0b1868303243..f0b1868303243 100644 --- a/drivers/clk/baikal-t1/Kconfig +++ b/drivers/clk/bt1/Kconfig diff --git a/drivers/clk/baikal-t1/Makefile b/drivers/clk/bt1/Makefile index 9c3637de94070..9c3637de94070 100644 --- a/drivers/clk/baikal-t1/Makefile +++ b/drivers/clk/bt1/Makefile diff --git a/drivers/clk/baikal-t1/ccu-div.c b/drivers/clk/bt1/ccu-div.c index cc48e580e1599..cc48e580e1599 100644 --- a/drivers/clk/baikal-t1/ccu-div.c +++ b/drivers/clk/bt1/ccu-div.c diff --git a/drivers/clk/baikal-t1/ccu-div.h b/drivers/clk/bt1/ccu-div.h index 76d8ee44d4157..76d8ee44d4157 100644 --- a/drivers/clk/baikal-t1/ccu-div.h +++ b/drivers/clk/bt1/ccu-div.h diff --git a/drivers/clk/baikal-t1/ccu-pll.c b/drivers/clk/bt1/ccu-pll.c index da7fbebb39abc..da7fbebb39abc 100644 --- a/drivers/clk/baikal-t1/ccu-pll.c +++ b/drivers/clk/bt1/ccu-pll.c diff --git a/drivers/clk/baikal-t1/ccu-pll.h b/drivers/clk/bt1/ccu-pll.h index a71bfd7b90ecc..a71bfd7b90ecc 100644 --- a/drivers/clk/baikal-t1/ccu-pll.h +++ b/drivers/clk/bt1/ccu-pll.h diff --git a/drivers/clk/baikal-t1/ccu-rst.c b/drivers/clk/bt1/ccu-rst.c index 969e5de381a8e..269994a3d5e1e 100644 --- a/drivers/clk/baikal-t1/ccu-rst.c +++ b/drivers/clk/bt1/ccu-rst.c @@ -64,11 +64,12 @@ struct ccu_rst_info { /* * Each AXI-bus clock divider is equipped with the corresponding clock-consumer - * domain reset (it's self-deasserted reset control). + * domain reset (it's mostly self-deasserted reset control). */ static const struct ccu_rst_info axi_rst_info[] = { [CCU_AXI_MAIN_RST] = CCU_RST_TRIG(CCU_AXI_MAIN_BASE, 1), - [CCU_AXI_DDR_RST] = CCU_RST_TRIG(CCU_AXI_DDR_BASE, 1), + [CCU_AXI_DDR_T_RST] = CCU_RST_TRIG(CCU_AXI_DDR_BASE, 1), + [CCU_AXI_DDR_D_RST] = CCU_RST_DIR(CCU_AXI_DDR_BASE, 8), [CCU_AXI_SATA_RST] = CCU_RST_TRIG(CCU_AXI_SATA_BASE, 1), [CCU_AXI_GMAC0_RST] = CCU_RST_TRIG(CCU_AXI_GMAC0_BASE, 1), [CCU_AXI_GMAC1_RST] = CCU_RST_TRIG(CCU_AXI_GMAC1_BASE, 1), diff --git a/drivers/clk/baikal-t1/ccu-rst.h b/drivers/clk/bt1/ccu-rst.h index d6e8b2f671f4d..d6e8b2f671f4d 100644 --- a/drivers/clk/baikal-t1/ccu-rst.h +++ b/drivers/clk/bt1/ccu-rst.h diff --git a/drivers/clk/baikal-t1/clk-ccu-div.c b/drivers/clk/bt1/clk-ccu-div.c index d32072e4dd495..d32072e4dd495 100644 --- a/drivers/clk/baikal-t1/clk-ccu-div.c +++ b/drivers/clk/bt1/clk-ccu-div.c diff --git a/drivers/clk/baikal-t1/clk-ccu-pll.c b/drivers/clk/bt1/clk-ccu-pll.c index e5e4a6ea6f782..e5e4a6ea6f782 100644 --- a/drivers/clk/baikal-t1/clk-ccu-pll.c +++ b/drivers/clk/bt1/clk-ccu-pll.c 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/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/reset/bt1-ccu.h b/include/dt-bindings/reset/bt1-ccu.h index c691efaa678f7..1259bd08bd0e0 100644 --- a/include/dt-bindings/reset/bt1-ccu.h +++ b/include/dt-bindings/reset/bt1-ccu.h @@ -8,16 +8,17 @@ #define __DT_BINDINGS_RESET_BT1_CCU_H #define CCU_AXI_MAIN_RST 0 -#define CCU_AXI_DDR_RST 1 -#define CCU_AXI_SATA_RST 2 -#define CCU_AXI_GMAC0_RST 3 -#define CCU_AXI_GMAC1_RST 4 -#define CCU_AXI_XGMAC_RST 5 -#define CCU_AXI_PCIE_M_RST 6 -#define CCU_AXI_PCIE_S_RST 7 -#define CCU_AXI_USB_RST 8 -#define CCU_AXI_HWA_RST 9 -#define CCU_AXI_SRAM_RST 10 +#define CCU_AXI_DDR_T_RST 1 +#define CCU_AXI_DDR_D_RST 2 +#define CCU_AXI_SATA_RST 3 +#define CCU_AXI_GMAC0_RST 4 +#define CCU_AXI_GMAC1_RST 5 +#define CCU_AXI_XGMAC_RST 6 +#define CCU_AXI_PCIE_M_RST 7 +#define CCU_AXI_PCIE_S_RST 8 +#define CCU_AXI_USB_RST 9 +#define CCU_AXI_HWA_RST 10 +#define CCU_AXI_SRAM_RST 11 #define CCU_SYS_SATA_REF_RST 0 #define CCU_SYS_APB_RST 1 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; |
