From: Baikal Electronics Date: Tue, 13 Jun 2023 14:57:11 +0000 (+0300) Subject: Kernel from sdk5.9 X-Git-Tag: baikal/mips/sdk5.9 X-Git-Url: https://git.baikalelectronics.ru/sdk/?a=commitdiff_plain;h=refs%2Fheads%2Fbaikal%2Fmips%2F5.15.y;p=kernel.git Kernel from sdk5.9 --- diff --git a/Documentation/devicetree/bindings/arm/hisilicon/controller/hip04-bootwrapper.yaml b/Documentation/devicetree/bindings/arm/hisilicon/controller/hip04-bootwrapper.yaml index 7378159e61df9..483caf0ce25b9 100644 --- a/Documentation/devicetree/bindings/arm/hisilicon/controller/hip04-bootwrapper.yaml +++ b/Documentation/devicetree/bindings/arm/hisilicon/controller/hip04-bootwrapper.yaml @@ -17,14 +17,15 @@ properties: - const: hisilicon,hip04-bootwrapper boot-method: + $ref: /schemas/types.yaml#/definitions/uint32-array description: | Address and size of boot method. [0]: bootwrapper physical address [1]: bootwrapper size [2]: relocation physical address [3]: relocation size - minItems: 1 - maxItems: 2 + minItems: 2 + maxItems: 4 required: - compatible diff --git a/Documentation/devicetree/bindings/ata/ahci-common.yaml b/Documentation/devicetree/bindings/ata/ahci-common.yaml new file mode 100644 index 0000000000000..94d72aeaad0f7 --- /dev/null +++ b/Documentation/devicetree/bindings/ata/ahci-common.yaml @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/ahci-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common Properties for Serial ATA AHCI controllers + +maintainers: + - Hans de Goede + - Damien Le Moal + +description: + This document defines device tree properties for a common AHCI SATA + controller implementation. It's hardware interface is supposed to + conform to the technical standard defined by Intel (see Serial ATA + Advanced Host Controller Interface specification for details). The + document doesn't constitute a DT-node binding by itself but merely + defines a set of common properties for the AHCI-compatible devices. + +select: false + +allOf: + - $ref: sata-common.yaml# + +properties: + reg: + description: + Generic AHCI registers space conforming to the Serial ATA AHCI + specification. + + reg-names: + description: CSR space IDs + contains: + const: ahci + + interrupts: + description: + Generic AHCI state change interrupt. Can be implemented either as a + single line attached to the controller or as a set of the signals + indicating the particular port events. + minItems: 1 + maxItems: 32 + + ahci-supply: + description: Power regulator for AHCI controller + + target-supply: + description: Power regulator for SATA target device + + phy-supply: + description: Power regulator for SATA PHY + + phys: + description: Reference to the SATA PHY node + maxItems: 1 + + phy-names: + const: sata-phy + + hba-cap: + $ref: '/schemas/types.yaml#/definitions/uint32' + description: + Bitfield of the HBA generic platform capabilities like Staggered + Spin-up or Mechanical Presence Switch support. It can be used to + appropriately initialize the HWinit fields of the HBA CAP register + in case if the system firmware hasn't done it. + + ports-implemented: + $ref: '/schemas/types.yaml#/definitions/uint32' + description: + Mask that indicates which ports the HBA supports. Useful if PI is not + programmed by the BIOS, which is true for some embedded SoC's. + +patternProperties: + "^sata-port@[0-9a-f]+$": + $ref: '#/$defs/ahci-port' + description: + It is optionally possible to describe the ports as sub-nodes so + to enable each port independently when dealing with multiple PHYs. + +required: + - reg + - interrupts + +additionalProperties: true + +$defs: + ahci-port: + $ref: /schemas/ata/sata-common.yaml#/$defs/sata-port + + properties: + reg: + description: + AHCI SATA port identifier. By design AHCI controller can't have + more than 32 ports due to the CAP.NP fields and PI register size + constraints. + minimum: 0 + maximum: 31 + + phys: + description: Individual AHCI SATA port PHY + maxItems: 1 + + phy-names: + description: AHCI SATA port PHY ID + const: sata-phy + + target-supply: + description: Power regulator for SATA port target device + + hba-port-cap: + $ref: '/schemas/types.yaml#/definitions/uint32' + description: + Bitfield of the HBA port-specific platform capabilities like Hot + plugging, eSATA, FIS-based Switching, etc (see AHCI specification + for details). It can be used to initialize the HWinit fields of + the PxCMD register in case if the system firmware hasn't done it. + + required: + - reg + +... diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt deleted file mode 100644 index 77091a2776426..0000000000000 --- a/Documentation/devicetree/bindings/ata/ahci-platform.txt +++ /dev/null @@ -1,79 +0,0 @@ -* AHCI SATA Controller - -SATA nodes are defined to describe on-chip Serial ATA controllers. -Each SATA controller should have its own node. - -It is possible, but not required, to represent each port as a sub-node. -It allows to enable each port independently when dealing with multiple -PHYs. - -Required properties: -- compatible : compatible string, one of: - - "brcm,iproc-ahci" - - "hisilicon,hisi-ahci" - - "cavium,octeon-7130-ahci" - - "ibm,476gtr-ahci" - - "marvell,armada-380-ahci" - - "marvell,armada-3700-ahci" - - "snps,dwc-ahci" - - "snps,spear-ahci" - - "generic-ahci" -- interrupts : -- reg : - -Please note that when using "generic-ahci" you must also specify a SoC specific -compatible: - compatible = "manufacturer,soc-model-ahci", "generic-ahci"; - -Optional properties: -- dma-coherent : Present if dma operations are coherent -- clocks : a list of phandle + clock specifier pairs -- resets : a list of phandle + reset specifier pairs -- target-supply : regulator for SATA target power -- phy-supply : regulator for PHY power -- phys : reference to the SATA PHY node -- phy-names : must be "sata-phy" -- ahci-supply : regulator for AHCI controller -- ports-implemented : Mask that indicates which ports that the HBA supports - are available for software to use. Useful if PORTS_IMPL - is not programmed by the BIOS, which is true with - some embedded SOC's. - -Required properties when using sub-nodes: -- #address-cells : number of cells to encode an address -- #size-cells : number of cells representing the size of an address - -Sub-nodes required properties: -- reg : the port number -And at least one of the following properties: -- phys : reference to the SATA PHY node -- target-supply : regulator for SATA target power - -Examples: - sata@ffe08000 { - compatible = "snps,spear-ahci"; - reg = <0xffe08000 0x1000>; - interrupts = <115>; - }; - -With sub-nodes: - sata@f7e90000 { - compatible = "marvell,berlin2q-achi", "generic-ahci"; - reg = <0xe90000 0x1000>; - interrupts = ; - clocks = <&chip CLKID_SATA>; - #address-cells = <1>; - #size-cells = <0>; - - sata0: sata-port@0 { - reg = <0>; - phys = <&sata_phy 0>; - target-supply = <®_sata0>; - }; - - sata1: sata-port@1 { - reg = <1>; - phys = <&sata_phy 1>; - target-supply = <®_sata1>;; - }; - }; diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.yaml b/Documentation/devicetree/bindings/ata/ahci-platform.yaml new file mode 100644 index 0000000000000..7dc2a2e8f598b --- /dev/null +++ b/Documentation/devicetree/bindings/ata/ahci-platform.yaml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/ahci-platform.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AHCI SATA Controller + +description: | + SATA nodes are defined to describe on-chip Serial ATA controllers. + Each SATA controller should have its own node. + + It is possible, but not required, to represent each port as a sub-node. + It allows to enable each port independently when dealing with multiple + PHYs. + +maintainers: + - Hans de Goede + - Jens Axboe + +select: + properties: + compatible: + contains: + enum: + - brcm,iproc-ahci + - cavium,octeon-7130-ahci + - hisilicon,hisi-ahci + - ibm,476gtr-ahci + - marvell,armada-3700-ahci + - marvell,armada-8k-ahci + - marvell,berlin2q-ahci + required: + - compatible + +allOf: + - $ref: "ahci-common.yaml#" + +properties: + compatible: + oneOf: + - items: + - enum: + - brcm,iproc-ahci + - marvell,armada-8k-ahci + - marvell,berlin2-ahci + - marvell,berlin2q-ahci + - const: generic-ahci + - enum: + - cavium,octeon-7130-ahci + - hisilicon,hisi-ahci + - ibm,476gtr-ahci + - marvell,armada-3700-ahci + + reg: + minItems: 1 + maxItems: 2 + + reg-names: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 3 + + clock-names: + minItems: 1 + maxItems: 3 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + +patternProperties: + "^sata-port@[0-9a-f]+$": + $ref: /schemas/ata/ahci-common.yaml#/$defs/ahci-port + + anyOf: + - required: [ phys ] + - required: [ target-supply ] + + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + sata@ffe08000 { + compatible = "snps,spear-ahci"; + reg = <0xffe08000 0x1000>; + interrupts = <115>; + }; + - | + #include + #include + #include + + sata@f7e90000 { + compatible = "marvell,berlin2q-ahci", "generic-ahci"; + reg = <0xf7e90000 0x1000>; + interrupts = ; + clocks = <&chip CLKID_SATA>; + #address-cells = <1>; + #size-cells = <0>; + + hba-cap = ; + + sata0: sata-port@0 { + reg = <0>; + + phys = <&sata_phy 0>; + target-supply = <®_sata0>; + + hba-port-cap = <(HBA_PORT_FBSCP | HBA_PORT_ESP)>; + }; + + sata1: sata-port@1 { + reg = <1>; + + phys = <&sata_phy 1>; + target-supply = <®_sata1>; + + hba-port-cap = <(HBA_PORT_HPCP | HBA_PORT_MPSP | HBA_PORT_FBSCP)>; + }; + }; diff --git a/Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml b/Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml new file mode 100644 index 0000000000000..9b7ca4759bd72 --- /dev/null +++ b/Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/baikal,bt1-ahci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Baikal-T1 SoC AHCI SATA controller + +maintainers: + - Serge Semin + +description: + AHCI SATA controller embedded into the Baikal-T1 SoC is based on the + DWC AHCI SATA v4.10a IP-core. + +allOf: + - $ref: snps,dwc-ahci-common.yaml# + +properties: + compatible: + const: baikal,bt1-ahci + + clocks: + items: + - description: Peripheral APB bus clock + - description: Application AXI BIU clock + - description: SATA Ports reference clock + + clock-names: + items: + - const: pclk + - const: aclk + - const: ref + + resets: + items: + - description: Application AXI BIU domain reset + - description: SATA Ports clock domain reset + + reset-names: + items: + - const: arst + - const: ref + + ports-implemented: + maximum: 0x3 + +patternProperties: + "^sata-port@[0-1]$": + $ref: /schemas/ata/snps,dwc-ahci-common.yaml#/$defs/dwc-ahci-port + + properties: + reg: + minimum: 0 + maximum: 1 + + snps,tx-ts-max: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Due to having AXI3 bus interface utilized the maximum Tx DMA + transaction size can't exceed 16 beats (AxLEN[3:0]). + enum: [ 1, 2, 4, 8, 16 ] + + snps,rx-ts-max: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Due to having AXI3 bus interface utilized the maximum Rx DMA + transaction size can't exceed 16 beats (AxLEN[3:0]). + enum: [ 1, 2, 4, 8, 16 ] + + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + +unevaluatedProperties: false + +examples: + - | + sata@1f050000 { + compatible = "baikal,bt1-ahci"; + reg = <0x1f050000 0x2000>; + #address-cells = <1>; + #size-cells = <0>; + + interrupts = <0 64 4>; + + clocks = <&ccu_sys 1>, <&ccu_axi 2>, <&sata_ref_clk>; + clock-names = "pclk", "aclk", "ref"; + + resets = <&ccu_axi 2>, <&ccu_sys 0>; + reset-names = "arst", "ref"; + + ports-implemented = <0x3>; + + sata-port@0 { + reg = <0>; + + snps,tx-ts-max = <4>; + snps,rx-ts-max = <4>; + }; + + sata-port@1 { + reg = <1>; + + snps,tx-ts-max = <4>; + snps,rx-ts-max = <4>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/ata/brcm,sata-brcm.txt b/Documentation/devicetree/bindings/ata/brcm,sata-brcm.txt deleted file mode 100644 index b9ae4ce4a0a06..0000000000000 --- a/Documentation/devicetree/bindings/ata/brcm,sata-brcm.txt +++ /dev/null @@ -1,45 +0,0 @@ -* Broadcom SATA3 AHCI Controller - -SATA nodes are defined to describe on-chip Serial ATA controllers. -Each SATA controller should have its own node. - -Required properties: -- compatible : should be one or more of - "brcm,bcm7216-ahci" - "brcm,bcm7425-ahci" - "brcm,bcm7445-ahci" - "brcm,bcm-nsp-ahci" - "brcm,sata3-ahci" - "brcm,bcm63138-ahci" -- reg : register mappings for AHCI and SATA_TOP_CTRL -- reg-names : "ahci" and "top-ctrl" -- interrupts : interrupt mapping for SATA IRQ - -Optional properties: - -- reset: for "brcm,bcm7216-ahci" must be a valid reset phandle - pointing to the RESCAL reset controller provider node. -- reset-names: for "brcm,bcm7216-ahci", must be "rescal". - -Also see ahci-platform.txt. - -Example: - - sata@f045a000 { - compatible = "brcm,bcm7445-ahci", "brcm,sata3-ahci"; - reg = <0xf045a000 0xa9c>, <0xf0458040 0x24>; - reg-names = "ahci", "top-ctrl"; - interrupts = <0 30 0>; - #address-cells = <1>; - #size-cells = <0>; - - sata0: sata-port@0 { - reg = <0>; - phys = <&sata_phy 0>; - }; - - sata1: sata-port@1 { - reg = <1>; - phys = <&sata_phy 1>; - }; - }; diff --git a/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml b/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml new file mode 100644 index 0000000000000..fa8ebc8f243f4 --- /dev/null +++ b/Documentation/devicetree/bindings/ata/brcm,sata-brcm.yaml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/brcm,sata-brcm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom SATA3 AHCI Controller + +description: + SATA nodes are defined to describe on-chip Serial ATA controllers. + Each SATA controller should have its own node. + +maintainers: + - Florian Fainelli + +allOf: + - $ref: ahci-common.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - brcm,bcm7216-ahci + - brcm,bcm7445-ahci + - brcm,bcm7425-ahci + - brcm,bcm63138-ahci + - const: brcm,sata3-ahci + - items: + - const: brcm,bcm-nsp-ahci + + reg: + minItems: 2 + maxItems: 2 + + reg-names: + items: + - const: ahci + - const: top-ctrl + + interrupts: + maxItems: 1 + +if: + properties: + compatible: + contains: + enum: + - brcm,bcm7216-ahci + - brcm,bcm63138-ahci +then: + properties: + resets: + maxItems: 1 + reset-names: + enum: + - rescal + - ahci + +required: + - compatible + - reg + - interrupts + - "#address-cells" + - "#size-cells" + +unevaluatedProperties: false + +examples: + - | + sata@f045a000 { + compatible = "brcm,bcm7445-ahci", "brcm,sata3-ahci"; + reg = <0xf045a000 0xa9c>, <0xf0458040 0x24>; + reg-names = "ahci", "top-ctrl"; + interrupts = <0 30 0>; + #address-cells = <1>; + #size-cells = <0>; + + sata0: sata-port@0 { + reg = <0>; + phys = <&sata_phy 0>; + }; + + sata1: sata-port@1 { + reg = <1>; + phys = <&sata_phy 1>; + }; + }; diff --git a/Documentation/devicetree/bindings/ata/sata-common.yaml b/Documentation/devicetree/bindings/ata/sata-common.yaml index 7ac77b1c5850d..58c9342b99255 100644 --- a/Documentation/devicetree/bindings/ata/sata-common.yaml +++ b/Documentation/devicetree/bindings/ata/sata-common.yaml @@ -31,22 +31,27 @@ properties: "#size-cells": const: 0 + dma-coherent: true + patternProperties: "^sata-port@[0-9a-e]$": + $ref: '#/$defs/sata-port' description: | DT nodes for ports connected on the SATA host. The SATA port nodes will be named "sata-port". + +additionalProperties: true + +$defs: + sata-port: type: object properties: reg: minimum: 0 - maximum: 14 description: - The ID number of the drive port SATA can potentially use a port - multiplier making it possible to connect up to 15 disks to a single - SATA port. - -additionalProperties: true + The ID number of the SATA port. Aside with being directly used, + each port can have a Port Multiplier attached thus allowing to + access more than one drive by means of a single SATA port. ... diff --git a/Documentation/devicetree/bindings/ata/snps,dwc-ahci-common.yaml b/Documentation/devicetree/bindings/ata/snps,dwc-ahci-common.yaml new file mode 100644 index 0000000000000..c1457910520bf --- /dev/null +++ b/Documentation/devicetree/bindings/ata/snps,dwc-ahci-common.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/snps,dwc-ahci-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DWC AHCI SATA controller properties + +maintainers: + - Serge Semin + +description: + This document defines device tree schema for the generic Synopsys DWC + AHCI controller properties. + +select: false + +allOf: + - $ref: ahci-common.yaml# + +properties: + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + description: + Basic DWC AHCI SATA clock sources like application AXI/AHB BIU clock, + PM-alive clock, RxOOB detection clock, embedded PHYs reference (Rx/Tx) + clock, etc. + minItems: 1 + maxItems: 4 + + clock-names: + minItems: 1 + maxItems: 4 + items: + oneOf: + - description: Application APB/AHB/AXI BIU clock + enum: + - pclk + - aclk + - hclk + - sata + - description: Power Module keep-alive clock + const: pmalive + - description: RxOOB detection clock + const: rxoob + - description: SATA Ports reference clock + const: ref + + resets: + description: + At least basic application and reference clock domains resets are + normally supported by the DWC AHCI SATA controller. + minItems: 1 + maxItems: 4 + + reset-names: + minItems: 1 + maxItems: 4 + items: + oneOf: + - description: Application AHB/AXI BIU clock domain reset control + enum: + - arst + - hrst + - description: Power Module keep-alive clock domain reset control + const: pmalive + - description: RxOOB detection clock domain reset control + const: rxoob + - description: Reference clock domain reset control + const: ref + +patternProperties: + "^sata-port@[0-9a-e]$": + $ref: '#/$defs/dwc-ahci-port' + +additionalProperties: true + +$defs: + dwc-ahci-port: + $ref: /schemas/ata/ahci-common.yaml#/$defs/ahci-port + + properties: + reg: + minimum: 0 + maximum: 7 + + snps,tx-ts-max: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Maximal size of Tx DMA transactions in FIFO words + enum: [ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 ] + + snps,rx-ts-max: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Maximal size of Rx DMA transactions in FIFO words + enum: [ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 ] + +... diff --git a/Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml b/Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml new file mode 100644 index 0000000000000..5afa4b57ce20c --- /dev/null +++ b/Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/snps,dwc-ahci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DWC AHCI SATA controller + +maintainers: + - Serge Semin + +description: + This document defines device tree bindings for the generic Synopsys DWC + implementation of the AHCI SATA controller. + +allOf: + - $ref: snps,dwc-ahci-common.yaml# + +properties: + compatible: + oneOf: + - description: Synopsys AHCI SATA-compatible devices + const: snps,dwc-ahci + - description: SPEAr1340 AHCI SATA device + const: snps,spear-ahci + - description: Rockhip RK3568 AHCI controller + items: + - const: rockchip,rk3568-dwc-ahci + - const: snps,dwc-ahci + +patternProperties: + "^sata-port@[0-9a-e]$": + $ref: /schemas/ata/snps,dwc-ahci-common.yaml#/$defs/dwc-ahci-port + + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + #include + + sata@122f0000 { + compatible = "snps,dwc-ahci"; + reg = <0x122F0000 0x1ff>; + #address-cells = <1>; + #size-cells = <0>; + + interrupts = ; + + clocks = <&clock1>, <&clock2>; + clock-names = "aclk", "ref"; + + phys = <&sata_phy>; + phy-names = "sata-phy"; + + ports-implemented = <0x1>; + + sata-port@0 { + reg = <0>; + + hba-port-cap = ; + + snps,tx-ts-max = <512>; + snps,rx-ts-max = <512>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/clock/samsung,exynos-audss-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,exynos-audss-clock.yaml index f14f1d39da362..d819dfaafff9b 100644 --- a/Documentation/devicetree/bindings/clock/samsung,exynos-audss-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,exynos-audss-clock.yaml @@ -8,7 +8,7 @@ title: Samsung Exynos SoC Audio SubSystem clock controller maintainers: - Chanwoo Choi - - Krzysztof Kozlowski + - Krzysztof Kozlowski - Sylwester Nawrocki - Tomasz Figa diff --git a/Documentation/devicetree/bindings/clock/samsung,exynos-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,exynos-clock.yaml index 4e8062860986a..0589a63e273a1 100644 --- a/Documentation/devicetree/bindings/clock/samsung,exynos-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,exynos-clock.yaml @@ -8,7 +8,7 @@ title: Samsung Exynos SoC clock controller maintainers: - Chanwoo Choi - - Krzysztof Kozlowski + - Krzysztof Kozlowski - Sylwester Nawrocki - Tomasz Figa diff --git a/Documentation/devicetree/bindings/clock/samsung,exynos-ext-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,exynos-ext-clock.yaml index 64d027dbe3b25..c98eff64f2b58 100644 --- a/Documentation/devicetree/bindings/clock/samsung,exynos-ext-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,exynos-ext-clock.yaml @@ -8,7 +8,7 @@ title: Samsung SoC external/osc/XXTI/XusbXTI clock maintainers: - Chanwoo Choi - - Krzysztof Kozlowski + - Krzysztof Kozlowski - Sylwester Nawrocki - Tomasz Figa diff --git a/Documentation/devicetree/bindings/clock/samsung,exynos4412-isp-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,exynos4412-isp-clock.yaml index 1ed64add4355d..b644bbd0df384 100644 --- a/Documentation/devicetree/bindings/clock/samsung,exynos4412-isp-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,exynos4412-isp-clock.yaml @@ -8,7 +8,7 @@ title: Samsung Exynos4412 SoC ISP clock controller maintainers: - Chanwoo Choi - - Krzysztof Kozlowski + - Krzysztof Kozlowski - Sylwester Nawrocki - Tomasz Figa diff --git a/Documentation/devicetree/bindings/clock/samsung,s5pv210-audss-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,s5pv210-audss-clock.yaml index ae8f8fc932338..2659854ea1c0a 100644 --- a/Documentation/devicetree/bindings/clock/samsung,s5pv210-audss-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,s5pv210-audss-clock.yaml @@ -8,7 +8,7 @@ title: Samsung S5Pv210 SoC Audio SubSystem clock controller maintainers: - Chanwoo Choi - - Krzysztof Kozlowski + - Krzysztof Kozlowski - Sylwester Nawrocki - Tomasz Figa diff --git a/Documentation/devicetree/bindings/devfreq/event/samsung,exynos-nocp.yaml b/Documentation/devicetree/bindings/devfreq/event/samsung,exynos-nocp.yaml index d318fccf78f10..2bdd05af6079b 100644 --- a/Documentation/devicetree/bindings/devfreq/event/samsung,exynos-nocp.yaml +++ b/Documentation/devicetree/bindings/devfreq/event/samsung,exynos-nocp.yaml @@ -8,7 +8,7 @@ title: Samsung Exynos NoC (Network on Chip) Probe maintainers: - Chanwoo Choi - - Krzysztof Kozlowski + - Krzysztof Kozlowski description: | The Samsung Exynos542x SoC has a NoC (Network on Chip) Probe for NoC bus. diff --git a/Documentation/devicetree/bindings/devfreq/event/samsung,exynos-ppmu.yaml b/Documentation/devicetree/bindings/devfreq/event/samsung,exynos-ppmu.yaml index c9a8cb5fd5558..e300df4b47f3d 100644 --- a/Documentation/devicetree/bindings/devfreq/event/samsung,exynos-ppmu.yaml +++ b/Documentation/devicetree/bindings/devfreq/event/samsung,exynos-ppmu.yaml @@ -8,7 +8,7 @@ title: Samsung Exynos SoC PPMU (Platform Performance Monitoring Unit) maintainers: - Chanwoo Choi - - Krzysztof Kozlowski + - Krzysztof Kozlowski description: | The Samsung Exynos SoC has PPMU (Platform Performance Monitoring Unit) for diff --git a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358768.yaml b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358768.yaml index eacfe71650832..ff98442aa2815 100644 --- a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358768.yaml +++ b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358768.yaml @@ -58,6 +58,7 @@ properties: properties: data-lines: + $ref: /schemas/types.yaml#/definitions/uint32 enum: [ 16, 18, 24 ] port@1: diff --git a/Documentation/devicetree/bindings/display/panel/panel-timing.yaml b/Documentation/devicetree/bindings/display/panel/panel-timing.yaml index 9bf592dc3033a..4444568a878ee 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-timing.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-timing.yaml @@ -152,6 +152,7 @@ properties: Horizontal sync pulse. 0 selects active low, 1 selects active high. If omitted then it is not used by the hardware + $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1] vsync-active: @@ -159,6 +160,7 @@ properties: Vertical sync pulse. 0 selects active low, 1 selects active high. If omitted then it is not used by the hardware + $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1] de-active: @@ -166,6 +168,7 @@ properties: Data enable. 0 selects active low, 1 selects active high. If omitted then it is not used by the hardware + $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1] pixelclk-active: @@ -175,6 +178,7 @@ properties: sample data on rising edge. Use 1 to drive pixel data on rising edge and sample data on falling edge + $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1] syncclk-active: @@ -185,6 +189,7 @@ properties: sample sync on rising edge of pixel clock. Use 1 to drive sync on rising edge and sample sync on falling edge of pixel clock + $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1] interlaced: diff --git a/Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml index 745dd247c409b..617aa8c8c03a7 100644 --- a/Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml +++ b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml @@ -24,6 +24,7 @@ properties: dsi-lanes: description: Number of DSI lanes to be used must be <3> or <4> + $ref: /schemas/types.yaml#/definitions/uint32 enum: [3, 4] v3p3-supply: diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa0.yaml b/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa0.yaml index ca959451557e0..1cdc91b3439f3 100644 --- a/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa0.yaml +++ b/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa0.yaml @@ -36,6 +36,7 @@ properties: init-delay: description: delay after initialization sequence [ms] + $ref: /schemas/types.yaml#/definitions/uint32 panel-width-mm: description: physical panel width [mm] diff --git a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml index 6b35089ac0172..5f3c507ce3856 100644 --- a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml @@ -15,7 +15,9 @@ allOf: properties: compatible: - const: snps,dma-spear1340 + oneOf: + - const: snps,dma-spear1340 + - const: baikal,bt1-dmac "#dma-cells": minimum: 3 diff --git a/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml b/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml index 5fe19fa5f67c5..a99e7842ca173 100644 --- a/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml +++ b/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml @@ -26,6 +26,7 @@ properties: const: 2 registers-number: + $ref: /schemas/types.yaml#/definitions/uint32 description: Number of daisy-chained shift registers enable-gpios: diff --git a/Documentation/devicetree/bindings/gpio/toshiba,gpio-visconti.yaml b/Documentation/devicetree/bindings/gpio/toshiba,gpio-visconti.yaml index 9ad470e019537..b085450b527f8 100644 --- a/Documentation/devicetree/bindings/gpio/toshiba,gpio-visconti.yaml +++ b/Documentation/devicetree/bindings/gpio/toshiba,gpio-visconti.yaml @@ -43,7 +43,6 @@ required: - gpio-controller - interrupt-controller - "#interrupt-cells" - - interrupt-parent additionalProperties: false diff --git a/Documentation/devicetree/bindings/input/google,cros-ec-keyb.yaml b/Documentation/devicetree/bindings/input/google,cros-ec-keyb.yaml index 5377b232fa10f..aa1e36b95841b 100644 --- a/Documentation/devicetree/bindings/input/google,cros-ec-keyb.yaml +++ b/Documentation/devicetree/bindings/input/google,cros-ec-keyb.yaml @@ -32,6 +32,7 @@ properties: type: boolean function-row-physmap: + $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 1 maxItems: 15 description: | diff --git a/Documentation/devicetree/bindings/interrupt-controller/samsung,exynos4210-combiner.yaml b/Documentation/devicetree/bindings/interrupt-controller/samsung,exynos4210-combiner.yaml index d631b7589d506..72456a07dac96 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/samsung,exynos4210-combiner.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/samsung,exynos4210-combiner.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Samsung Exynos SoC Interrupt Combiner Controller maintainers: - - Krzysztof Kozlowski + - Krzysztof Kozlowski description: | Samsung's Exynos4 architecture includes a interrupt combiner controller which diff --git a/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml b/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml index e864d798168d0..d433e496ec6e2 100644 --- a/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml +++ b/Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml @@ -175,15 +175,6 @@ required: - ti,mbox-num-fifos allOf: - - if: - properties: - compatible: - enum: - - ti,am654-mailbox - then: - required: - - interrupt-parent - - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/media/i2c/adv7604.yaml b/Documentation/devicetree/bindings/media/i2c/adv7604.yaml index de15cebe29553..1f0c3024f5ce8 100644 --- a/Documentation/devicetree/bindings/media/i2c/adv7604.yaml +++ b/Documentation/devicetree/bindings/media/i2c/adv7604.yaml @@ -59,7 +59,8 @@ properties: enables hot-plug detection. default-input: - maxItems: 1 + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] description: Select which input is selected after reset. diff --git a/Documentation/devicetree/bindings/memory-controllers/brcm,dpfe-cpu.yaml b/Documentation/devicetree/bindings/memory-controllers/brcm,dpfe-cpu.yaml index 769f132500474..08cbdcddfead0 100644 --- a/Documentation/devicetree/bindings/memory-controllers/brcm,dpfe-cpu.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/brcm,dpfe-cpu.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: DDR PHY Front End (DPFE) for Broadcom STB maintainers: - - Krzysztof Kozlowski + - Krzysztof Kozlowski - Markus Mayer properties: diff --git a/Documentation/devicetree/bindings/memory-controllers/marvell,mvebu-sdram-controller.yaml b/Documentation/devicetree/bindings/memory-controllers/marvell,mvebu-sdram-controller.yaml index 14a6bc8f421fc..9249624c4fa00 100644 --- a/Documentation/devicetree/bindings/memory-controllers/marvell,mvebu-sdram-controller.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/marvell,mvebu-sdram-controller.yaml @@ -8,7 +8,7 @@ title: Marvell MVEBU SDRAM controller maintainers: - Jan Luebbe - - Krzysztof Kozlowski + - Krzysztof Kozlowski properties: compatible: diff --git a/Documentation/devicetree/bindings/memory-controllers/qca,ath79-ddr-controller.yaml b/Documentation/devicetree/bindings/memory-controllers/qca,ath79-ddr-controller.yaml index 9566b3421f039..0c511ab906bf8 100644 --- a/Documentation/devicetree/bindings/memory-controllers/qca,ath79-ddr-controller.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/qca,ath79-ddr-controller.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm Atheros AR7xxx/AR9xxx DDR controller maintainers: - - Krzysztof Kozlowski + - Krzysztof Kozlowski description: | The DDR controller of the AR7xxx and AR9xxx families provides an interface to diff --git a/Documentation/devicetree/bindings/memory-controllers/renesas,h8300-bsc.yaml b/Documentation/devicetree/bindings/memory-controllers/renesas,h8300-bsc.yaml index 2b18cef99511f..514b2c5f88586 100644 --- a/Documentation/devicetree/bindings/memory-controllers/renesas,h8300-bsc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/renesas,h8300-bsc.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: H8/300 bus controller maintainers: - - Krzysztof Kozlowski + - Krzysztof Kozlowski - Yoshinori Sato properties: diff --git a/Documentation/devicetree/bindings/memory-controllers/samsung,exynos5422-dmc.yaml b/Documentation/devicetree/bindings/memory-controllers/samsung,exynos5422-dmc.yaml index 6f4fd5814bf47..05d607e146885 100644 --- a/Documentation/devicetree/bindings/memory-controllers/samsung,exynos5422-dmc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/samsung,exynos5422-dmc.yaml @@ -9,7 +9,7 @@ title: | Controller device maintainers: - - Krzysztof Kozlowski + - Krzysztof Kozlowski - Lukasz Luba description: | diff --git a/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml b/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml new file mode 100644 index 0000000000000..899a6c5f9806a --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/snps,dw-umctl2-ddrc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DesignWare Universal Multi-Protocol Memory Controller + +maintainers: + - Krzysztof Kozlowski + - Manish Narani + - Michal Simek + +description: | + Synopsys DesignWare Enhanced uMCTL2 DDR Memory Controller is cappable of + working with DDR devices upporting 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. + + For instance the ZynqMP DDR controller is based on the DW uMCTL2 v2.40a + controller. It has an optional SEC/DEC ECC support in 64-bit and 32-bit + bus width configurations. + +properties: + compatible: + enum: + - snps,ddrc-3.80a + - xlnx,zynqmp-ddrc-2.40a + - baikal,bt1-ddrc + + 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: + minItems: 1 + maxItems: 2 + + reg-names: + minItems: 1 + items: + - const: umctl2 + - const: phy + + 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 + +examples: + - | + memory-controller@fd070000 { + compatible = "xlnx,zynqmp-ddrc-2.40a"; + reg = <0xfd070000 0x30000>; + interrupt-parent = <&gic>; + interrupts = <0 112 4>; + }; + - | + memory-controller@fd070000 { + compatible = "snps,ddrc-3.80a"; + reg = <0x3d400000 0x400000>; + + interrupts = <0 147 4>, <0 148 4>, <0 149 4>, <0 150 4>; + interrupt-names = "ecc_ce", "ecc_ue", "ecc_sbr", "dfi_e"; + + clocks = <&rcu 0>, <&rcu 5>, <&rcu 6>, <&rcu 7>; + clock-names = "pclk", "aclk", "core", "sbr"; + }; +... diff --git a/Documentation/devicetree/bindings/memory-controllers/synopsys,ddrc-ecc.yaml b/Documentation/devicetree/bindings/memory-controllers/synopsys,ddrc-ecc.yaml deleted file mode 100644 index a245884746255..0000000000000 --- a/Documentation/devicetree/bindings/memory-controllers/synopsys,ddrc-ecc.yaml +++ /dev/null @@ -1,73 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/memory-controllers/synopsys,ddrc-ecc.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Synopsys IntelliDDR Multi Protocol memory controller - -maintainers: - - Krzysztof Kozlowski - - Manish Narani - - Michal Simek - -description: | - The ZynqMP DDR ECC controller has an optional ECC support in 64-bit and - 32-bit bus width configurations. - - The Zynq DDR ECC controller has an optional ECC support in half-bus width - (16-bit) configuration. - - These both ECC controllers correct single bit ECC errors and detect double bit - ECC errors. - -properties: - compatible: - enum: - - xlnx,zynq-ddrc-a05 - - xlnx,zynqmp-ddrc-2.40a - - interrupts: - maxItems: 1 - - reg: - maxItems: 1 - -required: - - compatible - - reg - -allOf: - - if: - properties: - compatible: - contains: - const: xlnx,zynqmp-ddrc-2.40a - then: - required: - - interrupts - else: - properties: - interrupts: false - -additionalProperties: false - -examples: - - | - memory-controller@f8006000 { - compatible = "xlnx,zynq-ddrc-a05"; - reg = <0xf8006000 0x1000>; - }; - - - | - axi { - #address-cells = <2>; - #size-cells = <2>; - - memory-controller@fd070000 { - compatible = "xlnx,zynqmp-ddrc-2.40a"; - reg = <0x0 0xfd070000 0x0 0x30000>; - interrupt-parent = <&gic>; - interrupts = <0 112 4>; - }; - }; diff --git a/Documentation/devicetree/bindings/memory-controllers/ti,da8xx-ddrctl.yaml b/Documentation/devicetree/bindings/memory-controllers/ti,da8xx-ddrctl.yaml index 9ed51185ff996..382ddab60fbda 100644 --- a/Documentation/devicetree/bindings/memory-controllers/ti,da8xx-ddrctl.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/ti,da8xx-ddrctl.yaml @@ -8,7 +8,7 @@ title: Texas Instruments da8xx DDR2/mDDR memory controller maintainers: - Bartosz Golaszewski - - Krzysztof Kozlowski + - Krzysztof Kozlowski description: | Documentation: diff --git a/Documentation/devicetree/bindings/memory-controllers/xlnx,zynq-ddrc-a05.yaml b/Documentation/devicetree/bindings/memory-controllers/xlnx,zynq-ddrc-a05.yaml new file mode 100644 index 0000000000000..8f72e2f8588ab --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/xlnx,zynq-ddrc-a05.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/xlnx,zynq-ddrc-a05.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Zynq A05 DDR Memory Controller + +maintainers: + - Krzysztof Kozlowski + - Manish Narani + - Michal Simek + +description: + The Zynq DDR ECC controller has an optional ECC support in half-bus width + (16-bit) configuration. It is cappable of correcting single bit ECC errors + and detecting double bit ECC errors. + +properties: + compatible: + const: xlnx,zynq-ddrc-a05 + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + memory-controller@f8006000 { + compatible = "xlnx,zynq-ddrc-a05"; + reg = <0xf8006000 0x1000>; + }; +... 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..cba31ff5c2c96 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/baikal,bt1-boot-con.yaml @@ -0,0 +1,104 @@ +# 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 + +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 + + 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..88b731ecfd3c0 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/baikal,bt1-sys-con.yaml @@ -0,0 +1,125 @@ +# 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 + +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 + + 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/memory-controllers/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/cirrus,madera.yaml b/Documentation/devicetree/bindings/mfd/cirrus,madera.yaml index 499c62c04daab..5dce62a7eff26 100644 --- a/Documentation/devicetree/bindings/mfd/cirrus,madera.yaml +++ b/Documentation/devicetree/bindings/mfd/cirrus,madera.yaml @@ -221,7 +221,6 @@ required: - '#gpio-cells' - interrupt-controller - '#interrupt-cells' - - interrupt-parent - interrupts - AVDD-supply - DBVDD1-supply diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml index abe3fd817e0b7..9cf29fbaf8a31 100644 --- a/Documentation/devicetree/bindings/mfd/syscon.yaml +++ b/Documentation/devicetree/bindings/mfd/syscon.yaml @@ -63,8 +63,7 @@ properties: minItems: 2 maxItems: 4 # Should be enough - reg: - maxItems: 1 + reg: true reg-io-width: description: | diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml b/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml index f827984936f6e..89ae8776b3d3c 100644 --- a/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml +++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml @@ -50,6 +50,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/mux/reg-mux.yaml b/Documentation/devicetree/bindings/mux/reg-mux.yaml index 60d5746eb39de..3b030b8fb47c4 100644 --- a/Documentation/devicetree/bindings/mux/reg-mux.yaml +++ b/Documentation/devicetree/bindings/mux/reg-mux.yaml @@ -25,8 +25,12 @@ properties: const: 1 mux-reg-masks: - description: an array of register offset and pre-shifted bitfield mask - pairs, each describing a single mux control. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: register offset + - description: pre-shifted bitfield mask + description: Each entry pair describes a single mux control. idle-states: true diff --git a/Documentation/devicetree/bindings/net/ingenic,mac.yaml b/Documentation/devicetree/bindings/net/ingenic,mac.yaml index d08a88125a5cc..1ad52e3565403 100644 --- a/Documentation/devicetree/bindings/net/ingenic,mac.yaml +++ b/Documentation/devicetree/bindings/net/ingenic,mac.yaml @@ -37,6 +37,7 @@ properties: const: stmmaceth mode-reg: + $ref: /schemas/types.yaml#/definitions/phandle description: An extra syscon register that control ethernet interface and timing delay rx-clk-delay-ps: diff --git a/Documentation/devicetree/bindings/net/ti,davinci-mdio.yaml b/Documentation/devicetree/bindings/net/ti,davinci-mdio.yaml index 5728fe23f5304..8be245e272d2b 100644 --- a/Documentation/devicetree/bindings/net/ti,davinci-mdio.yaml +++ b/Documentation/devicetree/bindings/net/ti,davinci-mdio.yaml @@ -34,6 +34,7 @@ properties: maxItems: 1 bus_freq: + $ref: /schemas/types.yaml#/definitions/uint32 maximum: 2500000 description: MDIO Bus frequency diff --git a/Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml b/Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml new file mode 100644 index 0000000000000..b00cb04357d8c --- /dev/null +++ b/Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml @@ -0,0 +1,153 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/baikal,bt1-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Baikal-T1 PCIe Root Port Controller + +maintainers: + - Serge Semin + +description: + Embedded into Baikal-T1 SoC Root Complex controller with a single port + activated. It's based on the DWC RC PCIe v4.60a IP-core, which is configured + to have just a single Root Port function and is capable of establishing the + link up to Gen.3 speed on x4 lanes. It doesn't have embedded clock and reset + control module, so the proper interface initialization is supposed to be + performed by software. There four in- and four outbound iATU regions + which can be used to emit all required TLP types on the PCIe bus. + +allOf: + - $ref: /schemas/pci/snps,dw-pcie.yaml# + +properties: + compatible: + const: baikal,bt1-pcie + + reg: + description: + DBI, DBI2 and at least 4KB outbound iATU-capable region for the + peripheral devices CFG-space access. + maxItems: 3 + + reg-names: + items: + - const: dbi + - const: dbi2 + - const: config + + interrupts: + description: + MSI, AER, PME, Hot-plug, Link Bandwidth Management, Link Equalization + request and eight Read/Write eDMA IRQ lines are available. + maxItems: 14 + + interrupt-names: + items: + - const: dma0 + - const: dma1 + - const: dma2 + - const: dma3 + - const: dma4 + - const: dma5 + - const: dma6 + - const: dma7 + - const: msi + - const: aer + - const: pme + - const: hp + - const: bw_mg + - const: l_eq + + clocks: + description: + DBI (attached to the APB bus), AXI-bus master and slave interfaces + are fed up by the dedicated application clocks. A common reference + clock signal is supposed to be attached to the corresponding Ref-pad + of the SoC. It will be redistributed amongst the controller core + sub-modules (pipe, core, aux, etc). + maxItems: 4 + + clock-names: + items: + - const: dbi + - const: mstr + - const: slv + - const: ref + + resets: + description: + A comprehensive controller reset logic is supposed to be implemented + by software, so almost all the possible application and core reset + signals are exposed via the system CCU module. + maxItems: 9 + + reset-names: + items: + - const: mstr + - const: slv + - const: pwr + - const: hot + - const: phy + - const: core + - const: pipe + - const: sticky + - const: non-sticky + + baikal,bt1-syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the Baikal-T1 System Controller DT node. It's required to + access some additional PM, Reset-related and LTSSM signals. + + num-lanes: + maximum: 4 + + max-link-speed: + maximum: 3 + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + +unevaluatedProperties: false + +examples: + - | + pcie@1f052000 { + compatible = "baikal,bt1-pcie"; + device_type = "pci"; + reg = <0x1f052000 0x1000>, <0x1f053000 0x1000>, <0x1bdbf000 0x1000>; + reg-names = "dbi", "dbi2", "config"; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x81000000 0 0x00000000 0x1bdb0000 0 0x00008000>, + <0x82000000 0 0x20000000 0x08000000 0 0x13db0000>; + bus-range = <0x0 0xff>; + + interrupts = <0 80 4>, <0 81 4>, <0 82 4>, <0 83 4>, + <0 84 4>, <0 85 4>, <0 86 4>, <0 87 4>, + <0 88 4>, <0 89 4>, <0 90 4>, <0 91 4>, + <0 92 4>, <0 93 4>; + interrupt-names = "dma0", "dma1", "dma2", "dma3", "dma4", "dma5", "dma6", + "dma7", "msi", "aer", "pme", "hp", "bw_mg", "l_eq"; + + clocks = <&ccu_sys 1>, <&ccu_axi 6>, <&ccu_axi 7>, <&clk_pcie>; + clock-names = "dbi", "mstr", "slv", "ref"; + + resets = <&ccu_axi 6>, <&ccu_axi 7>, <&ccu_sys 7>, <&ccu_sys 10>, + <&ccu_sys 4>, <&ccu_sys 6>, <&ccu_sys 5>, <&ccu_sys 8>, + <&ccu_sys 9>; + reset-names = "mstr", "slv", "pwr", "hot", "phy", "core", "pipe", + "sticky", "non-sticky"; + + reset-gpios = <&port0 0 1>; + + num-lanes = <4>; + max-link-speed = <3>; + }; +... diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml index acea1cd444fd5..1a255f039bc92 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml @@ -16,6 +16,47 @@ description: |+ allOf: - $ref: /schemas/pci/snps,dw-pcie.yaml# + - if: + properties: + compatible: + contains: + const: fsl,imx6sx-pcie + then: + properties: + clock-names: + items: + - const: pcie + - const: pcie_bus + - const: pcie_phy + - const: pcie_inbound_axi + - if: + properties: + compatible: + contains: + const: fsl,imx8mq-pcie + then: + properties: + clock-names: + items: + - const: pcie + - const: pcie_bus + - const: pcie_phy + - const: pcie_aux + - if: + properties: + compatible: + not: + contains: + enum: + - fsl,imx6sx-pcie + - fsl,imx8mq-pcie + then: + properties: + clock-names: + items: + - const: pcie + - const: pcie_bus + - const: pcie_phy properties: compatible: @@ -25,6 +66,8 @@ properties: - fsl,imx6qp-pcie - fsl,imx7d-pcie - fsl,imx8mq-pcie + - fsl,imx8mm-pcie + - fsl,imx8mp-pcie reg: items: @@ -55,11 +98,7 @@ properties: clock-names: minItems: 3 - items: - - const: pcie - - const: pcie_bus - - const: pcie_phy - - const: pcie_inbound_axi for imx6sx-pcie, pcie_aux for imx8mq-pcie + maxItems: 4 num-lanes: const: 1 @@ -127,6 +166,12 @@ properties: enum: [1, 2, 3, 4] default: 1 + phys: + maxItems: 1 + + phy-names: + const: pcie-phy + reset-gpio: description: Should specify the GPIO for controlling the PCI bus device reset signal. It's not polarity aware and defaults to active-low reset diff --git a/Documentation/devicetree/bindings/pci/hisilicon,kirin-pcie.yaml b/Documentation/devicetree/bindings/pci/hisilicon,kirin-pcie.yaml index cbee87802559b..c9f04999c9cf7 100644 --- a/Documentation/devicetree/bindings/pci/hisilicon,kirin-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/hisilicon,kirin-pcie.yaml @@ -37,6 +37,19 @@ properties: minItems: 3 maxItems: 4 + clocks: true + + clock-names: + items: + - const: pcie_phy_ref + - const: pcie_aux + - const: pcie_apb_phy + - const: pcie_apb_sys + - const: pcie_aclk + + phys: + maxItems: 1 + hisilicon,clken-gpios: description: | Clock input enablement GPIOs from PCI devices like Ethernet, M.2 and diff --git a/Documentation/devicetree/bindings/pci/rockchip-dw-pcie.yaml b/Documentation/devicetree/bindings/pci/rockchip-dw-pcie.yaml new file mode 100644 index 0000000000000..2be72ae1169f9 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/rockchip-dw-pcie.yaml @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/rockchip-dw-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: DesignWare based PCIe controller on Rockchip SoCs + +maintainers: + - Shawn Lin + - Simon Xue + - Heiko Stuebner + +description: |+ + RK3568 SoC PCIe host controller is based on the Synopsys DesignWare + PCIe IP and thus inherits all the common properties defined in + snps,dw-pcie.yaml. + +allOf: + - $ref: /schemas/pci/snps,dw-pcie.yaml# + +properties: + compatible: + items: + - const: rockchip,rk3568-pcie + + reg: + items: + - description: Data Bus Interface (DBI) registers + - description: Rockchip designed configuration registers + - description: Config registers + + reg-names: + items: + - const: dbi + - const: apb + - const: config + + clocks: + items: + - description: AHB clock for PCIe master + - description: AHB clock for PCIe slave + - description: AHB clock for PCIe dbi + - description: APB clock for PCIe + - description: Auxiliary clock for PCIe + + clock-names: + items: + - const: aclk_mst + - const: aclk_slv + - const: aclk_dbi + - const: pclk + - const: aux + + msi-map: true + + num-lanes: true + + phys: + maxItems: 1 + + phy-names: + const: pcie-phy + + power-domains: + maxItems: 1 + + ranges: + maxItems: 2 + + resets: + maxItems: 1 + + reset-names: + const: pipe + + vpcie3v3-supply: true + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - msi-map + - num-lanes + - phys + - phy-names + - power-domains + - resets + - reset-names + +unevaluatedProperties: false + +examples: + - | + + bus { + #address-cells = <2>; + #size-cells = <2>; + + pcie3x2: pcie@fe280000 { + compatible = "rockchip,rk3568-pcie"; + reg = <0x3 0xc0800000 0x0 0x390000>, + <0x0 0xfe280000 0x0 0x10000>, + <0x3 0x80000000 0x0 0x100000>; + reg-names = "dbi", "apb", "config"; + bus-range = <0x20 0x2f>; + clocks = <&cru 143>, <&cru 144>, + <&cru 145>, <&cru 146>, + <&cru 147>; + clock-names = "aclk_mst", "aclk_slv", + "aclk_dbi", "pclk", + "aux"; + device_type = "pci"; + linux,pci-domain = <2>; + max-link-speed = <2>; + msi-map = <0x2000 &its 0x2000 0x1000>; + num-lanes = <2>; + phys = <&pcie30phy>; + phy-names = "pcie-phy"; + power-domains = <&power 15>; + ranges = <0x81000000 0x0 0x80800000 0x3 0x80800000 0x0 0x100000>, + <0x83000000 0x0 0x80900000 0x3 0x80900000 0x0 0x3f700000>; + resets = <&cru 193>; + reset-names = "pipe"; + #address-cells = <3>; + #size-cells = <2>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml b/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml index 2b9d1d6fc661c..392f0ab488c24 100644 --- a/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml @@ -32,6 +32,8 @@ properties: - const: config - const: mgmt + dma-coherent: true + num-lanes: const: 8 @@ -61,10 +63,8 @@ required: - num-lanes - interrupts - interrupt-names - - interrupt-parent - interrupt-map-mask - interrupt-map - - clock-names - clocks - resets - pwren-gpios @@ -104,7 +104,6 @@ examples: <0x0 0x0 0x0 0x2 &plic0 58>, <0x0 0x0 0x0 0x3 &plic0 59>, <0x0 0x0 0x0 0x4 &plic0 60>; - clock-names = "pcie_aux"; clocks = <&prci PRCI_CLK_PCIE_AUX>; resets = <&prci 4>; pwren-gpios = <&gpio 5 0>; diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml new file mode 100644 index 0000000000000..188e708f375df --- /dev/null +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml @@ -0,0 +1,327 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/snps,dw-pcie-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DWC PCIe RP/EP controller + +maintainers: + - Jingoo Han + - Gustavo Pimentel + +description: + Generic Synopsys DesignWare PCIe Root Port and Endpoint controller + properties. + +select: false + +properties: + reg: + description: + DWC PCIe CSR space is normally accessed over the dedicated Data Bus + Interface - DBI. In accordance with the reference manual the register + configuration space belongs to the Configuration-Dependent Module (CDM) + and is split up into several sub-parts Standard PCIe configuration + space, Port Logic Registers (PL), Shadow Config-space Registers, + iATU/eDMA registers. The particular sub-space is selected by the + CDM/ELBI (dbi_cs) and CS2 (dbi_cs2) signals (selector bits). Such + configuration provides a flexible interface for the system engineers to + either map the particular space at a desired MMIO address or just leave + them in a contiguous memory space if pure Native or AXI Bridge DBI access + is selected. Note the PCIe CFG-space, PL and Shadow registers are + specific for each activated function, while the rest of the sub-spaces + are common for all of them (if there are more than one). + minItems: 2 + maxItems: 6 + + reg-names: + minItems: 2 + maxItems: 6 + + interrupts: + description: + There are two main sub-blocks which are normally capable of + generating interrupts. It's System Information Interface and MSI + interface. While the former one has some common for the Host and + Endpoint controllers IRQ-signals, the later interface is obviously + Root Complex specific since it's responsible for the incoming MSI + messages signalling. The System Information IRQ signals are mainly + responsible for reporting the generic PCIe hierarchy and Root + Complex events like VPD IO request, general AER, PME, Hot-plug, link + bandwidth change, link equalization request, INTx asserted/deasserted + Message detection, embedded DMA Tx/Rx/Error. + minItems: 1 + maxItems: 26 + + interrupt-names: + minItems: 1 + maxItems: 26 + + clocks: + description: + DWC PCIe reference manual explicitly defines a set of the clocks required + to get the controller working correctly. In general all of them can + be divided into two groups':' application and core clocks. Note the + platforms may have some of the clock sources unspecified in case if the + corresponding domains are fed up from a common clock source. + minItems: 1 + maxItems: 7 + + clock-names: + minItems: 1 + maxItems: 7 + + resets: + description: + DWC PCIe reference manual explicitly defines a set of the reset + signals required to be de-asserted to properly activate the controller + sub-parts. All of these signals can be divided into two sub-groups':' + application and core resets with respect to the main sub-domains they + are supposed to reset. Note the platforms may have some of these signals + unspecified in case if they are automatically handled or aggregated into + a comprehensive control module. + minItems: 1 + maxItems: 10 + + reset-names: + minItems: 1 + maxItems: 10 + + phys: + description: + There can be up to the number of possible lanes PHYs specified placed in + the phandle array in the line-based order. Obviously each the specified + PHYs are supposed to be able to work in the PCIe mode with a speed + implied by the DWC PCIe controller they are attached to. + minItems: 1 + maxItems: 16 + + phy-names: + minItems: 1 + maxItems: 16 + oneOf: + - items: + pattern: '^pcie[0-9]+$' + - deprecated: true + items: + pattern: '^pcie(-?phy[0-9]*)?$' + + reset-gpio: + deprecated: true + description: + Reference to the GPIO-controlled PERST# signal. It is used to reset all + the peripheral devices available on the PCIe bus. + maxItems: 1 + + reset-gpios: + description: + Reference to the GPIO-controlled PERST# signal. It is used to reset all + the peripheral devices available on the PCIe bus. + maxItems: 1 + + max-link-speed: + maximum: 5 + + num-lanes: + description: + Number of PCIe link lanes to use. Can be omitted if the already brought + up link is supposed to be preserved. + maximum: 16 + + num-ob-windows: + $ref: /schemas/types.yaml#/definitions/uint32 + deprecated: true + description: + Number of outbound address translation windows. This parameter can be + auto-detected based on the iATU memory writability. So there is no + point in having a dedicated DT-property for it. + maximum: 256 + + num-ib-windows: + $ref: /schemas/types.yaml#/definitions/uint32 + deprecated: true + description: + Number of inbound address translation windows. In the same way as + for the outbound AT windows, this parameter can be auto-detected based + on the iATU memory writability. There is no point having a dedicated + DT-property for it either. + maximum: 256 + + num-viewport: + $ref: /schemas/types.yaml#/definitions/uint32 + deprecated: true + description: + Number of outbound view ports configured in hardware. It's the same as + the number of outbound AT windows. + maximum: 256 + + snps,enable-cdm-check: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable automatic checking of CDM (Configuration Dependent Module) + registers for data corruption. CDM registers include standard PCIe + configuration space registers, Port Logic registers, DMA and iATU + registers. This feature has been available since DWC PCIe v4.80a. + + dma-coherent: true + +additionalProperties: true + +definitions: + reg-names: + description: + CSR space names common for the DWC PCIe Root Port and Endpoint + controllers. + oneOf: + - description: + Basic DWC PCIe controller configuration-space accessible over + the DBI interface. This memory space is either activated with + CDM/ELBI = 0 and CS2 = 0 or is a contiguous memory region + with all spaces. Note iATU/eDMA CSRs are indirectly accessible + via the PL viewports on the DWC PCIe controllers older than + v4.80a. + const: dbi + - description: + Shadow DWC PCIe config-space registers. This space is selected + by setting CDM/ELBI = 0 and CS2 = 1. This is an intermix of + the PCI-SIG PCIe CFG-space with the shadow registers for some + PCI Header space, PCI Standard and Extended Structures. It's + mainly relevant for the end-point controller configuration, + but still there are some shadow registers available for the + Root Port mode too. + const: dbi2 + - description: + External Local Bus registers. It's an application-dependent + registers normally defined by the platform engineers. The space + can be selected by setting CDM/ELBI = 1 and CS2 = 0 wires or can + be accessed over some platform-specific means (for instance + as a part of a system controller). + enum: [ elbi, app ] + - description: + iATU/eDMA registers common for all device functions. It's an + unrolled memory space with the internal Address Translation + Unit and Enhanced DMA, which is selected by setting CDM/ELBI = 1 + and CS2 = 1. For IP-core releases prior v4.80a, these registers + have been programmed via an indirect addressing scheme using a + set of viewport CSRs mapped into the PL space. Note iATU is + normally mapped to the 0x0 address of this region, while eDMA + is available at 0x80000 base address. + const: atu + - description: + Platform-specific eDMA registers. Some platforms may have eDMA + CSRs mapped in a non-standard base address. The registers offset + can be changed or the MS/LS-bits of the address can be attached + in an additional RTL block before the MEM-IO transactions reach + the DW PCIe slave interface. + const: dma + - description: + PHY/PCS configuration registers. Some platforms can have the + PCS and PHY CSRs accessible over a dedicated memory mapped + region, but mainly these registers are indirectly accessible + either by means of the embedded PHY viewport schema or by some + platform-specific method. + const: phy + + interrupt-names: + description: + IRQ signal names common for the DWC PCIe Root Port and Endpoint + controllers. + oneOf: + - description: + Controller request to read or write virtual product data + from/to the VPD capability registers. + const: vpd + - description: + Link Equalization Request flag is set in the Link Status 2 + register (applicable if the corresponding IRQ is enabled in + the Link Control 3 register). + const: l_eq + - description: + Indicates that the eDMA Tx/Rx transfer is complete or that an + error has occurred on the corresponding channel. eDMA can have + eight Tx (Write) and Rx (Read) eDMA channels thus supporting up + to 16 IRQ signals all together. Write eDMA channels shall go + first in the ordered row as per default edma_int[*] bus setup. + pattern: '^dma([0-9]|1[0-5])?$' + - description: + PCIe protocol correctable error or a Data Path protection + correctable error is detected by the automotive/safety + feature. + const: sft_ce + - description: + Indicates that the internal safety mechanism detected and + uncorrectable error. + const: sft_ue + + clock-names: + description: + Reference clock names common for the DWC PCIe Root Port and Endpoint + controllers. + anyOf: + - description: + Data Bus Interface (DBI) clock. Clock signal for the AXI-bus + interface of the Configuration-Dependent Module, which is + basically the set of the controller CSRs. + const: dbi + - description: + Application AXI-bus Master interface clock. Basically this is + a clock for the controller DMA interface (PCI-to-CPU). + const: mstr + - description: + Application AXI-bus Slave interface clock. This is a clock for + the CPU-to-PCI memory IO interface. + const: slv + - description: + Controller Core-PCS PIPE interface clock. It's normally + supplied by an external PCS-PHY. + const: pipe + - description: + Controller Primary clock. It's assumed that all controller input + signals (except resets) are synchronous to this clock. + const: core + - description: + Auxiliary clock for the controller PMC domain. The controller + partitioning implies having some parts to operate with this + clock in some power management states. + const: aux + - description: + Generic reference clock. In case if there are several + interfaces fed up with a common clock source it's advisable to + define it with this name (for instance pipe, core and aux can + be connected to a single source of the periodic signal). + const: ref + - description: + Clock for the PHY registers interface. Originally this is + a PHY-viewport-based interface, but some platform may have + specifically designed one. + const: phy_reg + + reset-names: + description: + Reset signal names common for the DWC PCIe Root Port and Endpoint + controllers. + anyOf: + - description: Data Bus Interface (DBI) domain reset + const: dbi + - description: AXI-bus Master interface reset + const: mstr + - description: AXI-bus Slave interface reset + const: slv + - description: Controller Non-sticky CSR flags reset + const: non-sticky + - description: Controller sticky CSR flags reset + const: sticky + - description: PIPE-interface (Core-PCS) logic reset + const: pipe + - description: + Controller primary reset (resets everything except PMC module) + const: core + - description: PCS/PHY block reset + const: phy + - description: PMC hot reset signal + const: hot + - description: Cold reset signal + const: pwr + +... diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml index b5935b1b153f2..e2da5339ce8b3 100644 --- a/Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml @@ -13,78 +13,137 @@ maintainers: description: | Synopsys DesignWare PCIe host controller endpoint +# Please create a separate DT-schema for your DWC PCIe Endpoint controller +# and make sure it's assigned with the vendor-specific compatible string. +select: + properties: + compatible: + const: snps,dw-pcie-ep + required: + - compatible + allOf: - $ref: /schemas/pci/pci-ep.yaml# + - $ref: /schemas/pci/snps,dw-pcie-common.yaml# properties: - compatible: - anyOf: - - {} - - const: snps,dw-pcie-ep - reg: - description: | - It should contain Data Bus Interface (dbi) and config registers for all - versions. - For designware core version >= 4.80, it may contain ATU address space. + description: + DBI, DBI2 reg-spaces and outbound memory window are required for the + normal controller functioning. iATU memory IO region is also required + if the space is unrolled (IP-core version >= 4.80a). minItems: 2 - maxItems: 4 + maxItems: 5 reg-names: minItems: 2 - maxItems: 4 + maxItems: 5 + items: + oneOf: + - $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/reg-names + - $ref: '#/definitions/reg-names' + - deprecated: true + oneOf: + - description: See native 'elbi/app' CSR region for details. + enum: [ link ] + allOf: + - contains: + const: dbi + - contains: + const: addr_space + + interrupts: + description: + There is no mandatory IRQ signals for the normal controller functioning, + but in addition to the native set the platforms may have a link- or + PM-related IRQs specified. + minItems: 1 + maxItems: 20 + + interrupt-names: + minItems: 1 + maxItems: 20 + items: + $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/interrupt-names + + clocks: + minItems: 1 + maxItems: 7 + + clock-names: + minItems: 1 + maxItems: 7 items: - enum: [dbi, dbi2, config, atu, addr_space, link, atu_dma, appl] - - reset-gpio: - description: GPIO pin number of PERST# signal - maxItems: 1 - deprecated: true - - reset-gpios: - description: GPIO controlled connection to PERST# signal - maxItems: 1 - - snps,enable-cdm-check: - type: boolean - description: | - This is a boolean property and if present enables - automatic checking of CDM (Configuration Dependent Module) registers - for data corruption. CDM registers include standard PCIe configuration - space registers, Port Logic registers, DMA and iATU (internal Address - Translation Unit) registers. - - num-ib-windows: - description: number of inbound address translation windows - maxItems: 1 - deprecated: true - - num-ob-windows: - description: number of outbound address translation windows - maxItems: 1 - deprecated: true + oneOf: + - $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/clock-names + - deprecated: true + oneOf: + - description: See native 'ref' clock for details. + enum: [ gio ] + - description: See native 'pipe' clock for details + enum: [ link ] + + resets: + minItems: 1 + maxItems: 10 + + reset-names: + minItems: 1 + maxItems: 10 + items: + oneOf: + - $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/reset-names + - deprecated: true + oneOf: + - description: See native 'core' reset for details + enum: [ gio ] + - description: See native 'phy' reset for details + enum: [ link ] max-functions: - $ref: /schemas/types.yaml#/definitions/uint32 - description: maximum number of functions that can be configured + maximum: 32 required: + - compatible - reg - reg-names - - compatible -unevaluatedProperties: false +additionalProperties: true + +definitions: + reg-names: + description: + DWC PCIe Endpoint specific CSR space names. + oneOf: + - description: + Outbound iATU-capable memory-region which will be used to + generate various application-specific traffic on the PCIe bus + hierarchy. It's usage scenario depends on the endpoint + functionality, for instance it can be used to create MSI(X) + messages. + const: addr_space examples: - | - bus { - #address-cells = <1>; - #size-cells = <1>; - pcie-ep@dfd00000 { - compatible = "snps,dw-pcie-ep"; - reg = <0xdfc00000 0x0001000>, /* IP registers 1 */ - <0xdfc01000 0x0001000>, /* IP registers 2 */ - <0xd0000000 0x2000000>; /* Configuration space */ - reg-names = "dbi", "dbi2", "addr_space"; - }; + pcie-ep@dfd00000 { + compatible = "snps,dw-pcie-ep"; + reg = <0xdfc00000 0x0001000>, /* IP registers 1 */ + <0xdfc01000 0x0001000>, /* IP registers 2 */ + <0xd0000000 0x2000000>; /* Configuration space */ + reg-names = "dbi", "dbi2", "addr_space"; + + interrupts = <23>, <24>; + interrupt-names = "dma0", "dma1"; + + clocks = <&sys_clk 12>, <&sys_clk 24>; + clock-names = "dbi", "ref"; + + resets = <&sys_rst 12>, <&sys_rst 24>; + reset-names = "dbi", "phy"; + + phys = <&pcie_phy0>, <&pcie_phy1>, <&pcie_phy2>, <&pcie_phy3>; + phy-names = "pcie0", "pcie1", "pcie2", "pcie3"; + + max-link-speed = <3>; + max-functions = /bits/ 8 <4>; }; diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml index 9ed0dfba7f89c..0fafaf60f6cd3 100644 --- a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml @@ -13,20 +13,25 @@ maintainers: description: | Synopsys DesignWare PCIe host controller +# Please create a separate DT-schema for your DWC PCIe Root Port controller +# and make sure it's assigned with the vendor-specific compatible string. +select: + properties: + compatible: + const: snps,dw-pcie + required: + - compatible + allOf: - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/pci/snps,dw-pcie-common.yaml# properties: - compatible: - anyOf: - - {} - - const: snps,dw-pcie - reg: - description: | - It should contain Data Bus Interface (dbi) and config registers for all - versions. - For designware core version >= 4.80, it may contain ATU address space. + description: + At least DBI reg-space and peripheral devices CFG-space outbound window + are required for the normal controller work. iATU memory IO region is + also required if the space is unrolled (IP-core version >= 4.80a). minItems: 2 maxItems: 5 @@ -34,69 +39,166 @@ properties: minItems: 2 maxItems: 5 items: - enum: [ dbi, dbi2, config, atu, app, elbi, mgmt, ctrl, parf, cfg, link, - ulreg, smu, mpu, apb, phy ] - - num-lanes: - description: | - number of lanes to use (this property should be specified unless - the link is brought already up in firmware) - maximum: 16 - - reset-gpio: - description: GPIO pin number of PERST# signal - maxItems: 1 - deprecated: true - - reset-gpios: - description: GPIO controlled connection to PERST# signal - maxItems: 1 - - interrupts: true - - interrupt-names: true - - clocks: true - - snps,enable-cdm-check: - type: boolean - description: | - This is a boolean property and if present enables - automatic checking of CDM (Configuration Dependent Module) registers - for data corruption. CDM registers include standard PCIe configuration - space registers, Port Logic registers, DMA and iATU (internal Address - Translation Unit) registers. - - num-viewport: - description: | - number of view ports configured in hardware. If a platform - does not specify it, the driver autodetects it. - deprecated: true - -unevaluatedProperties: false + oneOf: + - $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/reg-names + - $ref: '#/definitions/reg-names' + - deprecated: true + oneOf: + - description: See native 'elbi/app' CSR region for details. + enum: [ apb, mgmt, link, ulreg ] + - description: Syscon-related CSR regions. + enum: [ smu, mpu ] + allOf: + - contains: + const: dbi + - contains: + const: config + + interrupts: + description: + At least MSI interrupt signal is supposed to be specified for + the DWC PCIe host controller. + minItems: 1 + maxItems: 26 + + interrupt-names: + minItems: 1 + maxItems: 26 + items: + oneOf: + - $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/interrupt-names + - $ref: '#/definitions/interrupt-names' + - deprecated: true + oneOf: + - description: See native "msi" IRQ for details + enum: [ intr ] + allOf: + - contains: + const: msi + + clocks: + minItems: 1 + maxItems: 7 + + clock-names: + minItems: 1 + maxItems: 7 + items: + oneOf: + - $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/clock-names + - deprecated: true + oneOf: + - description: See native 'dbi' clock for details + enum: [ pcie, pcie_apb_sys, aclk_dbi ] + - description: See native 'mstr/slv' clock for details + enum: [ pcie_bus, pcie_inbound_axi, pcie_aclk, aclk_mst, aclk_slv ] + - description: See native 'pipe' clock for details + enum: [ pcie_phy, pcie_phy_ref ] + - description: See native 'aux' clock for details + enum: [ pcie_aux ] + - description: See nativs 'phy_reg' clock for details + enum: [ pcie_apb_phy, pclk ] + + resets: + minItems: 1 + maxItems: 10 + + reset-names: + minItems: 1 + maxItems: 10 + items: + oneOf: + - $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/reset-names + - deprecated: true + oneOf: + - description: See native 'core' reset for details + enum: [ apps ] + - description: See native 'phy' reset for details + enum: [ pciephy ] + - description: See native 'pwr' reset for details + enum: [ turnoff ] + +additionalProperties: true required: + - compatible - reg - reg-names - - compatible + +definitions: + reg-names: + description: + DWC PCIe Root Port/Complex specific CSR space names. + oneOf: + - description: + Outbound iATU-capable memory-region which will be used to access + the peripheral PCIe devices configuration space. + const: config + + interrupt-names: + description: + DWC PCIe Root Port/Complex specific IRQ signal names. + oneOf: + - description: + DSP AXI MSI Interrupt detected. It gets de-asserted when there is + no more MSI interrupt pending. The interrupt is relevant to the + iMSI-RX - Integrated MSI Receiver (AXI bridge). + const: msi + - description: + Legacy A/B/C/D interrupt signal. Basically it's triggered by + receiving a Assert_INT{A,B,C,D}/Desassert_INT{A,B,C,D} message + from the downstream device. + pattern: "^int(a|b|c|d)$" + - description: + Error condition detected and a bit is set in the Root Error Status + register of the AER capability. It's asserted when the RC + internally generated an error or an error message is received by + the RC. + const: aer + - description: + PME message is received by the port. That means having the PME + status bit set in the Root Status register (the event is + supposed to be unmasked in the Root Control register). + const: pme + - description: + Hot-plug event is detected. That is a bit has been set in the + Slot Status register and the corresponding event is enabled in + the Slot Control register. + const: hp + - description: + Link Autonomous Bandwidth Status flag has been set in the Link + Status register (the event is supposed to be unmasked in the + Link Control register). + const: bw_au + - description: + Bandwidth Management Status flag has been set in the Link + Status register (the event is supposed to be unmasked in the + Link Control register). + const: bw_mg examples: - | - bus { - #address-cells = <1>; - #size-cells = <1>; - pcie@dfc00000 { - device_type = "pci"; - compatible = "snps,dw-pcie"; - reg = <0xdfc00000 0x0001000>, /* IP registers */ - <0xd0000000 0x0002000>; /* Configuration space */ - reg-names = "dbi", "config"; - #address-cells = <3>; - #size-cells = <2>; - ranges = <0x81000000 0 0x00000000 0xde000000 0 0x00010000>, - <0x82000000 0 0xd0400000 0xd0400000 0 0x0d000000>; - interrupts = <25>, <24>; - #interrupt-cells = <1>; - num-lanes = <1>; - }; + pcie@dfc00000 { + compatible = "snps,dw-pcie"; + device_type = "pci"; + reg = <0xdfc00000 0x0001000>, /* IP registers */ + <0xd0000000 0x0002000>; /* Configuration space */ + reg-names = "dbi", "config"; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x81000000 0 0x00000000 0xde000000 0 0x00010000>, + <0x82000000 0 0xd0400000 0xd0400000 0 0x0d000000>; + bus-range = <0x0 0xff>; + + interrupts = <25>, <24>; + interrupt-names = "msi", "hp"; + #interrupt-cells = <1>; + + reset-gpios = <&port0 0 1>; + + phys = <&pcie_phy>; + phy-names = "pcie"; + + num-lanes = <1>; + max-link-speed = <3>; }; diff --git a/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml index 144cbcd60a1c3..437e61618d068 100644 --- a/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml +++ b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml @@ -20,7 +20,9 @@ allOf: properties: compatible: - const: socionext,uniphier-pro5-pcie-ep + enum: + - socionext,uniphier-pro5-pcie-ep + - socionext,uniphier-nx1-pcie-ep reg: minItems: 4 @@ -41,20 +43,26 @@ properties: - const: atu clocks: + minItems: 1 maxItems: 2 clock-names: - items: - - const: gio - - const: link + oneOf: + - items: # for Pro5 + - const: gio + - const: link + - const: link # for NX1 resets: + minItems: 1 maxItems: 2 reset-names: - items: - - const: gio - - const: link + oneOf: + - items: # for Pro5 + - const: gio + - const: link + - const: link # for NX1 num-ib-windows: const: 16 @@ -79,7 +87,7 @@ required: - resets - reset-names -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml new file mode 100644 index 0000000000000..638b99db04332 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/socionext,uniphier-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Socionext UniPhier PCIe host controller + +description: | + UniPhier PCIe host controller is based on the Synopsys DesignWare + PCI core. It shares common features with the PCIe DesignWare core and + inherits common properties defined in + Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml. + +maintainers: + - Kunihiko Hayashi + +allOf: + - $ref: /schemas/pci/snps,dw-pcie.yaml# + +properties: + compatible: + enum: + - socionext,uniphier-pcie + + reg: + minItems: 3 + maxItems: 4 + + reg-names: + minItems: 3 + items: + - const: dbi + - const: link + - const: config + - const: atu + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + num-viewport: true + + num-lanes: true + + phys: + maxItems: 1 + + phy-names: + const: pcie-phy + + interrupt-controller: + type: object + additionalProperties: false + + properties: + interrupt-controller: true + + '#interrupt-cells': + const: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - clocks + - resets + +unevaluatedProperties: false + +examples: + - | + bus { + gic: interrupt-controller { + interrupt-controller; + #interrupt-cells = <3>; + }; + }; + + pcie: pcie@66000000 { + compatible = "socionext,uniphier-pcie"; + reg-names = "dbi", "link", "config"; + reg = <0x66000000 0x1000>, <0x66010000 0x10000>, <0x2fff0000 0x10000>; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&sys_clk 24>; + resets = <&sys_rst 24>; + num-lanes = <1>; + num-viewport = <1>; + bus-range = <0x0 0xff>; + device_type = "pci"; + ranges = <0x81000000 0 0x00000000 0x2ffe0000 0 0x00010000>, + <0x82000000 0 0x00000000 0x20000000 0 0x0ffe0000>; + phy-names = "pcie-phy"; + phys = <&pcie_phy>; + #interrupt-cells = <1>; + interrupt-names = "dma", "msi"; + interrupt-parent = <&gic>; + interrupts = <0 224 4>, <0 225 4>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + + pcie_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic>; + interrupts = <0 226 4>; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/toshiba,visconti-pcie.yaml b/Documentation/devicetree/bindings/pci/toshiba,visconti-pcie.yaml index 30b6396d83c83..aea0e2bcdd778 100644 --- a/Documentation/devicetree/bindings/pci/toshiba,visconti-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/toshiba,visconti-pcie.yaml @@ -36,7 +36,7 @@ properties: - const: mpu interrupts: - maxItems: 1 + maxItems: 2 clocks: items: @@ -94,8 +94,9 @@ examples: #interrupt-cells = <1>; ranges = <0x81000000 0 0x40000000 0 0x40000000 0 0x00010000>, <0x82000000 0 0x50000000 0 0x50000000 0 0x20000000>; - interrupts = ; - interrupt-names = "intr"; + interrupts = , + ; + interrupt-names = "msi", "intr"; interrupt-map-mask = <0 0 0 7>; interrupt-map = <0 0 0 1 &gic GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH diff --git a/Documentation/devicetree/bindings/pci/uniphier-pcie.txt b/Documentation/devicetree/bindings/pci/uniphier-pcie.txt deleted file mode 100644 index 359585db049f8..0000000000000 --- a/Documentation/devicetree/bindings/pci/uniphier-pcie.txt +++ /dev/null @@ -1,82 +0,0 @@ -Socionext UniPhier PCIe host controller bindings - -This describes the devicetree bindings for PCIe host controller implemented -on Socionext UniPhier SoCs. - -UniPhier PCIe host controller is based on the Synopsys DesignWare PCI core. -It shares common functions with the PCIe DesignWare core driver and inherits -common properties defined in -Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml. - -Required properties: -- compatible: Should be "socionext,uniphier-pcie". -- reg: Specifies offset and length of the register set for the device. - According to the reg-names, appropriate register sets are required. -- reg-names: Must include the following entries: - "dbi" - controller configuration registers - "link" - SoC-specific glue layer registers - "config" - PCIe configuration space - "atu" - iATU registers for DWC version 4.80 or later -- clocks: A phandle to the clock gate for PCIe glue layer including - the host controller. -- resets: A phandle to the reset line for PCIe glue layer including - the host controller. -- interrupts: A list of interrupt specifiers. According to the - interrupt-names, appropriate interrupts are required. -- interrupt-names: Must include the following entries: - "dma" - DMA interrupt - "msi" - MSI interrupt - -Optional properties: -- phys: A phandle to generic PCIe PHY. According to the phy-names, appropriate - phys are required. -- phy-names: Must be "pcie-phy". - -Required sub-node: -- legacy-interrupt-controller: Specifies interrupt controller for legacy PCI - interrupts. - -Required properties for legacy-interrupt-controller: -- interrupt-controller: identifies the node as an interrupt controller. -- #interrupt-cells: specifies the number of cells needed to encode an - interrupt source. The value must be 1. -- interrupt-parent: Phandle to the parent interrupt controller. -- interrupts: An interrupt specifier for legacy interrupt. - -Example: - - pcie: pcie@66000000 { - compatible = "socionext,uniphier-pcie", "snps,dw-pcie"; - status = "disabled"; - reg-names = "dbi", "link", "config"; - reg = <0x66000000 0x1000>, <0x66010000 0x10000>, - <0x2fff0000 0x10000>; - #address-cells = <3>; - #size-cells = <2>; - clocks = <&sys_clk 24>; - resets = <&sys_rst 24>; - num-lanes = <1>; - num-viewport = <1>; - bus-range = <0x0 0xff>; - device_type = "pci"; - ranges = - /* downstream I/O */ - <0x81000000 0 0x00000000 0x2ffe0000 0 0x00010000 - /* non-prefetchable memory */ - 0x82000000 0 0x00000000 0x20000000 0 0x0ffe0000>; - #interrupt-cells = <1>; - interrupt-names = "dma", "msi"; - interrupts = <0 224 4>, <0 225 4>; - interrupt-map-mask = <0 0 0 7>; - interrupt-map = <0 0 0 1 &pcie_intc 0>, /* INTA */ - <0 0 0 2 &pcie_intc 1>, /* INTB */ - <0 0 0 3 &pcie_intc 2>, /* INTC */ - <0 0 0 4 &pcie_intc 3>; /* INTD */ - - pcie_intc: legacy-interrupt-controller { - interrupt-controller; - #interrupt-cells = <1>; - interrupt-parent = <&gic>; - interrupts = <0 226 4>; - }; - }; diff --git a/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml b/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml index a2bbc0eb7220e..32f4641085bc0 100644 --- a/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml +++ b/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml @@ -55,7 +55,6 @@ required: - reg-names - "#interrupt-cells" - interrupts - - interrupt-parent - interrupt-map - interrupt-map-mask - bus-range diff --git a/Documentation/devicetree/bindings/pinctrl/canaan,k210-fpioa.yaml b/Documentation/devicetree/bindings/pinctrl/canaan,k210-fpioa.yaml index 46fbc73ab26b5..1c5ec67496fd5 100644 --- a/Documentation/devicetree/bindings/pinctrl/canaan,k210-fpioa.yaml +++ b/Documentation/devicetree/bindings/pinctrl/canaan,k210-fpioa.yaml @@ -116,6 +116,7 @@ patternProperties: input-schmitt-disable: true input-polarity-invert: + type: boolean description: Enable or disable pin input polarity inversion. @@ -128,6 +129,7 @@ patternProperties: output-low: true output-polarity-invert: + type: boolean description: Enable or disable pin output polarity inversion. diff --git a/Documentation/devicetree/bindings/power/supply/battery.yaml b/Documentation/devicetree/bindings/power/supply/battery.yaml index d56ac484fec50..491488e7b9703 100644 --- a/Documentation/devicetree/bindings/power/supply/battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/battery.yaml @@ -85,8 +85,13 @@ properties: description: battery factory internal resistance resistance-temp-table: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: the temperature in degree Celsius + - description: battery internal resistance percent description: | - An array providing the temperature in degree Celsius + A table providing the temperature in degree Celsius and corresponding battery internal resistance percent, which is used to look up the resistance percent according to current temperature to get an accurate batterty internal resistance in different temperatures. diff --git a/Documentation/devicetree/bindings/power/supply/charger-manager.yaml b/Documentation/devicetree/bindings/power/supply/charger-manager.yaml index c863cfa67865f..fbb2204769aa8 100644 --- a/Documentation/devicetree/bindings/power/supply/charger-manager.yaml +++ b/Documentation/devicetree/bindings/power/supply/charger-manager.yaml @@ -36,6 +36,7 @@ properties: cm-poll-mode: description: polling mode + $ref: /schemas/types.yaml#/definitions/uint32 default: 0 enum: - 0 # disabled diff --git a/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.yaml b/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.yaml index a50c34d5d199a..765d9f9edd6ef 100644 --- a/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.yaml +++ b/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Samsung Exynos SoC True Random Number Generator maintainers: - - Krzysztof Kozlowski + - Krzysztof Kozlowski - Łukasz Stelmach properties: diff --git a/Documentation/devicetree/bindings/rng/st,stm32-rng.yaml b/Documentation/devicetree/bindings/rng/st,stm32-rng.yaml index 82bb2e97e889f..abb14976d4be6 100644 --- a/Documentation/devicetree/bindings/rng/st,stm32-rng.yaml +++ b/Documentation/devicetree/bindings/rng/st,stm32-rng.yaml @@ -27,6 +27,7 @@ properties: maxItems: 1 clock-error-detect: + type: boolean description: If set enable the clock detection management required: diff --git a/Documentation/devicetree/bindings/serial/8250.yaml b/Documentation/devicetree/bindings/serial/8250.yaml index fa767440f2816..8c703c894ff55 100644 --- a/Documentation/devicetree/bindings/serial/8250.yaml +++ b/Documentation/devicetree/bindings/serial/8250.yaml @@ -137,6 +137,7 @@ properties: description: The current active speed of the UART. reg-offset: + $ref: /schemas/types.yaml#/definitions/uint32 description: | Offset to apply to the mapbase from the start of the registers. diff --git a/Documentation/devicetree/bindings/sound/imx-audio-hdmi.yaml b/Documentation/devicetree/bindings/sound/imx-audio-hdmi.yaml index d5474f83ac2ca..e7e7bb65c366f 100644 --- a/Documentation/devicetree/bindings/sound/imx-audio-hdmi.yaml +++ b/Documentation/devicetree/bindings/sound/imx-audio-hdmi.yaml @@ -20,9 +20,11 @@ properties: description: User specified audio sound card name audio-cpu: + $ref: /schemas/types.yaml#/definitions/phandle description: The phandle of an CPU DAI controller hdmi-out: + type: boolean description: | This is a boolean property. If present, the transmitting function of HDMI will be enabled, indicating there's a physical HDMI out @@ -30,6 +32,7 @@ properties: block, such as an HDMI encoder or display-controller. hdmi-in: + type: boolean description: | This is a boolean property. If present, the receiving function of HDMI will be enabled, indicating there is a physical HDMI in diff --git a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml index 3eda5049d183b..eedcc045304f8 100644 --- a/Documentation/devicetree/bindings/sram/sram.yaml +++ b/Documentation/devicetree/bindings/sram/sram.yaml @@ -20,6 +20,32 @@ description: |+ reflect the purpose of the node. Unit address (@
) should be appended to the name. +allOf: + - $ref: /schemas/mux/mux-consumer.yaml# + - if: + properties: + compatible: + contains: + const: baikal,bt1-sram + then: + properties: + mux-controls: + maxItems: 1 + required: + - mux-controls + - if: + properties: + compatible: + not: + contains: + enum: + - rockchip,rk3288-pmu-sram + then: + required: + - "#address-cells" + - "#size-cells" + - ranges + properties: $nodename: pattern: "^sram(@.*)?" @@ -32,6 +58,7 @@ properties: - arm,juno-sram-ns - atmel,sama5d2-securam - rockchip,rk3288-pmu-sram + - baikal,bt1-sram reg: maxItems: 1 @@ -42,6 +69,14 @@ 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 reset the SRAM core. + + reset-names: true + "#address-cells": const: 1 @@ -90,6 +125,7 @@ patternProperties: - samsung,exynos4210-sysram - samsung,exynos4210-sysram-ns - socionext,milbeaut-smp-sram + - baikal,bt1-boot-sram reg: description: @@ -131,19 +167,7 @@ required: - compatible - reg -if: - properties: - compatible: - contains: - const: rockchip,rk3288-pmu-sram - -else: - required: - - "#address-cells" - - "#size-cells" - - ranges - -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/MAINTAINERS b/MAINTAINERS index edc32575828b5..845d5d60bb84d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3222,6 +3222,13 @@ F: drivers/video/backlight/ F: include/linux/backlight.h F: include/linux/pwm_backlight.h +BAIKAL-T1 PVT HARDWARE MONITOR DRIVER +M: Serge Semin +L: linux-hwmon@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml +F: drivers/hwmon/bt1-pvt.* + BATMAN ADVANCED M: Marek Lindner M: Simon Wunderlich @@ -10639,6 +10646,15 @@ F: drivers/ata/ahci_platform.c F: drivers/ata/libahci_platform.c F: include/linux/ahci_platform.h +LIBATA SATA AHCI SYNOPSYS DWC CONTROLLER DRIVER +M: Serge Semin +L: linux-ide@vger.kernel.org +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata.git +F: Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml +F: Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml +F: drivers/ata/ahci_dwc.c + LIBATA SATA PROMISE TX2/TX4 CONTROLLER DRIVER M: Mikael Pettersson L: linux-ide@vger.kernel.org @@ -12545,6 +12561,21 @@ F: Documentation/mips/ F: arch/mips/ F: drivers/platform/mips/ +MIPS/BAIKAL-T1 PLATFORM +M: Serge Semin +L: linux-mips@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/bus/baikal,bt1-* +F: Documentation/devicetree/bindings/clock/baikal,bt1-* +F: Documentation/devicetree/bindings/mfd/baikal,bt1-* +F: Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml +F: arch/mips/baikal-t1/ +F: arch/mips/boot/dts/baikal-t1/ +F: arch/mips/include/asm/mach-baikal-t1/ +F: drivers/clk/baikal-t1/ +F: drivers/bus/bt1-* +F: drivers/memory/bt1-l2-ctl.c + MIPS BOSTON DEVELOPMENT BOARD M: Paul Burton L: linux-mips@vger.kernel.org @@ -13363,9 +13394,10 @@ F: tools/testing/selftests/ntb/ NTB IDT DRIVER M: Serge Semin -L: linux-ntb@googlegroups.com -S: Supported +L: ntb@lists.linux.dev +S: Maintained F: drivers/ntb/hw/idt/ +F: drivers/misc/eeprom/idt_89hpesx.c NTB INTEL DRIVER M: Dave Jiang @@ -14559,6 +14591,13 @@ S: Maintained F: Documentation/devicetree/bindings/pci/axis,artpec* F: drivers/pci/controller/dwc/*artpec* +PCIE DRIVER FOR BAIKAL-T1 +M: Serge Semin +L: linux-pci@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml +F: drivers/pci/controller/dwc/pcie-bt1.c + PCIE DRIVER FOR CAVIUM THUNDERX M: Robert Richter L: linux-pci@vger.kernel.org @@ -14636,7 +14675,7 @@ PCIE DRIVER FOR SOCIONEXT UNIPHIER M: Kunihiko Hayashi L: linux-pci@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/pci/uniphier-pcie* +F: Documentation/devicetree/bindings/pci/socionext,uniphier-pcie* F: drivers/pci/controller/dwc/pcie-uniphier* PCIE DRIVER FOR ST SPEAR13XX diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms index 6e3f36c841e5d..f9d215205955b 100644 --- a/arch/mips/Kbuild.platforms +++ b/arch/mips/Kbuild.platforms @@ -5,6 +5,7 @@ platform-$(CONFIG_MIPS_ALCHEMY) += alchemy/ platform-$(CONFIG_AR7) += ar7/ platform-$(CONFIG_ATH25) += ath25/ platform-$(CONFIG_ATH79) += ath79/ +platform-$(CONFIG_MIPS_BAIKAL_T1) += baikal-t1/ platform-$(CONFIG_BCM47XX) += bcm47xx/ platform-$(CONFIG_BCM63XX) += bcm63xx/ platform-$(CONFIG_BMIPS_GENERIC) += bmips/ diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 393eb2133243f..b4d7615770702 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -518,6 +518,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 @@ -1056,6 +1104,7 @@ endchoice source "arch/mips/alchemy/Kconfig" source "arch/mips/ath25/Kconfig" source "arch/mips/ath79/Kconfig" +source "arch/mips/baikal-t1/Kconfig" source "arch/mips/bcm47xx/Kconfig" source "arch/mips/bcm63xx/Kconfig" source "arch/mips/bmips/Kconfig" @@ -2084,6 +2133,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 # 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/baikal-t1/Kconfig b/arch/mips/baikal-t1/Kconfig new file mode 100644 index 0000000000000..92327c1ac183e --- /dev/null +++ b/arch/mips/baikal-t1/Kconfig @@ -0,0 +1,144 @@ +# 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 a generic board. It just activates all + the Baikal-T1 SoC peripherals. So all the run-time detectable devices + will work out-of-box while undetectable platform devices will be left + untouched. + + config BT1_DTB_BFK + bool "Baikal Electronics BFK" + help + This option provides a dtb for the Baikal Electronics BFK boards. + It's a Baikal-T1 SoC evaluation board specifically designed for + the SoC-based software prototyping. + + config BT1_DTB_EM406 + bool "Edelweiss EM406" + help + This option provides a dtb for a 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 a 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 a Edelweiss TF306 mini-ITX board. + It is based on the Baikal-T1 SoC, designed to be of mITX form-factor + with DDR3 SoDIMM plug, 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/baikal-t1/Makefile b/arch/mips/baikal-t1/Makefile new file mode 100644 index 0000000000000..9052fa7ca6f6d --- /dev/null +++ b/arch/mips/baikal-t1/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/baikal-t1/Platform b/arch/mips/baikal-t1/Platform new file mode 100644 index 0000000000000..089969fa8743f --- /dev/null +++ b/arch/mips/baikal-t1/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) += baikal-t1/ +cflags-$(CONFIG_MIPS_BAIKAL_T1) += -I$(srctree)/arch/mips/include/asm/mach-baikal-t1 +ifdef CONFIG_KVM_GUEST + load-$(CONFIG_MIPS_BAIKAL_T1) += 0x0000000040100000 + zload-$(CONFIG_MIPS_BAIKAL_T1) += 0xffffffff45100000 +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/baikal-t1/early_printk.c b/arch/mips/baikal-t1/early_printk.c new file mode 100644 index 0000000000000..21cc2c7276837 --- /dev/null +++ b/arch/mips/baikal-t1/early_printk.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Authors: + * Alexey Malahov + * Serge Semin + * + * Baikal-T1 early printk + */ +#include +#include +#include + +#include + +#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/baikal-t1/init.c b/arch/mips/baikal-t1/init.c new file mode 100644 index 0000000000000..85881c8220d74 --- /dev/null +++ b/arch/mips/baikal-t1/init.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Authors: + * Alexey Malahov + * Serge Semin + * + * Baikal-T1 platform initialization + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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, UHI and built-in 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) { + fdt = phys_to_virt(fw_arg3); + str = "Legacy position"; + } + + if (!fdt || fdt_check_header(fdt)) + panic("No valid dtb found. Can't continue."); + + pr_info("%s DTB found at %p\n", str, fdt); + + 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); + +#ifdef CONFIG_HIGHMEM + memblock_add(BT1_HIMEM_BASE, BT1_HIMEM_SIZE); +#endif + +#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) +{ + unsigned long swiotlb_nslabs; + size_t swiotlb_size; + phys_addr_t top; + void *swiotlb; + int ret; + + /* + * 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_size = swiotlb_size_or_default(); + swiotlb_nslabs = swiotlb_size >> IO_TLB_SHIFT; + + swiotlb = memblock_alloc_low(swiotlb_size, PAGE_SIZE); + if (!swiotlb) { + panic("Failed to allocate %zu bytes (align=%lx) for SWIOTLB", + swiotlb_size, PAGE_SIZE); + } + + ret = swiotlb_init_with_tbl(swiotlb, swiotlb_nslabs, 1); + if (ret) + panic("Failed to init the SWIOTLB table"); +} + +#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); + +int __uncached_access(struct file *file, unsigned long addr) +{ + if (file->f_flags & O_DSYNC) + return 1; + + return addr >= __pa(high_memory) || + ((addr >= BT1_MMIO_START) && (addr < BT1_MMIO_END)); +} + +#ifdef CONFIG_CPU_SUPPORTS_UNCACHED_ACCELERATED + +static phys_addr_t uca_start, uca_end; + +pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, + unsigned long size, pgprot_t vma_prot) +{ + phys_addr_t offset = PFN_PHYS(pfn), end = offset + size; + + if (__uncached_access(file, offset)) { + if (uca_start && (offset >= uca_start) && + (end <= uca_end)) + return __pgprot((pgprot_val(vma_prot) & + ~_CACHE_MASK) | + _CACHE_UNCACHED_ACCELERATED); + else + return pgprot_noncached(vma_prot); + } + return vma_prot; +} + +int mips_set_uca_range(phys_addr_t start, phys_addr_t end) +{ + if (end <= start || end <= BT1_MMIO_START) + return -EINVAL; + + uca_start = start; + uca_end = end; + return 0; +} + +#endif /* !CONFIG_CPU_SUPPORTS_UNCACHED_ACCELERATED */ diff --git a/arch/mips/baikal-t1/irq.c b/arch/mips/baikal-t1/irq.c new file mode 100644 index 0000000000000..d38e6a44c5f26 --- /dev/null +++ b/arch/mips/baikal-t1/irq.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 IRQ initialization + */ +#include + +#include +#include +#include +#include + +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/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile index 705b9e7f8035a..9724661476464 100644 --- a/arch/mips/boot/compressed/Makefile +++ b/arch/mips/boot/compressed/Makefile @@ -47,6 +47,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..ef555577bac42 --- /dev/null +++ b/arch/mips/boot/compressed/uart-bt1.c @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "../../baikal-t1/early_printk.c" diff --git a/arch/mips/boot/dts/Makefile b/arch/mips/boot/dts/Makefile index be96d35eb5821..4dd863189ab15 100644 --- a/arch/mips/boot/dts/Makefile +++ b/arch/mips/boot/dts/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +subdir-$(CONFIG_MIPS_BAIKAL_T1) += baikal-t1 subdir-$(CONFIG_BMIPS_GENERIC) += brcm subdir-$(CONFIG_CAVIUM_OCTEON_SOC) += cavium-octeon subdir-$(CONFIG_FIT_IMAGE_FDT_MARDUK) += img diff --git a/arch/mips/boot/dts/baikal-t1/Makefile b/arch/mips/boot/dts/baikal-t1/Makefile new file mode 100644 index 0000000000000..edab199bd7b41 --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2022 BAIKAL ELECTRONICS, JSC +# +# Baikal-T1 dtb makefile +# +dtb-$(CONFIG_BT1_DTB_ALL) += generic.dtb bfk3.dtb xa1-em406.dtb cb2-em406.dtb \ + xa1-em416.dtb tf306.dtb +dtb-$(CONFIG_BT1_DTB_GENERIC) += generic.dtb +dtb-$(CONFIG_BT1_DTB_BFK) += 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 + +obj-$(CONFIG_BUILTIN_DTB) += $(addsuffix .o, $(dtb-y)) diff --git a/arch/mips/boot/dts/baikal-t1/bfk3.dts b/arch/mips/boot/dts/baikal-t1/bfk3.dts new file mode 100644 index 0000000000000..42edc02ca87d0 --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/bfk3.dts @@ -0,0 +1,358 @@ +// 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 "soc.dtsi" +#include "krkx4.dtsi" + +/ { + model = "Baikal Electronics BFK v3.x Evaluation Board"; + compatible = "baikal,bfk3", "baikal,bt1"; + + chosen { + bootargs = "console=ttyS0,115200n8 earlycon maxcpus=2"; + stdout-path = "serial0:115200n8"; + + /* Bootloader may use these props to pass the initrd image */ + linux,initrd-start = <0 0>; + linux,initrd-end = <0 0>; + }; + + memory { + /* + * Assume at least 512MB of RAM: + * low memory - 128MB, high memory - 256MB. + */ + device_type = "memory"; + reg = <0 0x00000000 0 0x08000000>, + <0 0x20000000 0 0x10000000>; + }; + + clocks { + /* + * SATA/PCIe/xGMAC reference clocks are provided by the + * IDT 5P49V5901 which is out of the SoC reach and is + * initialized by the embedded BMC. + */ + xgmac_ref_clk: clock-oscillator-vc5p1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <156250000>; + clock-output-names = "xgmac156m"; + }; + + pcie_ref_clk: clock-oscillator-vc5p3 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <100000000>; + clock-output-names = "pcie100m"; + }; + + sata_ref_clk: clock-oscillator-vc5p4 { + 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"; + }; + }; +}; + +&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"; +}; + +&usb { + status = "okay"; + + ulpi { + phy { + clocks = <&usb_phy_clk>; + clock-names = "ref"; + }; + }; +}; + +&sram { + status = "okay"; +}; + +&dma { + status = "okay"; +}; + +&mc { + 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 = ; + + status = "okay"; +}; + +&sata1 { + hba-port-cap = ; + + status = "okay"; +}; + +&xgmac { + clocks = <&ccu_sys CCU_SYS_APB_CLK>, + <&ccu_axi CCU_AXI_XGMAC_CLK>, + <&ccu_sys CCU_SYS_XGMAC_PTP_CLK>, + <&xgmac_ref_clk>; + clock-names = "pclk", "stmmaceth", "ptp_ref", "tx"; + + mac-address = [ 00 20 13 ba 1c a1 ]; + + status = "okay"; +}; + +&hwa { + status = "okay"; +}; + +&xpcs { + clocks = <&ccu_sys CCU_SYS_APB_CLK>, + <&ccu_sys CCU_SYS_XGMAC_REF_CLK>, + <&xgmac_ref_clk>; + clock-names = "pclk", "core", "pad"; + + status = "disabled"; +}; + +&usb { + status = "okay"; +}; + +&mdio0 { + reset-delay-us = <10200>; + reset-post-delay-us = <1000>; + + /* Micrel KSZ9031RNX */ + gmac0_phy: ethernet-phy@3 { + compatible = "ethernet-phy-id0022.1620"; + reg = <0x3>; + + clocks = <&gmac0_phy_clk>; + clock-names = "ref"; + }; +}; + +&gmac0 { + mac-address = [ 00 26 58 80 01 02 ]; + + phy-handle = <&gmac0_phy>; + + status = "okay"; +}; + +&mdio1 { + reset-delay-us = <10200>; + reset-post-delay-us = <1000>; + + /* Micrel KSZ9031RNX */ + gmac1_phy: ethernet-phy@3 { + compatible = "ethernet-phy-id0022.1620"; + reg = <0x3>; + + clocks = <&gmac1_phy_clk>; + clock-names = "ref"; + }; +}; + +&gmac1 { + mac-address = [ 00 26 58 80 01 03 ]; + + phy-handle = <&gmac1_phy>; + + status = "okay"; +}; + +&l2 { + status = "okay"; +}; + +&i2c0 { + 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"; + + /* STM32F205VET-based Board Management Controller */ + bmc: bmc@8 { + compatible = "baikal,bt1-bmc"; + reg = <0x08>; + }; +}; + +&i2c2 { + status = "okay"; + + spd: eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + + pagesize = <8>; + }; + + /* Might be absent */ + fw: eeprom@54 { + compatible = "atmel,24cs04"; + reg = <0x54>; + + pagesize = <8>; + }; + + rtc: rtc@56 { + compatible = "abracon,abeoz9"; + reg = <0x56>; + + trickle-resistor-ohms = <5000>; + }; +}; + +&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>; + + /* + * XP20 port switches between CS0 and port1:0 chip-selects. + * XP21 port switches between CS1 and port1:1 chip-selects. + */ + cs-gpios = <0>, <0>, + <&port1 0 GPIO_ACTIVE_LOW>, <&port1 1 GPIO_ACTIVE_LOW>; + + status = "okay"; + + /* 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; + }; + + /* Micron N25Q256A13EF */ + test_flash13: flash@3 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + reg = <3>; + + spi-max-frequency = <25000000>; + m25p,fast-read; + }; +}; + +&spi2 { + /* XP19 port switches between CS0 and port1:2 chip-selects */ + cs-gpios = <0>, <&port1 2 GPIO_ACTIVE_LOW>; + + status = "okay"; +}; + +&pvt { + status = "okay"; +}; + +&efuse { + status = "okay"; +}; diff --git a/arch/mips/boot/dts/baikal-t1/cb2-em406.dts b/arch/mips/boot/dts/baikal-t1/cb2-em406.dts new file mode 100644 index 0000000000000..83c64c10b280f --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/cb2-em406.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC + * + * Edelweiss EM406 carrier board device tree + */ + +/dts-v1/; + +#include "em406.dtsi" +#include "krkx4.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-t1/em406.dtsi b/arch/mips/boot/dts/baikal-t1/em406.dtsi new file mode 100644 index 0000000000000..9a511a0a53d2a --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/em406.dtsi @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC + * + * Edelweiss EM406 Mezzanine Card device tree + */ + +#include "soc.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 may use these props to pass the initrd image */ + linux,initrd-start = <0 0>; + linux,initrd-end = <0 0>; + }; + + 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"; + }; + }; +}; + +&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 = <&gpio2 15 GPIO_ACTIVE_HIGH>; + + status = "okay"; +}; + +&usb { + status = "okay"; + + ulpi { + phy { + clocks = <&usb_phy_clk>; + clock-names = "ref"; + + reset-gpios = <&port0 25 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&sram { + status = "okay"; +}; + +&dma { + status = "okay"; +}; + +&mc { + 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 = ; + + status = "okay"; +}; + +&sata1 { + hba-port-cap = ; + + status = "okay"; +}; + +&xgmac { + clocks = <&ccu_sys CCU_SYS_APB_CLK>, + <&ccu_axi CCU_AXI_XGMAC_CLK>, + <&ccu_sys CCU_SYS_XGMAC_PTP_CLK>, + <&clk_gen 1>; + clock-names = "pclk", "stmmaceth", "ptp_ref", "tx"; + + mac-address = [ 00 26 58 80 01 01 ]; + + status = "okay"; +}; + +&hwa { + status = "okay"; +}; + +&xpcs { + clocks = <&ccu_sys CCU_SYS_APB_CLK>, + <&ccu_sys CCU_SYS_XGMAC_REF_CLK>, + <&clk_gen 1>; + clock-names = "pclk", "core", "pad"; + + status = "disabled"; +}; + +&usb { + status = "okay"; +}; + +&mdio0 { + reset-delay-us = <11000>; + reset-post-delay-us = <31000>; + + /* Realtek RTL8211E */ + gmac0_phy: ethernet-phy@3 { + compatible = "ethernet-phy-id001c.c915"; + reg = <0x3>; + + clocks = <&clk_gen 0>; + clock-names = "xtal"; + }; +}; + +&gmac0 { + mac-address = [ 00 26 58 80 01 02 ]; + + phy-handle = <&gmac0_phy>; + + status = "okay"; +}; + +&mdio1 { + reset-delay-us = <11000>; + reset-post-delay-us = <31000>; + + /* Realtek RTL8211E */ + gmac1_phy: ethernet-phy@3 { + compatible = "ethernet-phy-id001c.c915"; + reg = <0x3>; + + clocks = <&clk_gen 0>; + clock-names = "xtal"; + }; +}; + +&gmac1 { + mac-address = [ 00 26 58 80 01 03 ]; + + phy-handle = <&gmac1_phy>; + + status = "okay"; +}; + +&l2 { + status = "okay"; +}; + +&i2c0 { + status = "okay"; +}; + +&int_rom { + status = "okay"; +}; + +&spi0 { + num-cs = <1>; + + status = "okay"; + + /* 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; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; + + /* STM32L151XB-based Board Management Controller */ + bmc: bmc@8 { + compatible = "edelweiss,bt1-bmc"; + reg = <0x08>; + }; + + /* EM406-specific system control expander */ + gpio2: gpio@20 { + compatible = "nxp,pca9535"; + reg = <0x20>; + + gpio-controller; /* 16 */ + #gpio-cells = <2>; + + /* + * Note: IRQ can be fused up to the port0:29 pin triggered + * by the level-low signal. + */ + + /* nc - not connected */ + gpio-line-names = "MAJ0", "MAJ1", "MAJ2", "MAJ3", "SLEEP", + "LID", "nCHRG", "nCHRG_PRSNT", "nLOWBAT", + "CAR_STB", "nc", "nc", "nPCI_WAKE", "nc", + "PCI_RST"; + }; + + rtc: rtc@51 { + compatible = "nxp,pcf85263"; + reg = <0x51>; + }; + + fw: eeprom@53 { + compatible = "atmel,24c32"; + reg = <0x53>; + + pagesize = <32>; + }; + + spd: eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + + pagesize = <8>; + }; + + temp: temperature-sensor@48 { + compatible = "ti,tmp102"; + reg = <0x48>; + }; + + clk_gen: clock@6a { + compatible = "idt,5p49v6901"; + reg = <0x6a>; + #clock-cells = <1>; + + clocks = <&ref_clk>; + clock-names = "xin"; + + /* OUT0: Core act., OUT3: Ext PCIe */ + }; +}; + +&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"; +}; diff --git a/arch/mips/boot/dts/baikal-t1/em416.dtsi b/arch/mips/boot/dts/baikal-t1/em416.dtsi new file mode 100644 index 0000000000000..3a395df92f502 --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/em416.dtsi @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC + * + * Edelweiss EM416 Mezzanine Card device tree + */ + +#include + +#include "soc.dtsi" + +/ { + model = "Edelweiss EM416 Mezzanine Card"; + compatible = "edelweiss,em416", "baikal,bt1"; + + chosen { + bootargs = "console=ttyS0,115200n8 earlycon maxcpus=2"; + stdout-path = "serial0:115200n8"; + + /* Bootloader may use these props to pass the initrd image */ + linux,initrd-start = <0 0>; + linux,initrd-end = <0 0>; + }; + + 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"; + }; + }; +}; + +&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"; +}; + +&usb { + status = "okay"; + + ulpi { + phy { + clocks = <&clk_gen 3>; + clock-names = "ref"; + + reset-gpios = <&port0 13 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&sram { + status = "okay"; +}; + +&dma { + status = "okay"; +}; + +&mc { + 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 = ; + + status = "okay"; +}; + +&sata1 { + hba-port-cap = ; + + status = "okay"; +}; + +&hwa { + status = "okay"; +}; + +&usb { + status = "okay"; +}; + +&mdio0 { + reset-delay-us = <2000>; + reset-post-delay-us = <16000>; + + /* Microsemi VSC8531XMW */ + gmac0_phy: ethernet-phy@1 { + compatible = "ethernet-phy-id0007.0570"; + reg = <0x1>; + + clocks = <&gmac0_phy_clk>; + clock-names = "xtal"; + + vsc8531,vddmac = <1800>; + vsc8531,edge-slowdown = <0>; + vsc8531,led-0-mode = ; + vsc8531,led-1-mode = ; + }; +}; + +&gmac0 { + mac-address = [ 00 26 58 79 00 05 ]; + + phy-handle = <&gmac0_phy>; + + status = "okay"; +}; + +&mdio1 { + reset-delay-us = <2000>; + reset-post-delay-us = <16000>; + + /* Microsemi VSC8531XMW */ + gmac1_phy: ethernet-phy@2 { + compatible = "ethernet-phy-id0007.0570"; + reg = <0x2>; + + clocks = <&gmac1_phy_clk>; + clock-names = "xtal"; + + vsc8531,vddmac = <1800>; + vsc8531,edge-slowdown = <0>; + vsc8531,led-0-mode = ; + vsc8531,led-1-mode = ; + }; +}; + +&gmac1 { + mac-address = [ 00 26 58 79 00 06 ]; + + phy-handle = <&gmac1_phy>; + + status = "okay"; +}; + +&l2 { + status = "okay"; +}; + +&int_rom { + status = "okay"; +}; + +&spi0 { + num-cs = <1>; + + status = "okay"; + + /* 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; + }; +}; + +&port0 { + /* nc - not connected, gp - general purpose */ + gpio-line-names = "nc", "nc", "nc", "nc", "nc", "nc", "nc", "nc", + "nPCI_RST", "nPCI_PRSNT", "nc", "nc", "nc", + "nUSB_RST", "nSD_CS", "SD_CD", "nc", "nc", "nc", + "nc", "nc", "nc", "gp", "gp", "gp", "gp", "nEXP_INT", + "gp", "gp", "gp", "gp", "gp", "gp"; +}; + +&gpio0 { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; + + /* STM32F078CB-based Board Management Controller */ + bmc: bmc@8 { + compatible = "edelweiss,bt1-bmc"; + reg = <0x08>; + }; + + shred: 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>; + }; + + fw: eeprom@53 { + compatible = "atmel,24c32"; + reg = <0x53>; + + pagesize = <32>; + }; + + spd: eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + + pagesize = <8>; + }; + + 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"; +}; + +/* Connected to the STM32-based BMC */ +&uart1 { + status = "okay"; +}; + +&wdt { + status = "okay"; +}; + +&spi1 { + /* + * Both GPIO and native CS are supposed to be connected to the same + * MMC slot but only one of them can be fused up at a time. + */ + num-cs = <2>; + cs-gpios = <&port0 14 GPIO_ACTIVE_LOW>, <0>; + + status = "okay"; + + mmc: mmc-slot@0 { + compatible = "mmc-spi-slot"; + reg = <0>; + + voltage-ranges = <2000 3600>; + 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"; +}; diff --git a/arch/mips/boot/dts/baikal-t1/generic.dts b/arch/mips/boot/dts/baikal-t1/generic.dts new file mode 100644 index 0000000000000..9b14e840a87cb --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/generic.dts @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 generic platform device tree + */ + +/dts-v1/; + +#include "soc.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"; + + /* It's implied that the bootloader updates the initrd address */ + linux,initrd-start = <0 0>; + linux,initrd-end = <0 0>; + }; + + 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"; + }; + }; +}; + +&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"; +}; + +&usb { + status = "okay"; +}; + +&sram { + status = "okay"; +}; + +&dma { + status = "okay"; +}; + +&mc { + 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 = ; + + status = "okay"; +}; + +&sata1 { + hba-port-cap = ; + + status = "okay"; +}; + +&xgmac { + clocks = <&ccu_sys CCU_SYS_APB_CLK>, + <&ccu_axi CCU_AXI_XGMAC_CLK>, + <&ccu_sys CCU_SYS_XGMAC_PTP_CLK>, + <&xgmac_ref_clk>; + clock-names = "pclk", "stmmaceth", "ptp_ref", "tx"; + + mac-address = [ 00 20 13 ba 1c a1 ]; + + status = "okay"; +}; + +&hwa { + status = "okay"; +}; + +&xpcs { + clocks = <&ccu_sys CCU_SYS_APB_CLK>, + <&ccu_sys CCU_SYS_XGMAC_REF_CLK>, + <&xgmac_ref_clk>; + clock-names = "pclk", "core", "pad"; + + status = "disabled"; +}; + +&usb { + status = "okay"; +}; + +&mdio0 { + 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"; + }; +}; + +&gmac0 { + mac-address = [ 7a 72 6c 4a 7a 07 ]; + + phy-handle = <&gmac0_phy>; + + status = "okay"; +}; + +&mdio1 { + 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"; + }; +}; + +&gmac1 { + mac-address = [ 7a 72 6c 4a 7b 07 ]; + + phy-handle = <&gmac1_phy>; + + status = "okay"; +}; + +&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"; +}; + +&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"; +}; diff --git a/arch/mips/boot/dts/baikal-t1/krkx4.dtsi b/arch/mips/boot/dts/baikal-t1/krkx4.dtsi new file mode 100644 index 0000000000000..dd4c741526746 --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/krkx4.dtsi @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC + * + * Baikal Electronics KR/KX4 SFI Mezzanine Card device tree + */ + +#include + +/ { + aliases { + mdio-gpio2 = &mdio2; + }; + + mdio2: mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + + /* PORT0.9 - MDC, PORT0.10 - MDO, GPIO0.11 - MDI */ + gpios = <&port0 9 GPIO_ACTIVE_HIGH>, <&port0 11 GPIO_ACTIVE_HIGH>, + <&port0 10 GPIO_ACTIVE_HIGH>; + + reset-gpios = <&gpio3 0 GPIO_ACTIVE_HIGH>; + reset-delay-us = <10000>; + reset-post-delay-us = <10000>; + + /* Channel #0 s attached to the SFP+ port */ + mv_ch0: ethernet-phy@0c { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0x0c>; + + interrupt-parent = <&port0>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + + baikal,line-mode = "KR"; + baikal,host-mode = "KR"; + }; + + /* Channel #1-3 are terminated */ + mv_ch1: ethernet-phy@0d { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0x0d>; + + interrupt-parent = <&port0>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + + baikal,line-mode = "KR"; + baikal,host-mode = "KR"; + }; + + mv_ch2: ethernet-phy@0e { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0x0e>; + + interrupt-parent = <&port0>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + + baikal,line-mode = "KR"; + baikal,host-mode = "KR"; + + }; + + mv_ch3: ethernet-phy@0f { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0x0f>; + + interrupt-parent = <&port0>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + + baikal,line-mode = "KR"; + baikal,host-mode = "KR"; + }; + }; +}; + +&i2c1 { + status = "okay"; + + /* Marvell PHY Reset-controller (NXP PCA9500 8-bit GPIO) */ + gpio3: gpio@20 { + compatible = "nxp,pcf8574"; + reg = <0x20>; + + gpio-controller; /* 8 */ + #gpio-cells = <2>; + + /* nc - not connected */ + gpio-line-names = "RST_PHY", "nc", "nc", "nc", + "nc", "nc", "nc", "nc"; + }; + + /* Mezzanine card firmware (NXP PCA9500 2-kbit EEPROM) */ + fw1: eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + + pagesize = <4>; + }; +}; + +&xgmac { + phy-handle = <&mv_ch0>; +}; diff --git a/arch/mips/boot/dts/baikal-t1/oclk.dtsi b/arch/mips/boot/dts/baikal-t1/oclk.dtsi new file mode 100644 index 0000000000000..2216b21f69583 --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/oclk.dtsi @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 SoC overclocking device tree + */ + +#include + +/* + * Use the next points with care. Modern chips can be safely + * boosted up to 1.5GHz, while some older ones can't. + */ +&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; + }; +}; + +&pcie { + assigned-clocks = <&ccu_axi CCU_AXI_PCIE_M_CLK>, + <&ccu_axi CCU_AXI_PCIE_S_CLK>; + assigned-clock-rates = <600000000>, <600000000>; +}; + +&sata { + assigned-clocks = <&ccu_axi CCU_AXI_SATA_CLK>; + assigned-clock-rates = <300000000>; +}; + +&usb { + assigned-clocks = <&ccu_axi CCU_AXI_USB_CLK>; + assigned-clock-rates = <300000000>; +}; + +&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>; +}; + +/* + * Be careful with customizing this clock rate. The system is working well + * with freqs 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. + */ +&apb { + assigned-clocks = <&ccu_sys CCU_SYS_APB_CLK>; + assigned-clock-rates = <200000000>; +}; diff --git a/arch/mips/boot/dts/baikal-t1/soc.dtsi b/arch/mips/boot/dts/baikal-t1/soc.dtsi new file mode 100644 index 0000000000000..29a02284bd263 --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/soc.dtsi @@ -0,0 +1,1145 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 SoC generic device tree + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +/ { + 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; + 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 = "img,p5600"; + device_type = "cpu"; + reg = <0x0>; + #cooling-cells = <2>; + + clocks = <&ccu_pll CCU_CPU_PLL>; + clock-names = "cpu_clk"; + + operating-points-v2 = <&cpu_opp>; + }; + + cpu1: cpu@1 { + compatible = "img,p5600"; + device_type = "cpu"; + reg = <0x1>; + #cooling-cells = <2>; + + clocks = <&ccu_pll CCU_CPU_PLL>; + clock-names = "cpu_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 = ; + + clocks = <&ccu_pll CCU_CPU_PLL>; + }; + }; + + cpc: cpc@1bde0000 { + compatible = "mti,mips-cpc"; + reg = <0 0x1bde0000 0 0x8000>; + }; + + cdmm: cdmm@1bde8000 { + compatible = "mti,mips-cdmm"; + reg = <0 0x1bde8000 0 0x8000>; + }; + + 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"; + }; + }; + + 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 = ; + + 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 = , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "dma0", "dma1", "dma2", "dma3", + "dma4", "dma5", "dma6", "dma7", + "msi", "aer", "pme", "hp", "bw_mg", + "l_eq"; + + /* External reference clock source must be provided */ + clocks = <&ccu_sys CCU_SYS_APB_CLK>, + <&ccu_axi CCU_AXI_PCIE_M_CLK>, + <&ccu_axi CCU_AXI_PCIE_S_CLK>; + clock-names = "dbi", "mstr", "slv"; + + resets = <&ccu_axi CCU_AXI_PCIE_M_RST>, + <&ccu_axi CCU_AXI_PCIE_S_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@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 = ; + + /* 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>, + <0 0x1f043000 0 0x1000>; + reg-names = "umctl2", "phy"; + + interrupts = , + , + , + ; + 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"; + + 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 = ; + + /* 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>, + <0 0x1f05d000 0 0x1000>; + reg-names = "stmmaceth", "xpcs"; + + interrupts = , + , + , + , + , + ; + interrupt-names = "macirq", "dma_tx0", "dma_tx1", + "dma_rx0", "dma_rx1", "xpcs"; + + 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"; + + phy-mode = "xgmii"; + pcs-handle = <&xpcs>; + + rx-fifo-depth = <32768>; + tx-fifo-depth = <32768>; + + /* + * Actual burst length will be (32 * 8 * 16) bytes due + * to the snps,no-pbl-x8 property absence and having + * the AXI bus data width of 128 bits. + */ + snps,pbl = <32>; + snps,data-width = <16>; + + /* Enable TSO for all DMA channels */ + snps,tso; + + snps,perfect-filter-entries = <8>; + snps,multicast-filter-bins = <64>; + local-mac-address = [ 00 20 13 ba 1c a1 ]; + + status = "disabled"; + + 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>; + }; + }; + + hwa: hwa@1f05b000 { + compatible = "baikal,bt1-hwa"; + reg = <0 0x1f05b000 0 0x1000>, + <0 0x1f05c000 0 0x1000>; + reg-names = "core", "dma"; + + interrupts = ; + + 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"; + }; + + xpcs: ethernet-phy@1f05d000 { + compatible = "baikal,bt1-xpcs", "snps,dwxpcs-3.11b", "snps,dwxpcs", + "ethernet-phy-id7996.ced0", "ethernet-phy-ieee802.3-c45"; + reg = <0 0x1f05d000 0 0x1000>; + reg-names = "indirect"; + + interrupts = ; + + clocks = <&ccu_sys CCU_SYS_APB_CLK>, + <&ccu_sys CCU_SYS_XGMAC_REF_CLK>; + clock-names = "pclk", "core"; + + phy-is-integrated; + + status = "disabled"; + }; + + usb: usb@1f100000 { + compatible = "baikal,bt1-usb3", "snps,dwc3"; + reg = <0 0x1f100000 0 0x100000>; + #address-cells = <1>; + #size-cells = <0>; + + interrupts = ; + 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"; + }; + + 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 = ; + 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>; + + interrupt-controller; + #interrupt-cells = <2>; + + gpio-controller; + #gpio-cells = <2>; + + /* + * MAC always adds 2ns delay of TXC with respect to TXD + * so let the PHY to add some RXC delay if it's + * applicable. + */ + phy-mode = "rgmii-rxid"; + tx-internal-delay-ps = <2000>; + + rx-fifo-depth = <16384>; + tx-fifo-depth = <16384>; + + /* + * Actual burst length will be (32 * 8 * 16) bytes due + * to the snps,no-pbl-x8 property absence and having + * the AXI bus data width of 128 bits. + */ + snps,pbl = <32>; + snps,data-width = <16>; + + snps,perfect-filter-entries = <8>; + snps,multicast-filter-bins = <0>; + loacl-mac-address = [ 7a 72 6c 4a 7a 07 ]; + + status = "disabled"; + + axi-config { + snps,wr_osr_lmt = <0x3>; + snps,rd_osr_lmt = <0x3>; + snps,blen = <0 0 0 0 16 8 4>; + }; + + mdio0: 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 = ; + 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>; + + interrupt-controller; + #interrupt-cells = <2>; + + gpio-controller; + #gpio-cells = <2>; + + /* + * MAC always adds 2ns delay of TXC with respect to TXD + * so let the PHY to add some RXC delay if it's + * applicable. + */ + phy-mode = "rgmii-rxid"; + tx-internal-delay-ps = <2000>; + + rx-fifo-depth = <16384>; + tx-fifo-depth = <16384>; + + /* + * Actual burst length will be (32 * 8 * 16) bytes due + * to the snps,no-pbl-x8 property absence and having + * the AXI bus data width of 128 bits. + */ + snps,pbl = <32>; + snps,data-width = <16>; + + snps,perfect-filter-entries = <8>; + snps,multicast-filter-bins = <0>; + loacl-mac-address = [ 7a 72 6c 4a 7b 07 ]; + + status = "disabled"; + + axi-config { + snps,wr_osr_lmt = <0x3>; + snps,rd_osr_lmt = <0x3>; + snps,blen = <0 0 0 0 16 8 4>; + }; + + mdio1: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + }; + + 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 = ; + + 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>; + + status = "disabled"; + }; + + reboot-mode { + compatible = "syscon-reboot-mode"; + offset = <0x154>; + + mode-normal = ; + mode-loader = ; + mode-recovery = ; + }; + + i2c0: i2c@1f04d100 { + compatible = "baikal,bt1-sys-i2c"; + reg = <0x1f04d100 0x010>; + #address-cells = <1>; + #size-cells = <0>; + + interrupts = ; + + 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 = ; + 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 = ; + + 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 = ; + + 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 = ; + + 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 = ; + + 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 = ; + + clocks = <&ccu_sys CCU_SYS_TIMER2_CLK>, + <&ccu_sys CCU_SYS_APB_CLK>; + clock-names = "timer", "pclk"; + + status = "disabled"; + }; + + uart0: serial@1f04a000 { + compatible = "snps,dw-apb-uart"; + reg = <0x1f04a000 0x1000>; + + interrupts = ; + + 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 = "snps,dw-apb-uart"; + reg = <0x1f04b000 0x1000>; + + interrupts = ; + + 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 = ; + + 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 = "snps,dw-apb-ssi"; + reg = <0x1f04e000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + + interrupts = ; + + 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 = "snps,dw-apb-ssi"; + reg = <0x1f04f000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + + interrupts = ; + + 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 = ; + + 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"; + }; + }; +}; diff --git a/arch/mips/boot/dts/baikal-t1/tf306.dts b/arch/mips/boot/dts/baikal-t1/tf306.dts new file mode 100644 index 0000000000000..4d8ed36fdaf5d --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/tf306.dts @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC + * + * Edelweiss TF306 mini-ITX board device tree + */ + +/dts-v1/; + +#include "soc.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 is supposed to pass the initrd image address */ + linux,initrd-start = <0 0>; + linux,initrd-end = <0 0>; + }; + + memory { + /* + * Assume at least 512MB of RAM: + * 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"; + }; + }; +}; + +&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"; + }; +}; + +&usb { + reset-gpios = <&port0 13 GPIO_ACTIVE_LOW>; + + status = "okay"; + + ulpi { + 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 B */ + }; +}; + +&sram { + status = "okay"; +}; + +&dma { + status = "okay"; +}; + +&mc { + 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"; +}; + +/* InnoDisk DENSD-16GD06 - 16GB SSD */ +&sata0 { + status = "okay"; +}; + +&sata1 { + hba-port-cap = ; + + status = "okay"; +}; + +&hwa { + status = "okay"; +}; + +&mdio0 { + reset-delay-us = <11000>; + reset-post-delay-us = <31000>; + + /* Realtek RTL8211E */ + gmac0_phy: ethernet-phy@1 { + compatible = "ethernet-phy-id001c.c915"; + reg = <0x1>; + + clocks = <&gmac0_phy_clk>; + clock-names = "xtal"; + }; +}; + +&gmac0 { + mac-address = [ 00 26 58 78 10 00 ]; + + phy-handle = <&gmac0_phy>; + + status = "okay"; +}; + +&l2 { + status = "okay"; +}; + +&int_rom { + status = "okay"; +}; + +&spi0 { + num-cs = <1>; + + status = "okay"; + + boot_flash: flash@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + + spi-max-frequency = <25000000>; + m25p,fast-read; + }; +}; + +&port0 { + gpio-line-names = "", "", "", "", "", "", "", "", "", "", "", + "nRTC_INT", "nVIDEO_RST", "nUSB_RST", "nSD_CS", + "SD_CD", "", "", "HDD_LED", "", "", "", "", "", "", + "nEXP_INT", "", "", "", "", "", ""; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "disabled"; +}; + +&i2c2 { + status = "okay"; + + /* STM32F078CB-based Board Management Controller */ + bmc: bmc@8 { + compatible = "edelweiss,bt1-bmc"; + reg = <0x08>; + }; + + shred: 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: hub@2c { + compatible = "microchip,usb2517"; + reg = <0x2c>; + + /* Configs are set by the bootloader */ + skip-config; + }; + + 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>; + }; + + spd: eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + + pagesize = <8>; + }; + + 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"; +}; + +/* Connected to the STM32-based BMC */ +&uart1 { + status = "okay"; +}; + +&wdt { + status = "okay"; +}; + +&spi1 { + /* + * Both GPIO and native CS are supposed to be connected to the same + * MMC slot but only one of them can be fused up at a time. + */ + num-cs = <2>; + cs-gpios = <&port0 14 GPIO_ACTIVE_LOW>, <0>; + + status = "okay"; + + mmc: mmc-slot@0 { + compatible = "mmc-spi-slot"; + reg = <0>; + + voltage-ranges = <2000 3600>; + spi-max-frequency = <25000000>; + /* Card-Detect GPIO */ + gpios = <&port0 15 GPIO_ACTIVE_LOW>; + }; +}; + +&pvt { + status = "okay"; +}; + +&efuse { + status = "okay"; +}; diff --git a/arch/mips/boot/dts/baikal-t1/xa1-em406.dts b/arch/mips/boot/dts/baikal-t1/xa1-em406.dts new file mode 100644 index 0000000000000..7ac3d0f9bcec6 --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/xa1-em406.dts @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC + * + * Edelweiss EM406 debug board device tree + */ + +/dts-v1/; + +#include "em406.dtsi" +#include "krkx4.dtsi" + +/ { + model = "Edelweiss EM406 XA1 Debug Board"; + compatible = "edelweiss,xa1-em406", "edelweiss,em406", "baikal,bt1"; +}; + +&i2c0 { + test_temp: temperature-sensor@48 { + compatible = "ti,tmp102"; + reg = <0x48>; + }; +}; + +&i2c1 { + test_gpio3: gpio@22 { + compatible = "ti,tca6424"; + reg = <0x22>; + + gpio-controller; /* 24 */ + #gpio-cells = <2>; + }; + + test_gpio4: gpio@23 { + compatible = "ti,tca6424"; + reg = <0x23>; + + gpio-controller; /* 24 */ + #gpio-cells = <2>; + }; +}; + +&spi1 { + num-cs = <4>; + + /* Micron MT25QU128ABA1 */ + test_flash1: 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>; + + /* Micron MT25QU128ABA1 */ + test_flash2: 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-t1/xa1-em416.dts b/arch/mips/boot/dts/baikal-t1/xa1-em416.dts new file mode 100644 index 0000000000000..ce43dcc665e8a --- /dev/null +++ b/arch/mips/boot/dts/baikal-t1/xa1-em416.dts @@ -0,0 +1,40 @@ +// 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"; +}; + +&i2c1 { + test_gpio3: gpio@20 { + compatible = "nxp,pca9535"; + reg = <0x20>; + + gpio-controller; /* 16 */ + #gpio-cells = <2>; + }; +}; + +&spi2 { + num-cs = <4>; + + /* Micron MT25QU128ABA1 */ + test_flash2: 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/configs/baikal_t1_defconfig b/arch/mips/configs/baikal_t1_defconfig new file mode 100644 index 0000000000000..7d7e424136aff --- /dev/null +++ b/arch/mips/configs/baikal_t1_defconfig @@ -0,0 +1,418 @@ +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_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y +CONFIG_PREEMPT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=15 +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_EMBEDDED=y +CONFIG_SLAB=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_PAGE_SIZE_16KB=y +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_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_BLK_DEV_BSGLIB=y +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_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=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_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=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_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_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_SYNPROXY=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=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_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=y +CONFIG_NET_SCH_ETF=y +CONFIG_NET_SCH_TAPRIO=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOWER=y +CONFIG_DCB=y +CONFIG_DNS_RESOLVER=y +CONFIG_NETLINK_DIAG=y +CONFIG_CGROUP_NET_PRIO=y +# CONFIG_WIRELESS is not set +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_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_AMD_XGBE=y +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC 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_DEC is not set +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX 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_MYRI is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETERION 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=y +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_TOSHIBA is not set +# CONFIG_NET_VENDOR_VIA 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_KR_PHY=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_DEPRECATED_OPTIONS is not set +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_MIPS_EJTAG_FDC_TTY=y +# CONFIG_HW_RANDOM is not set +# CONFIG_RANDOM_TRUST_BOOTLOADER is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_SLAVE=y +CONFIG_I2C_DESIGNWARE_PLATFORM=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_GPIOLIB=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_WRITABLE_TRIPS=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_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_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_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_COMMON_CLK_VC5=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_MEMORY=y +CONFIG_BT1_L2_CTL=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=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_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NFS_USE_LEGACY_DNS=y +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_LSM="lockdown,yama,loadpin,safesetid,integrity,bpf" +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_ANSI_CPRNG=y +# CONFIG_CRYPTO_HW is not set +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +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/configs/bfk3_defconfig b/arch/mips/configs/bfk3_defconfig new file mode 100644 index 0000000000000..b614e534ad2f1 --- /dev/null +++ b/arch/mips/configs/bfk3_defconfig @@ -0,0 +1,399 @@ +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_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y +CONFIG_PREEMPT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=15 +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_EMBEDDED=y +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_MIPS_BAIKAL_T1=y +CONFIG_BT1_CPU_FEATURE_OVERRIDES=y +CONFIG_BT1_DTB_BFK=y +CONFIG_CPU_P5600=y +CONFIG_CPU_MIPS32_R5_FEATURES=y +CONFIG_CPU_MIPS32_R5_XPA=y +CONFIG_PAGE_SIZE_16KB=y +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_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_BLK_DEV_BSGLIB=y +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_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=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_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=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_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_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_SYNPROXY=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=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_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=y +CONFIG_NET_SCH_ETF=y +CONFIG_NET_SCH_TAPRIO=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOWER=y +CONFIG_DCB=y +CONFIG_DNS_RESOLVER=y +CONFIG_NETLINK_DIAG=y +CONFIG_CGROUP_NET_PRIO=y +# CONFIG_WIRELESS is not set +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_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_AMD_XGBE=y +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC 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_DEC is not set +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX 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_MYRI is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETERION 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=y +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_TOSHIBA is not set +# CONFIG_NET_VENDOR_VIA 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_KR_PHY=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_DEPRECATED_OPTIONS is not set +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_MIPS_EJTAG_FDC_TTY=y +# CONFIG_HW_RANDOM is not set +# CONFIG_RANDOM_TRUST_BOOTLOADER is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_SLAVE=y +CONFIG_I2C_DESIGNWARE_PLATFORM=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_GPIOLIB=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_WRITABLE_TRIPS=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_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_COMMON_CLK_VC5=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_MEMORY=y +CONFIG_BT1_L2_CTL=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=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_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NFS_USE_LEGACY_DNS=y +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_LSM="lockdown,yama,loadpin,safesetid,integrity,bpf" +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_ANSI_CPRNG=y +# CONFIG_CRYPTO_HW is not set +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +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/configs/em406_defconfig b/arch/mips/configs/em406_defconfig new file mode 100644 index 0000000000000..c4a032649eedb --- /dev/null +++ b/arch/mips/configs/em406_defconfig @@ -0,0 +1,398 @@ +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_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y +CONFIG_PREEMPT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=15 +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_EMBEDDED=y +CONFIG_SLAB=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_PAGE_SIZE_16KB=y +CONFIG_CPU_HAS_MSA=y +CONFIG_NR_CPUS=2 +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_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_BLK_DEV_BSGLIB=y +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_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=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_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=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_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_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_SYNPROXY=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=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_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=y +CONFIG_NET_SCH_ETF=y +CONFIG_NET_SCH_TAPRIO=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOWER=y +CONFIG_DCB=y +CONFIG_DNS_RESOLVER=y +CONFIG_NETLINK_DIAG=y +CONFIG_CGROUP_NET_PRIO=y +# CONFIG_WIRELESS is not set +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_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_AMD_XGBE=y +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC 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_DEC is not set +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX 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_MYRI is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETERION 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=y +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_TOSHIBA is not set +# CONFIG_NET_VENDOR_VIA 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_KR_PHY=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_DEPRECATED_OPTIONS is not set +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_MIPS_EJTAG_FDC_TTY=y +# CONFIG_HW_RANDOM is not set +# CONFIG_RANDOM_TRUST_BOOTLOADER is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_SLAVE=y +CONFIG_I2C_DESIGNWARE_PLATFORM=y +CONFIG_I2C_SLAVE_EEPROM=y +CONFIG_SPI=y +CONFIG_SPI_DESIGNWARE=y +CONFIG_SPI_DW_DMA=y +CONFIG_SPI_DW_PCI=y +CONFIG_SPI_DW_MMIO=y +CONFIG_SPI_DW_BT1=y +CONFIG_SPI_SPIDEV=y +CONFIG_GPIOLIB=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_WRITABLE_TRIPS=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_COMMON_CLK_VC5=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_MEMORY=y +CONFIG_BT1_L2_CTL=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=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_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NFS_USE_LEGACY_DNS=y +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_LSM="lockdown,yama,loadpin,safesetid,integrity,bpf" +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_ANSI_CPRNG=y +# CONFIG_CRYPTO_HW is not set +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +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/configs/em416_defconfig b/arch/mips/configs/em416_defconfig new file mode 100644 index 0000000000000..dfa8bde5bd6e3 --- /dev/null +++ b/arch/mips/configs/em416_defconfig @@ -0,0 +1,389 @@ +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_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y +CONFIG_PREEMPT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=15 +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_EMBEDDED=y +CONFIG_SLAB=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_PAGE_SIZE_16KB=y +CONFIG_CPU_HAS_MSA=y +CONFIG_NR_CPUS=2 +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_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_BLK_DEV_BSGLIB=y +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_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=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_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=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_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_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_SYNPROXY=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=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_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=y +CONFIG_NET_SCH_ETF=y +CONFIG_NET_SCH_TAPRIO=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOWER=y +CONFIG_DCB=y +CONFIG_DNS_RESOLVER=y +CONFIG_NETLINK_DIAG=y +CONFIG_CGROUP_NET_PRIO=y +# CONFIG_WIRELESS is not set +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_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_AMD is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC 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_DEC is not set +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX 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_MYRI is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETERION 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=y +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_TOSHIBA is not set +# CONFIG_NET_VENDOR_VIA 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_DEPRECATED_OPTIONS is not set +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_MIPS_EJTAG_FDC_TTY=y +# CONFIG_HW_RANDOM is not set +# CONFIG_RANDOM_TRUST_BOOTLOADER is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_SLAVE=y +CONFIG_I2C_DESIGNWARE_PLATFORM=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_GPIOLIB=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_WRITABLE_TRIPS=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_COMMON_CLK_VC5=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_MEMORY=y +CONFIG_BT1_L2_CTL=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=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_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NFS_USE_LEGACY_DNS=y +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_LSM="lockdown,yama,loadpin,safesetid,integrity,bpf" +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_ANSI_CPRNG=y +# CONFIG_CRYPTO_HW is not set +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +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/configs/tf306_defconfig b/arch/mips/configs/tf306_defconfig new file mode 100644 index 0000000000000..486cb13ba5a41 --- /dev/null +++ b/arch/mips/configs/tf306_defconfig @@ -0,0 +1,401 @@ +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_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y +CONFIG_PREEMPT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=15 +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_EMBEDDED=y +CONFIG_SLAB=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_PAGE_SIZE_16KB=y +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_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_BLK_DEV_BSGLIB=y +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_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=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_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=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_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_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_SYNPROXY=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=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_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBS=y +CONFIG_NET_SCH_ETF=y +CONFIG_NET_SCH_TAPRIO=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOWER=y +CONFIG_DCB=y +CONFIG_DNS_RESOLVER=y +CONFIG_NETLINK_DIAG=y +CONFIG_CGROUP_NET_PRIO=y +# CONFIG_WIRELESS is not set +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_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_AMD is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC 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_DEC is not set +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX 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_MYRI is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETERION 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_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_TOSHIBA is not set +# CONFIG_NET_VENDOR_VIA 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_DEPRECATED_OPTIONS is not set +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_MIPS_EJTAG_FDC_TTY=y +# CONFIG_HW_RANDOM is not set +# CONFIG_RANDOM_TRUST_BOOTLOADER is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_SLAVE=y +CONFIG_I2C_DESIGNWARE_PLATFORM=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_GPIOLIB=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_WRITABLE_TRIPS=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_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_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_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_COMMON_CLK_VC5=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_MEMORY=y +CONFIG_BT1_L2_CTL=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=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_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NFS_USE_LEGACY_DNS=y +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_LSM="lockdown,yama,loadpin,safesetid,integrity,bpf" +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_ANSI_CPRNG=y +# CONFIG_CRYPTO_HW is not set +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +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/cpu-info.h b/arch/mips/include/asm/cpu-info.h index a600670d00e97..b266d48925577 100644 --- a/arch/mips/include/asm/cpu-info.h +++ b/arch/mips/include/asm/cpu-info.h @@ -114,7 +114,7 @@ struct cpuinfo_mips { */ u32 loongson3_cpucfg_data[3]; #endif -} __attribute__((aligned(SMP_CACHE_BYTES))); +} __aligned(SMP_CACHE_BYTES) __randomize_layout; extern struct cpuinfo_mips cpu_data[]; #define current_cpu_data cpu_data[smp_processor_id()] diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h index 6f5c86d2bab45..7c07010bf7c0f 100644 --- a/arch/mips/include/asm/io.h +++ b/arch/mips/include/asm/io.h @@ -225,7 +225,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) \ @@ -233,7 +233,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(); \ @@ -274,7 +274,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)) \ @@ -306,14 +306,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, p) \ +#define __BUILD_IOPORT_SINGLE(pfx, bwlq, type, relax, p) \ \ static inline void pfx##out##bwlq##p(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(); \ @@ -337,7 +337,7 @@ static inline type pfx##in##bwlq##p(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; \ @@ -350,7 +350,7 @@ static inline type pfx##in##bwlq##p(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) \ \ @@ -370,8 +370,8 @@ __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, 1, 0, _p) + __BUILD_IOPORT_SINGLE(bus, bwlq, type, 0,) \ + __BUILD_IOPORT_SINGLE(bus, bwlq, type, 0, _p) #define BUILDIO_IOPORT(bwlq, type) \ __BUILD_IOPORT_PFX(, bwlq, type) \ @@ -386,7 +386,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/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 696f6b0093776..7e68d7107d201 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -757,7 +757,7 @@ struct kvm_mips_callbacks { int (*vcpu_put)(struct kvm_vcpu *vcpu, int cpu); int (*vcpu_run)(struct kvm_vcpu *vcpu); void (*vcpu_reenter)(struct kvm_vcpu *vcpu); -}; +} __no_randomize_layout; extern struct kvm_mips_callbacks *kvm_mips_callbacks; int kvm_mips_emulation_init(struct kvm_mips_callbacks **install_callbacks); diff --git a/arch/mips/include/asm/mach-baikal-t1/cpu-feature-overrides.h b/arch/mips/include/asm/mach-baikal-t1/cpu-feature-overrides.h new file mode 100644 index 0000000000000..a35eba5cd8f0a --- /dev/null +++ b/arch/mips/include/asm/mach-baikal-t1/cpu-feature-overrides.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 core features override + */ +#ifndef __ASM_MACH_BAIKAL_T1_CPU_FEATURE_OVERRIDES_H__ +#define __ASM_MACH_BAIKAL_T1_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_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_BAIKAL_T1_CPU_FEATURE_OVERRIDES_H__ */ diff --git a/arch/mips/include/asm/mach-baikal-t1/irq.h b/arch/mips/include/asm/mach-baikal-t1/irq.h new file mode 100644 index 0000000000000..343b200de1625 --- /dev/null +++ b/arch/mips/include/asm/mach-baikal-t1/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_BAIKAL_T1_IRQ_H__ +#define __ASM_MACH_BAIKAL_T1_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 + +#endif /* __ASM_MACH_BAIKAL_T1_IRQ_H__ */ diff --git a/arch/mips/include/asm/mach-baikal-t1/kernel-entry-init.h b/arch/mips/include/asm/mach-baikal-t1/kernel-entry-init.h new file mode 100644 index 0000000000000..a681bd927b776 --- /dev/null +++ b/arch/mips/include/asm/mach-baikal-t1/kernel-entry-init.h @@ -0,0 +1,253 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 platform low-level initialization + */ +#ifndef __ASM_MACH_BAIKAL_T1_KERNEL_ENTRY_INIT_H__ +#define __ASM_MACH_BAIKAL_T1_KERNEL_ENTRY_INIT_H__ + +#include +#include + + /* + * Prepare segments for EVA boot: + * + * This is in case the processor boots in legacy configuration + * (SI_EVAReset is de-asserted and CONFIG5.K == 0) + * + * =========================== Mappings =============================== + * CFG Virtual memory Physical memory CCA Mapping + * 5 0x00000000 0x3fffffff 0x80000000 0xBffffffff K0 MUSUK (kuseg) + * 4 0x40000000 0x7fffffff 0xC0000000 0xfffffffff K0 MUSUK (kuseg) + * Flat 2GB physical mem + * + * 3 0x80000000 0x9fffffff 0x00000000 0x1ffffffff K0 MUSUK (kseg0) + * 2 0xa0000000 0xbf000000 0x00000000 0x1ffffffff UC MUSUK (kseg1) + * 1 0xc0000000 0xdfffffff - K0 MK (kseg2) + * 0 0xe0000000 0xffffffff - K0 MK (kseg3) + * where UC = 2 Uncached non-coherent, + * WB = 3 Cacheable, non-coherent, write-back, write allocate, + * CWBE = 4 Cacheable, coherent, write-back, write-allocate, read + * misses request Exclusive, + * CWB = 5 Cacheable, coherent, write-back, write-allocate, read misses + * request Shared, + * UCA = 7 Uncached Accelerated, non-coherent. + * 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. + * + * Note K0 = 2 by default on MIPS Warrior P5600. + * + * Lowmem is expanded to 2GB. + * + * The following code uses the t0, t1, t2 and ra registers without + * previously preserving them. + * + */ + .macro platform_eva_init + + .set push + .set reorder + /* + * Get Config.K0 value and use it to program + * the segmentation registers + */ + mfc0 t1, CP0_CONFIG + andi t1, 0x7 /* CCA */ + move t2, t1 + ins t2, t1, 16, 3 + /* SegCtl0 */ + li t0, ((MIPS_SEGCFG_MK << MIPS_SEGCFG_AM_SHIFT) | \ + (0 << MIPS_SEGCFG_PA_SHIFT) | \ + (1 << MIPS_SEGCFG_EU_SHIFT)) | \ + (((MIPS_SEGCFG_MK << MIPS_SEGCFG_AM_SHIFT) | \ + (0 << MIPS_SEGCFG_PA_SHIFT) | \ + (1 << MIPS_SEGCFG_EU_SHIFT)) << 16) + or t0, t2 + mtc0 t0, CP0_SEGCTL0 + + /* SegCtl1 */ + li t0, ((MIPS_SEGCFG_MUSUK << MIPS_SEGCFG_AM_SHIFT) | \ + (0 << MIPS_SEGCFG_PA_SHIFT) | \ + (2 << MIPS_SEGCFG_C_SHIFT) | \ + (1 << MIPS_SEGCFG_EU_SHIFT)) | \ + (((MIPS_SEGCFG_MUSUK << MIPS_SEGCFG_AM_SHIFT) | \ + (0 << MIPS_SEGCFG_PA_SHIFT) | \ + (1 << MIPS_SEGCFG_EU_SHIFT)) << 16) + ins t0, t1, 16, 3 + mtc0 t0, CP0_SEGCTL1 + + /* SegCtl2 */ + li t0, ((MIPS_SEGCFG_MUSUK << MIPS_SEGCFG_AM_SHIFT) | \ + (6 << MIPS_SEGCFG_PA_SHIFT) | \ + (1 << MIPS_SEGCFG_EU_SHIFT)) | \ + (((MIPS_SEGCFG_MUSUK << MIPS_SEGCFG_AM_SHIFT) | \ + (4 << MIPS_SEGCFG_PA_SHIFT) | \ + (1 << MIPS_SEGCFG_EU_SHIFT)) << 16) + or t0, t2 + mtc0 t0, CP0_SEGCTL2 + + jal mips_ihb + mfc0 t0, CP0_CONFIG5 + li t2, MIPS_CONF5_K /* K bit */ + or t0, t0, t2 + mtc0 t0, CP0_CONFIG5 + sync + jal mips_ihb + nop + + .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 0xbf000000 0x00000000 0x1ffffffff 2 UK (kseg1) + * 1 0xc0000000 0xdfffffff - CWB MSK (kseg2) + * 0 0xe0000000 0xffffffff - CWB MK (kseg3) + * + * The following code uses the t0, t1, t2 and ra registers without + * previously preserving them. + * + */ + .macro platform_legacy_init + + .set push + .set reorder + + /* + * Directly use cacheable, coherent, write-back, write-allocate, read + * misses request shared attribute (CWB). + */ + li t1, 0x5 + move t2, t1 + ins t2, t1, 16, 3 + /* SegCtl0 */ + li t0, ((MIPS_SEGCFG_MK << MIPS_SEGCFG_AM_SHIFT) | \ + (0 << MIPS_SEGCFG_PA_SHIFT)) | \ + (((MIPS_SEGCFG_MSK << MIPS_SEGCFG_AM_SHIFT) | \ + (0 << MIPS_SEGCFG_PA_SHIFT)) << 16) + or t0, t2 + mtc0 t0, CP0_SEGCTL0 + + /* SegCtl1 */ + li t0, ((MIPS_SEGCFG_UK << MIPS_SEGCFG_AM_SHIFT) | \ + (0 << MIPS_SEGCFG_PA_SHIFT) | \ + (2 << MIPS_SEGCFG_C_SHIFT)) | \ + (((MIPS_SEGCFG_UK << MIPS_SEGCFG_AM_SHIFT) | \ + (0 << MIPS_SEGCFG_PA_SHIFT)) << 16) + ins t0, t1, 16, 3 + mtc0 t0, CP0_SEGCTL1 + + /* SegCtl2 */ + li t0, ((MIPS_SEGCFG_MUSK << MIPS_SEGCFG_AM_SHIFT) | \ + (6 << MIPS_SEGCFG_PA_SHIFT) | \ + (1 << MIPS_SEGCFG_EU_SHIFT)) | \ + (((MIPS_SEGCFG_MUSK << MIPS_SEGCFG_AM_SHIFT) | \ + (4 << MIPS_SEGCFG_PA_SHIFT) | \ + (1 << MIPS_SEGCFG_EU_SHIFT)) << 16) + or t0, t2 + mtc0 t0, CP0_SEGCTL2 + + jal mips_ihb + nop + + mfc0 t0, CP0_CONFIG, 5 + li t2, MIPS_CONF5_K /* K bit */ + or t0, t0, t2 + mtc0 t0, CP0_CONFIG, 5 + sync + jal mips_ihb + nop + + .set pop + .endm + + /* + * Baikal-T1 engineering chip had problems when the next features + * were enabled. + */ + .macro platform_errata_jr_ls_fix + + .set push + .set reorder + + jal mips_ihb + nop + + /* Disable load/store bonding. */ + mfc0 t0, CP0_CONFIG, 6 + lui t1, (MIPS_CONF6_DLSB >> 16) + or t0, t0, t1 + /* Disable all JR prediction except JR $31. */ + ori t0, t0, MIPS_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_BAIKAL_T1_KERNEL_ENTRY_INIT_H__ */ diff --git a/arch/mips/include/asm/mach-baikal-t1/memory.h b/arch/mips/include/asm/mach-baikal-t1/memory.h new file mode 100644 index 0000000000000..a2c3f6cbcd711 --- /dev/null +++ b/arch/mips/include/asm/mach-baikal-t1/memory.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 SoC Static Memory Mapping + */ +#ifndef __ASM_MACH_BAIKAL_T1_MEMORY_H__ +#define __ASM_MACH_BAIKAL_T1_MEMORY_H__ + +#include +#include + +#define BT1_LOMEM_BASE 0x00000000 +#define BT1_LOMEM_SIZE SZ_128M + +#define BT1_MMIO_START BT1_PCIE_MAP_BASE + +#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_BAIKAL_T1_MEMORY_H__ */ diff --git a/arch/mips/include/asm/mach-baikal-t1/platform.h b/arch/mips/include/asm/mach-baikal-t1/platform.h new file mode 100644 index 0000000000000..5c873de374f21 --- /dev/null +++ b/arch/mips/include/asm/mach-baikal-t1/platform.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 platform declarations + */ +#ifndef __ASM_MACH_BAIKAL_T1_PLATFORM_H__ +#define __ASM_MACH_BAIKAL_T1_PLATFORM_H__ + +#ifdef CONFIG_CPU_SUPPORTS_UNCACHED_ACCELERATED + +int mips_set_uca_range(phys_addr_t start, phys_addr_t end); + +#else /* !CONFIG_CPU_SUPPORTS_UNCACHED_ACCELERATED */ + +static inline int mips_set_uca_range(phys_addr_t start, phys_addr_t end) +{ + return 0; +} + +#endif /* !CONFIG_CPU_SUPPORTS_UNCACHED_ACCELERATED */ + +#endif /* __ASM_MACH_BAIKAL_T1_PLATFORM_H__ */ diff --git a/arch/mips/include/asm/mach-baikal-t1/spaces.h b/arch/mips/include/asm/mach-baikal-t1/spaces.h new file mode 100644 index 0000000000000..beccd1129cee5 --- /dev/null +++ b/arch/mips/include/asm/mach-baikal-t1/spaces.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 SoC Memory Spaces + */ +#ifndef __ASM_MACH_BAIKAL_T1_SPACES_H__ +#define __ASM_MACH_BAIKAL_T1_SPACES_H__ + +#define PCI_IOBASE mips_io_port_base +#define PCI_IOSIZE SZ_64K +#define IO_SPACE_LIMIT (PCI_IOSIZE - 1) + +#define pci_remap_iospace pci_remap_iospace + +#include + +#endif /* __ASM_MACH_BAIKAL_T1_SPACES_H__ */ diff --git a/arch/mips/include/asm/mach-ralink/spaces.h b/arch/mips/include/asm/mach-ralink/spaces.h index 87d085c9ad610..eb62d06b78f65 100644 --- a/arch/mips/include/asm/mach-ralink/spaces.h +++ b/arch/mips/include/asm/mach-ralink/spaces.h @@ -6,5 +6,9 @@ #define PCI_IOSIZE SZ_16M #define IO_SPACE_LIMIT (PCI_IOSIZE - 1) +#ifdef CONFIG_PCI_DRIVERS_GENERIC +#define pci_remap_iospace pci_remap_iospace +#endif + #include #endif diff --git a/arch/mips/include/asm/mips-cm.h b/arch/mips/include/asm/mips-cm.h index 23c67c0871b17..6ed1978830dd9 100644 --- a/arch/mips/include/asm/mips-cm.h +++ b/arch/mips/include/asm/mips-cm.h @@ -144,6 +144,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/mipsregs.h b/arch/mips/include/asm/mipsregs.h index acdf8c69220b0..4e0db88d602c8 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -2848,6 +2848,7 @@ set_##name(unsigned int set) \ res = read_##name(); \ new = res | set; \ write_##name(new); \ + _ehb(); \ \ return res; \ } \ @@ -2860,6 +2861,7 @@ clear_##name(unsigned int clear) \ res = read_##name(); \ new = res & ~clear; \ write_##name(new); \ + _ehb(); \ \ return res; \ } \ @@ -2873,6 +2875,7 @@ change_##name(unsigned int change, unsigned int val) \ new = res & ~change; \ new |= (val & change); \ write_##name(new); \ + _ehb(); \ \ return res; \ } diff --git a/arch/mips/include/asm/mmu_context.h b/arch/mips/include/asm/mmu_context.h index ed9f2d748f633..c0f8a4ee3fd70 100644 --- a/arch/mips/include/asm/mmu_context.h +++ b/arch/mips/include/asm/mmu_context.h @@ -232,6 +232,67 @@ drop_mmu_context(struct mm_struct *mm) local_irq_restore(flags); } +#ifdef CONFIG_MIPS_BAIKAL_T1 +/* Workaround for core stuck on TLB load exception */ +#define tlb_prefetch tlb_prefetch +static inline void tlb_prefetch(unsigned long addr) +{ + pgd_t *pgdp; + p4d_t *p4dp; + pud_t *pudp; + pmd_t *pmdp; + pte_t *ptep; + int idx, pid; + + if (addr < MAP_BASE) + return; + + addr &= (PAGE_MASK << 1); + if (cpu_has_mmid) { + write_c0_entryhi(addr); + } else { + pid = read_c0_entryhi() & cpu_asid_mask(¤t_cpu_data); + write_c0_entryhi(addr | pid); + } + pgdp = pgd_offset(&init_mm, addr); + mtc0_tlbw_hazard(); + tlb_probe(); + tlb_probe_hazard(); + p4dp = p4d_offset(pgdp, addr); + pudp = pud_offset(p4dp, addr); + pmdp = pmd_offset(pudp, addr); + idx = read_c0_index(); + + ptep = pte_offset_map(pmdp, addr); + +#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32) +#ifdef CONFIG_XPA + write_c0_entrylo0(pte_to_entrylo(ptep->pte_high)); + if (cpu_has_xpa) + writex_c0_entrylo0(ptep->pte_low & _PFNX_MASK); + ptep++; + write_c0_entrylo1(pte_to_entrylo(ptep->pte_high)); + if (cpu_has_xpa) + writex_c0_entrylo1(ptep->pte_low & _PFNX_MASK); +#else + write_c0_entrylo0(ptep->pte_high); + ptep++; + write_c0_entrylo1(ptep->pte_high); +#endif +#else + write_c0_entrylo0(pte_to_entrylo(pte_val(*ptep++))); + write_c0_entrylo1(pte_to_entrylo(pte_val(*ptep))); +#endif + mtc0_tlbw_hazard(); + if (idx < 0) + tlb_write_random(); + else + tlb_write_indexed(); + + tlbw_use_hazard(); +} +#endif + #include #endif /* _ASM_MMU_CONTEXT_H */ diff --git a/arch/mips/include/asm/smp-ops.h b/arch/mips/include/asm/smp-ops.h index 65618ff1280c9..5959c85fd7083 100644 --- a/arch/mips/include/asm/smp-ops.h +++ b/arch/mips/include/asm/smp-ops.h @@ -37,7 +37,7 @@ struct plat_smp_ops { #ifdef CONFIG_KEXEC void (*kexec_nonboot_cpu)(void); #endif -}; +} __no_randomize_layout; extern void register_smp_ops(const struct plat_smp_ops *ops); diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index ef73ba1e0ec10..5a49e03cb11d8 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -321,11 +321,11 @@ static void __init bootmem_init(void) panic("Incorrect memory mapping !!!"); if (max_pfn > PFN_DOWN(HIGHMEM_START)) { + max_low_pfn = PFN_DOWN(HIGHMEM_START); #ifdef CONFIG_HIGHMEM - highstart_pfn = PFN_DOWN(HIGHMEM_START); + highstart_pfn = max_low_pfn; highend_pfn = max_pfn; #else - max_low_pfn = PFN_DOWN(HIGHMEM_START); max_pfn = max_low_pfn; #endif } diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 046d51a454afc..2a5330ab08aa3 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -363,6 +363,7 @@ static struct work_registers build_get_work_registers(u32 **p) if (scratch_reg >= 0) { /* Save in CPU local C0_KScratch? */ UASM_i_MTC0(p, 1, c0_kscratch(), scratch_reg); + uasm_i_ehb(p); r.r1 = K0; r.r2 = K1; r.r3 = 1; diff --git a/arch/mips/pci/pci-generic.c b/arch/mips/pci/pci-generic.c index 95b00017886c2..d2d68bac3d250 100644 --- a/arch/mips/pci/pci-generic.c +++ b/arch/mips/pci/pci-generic.c @@ -46,3 +46,19 @@ void pcibios_fixup_bus(struct pci_bus *bus) { pci_read_bridge_bases(bus); } + +#ifdef pci_remap_iospace +int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr) +{ + unsigned long vaddr; + + if (res->start != 0) { + WARN_ONCE(1, "resource start address is not zero\n"); + return -ENODEV; + } + + vaddr = (unsigned long)ioremap(phys_addr, resource_size(res)); + set_io_port_base(vaddr); + return 0; +} +#endif diff --git a/block/sed-opal.c b/block/sed-opal.c index daafadbb88cae..402ade9c427ac 100644 --- a/block/sed-opal.c +++ b/block/sed-opal.c @@ -73,6 +73,7 @@ struct parsed_resp { struct opal_resp_tok toks[MAX_TOKS]; }; +/* Presumably DMA-able buffers must be cache-aligned */ struct opal_dev { bool supported; bool mbr_enabled; @@ -88,8 +89,8 @@ struct opal_dev { u64 lowest_lba; size_t pos; - u8 cmd[IO_BUFFER_LENGTH]; - u8 resp[IO_BUFFER_LENGTH]; + u8 cmd[IO_BUFFER_LENGTH] ____cacheline_aligned; + u8 resp[IO_BUFFER_LENGTH] ____cacheline_aligned; struct parsed_resp parsed; size_t prev_d_len; diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index a7da8ea7b3ed4..5e5d7d7ab15c5 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -174,6 +174,16 @@ config AHCI_DM816 If unsure, say N. +config AHCI_DWC + tristate "Synopsys DWC AHCI SATA support" + select SATA_HOST + select MFD_SYSCON if (MIPS_BAIKAL_T1 || COMPILE_TEST) + help + This option enables support for the Synopsys DWC AHCI SATA + controller implementation. + + If unsure, say N. + config AHCI_ST tristate "ST AHCI SATA support" depends on ARCH_STI diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index b8aebfb14e825..34623365d9a67 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_AHCI_BRCM) += ahci_brcm.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_CEVA) += ahci_ceva.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_DA850) += ahci_da850.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_DM816) += ahci_dm816.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_DWC) += ahci_dwc.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_MTK) += ahci_mtk.o libahci.o libahci_platform.o obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o libahci.o libahci_platform.o diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 812731e80f8e0..f1f0c59744150 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -648,7 +648,7 @@ static void ahci_pci_save_initial_config(struct pci_dev *pdev, { if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361) { dev_info(&pdev->dev, "JMB361 has only one port\n"); - hpriv->force_port_map = 1; + hpriv->saved_port_map = 1; } /* @@ -681,7 +681,7 @@ static void ahci_pci_init_controller(struct ata_host *host) mv = 2; else mv = 4; - port_mmio = __ahci_port_base(host, mv); + port_mmio = __ahci_port_base(hpriv, mv); writel(0, port_mmio + PORT_IRQ_MASK); diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 2e89499bd9c3d..13f11f1262d21 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -38,7 +38,6 @@ enum { AHCI_MAX_PORTS = 32, - AHCI_MAX_CLKS = 5, AHCI_MAX_SG = 168, /* hardware max is 64K */ AHCI_DMA_BOUNDARY = 0xffffffff, AHCI_MAX_CMDS = 32, @@ -139,7 +138,7 @@ enum { PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */ PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */ - PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */ + PORT_IRQ_DMPS = (1 << 7), /* mechanical presence status */ PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */ PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */ PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */ @@ -167,6 +166,8 @@ enum { PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */ PORT_CMD_FBSCP = (1 << 22), /* FBS Capable Port */ PORT_CMD_ESP = (1 << 21), /* External Sata Port */ + PORT_CMD_CPD = (1 << 20), /* Cold Presence Detection */ + PORT_CMD_MPSP = (1 << 19), /* Mechanical Presence Switch */ PORT_CMD_HPCP = (1 << 18), /* HotPlug Capable Port */ PORT_CMD_PMP = (1 << 17), /* PMP attached */ PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */ @@ -182,6 +183,10 @@ enum { PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ + /* PORT_CMD capabilities mask */ + PORT_CMD_CAP = PORT_CMD_HPCP | PORT_CMD_MPSP | + PORT_CMD_CPD | PORT_CMD_ESP | PORT_CMD_FBSCP, + /* PORT_FBS bits */ PORT_FBS_DWE_OFFSET = 16, /* FBS device with error offset */ PORT_FBS_ADO_OFFSET = 12, /* FBS active dev optimization offset */ @@ -325,7 +330,6 @@ struct ahci_port_priv { struct ahci_host_priv { /* Input fields */ unsigned int flags; /* AHCI_HFLAG_* */ - u32 force_port_map; /* force port map */ u32 mask_port_map; /* mask out particular bits */ void __iomem * mmio; /* bus-independent mem map */ @@ -336,12 +340,15 @@ struct ahci_host_priv { u32 saved_cap; /* saved initial cap */ u32 saved_cap2; /* saved initial cap2 */ u32 saved_port_map; /* saved initial port_map */ + u32 saved_port_cap[AHCI_MAX_PORTS]; /* saved port_cap */ u32 em_loc; /* enclosure management location */ u32 em_buf_sz; /* EM buffer size in byte */ u32 em_msg_type; /* EM message type */ u32 remapped_nvme; /* NVMe remapped device count */ bool got_runtime_pm; /* Did we do pm_runtime_get? */ - struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + unsigned int n_clks; + struct clk_bulk_data *clks; /* Optional */ + unsigned int f_rsts; struct reset_control *rsts; /* Optional */ struct regulator **target_pwrs; /* Optional */ struct regulator *ahci_regulator;/* Optional */ @@ -428,10 +435,9 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht); void ahci_error_handler(struct ata_port *ap); u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked); -static inline void __iomem *__ahci_port_base(struct ata_host *host, +static inline void __iomem *__ahci_port_base(struct ahci_host_priv *hpriv, unsigned int port_no) { - struct ahci_host_priv *hpriv = host->private_data; void __iomem *mmio = hpriv->mmio; return mmio + 0x100 + (port_no * 0x80); @@ -439,7 +445,9 @@ static inline void __iomem *__ahci_port_base(struct ata_host *host, static inline void __iomem *ahci_port_base(struct ata_port *ap) { - return __ahci_port_base(ap->host, ap->port_no); + struct ahci_host_priv *hpriv = ap->host->private_data; + + return __ahci_port_base(hpriv, ap->port_no); } static inline int ahci_nr_ports(u32 cap) diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c index 0e82766007128..389268c1ef456 100644 --- a/drivers/ata/ahci_da850.c +++ b/drivers/ata/ahci_da850.c @@ -163,7 +163,6 @@ static int ahci_da850_probe(struct platform_device *pdev) struct ahci_host_priv *hpriv; void __iomem *pwrdn_reg; struct resource *res; - struct clk *clk; u32 mpy; int rc; @@ -172,36 +171,28 @@ static int ahci_da850_probe(struct platform_device *pdev) return PTR_ERR(hpriv); /* - * Internally ahci_platform_get_resources() calls clk_get(dev, NULL) - * when trying to obtain the functional clock. This SATA controller - * uses two clocks for which we specify two connection ids. If we don't - * have the functional clock at this point - call clk_get() again with - * con_id = "fck". + * Internally ahci_platform_get_resources() calls the bulk clocks + * get method or falls back to using a single clk_get_optional(). + * This AHCI SATA controller uses two clocks: functional clock + * with "fck" connection id and external reference clock with + * "refclk" id. If we haven't got all of them re-try the clocks + * getting procedure with the explicitly specified ids. */ - if (!hpriv->clks[0]) { - clk = clk_get(dev, "fck"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - hpriv->clks[0] = clk; - } - - /* - * The second clock used by ahci-da850 is the external REFCLK. If we - * didn't get it from ahci_platform_get_resources(), let's try to - * specify the con_id in clk_get(). - */ - if (!hpriv->clks[1]) { - clk = clk_get(dev, "refclk"); - if (IS_ERR(clk)) { - dev_err(dev, "unable to obtain the reference clock"); - return -ENODEV; - } - - hpriv->clks[1] = clk; + if (hpriv->n_clks < 2) { + hpriv->clks = devm_kcalloc(dev, 2, sizeof(*hpriv->clks), GFP_KERNEL); + if (!hpriv->clks) + return -ENOMEM; + + hpriv->clks[0].id = "fck"; + hpriv->clks[1].id = "refclk"; + hpriv->n_clks = 2; + + rc = devm_clk_bulk_get(dev, hpriv->n_clks, hpriv->clks); + if (rc) + return rc; } - mpy = ahci_da850_calculate_mpy(clk_get_rate(hpriv->clks[1])); + mpy = ahci_da850_calculate_mpy(clk_get_rate(hpriv->clks[1].clk)); if (mpy == 0) { dev_err(dev, "invalid REFCLK multiplier value: 0x%x", mpy); return -EINVAL; diff --git a/drivers/ata/ahci_dm816.c b/drivers/ata/ahci_dm816.c index 8bec410416714..ec83ba8858060 100644 --- a/drivers/ata/ahci_dm816.c +++ b/drivers/ata/ahci_dm816.c @@ -69,12 +69,12 @@ static int ahci_dm816_phy_init(struct ahci_host_priv *hpriv, struct device *dev) * keep-alive clock and the external reference clock. We need the * rate of the latter to calculate the correct value of MPY bits. */ - if (!hpriv->clks[1]) { + if (hpriv->n_clks < 2) { dev_err(dev, "reference clock not supplied\n"); return -EINVAL; } - refclk_rate = clk_get_rate(hpriv->clks[1]); + refclk_rate = clk_get_rate(hpriv->clks[1].clk); if ((refclk_rate % 100) != 0) { dev_err(dev, "reference clock rate must be divisible by 100\n"); return -EINVAL; diff --git a/drivers/ata/ahci_dwc.c b/drivers/ata/ahci_dwc.c new file mode 100644 index 0000000000000..03a1af1c2b1f1 --- /dev/null +++ b/drivers/ata/ahci_dwc.c @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DWC AHCI SATA Platform driver + * + * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ahci.h" + +#define DRV_NAME "ahci-dwc" + +#define AHCI_DWC_FBS_PMPN_MAX 15 + +/* DWC AHCI SATA controller specific registers */ +#define AHCI_DWC_HOST_OOBR 0xbc +#define AHCI_DWC_HOST_OOB_WE BIT(31) +#define AHCI_DWC_HOST_CWMIN_MASK GENMASK(30, 24) +#define AHCI_DWC_HOST_CWMAX_MASK GENMASK(23, 16) +#define AHCI_DWC_HOST_CIMIN_MASK GENMASK(15, 8) +#define AHCI_DWC_HOST_CIMAX_MASK GENMASK(7, 0) + +#define AHCI_DWC_HOST_GPCR 0xd0 +#define AHCI_DWC_HOST_GPSR 0xd4 + +#define AHCI_DWC_HOST_TIMER1MS 0xe0 +#define AHCI_DWC_HOST_TIMV_MASK GENMASK(19, 0) + +#define AHCI_DWC_HOST_GPARAM1R 0xe8 +#define AHCI_DWC_HOST_ALIGN_M BIT(31) +#define AHCI_DWC_HOST_RX_BUFFER BIT(30) +#define AHCI_DWC_HOST_PHY_DATA_MASK GENMASK(29, 28) +#define AHCI_DWC_HOST_PHY_RST BIT(27) +#define AHCI_DWC_HOST_PHY_CTRL_MASK GENMASK(26, 21) +#define AHCI_DWC_HOST_PHY_STAT_MASK GENMASK(20, 15) +#define AHCI_DWC_HOST_LATCH_M BIT(14) +#define AHCI_DWC_HOST_PHY_TYPE_MASK GENMASK(13, 11) +#define AHCI_DWC_HOST_RET_ERR BIT(10) +#define AHCI_DWC_HOST_AHB_ENDIAN_MASK GENMASK(9, 8) +#define AHCI_DWC_HOST_S_HADDR BIT(7) +#define AHCI_DWC_HOST_M_HADDR BIT(6) +#define AHCI_DWC_HOST_S_HDATA_MASK GENMASK(5, 3) +#define AHCI_DWC_HOST_M_HDATA_MASK GENMASK(2, 0) + +#define AHCI_DWC_HOST_GPARAM2R 0xec +#define AHCI_DWC_HOST_FBS_MEM_S BIT(19) +#define AHCI_DWC_HOST_FBS_PMPN_MASK GENMASK(17, 16) +#define AHCI_DWC_HOST_FBS_SUP BIT(15) +#define AHCI_DWC_HOST_DEV_CP BIT(14) +#define AHCI_DWC_HOST_DEV_MP BIT(13) +#define AHCI_DWC_HOST_ENCODE_M BIT(12) +#define AHCI_DWC_HOST_RXOOB_CLK_M BIT(11) +#define AHCI_DWC_HOST_RXOOB_M BIT(10) +#define AHCI_DWC_HOST_TXOOB_M BIT(9) +#define AHCI_DWC_HOST_RXOOB_M BIT(10) +#define AHCI_DWC_HOST_RXOOB_CLK_MASK GENMASK(8, 0) + +#define AHCI_DWC_HOST_PPARAMR 0xf0 +#define AHCI_DWC_HOST_TX_MEM_M BIT(11) +#define AHCI_DWC_HOST_TX_MEM_S BIT(10) +#define AHCI_DWC_HOST_RX_MEM_M BIT(9) +#define AHCI_DWC_HOST_RX_MEM_S BIT(8) +#define AHCI_DWC_HOST_TXFIFO_DEPTH GENMASK(7, 4) +#define AHCI_DWC_HOST_RXFIFO_DEPTH GENMASK(3, 0) + +#define AHCI_DWC_HOST_TESTR 0xf4 +#define AHCI_DWC_HOST_PSEL_MASK GENMASK(18, 16) +#define AHCI_DWC_HOST_TEST_IF BIT(0) + +#define AHCI_DWC_HOST_VERSIONR 0xf8 +#define AHCI_DWC_HOST_IDR 0xfc + +#define AHCI_DWC_PORT_DMACR 0x70 +#define AHCI_DWC_PORT_RXABL_MASK GENMASK(15, 12) +#define AHCI_DWC_PORT_TXABL_MASK GENMASK(11, 8) +#define AHCI_DWC_PORT_RXTS_MASK GENMASK(7, 4) +#define AHCI_DWC_PORT_TXTS_MASK GENMASK(3, 0) +#define AHCI_DWC_PORT_PHYCR 0x74 +#define AHCI_DWC_PORT_PHYSR 0x78 + +/* Baikal-T1 AHCI SATA specific registers */ +#define AHCI_BT1_HOST_PHYCR AHCI_DWC_HOST_GPCR +#define AHCI_BT1_HOST_MPLM_MASK GENMASK(29, 23) +#define AHCI_BT1_HOST_LOSDT_MASK GENMASK(22, 20) +#define AHCI_BT1_HOST_CRR BIT(19) +#define AHCI_BT1_HOST_CRW BIT(18) +#define AHCI_BT1_HOST_CRCD BIT(17) +#define AHCI_BT1_HOST_CRCA BIT(16) +#define AHCI_BT1_HOST_CRDI_MASK GENMASK(15, 0) + +#define AHCI_BT1_HOST_PHYSR AHCI_DWC_HOST_GPSR +#define AHCI_BT1_HOST_CRA BIT(16) +#define AHCI_BT1_HOST_CRDO_MASK GENMASK(15, 0) + +struct ahci_dwc_plat_data { + unsigned int pflags; + unsigned int hflags; + int (*init)(struct ahci_host_priv *hpriv); + int (*reinit)(struct ahci_host_priv *hpriv); + void (*clear)(struct ahci_host_priv *hpriv); +}; + +struct ahci_dwc_host_priv { + const struct ahci_dwc_plat_data *pdata; + struct platform_device *pdev; + + u32 timv; + u32 dmacr[AHCI_MAX_PORTS]; +}; + +static int ahci_bt1_init(struct ahci_host_priv *hpriv) +{ + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + int ret; + + /* APB, application and reference clocks are required */ + if (!ahci_platform_find_clk(hpriv, "pclk") || + !ahci_platform_find_clk(hpriv, "aclk") || + !ahci_platform_find_clk(hpriv, "ref")) { + dev_err(&dpriv->pdev->dev, "No system clocks specified\n"); + return -EINVAL; + } + + /* + * Fully reset the SATA AXI and ref clocks domain to ensure the state + * machine is working from scratch especially if the reference clocks + * source has been changed. + */ + ret = ahci_platform_assert_rsts(hpriv); + if (ret) { + dev_err(&dpriv->pdev->dev, "Couldn't assert the resets\n"); + return ret; + } + + ret = ahci_platform_deassert_rsts(hpriv); + if (ret) { + dev_err(&dpriv->pdev->dev, "Couldn't de-assert the resets\n"); + return ret; + } + + return 0; +} + +static struct ahci_host_priv *ahci_dwc_get_resources(struct platform_device *pdev) +{ + struct ahci_dwc_host_priv *dpriv; + struct ahci_host_priv *hpriv; + + dpriv = devm_kzalloc(&pdev->dev, sizeof(*dpriv), GFP_KERNEL); + if (!dpriv) + return ERR_PTR(-ENOMEM); + + dpriv->pdev = pdev; + dpriv->pdata = device_get_match_data(&pdev->dev); + if (!dpriv->pdata) + return ERR_PTR(-EINVAL); + + hpriv = ahci_platform_get_resources(pdev, dpriv->pdata->pflags); + if (IS_ERR(hpriv)) + return hpriv; + + hpriv->flags |= dpriv->pdata->hflags; + hpriv->plat_data = (void *)dpriv; + + return hpriv; +} + +static void ahci_dwc_check_cap(struct ahci_host_priv *hpriv) +{ + unsigned long port_map = hpriv->saved_port_map | hpriv->mask_port_map; + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + bool dev_mp, dev_cp, fbs_sup; + unsigned int fbs_pmp; + u32 param; + int i; + + param = readl(hpriv->mmio + AHCI_DWC_HOST_GPARAM2R); + dev_mp = !!(param & AHCI_DWC_HOST_DEV_MP); + dev_cp = !!(param & AHCI_DWC_HOST_DEV_CP); + fbs_sup = !!(param & AHCI_DWC_HOST_FBS_SUP); + fbs_pmp = 5 * FIELD_GET(AHCI_DWC_HOST_FBS_PMPN_MASK, param); + + if (!dev_mp && hpriv->saved_cap & HOST_CAP_MPS) { + dev_warn(&dpriv->pdev->dev, "MPS is unsupported\n"); + hpriv->saved_cap &= ~HOST_CAP_MPS; + } + + + if (fbs_sup && fbs_pmp < AHCI_DWC_FBS_PMPN_MAX) { + dev_warn(&dpriv->pdev->dev, "PMPn is limited up to %u ports\n", + fbs_pmp); + } + + for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) { + if (!dev_mp && hpriv->saved_port_cap[i] & PORT_CMD_MPSP) { + dev_warn(&dpriv->pdev->dev, "MPS incapable port %d\n", i); + hpriv->saved_port_cap[i] &= ~PORT_CMD_MPSP; + } + + if (!dev_cp && hpriv->saved_port_cap[i] & PORT_CMD_CPD) { + dev_warn(&dpriv->pdev->dev, "CPD incapable port %d\n", i); + hpriv->saved_port_cap[i] &= ~PORT_CMD_CPD; + } + + if (!fbs_sup && hpriv->saved_port_cap[i] & PORT_CMD_FBSCP) { + dev_warn(&dpriv->pdev->dev, "FBS incapable port %d\n", i); + hpriv->saved_port_cap[i] &= ~PORT_CMD_FBSCP; + } + } +} + +static void ahci_dwc_init_timer(struct ahci_host_priv *hpriv) +{ + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + unsigned long rate; + struct clk *aclk; + u32 cap, cap2; + + /* 1ms tick is generated only for the CCC or DevSleep features */ + cap = readl(hpriv->mmio + HOST_CAP); + cap2 = readl(hpriv->mmio + HOST_CAP2); + if (!(cap & HOST_CAP_CCC) && !(cap2 & HOST_CAP2_SDS)) + return; + + /* + * Tick is generated based on the AXI/AHB application clocks signal + * so we need to be sure in the clock we are going to use. + */ + aclk = ahci_platform_find_clk(hpriv, "aclk"); + if (!aclk) + return; + + /* 1ms timer interval is set as TIMV = AMBA_FREQ[MHZ] * 1000 */ + dpriv->timv = readl(hpriv->mmio + AHCI_DWC_HOST_TIMER1MS); + dpriv->timv = FIELD_GET(AHCI_DWC_HOST_TIMV_MASK, dpriv->timv); + rate = clk_get_rate(aclk) / 1000UL; + if (rate == dpriv->timv) + return; + + dev_info(&dpriv->pdev->dev, "Update CCC/DevSlp timer for Fapp %lu MHz\n", + rate / 1000UL); + dpriv->timv = FIELD_PREP(AHCI_DWC_HOST_TIMV_MASK, rate); + writel(dpriv->timv, hpriv->mmio + AHCI_DWC_HOST_TIMER1MS); +} + +static int ahci_dwc_init_dmacr(struct ahci_host_priv *hpriv) +{ + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + struct device_node *child; + void __iomem *port_mmio; + u32 port, dmacr, ts; + + /* + * Update the DMA Tx/Rx transaction sizes in accordance with the + * platform setup. Note values exceeding maximal or minimal limits will + * be automatically clamped. Also note the register isn't affected by + * the HBA global reset so we can freely initialize it once until the + * next system reset. + */ + for_each_child_of_node(dpriv->pdev->dev.of_node, child) { + if (!of_device_is_available(child)) + continue; + + if (of_property_read_u32(child, "reg", &port)) { + of_node_put(child); + return -EINVAL; + } + + port_mmio = __ahci_port_base(hpriv, port); + dmacr = readl(port_mmio + AHCI_DWC_PORT_DMACR); + + if (!of_property_read_u32(child, "snps,tx-ts-max", &ts)) { + ts = ilog2(ts); + dmacr &= ~AHCI_DWC_PORT_TXTS_MASK; + dmacr |= FIELD_PREP(AHCI_DWC_PORT_TXTS_MASK, ts); + } + + if (!of_property_read_u32(child, "snps,rx-ts-max", &ts)) { + ts = ilog2(ts); + dmacr &= ~AHCI_DWC_PORT_RXTS_MASK; + dmacr |= FIELD_PREP(AHCI_DWC_PORT_RXTS_MASK, ts); + } + + writel(dmacr, port_mmio + AHCI_DWC_PORT_DMACR); + dpriv->dmacr[port] = dmacr; + } + + return 0; +} + +static int ahci_dwc_init_host(struct ahci_host_priv *hpriv) +{ + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + int rc; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + if (dpriv->pdata->init) { + rc = dpriv->pdata->init(hpriv); + if (rc) + goto err_disable_resources; + } + + ahci_dwc_check_cap(hpriv); + + ahci_dwc_init_timer(hpriv); + + rc = ahci_dwc_init_dmacr(hpriv); + if (rc) + goto err_clear_platform; + + return 0; + +err_clear_platform: + if (dpriv->pdata->clear) + dpriv->pdata->clear(hpriv); + +err_disable_resources: + ahci_platform_disable_resources(hpriv); + + return rc; +} + +static int ahci_dwc_reinit_host(struct ahci_host_priv *hpriv) +{ + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + unsigned long port_map = hpriv->port_map; + void __iomem *port_mmio; + int i, rc; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + if (dpriv->pdata->reinit) { + rc = dpriv->pdata->reinit(hpriv); + if (rc) + goto err_disable_resources; + } + + writel(dpriv->timv, hpriv->mmio + AHCI_DWC_HOST_TIMER1MS); + + for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) { + port_mmio = __ahci_port_base(hpriv, i); + writel(dpriv->dmacr[i], port_mmio + AHCI_DWC_PORT_DMACR); + } + + return 0; + +err_disable_resources: + ahci_platform_disable_resources(hpriv); + + return rc; +} + +static void ahci_dwc_clear_host(struct ahci_host_priv *hpriv) +{ + struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; + + if (dpriv->pdata->clear) + dpriv->pdata->clear(hpriv); + + ahci_platform_disable_resources(hpriv); +} + +static void ahci_dwc_stop_host(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + + ahci_dwc_clear_host(hpriv); +} + +static struct ata_port_operations ahci_dwc_port_ops = { + .inherits = &ahci_platform_ops, + .host_stop = ahci_dwc_stop_host, +}; + +static const struct ata_port_info ahci_dwc_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_dwc_port_ops, +}; + +static struct scsi_host_template ahci_dwc_scsi_info = { + AHCI_SHT(DRV_NAME), +}; + +static int ahci_dwc_probe(struct platform_device *pdev) +{ + struct ahci_host_priv *hpriv; + int rc; + + hpriv = ahci_dwc_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + rc = ahci_dwc_init_host(hpriv); + if (rc) + return rc; + + rc = ahci_platform_init_host(pdev, hpriv, &ahci_dwc_port_info, + &ahci_dwc_scsi_info); + if (rc) + goto err_clear_host; + + return 0; + +err_clear_host: + ahci_dwc_clear_host(hpriv); + + return rc; +} + +#ifdef CONFIG_PM_SLEEP +static int ahci_dwc_suspend(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + + rc = ahci_platform_suspend_host(dev); + if (rc) + return rc; + + ahci_dwc_clear_host(hpriv); + + return 0; +} + +static int ahci_dwc_resume(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + + rc = ahci_dwc_reinit_host(hpriv); + if (rc) + return rc; + + return ahci_platform_resume_host(dev); +} +#endif + +static DEFINE_SIMPLE_DEV_PM_OPS(ahci_dwc_pm_ops, ahci_dwc_suspend, + ahci_dwc_resume); + +static struct ahci_dwc_plat_data ahci_dwc_plat = { + .pflags = AHCI_PLATFORM_GET_RESETS, +}; + +static struct ahci_dwc_plat_data ahci_bt1_plat = { + .pflags = AHCI_PLATFORM_GET_RESETS | AHCI_PLATFORM_RST_TRIGGER, + .init = ahci_bt1_init, +}; + +static const struct of_device_id ahci_dwc_of_match[] = { + { .compatible = "snps,dwc-ahci", &ahci_dwc_plat }, + { .compatible = "snps,spear-ahci", &ahci_dwc_plat }, + { .compatible = "baikal,bt1-ahci", &ahci_bt1_plat }, + {}, +}; +MODULE_DEVICE_TABLE(of, ahci_dwc_of_match); + +static struct platform_driver ahci_dwc_driver = { + .probe = ahci_dwc_probe, + .remove = ata_platform_remove_one, + .shutdown = ahci_platform_shutdown, + .driver = { + .name = DRV_NAME, + .of_match_table = ahci_dwc_of_match, + .pm = &ahci_dwc_pm_ops, + }, +}; +module_platform_driver(ahci_dwc_driver); + +MODULE_DESCRIPTION("DWC AHCI SATA platform driver"); +MODULE_AUTHOR("Serge Semin "); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/ahci_mtk.c b/drivers/ata/ahci_mtk.c index d9b08ae7c3b22..8f8539952af11 100644 --- a/drivers/ata/ahci_mtk.c +++ b/drivers/ata/ahci_mtk.c @@ -118,8 +118,6 @@ static int mtk_ahci_parse_property(struct ahci_host_priv *hpriv, SYS_CFG_SATA_EN); } - of_property_read_u32(np, "ports-implemented", &hpriv->force_port_map); - return 0; } diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 3aab2e3d57f33..052eaa30d2624 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -56,9 +56,6 @@ static int ahci_probe(struct platform_device *pdev) if (rc) return rc; - of_property_read_u32(dev->of_node, - "ports-implemented", &hpriv->force_port_map); - if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci")) hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ; @@ -83,9 +80,7 @@ static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, static const struct of_device_id ahci_of_match[] = { { .compatible = "generic-ahci", }, /* Keep the following compatibles for device tree compatibility */ - { .compatible = "snps,spear-ahci", }, { .compatible = "ibm,476gtr-ahci", }, - { .compatible = "snps,dwc-ahci", }, { .compatible = "hisilicon,hisi-ahci", }, { .compatible = "cavium,octeon-7130-ahci", }, {}, diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index c268264c2129c..e010f2fd1fa2d 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -168,9 +168,6 @@ static int st_ahci_probe(struct platform_device *pdev) st_ahci_configure_oob(hpriv->mmio); - of_property_read_u32(dev->of_node, - "ports-implemented", &hpriv->force_port_map); - err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, &ahci_platform_sht); if (err) { diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 395772fa39432..d5f5f0645d628 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -16,6 +16,7 @@ * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf */ +#include #include #include #include @@ -425,17 +426,28 @@ static ssize_t ahci_show_em_supported(struct device *dev, void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) { void __iomem *mmio = hpriv->mmio; - u32 cap, cap2, vers, port_map; + void __iomem *port_mmio; + unsigned long port_map; + u32 cap, cap2, vers; int i; /* make sure AHCI mode is enabled before accessing CAP */ ahci_enable_ahci(mmio); - /* Values prefixed with saved_ are written back to host after - * reset. Values without are used for driver operation. + /* + * Values prefixed with saved_ are written back to the HBA and ports + * registers after reset. Values without are used for driver operation. + */ + + /* + * Override HW-init HBA capability fields with the platform-specific + * values. The rest of the HBA capabilities are defined as Read-only + * and can't be modified in CSR anyway. */ - hpriv->saved_cap = cap = readl(mmio + HOST_CAP); - hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL); + cap = readl(mmio + HOST_CAP); + if (hpriv->saved_cap) + cap = (cap & ~(HOST_CAP_SSS | HOST_CAP_MPS)) | hpriv->saved_cap; + hpriv->saved_cap = cap; /* CAP2 register is only defined for AHCI 1.2 and later */ vers = readl(mmio + HOST_VERSION); @@ -499,15 +511,18 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) cap &= ~HOST_CAP_SXS; } - if (hpriv->force_port_map && port_map != hpriv->force_port_map) { - dev_info(dev, "forcing port_map 0x%x -> 0x%x\n", - port_map, hpriv->force_port_map); - port_map = hpriv->force_port_map; + /* Override the HBA ports mapping if the platform needs it */ + port_map = readl(mmio + HOST_PORTS_IMPL); + if (hpriv->saved_port_map && port_map != hpriv->saved_port_map) { + dev_info(dev, "forcing port_map 0x%lx -> 0x%x\n", + port_map, hpriv->saved_port_map); + port_map = hpriv->saved_port_map; + } else { hpriv->saved_port_map = port_map; } if (hpriv->mask_port_map) { - dev_warn(dev, "masking port_map 0x%x -> 0x%x\n", + dev_warn(dev, "masking port_map 0x%lx -> 0x%lx\n", port_map, port_map & hpriv->mask_port_map); port_map &= hpriv->mask_port_map; @@ -526,7 +541,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) */ if (map_ports > ahci_nr_ports(cap)) { dev_warn(dev, - "implemented port map (0x%x) contains more ports than nr_ports (%u), using nr_ports\n", + "implemented port map (0x%lx) contains more ports than nr_ports (%u), using nr_ports\n", port_map, ahci_nr_ports(cap)); port_map = 0; } @@ -535,16 +550,30 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) /* fabricate port_map from cap.nr_ports for < AHCI 1.3 */ if (!port_map && vers < 0x10300) { port_map = (1 << ahci_nr_ports(cap)) - 1; - dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map); + dev_warn(dev, "forcing PORTS_IMPL to 0x%lx\n", port_map); /* write the fixed up value to the PI register */ hpriv->saved_port_map = port_map; } + /* + * Preserve the ports capabilities defined by the platform. Note there + * is no need in storing the rest of the P#.CMD fields since they are + * volatile. + */ + for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) { + if (hpriv->saved_port_cap[i]) + continue; + + port_mmio = __ahci_port_base(hpriv, i); + hpriv->saved_port_cap[i] = + readl(port_mmio + PORT_CMD) & PORT_CMD_CAP; + } + /* record values to use during operation */ hpriv->cap = cap; hpriv->cap2 = cap2; - hpriv->version = readl(mmio + HOST_VERSION); + hpriv->version = vers; hpriv->port_map = port_map; if (!hpriv->start_engine) @@ -570,13 +599,21 @@ EXPORT_SYMBOL_GPL(ahci_save_initial_config); static void ahci_restore_initial_config(struct ata_host *host) { struct ahci_host_priv *hpriv = host->private_data; + unsigned long port_map = hpriv->port_map; void __iomem *mmio = hpriv->mmio; + void __iomem *port_mmio; + int i; writel(hpriv->saved_cap, mmio + HOST_CAP); if (hpriv->saved_cap2) writel(hpriv->saved_cap2, mmio + HOST_CAP2); writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL); (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ + + for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) { + port_mmio = __ahci_port_base(hpriv, i); + writel(hpriv->saved_port_cap[i], port_mmio + PORT_CMD); + } } static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg) diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 0910441321f72..bb13f86b4cf30 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -8,6 +8,7 @@ * Anton Vorontsov */ +#include #include #include #include @@ -94,31 +95,41 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) EXPORT_SYMBOL_GPL(ahci_platform_disable_phys); /** - * ahci_platform_enable_clks - Enable platform clocks + * ahci_platform_find_clk - Find platform clock * @hpriv: host private area to store config values + * @con_id: clock connection ID * - * This function enables all the clks found in hpriv->clks, starting at - * index 0. If any clk fails to enable it disables all the clks already - * enabled in reverse order, and then returns an error. + * This function returns a pointer to the clock descriptor of the clock with + * the passed ID. * * RETURNS: - * 0 on success otherwise a negative error code + * Pointer to the clock descriptor on success otherwise NULL */ -int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) +struct clk *ahci_platform_find_clk(struct ahci_host_priv *hpriv, const char *con_id) { - int c, rc; + int i; - for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { - rc = clk_prepare_enable(hpriv->clks[c]); - if (rc) - goto disable_unprepare_clk; + for (i = 0; i < hpriv->n_clks; i++) { + if (!strcmp(hpriv->clks[i].id, con_id)) + return hpriv->clks[i].clk; } - return 0; -disable_unprepare_clk: - while (--c >= 0) - clk_disable_unprepare(hpriv->clks[c]); - return rc; + return NULL; +} +EXPORT_SYMBOL_GPL(ahci_platform_find_clk); + +/** + * ahci_platform_enable_clks - Enable platform clocks + * @hpriv: host private area to store config values + * + * This function enables all the clks found for the AHCI device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) +{ + return clk_bulk_prepare_enable(hpriv->n_clks, hpriv->clks); } EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); @@ -126,19 +137,54 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); * ahci_platform_disable_clks - Disable platform clocks * @hpriv: host private area to store config values * - * This function disables all the clks found in hpriv->clks, in reverse - * order of ahci_platform_enable_clks (starting at the end of the array). + * This function disables all the clocks enabled before + * (bulk-clocks-disable function is supposed to do that in reverse + * from the enabling procedure order). */ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) { - int c; - - for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) - if (hpriv->clks[c]) - clk_disable_unprepare(hpriv->clks[c]); + clk_bulk_disable_unprepare(hpriv->n_clks, hpriv->clks); } EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); +/** + * ahci_platform_deassert_rsts - Deassert/trigger platform resets + * @hpriv: host private area to store config values + * + * This function deasserts or triggers all the reset lines found for + * the AHCI device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_deassert_rsts(struct ahci_host_priv *hpriv) +{ + if (hpriv->f_rsts & AHCI_PLATFORM_RST_TRIGGER) + return reset_control_reset(hpriv->rsts); + + return reset_control_deassert(hpriv->rsts); +} +EXPORT_SYMBOL_GPL(ahci_platform_deassert_rsts); + +/** + * ahci_platform_assert_rsts - Assert/rearm platform resets + * @hpriv: host private area to store config values + * + * This function asserts or rearms (for self-deasserting resets) all + * the reset controls found for the AHCI device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_assert_rsts(struct ahci_host_priv *hpriv) +{ + if (hpriv->f_rsts & AHCI_PLATFORM_RST_TRIGGER) + return reset_control_rearm(hpriv->rsts); + + return reset_control_assert(hpriv->rsts); +} +EXPORT_SYMBOL_GPL(ahci_platform_assert_rsts); + /** * ahci_platform_enable_regulators - Enable regulators * @hpriv: host private area to store config values @@ -236,18 +282,18 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) if (rc) goto disable_regulator; - rc = reset_control_deassert(hpriv->rsts); + rc = ahci_platform_deassert_rsts(hpriv); if (rc) goto disable_clks; rc = ahci_platform_enable_phys(hpriv); if (rc) - goto disable_resets; + goto disable_rsts; return 0; -disable_resets: - reset_control_assert(hpriv->rsts); +disable_rsts: + ahci_platform_assert_rsts(hpriv); disable_clks: ahci_platform_disable_clks(hpriv); @@ -274,7 +320,7 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) { ahci_platform_disable_phys(hpriv); - reset_control_assert(hpriv->rsts); + ahci_platform_assert_rsts(hpriv); ahci_platform_disable_clks(hpriv); @@ -292,8 +338,6 @@ static void ahci_platform_put_resources(struct device *dev, void *res) pm_runtime_disable(dev); } - for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) - clk_put(hpriv->clks[c]); /* * The regulators are tied to child node device and not to the * SATA device itself. So we can't use devm for automatically @@ -363,6 +407,34 @@ static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port, return rc; } +static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv, + struct device *dev) +{ + struct device_node *child; + u32 port; + + if (!of_property_read_u32(dev->of_node, "hba-cap", &hpriv->saved_cap)) + hpriv->saved_cap &= (HOST_CAP_SSS | HOST_CAP_MPS); + + of_property_read_u32(dev->of_node, + "ports-implemented", &hpriv->saved_port_map); + + for_each_child_of_node(dev->of_node, child) { + if (!of_device_is_available(child)) + continue; + + if (of_property_read_u32(child, "reg", &port)) { + of_node_put(child); + return -EINVAL; + } + + if (!of_property_read_u32(child, "hba-port-cap", &hpriv->saved_port_cap[port])) + hpriv->saved_port_cap[port] &= PORT_CMD_CAP; + } + + return 0; +} + /** * ahci_platform_get_resources - Get platform resources * @pdev: platform device to get resources for @@ -374,8 +446,8 @@ static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port, * 1) mmio registers (IORESOURCE_MEM 0, mandatory) * 2) regulator for controlling the targets power (optional) * regulator for controlling the AHCI controller (optional) - * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, - * or for non devicetree enabled platforms a single clock + * 3) all clocks specified in the devicetree node, or a single + * clock for non-OF platforms (optional) * 4) resets, if flags has AHCI_PLATFORM_GET_RESETS (optional) * 5) phys (optional) * @@ -385,11 +457,10 @@ static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port, struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, unsigned int flags) { + int child_nodes, rc = -ENOMEM, enabled_ports = 0; struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; - struct clk *clk; struct device_node *child; - int i, enabled_ports = 0, rc = -ENOMEM, child_nodes; u32 mask_port_map = 0; if (!devres_open_group(dev, NULL, GFP_KERNEL)) @@ -402,32 +473,51 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, devres_add(dev, hpriv); - hpriv->mmio = devm_ioremap_resource(dev, - platform_get_resource(pdev, IORESOURCE_MEM, 0)); + /* + * If the DT provided an "ahci" named resource, use it. Otherwise, + * fallback to using the default first resource for the device node. + */ + if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "ahci")) + hpriv->mmio = devm_platform_ioremap_resource_byname(pdev, "ahci"); + else + hpriv->mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hpriv->mmio)) { rc = PTR_ERR(hpriv->mmio); goto err_out; } - for (i = 0; i < AHCI_MAX_CLKS; i++) { + /* + * Bulk clocks getting procedure can fail to find any clock due to + * running on a non-OF platform or due to the clocks being defined in + * bypass of the DT firmware (like da850, spear13xx). In that case we + * fallback to getting a single clock source right from the dev clocks + * list. + */ + rc = devm_clk_bulk_get_all(dev, &hpriv->clks); + if (rc < 0) + goto err_out; + + if (rc > 0) { + /* Got clocks in bulk */ + hpriv->n_clks = rc; + } else { /* - * For now we must use clk_get(dev, NULL) for the first clock, - * because some platforms (da850, spear13xx) are not yet - * converted to use devicetree for clocks. For new platforms - * this is equivalent to of_clk_get(dev->of_node, 0). + * No clock bulk found: fallback to manually getting + * the optional clock. */ - if (i == 0) - clk = clk_get(dev, NULL); - else - clk = of_clk_get(dev->of_node, i); - - if (IS_ERR(clk)) { - rc = PTR_ERR(clk); - if (rc == -EPROBE_DEFER) - goto err_out; - break; + hpriv->clks = devm_kzalloc(dev, sizeof(*hpriv->clks), GFP_KERNEL); + if (!hpriv->clks) { + rc = -ENOMEM; + goto err_out; + } + hpriv->clks->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(hpriv->clks->clk)) { + rc = PTR_ERR(hpriv->clks->clk); + goto err_out; + } else if (hpriv->clks->clk) { + hpriv->clks->id = __clk_get_name(hpriv->clks->clk); + hpriv->n_clks = 1; } - hpriv->clks[i] = clk; } hpriv->ahci_regulator = devm_regulator_get(dev, "ahci"); @@ -449,16 +539,28 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, rc = PTR_ERR(hpriv->rsts); goto err_out; } + + hpriv->f_rsts = flags & AHCI_PLATFORM_RST_TRIGGER; } - hpriv->nports = child_nodes = of_get_child_count(dev->of_node); + /* + * Too many sub-nodes most likely means having something wrong with + * the firmware. + */ + child_nodes = of_get_child_count(dev->of_node); + if (child_nodes > AHCI_MAX_PORTS) { + rc = -EINVAL; + goto err_out; + } /* * If no sub-node was found, we still need to set nports to * one in order to be able to use the * ahci_platform_[en|dis]able_[phys|regulators] functions. */ - if (!child_nodes) + if (child_nodes) + hpriv->nports = child_nodes; + else hpriv->nports = 1; hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL); @@ -540,6 +642,15 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, if (rc == -EPROBE_DEFER) goto err_out; } + + /* + * Retrieve firmware-specific flags which then will be used to set + * the HW-init fields of HBA and its ports + */ + rc = ahci_platform_get_firmware(hpriv, dev); + if (rc) + goto err_out; + pm_runtime_enable(dev); pm_runtime_get_sync(dev); hpriv->got_runtime_pm = true; diff --git a/drivers/bus/bt1-apb.c b/drivers/bus/bt1-apb.c index b25ff941e7c7f..63b1b4a76671d 100644 --- a/drivers/bus/bt1-apb.c +++ b/drivers/bus/bt1-apb.c @@ -175,10 +175,9 @@ static int bt1_apb_request_rst(struct bt1_apb *apb) int ret; apb->prst = devm_reset_control_get_optional_exclusive(apb->dev, "prst"); - if (IS_ERR(apb->prst)) { - dev_warn(apb->dev, "Couldn't get reset control line\n"); - return PTR_ERR(apb->prst); - } + if (IS_ERR(apb->prst)) + return dev_err_probe(apb->dev, PTR_ERR(apb->prst), + "Couldn't get reset control line\n"); ret = reset_control_deassert(apb->prst); if (ret) @@ -199,10 +198,9 @@ static int bt1_apb_request_clk(struct bt1_apb *apb) int ret; apb->pclk = devm_clk_get(apb->dev, "pclk"); - if (IS_ERR(apb->pclk)) { - dev_err(apb->dev, "Couldn't get APB clock descriptor\n"); - return PTR_ERR(apb->pclk); - } + if (IS_ERR(apb->pclk)) + return dev_err_probe(apb->dev, PTR_ERR(apb->pclk), + "Couldn't get APB clock descriptor\n"); ret = clk_prepare_enable(apb->pclk); if (ret) { diff --git a/drivers/bus/bt1-axi.c b/drivers/bus/bt1-axi.c index e7a6744acc7b8..70e49a6e53746 100644 --- a/drivers/bus/bt1-axi.c +++ b/drivers/bus/bt1-axi.c @@ -135,10 +135,9 @@ static int bt1_axi_request_rst(struct bt1_axi *axi) int ret; axi->arst = devm_reset_control_get_optional_exclusive(axi->dev, "arst"); - if (IS_ERR(axi->arst)) { - dev_warn(axi->dev, "Couldn't get reset control line\n"); - return PTR_ERR(axi->arst); - } + if (IS_ERR(axi->arst)) + return dev_err_probe(axi->dev, PTR_ERR(axi->arst), + "Couldn't get reset control line\n"); ret = reset_control_deassert(axi->arst); if (ret) @@ -159,10 +158,9 @@ static int bt1_axi_request_clk(struct bt1_axi *axi) int ret; axi->aclk = devm_clk_get(axi->dev, "aclk"); - if (IS_ERR(axi->aclk)) { - dev_err(axi->dev, "Couldn't get AXI Interconnect clock\n"); - return PTR_ERR(axi->aclk); - } + if (IS_ERR(axi->aclk)) + return dev_err_probe(axi->dev, PTR_ERR(axi->aclk), + "Couldn't get AXI Interconnect clock\n"); ret = clk_prepare_enable(axi->aclk); if (ret) { diff --git a/drivers/clk/baikal-t1/Kconfig b/drivers/clk/baikal-t1/Kconfig index 03102f1094bca..f0b1868303243 100644 --- a/drivers/clk/baikal-t1/Kconfig +++ b/drivers/clk/baikal-t1/Kconfig @@ -29,7 +29,6 @@ config CLK_BT1_CCU_PLL config CLK_BT1_CCU_DIV bool "Baikal-T1 CCU Dividers support" - select RESET_CONTROLLER select MFD_SYSCON default MIPS_BAIKAL_T1 help @@ -39,4 +38,15 @@ config CLK_BT1_CCU_DIV either gateable or ungateable. Some of the CCU dividers can be as well used to reset the domains they're supplying clock to. +config CLK_BT1_CCU_RST + bool "Baikal-T1 CCU Resets support" + select RESET_CONTROLLER + select MFD_SYSCON + default MIPS_BAIKAL_T1 + help + Enable this to support the CCU reset blocks responsible for the + AXI-bus and some subsystems reset. These are mainly the + self-deasserted reset controls but there are several lines which + can be directly asserted/de-asserted (PCIe and DDR sub-domains). + endif diff --git a/drivers/clk/baikal-t1/Makefile b/drivers/clk/baikal-t1/Makefile index b3b9590b95ed9..9c3637de94070 100644 --- a/drivers/clk/baikal-t1/Makefile +++ b/drivers/clk/baikal-t1/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CLK_BT1_CCU_PLL) += ccu-pll.o clk-ccu-pll.o obj-$(CONFIG_CLK_BT1_CCU_DIV) += ccu-div.o clk-ccu-div.o +obj-$(CONFIG_CLK_BT1_CCU_RST) += ccu-rst.o diff --git a/drivers/clk/baikal-t1/ccu-div.c b/drivers/clk/baikal-t1/ccu-div.c index 4062092d67f90..8d5fc7158f33f 100644 --- a/drivers/clk/baikal-t1/ccu-div.c +++ b/drivers/clk/baikal-t1/ccu-div.c @@ -34,9 +34,9 @@ #define CCU_DIV_CTL_CLKDIV_MASK(_width) \ GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD) #define CCU_DIV_CTL_LOCK_SHIFTED BIT(27) +#define CCU_DIV_CTL_GATE_REF_BUF BIT(28) #define CCU_DIV_CTL_LOCK_NORMAL BIT(31) -#define CCU_DIV_RST_DELAY_US 1 #define CCU_DIV_LOCK_CHECK_RETRIES 50 #define CCU_DIV_CLKDIV_MIN 0 @@ -170,6 +170,40 @@ static int ccu_div_gate_is_enabled(struct clk_hw *hw) return !!(val & CCU_DIV_CTL_EN); } +static int ccu_div_buf_enable(struct clk_hw *hw) +{ + struct ccu_div *div = to_ccu_div(hw); + unsigned long flags; + + spin_lock_irqsave(&div->lock, flags); + regmap_update_bits(div->sys_regs, div->reg_ctl, + CCU_DIV_CTL_GATE_REF_BUF, 0); + spin_unlock_irqrestore(&div->lock, flags); + + return 0; +} + +static void ccu_div_buf_disable(struct clk_hw *hw) +{ + struct ccu_div *div = to_ccu_div(hw); + unsigned long flags; + + spin_lock_irqsave(&div->lock, flags); + regmap_update_bits(div->sys_regs, div->reg_ctl, + CCU_DIV_CTL_GATE_REF_BUF, CCU_DIV_CTL_GATE_REF_BUF); + spin_unlock_irqrestore(&div->lock, flags); +} + +static int ccu_div_buf_is_enabled(struct clk_hw *hw) +{ + struct ccu_div *div = to_ccu_div(hw); + u32 val = 0; + + regmap_read(div->sys_regs, div->reg_ctl, &val); + + return !(val & CCU_DIV_CTL_GATE_REF_BUF); +} + static unsigned long ccu_div_var_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -288,24 +322,6 @@ static int ccu_div_fixed_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -int ccu_div_reset_domain(struct ccu_div *div) -{ - unsigned long flags; - - if (!div || !(div->features & CCU_DIV_RESET_DOMAIN)) - return -EINVAL; - - spin_lock_irqsave(&div->lock, flags); - regmap_update_bits(div->sys_regs, div->reg_ctl, - CCU_DIV_CTL_RST, CCU_DIV_CTL_RST); - spin_unlock_irqrestore(&div->lock, flags); - - /* The next delay must be enough to cover all the resets. */ - udelay(CCU_DIV_RST_DELAY_US); - - return 0; -} - #ifdef CONFIG_DEBUG_FS struct ccu_div_dbgfs_bit { @@ -323,6 +339,7 @@ static const struct ccu_div_dbgfs_bit ccu_div_bits[] = { CCU_DIV_DBGFS_BIT_ATTR("div_en", CCU_DIV_CTL_EN), CCU_DIV_DBGFS_BIT_ATTR("div_rst", CCU_DIV_CTL_RST), CCU_DIV_DBGFS_BIT_ATTR("div_bypass", CCU_DIV_CTL_SET_CLKDIV), + CCU_DIV_DBGFS_BIT_ATTR("div_buf", CCU_DIV_CTL_GATE_REF_BUF), CCU_DIV_DBGFS_BIT_ATTR("div_lock", CCU_DIV_CTL_LOCK_NORMAL) }; @@ -441,6 +458,9 @@ static void ccu_div_var_debug_init(struct clk_hw *hw, struct dentry *dentry) continue; } + if (!strcmp("div_buf", name)) + continue; + bits[didx] = ccu_div_bits[bidx]; bits[didx].div = div; @@ -477,6 +497,21 @@ static void ccu_div_gate_debug_init(struct clk_hw *hw, struct dentry *dentry) &ccu_div_dbgfs_fixed_clkdiv_fops); } +static void ccu_div_buf_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + struct ccu_div *div = to_ccu_div(hw); + struct ccu_div_dbgfs_bit *bit; + + bit = kmalloc(sizeof(*bit), GFP_KERNEL); + if (!bit) + return; + + *bit = ccu_div_bits[3]; + bit->div = div; + debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit, + &ccu_div_dbgfs_bit_fops); +} + static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry) { struct ccu_div *div = to_ccu_div(hw); @@ -489,6 +524,7 @@ static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry) #define ccu_div_var_debug_init NULL #define ccu_div_gate_debug_init NULL +#define ccu_div_buf_debug_init NULL #define ccu_div_fixed_debug_init NULL #endif /* !CONFIG_DEBUG_FS */ @@ -520,6 +556,13 @@ static const struct clk_ops ccu_div_gate_ops = { .debug_init = ccu_div_gate_debug_init }; +static const struct clk_ops ccu_div_buf_ops = { + .enable = ccu_div_buf_enable, + .disable = ccu_div_buf_disable, + .is_enabled = ccu_div_buf_is_enabled, + .debug_init = ccu_div_buf_debug_init +}; + static const struct clk_ops ccu_div_fixed_ops = { .recalc_rate = ccu_div_fixed_recalc_rate, .round_rate = ccu_div_fixed_round_rate, @@ -566,6 +609,8 @@ struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init) } else if (div_init->type == CCU_DIV_GATE) { hw_init.ops = &ccu_div_gate_ops; div->divider = div_init->divider; + } else if (div_init->type == CCU_DIV_BUF) { + hw_init.ops = &ccu_div_buf_ops; } else if (div_init->type == CCU_DIV_FIXED) { hw_init.ops = &ccu_div_fixed_ops; div->divider = div_init->divider; @@ -579,6 +624,7 @@ struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init) goto err_free_div; } parent_data.fw_name = div_init->parent_name; + parent_data.name = div_init->parent_name; hw_init.parent_data = &parent_data; hw_init.num_parents = 1; diff --git a/drivers/clk/baikal-t1/ccu-div.h b/drivers/clk/baikal-t1/ccu-div.h index 795665caefbdc..76d8ee44d4157 100644 --- a/drivers/clk/baikal-t1/ccu-div.h +++ b/drivers/clk/baikal-t1/ccu-div.h @@ -13,15 +13,26 @@ #include #include +/* + * CCU Divider private clock IDs + * @CCU_SYS_SATA_CLK: CCU SATA internal clock + * @CCU_SYS_XGMAC_CLK: CCU XGMAC internal clock + */ +#define CCU_SYS_SATA_CLK -1 +#define CCU_SYS_XGMAC_CLK -2 + /* * CCU Divider private flags + * @CCU_DIV_BASIC: Basic divider clock required by the kernel as early as + * possible. * @CCU_DIV_SKIP_ONE: Due to some reason divider can't be set to 1. * It can be 0 though, which is functionally the same. * @CCU_DIV_SKIP_ONE_TO_THREE: For some reason divider can't be within [1,3]. * It can be either 0 or greater than 3. * @CCU_DIV_LOCK_SHIFTED: Find lock-bit at non-standard position. - * @CCU_DIV_RESET_DOMAIN: Provide reset clock domain method. + * @CCU_DIV_RESET_DOMAIN: There is a clock domain reset handle. */ +#define CCU_DIV_BASIC BIT(0) #define CCU_DIV_SKIP_ONE BIT(1) #define CCU_DIV_SKIP_ONE_TO_THREE BIT(2) #define CCU_DIV_LOCK_SHIFTED BIT(3) @@ -31,11 +42,13 @@ * enum ccu_div_type - CCU Divider types * @CCU_DIV_VAR: Clocks gate with variable divider. * @CCU_DIV_GATE: Clocks gate with fixed divider. + * @CCU_DIV_BUF: Clock gate with no divider. * @CCU_DIV_FIXED: Ungateable clock with fixed divider. */ enum ccu_div_type { CCU_DIV_VAR, CCU_DIV_GATE, + CCU_DIV_BUF, CCU_DIV_FIXED }; @@ -105,6 +118,4 @@ struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *init); void ccu_div_hw_unregister(struct ccu_div *div); -int ccu_div_reset_domain(struct ccu_div *div); - #endif /* __CLK_BT1_CCU_DIV_H__ */ diff --git a/drivers/clk/baikal-t1/ccu-pll.h b/drivers/clk/baikal-t1/ccu-pll.h index 76cd9132a2196..a71bfd7b90ecc 100644 --- a/drivers/clk/baikal-t1/ccu-pll.h +++ b/drivers/clk/baikal-t1/ccu-pll.h @@ -13,6 +13,12 @@ #include #include +/* + * CCU PLL private flags + * @CCU_PLL_BASIC: Basic PLL required by the kernel as early as possible. + */ +#define CCU_PLL_BASIC BIT(0) + /* * struct ccu_pll_init_data - CCU PLL initialization data * @id: Clock private identifier. @@ -22,6 +28,7 @@ * @sys_regs: Baikal-T1 System Controller registers map. * @np: Pointer to the node describing the CCU PLLs. * @flags: PLL clock flags. + * @features: PLL private features. */ struct ccu_pll_init_data { unsigned int id; @@ -31,6 +38,7 @@ struct ccu_pll_init_data { struct regmap *sys_regs; struct device_node *np; unsigned long flags; + unsigned long features; }; /* diff --git a/drivers/clk/baikal-t1/ccu-rst.c b/drivers/clk/baikal-t1/ccu-rst.c new file mode 100644 index 0000000000000..40023ea674636 --- /dev/null +++ b/drivers/clk/baikal-t1/ccu-rst.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC + * + * Authors: + * Serge Semin + * + * Baikal-T1 CCU Resets interface driver + */ + +#define pr_fmt(fmt) "bt1-ccu-rst: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ccu-rst.h" + +#define CCU_AXI_MAIN_BASE 0x030 +#define CCU_AXI_DDR_BASE 0x034 +#define CCU_AXI_SATA_BASE 0x038 +#define CCU_AXI_GMAC0_BASE 0x03C +#define CCU_AXI_GMAC1_BASE 0x040 +#define CCU_AXI_XGMAC_BASE 0x044 +#define CCU_AXI_PCIE_M_BASE 0x048 +#define CCU_AXI_PCIE_S_BASE 0x04C +#define CCU_AXI_USB_BASE 0x050 +#define CCU_AXI_HWA_BASE 0x054 +#define CCU_AXI_SRAM_BASE 0x058 + +#define CCU_SYS_DDR_BASE 0x02c +#define CCU_SYS_SATA_REF_BASE 0x060 +#define CCU_SYS_APB_BASE 0x064 +#define CCU_SYS_PCIE_BASE 0x144 + +#define CCU_RST_DELAY_US 1 + +#define CCU_RST_TRIG(_base, _ofs) \ + { \ + .type = CCU_RST_TRIG, \ + .base = _base, \ + .mask = BIT(_ofs), \ + } + +#define CCU_RST_DIR(_base, _ofs) \ + { \ + .type = CCU_RST_DIR, \ + .base = _base, \ + .mask = BIT(_ofs), \ + } + +struct ccu_rst_info { + enum ccu_rst_type type; + unsigned int base; + unsigned int mask; +}; + +/* + * Each AXI-bus clock divider is equipped with the corresponding clock-consumer + * domain reset (it's 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_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), + [CCU_AXI_XGMAC_RST] = CCU_RST_TRIG(CCU_AXI_XGMAC_BASE, 1), + [CCU_AXI_PCIE_M_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_M_BASE, 1), + [CCU_AXI_PCIE_S_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_S_BASE, 1), + [CCU_AXI_USB_RST] = CCU_RST_TRIG(CCU_AXI_USB_BASE, 1), + [CCU_AXI_HWA_RST] = CCU_RST_TRIG(CCU_AXI_HWA_BASE, 1), + [CCU_AXI_SRAM_RST] = CCU_RST_TRIG(CCU_AXI_SRAM_BASE, 1), +}; + +/* + * SATA reference clock domain and APB-bus domain are connected with the + * sefl-deasserted reset control, which can be activated via the corresponding + * clock divider register. DDR and PCIe sub-domains can be reset with directly + * controlled reset signals. Resetting the DDR controller though won't end up + * well while the Linux kernel is working. + */ +static const struct ccu_rst_info sys_rst_info[] = { + [CCU_SYS_SATA_REF_RST] = CCU_RST_TRIG(CCU_SYS_SATA_REF_BASE, 1), + [CCU_SYS_APB_RST] = CCU_RST_TRIG(CCU_SYS_APB_BASE, 1), + [CCU_SYS_DDR_FULL_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 1), + [CCU_SYS_DDR_INIT_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 2), + [CCU_SYS_PCIE_PCS_PHY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 0), + [CCU_SYS_PCIE_PIPE0_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 4), + [CCU_SYS_PCIE_CORE_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 8), + [CCU_SYS_PCIE_PWR_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 9), + [CCU_SYS_PCIE_STICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 10), + [CCU_SYS_PCIE_NSTICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 11), + [CCU_SYS_PCIE_HOT_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 12), +}; + +static int ccu_rst_reset(struct reset_controller_dev *rcdev, unsigned long idx) +{ + struct ccu_rst *rst = to_ccu_rst(rcdev); + const struct ccu_rst_info *info = &rst->rsts_info[idx]; + + if (info->type != CCU_RST_TRIG) + return -EOPNOTSUPP; + + regmap_update_bits(rst->sys_regs, info->base, info->mask, info->mask); + + /* The next delay must be enough to cover all the resets. */ + udelay(CCU_RST_DELAY_US); + + return 0; +} + +static int ccu_rst_set(struct reset_controller_dev *rcdev, + unsigned long idx, bool high) +{ + struct ccu_rst *rst = to_ccu_rst(rcdev); + const struct ccu_rst_info *info = &rst->rsts_info[idx]; + + if (info->type != CCU_RST_DIR) + return high ? -EOPNOTSUPP : 0; + + return regmap_update_bits(rst->sys_regs, info->base, + info->mask, high ? info->mask : 0); +} + +static int ccu_rst_assert(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + return ccu_rst_set(rcdev, idx, true); +} + +static int ccu_rst_deassert(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + return ccu_rst_set(rcdev, idx, false); +} + +static int ccu_rst_status(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + struct ccu_rst *rst = to_ccu_rst(rcdev); + const struct ccu_rst_info *info = &rst->rsts_info[idx]; + u32 val; + + if (info->type != CCU_RST_DIR) + return -EOPNOTSUPP; + + regmap_read(rst->sys_regs, info->base, &val); + + return !!(val & info->mask); +} + +static const struct reset_control_ops ccu_rst_ops = { + .reset = ccu_rst_reset, + .assert = ccu_rst_assert, + .deassert = ccu_rst_deassert, + .status = ccu_rst_status, +}; + +struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *rst_init) +{ + struct ccu_rst *rst; + int ret; + + if (!rst_init) + return ERR_PTR(-EINVAL); + + rst = kzalloc(sizeof(*rst), GFP_KERNEL); + if (!rst) + return ERR_PTR(-ENOMEM); + + rst->sys_regs = rst_init->sys_regs; + if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-axi")) { + rst->rcdev.nr_resets = ARRAY_SIZE(axi_rst_info); + rst->rsts_info = axi_rst_info; + } else if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-sys")) { + rst->rcdev.nr_resets = ARRAY_SIZE(sys_rst_info); + rst->rsts_info = sys_rst_info; + } else { + pr_err("Incompatible DT node '%s' specified\n", + of_node_full_name(rst_init->np)); + ret = -EINVAL; + goto err_kfree_rst; + } + + rst->rcdev.owner = THIS_MODULE; + rst->rcdev.ops = &ccu_rst_ops; + rst->rcdev.of_node = rst_init->np; + + ret = reset_controller_register(&rst->rcdev); + if (ret) { + pr_err("Couldn't register '%s' reset controller\n", + of_node_full_name(rst_init->np)); + goto err_kfree_rst; + } + + return rst; + +err_kfree_rst: + kfree(rst); + + return ERR_PTR(ret); +} + +void ccu_rst_hw_unregister(struct ccu_rst *rst) +{ + reset_controller_unregister(&rst->rcdev); + + kfree(rst); +} diff --git a/drivers/clk/baikal-t1/ccu-rst.h b/drivers/clk/baikal-t1/ccu-rst.h new file mode 100644 index 0000000000000..d6e8b2f671f4d --- /dev/null +++ b/drivers/clk/baikal-t1/ccu-rst.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 CCU Resets interface driver + */ +#ifndef __CLK_BT1_CCU_RST_H__ +#define __CLK_BT1_CCU_RST_H__ + +#include +#include +#include + +struct ccu_rst_info; + +/* + * enum ccu_rst_type - CCU Reset types + * @CCU_RST_TRIG: Self-deasserted reset signal. + * @CCU_RST_DIR: Directly controlled reset signal. + */ +enum ccu_rst_type { + CCU_RST_TRIG, + CCU_RST_DIR, +}; + +/* + * struct ccu_rst_init_data - CCU Resets initialization data + * @sys_regs: Baikal-T1 System Controller registers map. + * @np: Pointer to the node with the System CCU block. + */ +struct ccu_rst_init_data { + struct regmap *sys_regs; + struct device_node *np; +}; + +/* + * struct ccu_rst - CCU Reset descriptor + * @rcdev: Reset controller descriptor. + * @sys_regs: Baikal-T1 System Controller registers map. + * @rsts_info: Reset flag info (base address and mask). + */ +struct ccu_rst { + struct reset_controller_dev rcdev; + struct regmap *sys_regs; + const struct ccu_rst_info *rsts_info; +}; +#define to_ccu_rst(_rcdev) container_of(_rcdev, struct ccu_rst, rcdev) + +#ifdef CONFIG_CLK_BT1_CCU_RST + +struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *init); + +void ccu_rst_hw_unregister(struct ccu_rst *rst); + +#else + +static inline +struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *init) +{ + return NULL; +} + +static inline void ccu_rst_hw_unregister(struct ccu_rst *rst) {} + +#endif + +#endif /* __CLK_BT1_CCU_RST_H__ */ diff --git a/drivers/clk/baikal-t1/clk-ccu-div.c b/drivers/clk/baikal-t1/clk-ccu-div.c index f141fda12b09a..ce710ad8ebc32 100644 --- a/drivers/clk/baikal-t1/clk-ccu-div.c +++ b/drivers/clk/baikal-t1/clk-ccu-div.c @@ -12,6 +12,8 @@ #define pr_fmt(fmt) "bt1-ccu-div: " fmt #include +#include +#include #include #include #include @@ -24,9 +26,9 @@ #include #include -#include #include "ccu-div.h" +#include "ccu-rst.h" #define CCU_AXI_MAIN_BASE 0x030 #define CCU_AXI_DDR_BASE 0x034 @@ -76,6 +78,16 @@ .divider = _divider \ } +#define CCU_DIV_BUF_INFO(_id, _name, _pname, _base, _flags) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_name = _pname, \ + .base = _base, \ + .type = CCU_DIV_BUF, \ + .flags = _flags \ + } + #define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider) \ { \ .id = _id, \ @@ -85,12 +97,6 @@ .divider = _divider \ } -#define CCU_DIV_RST_MAP(_rst_id, _clk_id) \ - { \ - .rst_id = _rst_id, \ - .clk_id = _clk_id \ - } - struct ccu_div_info { unsigned int id; const char *name; @@ -105,11 +111,6 @@ struct ccu_div_info { unsigned long features; }; -struct ccu_div_rst_map { - unsigned int rst_id; - unsigned int clk_id; -}; - struct ccu_div_data { struct device_node *np; struct regmap *sys_regs; @@ -118,11 +119,8 @@ struct ccu_div_data { const struct ccu_div_info *divs_info; struct ccu_div **divs; - unsigned int rst_num; - const struct ccu_div_rst_map *rst_map; - struct reset_controller_dev rcdev; + struct ccu_rst *rsts; }; -#define to_ccu_div_data(_rcdev) container_of(_rcdev, struct ccu_div_data, rcdev) /* * AXI Main Interconnect (axi_main_clk) and DDR AXI-bus (axi_ddr_clk) clocks @@ -169,33 +167,22 @@ static const struct ccu_div_info axi_info[] = { CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN) }; -static const struct ccu_div_rst_map axi_rst_map[] = { - CCU_DIV_RST_MAP(CCU_AXI_MAIN_RST, CCU_AXI_MAIN_CLK), - CCU_DIV_RST_MAP(CCU_AXI_DDR_RST, CCU_AXI_DDR_CLK), - CCU_DIV_RST_MAP(CCU_AXI_SATA_RST, CCU_AXI_SATA_CLK), - CCU_DIV_RST_MAP(CCU_AXI_GMAC0_RST, CCU_AXI_GMAC0_CLK), - CCU_DIV_RST_MAP(CCU_AXI_GMAC1_RST, CCU_AXI_GMAC1_CLK), - CCU_DIV_RST_MAP(CCU_AXI_XGMAC_RST, CCU_AXI_XGMAC_CLK), - CCU_DIV_RST_MAP(CCU_AXI_PCIE_M_RST, CCU_AXI_PCIE_M_CLK), - CCU_DIV_RST_MAP(CCU_AXI_PCIE_S_RST, CCU_AXI_PCIE_S_CLK), - CCU_DIV_RST_MAP(CCU_AXI_USB_RST, CCU_AXI_USB_CLK), - CCU_DIV_RST_MAP(CCU_AXI_HWA_RST, CCU_AXI_HWA_CLK), - CCU_DIV_RST_MAP(CCU_AXI_SRAM_RST, CCU_AXI_SRAM_CLK) -}; - /* * APB-bus clock is marked as critical since it's a main communication bus * for the SoC devices registers IO-operations. */ static const struct ccu_div_info sys_info[] = { - CCU_DIV_VAR_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk", + CCU_DIV_VAR_INFO(CCU_SYS_SATA_CLK, "sys_sata_clk", "sata_clk", CCU_SYS_SATA_REF_BASE, 4, CLK_SET_RATE_GATE, CCU_DIV_SKIP_ONE | CCU_DIV_LOCK_SHIFTED | CCU_DIV_RESET_DOMAIN), + CCU_DIV_BUF_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk", + "sys_sata_clk", CCU_SYS_SATA_REF_BASE, + CLK_SET_RATE_PARENT), CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk", "pcie_clk", CCU_SYS_APB_BASE, 5, - CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN), + CLK_IS_CRITICAL, CCU_DIV_BASIC | CCU_DIV_RESET_DOMAIN), CCU_DIV_GATE_INFO(CCU_SYS_GMAC0_TX_CLK, "sys_gmac0_tx_clk", "eth_clk", CCU_SYS_GMAC0_BASE, 5), CCU_DIV_FIXED_INFO(CCU_SYS_GMAC0_PTP_CLK, "sys_gmac0_ptp_clk", @@ -204,10 +191,12 @@ static const struct ccu_div_info sys_info[] = { "eth_clk", CCU_SYS_GMAC1_BASE, 5), CCU_DIV_FIXED_INFO(CCU_SYS_GMAC1_PTP_CLK, "sys_gmac1_ptp_clk", "eth_clk", 10), - CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk", - "eth_clk", CCU_SYS_XGMAC_BASE, 8), + CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_CLK, "sys_xgmac_clk", + "eth_clk", CCU_SYS_XGMAC_BASE, 1), + CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk", + "sys_xgmac_clk", 8), CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_PTP_CLK, "sys_xgmac_ptp_clk", - "eth_clk", 10), + "sys_xgmac_clk", 8), CCU_DIV_GATE_INFO(CCU_SYS_USB_CLK, "sys_usb_clk", "eth_clk", CCU_SYS_USB_BASE, 10), CCU_DIV_VAR_INFO(CCU_SYS_PVT_CLK, "sys_pvt_clk", @@ -227,74 +216,58 @@ static const struct ccu_div_info sys_info[] = { "ref_clk", 25), CCU_DIV_VAR_INFO(CCU_SYS_TIMER0_CLK, "sys_timer0_clk", "ref_clk", CCU_SYS_TIMER0_BASE, 17, - CLK_SET_RATE_GATE, 0), + CLK_SET_RATE_GATE, CCU_DIV_BASIC), CCU_DIV_VAR_INFO(CCU_SYS_TIMER1_CLK, "sys_timer1_clk", "ref_clk", CCU_SYS_TIMER1_BASE, 17, - CLK_SET_RATE_GATE, 0), + CLK_SET_RATE_GATE, CCU_DIV_BASIC), CCU_DIV_VAR_INFO(CCU_SYS_TIMER2_CLK, "sys_timer2_clk", "ref_clk", CCU_SYS_TIMER2_BASE, 17, - CLK_SET_RATE_GATE, 0), + CLK_SET_RATE_GATE, CCU_DIV_BASIC), CCU_DIV_VAR_INFO(CCU_SYS_WDT_CLK, "sys_wdt_clk", "eth_clk", CCU_SYS_WDT_BASE, 17, CLK_SET_RATE_GATE, CCU_DIV_SKIP_ONE_TO_THREE) }; -static const struct ccu_div_rst_map sys_rst_map[] = { - CCU_DIV_RST_MAP(CCU_SYS_SATA_REF_RST, CCU_SYS_SATA_REF_CLK), - CCU_DIV_RST_MAP(CCU_SYS_APB_RST, CCU_SYS_APB_CLK), -}; +static struct ccu_div_data *axi_data; +static struct ccu_div_data *sys_data; -static struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data, - unsigned int clk_id) +static void ccu_div_set_data(struct ccu_div_data *data) { - struct ccu_div *div; - int idx; - - for (idx = 0; idx < data->divs_num; ++idx) { - div = data->divs[idx]; - if (div && div->id == clk_id) - return div; - } - - return ERR_PTR(-EINVAL); + struct device_node *np = data->np; + + if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) + axi_data = data; + else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) + sys_data = data; + else + pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np)); } -static int ccu_div_reset(struct reset_controller_dev *rcdev, - unsigned long rst_id) +static struct ccu_div_data *ccu_div_get_data(struct device_node *np) { - struct ccu_div_data *data = to_ccu_div_data(rcdev); - const struct ccu_div_rst_map *map; - struct ccu_div *div; - int idx, ret; + if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) + return axi_data; + else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) + return sys_data; - for (idx = 0, map = data->rst_map; idx < data->rst_num; ++idx, ++map) { - if (map->rst_id == rst_id) - break; - } - if (idx == data->rst_num) { - pr_err("Invalid reset ID %lu specified\n", rst_id); - return -EINVAL; - } + pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np)); - div = ccu_div_find_desc(data, map->clk_id); - if (IS_ERR(div)) { - pr_err("Invalid clock ID %d in mapping\n", map->clk_id); - return PTR_ERR(div); - } + return NULL; +} - ret = ccu_div_reset_domain(div); - if (ret) { - pr_err("Reset isn't supported by divider %s\n", - clk_hw_get_name(ccu_div_get_clk_hw(div))); +static struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data, + unsigned int clk_id) +{ + int idx; + + for (idx = 0; idx < data->divs_num; ++idx) { + if (data->divs_info[idx].id == clk_id) + return data->divs[idx]; } - return ret; + return ERR_PTR(-EINVAL); } -static const struct reset_control_ops ccu_div_rst_ops = { - .reset = ccu_div_reset, -}; - static struct ccu_div_data *ccu_div_create_data(struct device_node *np) { struct ccu_div_data *data; @@ -308,13 +281,9 @@ static struct ccu_div_data *ccu_div_create_data(struct device_node *np) if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) { data->divs_num = ARRAY_SIZE(axi_info); data->divs_info = axi_info; - data->rst_num = ARRAY_SIZE(axi_rst_map); - data->rst_map = axi_rst_map; } else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) { data->divs_num = ARRAY_SIZE(sys_info); data->divs_info = sys_info; - data->rst_num = ARRAY_SIZE(sys_rst_map); - data->rst_map = sys_rst_map; } else { pr_err("Incompatible DT node '%s' specified\n", of_node_full_name(np)); @@ -365,14 +334,16 @@ static struct clk_hw *ccu_div_of_clk_hw_get(struct of_phandle_args *clkspec, clk_id = clkspec->args[0]; div = ccu_div_find_desc(data, clk_id); if (IS_ERR(div)) { - pr_info("Invalid clock ID %d specified\n", clk_id); + if (div != ERR_PTR(-EPROBE_DEFER)) + pr_info("Invalid clock ID %d specified\n", clk_id); + return ERR_CAST(div); } return ccu_div_get_clk_hw(div); } -static int ccu_div_clk_register(struct ccu_div_data *data) +static int ccu_div_clk_register(struct ccu_div_data *data, bool defer) { int idx, ret; @@ -380,6 +351,13 @@ static int ccu_div_clk_register(struct ccu_div_data *data) const struct ccu_div_info *info = &data->divs_info[idx]; struct ccu_div_init_data init = {0}; + if (!!(info->features & CCU_DIV_BASIC) ^ defer) { + if (!data->divs[idx]) + data->divs[idx] = ERR_PTR(-EPROBE_DEFER); + + continue; + } + init.id = info->id; init.name = info->name; init.parent_name = info->parent_name; @@ -396,6 +374,9 @@ static int ccu_div_clk_register(struct ccu_div_data *data) init.base = info->base; init.sys_regs = data->sys_regs; init.divider = info->divider; + } else if (init.type == CCU_DIV_BUF) { + init.base = info->base; + init.sys_regs = data->sys_regs; } else { init.divider = info->divider; } @@ -409,49 +390,105 @@ static int ccu_div_clk_register(struct ccu_div_data *data) } } - ret = of_clk_add_hw_provider(data->np, ccu_div_of_clk_hw_get, data); - if (ret) { - pr_err("Couldn't register dividers '%s' clock provider\n", - of_node_full_name(data->np)); - goto err_hw_unregister; - } - return 0; err_hw_unregister: - for (--idx; idx >= 0; --idx) + for (--idx; idx >= 0; --idx) { + if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer) + continue; + ccu_div_hw_unregister(data->divs[idx]); + } return ret; } -static void ccu_div_clk_unregister(struct ccu_div_data *data) +static void ccu_div_clk_unregister(struct ccu_div_data *data, bool defer) { int idx; - of_clk_del_provider(data->np); + /* Uninstall only the clocks registered on the specfied stage */ + for (idx = 0; idx < data->divs_num; ++idx) { + if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer) + continue; - for (idx = 0; idx < data->divs_num; ++idx) ccu_div_hw_unregister(data->divs[idx]); + } } -static int ccu_div_rst_register(struct ccu_div_data *data) +static int ccu_div_of_register(struct ccu_div_data *data) { int ret; - data->rcdev.ops = &ccu_div_rst_ops; - data->rcdev.of_node = data->np; - data->rcdev.nr_resets = data->rst_num; + ret = of_clk_add_hw_provider(data->np, ccu_div_of_clk_hw_get, data); + if (ret) { + pr_err("Couldn't register dividers '%s' clock provider\n", + of_node_full_name(data->np)); + } - ret = reset_controller_register(&data->rcdev); - if (ret) + return ret; +} + +static int ccu_div_rst_register(struct ccu_div_data *data) +{ + struct ccu_rst_init_data init = {0}; + + init.sys_regs = data->sys_regs; + init.np = data->np; + + data->rsts = ccu_rst_hw_register(&init); + if (IS_ERR(data->rsts)) { pr_err("Couldn't register divider '%s' reset controller\n", of_node_full_name(data->np)); + return PTR_ERR(data->rsts); + } + + return 0; +} + +static int ccu_div_probe(struct platform_device *pdev) +{ + struct ccu_div_data *data; + int ret; + + data = ccu_div_get_data(dev_of_node(&pdev->dev)); + if (!data) + return -EINVAL; + + ret = ccu_div_clk_register(data, false); + if (ret) + return ret; + + ret = ccu_div_rst_register(data); + if (ret) + goto err_clk_unregister; + + return 0; + +err_clk_unregister: + ccu_div_clk_unregister(data, false); return ret; } -static void ccu_div_init(struct device_node *np) +static const struct of_device_id ccu_div_of_match[] = { + { .compatible = "baikal,bt1-ccu-axi" }, + { .compatible = "baikal,bt1-ccu-sys" }, + { } +}; +MODULE_DEVICE_TABLE(of, ccu_div_of_match); + +static struct platform_driver ccu_div_driver = { + .probe = ccu_div_probe, + .driver = { + .name = "clk-ccu-div", + .of_match_table = ccu_div_of_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(ccu_div_driver); + +static __init void ccu_div_init(struct device_node *np) { struct ccu_div_data *data; int ret; @@ -464,22 +501,27 @@ static void ccu_div_init(struct device_node *np) if (ret) goto err_free_data; - ret = ccu_div_clk_register(data); + ret = ccu_div_clk_register(data, true); if (ret) goto err_free_data; - ret = ccu_div_rst_register(data); + ret = ccu_div_of_register(data); if (ret) goto err_clk_unregister; + ccu_div_set_data(data); + return; err_clk_unregister: - ccu_div_clk_unregister(data); + ccu_div_clk_unregister(data, true); err_free_data: ccu_div_free_data(data); } +CLK_OF_DECLARE_DRIVER(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init); +CLK_OF_DECLARE_DRIVER(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init); -CLK_OF_DECLARE(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init); -CLK_OF_DECLARE(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init); +MODULE_AUTHOR("Serge Semin "); +MODULE_DESCRIPTION("Baikal-T1 CCU Dividers clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/baikal-t1/clk-ccu-pll.c b/drivers/clk/baikal-t1/clk-ccu-pll.c index 2445d4b12bafa..313095587c266 100644 --- a/drivers/clk/baikal-t1/clk-ccu-pll.c +++ b/drivers/clk/baikal-t1/clk-ccu-pll.c @@ -12,6 +12,8 @@ #define pr_fmt(fmt) "bt1-ccu-pll: " fmt #include +#include +#include #include #include #include @@ -31,13 +33,14 @@ #define CCU_PCIE_PLL_BASE 0x018 #define CCU_ETH_PLL_BASE 0x020 -#define CCU_PLL_INFO(_id, _name, _pname, _base, _flags) \ - { \ - .id = _id, \ - .name = _name, \ - .parent_name = _pname, \ - .base = _base, \ - .flags = _flags \ +#define CCU_PLL_INFO(_id, _name, _pname, _base, _flags, _features) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_name = _pname, \ + .base = _base, \ + .flags = _flags, \ + .features = _features, \ } #define CCU_PLL_NUM ARRAY_SIZE(pll_info) @@ -48,6 +51,7 @@ struct ccu_pll_info { const char *parent_name; unsigned int base; unsigned long flags; + unsigned long features; }; /* @@ -61,15 +65,15 @@ struct ccu_pll_info { */ static const struct ccu_pll_info pll_info[] = { CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll", "ref_clk", CCU_CPU_PLL_BASE, - CLK_IS_CRITICAL), + CLK_IS_CRITICAL, CCU_PLL_BASIC), CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll", "ref_clk", CCU_SATA_PLL_BASE, - CLK_IS_CRITICAL | CLK_SET_RATE_GATE), + CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0), CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll", "ref_clk", CCU_DDR_PLL_BASE, - CLK_IS_CRITICAL | CLK_SET_RATE_GATE), + CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0), CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE, - CLK_IS_CRITICAL), + CLK_IS_CRITICAL, CCU_PLL_BASIC), CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE, - CLK_IS_CRITICAL | CLK_SET_RATE_GATE) + CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0) }; struct ccu_pll_data { @@ -78,16 +82,16 @@ struct ccu_pll_data { struct ccu_pll *plls[CCU_PLL_NUM]; }; +static struct ccu_pll_data *pll_data; + static struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data, unsigned int clk_id) { - struct ccu_pll *pll; int idx; for (idx = 0; idx < CCU_PLL_NUM; ++idx) { - pll = data->plls[idx]; - if (pll && pll->id == clk_id) - return pll; + if (pll_info[idx].id == clk_id) + return data->plls[idx]; } return ERR_PTR(-EINVAL); @@ -133,14 +137,16 @@ static struct clk_hw *ccu_pll_of_clk_hw_get(struct of_phandle_args *clkspec, clk_id = clkspec->args[0]; pll = ccu_pll_find_desc(data, clk_id); if (IS_ERR(pll)) { - pr_info("Invalid PLL clock ID %d specified\n", clk_id); + if (pll != ERR_PTR(-EPROBE_DEFER)) + pr_info("Invalid PLL clock ID %d specified\n", clk_id); + return ERR_CAST(pll); } return ccu_pll_get_clk_hw(pll); } -static int ccu_pll_clk_register(struct ccu_pll_data *data) +static int ccu_pll_clk_register(struct ccu_pll_data *data, bool defer) { int idx, ret; @@ -148,6 +154,14 @@ static int ccu_pll_clk_register(struct ccu_pll_data *data) const struct ccu_pll_info *info = &pll_info[idx]; struct ccu_pll_init_data init = {0}; + /* Defer non-basic PLLs allocation for the probe stage */ + if (!!(info->features & CCU_PLL_BASIC) ^ defer) { + if (!data->plls[idx]) + data->plls[idx] = ERR_PTR(-EPROBE_DEFER); + + continue; + } + init.id = info->id; init.name = info->name; init.parent_name = info->parent_name; @@ -155,6 +169,7 @@ static int ccu_pll_clk_register(struct ccu_pll_data *data) init.sys_regs = data->sys_regs; init.np = data->np; init.flags = info->flags; + init.features = info->features; data->plls[idx] = ccu_pll_hw_register(&init); if (IS_ERR(data->plls[idx])) { @@ -165,22 +180,71 @@ static int ccu_pll_clk_register(struct ccu_pll_data *data) } } + return 0; + +err_hw_unregister: + for (--idx; idx >= 0; --idx) { + if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer) + continue; + + ccu_pll_hw_unregister(data->plls[idx]); + } + + return ret; +} + +static void ccu_pll_clk_unregister(struct ccu_pll_data *data, bool defer) +{ + int idx; + + /* Uninstall only the clocks registered on the specfied stage */ + for (idx = 0; idx < CCU_PLL_NUM; ++idx) { + if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer) + continue; + + ccu_pll_hw_unregister(data->plls[idx]); + } +} + +static int ccu_pll_of_register(struct ccu_pll_data *data) +{ + int ret; + ret = of_clk_add_hw_provider(data->np, ccu_pll_of_clk_hw_get, data); if (ret) { pr_err("Couldn't register PLL provider of '%s'\n", of_node_full_name(data->np)); - goto err_hw_unregister; } - return 0; + return ret; +} -err_hw_unregister: - for (--idx; idx >= 0; --idx) - ccu_pll_hw_unregister(data->plls[idx]); +static int ccu_pll_probe(struct platform_device *pdev) +{ + struct ccu_pll_data *data = pll_data; - return ret; + if (!data) + return -EINVAL; + + return ccu_pll_clk_register(data, false); } +static const struct of_device_id ccu_pll_of_match[] = { + { .compatible = "baikal,bt1-ccu-pll" }, + { } +}; +MODULE_DEVICE_TABLE(of, ccu_pll_of_match); + +static struct platform_driver ccu_pll_driver = { + .probe = ccu_pll_probe, + .driver = { + .name = "clk-ccu-pll", + .of_match_table = ccu_pll_of_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(ccu_pll_driver); + static __init void ccu_pll_init(struct device_node *np) { struct ccu_pll_data *data; @@ -194,13 +258,26 @@ static __init void ccu_pll_init(struct device_node *np) if (ret) goto err_free_data; - ret = ccu_pll_clk_register(data); + ret = ccu_pll_clk_register(data, true); if (ret) goto err_free_data; + ret = ccu_pll_of_register(data); + if (ret) + goto err_clk_unregister; + + pll_data = data; + return; +err_clk_unregister: + ccu_pll_clk_unregister(data, true); + err_free_data: ccu_pll_free_data(data); } -CLK_OF_DECLARE(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init); +CLK_OF_DECLARE_DRIVER(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init); + +MODULE_AUTHOR("Serge Semin "); +MODULE_DESCRIPTION("Baikal-T1 CCU PLL clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index c6d3b1ab3d55c..5f8bd49b0810c 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -1204,7 +1204,7 @@ static const struct vc5_chip_info idt_5p49v6901_info = { .model = IDT_VC6_5P49V6901, .clk_fod_cnt = 4, .clk_out_cnt = 5, - .flags = VC5_HAS_PFD_FREQ_DBL, + .flags = VC5_HAS_PFD_FREQ_DBL | VC5_HAS_BYPASS_SYNC_BIT, }; static const struct vc5_chip_info idt_5p49v6965_info = { diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index 36b3fe1b6b0f9..5736a537f4c8a 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -40,6 +40,17 @@ struct dw_edma_desc *vd2dw_edma_desc(struct virt_dma_desc *vd) return container_of(vd, struct dw_edma_desc, vd); } +static inline +u64 dw_edma_get_pci_address(struct dw_edma_chan *chan, phys_addr_t cpu_addr) +{ + struct dw_edma_chip *chip = chan->dw->chip; + + if (chip->ops->pci_address) + return chip->ops->pci_address(chip->dev, cpu_addr); + + return cpu_addr; +} + static struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk) { struct dw_edma_burst *burst; @@ -64,8 +75,8 @@ static struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk) static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc) { + struct dw_edma_chip *chip = desc->chan->dw->chip; struct dw_edma_chan *chan = desc->chan; - struct dw_edma *dw = chan->chip->dw; struct dw_edma_chunk *chunk; chunk = kzalloc(sizeof(*chunk), GFP_NOWAIT); @@ -82,11 +93,11 @@ static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc) */ chunk->cb = !(desc->chunks_alloc % 2); if (chan->dir == EDMA_DIR_WRITE) { - chunk->ll_region.paddr = dw->ll_region_wr[chan->id].paddr; - chunk->ll_region.vaddr = dw->ll_region_wr[chan->id].vaddr; + chunk->ll_region.paddr = chip->ll_region_wr[chan->id].paddr; + chunk->ll_region.vaddr = chip->ll_region_wr[chan->id].vaddr; } else { - chunk->ll_region.paddr = dw->ll_region_rd[chan->id].paddr; - chunk->ll_region.vaddr = dw->ll_region_rd[chan->id].vaddr; + chunk->ll_region.paddr = chip->ll_region_rd[chan->id].paddr; + chunk->ll_region.vaddr = chip->ll_region_rd[chan->id].vaddr; } if (desc->chunk) { @@ -198,6 +209,24 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan) desc->chunks_alloc--; } +static void dw_edma_device_caps(struct dma_chan *dchan, + struct dma_slave_caps *caps) +{ + struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); + + if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { + if (chan->dir == EDMA_DIR_READ) + caps->directions = BIT(DMA_DEV_TO_MEM); + else + caps->directions = BIT(DMA_MEM_TO_DEV); + } else { + if (chan->dir == EDMA_DIR_WRITE) + caps->directions = BIT(DMA_DEV_TO_MEM); + else + caps->directions = BIT(DMA_MEM_TO_DEV); + } +} + static int dw_edma_device_config(struct dma_chan *dchan, struct dma_slave_config *config) { @@ -249,7 +278,6 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan) { struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); int err = 0; - LIST_HEAD(head); if (!chan->configured) { /* Do nothing */ @@ -329,32 +357,52 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer) { struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan); enum dma_transfer_direction dir = xfer->direction; - phys_addr_t src_addr, dst_addr; struct scatterlist *sg = NULL; struct dw_edma_chunk *chunk; struct dw_edma_burst *burst; struct dw_edma_desc *desc; + u64 src_addr, dst_addr; + size_t fsz = 0; u32 cnt = 0; int i; if (!chan->configured) return NULL; - switch (chan->config.direction) { - case DMA_DEV_TO_MEM: /* local DMA */ - if (dir == DMA_DEV_TO_MEM && chan->dir == EDMA_DIR_READ) - break; - return NULL; - case DMA_MEM_TO_DEV: /* local DMA */ - if (dir == DMA_MEM_TO_DEV && chan->dir == EDMA_DIR_WRITE) - break; - return NULL; - default: /* remote DMA */ - if (dir == DMA_MEM_TO_DEV && chan->dir == EDMA_DIR_READ) - break; - if (dir == DMA_DEV_TO_MEM && chan->dir == EDMA_DIR_WRITE) - break; - return NULL; + /* + * Local Root Port/End-point Remote End-point + * +-----------------------+ PCIe bus +----------------------+ + * | | +-+ | | + * | DEV_TO_MEM Rx Ch <----+ +---+ Tx Ch DEV_TO_MEM | + * | | | | | | + * | MEM_TO_DEV Tx Ch +----+ +---> Rx Ch MEM_TO_DEV | + * | | +-+ | | + * +-----------------------+ +----------------------+ + * + * 1. Normal logic: + * If eDMA is embedded into the DW PCIe RP/EP and controlled from the + * CPU/Application side, the Rx channel (EDMA_DIR_READ) will be used + * for the device read operations (DEV_TO_MEM) and the Tx channel + * (EDMA_DIR_WRITE) - for the write operations (MEM_TO_DEV). + * + * 2. Inverted logic: + * If eDMA is embedded into a Remote PCIe EP and is controlled by the + * MWr/MRd TLPs sent from the CPU's PCIe host controller, the Tx + * channel (EDMA_DIR_WRITE) will be used for the device read operations + * (DEV_TO_MEM) and the Rx channel (EDMA_DIR_READ) - for the write + * operations (MEM_TO_DEV). + * + * It is the client driver responsibility to choose a proper channel + * for the DMA transfers. + */ + if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { + if ((chan->dir == EDMA_DIR_READ && dir != DMA_DEV_TO_MEM) || + (chan->dir == EDMA_DIR_WRITE && dir != DMA_MEM_TO_DEV)) + return NULL; + } else { + if ((chan->dir == EDMA_DIR_WRITE && dir != DMA_DEV_TO_MEM) || + (chan->dir == EDMA_DIR_READ && dir != DMA_MEM_TO_DEV)) + return NULL; } if (xfer->type == EDMA_XFER_CYCLIC) { @@ -364,9 +412,9 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer) if (xfer->xfer.sg.len < 1) return NULL; } else if (xfer->type == EDMA_XFER_INTERLEAVED) { - if (!xfer->xfer.il->numf) + if (!xfer->xfer.il->numf || xfer->xfer.il->frame_size < 1) return NULL; - if (xfer->xfer.il->numf > 0 && xfer->xfer.il->frame_size > 0) + if (!xfer->xfer.il->src_inc || !xfer->xfer.il->dst_inc) return NULL; } else { return NULL; @@ -388,16 +436,19 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer) dst_addr = chan->config.dst_addr; } + if (dir == DMA_DEV_TO_MEM) + src_addr = dw_edma_get_pci_address(chan, (phys_addr_t)src_addr); + else + dst_addr = dw_edma_get_pci_address(chan, (phys_addr_t)dst_addr); + if (xfer->type == EDMA_XFER_CYCLIC) { cnt = xfer->xfer.cyclic.cnt; } else if (xfer->type == EDMA_XFER_SCATTER_GATHER) { cnt = xfer->xfer.sg.len; sg = xfer->xfer.sg.sgl; } else if (xfer->type == EDMA_XFER_INTERLEAVED) { - if (xfer->xfer.il->numf > 0) - cnt = xfer->xfer.il->numf; - else - cnt = xfer->xfer.il->frame_size; + cnt = xfer->xfer.il->numf * xfer->xfer.il->frame_size; + fsz = xfer->xfer.il->frame_size; } for (i = 0; i < cnt; i++) { @@ -419,7 +470,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer) else if (xfer->type == EDMA_XFER_SCATTER_GATHER) burst->sz = sg_dma_len(sg); else if (xfer->type == EDMA_XFER_INTERLEAVED) - burst->sz = xfer->xfer.il->sgl[i].size; + burst->sz = xfer->xfer.il->sgl[i % fsz].size; chunk->ll_region.sz += burst->sz; desc->alloc_sz += burst->sz; @@ -438,6 +489,8 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer) * and destination addresses are increased * by the same portion (data length) */ + } else if (xfer->type == EDMA_XFER_INTERLEAVED) { + burst->dar = dst_addr; } } else { burst->dar = dst_addr; @@ -453,25 +506,24 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer) * and destination addresses are increased * by the same portion (data length) */ + } else if (xfer->type == EDMA_XFER_INTERLEAVED) { + burst->sar = src_addr; } } if (xfer->type == EDMA_XFER_SCATTER_GATHER) { sg = sg_next(sg); - } else if (xfer->type == EDMA_XFER_INTERLEAVED && - xfer->xfer.il->frame_size > 0) { + } else if (xfer->type == EDMA_XFER_INTERLEAVED) { struct dma_interleaved_template *il = xfer->xfer.il; - struct data_chunk *dc = &il->sgl[i]; + struct data_chunk *dc = &il->sgl[i % fsz]; - if (il->src_sgl) { - src_addr += burst->sz; + src_addr += burst->sz; + if (il->src_sgl) src_addr += dmaengine_get_src_icg(il, dc); - } - if (il->dst_sgl) { - dst_addr += burst->sz; + dst_addr += burst->sz; + if (il->dst_sgl) dst_addr += dmaengine_get_dst_icg(il, dc); - } } } @@ -664,7 +716,27 @@ static int dw_edma_alloc_chan_resources(struct dma_chan *dchan) if (chan->status != EDMA_ST_IDLE) return -EBUSY; - pm_runtime_get(chan->chip->dev); + /* Bypass the dma-ranges based memory regions mapping for the eDMA + * controlled from the CPU/Application side since in that case + * the local memory address is left untranslated. + */ + if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { + dchan->dev->chan_dma_dev = true; + +#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ + defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) + dchan->dev->device.dma_coherent = chan->dw->chip->dev->dma_coherent; +#endif + + dma_coerce_mask_and_coherent(&dchan->dev->device, + dma_get_mask(chan->dw->chip->dev)); + dchan->dev->device.dma_parms = chan->dw->chip->dev->dma_parms; + } else { + dchan->dev->chan_dma_dev = false; + } + + pm_runtime_get(chan->dw->chip->dev); return 0; } @@ -686,95 +758,79 @@ static void dw_edma_free_chan_resources(struct dma_chan *dchan) cpu_relax(); } - pm_runtime_put(chan->chip->dev); + pm_runtime_put(chan->dw->chip->dev); } -static int dw_edma_channel_setup(struct dw_edma_chip *chip, bool write, - u32 wr_alloc, u32 rd_alloc) +static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) { - struct dw_edma_region *dt_region; + struct dw_edma_chip *chip = dw->chip; struct device *dev = chip->dev; - struct dw_edma *dw = chip->dw; struct dw_edma_chan *chan; struct dw_edma_irq *irq; struct dma_device *dma; - u32 alloc, off_alloc; - u32 i, j, cnt; - int err = 0; + u32 i, ch_cnt; u32 pos; - if (write) { - i = 0; - cnt = dw->wr_ch_cnt; - dma = &dw->wr_edma; - alloc = wr_alloc; - off_alloc = 0; - } else { - i = dw->wr_ch_cnt; - cnt = dw->rd_ch_cnt; - dma = &dw->rd_edma; - alloc = rd_alloc; - off_alloc = wr_alloc; - } + ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt; + dma = &dw->dma; INIT_LIST_HEAD(&dma->channels); - for (j = 0; (alloc || dw->nr_irqs == 1) && j < cnt; j++, i++) { + + for (i = 0; i < ch_cnt; i++) { chan = &dw->chan[i]; - dt_region = devm_kzalloc(dev, sizeof(*dt_region), GFP_KERNEL); - if (!dt_region) - return -ENOMEM; + chan->dw = dw; - chan->vc.chan.private = dt_region; + if (i < dw->wr_ch_cnt) { + chan->id = i; + chan->dir = EDMA_DIR_WRITE; + } else { + chan->id = i - dw->wr_ch_cnt; + chan->dir = EDMA_DIR_READ; + } - chan->chip = chip; - chan->id = j; - chan->dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ; chan->configured = false; chan->request = EDMA_REQ_NONE; chan->status = EDMA_ST_IDLE; - if (write) - chan->ll_max = (dw->ll_region_wr[j].sz / EDMA_LL_SZ); + if (chan->dir == EDMA_DIR_WRITE) + chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ); else - chan->ll_max = (dw->ll_region_rd[j].sz / EDMA_LL_SZ); + chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ); chan->ll_max -= 1; dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n", - write ? "write" : "read", j, chan->ll_max); + chan->dir == EDMA_DIR_WRITE ? "write" : "read", + chan->id, chan->ll_max); if (dw->nr_irqs == 1) pos = 0; + else if (chan->dir == EDMA_DIR_WRITE) + pos = chan->id % wr_alloc; else - pos = off_alloc + (j % alloc); + pos = wr_alloc + chan->id % rd_alloc; irq = &dw->irq[pos]; - if (write) - irq->wr_mask |= BIT(j); + if (chan->dir == EDMA_DIR_WRITE) + irq->wr_mask |= BIT(chan->id); else - irq->rd_mask |= BIT(j); + irq->rd_mask |= BIT(chan->id); irq->dw = dw; memcpy(&chan->msi, &irq->msi, sizeof(chan->msi)); dev_vdbg(dev, "MSI:\t\tChannel %s[%u] addr=0x%.8x%.8x, data=0x%.8x\n", - write ? "write" : "read", j, + chan->dir == EDMA_DIR_WRITE ? "write" : "read", chan->id, chan->msi.address_hi, chan->msi.address_lo, chan->msi.data); chan->vc.desc_free = vchan_free_desc; - vchan_init(&chan->vc, dma); + chan->vc.chan.private = chan->dir == EDMA_DIR_WRITE ? + &dw->chip->dt_region_wr[chan->id] : + &dw->chip->dt_region_rd[chan->id]; - if (write) { - dt_region->paddr = dw->dt_region_wr[j].paddr; - dt_region->vaddr = dw->dt_region_wr[j].vaddr; - dt_region->sz = dw->dt_region_wr[j].sz; - } else { - dt_region->paddr = dw->dt_region_rd[j].paddr; - dt_region->vaddr = dw->dt_region_rd[j].vaddr; - dt_region->sz = dw->dt_region_rd[j].sz; - } + vchan_init(&chan->vc, dma); dw_edma_v0_core_device_config(chan); } @@ -785,16 +841,16 @@ static int dw_edma_channel_setup(struct dw_edma_chip *chip, bool write, dma_cap_set(DMA_CYCLIC, dma->cap_mask); dma_cap_set(DMA_PRIVATE, dma->cap_mask); dma_cap_set(DMA_INTERLEAVE, dma->cap_mask); - dma->directions = BIT(write ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV); + dma->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); dma->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); dma->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); dma->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; - dma->chancnt = cnt; /* Set DMA channel callbacks */ dma->dev = chip->dev; dma->device_alloc_chan_resources = dw_edma_alloc_chan_resources; dma->device_free_chan_resources = dw_edma_free_chan_resources; + dma->device_caps = dw_edma_device_caps; dma->device_config = dw_edma_device_config; dma->device_pause = dw_edma_device_pause; dma->device_resume = dw_edma_device_resume; @@ -808,9 +864,7 @@ static int dw_edma_channel_setup(struct dw_edma_chip *chip, bool write, dma_set_max_seg_size(dma->dev, U32_MAX); /* Register DMA device */ - err = dma_async_device_register(dma); - - return err; + return dma_async_device_register(dma); } static inline void dw_edma_dec_irq_alloc(int *nr_irqs, u32 *alloc, u16 cnt) @@ -827,11 +881,11 @@ static inline void dw_edma_add_irq_mask(u32 *mask, u32 alloc, u16 cnt) (*mask)++; } -static int dw_edma_irq_request(struct dw_edma_chip *chip, +static int dw_edma_irq_request(struct dw_edma *dw, u32 *wr_alloc, u32 *rd_alloc) { - struct device *dev = chip->dev; - struct dw_edma *dw = chip->dw; + struct dw_edma_chip *chip = dw->chip; + struct device *dev = dw->chip->dev; u32 wr_mask = 1; u32 rd_mask = 1; int i, err = 0; @@ -840,12 +894,16 @@ static int dw_edma_irq_request(struct dw_edma_chip *chip, ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt; - if (dw->nr_irqs < 1) + if (chip->nr_irqs < 1 || !chip->ops->irq_vector) return -EINVAL; - if (dw->nr_irqs == 1) { + dw->irq = devm_kcalloc(dev, chip->nr_irqs, sizeof(*dw->irq), GFP_KERNEL); + if (!dw->irq) + return -ENOMEM; + + if (chip->nr_irqs == 1) { /* Common IRQ shared among all channels */ - irq = dw->ops->irq_vector(dev, 0); + irq = chip->ops->irq_vector(dev, 0); err = request_irq(irq, dw_edma_interrupt_common, IRQF_SHARED, dw->name, &dw->irq[0]); if (err) { @@ -855,9 +913,11 @@ static int dw_edma_irq_request(struct dw_edma_chip *chip, if (irq_get_msi_desc(irq)) get_cached_msi_msg(irq, &dw->irq[0].msi); + + dw->nr_irqs = 1; } else { /* Distribute IRQs equally among all channels */ - int tmp = dw->nr_irqs; + int tmp = chip->nr_irqs; while (tmp && (*wr_alloc + *rd_alloc) < ch_cnt) { dw_edma_dec_irq_alloc(&tmp, wr_alloc, dw->wr_ch_cnt); @@ -868,17 +928,15 @@ static int dw_edma_irq_request(struct dw_edma_chip *chip, dw_edma_add_irq_mask(&rd_mask, *rd_alloc, dw->rd_ch_cnt); for (i = 0; i < (*wr_alloc + *rd_alloc); i++) { - irq = dw->ops->irq_vector(dev, i); + irq = chip->ops->irq_vector(dev, i); err = request_irq(irq, i < *wr_alloc ? dw_edma_interrupt_write : dw_edma_interrupt_read, IRQF_SHARED, dw->name, &dw->irq[i]); - if (err) { - dw->nr_irqs = i; - return err; - } + if (err) + goto err_irq_free; if (irq_get_msi_desc(irq)) get_cached_msi_msg(irq, &dw->irq[i].msi); @@ -887,6 +945,14 @@ static int dw_edma_irq_request(struct dw_edma_chip *chip, dw->nr_irqs = i; } + return 0; + +err_irq_free: + for (i--; i >= 0; i--) { + irq = chip->ops->irq_vector(dev, i); + free_irq(irq, &dw->irq[i]); + } + return err; } @@ -902,20 +968,22 @@ int dw_edma_probe(struct dw_edma_chip *chip) return -EINVAL; dev = chip->dev; - if (!dev) + if (!dev || !chip->ops) return -EINVAL; - dw = chip->dw; - if (!dw || !dw->irq || !dw->ops || !dw->ops->irq_vector) - return -EINVAL; + dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL); + if (!dw) + return -ENOMEM; + + dw->chip = chip; raw_spin_lock_init(&dw->lock); - dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, + dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt, dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE)); dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH); - dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, + dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt, dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ)); dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH); @@ -931,23 +999,19 @@ int dw_edma_probe(struct dw_edma_chip *chip) if (!dw->chan) return -ENOMEM; - snprintf(dw->name, sizeof(dw->name), "dw-edma-core:%d", chip->id); + snprintf(dw->name, sizeof(dw->name), "dw-edma-core:%s", + dev_name(chip->dev)); /* Disable eDMA, only to establish the ideal initial conditions */ dw_edma_v0_core_off(dw); /* Request IRQs */ - err = dw_edma_irq_request(chip, &wr_alloc, &rd_alloc); + err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc); if (err) return err; - /* Setup write channels */ - err = dw_edma_channel_setup(chip, true, wr_alloc, rd_alloc); - if (err) - goto err_irq_free; - - /* Setup read channels */ - err = dw_edma_channel_setup(chip, false, wr_alloc, rd_alloc); + /* Setup write/read channels */ + err = dw_edma_channel_setup(dw, wr_alloc, rd_alloc); if (err) goto err_irq_free; @@ -955,15 +1019,15 @@ int dw_edma_probe(struct dw_edma_chip *chip) pm_runtime_enable(dev); /* Turn debugfs on */ - dw_edma_v0_core_debugfs_on(chip); + dw_edma_v0_core_debugfs_on(dw); + + chip->dw = dw; return 0; err_irq_free: for (i = (dw->nr_irqs - 1); i >= 0; i--) - free_irq(dw->ops->irq_vector(dev, i), &dw->irq[i]); - - dw->nr_irqs = 0; + free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]); return err; } @@ -976,34 +1040,28 @@ int dw_edma_remove(struct dw_edma_chip *chip) struct dw_edma *dw = chip->dw; int i; + /* Skip removal if no private data found */ + if (!dw) + return -ENODEV; + /* Disable eDMA */ dw_edma_v0_core_off(dw); /* Free irqs */ for (i = (dw->nr_irqs - 1); i >= 0; i--) - free_irq(dw->ops->irq_vector(dev, i), &dw->irq[i]); + free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]); /* Power management */ pm_runtime_disable(dev); /* Deregister eDMA device */ - dma_async_device_unregister(&dw->wr_edma); - list_for_each_entry_safe(chan, _chan, &dw->wr_edma.channels, + dma_async_device_unregister(&dw->dma); + list_for_each_entry_safe(chan, _chan, &dw->dma.channels, vc.chan.device_node) { tasklet_kill(&chan->vc.task); list_del(&chan->vc.chan.device_node); } - dma_async_device_unregister(&dw->rd_edma); - list_for_each_entry_safe(chan, _chan, &dw->rd_edma.channels, - vc.chan.device_node) { - tasklet_kill(&chan->vc.task); - list_del(&chan->vc.chan.device_node); - } - - /* Turn debugfs off */ - dw_edma_v0_core_debugfs_off(chip); - return 0; } EXPORT_SYMBOL_GPL(dw_edma_remove); diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index 60316d408c3e0..0ab2b6dba8804 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -15,20 +15,12 @@ #include "../virt-dma.h" #define EDMA_LL_SZ 24 -#define EDMA_MAX_WR_CH 8 -#define EDMA_MAX_RD_CH 8 enum dw_edma_dir { EDMA_DIR_WRITE = 0, EDMA_DIR_READ }; -enum dw_edma_map_format { - EDMA_MF_EDMA_LEGACY = 0x0, - EDMA_MF_EDMA_UNROLL = 0x1, - EDMA_MF_HDMA_COMPAT = 0x5 -}; - enum dw_edma_request { EDMA_REQ_NONE = 0, EDMA_REQ_STOP, @@ -57,12 +49,6 @@ struct dw_edma_burst { u32 sz; }; -struct dw_edma_region { - phys_addr_t paddr; - void __iomem *vaddr; - size_t sz; -}; - struct dw_edma_chunk { struct list_head list; struct dw_edma_chan *chan; @@ -87,7 +73,7 @@ struct dw_edma_desc { struct dw_edma_chan { struct virt_dma_chan vc; - struct dw_edma_chip *chip; + struct dw_edma *dw; int id; enum dw_edma_dir dir; @@ -109,37 +95,22 @@ struct dw_edma_irq { struct dw_edma *dw; }; -struct dw_edma_core_ops { - int (*irq_vector)(struct device *dev, unsigned int nr); -}; - struct dw_edma { - char name[20]; + char name[32]; - struct dma_device wr_edma; - u16 wr_ch_cnt; + struct dma_device dma; - struct dma_device rd_edma; + u16 wr_ch_cnt; u16 rd_ch_cnt; - struct dw_edma_region rg_region; /* Registers */ - struct dw_edma_region ll_region_wr[EDMA_MAX_WR_CH]; - struct dw_edma_region ll_region_rd[EDMA_MAX_RD_CH]; - struct dw_edma_region dt_region_wr[EDMA_MAX_WR_CH]; - struct dw_edma_region dt_region_rd[EDMA_MAX_RD_CH]; - struct dw_edma_irq *irq; int nr_irqs; - enum dw_edma_map_format mf; - struct dw_edma_chan *chan; - const struct dw_edma_core_ops *ops; raw_spinlock_t lock; /* Only for legacy */ -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs; -#endif /* CONFIG_DEBUG_FS */ + + struct dw_edma_chip *chip; }; struct dw_edma_sg { diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 44f6e09bdb531..3f9dadc73854c 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -95,8 +95,23 @@ static int dw_edma_pcie_irq_vector(struct device *dev, unsigned int nr) return pci_irq_vector(to_pci_dev(dev), nr); } +static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t cpu_addr) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pci_bus_region region; + struct resource res = { + .flags = IORESOURCE_MEM, + .start = cpu_addr, + .end = cpu_addr, + }; + + pcibios_resource_to_bus(pdev->bus, ®ion, &res); + return region.start; +} + static const struct dw_edma_core_ops dw_edma_pcie_core_ops = { .irq_vector = dw_edma_pcie_irq_vector, + .pci_address = dw_edma_pcie_address, }; static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, @@ -148,7 +163,6 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, struct dw_edma_pcie_data vsec_data; struct device *dev = &pdev->dev; struct dw_edma_chip *chip; - struct dw_edma *dw; int err, nr_irqs; int i, mask; @@ -186,27 +200,10 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, pci_set_master(pdev); /* DMA configuration */ - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); - if (!err) { - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); - if (err) { - pci_err(pdev, "consistent DMA mask 64 set failed\n"); - return err; - } - } else { + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (err) { pci_err(pdev, "DMA mask 64 set failed\n"); - - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err) { - pci_err(pdev, "DMA mask 32 set failed\n"); - return err; - } - - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err) { - pci_err(pdev, "consistent DMA mask 32 set failed\n"); - return err; - } + return err; } /* Data structure allocation */ @@ -214,10 +211,6 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, if (!chip) return -ENOMEM; - dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL); - if (!dw) - return -ENOMEM; - /* IRQs allocation */ nr_irqs = pci_alloc_irq_vectors(pdev, 1, vsec_data.irqs, PCI_IRQ_MSI | PCI_IRQ_MSIX); @@ -228,29 +221,22 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, } /* Data structure initialization */ - chip->dw = dw; chip->dev = dev; - chip->id = pdev->devfn; - chip->irq = pdev->irq; - dw->mf = vsec_data.mf; - dw->nr_irqs = nr_irqs; - dw->ops = &dw_edma_pcie_core_ops; - dw->wr_ch_cnt = vsec_data.wr_ch_cnt; - dw->rd_ch_cnt = vsec_data.rd_ch_cnt; + chip->mf = vsec_data.mf; + chip->nr_irqs = nr_irqs; + chip->ops = &dw_edma_pcie_core_ops; - dw->rg_region.vaddr = pcim_iomap_table(pdev)[vsec_data.rg.bar]; - if (!dw->rg_region.vaddr) - return -ENOMEM; + chip->ll_wr_cnt = vsec_data.wr_ch_cnt; + chip->ll_rd_cnt = vsec_data.rd_ch_cnt; - dw->rg_region.vaddr += vsec_data.rg.off; - dw->rg_region.paddr = pdev->resource[vsec_data.rg.bar].start; - dw->rg_region.paddr += vsec_data.rg.off; - dw->rg_region.sz = vsec_data.rg.sz; + chip->reg_base = pcim_iomap_table(pdev)[vsec_data.rg.bar]; + if (!chip->reg_base) + return -ENOMEM; - for (i = 0; i < dw->wr_ch_cnt; i++) { - struct dw_edma_region *ll_region = &dw->ll_region_wr[i]; - struct dw_edma_region *dt_region = &dw->dt_region_wr[i]; + for (i = 0; i < chip->ll_wr_cnt; i++) { + struct dw_edma_region *ll_region = &chip->ll_region_wr[i]; + struct dw_edma_region *dt_region = &chip->dt_region_wr[i]; struct dw_edma_block *ll_block = &vsec_data.ll_wr[i]; struct dw_edma_block *dt_block = &vsec_data.dt_wr[i]; @@ -259,7 +245,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -ENOMEM; ll_region->vaddr += ll_block->off; - ll_region->paddr = pdev->resource[ll_block->bar].start; + ll_region->paddr = pci_bus_address(pdev, ll_block->bar); ll_region->paddr += ll_block->off; ll_region->sz = ll_block->sz; @@ -268,14 +254,14 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -ENOMEM; dt_region->vaddr += dt_block->off; - dt_region->paddr = pdev->resource[dt_block->bar].start; + dt_region->paddr = pci_bus_address(pdev, dt_block->bar); dt_region->paddr += dt_block->off; dt_region->sz = dt_block->sz; } - for (i = 0; i < dw->rd_ch_cnt; i++) { - struct dw_edma_region *ll_region = &dw->ll_region_rd[i]; - struct dw_edma_region *dt_region = &dw->dt_region_rd[i]; + for (i = 0; i < chip->ll_rd_cnt; i++) { + struct dw_edma_region *ll_region = &chip->ll_region_rd[i]; + struct dw_edma_region *dt_region = &chip->dt_region_rd[i]; struct dw_edma_block *ll_block = &vsec_data.ll_rd[i]; struct dw_edma_block *dt_block = &vsec_data.dt_rd[i]; @@ -284,7 +270,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -ENOMEM; ll_region->vaddr += ll_block->off; - ll_region->paddr = pdev->resource[ll_block->bar].start; + ll_region->paddr = pci_bus_address(pdev, ll_block->bar); ll_region->paddr += ll_block->off; ll_region->sz = ll_block->sz; @@ -293,51 +279,51 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -ENOMEM; dt_region->vaddr += dt_block->off; - dt_region->paddr = pdev->resource[dt_block->bar].start; + dt_region->paddr = pci_bus_address(pdev, dt_block->bar); dt_region->paddr += dt_block->off; dt_region->sz = dt_block->sz; } /* Debug info */ - if (dw->mf == EDMA_MF_EDMA_LEGACY) - pci_dbg(pdev, "Version:\teDMA Port Logic (0x%x)\n", dw->mf); - else if (dw->mf == EDMA_MF_EDMA_UNROLL) - pci_dbg(pdev, "Version:\teDMA Unroll (0x%x)\n", dw->mf); - else if (dw->mf == EDMA_MF_HDMA_COMPAT) - pci_dbg(pdev, "Version:\tHDMA Compatible (0x%x)\n", dw->mf); + if (chip->mf == EDMA_MF_EDMA_LEGACY) + pci_dbg(pdev, "Version:\teDMA Port Logic (0x%x)\n", chip->mf); + else if (chip->mf == EDMA_MF_EDMA_UNROLL) + pci_dbg(pdev, "Version:\teDMA Unroll (0x%x)\n", chip->mf); + else if (chip->mf == EDMA_MF_HDMA_COMPAT) + pci_dbg(pdev, "Version:\tHDMA Compatible (0x%x)\n", chip->mf); else - pci_dbg(pdev, "Version:\tUnknown (0x%x)\n", dw->mf); + pci_dbg(pdev, "Version:\tUnknown (0x%x)\n", chip->mf); - pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", + pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p)\n", vsec_data.rg.bar, vsec_data.rg.off, vsec_data.rg.sz, - dw->rg_region.vaddr, &dw->rg_region.paddr); + chip->reg_base); - for (i = 0; i < dw->wr_ch_cnt; i++) { + for (i = 0; i < chip->ll_wr_cnt; i++) { pci_dbg(pdev, "L. List:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", i, vsec_data.ll_wr[i].bar, - vsec_data.ll_wr[i].off, dw->ll_region_wr[i].sz, - dw->ll_region_wr[i].vaddr, &dw->ll_region_wr[i].paddr); + vsec_data.ll_wr[i].off, chip->ll_region_wr[i].sz, + chip->ll_region_wr[i].vaddr, &chip->ll_region_wr[i].paddr); pci_dbg(pdev, "Data:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", i, vsec_data.dt_wr[i].bar, - vsec_data.dt_wr[i].off, dw->dt_region_wr[i].sz, - dw->dt_region_wr[i].vaddr, &dw->dt_region_wr[i].paddr); + vsec_data.dt_wr[i].off, chip->dt_region_wr[i].sz, + chip->dt_region_wr[i].vaddr, &chip->dt_region_wr[i].paddr); } - for (i = 0; i < dw->rd_ch_cnt; i++) { + for (i = 0; i < chip->ll_rd_cnt; i++) { pci_dbg(pdev, "L. List:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", i, vsec_data.ll_rd[i].bar, - vsec_data.ll_rd[i].off, dw->ll_region_rd[i].sz, - dw->ll_region_rd[i].vaddr, &dw->ll_region_rd[i].paddr); + vsec_data.ll_rd[i].off, chip->ll_region_rd[i].sz, + chip->ll_region_rd[i].vaddr, &chip->ll_region_rd[i].paddr); pci_dbg(pdev, "Data:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", i, vsec_data.dt_rd[i].bar, - vsec_data.dt_rd[i].off, dw->dt_region_rd[i].sz, - dw->dt_region_rd[i].vaddr, &dw->dt_region_rd[i].paddr); + vsec_data.dt_rd[i].off, chip->dt_region_rd[i].sz, + chip->dt_region_rd[i].vaddr, &chip->dt_region_rd[i].paddr); } - pci_dbg(pdev, "Nr. IRQs:\t%u\n", dw->nr_irqs); + pci_dbg(pdev, "Nr. IRQs:\t%u\n", chip->nr_irqs); /* Validating if PCI interrupts were enabled */ if (!pci_dev_msi_enabled(pdev)) { @@ -345,10 +331,6 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -EPERM; } - dw->irq = devm_kcalloc(dev, nr_irqs, sizeof(*dw->irq), GFP_KERNEL); - if (!dw->irq) - return -ENOMEM; - /* Starting eDMA driver */ err = dw_edma_probe(chip); if (err) { diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index b5b8f8181e776..51a34b43434c7 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c @@ -8,6 +8,8 @@ #include +#include + #include "dw-edma-core.h" #include "dw-edma-v0-core.h" #include "dw-edma-v0-regs.h" @@ -25,7 +27,7 @@ enum dw_edma_control { static inline struct dw_edma_v0_regs __iomem *__dw_regs(struct dw_edma *dw) { - return dw->rg_region.vaddr; + return dw->chip->reg_base; } #define SET_32(dw, name, value) \ @@ -53,8 +55,6 @@ static inline struct dw_edma_v0_regs __iomem *__dw_regs(struct dw_edma *dw) SET_32(dw, rd_##name, value); \ } while (0) -#ifdef CONFIG_64BIT - #define SET_64(dw, name, value) \ writeq(value, &(__dw_regs(dw)->name)) @@ -80,8 +80,6 @@ static inline struct dw_edma_v0_regs __iomem *__dw_regs(struct dw_edma *dw) SET_64(dw, rd_##name, value); \ } while (0) -#endif /* CONFIG_64BIT */ - #define SET_COMPAT(dw, name, value) \ writel(value, &(__dw_regs(dw)->type.unroll.name)) @@ -96,7 +94,7 @@ static inline struct dw_edma_v0_regs __iomem *__dw_regs(struct dw_edma *dw) static inline struct dw_edma_v0_ch_regs __iomem * __dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch) { - if (dw->mf == EDMA_MF_EDMA_LEGACY) + if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) return &(__dw_regs(dw)->type.legacy.ch); if (dir == EDMA_DIR_WRITE) @@ -108,7 +106,7 @@ __dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch) static inline void writel_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, u32 value, void __iomem *addr) { - if (dw->mf == EDMA_MF_EDMA_LEGACY) { + if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) { u32 viewport_sel; unsigned long flags; @@ -133,7 +131,7 @@ static inline u32 readl_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, { u32 value; - if (dw->mf == EDMA_MF_EDMA_LEGACY) { + if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) { u32 viewport_sel; unsigned long flags; @@ -164,14 +162,13 @@ static inline u32 readl_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, #define SET_LL_32(ll, value) \ writel(value, ll) -#ifdef CONFIG_64BIT - static inline void writeq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, u64 value, void __iomem *addr) { - if (dw->mf == EDMA_MF_EDMA_LEGACY) { + unsigned long flags; + + if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) { u32 viewport_sel; - unsigned long flags; raw_spin_lock_irqsave(&dw->lock, flags); @@ -181,22 +178,22 @@ static inline void writeq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, writel(viewport_sel, &(__dw_regs(dw)->type.legacy.viewport_sel)); - writeq(value, addr); + } + + writeq(value, addr); + if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) raw_spin_unlock_irqrestore(&dw->lock, flags); - } else { - writeq(value, addr); - } } static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, const void __iomem *addr) { - u32 value; + unsigned long flags; + u64 value; - if (dw->mf == EDMA_MF_EDMA_LEGACY) { + if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) { u32 viewport_sel; - unsigned long flags; raw_spin_lock_irqsave(&dw->lock, flags); @@ -206,12 +203,12 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, writel(viewport_sel, &(__dw_regs(dw)->type.legacy.viewport_sel)); - value = readq(addr); + } + + value = readq(addr); + if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) raw_spin_unlock_irqrestore(&dw->lock, flags); - } else { - value = readq(addr); - } return value; } @@ -225,8 +222,6 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, #define SET_LL_64(ll, value) \ writeq(value, ll) -#endif /* CONFIG_64BIT */ - /* eDMA management callbacks */ void dw_edma_v0_core_off(struct dw_edma *dw) { @@ -256,7 +251,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) { - struct dw_edma *dw = chan->chip->dw; + struct dw_edma *dw = chan->dw; u32 tmp; tmp = FIELD_GET(EDMA_V0_CH_STATUS_MASK, @@ -272,7 +267,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) { - struct dw_edma *dw = chan->chip->dw; + struct dw_edma *dw = chan->dw; SET_RW_32(dw, chan->dir, int_clear, FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id))); @@ -280,7 +275,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) { - struct dw_edma *dw = chan->chip->dw; + struct dw_edma *dw = chan->dw; SET_RW_32(dw, chan->dir, int_clear, FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id))); @@ -301,6 +296,7 @@ u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) { struct dw_edma_burst *child; + struct dw_edma_chan *chan = chunk->chan; struct dw_edma_v0_lli __iomem *lli; struct dw_edma_v0_llp __iomem *llp; u32 control = 0, i = 0; @@ -314,27 +310,20 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) j = chunk->bursts_alloc; list_for_each_entry(child, &chunk->burst->list, list) { j--; - if (!j) - control |= (DW_EDMA_V0_LIE | DW_EDMA_V0_RIE); - + if (!j) { + control |= DW_EDMA_V0_LIE; + if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL)) + control |= DW_EDMA_V0_RIE; + } /* Channel control */ SET_LL_32(&lli[i].control, control); /* Transfer size */ SET_LL_32(&lli[i].transfer_size, child->sz); /* SAR */ - #ifdef CONFIG_64BIT - SET_LL_64(&lli[i].sar.reg, child->sar); - #else /* CONFIG_64BIT */ - SET_LL_32(&lli[i].sar.lsb, lower_32_bits(child->sar)); - SET_LL_32(&lli[i].sar.msb, upper_32_bits(child->sar)); - #endif /* CONFIG_64BIT */ + SET_LL_64(&lli[i].sar.reg, child->sar); /* DAR */ - #ifdef CONFIG_64BIT - SET_LL_64(&lli[i].dar.reg, child->dar); - #else /* CONFIG_64BIT */ - SET_LL_32(&lli[i].dar.lsb, lower_32_bits(child->dar)); - SET_LL_32(&lli[i].dar.msb, upper_32_bits(child->dar)); - #endif /* CONFIG_64BIT */ + SET_LL_64(&lli[i].dar.reg, child->dar); + i++; } @@ -346,18 +335,13 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) /* Channel control */ SET_LL_32(&llp->control, control); /* Linked list */ - #ifdef CONFIG_64BIT - SET_LL_64(&llp->llp.reg, chunk->ll_region.paddr); - #else /* CONFIG_64BIT */ - SET_LL_32(&llp->llp.lsb, lower_32_bits(chunk->ll_region.paddr)); - SET_LL_32(&llp->llp.msb, upper_32_bits(chunk->ll_region.paddr)); - #endif /* CONFIG_64BIT */ + SET_LL_64(&llp->llp.reg, chunk->ll_region.paddr); } void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) { struct dw_edma_chan *chan = chunk->chan; - struct dw_edma *dw = chan->chip->dw; + struct dw_edma *dw = chan->dw; u32 tmp; dw_edma_v0_core_write_chunk(chunk); @@ -365,7 +349,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) if (first) { /* Enable engine */ SET_RW_32(dw, chan->dir, engine_en, BIT(0)); - if (dw->mf == EDMA_MF_HDMA_COMPAT) { + if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) { switch (chan->id) { case 0: SET_RW_COMPAT(dw, chan->dir, ch0_pwr_en, @@ -414,18 +398,11 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) SET_CH_32(dw, chan->dir, chan->id, ch_control1, (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE)); /* Linked list */ - #ifdef CONFIG_64BIT - /* llp is not aligned on 64bit -> keep 32bit accesses */ - SET_CH_32(dw, chan->dir, chan->id, llp.lsb, - lower_32_bits(chunk->ll_region.paddr)); - SET_CH_32(dw, chan->dir, chan->id, llp.msb, - upper_32_bits(chunk->ll_region.paddr)); - #else /* CONFIG_64BIT */ - SET_CH_32(dw, chan->dir, chan->id, llp.lsb, - lower_32_bits(chunk->ll_region.paddr)); - SET_CH_32(dw, chan->dir, chan->id, llp.msb, - upper_32_bits(chunk->ll_region.paddr)); - #endif /* CONFIG_64BIT */ + /* llp is not aligned on 64bit -> keep 32bit accesses */ + SET_CH_32(dw, chan->dir, chan->id, llp.lsb, + lower_32_bits(chunk->ll_region.paddr)); + SET_CH_32(dw, chan->dir, chan->id, llp.msb, + upper_32_bits(chunk->ll_region.paddr)); } /* Doorbell */ SET_RW_32(dw, chan->dir, doorbell, @@ -434,7 +411,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) { - struct dw_edma *dw = chan->chip->dw; + struct dw_edma *dw = chan->dw; u32 tmp = 0; /* MSI done addr - low, high */ @@ -504,12 +481,7 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) } /* eDMA debugfs callbacks */ -void dw_edma_v0_core_debugfs_on(struct dw_edma_chip *chip) -{ - dw_edma_v0_debugfs_on(chip); -} - -void dw_edma_v0_core_debugfs_off(struct dw_edma_chip *chip) +void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) { - dw_edma_v0_debugfs_off(chip); + dw_edma_v0_debugfs_on(dw); } diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h index 2afa626b8300c..ab96a1f480809 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.h +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h @@ -22,7 +22,6 @@ u32 dw_edma_v0_core_status_abort_int(struct dw_edma *chan, enum dw_edma_dir dir) void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first); int dw_edma_v0_core_device_config(struct dw_edma_chan *chan); /* eDMA debug fs callbacks */ -void dw_edma_v0_core_debugfs_on(struct dw_edma_chip *chip); -void dw_edma_v0_core_debugfs_off(struct dw_edma_chip *chip); +void dw_edma_v0_core_debugfs_on(struct dw_edma *dw); #endif /* _DW_EDMA_V0_CORE_H */ diff --git a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c index 4b3bcffd15ef1..d12c607433bf9 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c +++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c @@ -13,76 +13,79 @@ #include "dw-edma-v0-regs.h" #include "dw-edma-core.h" -#define REGS_ADDR(name) \ - ((void __force *)®s->name) -#define REGISTER(name) \ - { #name, REGS_ADDR(name) } - -#define WR_REGISTER(name) \ - { #name, REGS_ADDR(wr_##name) } -#define RD_REGISTER(name) \ - { #name, REGS_ADDR(rd_##name) } - -#define WR_REGISTER_LEGACY(name) \ - { #name, REGS_ADDR(type.legacy.wr_##name) } +#define REGS_ADDR(dw, name) \ + ({ \ + struct dw_edma_v0_regs __iomem *__regs = (dw)->chip->reg_base; \ + \ + (void __iomem *)&__regs->name; \ + }) + +#define REGS_CH_ADDR(dw, name, _dir, _ch) \ + ({ \ + struct dw_edma_v0_ch_regs __iomem *__ch_regs; \ + \ + if ((dw)->chip->mf == EDMA_MF_EDMA_LEGACY) \ + __ch_regs = REGS_ADDR(dw, type.legacy.ch); \ + else if (_dir == EDMA_DIR_READ) \ + __ch_regs = REGS_ADDR(dw, type.unroll.ch[_ch].rd); \ + else \ + __ch_regs = REGS_ADDR(dw, type.unroll.ch[_ch].wr); \ + \ + (void __iomem *)&__ch_regs->name; \ + }) + +#define REGISTER(dw, name) \ + { dw, #name, REGS_ADDR(dw, name) } + +#define CTX_REGISTER(dw, name, dir, ch) \ + { dw, #name, REGS_CH_ADDR(dw, name, dir, ch), dir, ch } + +#define WR_REGISTER(dw, name) \ + { dw, #name, REGS_ADDR(dw, wr_##name) } +#define RD_REGISTER(dw, name) \ + { dw, #name, REGS_ADDR(dw, rd_##name) } + +#define WR_REGISTER_LEGACY(dw, name) \ + { dw, #name, REGS_ADDR(dw, type.legacy.wr_##name) } #define RD_REGISTER_LEGACY(name) \ - { #name, REGS_ADDR(type.legacy.rd_##name) } + { dw, #name, REGS_ADDR(dw, type.legacy.rd_##name) } -#define WR_REGISTER_UNROLL(name) \ - { #name, REGS_ADDR(type.unroll.wr_##name) } -#define RD_REGISTER_UNROLL(name) \ - { #name, REGS_ADDR(type.unroll.rd_##name) } +#define WR_REGISTER_UNROLL(dw, name) \ + { dw, #name, REGS_ADDR(dw, type.unroll.wr_##name) } +#define RD_REGISTER_UNROLL(dw, name) \ + { dw, #name, REGS_ADDR(dw, type.unroll.rd_##name) } #define WRITE_STR "write" #define READ_STR "read" #define CHANNEL_STR "channel" #define REGISTERS_STR "registers" -static struct dw_edma *dw; -static struct dw_edma_v0_regs __iomem *regs; - -static struct { - void __iomem *start; - void __iomem *end; -} lim[2][EDMA_V0_MAX_NR_CH]; - -struct debugfs_entries { +struct dw_edma_debugfs_entry { + struct dw_edma *dw; const char *name; - dma_addr_t *reg; + void __iomem *reg; + enum dw_edma_dir dir; + u16 ch; }; static int dw_edma_debugfs_u32_get(void *data, u64 *val) { - void __iomem *reg = (void __force __iomem *)data; - if (dw->mf == EDMA_MF_EDMA_LEGACY && - reg >= (void __iomem *)®s->type.legacy.ch) { - void __iomem *ptr = ®s->type.legacy.ch; - u32 viewport_sel = 0; + struct dw_edma_debugfs_entry *entry = data; + struct dw_edma *dw = entry->dw; + void __iomem *reg = entry->reg; + + if (dw->chip->mf == EDMA_MF_EDMA_LEGACY && + reg >= REGS_ADDR(dw, type.legacy.ch)) { unsigned long flags; - u16 ch; - - for (ch = 0; ch < dw->wr_ch_cnt; ch++) - if (lim[0][ch].start >= reg && reg < lim[0][ch].end) { - ptr += (reg - lim[0][ch].start); - goto legacy_sel_wr; - } - - for (ch = 0; ch < dw->rd_ch_cnt; ch++) - if (lim[1][ch].start >= reg && reg < lim[1][ch].end) { - ptr += (reg - lim[1][ch].start); - goto legacy_sel_rd; - } - - return 0; -legacy_sel_rd: - viewport_sel = BIT(31); -legacy_sel_wr: - viewport_sel |= FIELD_PREP(EDMA_V0_VIEWPORT_MASK, ch); + u32 viewport_sel; + + viewport_sel = entry->dir == EDMA_DIR_READ ? BIT(31) : 0; + viewport_sel |= FIELD_PREP(EDMA_V0_VIEWPORT_MASK, entry->ch); raw_spin_lock_irqsave(&dw->lock, flags); - writel(viewport_sel, ®s->type.legacy.viewport_sel); - *val = readl(ptr); + writel(viewport_sel, REGS_ADDR(dw, type.legacy.viewport_sel)); + *val = readl(reg); raw_spin_unlock_irqrestore(&dw->lock, flags); } else { @@ -93,222 +96,195 @@ legacy_sel_wr: } DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_edma_debugfs_u32_get, NULL, "0x%08llx\n"); -static void dw_edma_debugfs_create_x32(const struct debugfs_entries entries[], - int nr_entries, struct dentry *dir) +static void dw_edma_debugfs_create_x32(struct dw_edma *dw, + const struct dw_edma_debugfs_entry ini[], + int nr_entries, struct dentry *dent) { + struct dw_edma_debugfs_entry *entries; int i; + entries = devm_kcalloc(dw->chip->dev, nr_entries, sizeof(*entries), + GFP_KERNEL); + if (!entries) + return; + for (i = 0; i < nr_entries; i++) { - if (!debugfs_create_file_unsafe(entries[i].name, 0444, dir, - entries[i].reg, &fops_x32)) - break; + entries[i] = ini[i]; + + debugfs_create_file_unsafe(entries[i].name, 0444, dent, + &entries[i], &fops_x32); } } -static void dw_edma_debugfs_regs_ch(struct dw_edma_v0_ch_regs __iomem *regs, - struct dentry *dir) +static void dw_edma_debugfs_regs_ch(struct dw_edma *dw, enum dw_edma_dir dir, + u16 ch, struct dentry *dent) { - int nr_entries; - const struct debugfs_entries debugfs_regs[] = { - REGISTER(ch_control1), - REGISTER(ch_control2), - REGISTER(transfer_size), - REGISTER(sar.lsb), - REGISTER(sar.msb), - REGISTER(dar.lsb), - REGISTER(dar.msb), - REGISTER(llp.lsb), - REGISTER(llp.msb), + struct dw_edma_debugfs_entry debugfs_regs[] = { + CTX_REGISTER(dw, ch_control1, dir, ch), + CTX_REGISTER(dw, ch_control2, dir, ch), + CTX_REGISTER(dw, transfer_size, dir, ch), + CTX_REGISTER(dw, sar.lsb, dir, ch), + CTX_REGISTER(dw, sar.msb, dir, ch), + CTX_REGISTER(dw, dar.lsb, dir, ch), + CTX_REGISTER(dw, dar.msb, dir, ch), + CTX_REGISTER(dw, llp.lsb, dir, ch), + CTX_REGISTER(dw, llp.msb, dir, ch), }; + int nr_entries; nr_entries = ARRAY_SIZE(debugfs_regs); - dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, dir); + dw_edma_debugfs_create_x32(dw, debugfs_regs, nr_entries, dent); } -static void dw_edma_debugfs_regs_wr(struct dentry *dir) +static void dw_edma_debugfs_regs_wr(struct dw_edma *dw, struct dentry *dent) { - const struct debugfs_entries debugfs_regs[] = { + const struct dw_edma_debugfs_entry debugfs_regs[] = { /* eDMA global registers */ - WR_REGISTER(engine_en), - WR_REGISTER(doorbell), - WR_REGISTER(ch_arb_weight.lsb), - WR_REGISTER(ch_arb_weight.msb), + WR_REGISTER(dw, engine_en), + WR_REGISTER(dw, doorbell), + WR_REGISTER(dw, ch_arb_weight.lsb), + WR_REGISTER(dw, ch_arb_weight.msb), /* eDMA interrupts registers */ - WR_REGISTER(int_status), - WR_REGISTER(int_mask), - WR_REGISTER(int_clear), - WR_REGISTER(err_status), - WR_REGISTER(done_imwr.lsb), - WR_REGISTER(done_imwr.msb), - WR_REGISTER(abort_imwr.lsb), - WR_REGISTER(abort_imwr.msb), - WR_REGISTER(ch01_imwr_data), - WR_REGISTER(ch23_imwr_data), - WR_REGISTER(ch45_imwr_data), - WR_REGISTER(ch67_imwr_data), - WR_REGISTER(linked_list_err_en), + WR_REGISTER(dw, int_status), + WR_REGISTER(dw, int_mask), + WR_REGISTER(dw, int_clear), + WR_REGISTER(dw, err_status), + WR_REGISTER(dw, done_imwr.lsb), + WR_REGISTER(dw, done_imwr.msb), + WR_REGISTER(dw, abort_imwr.lsb), + WR_REGISTER(dw, abort_imwr.msb), + WR_REGISTER(dw, ch01_imwr_data), + WR_REGISTER(dw, ch23_imwr_data), + WR_REGISTER(dw, ch45_imwr_data), + WR_REGISTER(dw, ch67_imwr_data), + WR_REGISTER(dw, linked_list_err_en), }; - const struct debugfs_entries debugfs_unroll_regs[] = { + const struct dw_edma_debugfs_entry debugfs_unroll_regs[] = { /* eDMA channel context grouping */ - WR_REGISTER_UNROLL(engine_chgroup), - WR_REGISTER_UNROLL(engine_hshake_cnt.lsb), - WR_REGISTER_UNROLL(engine_hshake_cnt.msb), - WR_REGISTER_UNROLL(ch0_pwr_en), - WR_REGISTER_UNROLL(ch1_pwr_en), - WR_REGISTER_UNROLL(ch2_pwr_en), - WR_REGISTER_UNROLL(ch3_pwr_en), - WR_REGISTER_UNROLL(ch4_pwr_en), - WR_REGISTER_UNROLL(ch5_pwr_en), - WR_REGISTER_UNROLL(ch6_pwr_en), - WR_REGISTER_UNROLL(ch7_pwr_en), + WR_REGISTER_UNROLL(dw, engine_chgroup), + WR_REGISTER_UNROLL(dw, engine_hshake_cnt.lsb), + WR_REGISTER_UNROLL(dw, engine_hshake_cnt.msb), + WR_REGISTER_UNROLL(dw, ch0_pwr_en), + WR_REGISTER_UNROLL(dw, ch1_pwr_en), + WR_REGISTER_UNROLL(dw, ch2_pwr_en), + WR_REGISTER_UNROLL(dw, ch3_pwr_en), + WR_REGISTER_UNROLL(dw, ch4_pwr_en), + WR_REGISTER_UNROLL(dw, ch5_pwr_en), + WR_REGISTER_UNROLL(dw, ch6_pwr_en), + WR_REGISTER_UNROLL(dw, ch7_pwr_en), }; - struct dentry *regs_dir, *ch_dir; + struct dentry *regs_dent, *ch_dent; int nr_entries, i; char name[16]; - regs_dir = debugfs_create_dir(WRITE_STR, dir); - if (!regs_dir) - return; + regs_dent = debugfs_create_dir(WRITE_STR, dent); nr_entries = ARRAY_SIZE(debugfs_regs); - dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir); + dw_edma_debugfs_create_x32(dw, debugfs_regs, nr_entries, regs_dent); - if (dw->mf == EDMA_MF_HDMA_COMPAT) { + if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) { nr_entries = ARRAY_SIZE(debugfs_unroll_regs); - dw_edma_debugfs_create_x32(debugfs_unroll_regs, nr_entries, - regs_dir); + dw_edma_debugfs_create_x32(dw, debugfs_unroll_regs, nr_entries, + regs_dent); } for (i = 0; i < dw->wr_ch_cnt; i++) { snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i); - ch_dir = debugfs_create_dir(name, regs_dir); - if (!ch_dir) - return; - - dw_edma_debugfs_regs_ch(®s->type.unroll.ch[i].wr, ch_dir); + ch_dent = debugfs_create_dir(name, regs_dent); - lim[0][i].start = ®s->type.unroll.ch[i].wr; - lim[0][i].end = ®s->type.unroll.ch[i].padding_1[0]; + dw_edma_debugfs_regs_ch(dw, EDMA_DIR_WRITE, i, ch_dent); } } -static void dw_edma_debugfs_regs_rd(struct dentry *dir) +static void dw_edma_debugfs_regs_rd(struct dw_edma *dw, struct dentry *dent) { - const struct debugfs_entries debugfs_regs[] = { + const struct dw_edma_debugfs_entry debugfs_regs[] = { /* eDMA global registers */ - RD_REGISTER(engine_en), - RD_REGISTER(doorbell), - RD_REGISTER(ch_arb_weight.lsb), - RD_REGISTER(ch_arb_weight.msb), + RD_REGISTER(dw, engine_en), + RD_REGISTER(dw, doorbell), + RD_REGISTER(dw, ch_arb_weight.lsb), + RD_REGISTER(dw, ch_arb_weight.msb), /* eDMA interrupts registers */ - RD_REGISTER(int_status), - RD_REGISTER(int_mask), - RD_REGISTER(int_clear), - RD_REGISTER(err_status.lsb), - RD_REGISTER(err_status.msb), - RD_REGISTER(linked_list_err_en), - RD_REGISTER(done_imwr.lsb), - RD_REGISTER(done_imwr.msb), - RD_REGISTER(abort_imwr.lsb), - RD_REGISTER(abort_imwr.msb), - RD_REGISTER(ch01_imwr_data), - RD_REGISTER(ch23_imwr_data), - RD_REGISTER(ch45_imwr_data), - RD_REGISTER(ch67_imwr_data), + RD_REGISTER(dw, int_status), + RD_REGISTER(dw, int_mask), + RD_REGISTER(dw, int_clear), + RD_REGISTER(dw, err_status.lsb), + RD_REGISTER(dw, err_status.msb), + RD_REGISTER(dw, linked_list_err_en), + RD_REGISTER(dw, done_imwr.lsb), + RD_REGISTER(dw, done_imwr.msb), + RD_REGISTER(dw, abort_imwr.lsb), + RD_REGISTER(dw, abort_imwr.msb), + RD_REGISTER(dw, ch01_imwr_data), + RD_REGISTER(dw, ch23_imwr_data), + RD_REGISTER(dw, ch45_imwr_data), + RD_REGISTER(dw, ch67_imwr_data), }; - const struct debugfs_entries debugfs_unroll_regs[] = { + const struct dw_edma_debugfs_entry debugfs_unroll_regs[] = { /* eDMA channel context grouping */ - RD_REGISTER_UNROLL(engine_chgroup), - RD_REGISTER_UNROLL(engine_hshake_cnt.lsb), - RD_REGISTER_UNROLL(engine_hshake_cnt.msb), - RD_REGISTER_UNROLL(ch0_pwr_en), - RD_REGISTER_UNROLL(ch1_pwr_en), - RD_REGISTER_UNROLL(ch2_pwr_en), - RD_REGISTER_UNROLL(ch3_pwr_en), - RD_REGISTER_UNROLL(ch4_pwr_en), - RD_REGISTER_UNROLL(ch5_pwr_en), - RD_REGISTER_UNROLL(ch6_pwr_en), - RD_REGISTER_UNROLL(ch7_pwr_en), + RD_REGISTER_UNROLL(dw, engine_chgroup), + RD_REGISTER_UNROLL(dw, engine_hshake_cnt.lsb), + RD_REGISTER_UNROLL(dw, engine_hshake_cnt.msb), + RD_REGISTER_UNROLL(dw, ch0_pwr_en), + RD_REGISTER_UNROLL(dw, ch1_pwr_en), + RD_REGISTER_UNROLL(dw, ch2_pwr_en), + RD_REGISTER_UNROLL(dw, ch3_pwr_en), + RD_REGISTER_UNROLL(dw, ch4_pwr_en), + RD_REGISTER_UNROLL(dw, ch5_pwr_en), + RD_REGISTER_UNROLL(dw, ch6_pwr_en), + RD_REGISTER_UNROLL(dw, ch7_pwr_en), }; - struct dentry *regs_dir, *ch_dir; + struct dentry *regs_dent, *ch_dent; int nr_entries, i; char name[16]; - regs_dir = debugfs_create_dir(READ_STR, dir); - if (!regs_dir) - return; + regs_dent = debugfs_create_dir(READ_STR, dent); nr_entries = ARRAY_SIZE(debugfs_regs); - dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir); + dw_edma_debugfs_create_x32(dw, debugfs_regs, nr_entries, regs_dent); - if (dw->mf == EDMA_MF_HDMA_COMPAT) { + if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) { nr_entries = ARRAY_SIZE(debugfs_unroll_regs); - dw_edma_debugfs_create_x32(debugfs_unroll_regs, nr_entries, - regs_dir); + dw_edma_debugfs_create_x32(dw, debugfs_unroll_regs, nr_entries, + regs_dent); } for (i = 0; i < dw->rd_ch_cnt; i++) { snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i); - ch_dir = debugfs_create_dir(name, regs_dir); - if (!ch_dir) - return; - - dw_edma_debugfs_regs_ch(®s->type.unroll.ch[i].rd, ch_dir); + ch_dent = debugfs_create_dir(name, regs_dent); - lim[1][i].start = ®s->type.unroll.ch[i].rd; - lim[1][i].end = ®s->type.unroll.ch[i].padding_2[0]; + dw_edma_debugfs_regs_ch(dw, EDMA_DIR_READ, i, ch_dent); } } -static void dw_edma_debugfs_regs(void) +static void dw_edma_debugfs_regs(struct dw_edma *dw) { - const struct debugfs_entries debugfs_regs[] = { - REGISTER(ctrl_data_arb_prior), - REGISTER(ctrl), + const struct dw_edma_debugfs_entry debugfs_regs[] = { + REGISTER(dw, ctrl_data_arb_prior), + REGISTER(dw, ctrl), }; - struct dentry *regs_dir; + struct dentry *regs_dent; int nr_entries; - regs_dir = debugfs_create_dir(REGISTERS_STR, dw->debugfs); - if (!regs_dir) - return; + regs_dent = debugfs_create_dir(REGISTERS_STR, dw->dma.dbg_dev_root); nr_entries = ARRAY_SIZE(debugfs_regs); - dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir); + dw_edma_debugfs_create_x32(dw, debugfs_regs, nr_entries, regs_dent); - dw_edma_debugfs_regs_wr(regs_dir); - dw_edma_debugfs_regs_rd(regs_dir); + dw_edma_debugfs_regs_wr(dw, regs_dent); + dw_edma_debugfs_regs_rd(dw, regs_dent); } -void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip) +void dw_edma_v0_debugfs_on(struct dw_edma *dw) { - dw = chip->dw; - if (!dw) - return; - - regs = dw->rg_region.vaddr; - if (!regs) - return; - - dw->debugfs = debugfs_create_dir(dw->name, NULL); - if (!dw->debugfs) + if (!debugfs_initialized()) return; - debugfs_create_u32("mf", 0444, dw->debugfs, &dw->mf); - debugfs_create_u16("wr_ch_cnt", 0444, dw->debugfs, &dw->wr_ch_cnt); - debugfs_create_u16("rd_ch_cnt", 0444, dw->debugfs, &dw->rd_ch_cnt); - - dw_edma_debugfs_regs(); -} - -void dw_edma_v0_debugfs_off(struct dw_edma_chip *chip) -{ - dw = chip->dw; - if (!dw) - return; + debugfs_create_u32("mf", 0444, dw->dma.dbg_dev_root, &dw->chip->mf); + debugfs_create_u16("wr_ch_cnt", 0444, dw->dma.dbg_dev_root, &dw->wr_ch_cnt); + debugfs_create_u16("rd_ch_cnt", 0444, dw->dma.dbg_dev_root, &dw->rd_ch_cnt); - debugfs_remove_recursive(dw->debugfs); - dw->debugfs = NULL; + dw_edma_debugfs_regs(dw); } diff --git a/drivers/dma/dw-edma/dw-edma-v0-debugfs.h b/drivers/dma/dw-edma/dw-edma-v0-debugfs.h index d0ff25a9ea5c9..fb3342d97d6de 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-debugfs.h +++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.h @@ -12,14 +12,9 @@ #include #ifdef CONFIG_DEBUG_FS -void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip); -void dw_edma_v0_debugfs_off(struct dw_edma_chip *chip); +void dw_edma_v0_debugfs_on(struct dw_edma *dw); #else -static inline void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip) -{ -} - -static inline void dw_edma_v0_debugfs_off(struct dw_edma_chip *chip) +static inline void dw_edma_v0_debugfs_on(struct dw_edma *dw) { } #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 2fc4c3f91fd54..be00b9440a12d 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -484,7 +484,6 @@ config EDAC_ARMADA_XP config EDAC_SYNOPSYS tristate "Synopsys DDR Memory Controller" - depends on ARCH_ZYNQ || ARCH_ZYNQMP help Support for error detection and correction on the Synopsys DDR memory controller. @@ -539,4 +538,11 @@ 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 + help + Support for error detection and correction on the Xilinx Zynq A05 + DDR memory controller. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 2d1641a27a28f..83e063f53b22f 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -84,3 +84,4 @@ obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o obj-$(CONFIG_EDAC_DMC520) += dmc520_edac.o +obj-$(CONFIG_EDAC_ZYNQ) += zynq_edac.o diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index a859ddd9d4a13..a3f4e037229ce 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -29,6 +29,9 @@ #include #include #include +#include +#include + #include #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. @@ -105,7 +109,6 @@ static void edac_mc_dump_dimm(struct dimm_info *dimm) edac_dbg(4, " dimm->label = '%s'\n", dimm->label); edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages); edac_dbg(4, " dimm->grain = %d\n", dimm->grain); - edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages); } static void edac_mc_dump_csrow(struct csrow_info *csrow) @@ -150,10 +153,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", @@ -310,7 +315,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 @@ -325,7 +329,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]; @@ -336,26 +339,10 @@ 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 = snprintf(p, len, "mc#%u", mci->mc_idx); - p += n; - len -= n; - for (layer = 0; layer < mci->n_layers; layer++) { - n = snprintf(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]; - if (len <= 0) - break; - } - /* Link it to the csrows old API data */ chan->dimm = dimm; dimm->csrow = row; @@ -582,7 +569,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 @@ -674,7 +745,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 @@ -711,18 +783,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) { @@ -746,9 +830,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; @@ -776,6 +863,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 +#include #include #include #include @@ -37,6 +38,9 @@ #include #include +/* 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/synopsys_edac.c b/drivers/edac/synopsys_edac.c index 8557781bb8dce..b99b7ea9747e2 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -1,108 +1,48 @@ +// 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. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details */ +#include +#include +#include #include +#include +#include #include +#include #include +#include +#include +#include +#include #include #include #include #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 +#define SNPS_EDAC_NR_CHANS 1 -/* 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 +#define SNPS_EDAC_MSG_SIZE 256 -/* Uncorrectable error info registers */ -#define UE_LOG_OFST 0xDC -#define UE_ADDR_OFST 0xE0 -#define UE_DATA_31_0_OFST 0xE4 +#define SNPS_EDAC_MOD_STRING "snps_edac" +#define SNPS_EDAC_MOD_VER "1" -#define STAT_OFST 0xF0 -#define SCRUB_OFST 0xF4 +/* DDR capabilities */ +#define SNPS_CAP_ECC_SCRUB BIT(0) +#define SNPS_CAP_ECC_SCRUBBER BIT(1) +#define SNPS_CAP_ZYNQMP BIT(31) -/* Control register bit field definitions */ -#define CTRL_BW_MASK 0xC -#define CTRL_BW_SHIFT 2 +/* Synopsys uMCTL2 DDR controller registers that are relevant to ECC */ -#define DDRCTL_WDTH_16 1 -#define DDRCTL_WDTH_32 0 +/* DDRC master0 Register */ +#define DDR_MSTR_OFST 0x0 -/* 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) - -/* ZynqMP Enhanced DDR memory controller registers that are relevant to ECC */ /* ECC Configuration Registers */ #define ECC_CFG0_OFST 0x70 #define ECC_CFG1_OFST 0x74 @@ -143,92 +83,128 @@ #define ECC_POISON0_OFST 0xB8 #define ECC_POISON1_OFST 0xBC -#define ECC_ADDRMAP0_OFFSET 0x200 +/* DDR CRC/Parity register */ +#define DDR_CRCPARCTL0_OFST 0xC0 +#define DDR_CRCPARCTL1_OFST 0xC4 +#define DDR_CRCPARCTL2_OFST 0xC8 +#define DDR_CRCPARSTAT_OFST 0xCC + +/* DDR Address map0 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 SAR0 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 + +/* DDR Master Register 0 definitions */ +#define DDR_MSTR_DEV_CFG_MASK GENMASK(31, 30) +#define DDR_MSTR_DEV_X4 0x0 +#define DDR_MSTR_DEV_X8 0x1 +#define DDR_MSTR_DEV_X16 0x2 +#define DDR_MSTR_DEV_X32 0x3 +#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_DDR2 0 +#define DDR_MSTR_MEM_DDR3 BIT(0) +#define DDR_MSTR_MEM_LPDDR BIT(1) +#define DDR_MSTR_MEM_LPDDR2 BIT(2) +#define DDR_MSTR_MEM_LPDDR3 BIT(3) +#define DDR_MSTR_MEM_DDR4 BIT(4) +#define DDR_MSTR_MEM_LPDDR4 BIT(5) + +/* 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 +#define ECC_ERRCNT_UECNT_MASK GENMASK(31, 16) +#define ECC_ERRCNT_CECNT_MASK GENMASK(15, 0) /* 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_QOSUE_MASK BIT(2) +#define DDR_QOSCE_MASK BIT(1) #define DDR_QOS_IRQ_EN_OFST 0x20208 #define DDR_QOS_IRQ_DB_OFST 0x2020C /* 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 +#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 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_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 @@ -270,1019 +246,2163 @@ #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 (ECC_SBRCTL_SCRUB_INTERVAL >> 8) +#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) /** - * struct ecc_error_info - ECC error log information. - * @row: Row number. - * @col: Column number. - * @bank: Bank number. - * @bitpos: Bit position. - * @data: Data causing the error. - * @bankgrpnr: Bank group number. - * @blknr: Block number. + * 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. */ -struct ecc_error_info { - u32 row; - u32 col; - u32 bank; - u32 bitpos; - u32 data; - u32 bankgrpnr; - u32 blknr; +enum snps_dq_width { + SNPS_DQ_32 = 2, + SNPS_DQ_64 = 3, }; /** - * 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. + * 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. */ -struct synps_ecc_status { - u32 ce_cnt; - u32 ue_cnt; - struct ecc_error_info ceinfo; - struct ecc_error_info ueinfo; +enum snps_dq_mode { + SNPS_DQ_FULL = 0, + SNPS_DQ_HALF = 1, + SNPS_DQ_QRTR = 2, }; /** - * struct synps_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. - * @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. + * 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. */ -struct synps_edac_priv { - void __iomem *baseaddr; - 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 +enum snps_burst_length { + SNPS_DDR_BL2 = 2, + SNPS_DDR_BL4 = 4, + SNPS_DDR_BL8 = 8, + SNPS_DDR_BL16 = 16, }; /** - * struct synps_platform_data - synps platform data structure. - * @get_error_info: Get EDAC error info. - * @get_mtype: Get mtype. - * @get_dtype: Get dtype. - * @get_ecc_state: Get ECC state. - * @quirks: To differentiate IPs. + * enum snps_freq_ratio - HIF:SDRAM frequency ratio mode. + * @SNPS_FREQ_RATIO11: 1:1 frequency mode. + * @SNPS_FREQ_RATIO12: 1:2 frequency mode. */ -struct synps_platform_data { - 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); - bool (*get_ecc_state)(void __iomem *base); - int quirks; +enum snps_freq_ratio { + SNPS_FREQ_RATIO11 = 1, + SNPS_FREQ_RATIO12 = 2, }; /** - * 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. + * 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). */ -static int zynq_get_error_info(struct synps_edac_priv *priv) -{ - struct synps_ecc_status *p; - u32 regval, clearval = 0; - void __iomem *base; - - base = priv->baseaddr; - p = &priv->stat; +enum snps_ecc_mode { + SNPS_ECC_DISABLED = 0, + SNPS_ECC_SECDED = 4, + SNPS_ECC_ADVX4X8 = 5, +}; - regval = readl(base + STAT_OFST); - if (!regval) - return 1; +/** + * 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 +}; - 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); +/** + * 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; +}; - return 0; -} +/** + * 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]; +}; /** - * zynqmp_get_error_info - Get the current ECC error info. - * @priv: DDR memory controller private instance data. + * 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. * - * Return: one if there is no error otherwise returns zero. + * For example, row[0] = 6 means row bit #0 is encoded by the HIF + * address bit #6 and vice-versa. */ -static int zynqmp_get_error_info(struct synps_edac_priv *priv) -{ - struct synps_ecc_status *p; - u32 regval, clearval = 0; - void __iomem *base; - - base = priv->baseaddr; - p = &priv->stat; - - 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; +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]; +}; - regval = readl(base + ECC_STAT_OFST); - if (!regval) - return 1; +/** + * struct snps_sdram_addr - SDRAM address. + * @row: Row number. + * @col: Column number. + * @bank: Bank number. + * @bankgrp: Bank group number. + * @rank: Rank number. + */ +struct snps_sdram_addr { + u16 row; + u16 col; + u8 bank; + u8 bankgrp; + u8 rank; +}; - 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: - clearval = ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT; - clearval |= ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT; - writel(clearval, base + ECC_CLR_OFST); - writel(0x0, base + ECC_CLR_OFST); +/** + * struct snps_ecc_error_info - ECC error log information. + * @sdram: SDRAM address. + * @ecnt: Number of detected errors. + * @bitpos: Bit position. + * @data: Data causing the error. + * @syndrome: Erroneous data syndrome. + */ +struct snps_ecc_error_info { + struct snps_sdram_addr sdram; + u16 ecnt; + u32 bitpos; + u64 data; + u32 syndrome; +}; - return 0; -} +/** + * 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. + * @lock: Concurrent CSRs access lock. + * @clks: Controller reference clocks. + * @message: Buffer for framing the event specific info. + */ +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 lock; + struct clk_bulk_data clks[SNPS_MAX_NCLK]; + char message[SNPS_EDAC_MSG_SIZE]; +}; /** - * handle_error - Handle Correctable and Uncorrectable errors. - * @mci: EDAC memory controller instance. - * @p: Synopsys ECC status structure. + * 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: * - * Handles ECC correctable and uncorrectable errors. + * 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 handle_error(struct mem_ctl_info *mci, struct synps_ecc_status *p) +static void snps_map_sys_to_app(struct snps_edac_priv *priv, + dma_addr_t sys, u64 *app) { - 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); - } - - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, - p->ce_cnt, 0, 0, 0, 0, 0, -1, - priv->message, ""); - } + struct snps_sys_app_map *map = &priv->sys_app_map; + u64 ofst; + int i; - 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); - } + ofst = 0; + for (i = 0; i < map->nsar; i++) { + if (sys < map->sar[i].base) + break; - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, - p->ue_cnt, 0, 0, 0, 0, 0, -1, - priv->message, ""); + ofst = map->sar[i].ofst; } - memset(p, 0, sizeof(*p)); + *app = sys - ofst; } /** - * intr_handler - Interrupt Handler for ECC interrupts. - * @irq: IRQ number. - * @dev_id: Device ID. + * snps_map_sys_to_app - Map Application address to System address. + * @priv: DDR memory controller private instance data. + * @app: Application address (source). + * @sys: System address (destination). * - * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise. + * 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. */ -static irqreturn_t intr_handler(int irq, void *dev_id) +static void snps_map_app_to_sys(struct snps_edac_priv *priv, + u64 app, dma_addr_t *sys) { - const struct synps_platform_data *p_data; - struct mem_ctl_info *mci = dev_id; - struct synps_edac_priv *priv; - int status, regval; - - priv = mci->pvt_info; - p_data = priv->p_data; - - 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; - - status = p_data->get_error_info(priv); - if (status) - return IRQ_NONE; - - priv->ce_cnt += priv->stat.ce_cnt; - priv->ue_cnt += priv->stat.ue_cnt; - handle_error(mci, &priv->stat); + 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; + } - edac_dbg(3, "Total error count CE %d UE %d\n", - priv->ce_cnt, priv->ue_cnt); - writel(regval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); - return IRQ_HANDLED; + *sys = app + ofst; } /** - * check_errors - Check controller for ECC errors. - * @mci: EDAC memory controller instance. + * 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). * - * Check and post ECC errors. Called by the polling thread. + * 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 void check_errors(struct mem_ctl_info *mci) +static void snps_map_app_to_hif(struct snps_edac_priv *priv, + u64 app, u64 *hif) { - const struct synps_platform_data *p_data; - struct synps_edac_priv *priv; - int status; - - priv = mci->pvt_info; - p_data = priv->p_data; - - status = p_data->get_error_info(priv); - if (status) - return; - - priv->ce_cnt += priv->stat.ce_cnt; - priv->ue_cnt += priv->stat.ue_cnt; - handle_error(mci, &priv->stat); - - edac_dbg(3, "Total error count CE %d UE %d\n", - priv->ce_cnt, priv->ue_cnt); + *hif = app >> priv->info.dq_width; } /** - * 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. + * 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). * - * Return: a device type width enumeration. + * Backward HIF-to-App translation is just the opposite DQ-width-based + * shift operation. */ -static enum dev_type zynq_get_dtype(const void __iomem *base) +static void snps_map_hif_to_app(struct snps_edac_priv *priv, + u64 hif, u64 *app) { - 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; - } - - return dt; + *app = hif << priv->info.dq_width; } /** - * zynqmp_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. + * 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). * - * Return: a device type width enumeration. + * 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 enum dev_type zynqmp_get_dtype(const void __iomem *base) +static void snps_map_hif_to_sdram(struct snps_edac_priv *priv, + u64 hif, struct snps_sdram_addr *sdram) { - 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; - } + struct snps_hif_sdram_map *map = &priv->hif_sdram_map; + int i; - return dt; -} + 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); + } -/** - * zynq_get_ecc_state - Return the controller ECC enable/disable status. - * @base: DDR memory controller base address. - * - * Get the ECC enable/disable status of the controller. - * - * Return: true if enabled, otherwise false. - */ -static bool zynq_get_ecc_state(void __iomem *base) -{ - enum dev_type dt; - u32 ecctype; + 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); + } - dt = zynq_get_dtype(base); - if (dt == DEV_UNKNOWN) - return false; + 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); + } - ecctype = readl(base + SCRUB_OFST) & SCRUB_MODE_MASK; - if ((ecctype == SCRUB_MODE_SECDED) && (dt == DEV_X2)) - return true; + 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); + } - return false; + 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); + } } /** - * zynqmp_get_ecc_state - Return the controller ECC enable/disable status. - * @base: DDR memory controller base address. + * 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). * - * Get the ECC enable/disable status for the controller. + * SDRAM-HIF address mapping is similar to the HIF-SDRAM mapping procedure, but + * we'll traverse each SDRAM rank/bank/column/row bit. * - * Return: a ECC status boolean i.e true/false - enabled/disabled. + * Note the unmapped bits of the SDRAM address components will be just + * ignored. So make sure the source address is valid. */ -static bool zynqmp_get_ecc_state(void __iomem *base) +static void snps_map_sdram_to_hif(struct snps_edac_priv *priv, + struct snps_sdram_addr *sdram, u64 *hif) { - enum dev_type dt; - u32 ecctype; + struct snps_hif_sdram_map *map = &priv->hif_sdram_map; + unsigned long addr; + int i; - dt = zynqmp_get_dtype(base); - if (dt == DEV_UNKNOWN) - return false; + *hif = 0; - ecctype = readl(base + ECC_CFG0_OFST) & SCRUB_MODE_MASK; - if ((ecctype == SCRUB_MODE_SECDED) && - ((dt == DEV_X2) || (dt == DEV_X4) || (dt == DEV_X8))) - return true; + 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]); + } - return false; -} + 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]); + } -/** - * get_memsize - Read the size of the attached memory device. - * - * Return: the memory size in bytes. - */ -static u32 get_memsize(void) -{ - struct sysinfo inf; + 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]); + } - si_meminfo(&inf); + 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]); + } - return inf.totalram * inf.mem_unit; + 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]); + } } /** - * zynq_get_mtype - Return the controller memory type. - * @base: Synopsys ECC status structure. - * - * Get the EDAC memory type appropriate for the current controller - * configuration. + * 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: a memory type enumeration. + * Perform a full mapping of the system address (detected on the controller + * ports) to the SDRAM address tuple row/column/bank/etc. */ -static enum mem_type zynq_get_mtype(const void __iomem *base) +static void snps_map_sys_to_sdram(struct snps_edac_priv *priv, + dma_addr_t sys, struct snps_sdram_addr *sdram) { - enum mem_type mt; - u32 memtype; + u64 app, hif; - memtype = readl(base + T_ZQ_OFST); + snps_map_sys_to_app(priv, sys, &app); - if (memtype & T_ZQ_DDRMODE_MASK) - mt = MEM_DDR3; - else - mt = MEM_DDR2; + snps_map_app_to_hif(priv, app, &hif); - return mt; + snps_map_hif_to_sdram(priv, hif, sdram); } /** - * zynqmp_get_mtype - Returns controller memory type. - * @base: Synopsys ECC status structure. - * - * Get the EDAC memory type appropriate for the current controller - * configuration. + * snps_map_sdram_to_sys - Map SDRAM address to SDRAM address. + * @priv: DDR memory controller private instance data. + * @sys: System address (source). + * @sdram: SDRAM address (destination). * - * Return: a memory type enumeration. + * Perform a full mapping of the SDRAM address (row/column/bank/etc) to + * the system address specific to the controller system bus ports. */ -static enum mem_type zynqmp_get_mtype(const void __iomem *base) +static void snps_map_sdram_to_sys(struct snps_edac_priv *priv, + struct snps_sdram_addr *sdram, dma_addr_t *sys) { - enum mem_type mt; - u32 memtype; + u64 app, hif; - memtype = readl(base + CTRL_OFST); + snps_map_sdram_to_hif(priv, sdram, &hif); - 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; + snps_map_hif_to_app(priv, hif, &app); - return mt; + snps_map_app_to_sys(priv, app, sys); } /** - * init_csrows - Initialize the csrow data. - * @mci: EDAC memory controller instance. + * snps_get_bitpos - Get DQ-bus corrected bit position. + * @bitnum: Bit number retrieved from the ECCSTAT.corrected_bit_num field. + * @dq_width: Controller DQ-bus width. * - * Initialize the chip select rows associated with the EDAC memory - * controller instance. + * Return: actual corrected DQ-bus bit position starting from 0. */ -static void init_csrows(struct mem_ctl_info *mci) +static inline u32 snps_get_bitpos(u32 bitnum, enum snps_dq_width dq_width) { - struct synps_edac_priv *priv = mci->pvt_info; - const struct synps_platform_data *p_data; - struct csrow_info *csi; - struct dimm_info *dimm; - u32 size, row; - int j; - - p_data = priv->p_data; + /* ecc[0] bit */ + if (bitnum == 0) + return BITS_PER_BYTE << dq_width; - for (row = 0; row < mci->nr_csrows; row++) { - csi = mci->csrows[row]; - size = get_memsize(); + /* ecc[1:x] bit */ + if (is_power_of_2(bitnum)) + return (BITS_PER_BYTE << dq_width) + ilog2(bitnum) + 1; - 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); - } - } + /* data[0:y] bit */ + return bitnum - ilog2(bitnum) - 2; } /** - * mc_init - Initialize one driver instance. - * @mci: EDAC memory controller instance. - * @pdev: platform device. + * snps_ce_irq_handler - Corrected error interrupt handler. + * @irq: IRQ number. + * @dev_id: Device ID. * - * Perform initialization of the EDAC memory controller instance and - * related driver-private data associated with the memory controller the - * instance is bound to. + * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise. */ -static void mc_init(struct mem_ctl_info *mci, struct platform_device *pdev) +static irqreturn_t snps_ce_irq_handler(int irq, void *dev_id) { - struct synps_edac_priv *priv; - - mci->pdev = &pdev->dev; - priv = mci->pvt_info; - 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_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; - } else { - edac_op_state = EDAC_OPSTATE_POLL; - mci->edac_check = check_errors; + 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; + + /* Make sure IRQ is caused by a corrected ECC error */ + if (priv->info.caps & SNPS_CAP_ZYNQMP) { + qosval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); + if (!(qosval & DDR_QOSCE_MASK)) + return IRQ_NONE; + + qosval &= DDR_QOSCE_MASK; } - mci->ctl_page_to_phys = NULL; + regval = readl(priv->baseaddr + ECC_STAT_OFST); + if (!FIELD_GET(ECC_STAT_CE_MASK, regval)) + return IRQ_NONE; - init_csrows(mci); -} + /* Read error info like bit position, SDRAM address, data, syndrome */ + einfo.bitpos = FIELD_GET(ECC_STAT_BITNUM_MASK, regval); + einfo.bitpos = snps_get_bitpos(einfo.bitpos, priv->info.dq_width); -static void enable_intr(struct synps_edac_priv *priv) -{ - /* Enable UE/CE Interrupts */ - writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK, - priv->baseaddr + DDR_QOS_IRQ_EN_OFST); -} + regval = readl(priv->baseaddr + ECC_ERRCNT_OFST); + einfo.ecnt = FIELD_GET(ECC_ERRCNT_CECNT_MASK, regval); -static void disable_intr(struct synps_edac_priv *priv) -{ - /* Disable UE/CE Interrupts */ - writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK, - priv->baseaddr + DDR_QOS_IRQ_DB_OFST); -} + 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); -static int setup_irq(struct mem_ctl_info *mci, - struct platform_device *pdev) -{ - struct synps_edac_priv *priv = mci->pvt_info; - int ret, irq; + 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); - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - edac_printk(KERN_ERR, EDAC_MC, - "No IRQ %d in DT\n", irq); - return irq; - } + 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; - 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; - } + einfo.syndrome = readl(priv->baseaddr + ECC_CSYND2_OFST); - enable_intr(priv); + /* Report the detected errors with the corresponding sys address */ + snps_map_sdram_to_sys(priv, &einfo.sdram, &sys); - return 0; -} + snprintf(priv->message, SNPS_EDAC_MSG_SIZE, + "Row %hu Col %hu Bank %hhu Bank Group %hhu Rank %hhu Bit %d Data 0x%08llx", + einfo.sdram.row, einfo.sdram.col, einfo.sdram.bank, + einfo.sdram.bankgrp, einfo.sdram.rank, + einfo.bitpos, einfo.data); -static const struct synps_platform_data zynq_edac_def = { - .get_error_info = zynq_get_error_info, - .get_mtype = zynq_get_mtype, - .get_dtype = zynq_get_dtype, - .get_ecc_state = zynq_get_ecc_state, - .quirks = 0, -}; + 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->message, ""); -static const struct synps_platform_data zynqmp_edac_def = { - .get_error_info = zynqmp_get_error_info, - .get_mtype = zynqmp_get_mtype, - .get_dtype = zynqmp_get_dtype, - .get_ecc_state = zynqmp_get_ecc_state, - .quirks = (DDR_ECC_INTR_SUPPORT -#ifdef CONFIG_EDAC_DEBUG - | DDR_ECC_DATA_POISON_SUPPORT -#endif - ), -}; + /* Make sure the CE IRQ status is cleared */ + spin_lock_irqsave(&priv->lock, flags); -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 - }, - { - /* end of table */ - } -}; + regval = readl(priv->baseaddr + ECC_CLR_OFST) | + ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT; + writel(regval, priv->baseaddr + ECC_CLR_OFST); -MODULE_DEVICE_TABLE(of, synps_edac_match); + spin_unlock_irqrestore(&priv->lock, flags); -#ifdef CONFIG_EDAC_DEBUG -#define to_mci(k) container_of(k, struct mem_ctl_info, dev) + if (priv->info.caps & SNPS_CAP_ZYNQMP) + writel(qosval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); + + return IRQ_HANDLED; +} /** - * ddr_poison_setup - Update poison registers. - * @priv: DDR memory controller private instance data. + * snps_ue_irq_handler - Uncorrected error interrupt handler. + * @irq: IRQ number. + * @dev_id: Device ID. * - * Update poison registers as per DDR mapping. - * Return: none. + * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise. */ -static void ddr_poison_setup(struct synps_edac_priv *priv) +static irqreturn_t snps_ue_irq_handler(int irq, void *dev_id) { - int col = 0, row = 0, bank = 0, bankgrp = 0, rank = 0, regval; - int index; - ulong hif_addr = 0; + 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; + + /* Make sure IRQ is caused by an uncorrected ECC error */ + if (priv->info.caps & SNPS_CAP_ZYNQMP) { + qosval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); + if (!(regval & DDR_QOSUE_MASK)) + return IRQ_NONE; + + qosval &= DDR_QOSUE_MASK; + } - hif_addr = priv->poison_addr >> 3; + regval = readl(priv->baseaddr + ECC_STAT_OFST); + if (!FIELD_GET(ECC_STAT_UE_MASK, regval)) + return IRQ_NONE; - 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; - } + /* 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); - 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; - } + 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); - 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; - } + 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); - 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; - } + 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; - if (priv->rank_shift[0]) - rank = (hif_addr >> priv->rank_shift[0]) & BIT(0); + einfo.syndrome = readl(priv->baseaddr + ECC_UESYND2_OFST); - 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); + /* Report the detected errors with the corresponding sys address */ + snps_map_sdram_to_sys(priv, &einfo.sdram, &sys); - 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); -} + snprintf(priv->message, SNPS_EDAC_MSG_SIZE, + "Row %hu Col %hu Bank %hhu Bank Group %hhu Rank %hhu Data 0x%08llx", + einfo.sdram.row, einfo.sdram.col, einfo.sdram.bank, + einfo.sdram.bankgrp, einfo.sdram.rank, + einfo.data); -static ssize_t inject_data_error_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; - - 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); -} + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, einfo.ecnt, + PHYS_PFN(sys), offset_in_page(sys), + einfo.syndrome, einfo.sdram.rank, 0, -1, + priv->message, ""); -static ssize_t inject_data_error_store(struct device *dev, - struct device_attribute *mattr, - const char *data, size_t count) -{ - struct mem_ctl_info *mci = to_mci(dev); - struct synps_edac_priv *priv = mci->pvt_info; + /* Make sure the UE IRQ status is cleared */ + spin_lock_irqsave(&priv->lock, flags); + + regval = readl(priv->baseaddr + ECC_CLR_OFST) | + ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT; + writel(regval, priv->baseaddr + ECC_CLR_OFST); - if (kstrtoul(data, 0, &priv->poison_addr)) - return -EINVAL; + spin_unlock_irqrestore(&priv->lock, flags); - ddr_poison_setup(priv); + if (priv->info.caps & SNPS_CAP_ZYNQMP) + writel(qosval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); - return count; + return IRQ_HANDLED; } -static ssize_t inject_data_poison_show(struct device *dev, - struct device_attribute *mattr, - char *data) +/** + * 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 = to_mci(dev); - struct synps_edac_priv *priv = mci->pvt_info; + 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; - return sprintf(data, "Data Poisoning: %s\n\r", - (((readl(priv->baseaddr + ECC_CFG1_OFST)) & 0x3) == 0x3) - ? ("Correctable Error") : ("UnCorrectable Error")); -} + /* Just a number of CRC/Parity errors is available */ + ecnt = FIELD_GET(DDR_CRCPARSTAT_ALRT_CNT_MASK, regval); -static ssize_t inject_data_poison_store(struct device *dev, - struct device_attribute *mattr, - const char *data, size_t count) -{ - struct mem_ctl_info *mci = to_mci(dev); - struct synps_edac_priv *priv = mci->pvt_info; + /* Report the detected errors with just the custom message */ + snprintf(priv->message, SNPS_EDAC_MSG_SIZE, + "DFI CRC/Parity error detected on dfi_alert_n"); - 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); + edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, ecnt, + 0, 0, 0, 0, 0, -1, priv->message, ""); - return count; -} + /* Make sure the DFI alert IRQ status is cleared */ + spin_lock_irqsave(&priv->lock, flags); -static DEVICE_ATTR_RW(inject_data_error); -static DEVICE_ATTR_RW(inject_data_poison); + regval = readl(priv->baseaddr + DDR_CRCPARCTL0_OFST) | + DDR_CRCPARCTL0_CLR_ALRT_ERR | DDR_CRCPARCTL0_CLR_ALRT_ERRCNT; + writel(regval, priv->baseaddr + DDR_CRCPARCTL0_OFST); -static int edac_create_sysfs_attributes(struct mem_ctl_info *mci) -{ - int rc; + spin_unlock_irqrestore(&priv->lock, flags); - 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; + return IRQ_HANDLED; } -static void edac_remove_sysfs_attributes(struct mem_ctl_info *mci) +/** + * 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 betweeen 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) { - 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 = 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->lock, 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->lock, flags); + + edac_mc_printk(mci, KERN_WARNING, "Back-to-back scrubbing disabled\n"); + + return IRQ_HANDLED; +} + +/** + * 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; +} + +/** + * snps_get_sdram_bw - Get SDRAM bandwidth. + * @priv: DDR memory controller private instance data. + * + * 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 u64 snps_get_sdram_bw(struct snps_edac_priv *priv) +{ + unsigned long rate; + + /* + * 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; + + /* + * 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. + */ + 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 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); + + do_div(bw_ram, 1 + fac * interval); + + return bw_ram; +} + +/** + * 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) */ + bw_ram -= bw; + do_div(bw_ram, bw); + do_div(bw_ram, fac); + + return bw_ram; +} + +/** + * 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->lock, flags); + + regval = readl(priv->baseaddr + ECC_SBRCTL_OFST); + regval &= ~ECC_SBRCTL_SCRUB_EN; + writel(regval, priv->baseaddr + ECC_SBRCTL_OFST); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; + } + + /* 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); + + interval = snps_get_scrub_interval(priv, bw); + } else { + bw = clamp_val(bw, ECC_SBRCTL_INTERVAL_MIN, ECC_SBRCTL_INTERVAL_MAX); + + interval = ECC_SBRCTL_INTERVAL_MAX - bw; + } + + /* + * SBRCTL.scrub_en bitfield must be accessed separately from the other + * CSR bitfields. It means the flag must be set/clear with no updates + * to the rest of the fields. + */ + spin_lock_irqsave(&priv->lock, 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->lock, 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); +} + +/** + * snps_get_sdram_scrub_rate - Get the Scrubber bandwidth. + * @mci: EDAC memory controller instance. + * + * Return: Scrubber bandwidth (interval-based approximated bandwidth if the + * Core clock is unavailable) or zero if the Scrubber was disabled. + */ +static int snps_get_sdram_scrub_rate(struct mem_ctl_info *mci) +{ + struct snps_edac_priv *priv = mci->pvt_info; + u32 regval; + u64 bw; + + regval = readl(priv->baseaddr + ECC_SBRCTL_OFST); + if (!(regval & ECC_SBRCTL_SCRUB_EN)) + return 0; + + 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->lock); + + 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; + + 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.hif_burst_len = SNPS_DDR_BL8; + priv->sys_app_map.minsize = DDR_MIN_SARSIZE; + + return 0; +} + +/** + * snps_get_dtype - Return the controller memory width. + * @mstr: Master CSR value. + * + * Get the EDAC device type width appropriate for the current controller + * configuration. + * + * Return: a device type width enumeration. + */ +static inline enum dev_type snps_get_dtype(u32 mstr) +{ + 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 DEV_UNKNOWN; +} + +/** + * snps_get_mtype - Returns controller memory type. + * @mstr: Master CSR value. + * + * Get the EDAC memory type appropriate for the current controller + * configuration. + * + * Return: a memory type enumeration. + */ +static inline enum mem_type snps_get_mtype(u32 mstr) +{ + 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 MEM_RESERVED; +} + +/** + * 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) +{ + int (*init_plat)(struct snps_edac_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 -ENXIO; + } + + /* 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); + + /* 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 { + 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; + } + } + + /* + * 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; +} + +/** + * snps_get_hif_bank_map - Get HIF/SDRAM-bank address map. + * @priv: DDR memory controller private instance data. + * @addrmap: Array with ADDRMAP registers value. + * + * 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 void snps_get_hif_bank_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_BANK_WIDTH; i++) + map->bank[i] = DDR_ADDRMAP_UNUSED; + + 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; +} + +/** + * 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. + * + * 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. + * + * 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 void snps_get_hif_rank_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_RANK_WIDTH; i++) + map->rank[i] = DDR_ADDRMAP_UNUSED; + + 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; + } + + 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; + } +} + +/** + * snps_get_addr_map - Get HIF/SDRAM/etc address map from CSRs. + * @priv: DDR memory controller private instance data. + * + * 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. + * + * 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 u64 snps_get_sdram_size(struct snps_edac_priv *priv) +{ + struct snps_hif_sdram_map *map = &priv->hif_sdram_map; + u64 size = 0; + int i; + + for (i = 0; i < DDR_MAX_ROW_WIDTH; i++) { + if (map->row[i] != DDR_ADDRMAP_UNUSED) + size++; + } + + 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++; + } + + 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); +} + +/** + * 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 snps_init_csrows(struct mem_ctl_info *mci) +{ + struct snps_edac_priv *priv = mci->pvt_info; + struct csrow_info *csi; + struct dimm_info *dimm; + u32 row, width; + u64 size; + int j; + + /* 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 = 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 = priv->info.sdram_mode; + dimm->nr_pages = PHYS_PFN(size) / csi->nr_channels; + dimm->grain = width; + dimm->dtype = priv->info.dev_cfg; + } + } } -static void setup_row_address_map(struct synps_edac_priv *priv, u32 *addrmap) +/** + * snps_mc_create - Create and initialize MC instance. + * @priv: DDR memory controller private data. + * + * Allocate the EDAC memory controller descriptor and initialize it + * using the private data info. + * + * Return: MC data instance or negative errno. + */ +static struct mem_ctl_info *snps_mc_create(struct snps_edac_priv *priv) { - u32 addrmap_row_b2_10; - int index; + 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; - 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; + 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); + } - 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; + 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_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 { - 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; + mci->scrub_mode = SCRUB_SW_SRC; + mci->scrub_cap = SCRUB_FLAG_SW_SRC; } - 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); + 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; + + 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 void setup_column_address_map(struct synps_edac_priv *priv, u32 *addrmap) +/** + * 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) { - 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); + struct snps_edac_priv *priv = mci->pvt_info; + struct device *dev = &priv->pdev->dev; + int rc, irq; + + irq = platform_get_irq_byname_optional(priv->pdev, "ecc_ce"); + if (irq == -ENXIO) + return 1; + if (irq < 0) + return irq; + + 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; + } + + irq = platform_get_irq_byname(priv->pdev, "ecc_ue"); + if (irq < 0) + return irq; + + 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; + } + + 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; } - } 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); + } + + 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; } + } + + + 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; + + irq = platform_get_irq_byname_optional(priv->pdev, "ecc"); + if (irq < 0) { + irq = platform_get_irq(priv->pdev, 0); + if (irq < 0) + return irq; + } + + 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; + } + + return 0; +} + +static void snps_enable_irq(struct snps_edac_priv *priv) +{ + unsigned long flags; + + /* Enable UE/CE Interrupts */ + if (priv->info.caps & SNPS_CAP_ZYNQMP) { + writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK, + priv->baseaddr + DDR_QOS_IRQ_EN_OFST); + + return; + } + + /* + * ECC IRQs Enable/Disable feature has been available since v3.10a, + * while CRC/Parity interrupts control - since v2.10a. + */ + spin_lock_irqsave(&priv->lock, flags); + + writel(ECC_CTRL_EN_CE_IRQ | ECC_CTRL_EN_UE_IRQ, + priv->baseaddr + ECC_CLR_OFST); + writel(DDR_CRCPARCTL0_EN_ALRT_IRQ, + priv->baseaddr + DDR_CRCPARCTL0_OFST); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void snps_disable_irq(struct snps_edac_priv *priv) +{ + unsigned long flags; + + /* Disable UE/CE Interrupts */ + if (priv->info.caps & SNPS_CAP_ZYNQMP) { + writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK, + priv->baseaddr + DDR_QOS_IRQ_DB_OFST); + + return; + } + + spin_lock_irqsave(&priv->lock, flags); + + writel(0, priv->baseaddr + ECC_CLR_OFST); + writel(0, priv->baseaddr + DDR_CRCPARCTL0_OFST); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +/** + * snps_setup_irq - Request and enable DDRC IRQs. + * @mci: EDAC memory controller instance. + * + * 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 int snps_setup_irq(struct mem_ctl_info *mci) +{ + struct snps_edac_priv *priv = mci->pvt_info; + int rc; + + rc = snps_request_ind_irq(mci); + if (rc > 0) + rc = snps_request_com_irq(mci); + if (rc) + return rc; + + snps_enable_irq(priv); + + return 0; +} + +#ifdef CONFIG_EDAC_DEBUG + +#define SNPS_DEBUGFS_FOPS(__name, __read, __write) \ + static const struct file_operations __name = { \ + .owner = THIS_MODULE, \ + .open = simple_open, \ + .read = __read, \ + .write = __write, \ + } + +#define SNPS_DBGFS_BUF_LEN 128 + +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; + + seq_printf(s, "SDRAM: %s\n", edac_mem_types[priv->info.sdram_mode]); + + 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 { - 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); + seq_puts(s, " -"); + } + seq_putc(s, '\n'); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(snps_ddrc_info); + +static int snps_sys_app_map_show(struct seq_file *s, void *data) +{ + 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; +} + +DEFINE_SHOW_ATTRIBUTE(snps_sys_app_map); + +static u8 snps_find_sdram_dim(struct snps_edac_priv *priv, u8 hif, char *dim) +{ + struct snps_hif_sdram_map *map = &priv->hif_sdram_map; + int i; + + for (i = 0; i < DDR_MAX_ROW_WIDTH; i++) { + if (map->row[i] == hif) { + *dim = 'r'; + return i; + } + } + + for (i = 0; i < DDR_MAX_COL_WIDTH; i++) { + if (map->col[i] == hif) { + *dim = 'c'; + return i; + } + } + + for (i = 0; i < DDR_MAX_BANK_WIDTH; i++) { + if (map->bank[i] == hif) { + *dim = 'b'; + return i; + } + } + + for (i = 0; i < DDR_MAX_BANKGRP_WIDTH; i++) { + if (map->bankgrp[i] == hif) { + *dim = 'g'; + return i; } } - if (width) { - for (index = 9; index > width; index--) { - priv->col_shift[index] = priv->col_shift[index - width]; - priv->col_shift[index - width] = 0; + for (i = 0; i < DDR_MAX_RANK_WIDTH; i++) { + if (map->rank[i] == hif) { + *dim = 'a'; + return i; } } + return DDR_ADDRMAP_UNUSED; +} + +static int snps_hif_sdram_map_show(struct seq_file *s, void *data) +{ + 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; + + seq_printf(s, "%3s", ""); + for (i = 0; i < line_len; i++) + seq_printf(s, " %02d ", i); + + for (i = 0; i < DDR_MAX_HIF_WIDTH; i++) { + if (i % line_len == 0) + seq_printf(s, "\n%02d ", i); + + bit = snps_find_sdram_dim(priv, i, &dim); + + 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"); + + return 0; +} + +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) +{ + 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 Rank %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_bank_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) { - 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; + struct snps_sdram_addr sdram; + u32 regval; + u64 sys; + int rc; + + rc = kstrtou64_from_user(ubuf, size, 0, &sys); + if (rc) + return rc; + + snps_map_sys_to_sdram(priv, sys, &sdram); + + 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); + return size; } -static void setup_bg_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) { - 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); + 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"; + + pos = scnprintf(buf, sizeof(buf), "%s\n", errstr); + return simple_read_from_buffer(ubuf, size, offp, buf, pos); } -static void setup_rank_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->rank_shift[0] = ((addrmap[0] & RANK_MAX_VAL_MASK) == - RANK_MAX_VAL_MASK) ? 0 : ((addrmap[0] & - RANK_MAX_VAL_MASK) + RANK_B0_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; + + 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); + + 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. @@ -1290,56 +2410,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; - struct resource *res; int rc; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - baseaddr = devm_ioremap_resource(&pdev->dev, res); - 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; - if (!p_data->get_ecc_state(baseaddr)) { - edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); - return -ENXIO; - } + 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; - - 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 = snps_setup_irq(mci); + if (rc) + goto free_edac_mc; rc = edac_mc_add_mc(mci); if (rc) { @@ -1348,71 +2447,59 @@ 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 - - /* - * 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); + snps_create_debugfs_nodes(mci); - 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 int mc_remove(struct platform_device *pdev) +static int 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); return 0; } -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..66a2dedddfefc --- /dev/null +++ b/drivers/edac/zynq_edac.c @@ -0,0 +1,504 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#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; +} + +/** + * 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)); +} + +/** + * 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 of the controller. + * + * Return: true if enabled, otherwise false. + */ +static bool zynq_get_ecc_state(void __iomem *base) +{ + enum dev_type dt; + u32 ecctype; + + 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)) + 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) +{ + struct zynq_edac_priv *priv; + + mci->pdev = &pdev->dev; + priv = mci->pvt_info; + 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/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 899c8a2a34b6b..5a36ee4177ee3 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -169,7 +169,7 @@ config SUNLANCE config AMD_XGBE tristate "AMD 10GbE Ethernet driver" depends on (OF_ADDRESS || ACPI || PCI) && HAS_IOMEM - depends on X86 || ARM64 || COMPILE_TEST + depends on X86 || ARM64 || MIPS || COMPILE_TEST depends on PTP_1588_CLOCK_OPTIONAL select BITREVERSE select CRC32 diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile index 620785ffbd519..78d57625dfe68 100644 --- a/drivers/net/ethernet/amd/xgbe/Makefile +++ b/drivers/net/ethernet/amd/xgbe/Makefile @@ -4,7 +4,8 @@ obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \ xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \ xgbe-ptp.o \ - xgbe-i2c.o xgbe-phy-v1.o xgbe-phy-v2.o \ + xgbe-i2c.o \ + xgbe-phy-v1.o xgbe-phy-v2.o xgbe-phy-v3.o \ xgbe-platform.o amd-xgbe-$(CONFIG_PCI) += xgbe-pci.o diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index a218dc6f2edd5..94ff4464fe274 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -295,6 +295,16 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) return ret; } + /* Set platform-specific DMA bus settings */ + if (pdata->vdata->blen) + pdata->blen = pdata->vdata->blen; + if (pdata->vdata->pbl) + pdata->pbl = pdata->vdata->pbl; + if (pdata->vdata->rd_osr_limit) + pdata->rd_osr_limit = pdata->vdata->rd_osr_limit; + if (pdata->vdata->wr_osr_limit) + pdata->wr_osr_limit = pdata->vdata->wr_osr_limit; + /* Set default max values if not provided */ if (!pdata->tx_max_fifo_size) pdata->tx_max_fifo_size = pdata->hw_feat.tx_fifo_size; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v3.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v3.c new file mode 100644 index 0000000000000..90089f4772b6d --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v3.c @@ -0,0 +1,693 @@ +/* + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xgbe.h" +#include "xgbe-common.h" + +#define VR_XS_PMA_MII_Gen5_MPLL_CTRL 0x807A +#define VR_XS_PMA_MII_Gen5_MPLL_CTRL_REF_CLK_SEL_bit (1 << 13) +#define VR_XS_PCS_DIG_CTRL1 0x8000 +#define VR_XS_PCS_DIG_CTRL1_VR_RST_Bit MDIO_CTRL1_RESET +#define SR_XC_or_PCS_MMD_Control1 MDIO_CTRL1 +#define SR_XC_or_PCS_MMD_Control1_RST_Bit MDIO_CTRL1_RESET +#define DWC_GLBL_PLL_MONITOR 0x8010 +#define SDS_PCS_CLOCK_READY_mask 0x1C +#define SDS_PCS_CLOCK_READY_bit 0x10 +#define VR_XS_PMA_MII_ENT_GEN5_GEN_CTL 0x809C +#define VR_XS_PMA_MII_ENT_GEN5_GEN_CTL_LANE_MODE_KX4 (4 << 0) +#define VR_XS_PMA_MII_ENT_GEN5_GEN_CTL_LANE_MODE_MASK 0x0007 +#define VR_XS_PMA_MII_ENT_GEN5_GEN_CTL_LINK_WIDTH_4 (2 << 8) +#define VR_XS_PMA_MII_ENT_GEN5_GEN_CTL_LINK_WIDTH_MASK 0x0700 +#define VR_XS_OR_PCS_MMD_DIGITAL_CTL1_VR_RST (1 << 15) + +#define DELAY_COUNT 50 + +/* PHY related configuration information */ +struct xgbe_phy_data { + struct phy_device *phydev; +}; + +static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata); + +static int xgbe_an_restart_kr_training(struct xgbe_prv_data *pdata) +{ + int reg = 0; + + DBGPR("%s\n", __FUNCTION__); + + /* Restart training */ + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, 0x0096, 3); + msleep(500); + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, 0x0096, 1); + + /* The worse case when training continue till 500ms */ + msleep(500); + + reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, 0x0097); + /* Check training failure */ + if (reg & (1 << 3)) + return -1; + + /* Success */ + return 0; +} + +static int xgbe_an_enable_kr_training(struct xgbe_prv_data *pdata) +{ + DBGPR("%s\n", __FUNCTION__); + + /* Enable training */ + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, 0x0096, 2); + + return 0; +} + +static int xgbe_phy_pcs_power_cycle(struct xgbe_prv_data *pdata) +{ + int ret; + DBGPR("%s\n", __FUNCTION__); + + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + + ret |= MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, ret); + + usleep_range(75, 100); + + ret &= ~MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, ret); + + return 0; +} + +static int xgbe_phy_xgmii_mode_kx4(struct xgbe_prv_data *pdata) +{ + int ret, count; + + DBGPR_MDIO("%s\n", __FUNCTION__); + + /* Write 2'b01 to Bits[1:0] of SR PCS Control2 to set the xpcx_kr_0 + * output to 0. + */ + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + + ret &= ~MDIO_PCS_CTRL2_TYPE; + ret |= MDIO_PCS_CTRL2_10GBX; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, ret); + + /* Set Bit 13 SR PMA MMD Control1 Register (for back plane) to 1. */ + ret = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_CTRL1); + + ret |= 0x2000; + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_CTRL1, ret); + + /* Set LANE_MODE TO KX4 (4). */ + ret = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, VR_XS_PMA_MII_ENT_GEN5_GEN_CTL); + + ret &= ~VR_XS_PMA_MII_ENT_GEN5_GEN_CTL_LANE_MODE_MASK; + ret |= VR_XS_PMA_MII_ENT_GEN5_GEN_CTL_LANE_MODE_KX4; + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, VR_XS_PMA_MII_ENT_GEN5_GEN_CTL, ret); + + /* Set LANE_WIDTH (2) 4 lanes per link. */ + ret = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, VR_XS_PMA_MII_ENT_GEN5_GEN_CTL); + + ret &= ~VR_XS_PMA_MII_ENT_GEN5_GEN_CTL_LINK_WIDTH_MASK; + ret |= VR_XS_PMA_MII_ENT_GEN5_GEN_CTL_LINK_WIDTH_4; + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, VR_XS_PMA_MII_ENT_GEN5_GEN_CTL, ret); + + /* Initiate Software Reset. */ + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, VR_XS_PCS_DIG_CTRL1); + + ret |= VR_XS_OR_PCS_MMD_DIGITAL_CTL1_VR_RST; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, VR_XS_PCS_DIG_CTRL1, ret); + + /* Wait until reset done. */ + count = DELAY_COUNT; + do { + msleep(20); + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, VR_XS_PCS_DIG_CTRL1); + } while (!!(ret & VR_XS_OR_PCS_MMD_DIGITAL_CTL1_VR_RST) && --count); + + if (ret & VR_XS_OR_PCS_MMD_DIGITAL_CTL1_VR_RST) + return -ETIMEDOUT; + + return 0; +} + +static int xgbe_phy_xgmii_mode_kr(struct xgbe_prv_data *pdata) +{ + int ret; + DBGPR("%s\n", __FUNCTION__); + + /* Enable KR training */ + ret = xgbe_an_enable_kr_training(pdata); + if (ret < 0) + return ret; + + /* Set PCS to KR/10G speed */ + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + + ret &= ~MDIO_PCS_CTRL2_TYPE; + ret |= MDIO_PCS_CTRL2_10GBR; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, ret); + + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + + ret &= ~MDIO_CTRL1_SPEEDSEL; + ret |= MDIO_CTRL1_SPEED10G; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, ret); + + ret = xgbe_phy_pcs_power_cycle(pdata); + if (ret < 0) + return ret; + + return 0; +} + +static int xgbe_phy_xgmii_mode(struct xgbe_prv_data *pdata) +{ + const char *pm; + + if(!device_property_read_string(pdata->phy_dev, "baikal,line-mode", &pm)) { + if(strcasecmp(pm, "KX4") == 0){ + DBGPR("xgbe: mode KX4: %s\n", __FUNCTION__); + return xgbe_phy_xgmii_mode_kx4(pdata); + } + } + + DBGPR("xgbe: mode KR: %s\n", __FUNCTION__); + return xgbe_phy_xgmii_mode_kr(pdata); +} + +static int xgbe_phy_probe(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct phy_device *phydev; + int ret; + + phydev = device_phy_find_device(pdata->phy_dev); + if (!phydev) + return -ENODEV; + + ret = phy_init_hw(phydev); + if (ret) + return ret; + + if ((phydev->speed != SPEED_10000) && (phydev->duplex != DUPLEX_FULL)) + return -ENODEV; + + /* Initialize supported features */ + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Backplane_BIT, phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, phydev->supported, 1); + linkmode_copy(phydev->advertising, phydev->supported); + + XGBE_ZERO_SUP(lks); + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, Backplane); + XGBE_SET_SUP(lks, 10000baseKR_Full); + /*XGBE_SET_SUP(lks, 10000baseKX4_Full); + XGBE_SET_SUP(lks, 10000baseT_Full);*/ + + pdata->phy.pause_autoneg = AUTONEG_DISABLE; + pdata->phy.speed = phydev->speed; // SPEED_10000 + pdata->phy.duplex = phydev->duplex; // DUPLEX_FULL + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; + + phy_data->phydev = phydev; + + return 0; +} + +int xgbe_phy_config_init(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data; + int count = DELAY_COUNT; + int ret; + + DBGPR("%s\n", __FUNCTION__); + + phy_data = devm_kzalloc(pdata->dev, sizeof(*phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + + pdata->phy_data = phy_data; + + ret = xgbe_phy_probe(pdata); + if (ret) { + dev_info(pdata->dev, "Failed to probe external PHY\n"); + return ret; + } + + /* Switch XGMAC PHY PLL to use external ref clock from pad */ + ret = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, VR_XS_PMA_MII_Gen5_MPLL_CTRL); + ret &= ~(VR_XS_PMA_MII_Gen5_MPLL_CTRL_REF_CLK_SEL_bit); + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, VR_XS_PMA_MII_Gen5_MPLL_CTRL, ret); + wmb(); + + /* Make vendor specific soft reset */ + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, VR_XS_PCS_DIG_CTRL1); + ret |= VR_XS_PCS_DIG_CTRL1_VR_RST_Bit; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, VR_XS_PCS_DIG_CTRL1, ret); + wmb(); + + /* Wait reset finish */ + count = DELAY_COUNT; + do { + usleep_range(500, 600); + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, VR_XS_PCS_DIG_CTRL1); + } while(((ret & VR_XS_PCS_DIG_CTRL1_VR_RST_Bit) != 0) && count--); + + + DBGPR("%s %x\n", __FUNCTION__, ret); + /* + * Wait for the RST (bit 15) of the “SR XS or PCS MMD Control1” Register is 0. + * This bit is self-cleared when Bits[4:2] in VR XS or PCS MMD Digital + * Status Register are equal to 3’b100, that is, Tx/Rx clocks are stable + * and in Power_Good state. + */ + count = DELAY_COUNT; + do { + usleep_range(500, 600); + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, SR_XC_or_PCS_MMD_Control1); + } while(((ret & SR_XC_or_PCS_MMD_Control1_RST_Bit) != 0) && count--); + + /* + * This bit is self-cleared when Bits[4:2] in VR XS or PCS MMD Digital + * Status Register are equal to 3’b100, that is, Tx/Rx clocks are stable + * and in Power_Good state. + */ + count = DELAY_COUNT; + do { + usleep_range(500, 600); + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, DWC_GLBL_PLL_MONITOR); + } while(((ret & SDS_PCS_CLOCK_READY_mask) != SDS_PCS_CLOCK_READY_bit) && count-- ); + + /* Turn off and clear interrupts */ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); + wmb(); + + xgbe_phy_config_aneg(pdata); + + ret = xgbe_phy_xgmii_mode(pdata); + + count = DELAY_COUNT; + do + { + msleep(10); + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, 0x0001); + } while(((ret & 0x0004) != 0x0004) && count--); + + return 0; +} + +/** + * xgbe_phy_exit() - dummy + */ +static void xgbe_phy_exit(struct xgbe_prv_data *pdata) +{ + return; +} + +static int xgbe_phy_soft_reset(struct xgbe_prv_data *pdata) +{ + /* No real soft-reset for now. Sigh... */ + DBGPR("%s\n", __FUNCTION__); +#if 0 + int count, ret; + + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + + ret |= MDIO_CTRL1_RESET; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, ret); + + count = DELAY_COUNT; + do { + msleep(20); + ret = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + if (ret < 0) + return ret; + } while ((ret & MDIO_CTRL1_RESET) && --count); + + if (ret & MDIO_CTRL1_RESET) + return -ETIMEDOUT; +#endif + + return 0; +} + +static const char *xgbe_phy_fc_string(struct xgbe_prv_data *pdata) +{ + if (pdata->tx_pause && pdata->rx_pause) + return "rx/tx"; + else if (pdata->rx_pause) + return "rx"; + else if (pdata->tx_pause) + return "tx"; + else + return "off"; +} + +static const char *xgbe_phy_speed_string(int speed) +{ + switch (speed) { + case SPEED_10000: + return "10Gbps"; + case SPEED_UNKNOWN: + return "Unknown"; + default: + return "Unsupported"; + } +} + +static void xgbe_phy_print_status(struct xgbe_prv_data *pdata) +{ + if (pdata->phy.link) + netdev_info(pdata->netdev, + "Link is Up - %s/%s - flow control %s\n", + xgbe_phy_speed_string(pdata->phy.speed), + pdata->phy.duplex == DUPLEX_FULL ? "Full" : "Half", + xgbe_phy_fc_string(pdata)); + else + netdev_info(pdata->netdev, "Link is Down\n"); +} + +static void xgbe_phy_update_link(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct phy_device *phydev = phy_data->phydev; + int new_state = 0; + + /* Dummy read? Why? */ + phy_read_mmd(phydev, MDIO_MMD_PHYXS, 0x1001); + + if (pdata->phy.link) { + /* Flow control support */ + pdata->pause_autoneg = pdata->phy.pause_autoneg; + + if (pdata->tx_pause != pdata->phy.tx_pause) { + new_state = 1; + pdata->hw_if.config_tx_flow_control(pdata); + pdata->tx_pause = pdata->phy.tx_pause; + } + + if (pdata->rx_pause != pdata->phy.rx_pause) { + new_state = 1; + pdata->hw_if.config_rx_flow_control(pdata); + pdata->rx_pause = pdata->phy.rx_pause; + } + + /* Speed support */ + if (pdata->phy_speed != pdata->phy.speed) { + new_state = 1; + pdata->phy_speed = pdata->phy.speed; + } + + if (pdata->phy_link != pdata->phy.link) { + new_state = 1; + pdata->phy_link = pdata->phy.link; + } + } else if (pdata->phy_link) { + new_state = 1; + pdata->phy_link = 0; + pdata->phy_speed = SPEED_UNKNOWN; + } + + if (new_state && netif_msg_link(pdata)) + xgbe_phy_print_status(pdata); +} + +/** + * xgbe_phy_start() - dummy + */ +static int xgbe_phy_start(struct xgbe_prv_data *pdata) +{ + return 0; +} + +static void xgbe_phy_stop(struct xgbe_prv_data *pdata) +{ + netif_dbg(pdata, link, pdata->netdev, "stopping PHY\n"); + + /* Disable auto-negotiation interrupts */ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); + + pdata->phy.link = 0; + netif_carrier_off(pdata->netdev); + + xgbe_phy_update_link(pdata); +} + +static int xgbe_phy_aneg_done(struct xgbe_prv_data *pdata) +{ + int reg; + + DBGPR("%s\n", __FUNCTION__); + + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_STAT1); + + return (reg & MDIO_AN_STAT1_COMPLETE) ? 1 : 0; +} + +static void xgbe_phy_read_status(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct phy_device *phydev = phy_data->phydev; + int reg, link_aneg; + + pdata->phy.link = 1; + + if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) { + netif_carrier_off(pdata->netdev); + + pdata->phy.link = 0; + goto update_link; + } + + link_aneg = (pdata->phy.autoneg == AUTONEG_ENABLE); + + phydev->drv->read_status(phydev); + /* Pop out old values */ + phydev->drv->read_status(phydev); + if (!phydev->link) + pdata->phy.link &= phydev->link; + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + pdata->phy.link &= (reg & MDIO_STAT1_LSTATUS) ? 1 : 0; + + reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_STAT1); + pdata->phy.link &= (reg & MDIO_STAT1_LSTATUS) ? 1 : 0; + + if (pdata->phy.link) { + if (link_aneg && !xgbe_phy_aneg_done(pdata)) { + return; + } + + if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) + clear_bit(XGBE_LINK_INIT, &pdata->dev_state); + + netif_carrier_on(pdata->netdev); + } else { + if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) { + if (link_aneg) + return; + } + + netif_carrier_off(pdata->netdev); + + /* If KX4 mode is enabled training doesn't affect behavior */ + xgbe_an_restart_kr_training(pdata); + /* Pop out old values */ + XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_STAT1); + } + +update_link: + xgbe_phy_update_link(pdata); +} + +static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) +{ + int reg; + + DBGPR("%s\n", __FUNCTION__); + + pdata->link_check = jiffies; + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1); + + /* Disable auto negotiation in any case! */ + reg &= ~MDIO_AN_CTRL1_ENABLE; + pdata->phy.autoneg = AUTONEG_DISABLE; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_CTRL1, reg); + + return 0; +} + +static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) +{ + if (speed == SPEED_10000) + return true; + + return false; +} + +/** + * xgbe_an_isr() - dummy + */ +static irqreturn_t xgbe_an_isr(struct xgbe_prv_data *pdata) +{ + DBGPR("Unhandled AN IRQ\n"); + + return IRQ_HANDLED; +} + +void xgbe_init_function_ptrs_phy_v3(struct xgbe_phy_if *phy_if) +{ + phy_if->phy_init = xgbe_phy_config_init; + phy_if->phy_exit = xgbe_phy_exit; + + phy_if->phy_reset = xgbe_phy_soft_reset; + phy_if->phy_start = xgbe_phy_start; + phy_if->phy_stop = xgbe_phy_stop; + + phy_if->phy_status = xgbe_phy_read_status; + phy_if->phy_config_aneg = xgbe_phy_config_aneg; + + phy_if->phy_valid_speed = xgbe_phy_valid_speed; + + phy_if->an_isr = xgbe_an_isr; +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c index 4ebd2410185a9..7c980af1fabfa 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c @@ -126,6 +126,7 @@ #include #include #include +#include #include #include #include @@ -296,38 +297,18 @@ static struct xgbe_version_data *xgbe_get_vdata(struct xgbe_prv_data *pdata) : xgbe_of_vdata(pdata); } -static int xgbe_platform_probe(struct platform_device *pdev) +static int xgbe_init_function_plat_amd(struct xgbe_prv_data *pdata) { - struct xgbe_prv_data *pdata; - struct device *dev = &pdev->dev; + unsigned int phy_memnum, phy_irqnum, dma_irqnum, dma_irqend; + struct platform_device *pdev = pdata->platdev; struct platform_device *phy_pdev; - const char *phy_mode; - unsigned int phy_memnum, phy_irqnum; - unsigned int dma_irqnum, dma_irqend; - enum dev_dma_attr attr; + struct device *dev = pdata->dev; int ret; - pdata = xgbe_alloc_pdata(dev); - if (IS_ERR(pdata)) { - ret = PTR_ERR(pdata); - goto err_alloc; - } - - pdata->platdev = pdev; - pdata->adev = ACPI_COMPANION(dev); - platform_set_drvdata(pdev, pdata); - - /* Check if we should use ACPI or DT */ - pdata->use_acpi = dev->of_node ? 0 : 1; - - /* Get the version data */ - pdata->vdata = xgbe_get_vdata(pdata); - phy_pdev = xgbe_get_phy_pdev(pdata); if (!phy_pdev) { dev_err(dev, "unable to obtain phy device\n"); - ret = -EINVAL; - goto err_phydev; + return -EINVAL; } pdata->phy_platdev = phy_pdev; pdata->phy_dev = &phy_pdev->dev; @@ -400,28 +381,6 @@ static int xgbe_platform_probe(struct platform_device *pdev) if (netif_msg_probe(pdata)) dev_dbg(dev, "sir1_regs = %p\n", pdata->sir1_regs); - /* Retrieve the MAC address */ - ret = device_property_read_u8_array(dev, XGBE_MAC_ADDR_PROPERTY, - pdata->mac_addr, - sizeof(pdata->mac_addr)); - if (ret || !is_valid_ether_addr(pdata->mac_addr)) { - dev_err(dev, "invalid %s property\n", XGBE_MAC_ADDR_PROPERTY); - if (!ret) - ret = -EINVAL; - goto err_io; - } - - /* Retrieve the PHY mode - it must be "xgmii" */ - ret = device_property_read_string(dev, XGBE_PHY_MODE_PROPERTY, - &phy_mode); - if (ret || strcmp(phy_mode, phy_modes(PHY_INTERFACE_MODE_XGMII))) { - dev_err(dev, "invalid %s property\n", XGBE_PHY_MODE_PROPERTY); - if (!ret) - ret = -EINVAL; - goto err_io; - } - pdata->phy_mode = PHY_INTERFACE_MODE_XGMII; - /* Check for per channel interrupt support */ if (device_property_present(dev, XGBE_DMA_IRQS_PROPERTY)) { pdata->per_channel_irq = 1; @@ -436,29 +395,6 @@ static int xgbe_platform_probe(struct platform_device *pdev) if (ret) goto err_io; - /* Set the DMA coherency values */ - attr = device_get_dma_attr(dev); - if (attr == DEV_DMA_NOT_SUPPORTED) { - dev_err(dev, "DMA is not supported"); - ret = -ENODEV; - goto err_io; - } - pdata->coherent = (attr == DEV_DMA_COHERENT); - if (pdata->coherent) { - pdata->arcr = XGBE_DMA_OS_ARCR; - pdata->awcr = XGBE_DMA_OS_AWCR; - } else { - pdata->arcr = XGBE_DMA_SYS_ARCR; - pdata->awcr = XGBE_DMA_SYS_AWCR; - } - - /* Set the maximum fifo amounts */ - pdata->tx_max_fifo_size = pdata->vdata->tx_max_fifo_size; - pdata->rx_max_fifo_size = pdata->vdata->rx_max_fifo_size; - - /* Set the hardware channel and queue counts */ - xgbe_set_counts(pdata); - /* Always have XGMAC and XPCS (auto-negotiation) interrupts */ pdata->irq_count = 2; @@ -491,6 +427,219 @@ static int xgbe_platform_probe(struct platform_device *pdev) goto err_io; pdata->an_irq = ret; + return 0; + +err_io: + platform_device_put(phy_pdev); + + return ret; +} + +static void xgbe_init_function_disclk_baikal(void *data) +{ + struct xgbe_prv_data *pdata = data; + + clk_disable_unprepare(pdata->sysclk); +} + +static int xgbe_init_function_plat_baikal(struct xgbe_prv_data *pdata) +{ + struct platform_device *pdev = pdata->platdev; + struct device *dev = pdata->dev; + struct device_node *phy_node; + struct mdio_device *mdio_dev; + int ret; + + phy_node = of_parse_phandle(dev->of_node, "phy-handle", 0); + if (!phy_node) { + dev_err(dev, "unable to obtain phy node\n"); + return -ENODEV; + } + + /* Nothing more sophisticated available at the moment... */ + mdio_dev = of_mdio_find_device(phy_node); + of_node_put(phy_node); + if (!mdio_dev) { + dev_err_probe(dev, -EPROBE_DEFER, "unable to obtain mdio device\n"); + return -EPROBE_DEFER; + } + + pdata->phy_platdev = NULL; + pdata->phy_dev = &mdio_dev->dev; + + /* Obtain the CSR regions of the device */ + pdata->xgmac_regs = devm_platform_ioremap_resource_byname(pdev, "stmmaceth"); + if (IS_ERR(pdata->xgmac_regs)) { + dev_err(dev, "xgmac ioremap failed\n"); + ret = PTR_ERR(pdata->xgmac_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "xgmac_regs = %p\n", pdata->xgmac_regs); + + pdata->xpcs_regs = devm_platform_ioremap_resource_byname(pdev, "xpcs"); + if (IS_ERR(pdata->xpcs_regs)) { + dev_err(dev, "xpcs ioremap failed\n"); + ret = PTR_ERR(pdata->xpcs_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "xpcs_regs = %p\n", pdata->xpcs_regs); + + /* Obtain the platform clocks setting */ + pdata->apbclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(pdata->apbclk)) { + dev_err(dev, "apb devm_clk_get failed\n"); + ret = PTR_ERR(pdata->apbclk); + goto err_io; + } + + pdata->sysclk = devm_clk_get(dev, "stmmaceth"); + if (IS_ERR(pdata->sysclk)) { + dev_err(dev, "dma devm_clk_get failed\n"); + ret = PTR_ERR(pdata->sysclk); + goto err_io; + } + pdata->sysclk_rate = clk_get_rate(pdata->sysclk); + + pdata->ptpclk = devm_clk_get(dev, "ptp_ref"); + if (IS_ERR(pdata->ptpclk)) { + dev_err(dev, "ptp devm_clk_get failed\n"); + ret = PTR_ERR(pdata->ptpclk); + goto err_io; + } + pdata->ptpclk_rate = clk_get_rate(pdata->ptpclk); + + pdata->refclk = devm_clk_get(dev, "tx"); + if (IS_ERR(pdata->refclk)) { + dev_err(dev, "ref devm_clk_get failed\n"); + ret = PTR_ERR(pdata->refclk); + goto err_io; + } + + /* Even though it's claimed that the CSR clock source is different from + * the application clock the CSRs are still unavailable until the DMA + * clock signal is enabled. + */ + ret = clk_prepare_enable(pdata->sysclk); + if (ret) { + dev_err(dev, "sys clock enable failed\n"); + goto err_io; + } + + ret = devm_add_action_or_reset(dev, xgbe_init_function_disclk_baikal, pdata); + if (ret) { + dev_err(dev, "sys clock undo registration failed\n"); + goto err_io; + } + + /* Forget about the per-channel IRQs for now... */ + pdata->per_channel_irq = 0; // 1 + pdata->channel_irq_mode = XGBE_IRQ_MODE_EDGE; // XGBE_IRQ_MODE_LEVEL; + + pdata->irq_count = 1; + + ret = platform_get_irq_byname(pdev, "macirq"); + if (ret < 0) + goto err_io; + pdata->dev_irq = ret; + pdata->an_irq = pdata->dev_irq; + + return 0; + +err_io: + put_device(pdata->phy_dev); + + return ret; +} + +static int xgbe_platform_probe(struct platform_device *pdev) +{ + struct xgbe_prv_data *pdata; + struct device *dev = &pdev->dev; + const char *phy_mode; + enum dev_dma_attr attr; + int ret; + + pdata = xgbe_alloc_pdata(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + pdata->platdev = pdev; + pdata->adev = ACPI_COMPANION(dev); + platform_set_drvdata(pdev, pdata); + + /* Check if we should use ACPI or DT */ + pdata->use_acpi = dev->of_node ? 0 : 1; + + /* Get the version data */ + pdata->vdata = xgbe_get_vdata(pdata); + + /* Platform-specific resources setup */ + ret = pdata->vdata->init_function_plat_impl(pdata); + if (ret) + goto err_plat; + + /* Activate basic clocks */ + ret = clk_prepare_enable(pdata->apbclk); + if (ret) { + dev_err(dev, "apb clock enable failed\n"); + goto err_apb; + } + + ret = clk_prepare_enable(pdata->refclk); + if (ret) { + dev_err(dev, "ref clock enable failed\n"); + goto err_ref; + } + + /* Retrieve the MAC address */ + ret = device_property_read_u8_array(dev, XGBE_MAC_ADDR_PROPERTY, + pdata->mac_addr, + sizeof(pdata->mac_addr)); + if (ret || !is_valid_ether_addr(pdata->mac_addr)) { + dev_err(dev, "invalid %s property\n", XGBE_MAC_ADDR_PROPERTY); + if (!ret) + ret = -EINVAL; + goto err_io; + } + + /* Retrieve the PHY mode - it must be "xgmii" */ + ret = device_property_read_string(dev, XGBE_PHY_MODE_PROPERTY, + &phy_mode); + if (ret || strcmp(phy_mode, phy_modes(PHY_INTERFACE_MODE_XGMII))) { + dev_err(dev, "invalid %s property\n", XGBE_PHY_MODE_PROPERTY); + if (!ret) + ret = -EINVAL; + goto err_io; + } + pdata->phy_mode = PHY_INTERFACE_MODE_XGMII; + + /* Set the DMA coherency values */ + attr = device_get_dma_attr(dev); + if (attr == DEV_DMA_NOT_SUPPORTED) { + dev_err(dev, "DMA is not supported"); + ret = -ENODEV; + goto err_io; + } + pdata->coherent = (attr == DEV_DMA_COHERENT); + if (pdata->coherent) { + pdata->arcr = XGBE_DMA_OS_ARCR; + pdata->awcr = XGBE_DMA_OS_AWCR; + } else { + pdata->arcr = XGBE_DMA_SYS_ARCR; + pdata->awcr = XGBE_DMA_SYS_AWCR; + } + + /* Set the maximum fifo amounts */ + pdata->tx_max_fifo_size = pdata->vdata->tx_max_fifo_size; + pdata->rx_max_fifo_size = pdata->vdata->rx_max_fifo_size; + + /* Set the hardware channel and queue counts */ + xgbe_set_counts(pdata); + /* Configure the netdev resource */ ret = xgbe_config_netdev(pdata); if (ret) @@ -501,9 +650,15 @@ static int xgbe_platform_probe(struct platform_device *pdev) return 0; err_io: - platform_device_put(phy_pdev); + clk_disable_unprepare(pdata->refclk); + +err_ref: + clk_disable_unprepare(pdata->apbclk); -err_phydev: +err_apb: + put_device(pdata->phy_dev); + +err_plat: xgbe_free_pdata(pdata); err_alloc: @@ -518,7 +673,11 @@ static int xgbe_platform_remove(struct platform_device *pdev) xgbe_deconfig_netdev(pdata); - platform_device_put(pdata->phy_platdev); + clk_disable_unprepare(pdata->refclk); + + clk_disable_unprepare(pdata->apbclk); + + put_device(pdata->phy_dev); xgbe_free_pdata(pdata); @@ -573,6 +732,7 @@ static int xgbe_platform_resume(struct device *dev) #endif /* CONFIG_PM_SLEEP */ static const struct xgbe_version_data xgbe_v1 = { + .init_function_plat_impl = xgbe_init_function_plat_amd, .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v1, .xpcs_access = XGBE_XPCS_ACCESS_V1, .tx_max_fifo_size = 81920, @@ -580,6 +740,19 @@ static const struct xgbe_version_data xgbe_v1 = { .tx_tstamp_workaround = 1, }; +static const struct xgbe_version_data xgbe_v3 = { + .init_function_plat_impl = xgbe_init_function_plat_baikal, + .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v3, + .xpcs_access = XGBE_XPCS_ACCESS_V1, + .tx_max_fifo_size = 32768, + .rx_max_fifo_size = 32768, + .blen = DMA_SBMR_BLEN_16, + .pbl = DMA_PBL_256, + .rd_osr_limit = 8, + .wr_osr_limit = 8, + .tx_tstamp_workaround = 1, +}; + #ifdef CONFIG_ACPI static const struct acpi_device_id xgbe_acpi_match[] = { { .id = "AMDI8001", @@ -594,6 +767,8 @@ MODULE_DEVICE_TABLE(acpi, xgbe_acpi_match); static const struct of_device_id xgbe_of_match[] = { { .compatible = "amd,xgbe-seattle-v1a", .data = &xgbe_v1 }, + { .compatible = "baikal,bt1-xgmac", + .data = &xgbe_v3 }, {}, }; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 3305979a9f7c1..3a9d1214977f6 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -1000,11 +1000,16 @@ struct xgbe_hw_features { }; struct xgbe_version_data { + int (*init_function_plat_impl)(struct xgbe_prv_data *); void (*init_function_ptrs_phy_impl)(struct xgbe_phy_if *); enum xgbe_xpcs_access xpcs_access; unsigned int mmc_64bit; unsigned int tx_max_fifo_size; unsigned int rx_max_fifo_size; + unsigned int blen; + unsigned int pbl; + unsigned int rd_osr_limit; + unsigned int wr_osr_limit; unsigned int tx_tstamp_workaround; unsigned int ecc_support; unsigned int i2c_support; @@ -1179,6 +1184,8 @@ struct xgbe_prv_data { unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; /* Device clocks */ + struct clk *apbclk; + struct clk *refclk; struct clk *sysclk; unsigned long sysclk_rate; struct clk *ptpclk; @@ -1305,6 +1312,7 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *); void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *); void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *); void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *); +void xgbe_init_function_ptrs_phy_v3(struct xgbe_phy_if *); void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *); void xgbe_init_function_ptrs_i2c(struct xgbe_i2c_if *); const struct net_device_ops *xgbe_get_netdev_ops(void); diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 929cfc22cd0cf..16445f2e3ed3c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -66,6 +66,15 @@ config DWMAC_ANARION This selects the Anarion SoC glue layer support for the stmmac driver. +config DWMAC_BT1 + tristate "Baikal-T1 GMAC support" + depends on OF && (MIPS_BAIKAL_T1 || COMPILE_TEST) + help + Support for Baikal-T1 GMAC Ethernet controller. + + This selects the Baikal-T1 platform specific glue layer of the + STMMAC driver. + config DWMAC_INGENIC tristate "Ingenic MAC support" default MACH_INGENIC diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index d4e12e9ace4ff..222240510748d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -14,6 +14,7 @@ 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_INGENIC) += dwmac-ingenic.o obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o 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..bf3ec1e722aa5 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-bt1.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Baikal-T1 GMAC driver + * + * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dwmac1000.h" +#include "dwmac_dma.h" +#include "stmmac.h" +#include "stmmac_platform.h" + +/* General Purpose IO */ +#define GMAC_GPIO 0x000000e0 +#define GMAC_GPIO_GPIS BIT(0) +#define GMAC_GPIO_GPO BIT(8) + +struct bt1_gmac { + struct device *dev; + struct clk *tx_clk; +}; + +static int bt1_gmac_clks_config(void *bsp_priv, bool enable) +{ + struct bt1_gmac *btg = bsp_priv; + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(btg->tx_clk); + if (ret) + dev_err(btg->dev, "Failed to enable Tx clock\n"); + } else { + clk_disable_unprepare(btg->tx_clk); + } + + return ret; +} + +static int bt1_gmac_bus_reset(void *bsp_priv) +{ + struct bt1_gmac *btg = bsp_priv; + struct stmmac_priv *priv = netdev_priv(dev_get_drvdata(btg->dev)); + + writel(0, priv->ioaddr + GMAC_GPIO); + fsleep(priv->mii->reset_delay_us); + writel(GMAC_GPIO_GPO, priv->ioaddr + GMAC_GPIO); + if (priv->mii->reset_post_delay_us > 0) + fsleep(priv->mii->reset_post_delay_us); + + return 0; +} + +/* Clean the basic MAC registers up. Note the MAC interrupts are enabled by + * default after reset. Let's mask them out so not to have any spurious + * MAC-related IRQ generated during the cleanup procedure. + */ +static void bt1_gmac_core_clean(struct stmmac_priv *priv) +{ + int i; + + writel(0x7FF, priv->ioaddr + GMAC_INT_MASK); + writel(0, priv->ioaddr + GMAC_CONTROL); + writel(0, priv->ioaddr + GMAC_FRAME_FILTER); + writel(0, priv->ioaddr + GMAC_HASH_HIGH); + writel(0, priv->ioaddr + GMAC_HASH_LOW); + writel(0, priv->ioaddr + GMAC_FLOW_CTRL); + writel(0, priv->ioaddr + GMAC_VLAN_TAG); + writel(0, priv->ioaddr + GMAC_DEBUG); + writel(0x80000000, priv->ioaddr + GMAC_PMT); + writel(0, priv->ioaddr + LPI_CTRL_STATUS); + writel(0x03e80000, priv->ioaddr + LPI_TIMER_CTRL); + for (i = 0; i < 15; ++i) { + writel(0x0000ffff, priv->ioaddr + GMAC_ADDR_HIGH(i)); + writel(0xffffffff, priv->ioaddr + GMAC_ADDR_LOW(i)); + } + writel(0, priv->ioaddr + GMAC_PCS_BASE); + writel(0, priv->ioaddr + GMAC_RGSMIIIS); + writel(0x1, priv->ioaddr + GMAC_MMC_CTRL); + readl(priv->ioaddr + GMAC_INT_STATUS); + readl(priv->ioaddr + GMAC_PMT); + readl(priv->ioaddr + LPI_CTRL_STATUS); +} + +/* Clean the basic DMA registers up */ +static void bt1_gmac_dma_clean(struct stmmac_priv *priv) +{ + writel(0, priv->ioaddr + DMA_INTR_ENA); + writel(0x00020100, priv->ioaddr + DMA_BUS_MODE); + writel(0, priv->ioaddr + DMA_RCV_BASE_ADDR); + writel(0, priv->ioaddr + DMA_TX_BASE_ADDR); + writel(0x00100000, priv->ioaddr + DMA_CONTROL); + writel(0x00110001, priv->ioaddr + DMA_AXI_BUS_MODE); + writel(0x0001FFFF, priv->ioaddr + DMA_STATUS); +} + +static int bt1_gmac_swr_reset(void *bsp_priv) +{ + struct bt1_gmac *btg = bsp_priv; + struct stmmac_priv *priv = netdev_priv(dev_get_drvdata(btg->dev)); + + bt1_gmac_core_clean(priv); + + bt1_gmac_dma_clean(priv); + + return 0; +} + +static void bt1_gmac_fix_mac_speed(void *bsp_priv, unsigned int speed) +{ + struct bt1_gmac *btg = bsp_priv; + unsigned long rate; + int ret; + + switch (speed) { + case SPEED_1000: + rate = 250000000; + break; + case SPEED_100: + rate = 50000000; + break; + case SPEED_10: + rate = 5000000; + break; + default: + dev_err(btg->dev, "Unsupported speed %u\n", speed); + return; + } + + /* The clock must be gated to successfully update the rate */ + clk_disable_unprepare(btg->tx_clk); + + ret = clk_set_rate(btg->tx_clk, rate); + if (ret) + dev_err(btg->dev, "Failed to update Tx clock rate %lu\n", rate); + + ret = clk_prepare_enable(btg->tx_clk); + if (ret) + dev_err(btg->dev, "Failed to re-enable Tx clock\n"); + +} + +static int bt1_gmac_probe(struct platform_device *pdev) +{ + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + struct bt1_gmac *btg; + int ret; + + btg = devm_kzalloc(&pdev->dev, sizeof(*btg), GFP_KERNEL); + if (!btg) + return -ENOMEM; + + ret = stmmac_get_platform_resources(pdev, &stmmac_res); + if (ret) + return ret; + + plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac); + if (IS_ERR(plat_dat)) { + dev_err(&pdev->dev, "DT configuration failed\n"); + return PTR_ERR(plat_dat); + } + + btg->dev = &pdev->dev; + + btg->tx_clk = devm_clk_get(&pdev->dev, "tx"); + if (IS_ERR(btg->tx_clk)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(btg->tx_clk), + "Failed to get Tx clock\n"); + goto err_remove_config_dt; + } + + ret = clk_prepare_enable(btg->tx_clk); + if (ret) { + dev_err(btg->dev, "Failed to pre-enable Tx clock\n"); + goto err_remove_config_dt; + } + + plat_dat->addr64 = 32; + plat_dat->has_gmac = 1; + plat_dat->enh_desc = 1; + plat_dat->tx_coe = 1; + plat_dat->rx_coe = 1; + plat_dat->pmt = 1; + plat_dat->unicast_filter_entries = 8; + plat_dat->multicast_filter_bins = 0; + plat_dat->clks_config = bt1_gmac_clks_config; + plat_dat->bus_reset = bt1_gmac_bus_reset; + plat_dat->swr_reset = bt1_gmac_swr_reset; + plat_dat->fix_mac_speed = bt1_gmac_fix_mac_speed; + plat_dat->bsp_priv = btg; + plat_dat->mdio_bus_data->needs_reset = true; + + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (ret) + goto err_disable_tx_clk; + + return 0; + +err_disable_tx_clk: + clk_disable_unprepare(btg->tx_clk); + +err_remove_config_dt: + stmmac_remove_config_dt(pdev, plat_dat); + + return ret; +} + +static const struct of_device_id bt1_gmac_match[] = { + { .compatible = "baikal,bt1-gmac"}, + { } +}; +MODULE_DEVICE_TABLE(of, bt1_gmac_match); + +static struct platform_driver bt1_gmac_driver = { + .probe = bt1_gmac_probe, + .remove = stmmac_pltfr_remove, + .driver = { + .name = "bt1-gmac", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = of_match_ptr(bt1_gmac_match), + }, +}; +module_platform_driver(bt1_gmac_driver); + +MODULE_AUTHOR("Serge Semin "); +MODULE_DESCRIPTION("Baikal-T1 GMAC glue driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b4f83c8655684..cdf8559fcb175 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -296,7 +296,12 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv) { u32 clk_rate; - 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. @@ -2898,7 +2903,10 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE)) atds = 1; - ret = stmmac_reset(priv, priv->ioaddr); + if (priv->plat->swr_reset) + ret = priv->plat->swr_reset(priv->plat->bsp_priv); + else + ret = stmmac_reset(priv, priv->ioaddr); if (ret) { dev_err(priv->device, "Failed to reset the dma\n"); return ret; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index a5d150c5f3d8c..fdc9493b2c66e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -357,6 +357,13 @@ int stmmac_mdio_reset(struct mii_bus *bus) struct net_device *ndev = bus->priv; struct stmmac_priv *priv = netdev_priv(ndev); unsigned int mii_address = priv->hw->mii.addr; + int ret; + + if (priv->plat->bus_reset) { + ret = priv->plat->bus_reset(priv->plat->bsp_priv); + if (ret) + return ret; + } #ifdef CONFIG_OF if (priv->device->of_node) { @@ -450,7 +457,7 @@ int stmmac_mdio_register(struct net_device *ndev) if (!mdio_bus_data) return 0; - new_bus = mdiobus_alloc(); + priv->mii = new_bus = mdiobus_alloc(); if (!new_bus) return -ENOMEM; @@ -499,7 +506,7 @@ int stmmac_mdio_register(struct net_device *ndev) stmmac_xgmac2_mdio_read(new_bus, 0, MII_ADDR_C45); if (priv->plat->phy_node || mdio_node) - goto bus_register_done; + return 0; found = 0; for (addr = 0; addr < max_addr; addr++) { @@ -536,9 +543,6 @@ int stmmac_mdio_register(struct net_device *ndev) goto no_phy_found; } -bus_register_done: - priv->mii = new_bus; - return 0; no_phy_found: diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 9f5cac4000da6..51228e1585959 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: @@ -96,7 +97,8 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev) struct device_node *np; struct stmmac_axi *axi; - np = of_parse_phandle(pdev->dev.of_node, "snps,axi-config", 0); + np = of_parse_phandle(pdev->dev.of_node, "snps,axi-config", 0) ?: + of_get_child_by_name(pdev->dev.of_node, "axi-config"); if (!np) return NULL; @@ -151,11 +153,13 @@ static int stmmac_mtl_setup(struct platform_device *pdev, plat->rx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; plat->tx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; - rx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-rx-config", 0); + rx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-rx-config", 0) ?: + of_get_child_by_name(pdev->dev.of_node, "mtl-rx-config"); if (!rx_node) return ret; - tx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-tx-config", 0); + tx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-tx-config", 0) ?: + of_get_child_by_name(pdev->dev.of_node, "mtl-tx-config"); if (!tx_node) { of_node_put(rx_node); return ret; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 902495afcb381..3e89449235f7c 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -207,6 +207,12 @@ config MARVELL_88X2222_PHY Support for the Marvell 88X2222 Dual-port Multi-speed Ethernet Transceiver. +config MARVELL_88X2222_KR_PHY + tristate "Marvell 88X2222 KR/KX4 PHY" + help + Support for the Marvell 88X2222 KR/KX4 Dual-port Multi-speed + Ethernet Transceiver. + config MAXLINEAR_GPHY tristate "Maxlinear Ethernet PHYs" help diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index b2728d00fc9a1..b7f6f5eb5c6ea 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o obj-$(CONFIG_MARVELL_PHY) += marvell.o obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o +obj-$(CONFIG_MARVELL_88X2222_KR_PHY) += marvell-88x2222-kr.o obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o diff --git a/drivers/net/phy/marvell-88x2222-kr.c b/drivers/net/phy/marvell-88x2222-kr.c new file mode 100644 index 0000000000000..b963a722a4d1e --- /dev/null +++ b/drivers/net/phy/marvell-88x2222-kr.c @@ -0,0 +1,442 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Marvell Integrated Dual-port + * Multi-speed Ethernet Transceiver 88x2222 KR/KX4 mode + * + * Copyright (c) 2015, 2016, 2020 Baikal Electronics JSC. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 31.F002 Line side mode (ch.3.1.2, pg.46) */ +#define MV_MODE_LINE_SHF 8 +#define MV_MODE_LINE_10GBR (0x71UL << 8) +#define MV_MODE_LINE_10GBW (0x74UL << 8) +#define MV_MODE_LINE_2GBX_AN_OFF (0x76UL << 8) +#define MV_MODE_LINE_1GBR_AN_OFF (0x72UL << 8) +#define MV_MODE_LINE_1GBR_AN_ON (0x73UL << 8) +#define MV_MODE_LINE_SGMII_SYS_AN_OFF (0x7CUL << 8) +#define MV_MODE_LINE_SGMII_SYS_AN_ON (0x7DUL << 8) +#define MV_MODE_LINE_SGMII_NET_AN_OFF (0x7EUL << 8) +#define MV_MODE_LINE_SGMII_NET_AN_ON (0x7FUL << 8) +#define MV_MODE_LINE_DEFAULT MV_MODE_LINE_10GBR +#define MV_MODE_LINE_OF_NAME "baikal,line-mode" + +/* 31.F002 Host side mode (ch.3.1.2, pg.46) */ +#define MV_MODE_HOST_SHF 0 +#define MV_MODE_HOST_10GBR (0x71UL << 0) +#define MV_MODE_HOST_10GBX2 (0x72UL << 0) +#define MV_MODE_HOST_10GBX4 (0x73UL << 0) +#define MV_MODE_HOST_2GBX_AN_OFF (0x76UL << 0) +#define MV_MODE_HOST_1GBR_AN_OFF (0x7AUL << 0) +#define MV_MODE_HOST_1GBR_AN_ON (0x7BUL << 0) +#define MV_MODE_HOST_SGMII_SYS_AN_OFF (0x7CUL << 0) +#define MV_MODE_HOST_SGMII_SYS_AN_ON (0x7DUL << 0) +#define MV_MODE_HOST_SGMII_NET_AN_OFF (0x7EUL << 0) +#define MV_MODE_HOST_SGMII_NET_AN_ON (0x7FUL << 0) +#define MV_MODE_HOST_DEFAULT MV_MODE_HOST_10GBR +#define MV_MODE_HOST_OF_NAME "baikal,host-mode" + +/* 31.F402 Host side line muxing (ch.3.1.5, pg.48) */ +#define MV_ATT_10GBX2_SHF 11 +#define MV_ATT_10GBX2_LANE_0145 (0UL << 11) +#define MV_ATT_10GBX2_LANE_0123 (1UL << 11) +#define MV_ATT_10GBR_SHF 9 +#define MV_ATT_10GBR_LANE_0246 (0UL << 9) +#define MV_ATT_10GBR_LANE_0123 (1UL << 9) +#define MV_ATT_2GBR_SHF 8 +#define MV_ATT_2GBR_LANE_0246 (0UL << 8) +#define MV_ATT_2GBR_LANE_0123 (1UL << 8) +#define MV_ATT_1GBR_SHF 8 +#define MV_ATT_1GBR_LANE_0246 (0UL << 8) +#define MV_ATT_1GBR_LANE_0123 (1UL << 8) +#define MV_ATT_DEFAULT 0 +#define MV_ATT_OF_NAME "baikal,mux" + +/* 31.F003 Software reset (ch.3.2 pg.50) */ +#define MV_SW_RST_HOST_SHF 7 +#define MV_SW_RST_HOST (1UL << 7) +#define MV_SW_RST_LINE_SHF 15 +#define MV_SW_RST_LINE (1UL << 15) +#define MV_SW_RST_ALL (MV_SW_RST_HOST | MV_SW_RST_LINE) + +/* 31.F012 GPIO data */ +#define MV_GPIO_TXDISABLE_DATA_SHF 8 + +/* 31.F013 Tristate Control */ +#define MV_GPIO_TXDISABLE_OUTP_EN_SHF 8 + +/* 31.F016 Interrupt type 3 */ +#define MV_GPIO_TXDISABLE_FN_SHF 3 +#define MV_GPIO_TXDISABLE_FN_GPIO 0x1 + +/* Devices in package and registers */ +#define MV_DEV_10GBW_IRQ_ENABLE 0x8000 +#define MV_DEV_10GBW_IRQ_STATUS 0x8001 +#define MV_DEV_10GBW_IRQ_REALTIME 0x8002 + +#define MV_DEV_10GBR_ANEG 0x2000 +#define MV_DEV_10GBR_IRQ_ENABLE 0x8000 +#define MV_DEV_10GBR_IRQ_STATUS 0x8001 +#define MV_DEV_10GBR_IRQ_REALTIME 0x8002 + +#define MV_DEV_GBX_IRQ_ENABLE 0xA000 +#define MV_DEV_GBX_IRQ_STATUS 0xA001 +#define MV_DEV_GBX_IRQ_REALTIME 0xA002 + +#define MV_DEV_MISC_IRQ_ENABLE 0xF00A +#define MV_DEV_MISC_IRQ_STATUS 0xF00B + +#define MV_DEV_GPIO_DATA 0xF012 +#define MV_DEV_GPIO_TRISTATE_CTL 0xF013 +#define MV_DEV_GPIO_INTERRUPT_TYPE_3 0xF016 + +#define MV_DEV_CHIP_HOST_LINE 0xF002 +#define MV_DEV_CHIP_RESET 0xF003 +#define MV_DEV_CHIP_MUX 0xF402 +#define MV_DEV_CHIP_IRQ_STATUS 0xF420 +#define MV_DEV_CHIP_IRQ_CONTROL 0xF421 + +#define MV_RESET_DELAY_US 500 + +struct mode { + unsigned int mode_num; + char mode_name[16]; +}; + +static struct mode line_modes[] = { + {MV_MODE_LINE_10GBR, "KR"}, + {MV_MODE_LINE_10GBW, "10GBW"}, + {MV_MODE_LINE_2GBX_AN_OFF, "2GBX_AN_OFF"}, + {MV_MODE_LINE_1GBR_AN_OFF, "1GBR_AN_OFF"}, + {MV_MODE_LINE_1GBR_AN_ON, "1GBR_AN_ON"}, + {MV_MODE_LINE_SGMII_SYS_AN_OFF, "SGMII_SYS_AN_OFF"}, + {MV_MODE_LINE_SGMII_SYS_AN_ON, "SGMI_SYS_AN_ON"}, + {MV_MODE_LINE_SGMII_NET_AN_OFF, "SMGII_NET_AN_OFF"}, + {MV_MODE_LINE_SGMII_NET_AN_ON, "SGMII_NET_AN_ON"} +}; + +static struct mode host_modes[] = { + {MV_MODE_HOST_10GBR, "KR"}, + {MV_MODE_HOST_10GBX2, "10GBX2"}, + {MV_MODE_HOST_10GBX4, "KX4"}, + {MV_MODE_HOST_2GBX_AN_OFF, "2GBX_AN_OFF"}, + {MV_MODE_HOST_1GBR_AN_OFF, "1GBR_AN_OFF"}, + {MV_MODE_HOST_1GBR_AN_ON, "1GBR_AN_ON"}, + {MV_MODE_HOST_SGMII_SYS_AN_OFF, "SGMII_SYS_AN_OFF"}, + {MV_MODE_HOST_SGMII_SYS_AN_ON, "SGMII_SYS_AN_ON"}, + {MV_MODE_HOST_SGMII_NET_AN_OFF, "SGMII_NE_AN_OFF"}, + {MV_MODE_HOST_SGMII_NET_AN_ON, "SGMII_NET_AN_ON"} +}; + +struct mv88x2222_data { + int line_mode; + int host_mode; + int mux; +}; + +static void *marvell_of_get_data(struct phy_device *phydev) +{ + struct device_node *np = phydev->mdio.dev.of_node; + struct mv88x2222_data *pdata; + const char *pm; + char mode[32]; + int ret, i; + + pdata = devm_kzalloc(&phydev->mdio.dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + pm = mode; + pdata->line_mode = MV_MODE_LINE_DEFAULT; + ret = of_property_read_string(np, MV_MODE_LINE_OF_NAME, &pm); + if (!ret) { + for(i = 0; i < sizeof(line_modes) / sizeof(struct mode); ++i) { + if(strcasecmp(line_modes[i].mode_name, pm) == 0) { + pdata->line_mode = line_modes[i].mode_num; + break; + } + } + } + + pdata->host_mode = MV_MODE_HOST_DEFAULT; + ret = of_property_read_string(np, MV_MODE_HOST_OF_NAME, &pm); + if (!ret) { + for(i = 0; i < sizeof(host_modes) / sizeof(struct mode); ++i) { + if(strcasecmp(host_modes[i].mode_name, pm) == 0) { + pdata->host_mode = host_modes[i].mode_num; + break; + } + } + } + + /* Default value at now */ + pdata->mux = MV_ATT_DEFAULT; + + return pdata; +} + +static int marvell_soft_reset(struct phy_device *phydev) { + int ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_CHIP_RESET, + MV_SW_RST_ALL); + int count = 50; + + if (ret) { + dev_warn(&phydev->mdio.dev, "software reset failed\n"); + return ret; + } + + do { + usleep_range(MV_RESET_DELAY_US, MV_RESET_DELAY_US + 100); + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_CHIP_RESET); + } while ((ret & MV_SW_RST_ALL) || count--); + + return 0; +} + +static int marvell_config_init(struct phy_device *phydev) +{ + struct mv88x2222_data *pdata = phydev->priv; + int ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_CHIP_HOST_LINE, + pdata->line_mode | pdata->host_mode); + if (ret) + return 1; + + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + + /* + * This must be done after mode set; + */ + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_CHIP_HOST_LINE); + ret |= 0x8000; + phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_CHIP_HOST_LINE, ret); + + marvell_soft_reset(phydev); + + dev_info(&phydev->mdio.dev, "phy(%d, %x)=%x\n", MDIO_MMD_VEND2, + MV_DEV_CHIP_HOST_LINE, + phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_CHIP_HOST_LINE)); + + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Backplane_BIT, + phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + phydev->supported, 1); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported, 1); + + phydev->pause = 0; + phydev->asym_pause = 0; + phydev->interface = PHY_INTERFACE_MODE_XGMII; + phydev->duplex = DUPLEX_FULL; + + switch (pdata->line_mode) { + case MV_MODE_LINE_10GBR: + case MV_MODE_LINE_10GBW: + phydev->speed = SPEED_10000; + break; + case MV_MODE_LINE_2GBX_AN_OFF: + phydev->speed = SPEED_2500; + break; + default: + phydev->speed = SPEED_1000; + break; + } + + return 0; +} + +static int marvell_adjust_tx(struct phy_device *phydev) +{ + int reg, line_link = 1; + + /* Switch tristate to "write to pin/read from register" */ + reg = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_GPIO_TRISTATE_CTL); + phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_GPIO_TRISTATE_CTL,\ + reg | (1 << MV_GPIO_TXDISABLE_OUTP_EN_SHF)); + + /* Switch off TX_DISABLE */ + reg = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_GPIO_DATA); + phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_GPIO_DATA, reg & \ + ~(1 << MV_GPIO_TXDISABLE_DATA_SHF)); + + /* Check if opto-cable is plugged */ + reg = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); + if ((reg < 0) || !(reg & MDIO_STAT1_LSTATUS)) + line_link = 0; + + if (line_link) { + /* It's fine */ + return 0; + + } else { + /* Switch on TX_DISABLE */ + reg = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_GPIO_DATA); + phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_GPIO_DATA, reg | \ + (1 << MV_GPIO_TXDISABLE_DATA_SHF)); + } + + return 1; +} + +static int marvell_update_link(struct phy_device *phydev) +{ + int reg, host_mode, line_mode; + + /* Default link status */ + phydev->link = 1; + + reg = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_CHIP_HOST_LINE); + if (reg < 0) + { + phydev->link = 0; + return 0; + } + + host_mode = reg & 0x007F; + line_mode = reg & 0x7F00; + + /* Read host link status */ + if (host_mode == MV_MODE_HOST_10GBX4) + reg = phy_read_mmd(phydev, MDIO_MMD_PHYXS, 0x1001); + else + reg = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_STAT1); + + if ((reg < 0) || !(reg & MDIO_STAT1_LSTATUS)) + phydev->link = 0; + + /* Read line link status */ + if (line_mode == MV_MODE_LINE_10GBR) + reg = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); + else + reg = phy_read_mmd(phydev, MDIO_MMD_PCS, 0x2001); + + if ((reg < 0) || !(reg & MDIO_STAT1_LSTATUS)) + phydev->link = 0; + + /* + * PMAPMD link status is always broken + * later we need to update this driver; + */ + reg = marvell_adjust_tx(phydev); + if (reg < 0) + phydev->link = 0; + + return 0; +} + +static int marvell_read_status(struct phy_device *phydev) +{ + int reg; + + /* Update the link, but return if there was an error */ + reg = marvell_update_link(phydev); + if (reg < 0) + return reg; + + /* Read line control reg */ + reg = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); + if (reg < 0) + return reg; + + return 0; +} + +static int marvell_config_aneg(struct phy_device *phydev) +{ + linkmode_copy(phydev->advertising, phydev->supported); + + return 0; +} + +static int marvell_probe(struct phy_device *phydev) +{ + struct mv88x2222_data *pdata = NULL; + + if (phydev->mdio.dev.of_node) + pdata = marvell_of_get_data(phydev); + + if (!pdata) { + dev_err(&phydev->mdio.dev, "No PHY platform data\n"); + return -ENODEV; + } + + phydev->priv = pdata; + + dev_info(&phydev->mdio.dev, "PHY detected at 0x%02x\n", + phydev->mdio.addr); + + return 0; +} + +static int marvell_suspend(struct phy_device *phydev) +{ + int reg; + mutex_lock(&phydev->lock); + + /* Switch tristate to "write to pin/read from register" */ + reg = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_GPIO_TRISTATE_CTL); + phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_GPIO_TRISTATE_CTL,\ + reg | (1 << MV_GPIO_TXDISABLE_OUTP_EN_SHF)); + + /* Switch on TX_DISABLE */ + reg = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_GPIO_DATA); + phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_DEV_GPIO_DATA, reg | \ + (1 << MV_GPIO_TXDISABLE_DATA_SHF)); + /* TBD Probably switch to lowpower mode */ + + mutex_unlock(&phydev->lock); + + return 0; +} + +/*static int marvell_match_phy_device(struct phy_device *phydev) +{ + unsigned int phy_id = phydev->c45_ids.device_ids[MDIO_MMD_PCS] & MARVELL_PHY_ID_MASK; + + return (phy_id == MARVELL_PHY_ID_88X2222) || (phy_id == MARVELL_PHY_ID_88X2222R); +}*/ + +static struct phy_driver marvell_drivers[] = { + { + .phy_id = MARVELL_PHY_ID_88X2222, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88X2222 KR/KX4", + .features = 0, + .config_init = marvell_config_init, + .config_aneg = marvell_config_aneg, + .probe = marvell_probe, + //.match_phy_device = marvell_match_phy_device, + .read_status = marvell_read_status, + .soft_reset = marvell_soft_reset, + .resume = genphy_resume, + .suspend = marvell_suspend, + }, +}; +module_phy_driver(marvell_drivers); + +static struct mdio_device_id __maybe_unused marvell_tbl[] = { + { MARVELL_PHY_ID_88X2222, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88X2222R, MARVELL_PHY_ID_MASK }, + { } +}; +MODULE_DEVICE_TABLE(mdio, marvell_tbl); + +MODULE_DESCRIPTION("Marvell 88x2222 KR/KX4 ethernet transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/marvell-88x2222.c b/drivers/net/phy/marvell-88x2222.c index d8b31d4d2a73e..c587e7e890020 100644 --- a/drivers/net/phy/marvell-88x2222.c +++ b/drivers/net/phy/marvell-88x2222.c @@ -613,6 +613,7 @@ module_phy_driver(mv2222_drivers); static 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/realtek.c b/drivers/net/phy/realtek.c index 11be60333fa82..1cf068b2c4073 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -629,6 +629,42 @@ static int rtl822x_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, return ret; } +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; @@ -911,6 +947,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, }, { PHY_ID_MATCH_EXACT(0x001cc916), .name = "RTL8211F Gigabit Ethernet", diff --git a/drivers/nvme/host/hwmon.c b/drivers/nvme/host/hwmon.c index 0a586d7129201..94192ab7a02d8 100644 --- a/drivers/nvme/host/hwmon.c +++ b/drivers/nvme/host/hwmon.c @@ -10,9 +10,10 @@ #include "nvme.h" +/* DMA-noncoherent platforms require the cache-aligned buffers */ struct nvme_hwmon_data { + struct nvme_smart_log log ____cacheline_aligned; struct nvme_ctrl *ctrl; - struct nvme_smart_log log; struct mutex read_lock; }; diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 3cef835b375fd..27bf05c97986a 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -135,11 +135,30 @@ static struct pci_bus_region pci_high = {(pci_bus_addr_t) 0x100000000ULL, */ static void pci_clip_resource_to_region(struct pci_bus *bus, struct resource *res, - struct pci_bus_region *region) + struct pci_bus_region *region, + resource_size_t *align) { struct pci_bus_region r; + resource_size_t new_align, offset; pcibios_resource_to_bus(bus, &r, res); + + offset = res->start - r.start; + if (offset & (*align - 1) && (r.start & (*align - 1)) == 0) { + /* + * a) CPU address (resource) differs from PCI bus address + * (pci_bus_region), i.e. address translation is in effect; + * b) PCI bus address is aligned as required; + * c) CPU address is not aligned. + * So, we can relax alignment requirement for CPU address. + */ + new_align = 1 << __ffs(offset); + dev_info(&bus->dev, + "pci_clip_resource_to_region: relaxing alignment from %pa to %pa\n", + align, &new_align); + *align = new_align; + } + if (r.start < region->start) r.start = region->start; if (r.end > region->end) @@ -169,6 +188,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, pci_bus_for_each_resource(bus, r, i) { resource_size_t min_used = min; + resource_size_t res_align = align; if (!r) continue; @@ -184,7 +204,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, continue; avail = *r; - pci_clip_resource_to_region(bus, &avail, region); + pci_clip_resource_to_region(bus, &avail, region, &res_align); /* * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to @@ -199,7 +219,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, /* Ok, try it out.. */ ret = allocate_resource(r, res, size, min_used, max, - align, alignf, alignf_data); + res_align, alignf, alignf_data); if (ret == 0) return 0; } diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 76c0a63a3f648..4d0e84d6f3a24 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -8,12 +8,10 @@ config PCIE_DW config PCIE_DW_HOST bool - depends on PCI_MSI_IRQ_DOMAIN select PCIE_DW config PCIE_DW_EP bool - depends on PCI_ENDPOINT select PCIE_DW config PCI_DRA7XX @@ -22,8 +20,8 @@ config PCI_DRA7XX config PCI_DRA7XX_HOST bool "TI DRA7xx PCIe controller Host Mode" depends on SOC_DRA7XX || COMPILE_TEST - depends on PCI_MSI_IRQ_DOMAIN depends on OF && HAS_IOMEM && TI_PIPE3 + depends on PCI_MSI_IRQ_DOMAIN select PCIE_DW_HOST select PCI_DRA7XX default y if SOC_DRA7XX @@ -38,8 +36,8 @@ config PCI_DRA7XX_HOST config PCI_DRA7XX_EP bool "TI DRA7xx PCIe controller Endpoint Mode" depends on SOC_DRA7XX || COMPILE_TEST - depends on PCI_ENDPOINT depends on OF && HAS_IOMEM && TI_PIPE3 + depends on PCI_ENDPOINT select PCIE_DW_EP select PCI_DRA7XX help @@ -55,7 +53,7 @@ config PCIE_DW_PLAT config PCIE_DW_PLAT_HOST bool "Platform bus based DesignWare PCIe Controller - Host mode" - depends on PCI && PCI_MSI_IRQ_DOMAIN + depends on PCI_MSI_IRQ_DOMAIN select PCIE_DW_HOST select PCIE_DW_PLAT help @@ -138,8 +136,8 @@ config PCI_LAYERSCAPE bool "Freescale Layerscape PCIe controller - Host mode" depends on OF && (ARM || ARCH_LAYERSCAPE || COMPILE_TEST) depends on PCI_MSI_IRQ_DOMAIN - select MFD_SYSCON select PCIE_DW_HOST + select MFD_SYSCON help Say Y here if you want to enable PCIe controller support on Layerscape SoCs to work in Host mode. @@ -214,6 +212,15 @@ config PCIE_ARTPEC6_EP Enables support for the PCIe controller in the ARTPEC-6 SoC to work in endpoint mode. This uses the DesignWare core. +config PCIE_BT1 + tristate "Baikal-T1 PCIe controller" + depends on MIPS_BAIKAL_T1 || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Enables support for the PCIe controller in the Baikal-T1 SoC to work + in host mode. It's based on the Synopsys DWC PCIe v4.60a IP-core. + config PCIE_ROCKCHIP_DW_HOST bool "Rockchip DesignWare PCIe controller" select PCIE_DW @@ -283,8 +290,8 @@ config PCIE_HISI_STB config PCI_MESON tristate "MESON PCIe controller" - depends on PCI_MSI_IRQ_DOMAIN default m if ARCH_MESON + depends on PCI_MSI_IRQ_DOMAIN select PCIE_DW_HOST help Say Y here if you want to enable PCI controller support on Amlogic diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 73244409792cb..27bf06962ce5b 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o +obj-$(CONFIG_PCIE_BT1) += pcie-bt1.o obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index fbbb78f6885e7..cb435b36e6064 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -176,7 +176,7 @@ static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx) dra7xx_pcie_enable_msi_interrupts(dra7xx); } -static int dra7xx_pcie_host_init(struct pcie_port *pp) +static int dra7xx_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); @@ -200,7 +200,7 @@ static const struct irq_domain_ops intx_domain_ops = { .xlate = pci_irqd_intx_xlate, }; -static int dra7xx_pcie_handle_msi(struct pcie_port *pp, int index) +static int dra7xx_pcie_handle_msi(struct dw_pcie_rp *pp, int index) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); unsigned long val; @@ -222,7 +222,7 @@ static int dra7xx_pcie_handle_msi(struct pcie_port *pp, int index) return 1; } -static void dra7xx_pcie_handle_msi_irq(struct pcie_port *pp) +static void dra7xx_pcie_handle_msi_irq(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); int ret, i, count, num_ctrls; @@ -253,8 +253,8 @@ static void dra7xx_pcie_msi_irq_handler(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct dra7xx_pcie *dra7xx; + struct dw_pcie_rp *pp; struct dw_pcie *pci; - struct pcie_port *pp; unsigned long reg; u32 bit; @@ -342,7 +342,7 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp) +static int dra7xx_pcie_init_irq_domain(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct device *dev = pci->dev; @@ -473,7 +473,7 @@ static int dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, { int ret; struct dw_pcie *pci = dra7xx->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; struct device *dev = pci->dev; pp->irq = platform_get_irq(pdev, 1); diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index c24dab383654b..c2c18d8acdf2e 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -250,7 +250,7 @@ static int exynos_pcie_link_up(struct dw_pcie *pci) return (val & PCIE_ELBI_XMLH_LINKUP); } -static int exynos_pcie_host_init(struct pcie_port *pp) +static int exynos_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct exynos_pcie *ep = to_exynos_pcie(pci); @@ -277,7 +277,7 @@ static int exynos_add_pcie_port(struct exynos_pcie *ep, struct platform_device *pdev) { struct dw_pcie *pci = &ep->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; struct device *dev = &pdev->dev; int ret; @@ -407,7 +407,7 @@ static int __maybe_unused exynos_pcie_resume_noirq(struct device *dev) { struct exynos_pcie *ep = dev_get_drvdata(dev); struct dw_pcie *pci = &ep->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; int ret; ret = regulator_bulk_enable(ARRAY_SIZE(ep->supplies), ep->supplies); diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 67dbf9d88d222..b159c5fa9c121 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -837,7 +837,7 @@ err_reset_phy: return ret; } -static int imx6_pcie_host_init(struct pcie_port *pp) +static int imx6_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); @@ -954,7 +954,7 @@ static int imx6_pcie_resume_noirq(struct device *dev) { int ret; struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); - struct pcie_port *pp = &imx6_pcie->pci->pp; + struct dw_pcie_rp *pp = &imx6_pcie->pci->pp; if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_SUPPORTS_SUSPEND)) return 0; @@ -1227,7 +1227,7 @@ static struct platform_driver imx6_pcie_driver = { static void imx6_pcie_quirk(struct pci_dev *dev) { struct pci_bus *bus = dev->bus; - struct pcie_port *pp = bus->sysdata; + struct dw_pcie_rp *pp = bus->sysdata; /* Bus parent is the PCI bridge, its parent is this platform driver */ if (!bus->dev.parent || !bus->dev.parent->parent) diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 865258d8c53c1..0021cbbac8de7 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -109,7 +109,7 @@ struct ks_pcie_of_data { enum dw_pcie_device_mode mode; const struct dw_pcie_host_ops *host_ops; const struct dw_pcie_ep_ops *ep_ops; - unsigned int version; + u32 version; }; struct keystone_pcie { @@ -147,7 +147,7 @@ static void ks_pcie_app_writel(struct keystone_pcie *ks_pcie, u32 offset, static void ks_pcie_msi_irq_ack(struct irq_data *data) { - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(data); struct keystone_pcie *ks_pcie; u32 irq = data->hwirq; struct dw_pcie *pci; @@ -167,7 +167,7 @@ static void ks_pcie_msi_irq_ack(struct irq_data *data) static void ks_pcie_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(data); struct keystone_pcie *ks_pcie; struct dw_pcie *pci; u64 msi_target; @@ -192,7 +192,7 @@ static int ks_pcie_msi_set_affinity(struct irq_data *irq_data, static void ks_pcie_msi_mask(struct irq_data *data) { - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(data); struct keystone_pcie *ks_pcie; u32 irq = data->hwirq; struct dw_pcie *pci; @@ -216,7 +216,7 @@ static void ks_pcie_msi_mask(struct irq_data *data) static void ks_pcie_msi_unmask(struct irq_data *data) { - struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(data); struct keystone_pcie *ks_pcie; u32 irq = data->hwirq; struct dw_pcie *pci; @@ -247,7 +247,7 @@ static struct irq_chip ks_pcie_msi_irq_chip = { .irq_unmask = ks_pcie_msi_unmask, }; -static int ks_pcie_msi_host_init(struct pcie_port *pp) +static int ks_pcie_msi_host_init(struct dw_pcie_rp *pp) { pp->msi_irq_chip = &ks_pcie_msi_irq_chip; return dw_pcie_allocate_domains(pp); @@ -390,7 +390,7 @@ static void ks_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) u32 val; u32 num_viewport = ks_pcie->num_viewport; struct dw_pcie *pci = ks_pcie->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; u64 start, end; struct resource *mem; int i; @@ -428,7 +428,7 @@ static void ks_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) static void __iomem *ks_pcie_other_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { - struct pcie_port *pp = bus->sysdata; + struct dw_pcie_rp *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); u32 reg; @@ -456,7 +456,7 @@ static struct pci_ops ks_child_pcie_ops = { */ static int ks_pcie_v3_65_add_bus(struct pci_bus *bus) { - struct pcie_port *pp = bus->sysdata; + struct dw_pcie_rp *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); @@ -574,7 +574,7 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc) struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); u32 offset = irq - ks_pcie->msi_host_irq; struct dw_pcie *pci = ks_pcie->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; struct device *dev = pci->dev; struct irq_chip *chip = irq_desc_get_chip(desc); u32 vector, reg, pos; @@ -792,7 +792,7 @@ static int __init ks_pcie_init_id(struct keystone_pcie *ks_pcie) return 0; } -static int __init ks_pcie_host_init(struct pcie_port *pp) +static int __init ks_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); @@ -1048,19 +1048,19 @@ static int ks_pcie_am654_set_mode(struct device *dev, static const struct ks_pcie_of_data ks_pcie_rc_of_data = { .host_ops = &ks_pcie_host_ops, - .version = 0x365A, + .version = DW_PCIE_VER_365A, }; static const struct ks_pcie_of_data ks_pcie_am654_rc_of_data = { .host_ops = &ks_pcie_am654_host_ops, .mode = DW_PCIE_RC_TYPE, - .version = 0x490A, + .version = DW_PCIE_VER_490A, }; static const struct ks_pcie_of_data ks_pcie_am654_ep_of_data = { .ep_ops = &ks_pcie_am654_ep_ops, .mode = DW_PCIE_EP_TYPE, - .version = 0x490A, + .version = DW_PCIE_VER_490A, }; static const struct of_device_id ks_pcie_of_match[] = { @@ -1094,12 +1094,12 @@ static int __init ks_pcie_probe(struct platform_device *pdev) struct device_link **link; struct gpio_desc *gpiod; struct resource *res; - unsigned int version; void __iomem *base; u32 num_viewport; struct phy **phy; u32 num_lanes; char name[10]; + u32 version; int ret; int irq; int i; @@ -1214,7 +1214,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev) goto err_get_sync; } - if (pci->version >= 0x480A) + if (dw_pcie_ver_is_ge(pci, 480A)) ret = ks_pcie_am654_set_mode(dev, mode); else ret = ks_pcie_set_mode(dev); diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index 39f4664bd84c7..ad99707b3b994 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -32,15 +32,6 @@ struct ls_pcie_ep { const struct ls_pcie_ep_drvdata *drvdata; }; -static int ls_pcie_establish_link(struct dw_pcie *pci) -{ - return 0; -} - -static const struct dw_pcie_ops dw_ls_pcie_ep_ops = { - .start_link = ls_pcie_establish_link, -}; - static const struct pci_epc_features* ls_pcie_ep_get_features(struct dw_pcie_ep *ep) { @@ -106,19 +97,16 @@ static const struct dw_pcie_ep_ops ls_pcie_ep_ops = { static const struct ls_pcie_ep_drvdata ls1_ep_drvdata = { .ops = &ls_pcie_ep_ops, - .dw_pcie_ops = &dw_ls_pcie_ep_ops, }; static const struct ls_pcie_ep_drvdata ls2_ep_drvdata = { .func_offset = 0x20000, .ops = &ls_pcie_ep_ops, - .dw_pcie_ops = &dw_ls_pcie_ep_ops, }; static const struct ls_pcie_ep_drvdata lx2_ep_drvdata = { .func_offset = 0x8000, .ops = &ls_pcie_ep_ops, - .dw_pcie_ops = &dw_ls_pcie_ep_ops, }; static const struct of_device_id ls_pcie_ep_of_match[] = { diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c index 5b9c625df7b89..c888ed6f36b4c 100644 --- a/drivers/pci/controller/dwc/pci-layerscape.c +++ b/drivers/pci/controller/dwc/pci-layerscape.c @@ -123,7 +123,7 @@ static void ls_pcie_fix_error_response(struct ls_pcie *pcie) iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR); } -static int ls_pcie_host_init(struct pcie_port *pp) +static int ls_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct ls_pcie *pcie = to_ls_pcie(pci); diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 686ded034f224..d274b834a4265 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -370,7 +370,7 @@ static int meson_pcie_link_up(struct dw_pcie *pci) return 0; } -static int meson_pcie_host_init(struct pcie_port *pp) +static int meson_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct meson_pcie *mp = to_meson_pcie(pci); diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index e8afa50129a8f..b8cb77c9c4bd2 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -217,7 +217,7 @@ static inline void al_pcie_target_bus_set(struct al_pcie *pcie, static void __iomem *al_pcie_conf_addr_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { - struct pcie_port *pp = bus->sysdata; + struct dw_pcie_rp *pp = bus->sysdata; struct al_pcie *pcie = to_al_pcie(to_dw_pcie_from_pp(pp)); unsigned int busnr = bus->number; struct al_pcie_target_bus_cfg *target_bus_cfg = &pcie->target_bus_cfg; @@ -245,7 +245,7 @@ static struct pci_ops al_child_pci_ops = { static void al_pcie_config_prepare(struct al_pcie *pcie) { struct al_pcie_target_bus_cfg *target_bus_cfg; - struct pcie_port *pp = &pcie->pci->pp; + struct dw_pcie_rp *pp = &pcie->pci->pp; unsigned int ecam_bus_mask; u32 cfg_control_offset; u8 subordinate_bus; @@ -289,7 +289,7 @@ static void al_pcie_config_prepare(struct al_pcie *pcie) al_pcie_controller_writel(pcie, cfg_control_offset, reg); } -static int al_pcie_host_init(struct pcie_port *pp) +static int al_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct al_pcie *pcie = to_al_pcie(pci); diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index 4e2552dcf9827..8b113d3f30956 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -166,7 +166,7 @@ static int armada8k_pcie_start_link(struct dw_pcie *pci) return 0; } -static int armada8k_pcie_host_init(struct pcie_port *pp) +static int armada8k_pcie_host_init(struct dw_pcie_rp *pp) { u32 reg; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -233,7 +233,7 @@ static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, struct platform_device *pdev) { struct dw_pcie *pci = pcie->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; struct device *dev = &pdev->dev; int ret; diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index c91fc1954432a..7bd210088a744 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -97,7 +97,7 @@ static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr) { struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; struct dw_pcie_ep *ep = &pci->ep; switch (artpec6_pcie->mode) { @@ -315,7 +315,7 @@ static void artpec6_pcie_deassert_core_reset(struct artpec6_pcie *artpec6_pcie) usleep_range(100, 200); } -static int artpec6_pcie_host_init(struct pcie_port *pp) +static int artpec6_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); diff --git a/drivers/pci/controller/dwc/pcie-bt1.c b/drivers/pci/controller/dwc/pcie-bt1.c new file mode 100644 index 0000000000000..86b230575ddc9 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-bt1.c @@ -0,0 +1,653 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC + * + * Authors: + * Vadim Vlasov + * Serge Semin + * + * Baikal-T1 PCIe controller driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +/* Baikal-T1 System CCU control registers */ +#define BT1_CCU_PCIE_CLKC 0x140 +#define BT1_CCU_PCIE_REQ_PCS_CLK BIT(16) +#define BT1_CCU_PCIE_REQ_MAC_CLK BIT(17) +#define BT1_CCU_PCIE_REQ_PIPE_CLK BIT(18) + +#define BT1_CCU_PCIE_RSTC 0x144 +#define BT1_CCU_PCIE_REQ_LINK_RST BIT(13) +#define BT1_CCU_PCIE_REQ_SMLH_RST BIT(14) +#define BT1_CCU_PCIE_REQ_PHY_RST BIT(16) +#define BT1_CCU_PCIE_REQ_CORE_RST BIT(24) +#define BT1_CCU_PCIE_REQ_STICKY_RST BIT(26) +#define BT1_CCU_PCIE_REQ_NSTICKY_RST BIT(27) + +#define BT1_CCU_PCIE_PMSC 0x148 +#define BT1_CCU_PCIE_LTSSM_STATE_MASK GENMASK(5, 0) +#define BT1_CCU_PCIE_LTSSM_DET_QUIET 0x00 +#define BT1_CCU_PCIE_LTSSM_DET_ACT 0x01 +#define BT1_CCU_PCIE_LTSSM_POLL_ACT 0x02 +#define BT1_CCU_PCIE_LTSSM_POLL_COMP 0x03 +#define BT1_CCU_PCIE_LTSSM_POLL_CONF 0x04 +#define BT1_CCU_PCIE_LTSSM_PRE_DET_QUIET 0x05 +#define BT1_CCU_PCIE_LTSSM_DET_WAIT 0x06 +#define BT1_CCU_PCIE_LTSSM_CFG_LNKWD_START 0x07 +#define BT1_CCU_PCIE_LTSSM_CFG_LNKWD_ACEPT 0x08 +#define BT1_CCU_PCIE_LTSSM_CFG_LNNUM_WAIT 0x09 +#define BT1_CCU_PCIE_LTSSM_CFG_LNNUM_ACEPT 0x0a +#define BT1_CCU_PCIE_LTSSM_CFG_COMPLETE 0x0b +#define BT1_CCU_PCIE_LTSSM_CFG_IDLE 0x0c +#define BT1_CCU_PCIE_LTSSM_RCVR_LOCK 0x0d +#define BT1_CCU_PCIE_LTSSM_RCVR_SPEED 0x0e +#define BT1_CCU_PCIE_LTSSM_RCVR_RCVRCFG 0x0f +#define BT1_CCU_PCIE_LTSSM_RCVR_IDLE 0x10 +#define BT1_CCU_PCIE_LTSSM_L0 0x11 +#define BT1_CCU_PCIE_LTSSM_L0S 0x12 +#define BT1_CCU_PCIE_LTSSM_L123_SEND_IDLE 0x13 +#define BT1_CCU_PCIE_LTSSM_L1_IDLE 0x14 +#define BT1_CCU_PCIE_LTSSM_L2_IDLE 0x15 +#define BT1_CCU_PCIE_LTSSM_L2_WAKE 0x16 +#define BT1_CCU_PCIE_LTSSM_DIS_ENTRY 0x17 +#define BT1_CCU_PCIE_LTSSM_DIS_IDLE 0x18 +#define BT1_CCU_PCIE_LTSSM_DISABLE 0x19 +#define BT1_CCU_PCIE_LTSSM_LPBK_ENTRY 0x1a +#define BT1_CCU_PCIE_LTSSM_LPBK_ACTIVE 0x1b +#define BT1_CCU_PCIE_LTSSM_LPBK_EXIT 0x1c +#define BT1_CCU_PCIE_LTSSM_LPBK_EXIT_TOUT 0x1d +#define BT1_CCU_PCIE_LTSSM_HOT_RST_ENTRY 0x1e +#define BT1_CCU_PCIE_LTSSM_HOT_RST 0x1f +#define BT1_CCU_PCIE_LTSSM_RCVR_EQ0 0x20 +#define BT1_CCU_PCIE_LTSSM_RCVR_EQ1 0x21 +#define BT1_CCU_PCIE_LTSSM_RCVR_EQ2 0x22 +#define BT1_CCU_PCIE_LTSSM_RCVR_EQ3 0x23 +#define BT1_CCU_PCIE_SMLH_LINKUP BIT(6) +#define BT1_CCU_PCIE_RDLH_LINKUP BIT(7) +#define BT1_CCU_PCIE_PM_LINKSTATE_L0S BIT(8) +#define BT1_CCU_PCIE_PM_LINKSTATE_L1 BIT(9) +#define BT1_CCU_PCIE_PM_LINKSTATE_L2 BIT(10) +#define BT1_CCU_PCIE_L1_PENDING BIT(12) +#define BT1_CCU_PCIE_REQ_EXIT_L1 BIT(14) +#define BT1_CCU_PCIE_LTSSM_RCVR_EQ BIT(15) +#define BT1_CCU_PCIE_PM_DSTAT_MASK GENMASK(18, 16) +#define BT1_CCU_PCIE_PM_PME_EN BIT(20) +#define BT1_CCU_PCIE_PM_PME_STATUS BIT(21) +#define BT1_CCU_PCIE_AUX_PM_EN BIT(22) +#define BT1_CCU_PCIE_AUX_PWR_DET BIT(23) +#define BT1_CCU_PCIE_WAKE_DET BIT(24) +#define BT1_CCU_PCIE_TURNOFF_REQ BIT(30) +#define BT1_CCU_PCIE_TURNOFF_ACK BIT(31) + +#define BT1_CCU_PCIE_GENC 0x14c +#define BT1_CCU_PCIE_LTSSM_EN BIT(1) +#define BT1_CCU_PCIE_DBI2_MODE BIT(2) +#define BT1_CCU_PCIE_MGMT_EN BIT(3) +#define BT1_CCU_PCIE_RXLANE_FLIP_EN BIT(16) +#define BT1_CCU_PCIE_TXLANE_FLIP_EN BIT(17) +#define BT1_CCU_PCIE_SLV_XFER_PEND BIT(24) +#define BT1_CCU_PCIE_RCV_XFER_PEND BIT(25) +#define BT1_CCU_PCIE_DBI_XFER_PEND BIT(26) +#define BT1_CCU_PCIE_DMA_XFER_PEND BIT(27) + +#define BT1_CCU_PCIE_LTSSM_LINKUP(_pmsc) \ +({ \ + int __state = FIELD_GET(BT1_CCU_PCIE_LTSSM_STATE_MASK, _pmsc); \ + __state >= BT1_CCU_PCIE_LTSSM_L0 && __state <= BT1_CCU_PCIE_LTSSM_L2_WAKE; \ +}) + +/* Baikal-T1 PCIe specific control registers */ +#define BT1_PCIE_AXI2MGM_LANENUM 0xd04 +#define BT1_PCIE_AXI2MGM_LANESEL_MASK GENMASK(3, 0) + +#define BT1_PCIE_AXI2MGM_ADDRCTL 0xd08 +#define BT1_PCIE_AXI2MGM_PHYREG_ADDR_MASK GENMASK(20, 0) +#define BT1_PCIE_AXI2MGM_READ_FLAG BIT(29) +#define BT1_PCIE_AXI2MGM_DONE BIT(30) +#define BT1_PCIE_AXI2MGM_BUSY BIT(31) + +#define BT1_PCIE_AXI2MGM_WRITEDATA 0xd0c +#define BT1_PCIE_AXI2MGM_WDATA GENMASK(15, 0) + +#define BT1_PCIE_AXI2MGM_READDATA 0xd10 +#define BT1_PCIE_AXI2MGM_RDATA GENMASK(15, 0) + +/* Generic Baikal-T1 PCIe interface resources */ +#define BT1_PCIE_NUM_APP_CLKS ARRAY_SIZE(bt1_pcie_app_clks) +#define BT1_PCIE_NUM_CORE_CLKS ARRAY_SIZE(bt1_pcie_core_clks) +#define BT1_PCIE_NUM_APP_RSTS ARRAY_SIZE(bt1_pcie_app_rsts) +#define BT1_PCIE_NUM_CORE_RSTS ARRAY_SIZE(bt1_pcie_core_rsts) + +/* PCIe bus setup delays and timeouts */ +#define BT1_PCIE_RST_DELAY_MS 100 +#define BT1_PCIE_RUN_DELAY_US 100 +#define BT1_PCIE_REQ_DELAY_US 1 +#define BT1_PCIE_REQ_TIMEOUT_US 1000 +#define BT1_PCIE_LNK_DELAY_US 1000 +#define BT1_PCIE_LNK_TIMEOUT_US 1000000 + +static const enum dw_pcie_app_clk bt1_pcie_app_clks[] = { + DW_PCIE_DBI_CLK, DW_PCIE_MSTR_CLK, DW_PCIE_SLV_CLK, +}; + +static const enum dw_pcie_core_clk bt1_pcie_core_clks[] = { + DW_PCIE_REF_CLK, +}; + +static const enum dw_pcie_app_rst bt1_pcie_app_rsts[] = { + DW_PCIE_MSTR_RST, DW_PCIE_SLV_RST, +}; + +static const enum dw_pcie_core_rst bt1_pcie_core_rsts[] = { + DW_PCIE_NON_STICKY_RST, DW_PCIE_STICKY_RST, DW_PCIE_CORE_RST, + DW_PCIE_PIPE_RST, DW_PCIE_PHY_RST, DW_PCIE_HOT_RST, DW_PCIE_PWR_RST, +}; + +struct bt1_pcie { + struct dw_pcie dw; + struct platform_device *pdev; + struct regmap *sys_regs; +}; +#define to_bt1_pcie(_dw) container_of(_dw, struct bt1_pcie, dw) + +/* + * Baikal-T1 MMIO space must be read/written by the dword-aligned + * instructions. Note the methods are optimized to have the dword operations + * performed with minimum overhead as the most frequently used ones. + */ +static int bt1_pcie_read_mmio(void __iomem *addr, int size, u32 *val) +{ + unsigned int ofs = (uintptr_t)addr & 0x3; + + if (!IS_ALIGNED((uintptr_t)addr, size)) + return -EINVAL; + + *val = readl(addr - ofs) >> ofs * BITS_PER_BYTE; + if (size == 4) { + return 0; + } else if (size == 2) { + *val &= 0xffff; + return 0; + } else if (size == 1) { + *val &= 0xff; + return 0; + } + + return -EINVAL; +} + +static int bt1_pcie_write_mmio(void __iomem *addr, int size, u32 val) +{ + unsigned int ofs = (uintptr_t)addr & 0x3; + u32 tmp, mask; + + if (!IS_ALIGNED((uintptr_t)addr, size)) + return -EINVAL; + + if (size == 4) { + writel(val, addr); + return 0; + } else if (size == 2 || size == 1) { + mask = GENMASK(size * BITS_PER_BYTE - 1, 0); + tmp = readl(addr - ofs) & ~(mask << ofs * BITS_PER_BYTE); + tmp |= (val & mask) << ofs * BITS_PER_BYTE; + writel(tmp, addr - ofs); + return 0; + } + + return -EINVAL; +} + +static u32 bt1_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, + size_t size) +{ + int ret; + u32 val; + + ret = bt1_pcie_read_mmio(base + reg, size, &val); + if (ret) { + dev_err(pci->dev, "Read DBI address failed\n"); + return ~0U; + } + + return val; +} + +static void bt1_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, + size_t size, u32 val) +{ + int ret; + + ret = bt1_pcie_write_mmio(base + reg, size, val); + if (ret) + dev_err(pci->dev, "Write DBI address failed\n"); +} + +static void bt1_pcie_write_dbi2(struct dw_pcie *pci, void __iomem *base, u32 reg, + size_t size, u32 val) +{ + struct bt1_pcie *btpci = to_bt1_pcie(pci); + int ret; + + regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, + BT1_CCU_PCIE_DBI2_MODE, BT1_CCU_PCIE_DBI2_MODE); + + ret = bt1_pcie_write_mmio(base + reg, size, val); + if (ret) + dev_err(pci->dev, "Write DBI2 address failed\n"); + + regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, + BT1_CCU_PCIE_DBI2_MODE, 0); +} + +static int bt1_pcie_start_link(struct dw_pcie *pci) +{ + struct bt1_pcie *btpci = to_bt1_pcie(pci); + u32 val; + int ret; + + /* + * Enable LTSSM and make sure it was able to establish both PHY and + * data links. This procedure shall work fine to reach 2.5 GT/s speed. + */ + regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, + BT1_CCU_PCIE_LTSSM_EN, BT1_CCU_PCIE_LTSSM_EN); + + ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val, + (val & BT1_CCU_PCIE_SMLH_LINKUP), + BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US); + if (ret) { + dev_err(pci->dev, "LTSSM failed to set PHY link up\n"); + return ret; + } + + ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val, + (val & BT1_CCU_PCIE_RDLH_LINKUP), + BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US); + if (ret) { + dev_err(pci->dev, "LTSSM failed to set data link up\n"); + return ret; + } + + /* + * Activate direct speed change after the link is established in an + * attempt to reach a higher bus performance (up to Gen.3 - 8.0 GT/s). + * This is required at least to get 8.0 GT/s speed. + */ + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val |= PORT_LOGIC_SPEED_CHANGE; + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + + ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val, + BT1_CCU_PCIE_LTSSM_LINKUP(val), + BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US); + if (ret) + dev_err(pci->dev, "LTSSM failed to get into L0 state\n"); + + return ret; +} + +static void bt1_pcie_stop_link(struct dw_pcie *pci) +{ + struct bt1_pcie *btpci = to_bt1_pcie(pci); + + regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, + BT1_CCU_PCIE_LTSSM_EN, 0); +} + +static const struct dw_pcie_ops bt1_pcie_ops = { + .read_dbi = bt1_pcie_read_dbi, + .write_dbi = bt1_pcie_write_dbi, + .write_dbi2 = bt1_pcie_write_dbi2, + .start_link = bt1_pcie_start_link, + .stop_link = bt1_pcie_stop_link, +}; + +static struct pci_ops bt1_pci_ops = { + .map_bus = dw_pcie_own_conf_map_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, +}; + +static int bt1_pcie_get_resources(struct bt1_pcie *btpci) +{ + struct device *dev = btpci->dw.dev; + int i; + + /* DBI access is supposed to be performed by the dword-aligned IOs */ + btpci->dw.pp.bridge->ops = &bt1_pci_ops; + + /* These CSRs are in MMIO so we won't check the regmap-methods status */ + btpci->sys_regs = + syscon_regmap_lookup_by_phandle(dev->of_node, "baikal,bt1-syscon"); + if (IS_ERR(btpci->sys_regs)) + return dev_err_probe(dev, PTR_ERR(btpci->sys_regs), + "Failed to get syscon\n"); + + /* Make sure all the required resources have been specified */ + for (i = 0; i < BT1_PCIE_NUM_APP_CLKS; i++) { + if (!btpci->dw.app_clks[bt1_pcie_app_clks[i]].clk) { + dev_err(dev, "App clocks set is incomplete\n"); + return -ENOENT; + } + } + + for (i = 0; i < BT1_PCIE_NUM_CORE_CLKS; i++) { + if (!btpci->dw.core_clks[bt1_pcie_core_clks[i]].clk) { + dev_err(dev, "Core clocks set is incomplete\n"); + return -ENOENT; + } + } + + for (i = 0; i < BT1_PCIE_NUM_APP_RSTS; i++) { + if (!btpci->dw.app_rsts[bt1_pcie_app_rsts[i]].rstc) { + dev_err(dev, "App resets set is incomplete\n"); + return -ENOENT; + } + } + + for (i = 0; i < BT1_PCIE_NUM_CORE_RSTS; i++) { + if (!btpci->dw.core_rsts[bt1_pcie_core_rsts[i]].rstc) { + dev_err(dev, "Core resets set is incomplete\n"); + return -ENOENT; + } + } + + return 0; +} + +static void bt1_pcie_full_stop_bus(struct bt1_pcie *btpci, bool init) +{ + struct device *dev = btpci->dw.dev; + struct dw_pcie *pci = &btpci->dw; + int ret; + + /* Disable LTSSM for sure */ + regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, + BT1_CCU_PCIE_LTSSM_EN, 0); + + /* + * Application reset controls are trigger-based so assert the core + * resets only. + */ + ret = reset_control_bulk_assert(DW_PCIE_NUM_CORE_RSTS, pci->core_rsts); + if (ret) + dev_err(dev, "Failed to assert core resets\n"); + + /* + * Clocks are disabled by default at least in accordance with the clk + * enable counter value on init stage. + */ + if (!init) { + clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, pci->core_clks); + + clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks); + } + + /* The peripheral devices are unavailable anyway so reset them too */ + gpiod_set_value_cansleep(pci->pe_rst, 1); + + /* Make sure all the resets are settled */ + msleep(BT1_PCIE_RST_DELAY_MS); +} + +/* + * Implements the cold reset procedure in accordance with the reference manual + * and available PM signals. + */ +static int bt1_pcie_cold_start_bus(struct bt1_pcie *btpci) +{ + struct device *dev = btpci->dw.dev; + struct dw_pcie *pci = &btpci->dw; + u32 val; + int ret; + + /* First get out of the Power/Hot reset state */ + ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PWR_RST].rstc); + if (ret) { + dev_err(dev, "Failed to deassert PHY reset\n"); + return ret; + } + + ret = reset_control_deassert(pci->core_rsts[DW_PCIE_HOT_RST].rstc); + if (ret) { + dev_err(dev, "Failed to deassert hot reset\n"); + goto err_assert_pwr_rst; + } + + /* Wait for the PM-core to stop requesting the PHY reset */ + ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val, + !(val & BT1_CCU_PCIE_REQ_PHY_RST), + BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US); + if (ret) { + dev_err(dev, "Timed out waiting for PM to stop PHY resetting\n"); + goto err_assert_hot_rst; + } + + ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PHY_RST].rstc); + if (ret) { + dev_err(dev, "Failed to deassert PHY reset\n"); + goto err_assert_hot_rst; + } + + /* Clocks can be now enabled, but the ref one is crucial at this stage */ + ret = clk_bulk_prepare_enable(DW_PCIE_NUM_APP_CLKS, pci->app_clks); + if (ret) { + dev_err(dev, "Failed to enable app clocks\n"); + goto err_assert_phy_rst; + } + + ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, pci->core_clks); + if (ret) { + dev_err(dev, "Failed to enable ref clocks\n"); + goto err_disable_app_clk; + } + + /* Wait for the PM to stop requesting the controller core reset */ + ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val, + !(val & BT1_CCU_PCIE_REQ_CORE_RST), + BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US); + if (ret) { + dev_err(dev, "Timed out waiting for PM to stop core resetting\n"); + goto err_disable_core_clk; + } + + /* PCS-PIPE interface and controller core can be now activated */ + ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc); + if (ret) { + dev_err(dev, "Failed to deassert PIPE reset\n"); + goto err_disable_core_clk; + } + + ret = reset_control_deassert(pci->core_rsts[DW_PCIE_CORE_RST].rstc); + if (ret) { + dev_err(dev, "Failed to deassert core reset\n"); + goto err_assert_pipe_rst; + } + + /* It's recommended to reset the core and application logic together */ + ret = reset_control_bulk_reset(DW_PCIE_NUM_APP_RSTS, pci->app_rsts); + if (ret) { + dev_err(dev, "Failed to reset app domain\n"); + goto err_assert_core_rst; + } + + /* Sticky/Non-sticky CSR flags can be now unreset too */ + ret = reset_control_deassert(pci->core_rsts[DW_PCIE_STICKY_RST].rstc); + if (ret) { + dev_err(dev, "Failed to deassert sticky reset\n"); + goto err_assert_core_rst; + } + + ret = reset_control_deassert(pci->core_rsts[DW_PCIE_NON_STICKY_RST].rstc); + if (ret) { + dev_err(dev, "Failed to deassert non-sticky reset\n"); + goto err_assert_sticky_rst; + } + + /* Activate the PCIe bus peripheral devices */ + gpiod_set_value_cansleep(pci->pe_rst, 0); + + /* Make sure the state is settled (LTSSM is still disabled though) */ + usleep_range(BT1_PCIE_RUN_DELAY_US, BT1_PCIE_RUN_DELAY_US + 100); + + return 0; + +err_assert_sticky_rst: + reset_control_assert(pci->core_rsts[DW_PCIE_STICKY_RST].rstc); + +err_assert_core_rst: + reset_control_assert(pci->core_rsts[DW_PCIE_CORE_RST].rstc); + +err_assert_pipe_rst: + reset_control_assert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc); + +err_disable_core_clk: + clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, pci->core_clks); + +err_disable_app_clk: + clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks); + +err_assert_phy_rst: + reset_control_assert(pci->core_rsts[DW_PCIE_PHY_RST].rstc); + +err_assert_hot_rst: + reset_control_assert(pci->core_rsts[DW_PCIE_HOT_RST].rstc); + +err_assert_pwr_rst: + reset_control_assert(pci->core_rsts[DW_PCIE_PWR_RST].rstc); + + return ret; +} + +static int bt1_pcie_host_init(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct bt1_pcie *btpci = to_bt1_pcie(pci); + int ret; + + ret = bt1_pcie_get_resources(btpci); + if (ret) + return ret; + + bt1_pcie_full_stop_bus(btpci, true); + + return bt1_pcie_cold_start_bus(btpci); +} + +static void bt1_pcie_host_deinit(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct bt1_pcie *btpci = to_bt1_pcie(pci); + + bt1_pcie_full_stop_bus(btpci, false); +} + +static const struct dw_pcie_host_ops bt1_pcie_host_ops = { + .host_init = bt1_pcie_host_init, + .host_deinit = bt1_pcie_host_deinit, +}; + +static struct bt1_pcie *bt1_pcie_create_data(struct platform_device *pdev) +{ + struct bt1_pcie *btpci; + + btpci = devm_kzalloc(&pdev->dev, sizeof(*btpci), GFP_KERNEL); + if (!btpci) + return ERR_PTR(-ENOMEM); + + btpci->pdev = pdev; + + platform_set_drvdata(pdev, btpci); + + return btpci; +} + +static int bt1_pcie_add_port(struct bt1_pcie *btpci) +{ + struct device *dev = &btpci->pdev->dev; + int ret; + + /* + * DW PCIe Root Port controller is equipped with eDMA capable of + * working with the 64-bit memory addresses. + */ + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) + return ret; + + btpci->dw.version = DW_PCIE_VER_460A; + btpci->dw.dev = dev; + btpci->dw.ops = &bt1_pcie_ops; + + btpci->dw.pp.num_vectors = MAX_MSI_IRQS; + btpci->dw.pp.ops = &bt1_pcie_host_ops; + + dw_pcie_cap_set(&btpci->dw, REQ_RES); + + ret = dw_pcie_host_init(&btpci->dw.pp); + if (ret) + dev_err_probe(dev, ret, "Failed to initialize DWC PCIe host\n"); + + return ret; +} + +static void bt1_pcie_del_port(struct bt1_pcie *btpci) +{ + dw_pcie_host_deinit(&btpci->dw.pp); +} + +static int bt1_pcie_probe(struct platform_device *pdev) +{ + struct bt1_pcie *btpci; + + btpci = bt1_pcie_create_data(pdev); + if (IS_ERR(btpci)) + return PTR_ERR(btpci); + + return bt1_pcie_add_port(btpci); +} + +static int bt1_pcie_remove(struct platform_device *pdev) +{ + struct bt1_pcie *btpci = platform_get_drvdata(pdev); + + bt1_pcie_del_port(btpci); + + return 0; +} + +static const struct of_device_id bt1_pcie_of_match[] = { + { .compatible = "baikal,bt1-pcie" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bt1_pcie_of_match); + +static struct platform_driver bt1_pcie_driver = { + .probe = bt1_pcie_probe, + .remove = bt1_pcie_remove, + .driver = { + .name = "bt1-pcie", + .of_match_table = bt1_pcie_of_match, + }, +}; +module_platform_driver(bt1_pcie_driver); + +MODULE_AUTHOR("Serge Semin "); +MODULE_DESCRIPTION("Baikal-T1 PCIe driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 2af4ed90e12b3..bce3ad0c1ce4e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -13,8 +13,6 @@ #include #include -#include "../../pci.h" - void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) { struct pci_epc *epc = ep->epc; @@ -83,6 +81,7 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) for (func_no = 0; func_no < funcs; func_no++) __dw_pcie_ep_reset_bar(pci, func_no, bar, 0); } +EXPORT_SYMBOL_GPL(dw_pcie_ep_reset_bar); static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie_ep *ep, u8 func_no, u8 cap_ptr, u8 cap) @@ -153,9 +152,8 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, return 0; } -static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, - enum pci_barno bar, dma_addr_t cpu_addr, - enum dw_pcie_as_type as_type) +static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, + dma_addr_t cpu_addr, enum pci_barno bar) { int ret; u32 free_win; @@ -167,8 +165,8 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, return -EINVAL; } - ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, bar, cpu_addr, - as_type); + ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type, + cpu_addr, bar); if (ret < 0) { dev_err(pci->dev, "Failed to program IB window\n"); return ret; @@ -184,8 +182,9 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no, phys_addr_t phys_addr, u64 pci_addr, size_t size) { - u32 free_win; struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u32 free_win; + int ret; free_win = find_first_zero_bit(ep->ob_window_map, pci->num_ob_windows); if (free_win >= pci->num_ob_windows) { @@ -193,8 +192,10 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no, return -EINVAL; } - dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win, PCIE_ATU_TYPE_MEM, - phys_addr, pci_addr, size); + ret = dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win, PCIE_ATU_TYPE_MEM, + phys_addr, pci_addr, size); + if (ret) + return ret; set_bit(free_win, ep->ob_window_map); ep->outbound_addr[free_win] = phys_addr; @@ -212,7 +213,7 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, __dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags); - dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); clear_bit(atu_index, ep->ib_window_map); ep->epf_bar[bar] = NULL; } @@ -220,27 +221,25 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar) { - int ret; struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); enum pci_barno bar = epf_bar->barno; size_t size = epf_bar->size; int flags = epf_bar->flags; - enum dw_pcie_as_type as_type; - u32 reg; unsigned int func_offset = 0; + int ret, type; + u32 reg; func_offset = dw_pcie_ep_func_select(ep, func_no); reg = PCI_BASE_ADDRESS_0 + (4 * bar) + func_offset; if (!(flags & PCI_BASE_ADDRESS_SPACE)) - as_type = DW_PCIE_AS_MEM; + type = PCIE_ATU_TYPE_MEM; else - as_type = DW_PCIE_AS_IO; + type = PCIE_ATU_TYPE_IO; - ret = dw_pcie_ep_inbound_atu(ep, func_no, bar, - epf_bar->phys_addr, as_type); + ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar); if (ret) return ret; @@ -288,7 +287,7 @@ static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, if (ret < 0) return; - dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND); + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, atu_index); clear_bit(atu_index, ep->ob_window_map); } @@ -434,8 +433,7 @@ static void dw_pcie_ep_stop(struct pci_epc *epc) struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - if (pci->ops && pci->ops->stop_link) - pci->ops->stop_link(pci); + dw_pcie_stop_link(pci); } static int dw_pcie_ep_start(struct pci_epc *epc) @@ -443,10 +441,7 @@ static int dw_pcie_ep_start(struct pci_epc *epc) struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - if (!pci->ops || !pci->ops->start_link) - return -EINVAL; - - return pci->ops->start_link(pci); + return dw_pcie_start_link(pci); } static const struct pci_epc_features* @@ -607,8 +602,11 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, void dw_pcie_ep_exit(struct dw_pcie_ep *ep) { + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct pci_epc *epc = ep->epc; + dw_pcie_edma_remove(pci); + pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem, epc->mem->window.page_size); @@ -681,31 +679,14 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct device *dev = pci->dev; struct platform_device *pdev = to_platform_device(dev); - struct device_node *np = dev->of_node; const struct pci_epc_features *epc_features; struct dw_pcie_ep_func *ep_func; INIT_LIST_HEAD(&ep->func_list); - if (!pci->dbi_base) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pci->dbi_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - } - - if (!pci->dbi_base2) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); - if (!res) - pci->dbi_base2 = pci->dbi_base + SZ_4K; - else { - pci->dbi_base2 = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(pci->dbi_base2)) - return PTR_ERR(pci->dbi_base2); - } - } - - dw_pcie_iatu_detect(pci); + ret = dw_pcie_get_resources(pci); + if (ret) + return ret; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); if (!res) @@ -714,6 +695,10 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) ep->phys_base = res->start; ep->addr_size = resource_size(res); + dw_pcie_version_detect(pci); + + dw_pcie_iatu_detect(pci); + ep->ib_window_map = devm_kcalloc(dev, BITS_TO_LONGS(pci->num_ib_windows), sizeof(long), @@ -734,9 +719,6 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) return -ENOMEM; ep->outbound_addr = addr; - if (pci->link_gen < 1) - pci->link_gen = of_pci_get_max_link_speed(np); - epc = devm_pci_epc_create(dev, &epc_ops); if (IS_ERR(epc)) { dev_err(dev, "Failed to create epc device\n"); @@ -782,6 +764,10 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) goto err_exit_epc_mem; } + ret = dw_pcie_edma_detect(pci); + if (ret) + goto err_free_epc_mem; + if (ep->ops->get_features) { epc_features = ep->ops->get_features(ep); if (epc_features->core_init_notifier) @@ -790,10 +776,13 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) ret = dw_pcie_ep_init_complete(ep); if (ret) - goto err_free_epc_mem; + goto err_remove_edma; return 0; +err_remove_edma: + dw_pcie_edma_remove(pci); + err_free_epc_mem: pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem, epc->mem->window.page_size); diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 7cd4593ad12fa..6997c605bf3e4 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -16,7 +16,6 @@ #include #include -#include "../../pci.h" #include "pcie-designware.h" static struct pci_ops dw_pcie_ops; @@ -53,7 +52,7 @@ static struct msi_domain_info dw_pcie_msi_domain_info = { }; /* MSI int handler */ -irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) +irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp) { int i, pos; unsigned long val; @@ -88,7 +87,7 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) static void dw_chained_msi_isr(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); - struct pcie_port *pp; + struct dw_pcie_rp *pp; chained_irq_enter(chip, desc); @@ -100,7 +99,7 @@ static void dw_chained_msi_isr(struct irq_desc *desc) static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg) { - struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); u64 msi_target; @@ -123,7 +122,7 @@ static int dw_pci_msi_set_affinity(struct irq_data *d, static void dw_pci_bottom_mask(struct irq_data *d) { - struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); unsigned int res, bit, ctrl; unsigned long flags; @@ -142,7 +141,7 @@ static void dw_pci_bottom_mask(struct irq_data *d) static void dw_pci_bottom_unmask(struct irq_data *d) { - struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); unsigned int res, bit, ctrl; unsigned long flags; @@ -161,7 +160,7 @@ static void dw_pci_bottom_unmask(struct irq_data *d) static void dw_pci_bottom_ack(struct irq_data *d) { - struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); unsigned int res, bit, ctrl; @@ -185,7 +184,7 @@ static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *args) { - struct pcie_port *pp = domain->host_data; + struct dw_pcie_rp *pp = domain->host_data; unsigned long flags; u32 i; int bit; @@ -213,7 +212,7 @@ static void dw_pcie_irq_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs) { struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct pcie_port *pp = domain->host_data; + struct dw_pcie_rp *pp = domain->host_data; unsigned long flags; raw_spin_lock_irqsave(&pp->lock, flags); @@ -229,7 +228,7 @@ static const struct irq_domain_ops dw_pcie_msi_domain_ops = { .free = dw_pcie_irq_domain_free, }; -int dw_pcie_allocate_domains(struct pcie_port *pp) +int dw_pcie_allocate_domains(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node); @@ -255,7 +254,7 @@ int dw_pcie_allocate_domains(struct pcie_port *pp) return 0; } -static void dw_pcie_free_msi(struct pcie_port *pp) +static void dw_pcie_free_msi(struct dw_pcie_rp *pp) { if (pp->msi_irq) irq_set_chained_handler_and_data(pp->msi_irq, NULL, NULL); @@ -272,7 +271,7 @@ static void dw_pcie_free_msi(struct pcie_port *pp) } } -static void dw_pcie_msi_init(struct pcie_port *pp) +static void dw_pcie_msi_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); u64 msi_target = (u64)pp->msi_data; @@ -285,7 +284,7 @@ static void dw_pcie_msi_init(struct pcie_port *pp) dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target)); } -int dw_pcie_host_init(struct pcie_port *pp) +int dw_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct device *dev = pci->dev; @@ -293,17 +292,21 @@ int dw_pcie_host_init(struct pcie_port *pp) struct platform_device *pdev = to_platform_device(dev); struct resource_entry *win; struct pci_host_bridge *bridge; - struct resource *cfg_res; + struct resource *res; int ret; - raw_spin_lock_init(&pci->pp.lock); + raw_spin_lock_init(&pp->lock); - cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); - if (cfg_res) { - pp->cfg0_size = resource_size(cfg_res); - pp->cfg0_base = cfg_res->start; + ret = dw_pcie_get_resources(pci); + if (ret) + return ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); + if (res) { + pp->cfg0_size = resource_size(res); + pp->cfg0_base = res->start; - pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, cfg_res); + pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res); if (IS_ERR(pp->va_cfg0_base)) return PTR_ERR(pp->va_cfg0_base); } else { @@ -311,13 +314,6 @@ int dw_pcie_host_init(struct pcie_port *pp) return -ENODEV; } - if (!pci->dbi_base) { - struct resource *dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_res); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - } - bridge = devm_pci_alloc_host_bridge(dev, 0); if (!bridge) return -ENOMEM; @@ -332,8 +328,15 @@ int dw_pcie_host_init(struct pcie_port *pp) pp->io_base = pci_pio_to_address(win->res->start); } - if (pci->link_gen < 1) - pci->link_gen = of_pci_get_max_link_speed(np); + /* Set default bus ops */ + bridge->ops = &dw_pcie_ops; + bridge->child_ops = &dw_child_pcie_ops; + + if (pp->ops->host_init) { + ret = pp->ops->host_init(pp); + if (ret) + return ret; + } if (pci_msi_enabled()) { pp->has_msi_ctrl = !(pp->ops->msi_host_init || @@ -344,20 +347,29 @@ int dw_pcie_host_init(struct pcie_port *pp) pp->num_vectors = MSI_DEF_NUM_VECTORS; } else if (pp->num_vectors > MAX_MSI_IRQS) { dev_err(dev, "Invalid number of vectors\n"); - return -EINVAL; + ret = -EINVAL; + goto err_deinit_host; } if (pp->ops->msi_host_init) { ret = pp->ops->msi_host_init(pp); if (ret < 0) - return ret; + goto err_deinit_host; } else if (pp->has_msi_ctrl) { + u32 ctrl, num_ctrls; + + num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; + for (ctrl = 0; ctrl < num_ctrls; ctrl++) + pp->irq_mask[ctrl] = ~0; + if (!pp->msi_irq) { pp->msi_irq = platform_get_irq_byname_optional(pdev, "msi"); if (pp->msi_irq < 0) { pp->msi_irq = platform_get_irq(pdev, 0); - if (pp->msi_irq < 0) - return pp->msi_irq; + if (pp->msi_irq < 0) { + ret = pp->msi_irq; + goto err_deinit_host; + } } } @@ -365,22 +377,22 @@ int dw_pcie_host_init(struct pcie_port *pp) ret = dw_pcie_allocate_domains(pp); if (ret) - return ret; + goto err_deinit_host; if (pp->msi_irq > 0) irq_set_chained_handler_and_data(pp->msi_irq, dw_chained_msi_isr, pp); - ret = dma_set_mask(pci->dev, DMA_BIT_MASK(32)); + ret = dma_set_mask(dev, DMA_BIT_MASK(32)); if (ret) - dev_warn(pci->dev, "Failed to set DMA mask to 32-bit. Devices with only 32-bit MSI support may not work properly\n"); + dev_warn(dev, "Failed to set DMA mask to 32-bit. Devices with only 32-bit MSI support may not work properly\n"); - pp->msi_data = dma_map_single_attrs(pci->dev, &pp->msi_msg, + pp->msi_data = dma_map_single_attrs(dev, &pp->msi_msg, sizeof(pp->msi_msg), DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); - ret = dma_mapping_error(pci->dev, pp->msi_data); + ret = dma_mapping_error(dev, pp->msi_data); if (ret) { dev_err(pci->dev, "Failed to map MSI data\n"); pp->msi_data = 0; @@ -389,23 +401,22 @@ int dw_pcie_host_init(struct pcie_port *pp) } } - /* Set default bus ops */ - bridge->ops = &dw_pcie_ops; - bridge->child_ops = &dw_child_pcie_ops; + dw_pcie_version_detect(pci); - if (pp->ops->host_init) { - ret = pp->ops->host_init(pp); - if (ret) - goto err_free_msi; - } dw_pcie_iatu_detect(pci); - dw_pcie_setup_rc(pp); + ret = dw_pcie_edma_detect(pci); + if (ret) + goto err_free_msi; + + ret = dw_pcie_setup_rc(pp); + if (ret) + goto err_remove_edma; - if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) { - ret = pci->ops->start_link(pci); + if (!dw_pcie_link_up(pci)) { + ret = dw_pcie_start_link(pci); if (ret) - goto err_free_msi; + goto err_remove_edma; } /* Ignore errors, the link may come up later */ @@ -420,38 +431,49 @@ int dw_pcie_host_init(struct pcie_port *pp) return 0; err_stop_link: - if (pci->ops && pci->ops->stop_link) - pci->ops->stop_link(pci); + dw_pcie_stop_link(pci); + +err_remove_edma: + dw_pcie_edma_remove(pci); err_free_msi: if (pp->has_msi_ctrl) dw_pcie_free_msi(pp); + +err_deinit_host: + if (pp->ops->host_deinit) + pp->ops->host_deinit(pp); + return ret; } EXPORT_SYMBOL_GPL(dw_pcie_host_init); -void dw_pcie_host_deinit(struct pcie_port *pp) +void dw_pcie_host_deinit(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); pci_stop_root_bus(pp->bridge->bus); pci_remove_root_bus(pp->bridge->bus); - if (pci->ops && pci->ops->stop_link) - pci->ops->stop_link(pci); + dw_pcie_stop_link(pci); + + dw_pcie_edma_remove(pci); if (pp->has_msi_ctrl) dw_pcie_free_msi(pp); + + if (pp->ops->host_deinit) + pp->ops->host_deinit(pp); } EXPORT_SYMBOL_GPL(dw_pcie_host_deinit); static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { - int type; - u32 busdev; - struct pcie_port *pp = bus->sysdata; + struct dw_pcie_rp *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + int type, ret; + u32 busdev; /* * Checking whether the link is up here is a last line of defense @@ -472,8 +494,10 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, else type = PCIE_ATU_TYPE_CFG1; - - dw_pcie_prog_outbound_atu(pci, 0, type, pp->cfg0_base, busdev, pp->cfg0_size); + ret = dw_pcie_prog_outbound_atu(pci, 0, type, pp->cfg0_base, busdev, + pp->cfg0_size); + if (ret) + return NULL; return pp->va_cfg0_base + where; } @@ -481,33 +505,45 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { - int ret; - struct pcie_port *pp = bus->sysdata; + struct dw_pcie_rp *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + int ret; ret = pci_generic_config_read(bus, devfn, where, size, val); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; - if (!ret && pci->io_cfg_atu_shared) - dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, pp->io_base, - pp->io_bus_addr, pp->io_size); + if (pp->cfg0_io_shared) { + ret = dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, + pp->io_base, pp->io_bus_addr, + pp->io_size); + if (ret) + return PCIBIOS_SET_FAILED; + } - return ret; + return PCIBIOS_SUCCESSFUL; } static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { - int ret; - struct pcie_port *pp = bus->sysdata; + struct dw_pcie_rp *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + int ret; ret = pci_generic_config_write(bus, devfn, where, size, val); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; - if (!ret && pci->io_cfg_atu_shared) - dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, pp->io_base, - pp->io_bus_addr, pp->io_size); + if (pp->cfg0_io_shared) { + ret = dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, + pp->io_base, pp->io_bus_addr, + pp->io_size); + if (ret) + return PCIBIOS_SET_FAILED; + } - return ret; + return PCIBIOS_SUCCESSFUL; } static struct pci_ops dw_child_pcie_ops = { @@ -518,7 +554,7 @@ static struct pci_ops dw_child_pcie_ops = { void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { - struct pcie_port *pp = bus->sysdata; + struct dw_pcie_rp *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); if (PCI_SLOT(devfn) > 0) @@ -534,10 +570,98 @@ static struct pci_ops dw_pcie_ops = { .write = pci_generic_config_write, }; -void dw_pcie_setup_rc(struct pcie_port *pp) +static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) { - u32 val, ctrl, num_ctrls; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct resource_entry *entry; + int i, ret; + + /* Note the very first outbound ATU is used for CFG IOs */ + if (!pci->num_ob_windows) { + dev_err(pci->dev, "No outbound iATU found\n"); + return -EINVAL; + } + + /* + * Ensure all out/inbound windows are disabled before proceeding with + * the MEM/IO (dma-)ranges setups. + */ + for (i = 0; i < pci->num_ob_windows; i++) + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i); + + for (i = 0; i < pci->num_ib_windows; i++) + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i); + + i = 0; + resource_list_for_each_entry(entry, &pp->bridge->windows) { + if (resource_type(entry->res) != IORESOURCE_MEM) + continue; + + if (pci->num_ob_windows <= ++i) + break; + + ret = dw_pcie_prog_outbound_atu(pci, i, PCIE_ATU_TYPE_MEM, + entry->res->start, + entry->res->start - entry->offset, + resource_size(entry->res)); + if (ret) { + dev_err(pci->dev, "Failed to set MEM range %pr\n", + entry->res); + return ret; + } + } + + if (pp->io_size) { + if (pci->num_ob_windows > ++i) { + ret = dw_pcie_prog_outbound_atu(pci, i, PCIE_ATU_TYPE_IO, + pp->io_base, + pp->io_bus_addr, + pp->io_size); + if (ret) { + dev_err(pci->dev, "Failed to set IO range %pr\n", + entry->res); + return ret; + } + } else { + pp->cfg0_io_shared = true; + } + } + + if (pci->num_ob_windows <= i) + dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n", + pci->num_ob_windows); + + i = 0; + resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) { + if (resource_type(entry->res) != IORESOURCE_MEM) + continue; + + if (pci->num_ib_windows <= i) + break; + + ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM, + entry->res->start, + entry->res->start - entry->offset, + resource_size(entry->res)); + if (ret) { + dev_err(pci->dev, "Failed to set DMA range %pr\n", + entry->res); + return ret; + } + } + + if (pci->num_ib_windows <= i) + dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n", + pci->num_ib_windows); + + return 0; +} + +int dw_pcie_setup_rc(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + u32 val, ctrl, num_ctrls; + int ret; /* * Enable DBI read-only registers for writing/updating configuration. @@ -552,7 +676,6 @@ void dw_pcie_setup_rc(struct pcie_port *pp) /* Initialize IRQ Status array */ for (ctrl = 0; ctrl < num_ctrls; ctrl++) { - pp->irq_mask[ctrl] = ~0; dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), pp->irq_mask[ctrl]); @@ -593,42 +716,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp) * ATU, so we should not program the ATU here. */ if (pp->bridge->child_ops == &dw_child_pcie_ops) { - int i, atu_idx = 0; - struct resource_entry *entry; - - /* - * Disable all outbound windows to make sure a transaction - * can't match multiple windows. - */ - for (i = 0; i < pci->num_ob_windows; i++) - dw_pcie_disable_atu(pci, i, DW_PCIE_REGION_OUTBOUND); - - /* Get last memory resource entry */ - resource_list_for_each_entry(entry, &pp->bridge->windows) { - if (resource_type(entry->res) != IORESOURCE_MEM) - continue; - - if (pci->num_ob_windows <= ++atu_idx) - break; - - dw_pcie_prog_outbound_atu(pci, atu_idx, - PCIE_ATU_TYPE_MEM, entry->res->start, - entry->res->start - entry->offset, - resource_size(entry->res)); - } - - if (pp->io_size) { - if (pci->num_ob_windows > ++atu_idx) - dw_pcie_prog_outbound_atu(pci, atu_idx, - PCIE_ATU_TYPE_IO, pp->io_base, - pp->io_bus_addr, pp->io_size); - else - pci->io_cfg_atu_shared = true; - } - - if (pci->num_ob_windows <= atu_idx) - dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)", - pci->num_ob_windows); + ret = dw_pcie_iatu_setup(pp); + if (ret) + return ret; } dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); @@ -641,5 +731,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); dw_pcie_dbi_ro_wr_dis(pci); + + return 0; } EXPORT_SYMBOL_GPL(dw_pcie_setup_rc); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 8851eb161a0eb..1fcfb840f2385 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -17,13 +17,11 @@ #include #include #include -#include #include "pcie-designware.h" struct dw_plat_pcie { struct dw_pcie *pci; - struct regmap *regmap; enum dw_pcie_device_mode mode; }; @@ -31,20 +29,9 @@ struct dw_plat_pcie_of_data { enum dw_pcie_device_mode mode; }; -static const struct of_device_id dw_plat_pcie_of_match[]; - static const struct dw_pcie_host_ops dw_plat_pcie_host_ops = { }; -static int dw_plat_pcie_establish_link(struct dw_pcie *pci) -{ - return 0; -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .start_link = dw_plat_pcie_establish_link, -}; - static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); @@ -96,7 +83,7 @@ static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie, struct platform_device *pdev) { struct dw_pcie *pci = dw_plat_pcie->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; struct device *dev = &pdev->dev; int ret; @@ -122,15 +109,13 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) struct dw_plat_pcie *dw_plat_pcie; struct dw_pcie *pci; int ret; - const struct of_device_id *match; const struct dw_plat_pcie_of_data *data; enum dw_pcie_device_mode mode; - match = of_match_device(dw_plat_pcie_of_match, dev); - if (!match) + data = of_device_get_match_data(dev); + if (!data) return -EINVAL; - data = (struct dw_plat_pcie_of_data *)match->data; mode = (enum dw_pcie_device_mode)data->mode; dw_plat_pcie = devm_kzalloc(dev, sizeof(*dw_plat_pcie), GFP_KERNEL); @@ -142,7 +127,6 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) return -ENOMEM; pci->dev = dev; - pci->ops = &dw_pcie_ops; dw_plat_pcie->pci = pci; dw_plat_pcie->mode = mode; @@ -155,20 +139,21 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) return -ENODEV; ret = dw_plat_add_pcie_port(dw_plat_pcie, pdev); - if (ret < 0) - return ret; break; case DW_PCIE_EP_TYPE: if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_EP)) return -ENODEV; pci->ep.ops = &pcie_ep_ops; - return dw_pcie_ep_init(&pci->ep); + ret = dw_pcie_ep_init(&pci->ep); + break; default: dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode); + ret = -EINVAL; + break; } - return 0; + return ret; } static const struct dw_plat_pcie_of_data dw_plat_pcie_rc_of_data = { diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index e408ebf5bd738..6c7a83b75b82e 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -8,14 +8,199 @@ * Author: Jingoo Han */ +#include +#include +#include #include +#include +#include +#include #include #include +#include #include #include "../../pci.h" #include "pcie-designware.h" +static const char * const dw_pcie_app_clks[DW_PCIE_NUM_APP_CLKS] = { + [DW_PCIE_DBI_CLK] = "dbi", + [DW_PCIE_MSTR_CLK] = "mstr", + [DW_PCIE_SLV_CLK] = "slv", +}; + +static const char * const dw_pcie_core_clks[DW_PCIE_NUM_CORE_CLKS] = { + [DW_PCIE_PIPE_CLK] = "pipe", + [DW_PCIE_CORE_CLK] = "core", + [DW_PCIE_AUX_CLK] = "aux", + [DW_PCIE_REF_CLK] = "ref", +}; + +static const char * const dw_pcie_app_rsts[DW_PCIE_NUM_APP_RSTS] = { + [DW_PCIE_DBI_RST] = "dbi", + [DW_PCIE_MSTR_RST] = "mstr", + [DW_PCIE_SLV_RST] = "slv", +}; + +static const char * const dw_pcie_core_rsts[DW_PCIE_NUM_CORE_RSTS] = { + [DW_PCIE_NON_STICKY_RST] = "non-sticky", + [DW_PCIE_STICKY_RST] = "sticky", + [DW_PCIE_CORE_RST] = "core", + [DW_PCIE_PIPE_RST] = "pipe", + [DW_PCIE_PHY_RST] = "phy", + [DW_PCIE_HOT_RST] = "hot", + [DW_PCIE_PWR_RST] = "pwr", +}; + +static int dw_pcie_get_clocks(struct dw_pcie *pci) +{ + int i, ret; + + for (i = 0; i < DW_PCIE_NUM_APP_CLKS; i++) + pci->app_clks[i].id = dw_pcie_app_clks[i]; + + for (i = 0; i < DW_PCIE_NUM_CORE_CLKS; i++) + pci->core_clks[i].id = dw_pcie_core_clks[i]; + + ret = devm_clk_bulk_get_optional(pci->dev, DW_PCIE_NUM_APP_CLKS, + pci->app_clks); + if (ret) + return ret; + + return devm_clk_bulk_get_optional(pci->dev, DW_PCIE_NUM_CORE_CLKS, + pci->core_clks); +} + +static int dw_pcie_get_resets(struct dw_pcie *pci) +{ + int i, ret; + + for (i = 0; i < DW_PCIE_NUM_APP_RSTS; i++) + pci->app_rsts[i].id = dw_pcie_app_rsts[i]; + + for (i = 0; i < DW_PCIE_NUM_CORE_RSTS; i++) + pci->core_rsts[i].id = dw_pcie_core_rsts[i]; + + ret = devm_reset_control_bulk_get_optional_shared(pci->dev, + DW_PCIE_NUM_APP_RSTS, + pci->app_rsts); + if (ret) + return ret; + + ret = devm_reset_control_bulk_get_optional_exclusive(pci->dev, + DW_PCIE_NUM_CORE_RSTS, + pci->core_rsts); + if (ret) + return ret; + + pci->pe_rst = devm_gpiod_get_optional(pci->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(pci->pe_rst)) + return PTR_ERR(pci->pe_rst); + + return 0; +} + +int dw_pcie_get_resources(struct dw_pcie *pci) +{ + struct platform_device *pdev = to_platform_device(pci->dev); + struct device_node *np = dev_of_node(pci->dev); + struct resource *res; + int ret; + + if (!pci->dbi_base) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pci->dbi_base = devm_pci_remap_cfg_resource(pci->dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + } + + /* DBI2 is mainly useful for the endpoint controller */ + if (!pci->dbi_base2) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); + if (res) { + pci->dbi_base2 = devm_pci_remap_cfg_resource(pci->dev, res); + if (IS_ERR(pci->dbi_base2)) + return PTR_ERR(pci->dbi_base2); + } else { + pci->dbi_base2 = pci->dbi_base + SZ_4K; + } + } + + /* For non-unrolled iATU/eDMA platforms this range will be ignored */ + if (!pci->atu_base) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu"); + if (res) { + pci->atu_size = resource_size(res); + pci->atu_base = devm_ioremap_resource(pci->dev, res); + if (IS_ERR(pci->atu_base)) + return PTR_ERR(pci->atu_base); + } else { + pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET; + } + } + + /* Set a default value suitable for at most 8 in and 8 out windows */ + if (!pci->atu_size) + pci->atu_size = SZ_4K; + + /* eDMA region can be mapped to a custom base address */ + if (!pci->edma.reg_base) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + if (res) { + pci->edma.reg_base = devm_ioremap_resource(pci->dev, res); + if (IS_ERR(pci->edma.reg_base)) + return PTR_ERR(pci->edma.reg_base); + } else if (pci->atu_size >= 2 * DEFAULT_DBI_DMA_OFFSET) { + pci->edma.reg_base = pci->atu_base + DEFAULT_DBI_DMA_OFFSET; + } + } + + /* LLDD is supposed to manually switch the clocks and resets state */ + if (dw_pcie_cap_is(pci, REQ_RES)) { + ret = dw_pcie_get_clocks(pci); + if (ret) + return ret; + + ret = dw_pcie_get_resets(pci); + if (ret) + return ret; + } + + if (pci->link_gen < 1) + pci->link_gen = of_pci_get_max_link_speed(np); + + of_property_read_u32(np, "num-lanes", &pci->num_lanes); + + if (of_property_read_bool(np, "snps,enable-cdm-check")) + dw_pcie_cap_set(pci, CDM_CHECK); + + return 0; +} + +void dw_pcie_version_detect(struct dw_pcie *pci) +{ + u32 ver; + + /* The content of the CSR is zero on DWC PCIe older than v4.70a */ + ver = dw_pcie_readl_dbi(pci, PCIE_VERSION_NUMBER); + if (!ver) + return; + + if (pci->version && pci->version != ver) + dev_warn(pci->dev, "Versions don't match (%08x != %08x)\n", + pci->version, ver); + else + pci->version = ver; + + ver = dw_pcie_readl_dbi(pci, PCIE_VERSION_TYPE); + + if (pci->type && pci->type != ver) + dev_warn(pci->dev, "Types don't match (%08x != %08x)\n", + pci->type, ver); + else + pci->type = ver; +} + /* * These interfaces resemble the pci_find_*capability() interfaces, but these * are for configuring host controllers, which are bridges *to* PCI devices but @@ -181,48 +366,64 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val) dev_err(pci->dev, "write DBI address failed\n"); } -static u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 reg) +static inline void __iomem *dw_pcie_select_atu(struct dw_pcie *pci, u32 dir, + u32 index) { + void __iomem *base = pci->atu_base; + + if (dw_pcie_cap_is(pci, IATU_UNROLL)) + base += PCIE_ATU_UNROLL_BASE(dir, index); + else + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, dir | index); + + return base; +} + +static u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 dir, u32 index, u32 reg) +{ + void __iomem *base; int ret; u32 val; + base = dw_pcie_select_atu(pci, dir, index); + if (pci->ops && pci->ops->read_dbi) - return pci->ops->read_dbi(pci, pci->atu_base, reg, 4); + return pci->ops->read_dbi(pci, base, reg, 4); - ret = dw_pcie_read(pci->atu_base + reg, 4, &val); + ret = dw_pcie_read(base + reg, 4, &val); if (ret) dev_err(pci->dev, "Read ATU address failed\n"); return val; } -static void dw_pcie_writel_atu(struct dw_pcie *pci, u32 reg, u32 val) +static void dw_pcie_writel_atu(struct dw_pcie *pci, u32 dir, u32 index, + u32 reg, u32 val) { + void __iomem *base; int ret; + base = dw_pcie_select_atu(pci, dir, index); + if (pci->ops && pci->ops->write_dbi) { - pci->ops->write_dbi(pci, pci->atu_base, reg, 4, val); + pci->ops->write_dbi(pci, base, reg, 4, val); return; } - ret = dw_pcie_write(pci->atu_base + reg, 4, val); + ret = dw_pcie_write(base + reg, 4, val); if (ret) dev_err(pci->dev, "Write ATU address failed\n"); } -static u32 dw_pcie_readl_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg) +static inline u32 dw_pcie_readl_atu_ob(struct dw_pcie *pci, u32 index, u32 reg) { - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - - return dw_pcie_readl_atu(pci, offset + reg); + return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_OB, index, reg); } -static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg, - u32 val) +static inline void dw_pcie_writel_atu_ob(struct dw_pcie *pci, u32 index, u32 reg, + u32 val) { - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - - dw_pcie_writel_atu(pci, offset + reg, val); + dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_OB, index, reg, val); } static inline u32 dw_pcie_enable_ecrc(u32 val) @@ -266,53 +467,9 @@ static inline u32 dw_pcie_enable_ecrc(u32 val) return val | PCIE_ATU_TD; } -static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no, - int index, int type, - u64 cpu_addr, u64 pci_addr, - u64 size) -{ - u32 retries, val; - u64 limit_addr = cpu_addr + size - 1; - - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, - lower_32_bits(cpu_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, - upper_32_bits(cpu_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_LIMIT, - lower_32_bits(limit_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_LIMIT, - upper_32_bits(limit_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, - lower_32_bits(pci_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, - upper_32_bits(pci_addr)); - val = type | PCIE_ATU_FUNC_NUM(func_no); - if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr)) - val |= PCIE_ATU_INCREASE_REGION_SIZE; - if (pci->version == 0x490A) - val = dw_pcie_enable_ecrc(val); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, - PCIE_ATU_ENABLE); - - /* - * Make sure ATU enable takes effect before any subsequent config - * and I/O accesses. - */ - for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = dw_pcie_readl_ob_unroll(pci, index, - PCIE_ATU_UNR_REGION_CTRL2); - if (val & PCIE_ATU_ENABLE) - return; - - mdelay(LINK_WAIT_IATU); - } - dev_err(pci->dev, "Outbound iATU is not being enabled\n"); -} - -static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no, - int index, int type, u64 cpu_addr, - u64 pci_addr, u64 size) +static int __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no, + int index, int type, u64 cpu_addr, + u64 pci_addr, u64 size) { u32 retries, val; u64 limit_addr; @@ -320,224 +477,202 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no, if (pci->ops && pci->ops->cpu_addr_fixup) cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr); - if (pci->iatu_unroll_enabled) { - dw_pcie_prog_outbound_atu_unroll(pci, func_no, index, type, - cpu_addr, pci_addr, size); - return; + limit_addr = cpu_addr + size - 1; + + if ((limit_addr & ~pci->region_limit) != (cpu_addr & ~pci->region_limit) || + !IS_ALIGNED(cpu_addr, pci->region_align) || + !IS_ALIGNED(pci_addr, pci->region_align) || !size) { + return -EINVAL; } - limit_addr = cpu_addr + size - 1; + dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LOWER_BASE, + lower_32_bits(cpu_addr)); + dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_BASE, + upper_32_bits(cpu_addr)); + + dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LIMIT, + lower_32_bits(limit_addr)); + if (dw_pcie_ver_is_ge(pci, 460A)) + dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_LIMIT, + upper_32_bits(limit_addr)); + + dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LOWER_TARGET, + lower_32_bits(pci_addr)); + dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_TARGET, + upper_32_bits(pci_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, - PCIE_ATU_REGION_OUTBOUND | index); - dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, - lower_32_bits(cpu_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, - upper_32_bits(cpu_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, - lower_32_bits(limit_addr)); - if (pci->version >= 0x460A) - dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT, - upper_32_bits(limit_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, - lower_32_bits(pci_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, - upper_32_bits(pci_addr)); val = type | PCIE_ATU_FUNC_NUM(func_no); if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) && - pci->version >= 0x460A) + dw_pcie_ver_is_ge(pci, 460A)) val |= PCIE_ATU_INCREASE_REGION_SIZE; - if (pci->version == 0x490A) + if (dw_pcie_ver_is(pci, 490A)) val = dw_pcie_enable_ecrc(val); - dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val); - dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE); + dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_REGION_CTRL1, val); + + dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_REGION_CTRL2, PCIE_ATU_ENABLE); /* * Make sure ATU enable takes effect before any subsequent config * and I/O accesses. */ for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); + val = dw_pcie_readl_atu_ob(pci, index, PCIE_ATU_REGION_CTRL2); if (val & PCIE_ATU_ENABLE) - return; + return 0; mdelay(LINK_WAIT_IATU); } + dev_err(pci->dev, "Outbound iATU is not being enabled\n"); + + return -ETIMEDOUT; } -void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, - u64 cpu_addr, u64 pci_addr, u64 size) +int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, + u64 cpu_addr, u64 pci_addr, u64 size) { - __dw_pcie_prog_outbound_atu(pci, 0, index, type, - cpu_addr, pci_addr, size); + return __dw_pcie_prog_outbound_atu(pci, 0, index, type, + cpu_addr, pci_addr, size); } -void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index, - int type, u64 cpu_addr, u64 pci_addr, - u64 size) +int dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index, + int type, u64 cpu_addr, u64 pci_addr, + u64 size) { - __dw_pcie_prog_outbound_atu(pci, func_no, index, type, - cpu_addr, pci_addr, size); + return __dw_pcie_prog_outbound_atu(pci, func_no, index, type, + cpu_addr, pci_addr, size); } -static u32 dw_pcie_readl_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg) +static inline u32 dw_pcie_readl_atu_ib(struct dw_pcie *pci, u32 index, u32 reg) { - u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index); - - return dw_pcie_readl_atu(pci, offset + reg); + return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg); } -static void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg, - u32 val) +static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg, + u32 val) { - u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index); - - dw_pcie_writel_atu(pci, offset + reg, val); + dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val); } -static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, u8 func_no, - int index, int bar, u64 cpu_addr, - enum dw_pcie_as_type as_type) +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, + u64 cpu_addr, u64 pci_addr, u64 size) { - int type; + u64 limit_addr = pci_addr + size - 1; u32 retries, val; - dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, - lower_32_bits(cpu_addr)); - dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, - upper_32_bits(cpu_addr)); - - switch (as_type) { - case DW_PCIE_AS_MEM: - type = PCIE_ATU_TYPE_MEM; - break; - case DW_PCIE_AS_IO: - type = PCIE_ATU_TYPE_IO; - break; - default: + if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) || + !IS_ALIGNED(cpu_addr, pci->region_align) || + !IS_ALIGNED(pci_addr, pci->region_align) || !size) { return -EINVAL; } - dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, type | - PCIE_ATU_FUNC_NUM(func_no)); - dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, - PCIE_ATU_FUNC_NUM_MATCH_EN | - PCIE_ATU_ENABLE | - PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE, + lower_32_bits(pci_addr)); + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_BASE, + upper_32_bits(pci_addr)); + + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LIMIT, + lower_32_bits(limit_addr)); + if (dw_pcie_ver_is_ge(pci, 460A)) + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_LIMIT, + upper_32_bits(limit_addr)); + + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET, + lower_32_bits(cpu_addr)); + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET, + upper_32_bits(cpu_addr)); + + val = type; + if (upper_32_bits(limit_addr) > upper_32_bits(pci_addr) && + dw_pcie_ver_is_ge(pci, 460A)) + val |= PCIE_ATU_INCREASE_REGION_SIZE; + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, val); + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2, PCIE_ATU_ENABLE); /* * Make sure ATU enable takes effect before any subsequent config * and I/O accesses. */ for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = dw_pcie_readl_ib_unroll(pci, index, - PCIE_ATU_UNR_REGION_CTRL2); + val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2); if (val & PCIE_ATU_ENABLE) return 0; mdelay(LINK_WAIT_IATU); } + dev_err(pci->dev, "Inbound iATU is not being enabled\n"); - return -EBUSY; + return -ETIMEDOUT; } -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, - int bar, u64 cpu_addr, - enum dw_pcie_as_type as_type) +int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, + int type, u64 cpu_addr, u8 bar) { - int type; u32 retries, val; - if (pci->iatu_unroll_enabled) - return dw_pcie_prog_inbound_atu_unroll(pci, func_no, index, bar, - cpu_addr, as_type); - - dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND | - index); - dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, lower_32_bits(cpu_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, upper_32_bits(cpu_addr)); - - switch (as_type) { - case DW_PCIE_AS_MEM: - type = PCIE_ATU_TYPE_MEM; - break; - case DW_PCIE_AS_IO: - type = PCIE_ATU_TYPE_IO; - break; - default: + if (!IS_ALIGNED(cpu_addr, pci->region_align)) return -EINVAL; - } - dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type | - PCIE_ATU_FUNC_NUM(func_no)); - dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE | - PCIE_ATU_FUNC_NUM_MATCH_EN | - PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET, + lower_32_bits(cpu_addr)); + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET, + upper_32_bits(cpu_addr)); + + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, type | + PCIE_ATU_FUNC_NUM(func_no)); + dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2, + PCIE_ATU_ENABLE | PCIE_ATU_FUNC_NUM_MATCH_EN | + PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); /* * Make sure ATU enable takes effect before any subsequent config * and I/O accesses. */ for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); + val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2); if (val & PCIE_ATU_ENABLE) return 0; mdelay(LINK_WAIT_IATU); } + dev_err(pci->dev, "Inbound iATU is not being enabled\n"); - return -EBUSY; + return -ETIMEDOUT; } -void dw_pcie_disable_atu(struct dw_pcie *pci, int index, - enum dw_pcie_region_type type) +void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index) { - u32 region; - - switch (type) { - case DW_PCIE_REGION_INBOUND: - region = PCIE_ATU_REGION_INBOUND; - break; - case DW_PCIE_REGION_OUTBOUND: - region = PCIE_ATU_REGION_OUTBOUND; - break; - default: - return; - } - - if (pci->iatu_unroll_enabled) { - if (region == PCIE_ATU_REGION_INBOUND) { - dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, - ~(u32)PCIE_ATU_ENABLE); - } else { - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, - ~(u32)PCIE_ATU_ENABLE); - } - } else { - dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, region | index); - dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~(u32)PCIE_ATU_ENABLE); - } + dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0); } int dw_pcie_wait_for_link(struct dw_pcie *pci) { + u32 offset, val; int retries; /* Check if the link is up or not */ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (dw_pcie_link_up(pci)) { - dev_info(pci->dev, "Link up\n"); - return 0; - } + if (dw_pcie_link_up(pci)) + break; + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); } - dev_info(pci->dev, "Phy link never came up\n"); + if (retries >= LINK_WAIT_MAX_RETRIES) { + dev_err(pci->dev, "Phy link never came up\n"); + return -ETIMEDOUT; + } + + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA); - return -ETIMEDOUT; + dev_info(pci->dev, "PCIe Gen.%u x%u link up\n", + FIELD_GET(PCI_EXP_LNKSTA_CLS, val), + FIELD_GET(PCI_EXP_LNKSTA_NLW, val)); + + return 0; } EXPORT_SYMBOL_GPL(dw_pcie_wait_for_link); @@ -548,10 +683,11 @@ int dw_pcie_link_up(struct dw_pcie *pci) if (pci->ops && pci->ops->link_up) return pci->ops->link_up(pci); - val = readl(pci->dbi_base + PCIE_PORT_DEBUG1); + val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG1); return ((val & PCIE_PORT_DEBUG1_LINK_UP) && (!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING))); } +EXPORT_SYMBOL_GPL(dw_pcie_link_up); void dw_pcie_upconfig_setup(struct dw_pcie *pci) { @@ -599,120 +735,245 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen) } -static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) +void dw_pcie_iatu_detect(struct dw_pcie *pci) { - u32 val; + int max_region, ob, ib; + u32 val, min, dir; + u64 max; val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT); - if (val == 0xffffffff) - return 1; - - return 0; -} + if (val == 0xFFFFFFFF) { + dw_pcie_cap_set(pci, IATU_UNROLL); -static void dw_pcie_iatu_detect_regions_unroll(struct dw_pcie *pci) -{ - int max_region, i, ob = 0, ib = 0; - u32 val; + max_region = min((int)pci->atu_size / 512, 256); + } else { + pci->atu_base = pci->dbi_base + PCIE_ATU_VIEWPORT_BASE; + pci->atu_size = PCIE_ATU_VIEWPORT_SIZE; - max_region = min((int)pci->atu_size / 512, 256); + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, 0xFF); + max_region = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) + 1; + } - for (i = 0; i < max_region; i++) { - dw_pcie_writel_ob_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET, - 0x11110000); + for (ob = 0; ob < max_region; ob++) { + dw_pcie_writel_atu_ob(pci, ob, PCIE_ATU_LOWER_TARGET, 0x11110000); + val = dw_pcie_readl_atu_ob(pci, ob, PCIE_ATU_LOWER_TARGET); + if (val != 0x11110000) + break; + } - val = dw_pcie_readl_ob_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET); - if (val == 0x11110000) - ob++; - else + for (ib = 0; ib < max_region; ib++) { + dw_pcie_writel_atu_ib(pci, ib, PCIE_ATU_LOWER_TARGET, 0x11110000); + val = dw_pcie_readl_atu_ib(pci, ib, PCIE_ATU_LOWER_TARGET); + if (val != 0x11110000) break; } - for (i = 0; i < max_region; i++) { - dw_pcie_writel_ib_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET, - 0x11110000); + if (ob) { + dir = PCIE_ATU_REGION_DIR_OB; + } else if (ib) { + dir = PCIE_ATU_REGION_DIR_IB; + } else { + dev_err(pci->dev, "No iATU regions found\n"); + return; + } - val = dw_pcie_readl_ib_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET); - if (val == 0x11110000) - ib++; - else - break; + dw_pcie_writel_atu(pci, dir, 0, PCIE_ATU_LIMIT, 0x0); + min = dw_pcie_readl_atu(pci, dir, 0, PCIE_ATU_LIMIT); + + if (dw_pcie_ver_is_ge(pci, 460A)) { + dw_pcie_writel_atu(pci, dir, 0, PCIE_ATU_UPPER_LIMIT, 0xFFFFFFFF); + max = dw_pcie_readl_atu(pci, dir, 0, PCIE_ATU_UPPER_LIMIT); + } else { + max = 0; } - pci->num_ib_windows = ib; + pci->num_ob_windows = ob; + pci->num_ib_windows = ib; + pci->region_align = 1 << fls(min); + pci->region_limit = (max << 32) | (SZ_4G - 1); + + dev_info(pci->dev, "iATU: unroll %s, %u ob, %u ib, align %uK, limit %lluG\n", + dw_pcie_cap_is(pci, IATU_UNROLL) ? "T" : "F", + pci->num_ob_windows, pci->num_ib_windows, + pci->region_align / SZ_1K, (pci->region_limit + 1) / SZ_1G); +} + +static u32 dw_pcie_readl_dma(struct dw_pcie *pci, u32 reg) +{ + u32 val = 0; + int ret; + + if (pci->ops && pci->ops->read_dbi) + return pci->ops->read_dbi(pci, pci->edma.reg_base, reg, 4); + + ret = dw_pcie_read(pci->edma.reg_base + reg, 4, &val); + if (ret) + dev_err(pci->dev, "Read DMA address failed\n"); + + return val; } -static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci) +static int dw_pcie_edma_irq_vector(struct device *dev, unsigned int nr) +{ + struct platform_device *pdev = to_platform_device(dev); + char name[6]; + int ret; + + if (nr >= EDMA_MAX_WR_CH + EDMA_MAX_RD_CH) + return -EINVAL; + + ret = platform_get_irq_byname_optional(pdev, "dma"); + if (ret > 0) + return ret; + + snprintf(name, sizeof(name), "dma%u", nr); + + return platform_get_irq_byname_optional(pdev, name); +} + +static struct dw_edma_core_ops dw_pcie_edma_ops = { + .irq_vector = dw_pcie_edma_irq_vector, +}; + +static int dw_pcie_edma_find_chip(struct dw_pcie *pci) { - int max_region, i, ob = 0, ib = 0; u32 val; - dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, 0xFF); - max_region = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) + 1; + val = dw_pcie_readl_dbi(pci, PCIE_DMA_VIEWPORT_BASE + PCIE_DMA_CTRL); + if (val == 0xFFFFFFFF && pci->edma.reg_base) { + pci->edma.mf = EDMA_MF_EDMA_UNROLL; - for (i = 0; i < max_region; i++) { - dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_OUTBOUND | i); - dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, 0x11110000); - val = dw_pcie_readl_dbi(pci, PCIE_ATU_LOWER_TARGET); - if (val == 0x11110000) - ob++; - else - break; + val = dw_pcie_readl_dma(pci, PCIE_DMA_CTRL); + } else if (val != 0xFFFFFFFF) { + pci->edma.mf = EDMA_MF_EDMA_LEGACY; + + pci->edma.reg_base = pci->dbi_base + PCIE_DMA_VIEWPORT_BASE; + } else { + return -ENODEV; } - for (i = 0; i < max_region; i++) { - dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND | i); - dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, 0x11110000); - val = dw_pcie_readl_dbi(pci, PCIE_ATU_LOWER_TARGET); - if (val == 0x11110000) - ib++; - else - break; + pci->edma.dev = pci->dev; + + if (!pci->edma.ops) + pci->edma.ops = &dw_pcie_edma_ops; + + pci->edma.flags |= DW_EDMA_CHIP_LOCAL; + + pci->edma.ll_wr_cnt = FIELD_GET(PCIE_DMA_NUM_WR_CHAN, val); + pci->edma.ll_rd_cnt = FIELD_GET(PCIE_DMA_NUM_RD_CHAN, val); + + /* Sanity check the channels count if the mapping was incorrect */ + if (!pci->edma.ll_wr_cnt || pci->edma.ll_wr_cnt > EDMA_MAX_WR_CH || + !pci->edma.ll_rd_cnt || pci->edma.ll_rd_cnt > EDMA_MAX_RD_CH) + return -EINVAL; + + return 0; +} + +static int dw_pcie_edma_irq_verify(struct dw_pcie *pci) +{ + struct platform_device *pdev = to_platform_device(pci->dev); + u16 ch_cnt = pci->edma.ll_wr_cnt + pci->edma.ll_rd_cnt; + char name[6]; + int ret; + + if (pci->edma.nr_irqs == 1) + return 0; + else if (pci->edma.nr_irqs > 1) + return pci->edma.nr_irqs != ch_cnt ? -EINVAL : 0; + + ret = platform_get_irq_byname_optional(pdev, "dma"); + if (ret > 0) { + pci->edma.nr_irqs = 1; + return 0; } - pci->num_ib_windows = ib; - pci->num_ob_windows = ob; + for (; pci->edma.nr_irqs < ch_cnt; pci->edma.nr_irqs++) { + snprintf(name, sizeof(name), "dma%d", pci->edma.nr_irqs); + + ret = platform_get_irq_byname_optional(pdev, name); + if (ret <= 0) + return -EINVAL; + } + + return 0; } -void dw_pcie_iatu_detect(struct dw_pcie *pci) +static int dw_pcie_edma_ll_alloc(struct dw_pcie *pci) { - struct device *dev = pci->dev; - struct platform_device *pdev = to_platform_device(dev); + struct dw_edma_region *ll; + dma_addr_t paddr; + int i; + + for (i = 0; i < pci->edma.ll_wr_cnt; i++) { + ll = &pci->edma.ll_region_wr[i]; + ll->sz = DMA_LLP_MEM_SIZE; + ll->vaddr = dmam_alloc_coherent(pci->dev, ll->sz, + &paddr, GFP_KERNEL); + if (!ll->vaddr) + return -ENOMEM; + + ll->paddr = paddr; + } - if (pci->version >= 0x480A || (!pci->version && - dw_pcie_iatu_unroll_enabled(pci))) { - pci->iatu_unroll_enabled = true; - if (!pci->atu_base) { - struct resource *res = - platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu"); - if (res) { - pci->atu_size = resource_size(res); - pci->atu_base = devm_ioremap_resource(dev, res); - } - if (!pci->atu_base || IS_ERR(pci->atu_base)) - pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET; - } + for (i = 0; i < pci->edma.ll_rd_cnt; i++) { + ll = &pci->edma.ll_region_rd[i]; + ll->sz = DMA_LLP_MEM_SIZE; + ll->vaddr = dmam_alloc_coherent(pci->dev, ll->sz, + &paddr, GFP_KERNEL); + if (!ll->vaddr) + return -ENOMEM; + + ll->paddr = paddr; + } + + return 0; +} + +int dw_pcie_edma_detect(struct dw_pcie *pci) +{ + int ret; + + /* Don't fail if no eDMA was found (for the backward compatibility) */ + ret = dw_pcie_edma_find_chip(pci); + if (ret) + return 0; + + /* Don't fail on the IRQs verification (for the backward compatibility) */ + ret = dw_pcie_edma_irq_verify(pci); + if (ret) { + dev_err(pci->dev, "Invalid eDMA IRQs found\n"); + return 0; + } - if (!pci->atu_size) - /* Pick a minimal default, enough for 8 in and 8 out windows */ - pci->atu_size = SZ_4K; + ret = dw_pcie_edma_ll_alloc(pci); + if (ret) { + dev_err(pci->dev, "Couldn't allocate LLP memory\n"); + return ret; + } - dw_pcie_iatu_detect_regions_unroll(pci); - } else - dw_pcie_iatu_detect_regions(pci); + /* Don't fail if the DW eDMA driver can't find the device */ + ret = dw_edma_probe(&pci->edma); + if (ret && ret != -ENODEV) { + dev_err(pci->dev, "Couldn't register eDMA device\n"); + return ret; + } - dev_info(pci->dev, "iATU unroll: %s\n", pci->iatu_unroll_enabled ? - "enabled" : "disabled"); + dev_info(pci->dev, "eDMA: unroll %s, %hu wr, %hu rd\n", + pci->edma.mf == EDMA_MF_EDMA_UNROLL ? "T" : "F", + pci->edma.ll_wr_cnt, pci->edma.ll_rd_cnt); - dev_info(pci->dev, "Detected iATU regions: %u outbound, %u inbound", - pci->num_ob_windows, pci->num_ib_windows); + return 0; +} + +void dw_pcie_edma_remove(struct dw_pcie *pci) +{ + dw_edma_remove(&pci->edma); } void dw_pcie_setup(struct dw_pcie *pci) { u32 val; - struct device *dev = pci->dev; - struct device_node *np = dev->of_node; if (pci->link_gen > 0) dw_pcie_link_set_max_speed(pci, pci->link_gen); @@ -739,14 +1000,13 @@ void dw_pcie_setup(struct dw_pcie *pci) val |= PORT_LINK_DLL_LINK_EN; dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); - if (of_property_read_bool(np, "snps,enable-cdm-check")) { + if (dw_pcie_cap_is(pci, CDM_CHECK)) { val = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS); val |= PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS | PCIE_PL_CHK_REG_CHK_REG_START; dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val); } - of_property_read_u32(np, "num-lanes", &pci->num_lanes); if (!pci->num_lanes) { dev_dbg(pci->dev, "Using h/w default number of lanes\n"); return; diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 7d6e9b7576be5..832a0c43176ed 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -12,14 +12,53 @@ #define _PCIE_DESIGNWARE_H #include +#include +#include #include +#include +#include #include #include #include +#include #include #include +/* DWC PCIe IP-core versions (native support since v4.70a) */ +#define DW_PCIE_VER_365A 0x3336352a +#define DW_PCIE_VER_460A 0x3436302a +#define DW_PCIE_VER_470A 0x3437302a +#define DW_PCIE_VER_480A 0x3438302a +#define DW_PCIE_VER_490A 0x3439302a +#define DW_PCIE_VER_520A 0x3532302a + +#define __dw_pcie_ver_cmp(_pci, _ver, _op) \ + ((_pci)->version _op DW_PCIE_VER_ ## _ver) + +#define dw_pcie_ver_is(_pci, _ver) __dw_pcie_ver_cmp(_pci, _ver, ==) + +#define dw_pcie_ver_is_ge(_pci, _ver) __dw_pcie_ver_cmp(_pci, _ver, >=) + +#define dw_pcie_ver_type_is(_pci, _ver, _type) \ + (__dw_pcie_ver_cmp(_pci, _ver, ==) && \ + __dw_pcie_ver_cmp(_pci, TYPE_ ## _type, ==)) + +#define dw_pcie_ver_type_is_ge(_pci, _ver, _type) \ + (__dw_pcie_ver_cmp(_pci, _ver, ==) && \ + __dw_pcie_ver_cmp(_pci, TYPE_ ## _type, >=)) + +/* DWC PCIe controller capabilities */ +#define DW_PCIE_CAP_REQ_RES 0 +#define DW_PCIE_CAP_IATU_UNROLL 1 +#define DW_PCIE_CAP_CDM_CHECK 2 + +#define dw_pcie_cap_is(_pci, _cap) \ + test_bit(DW_PCIE_CAP_ ## _cap, &(_pci)->caps) + +#define dw_pcie_cap_set(_pci, _cap) \ + set_bit(DW_PCIE_CAP_ ## _cap, &(_pci)->caps) + /* Parameters for the waiting for link up routine */ #define LINK_WAIT_MAX_RETRIES 10 #define LINK_WAIT_USLEEP_MIN 90000 @@ -77,10 +116,24 @@ #define PCIE_PORT_MULTI_LANE_CTRL 0x8C0 #define PORT_MLTI_UPCFG_SUPPORT BIT(7) +#define PCIE_VERSION_NUMBER 0x8F8 +#define PCIE_VERSION_TYPE 0x8FC + +/* + * iATU inbound and outbound windows CSRs. Before the IP-core v4.80a each + * iATU region CSRs had been indirectly accessible by means of the dedicated + * viewport selector. The iATU/eDMA CSRs space was re-designed in DWC PCIe + * v4.80a in a way so the viewport was unrolled into the directly accessible + * iATU/eDMA CSRs space. + */ #define PCIE_ATU_VIEWPORT 0x900 -#define PCIE_ATU_REGION_INBOUND BIT(31) -#define PCIE_ATU_REGION_OUTBOUND 0 -#define PCIE_ATU_CR1 0x904 +#define PCIE_ATU_REGION_DIR_IB BIT(31) +#define PCIE_ATU_REGION_DIR_OB 0 +#define PCIE_ATU_VIEWPORT_BASE 0x904 +#define PCIE_ATU_UNROLL_BASE(dir, index) \ + (((index) << 9) | ((dir == PCIE_ATU_REGION_DIR_IB) ? BIT(8) : 0)) +#define PCIE_ATU_VIEWPORT_SIZE 0x2C +#define PCIE_ATU_REGION_CTRL1 0x000 #define PCIE_ATU_INCREASE_REGION_SIZE BIT(13) #define PCIE_ATU_TYPE_MEM 0x0 #define PCIE_ATU_TYPE_IO 0x2 @@ -88,19 +141,19 @@ #define PCIE_ATU_TYPE_CFG1 0x5 #define PCIE_ATU_TD BIT(8) #define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20) -#define PCIE_ATU_CR2 0x908 +#define PCIE_ATU_REGION_CTRL2 0x004 #define PCIE_ATU_ENABLE BIT(31) #define PCIE_ATU_BAR_MODE_ENABLE BIT(30) #define PCIE_ATU_FUNC_NUM_MATCH_EN BIT(19) -#define PCIE_ATU_LOWER_BASE 0x90C -#define PCIE_ATU_UPPER_BASE 0x910 -#define PCIE_ATU_LIMIT 0x914 -#define PCIE_ATU_LOWER_TARGET 0x918 +#define PCIE_ATU_LOWER_BASE 0x008 +#define PCIE_ATU_UPPER_BASE 0x00C +#define PCIE_ATU_LIMIT 0x010 +#define PCIE_ATU_LOWER_TARGET 0x014 #define PCIE_ATU_BUS(x) FIELD_PREP(GENMASK(31, 24), x) #define PCIE_ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x) #define PCIE_ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x) -#define PCIE_ATU_UPPER_TARGET 0x91C -#define PCIE_ATU_UPPER_LIMIT 0x924 +#define PCIE_ATU_UPPER_TARGET 0x018 +#define PCIE_ATU_UPPER_LIMIT 0x020 #define PCIE_MISC_CONTROL_1_OFF 0x8BC #define PCIE_DBI_RO_WR_EN BIT(0) @@ -108,6 +161,18 @@ #define PCIE_MSIX_DOORBELL 0x948 #define PCIE_MSIX_DOORBELL_PF_SHIFT 24 +/* + * eDMA CSRs. DW PCIe IP-core v4.70a and older had the eDMA registers accessible + * over the Port Logic registers space. Afterwords the unrolled mapping was + * introduced so eDMA and iATU could be accessed via a dedicated registers + * space. + */ +#define PCIE_DMA_VIEWPORT_BASE 0x970 +#define PCIE_DMA_UNROLL_BASE 0x80000 +#define PCIE_DMA_CTRL 0x008 +#define PCIE_DMA_NUM_WR_CHAN GENMASK(3, 0) +#define PCIE_DMA_NUM_RD_CHAN GENMASK(19, 16) + #define PCIE_PL_CHK_REG_CONTROL_STATUS 0xB20 #define PCIE_PL_CHK_REG_CHK_REG_START BIT(0) #define PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS BIT(1) @@ -117,19 +182,6 @@ #define PCIE_PL_CHK_REG_ERR_ADDR 0xB28 -/* - * iATU Unroll-specific register definitions - * From 4.80 core version the address translation will be made by unroll - */ -#define PCIE_ATU_UNR_REGION_CTRL1 0x00 -#define PCIE_ATU_UNR_REGION_CTRL2 0x04 -#define PCIE_ATU_UNR_LOWER_BASE 0x08 -#define PCIE_ATU_UNR_UPPER_BASE 0x0C -#define PCIE_ATU_UNR_LOWER_LIMIT 0x10 -#define PCIE_ATU_UNR_LOWER_TARGET 0x14 -#define PCIE_ATU_UNR_UPPER_TARGET 0x18 -#define PCIE_ATU_UNR_UPPER_LIMIT 0x20 - /* * The default address offset between dbi_base and atu_base. Root controller * drivers are not required to initialize atu_base if the offset matches this @@ -137,13 +189,7 @@ * this offset, if atu_base not set. */ #define DEFAULT_DBI_ATU_OFFSET (0x3 << 20) - -/* Register address builder */ -#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) \ - ((region) << 9) - -#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \ - (((region) << 9) | BIT(8)) +#define DEFAULT_DBI_DMA_OFFSET PCIE_DMA_UNROLL_BASE #define MAX_MSI_IRQS 256 #define MAX_MSI_IRQS_PER_CTRL 32 @@ -155,16 +201,13 @@ #define MAX_IATU_IN 256 #define MAX_IATU_OUT 256 -struct pcie_port; +/* Default eDMA LLP memory size */ +#define DMA_LLP_MEM_SIZE PAGE_SIZE + struct dw_pcie; +struct dw_pcie_rp; struct dw_pcie_ep; -enum dw_pcie_region_type { - DW_PCIE_REGION_UNKNOWN, - DW_PCIE_REGION_INBOUND, - DW_PCIE_REGION_OUTBOUND, -}; - enum dw_pcie_device_mode { DW_PCIE_UNKNOWN_TYPE, DW_PCIE_EP_TYPE, @@ -172,13 +215,48 @@ enum dw_pcie_device_mode { DW_PCIE_RC_TYPE, }; +enum dw_pcie_app_clk { + DW_PCIE_DBI_CLK, + DW_PCIE_MSTR_CLK, + DW_PCIE_SLV_CLK, + DW_PCIE_NUM_APP_CLKS +}; + +enum dw_pcie_core_clk { + DW_PCIE_PIPE_CLK, + DW_PCIE_CORE_CLK, + DW_PCIE_AUX_CLK, + DW_PCIE_REF_CLK, + DW_PCIE_NUM_CORE_CLKS +}; + +enum dw_pcie_app_rst { + DW_PCIE_DBI_RST, + DW_PCIE_MSTR_RST, + DW_PCIE_SLV_RST, + DW_PCIE_NUM_APP_RSTS +}; + +enum dw_pcie_core_rst { + DW_PCIE_NON_STICKY_RST, + DW_PCIE_STICKY_RST, + DW_PCIE_CORE_RST, + DW_PCIE_PIPE_RST, + DW_PCIE_PHY_RST, + DW_PCIE_HOT_RST, + DW_PCIE_PWR_RST, + DW_PCIE_NUM_CORE_RSTS +}; + struct dw_pcie_host_ops { - int (*host_init)(struct pcie_port *pp); - int (*msi_host_init)(struct pcie_port *pp); + int (*host_init)(struct dw_pcie_rp *pp); + void (*host_deinit)(struct dw_pcie_rp *pp); + int (*msi_host_init)(struct dw_pcie_rp *pp); }; -struct pcie_port { +struct dw_pcie_rp { bool has_msi_ctrl:1; + bool cfg0_io_shared:1; u64 cfg0_base; void __iomem *va_cfg0_base; u32 cfg0_size; @@ -200,12 +278,6 @@ struct pcie_port { DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; -enum dw_pcie_as_type { - DW_PCIE_AS_UNKNOWN, - DW_PCIE_AS_MEM, - DW_PCIE_AS_IO, -}; - struct dw_pcie_ep_ops { void (*ep_init)(struct dw_pcie_ep *ep); int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, @@ -261,20 +333,27 @@ struct dw_pcie { struct device *dev; void __iomem *dbi_base; void __iomem *dbi_base2; - /* Used when iatu_unroll_enabled is true */ void __iomem *atu_base; size_t atu_size; u32 num_ib_windows; u32 num_ob_windows; - struct pcie_port pp; + u32 region_align; + u64 region_limit; + struct dw_pcie_rp pp; struct dw_pcie_ep ep; const struct dw_pcie_ops *ops; - unsigned int version; + u32 version; + u32 type; + unsigned long caps; int num_lanes; int link_gen; u8 n_fts[2]; - bool iatu_unroll_enabled: 1; - bool io_cfg_atu_shared: 1; + struct dw_edma_chip edma; + struct clk_bulk_data app_clks[DW_PCIE_NUM_APP_CLKS]; + struct clk_bulk_data core_clks[DW_PCIE_NUM_CORE_CLKS]; + struct reset_control_bulk_data app_rsts[DW_PCIE_NUM_APP_RSTS]; + struct reset_control_bulk_data core_rsts[DW_PCIE_NUM_CORE_RSTS]; + struct gpio_desc *pe_rst; }; #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp) @@ -282,6 +361,10 @@ struct dw_pcie { #define to_dw_pcie_from_ep(endpoint) \ container_of((endpoint), struct dw_pcie, ep) +int dw_pcie_get_resources(struct dw_pcie *pci); + +void dw_pcie_version_detect(struct dw_pcie *pci); + u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap); u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap); @@ -294,19 +377,19 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val); int dw_pcie_link_up(struct dw_pcie *pci); void dw_pcie_upconfig_setup(struct dw_pcie *pci); int dw_pcie_wait_for_link(struct dw_pcie *pci); -void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, - int type, u64 cpu_addr, u64 pci_addr, - u64 size); -void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index, - int type, u64 cpu_addr, u64 pci_addr, - u64 size); -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, - int bar, u64 cpu_addr, - enum dw_pcie_as_type as_type); -void dw_pcie_disable_atu(struct dw_pcie *pci, int index, - enum dw_pcie_region_type type); +int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, + u64 cpu_addr, u64 pci_addr, u64 size); +int dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index, + int type, u64 cpu_addr, u64 pci_addr, u64 size); +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, + u64 cpu_addr, u64 pci_addr, u64 size); +int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, + int type, u64 cpu_addr, u8 bar); +void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index); void dw_pcie_setup(struct dw_pcie *pci); void dw_pcie_iatu_detect(struct dw_pcie *pci); +int dw_pcie_edma_detect(struct dw_pcie *pci); +void dw_pcie_edma_remove(struct dw_pcie *pci); static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) { @@ -365,34 +448,49 @@ static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci) dw_pcie_writel_dbi(pci, reg, val); } +static inline int dw_pcie_start_link(struct dw_pcie *pci) +{ + if (pci->ops && pci->ops->start_link) + return pci->ops->start_link(pci); + + return 0; +} + +static inline void dw_pcie_stop_link(struct dw_pcie *pci) +{ + if (pci->ops && pci->ops->stop_link) + pci->ops->stop_link(pci); +} + #ifdef CONFIG_PCIE_DW_HOST -irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); -void dw_pcie_setup_rc(struct pcie_port *pp); -int dw_pcie_host_init(struct pcie_port *pp); -void dw_pcie_host_deinit(struct pcie_port *pp); -int dw_pcie_allocate_domains(struct pcie_port *pp); +irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp); +int dw_pcie_setup_rc(struct dw_pcie_rp *pp); +int dw_pcie_host_init(struct dw_pcie_rp *pp); +void dw_pcie_host_deinit(struct dw_pcie_rp *pp); +int dw_pcie_allocate_domains(struct dw_pcie_rp *pp); void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn, int where); #else -static inline irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) +static inline irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp) { return IRQ_NONE; } -static inline void dw_pcie_setup_rc(struct pcie_port *pp) +static inline int dw_pcie_setup_rc(struct dw_pcie_rp *pp) { + return 0; } -static inline int dw_pcie_host_init(struct pcie_port *pp) +static inline int dw_pcie_host_init(struct dw_pcie_rp *pp) { return 0; } -static inline void dw_pcie_host_deinit(struct pcie_port *pp) +static inline void dw_pcie_host_deinit(struct dw_pcie_rp *pp) { } -static inline int dw_pcie_allocate_domains(struct pcie_port *pp) +static inline int dw_pcie_allocate_domains(struct dw_pcie_rp *pp) { return 0; } diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index c9b341e55cbb7..aeded0a58a149 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -107,7 +107,7 @@ static int rockchip_pcie_start_link(struct dw_pcie *pci) return 0; } -static int rockchip_pcie_host_init(struct pcie_port *pp) +static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); @@ -203,7 +203,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rockchip_pcie *rockchip; - struct pcie_port *pp; + struct dw_pcie_rp *pp; int ret; rockchip = devm_kzalloc(dev, sizeof(*rockchip), GFP_KERNEL); diff --git a/drivers/pci/controller/dwc/pcie-fu740.c b/drivers/pci/controller/dwc/pcie-fu740.c index 78d002be4f821..9e4e6adf6d12f 100644 --- a/drivers/pci/controller/dwc/pcie-fu740.c +++ b/drivers/pci/controller/dwc/pcie-fu740.c @@ -236,7 +236,7 @@ err: return ret; } -static int fu740_pcie_host_init(struct pcie_port *pp) +static int fu740_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct fu740_pcie *afp = to_fu740_pcie(pci); diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index 86f9d16c50d75..be97bb309f345 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -74,7 +74,7 @@ static void histb_pcie_writel(struct histb_pcie *histb_pcie, u32 reg, u32 val) writel(val, histb_pcie->ctrl + reg); } -static void histb_pcie_dbi_w_mode(struct pcie_port *pp, bool enable) +static void histb_pcie_dbi_w_mode(struct dw_pcie_rp *pp, bool enable) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct histb_pcie *hipcie = to_histb_pcie(pci); @@ -88,7 +88,7 @@ static void histb_pcie_dbi_w_mode(struct pcie_port *pp, bool enable) histb_pcie_writel(hipcie, PCIE_SYS_CTRL0, val); } -static void histb_pcie_dbi_r_mode(struct pcie_port *pp, bool enable) +static void histb_pcie_dbi_r_mode(struct dw_pcie_rp *pp, bool enable) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct histb_pcie *hipcie = to_histb_pcie(pci); @@ -182,7 +182,7 @@ static int histb_pcie_start_link(struct dw_pcie *pci) return 0; } -static int histb_pcie_host_init(struct pcie_port *pp) +static int histb_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct histb_pcie *hipcie = to_histb_pcie(pci); @@ -221,7 +221,7 @@ static void histb_pcie_host_disable(struct histb_pcie *hipcie) regulator_disable(hipcie->vpcie); } -static int histb_pcie_host_enable(struct pcie_port *pp) +static int histb_pcie_host_enable(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct histb_pcie *hipcie = to_histb_pcie(pci); @@ -299,7 +299,7 @@ static int histb_pcie_probe(struct platform_device *pdev) { struct histb_pcie *hipcie; struct dw_pcie *pci; - struct pcie_port *pp; + struct dw_pcie_rp *pp; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; enum of_gpio_flags of_flags; diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index d15cf35fa7f2d..2755f2187a9a4 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -58,11 +58,7 @@ #define BUS_IATU_OFFSET SZ_256M #define RESET_INTERVAL_MS 100 -struct intel_pcie_soc { - unsigned int pcie_ver; -}; - -struct intel_pcie_port { +struct intel_pcie { struct dw_pcie pci; void __iomem *app_base; struct gpio_desc *reset_gpio; @@ -306,7 +302,11 @@ static int intel_pcie_host_setup(struct intel_pcie_port *lpp) intel_pcie_ltssm_disable(lpp); intel_pcie_link_setup(lpp); intel_pcie_init_n_fts(pci); - dw_pcie_setup_rc(&pci->pp); + + ret = dw_pcie_setup_rc(&pci->pp); + if (ret) + goto app_init_err; + dw_pcie_upconfig_setup(pci); intel_pcie_device_rst_deassert(lpp); @@ -343,7 +343,7 @@ static void __intel_pcie_remove(struct intel_pcie_port *lpp) static int intel_pcie_remove(struct platform_device *pdev) { struct intel_pcie_port *lpp = platform_get_drvdata(pdev); - struct pcie_port *pp = &lpp->pci.pp; + struct dw_pcie_rp *pp = &pcie->pci.pp; dw_pcie_host_deinit(pp); __intel_pcie_remove(lpp); @@ -373,7 +373,7 @@ static int __maybe_unused intel_pcie_resume_noirq(struct device *dev) return intel_pcie_host_setup(lpp); } -static int intel_pcie_rc_init(struct pcie_port *pp) +static int intel_pcie_rc_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct intel_pcie_port *lpp = dev_get_drvdata(pci->dev); @@ -394,16 +394,11 @@ static const struct dw_pcie_host_ops intel_pcie_dw_ops = { .host_init = intel_pcie_rc_init, }; -static const struct intel_pcie_soc pcie_data = { - .pcie_ver = 0x520A, -}; - static int intel_pcie_probe(struct platform_device *pdev) { - const struct intel_pcie_soc *data; struct device *dev = &pdev->dev; struct intel_pcie_port *lpp; - struct pcie_port *pp; + struct dw_pcie_rp *pp; struct dw_pcie *pci; int ret; @@ -424,12 +419,7 @@ static int intel_pcie_probe(struct platform_device *pdev) if (ret) return ret; - data = device_get_match_data(dev); - if (!data) - return -ENODEV; - pci->ops = &intel_pcie_ops; - pci->version = data->pcie_ver; pp->ops = &intel_pcie_dw_ops; ret = dw_pcie_host_init(pp); @@ -447,7 +437,7 @@ static const struct dev_pm_ops intel_pcie_pm_ops = { }; static const struct of_device_id of_intel_pcie_match[] = { - { .compatible = "intel,lgm-pcie", .data = &pcie_data }, + { .compatible = "intel,lgm-pcie" }, {} }; diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 1ac29a6eef220..58f3caf75cff1 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -231,7 +231,7 @@ static void keembay_pcie_msi_irq_handler(struct irq_desc *desc) struct keembay_pcie *pcie = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); u32 val, mask, status; - struct pcie_port *pp; + struct dw_pcie_rp *pp; /* * Keem Bay PCIe Controller provides an additional IP logic on top of @@ -332,7 +332,7 @@ static int keembay_pcie_add_pcie_port(struct keembay_pcie *pcie, struct platform_device *pdev) { struct dw_pcie *pci = &pcie->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; struct device *dev = &pdev->dev; u32 val; int ret; diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index 026fd1e42a555..908a64ba32c46 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -401,7 +401,7 @@ static int kirin_pcie_start_link(struct dw_pcie *pci) return 0; } -static int kirin_pcie_host_init(struct pcie_port *pp) +static int kirin_pcie_host_init(struct dw_pcie_rp *pp) { pp->bridge->ops = &kirin_pci_ops; diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 4c599699e3c88..a42b02ed72592 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1344,7 +1344,7 @@ static int qcom_pcie_config_sid_sm8250(struct qcom_pcie *pcie) return 0; } -static int qcom_pcie_host_init(struct pcie_port *pp) +static int qcom_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct qcom_pcie *pcie = to_qcom_pcie(pci); @@ -1464,7 +1464,7 @@ static const struct dw_pcie_ops dw_pcie_ops = { static int qcom_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct pcie_port *pp; + struct dw_pcie_rp *pp; struct dw_pcie *pci; struct qcom_pcie *pcie; int ret; diff --git a/drivers/pci/controller/dwc/pcie-spear13xx.c b/drivers/pci/controller/dwc/pcie-spear13xx.c index 1a9e353bef554..fce0e280c3f44 100644 --- a/drivers/pci/controller/dwc/pcie-spear13xx.c +++ b/drivers/pci/controller/dwc/pcie-spear13xx.c @@ -85,7 +85,7 @@ static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg) struct spear13xx_pcie *spear13xx_pcie = arg; struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; struct dw_pcie *pci = spear13xx_pcie->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; unsigned int status; status = readl(&app_reg->int_sts); @@ -121,7 +121,7 @@ static int spear13xx_pcie_link_up(struct dw_pcie *pci) return 0; } -static int spear13xx_pcie_host_init(struct pcie_port *pp) +static int spear13xx_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci); @@ -155,7 +155,7 @@ static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, struct platform_device *pdev) { struct dw_pcie *pci = spear13xx_pcie->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; struct device *dev = &pdev->dev; int ret; diff --git a/drivers/pci/controller/dwc/pcie-tegra194-acpi.c b/drivers/pci/controller/dwc/pcie-tegra194-acpi.c index c2de6ed4d86f3..55f61914a9860 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194-acpi.c +++ b/drivers/pci/controller/dwc/pcie-tegra194-acpi.c @@ -39,7 +39,8 @@ static int tegra194_acpi_init(struct pci_config_window *cfg) static void atu_reg_write(struct tegra194_pcie_ecam *pcie_ecam, int index, u32 val, u32 reg) { - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); + u32 offset = PCIE_ATU_UNROLL_BASE(PCIE_ATU_REGION_DIR_OB, index) + + PCIE_ATU_VIEWPORT_BASE; writel(val, pcie_ecam->iatu_base + offset + reg); } @@ -58,8 +59,8 @@ static void program_outbound_atu(struct tegra194_pcie_ecam *pcie_ecam, PCIE_ATU_LIMIT); atu_reg_write(pcie_ecam, index, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET); - atu_reg_write(pcie_ecam, index, type, PCIE_ATU_CR1); - atu_reg_write(pcie_ecam, index, PCIE_ATU_ENABLE, PCIE_ATU_CR2); + atu_reg_write(pcie_ecam, index, type, PCIE_ATU_REGION_CTRL1); + atu_reg_write(pcie_ecam, index, PCIE_ATU_ENABLE, PCIE_ATU_REGION_CTRL2); } static void __iomem *tegra194_map_bus(struct pci_bus *bus, diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index bdd84765e6460..5e7f5ec15adaa 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -313,7 +313,7 @@ struct tegra_pcie_soc { enum dw_pcie_device_mode mode; }; -static void apply_bad_link_workaround(struct pcie_port *pp) +static void apply_bad_link_workaround(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); @@ -351,7 +351,7 @@ static irqreturn_t tegra_pcie_rp_irq_handler(int irq, void *arg) { struct tegra_pcie_dw *pcie = arg; struct dw_pcie *pci = &pcie->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; u32 val, status_l0, status_l1; u16 val_w; @@ -698,7 +698,7 @@ static inline void init_host_aspm(struct tegra_pcie_dw *pcie) { return; } static inline void init_debugfs(struct tegra_pcie_dw *pcie) { return; } #endif -static void tegra_pcie_enable_system_interrupts(struct pcie_port *pp) +static void tegra_pcie_enable_system_interrupts(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); @@ -736,7 +736,7 @@ static void tegra_pcie_enable_system_interrupts(struct pcie_port *pp) val_w); } -static void tegra_pcie_enable_legacy_interrupts(struct pcie_port *pp) +static void tegra_pcie_enable_legacy_interrupts(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); @@ -757,7 +757,7 @@ static void tegra_pcie_enable_legacy_interrupts(struct pcie_port *pp) appl_writel(pcie, val, APPL_INTR_EN_L1_8_0); } -static void tegra_pcie_enable_msi_interrupts(struct pcie_port *pp) +static void tegra_pcie_enable_msi_interrupts(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); @@ -770,7 +770,7 @@ static void tegra_pcie_enable_msi_interrupts(struct pcie_port *pp) appl_writel(pcie, val, APPL_INTR_EN_L0_0); } -static void tegra_pcie_enable_interrupts(struct pcie_port *pp) +static void tegra_pcie_enable_interrupts(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); @@ -851,7 +851,7 @@ static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie) dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); } -static int tegra_pcie_dw_host_init(struct pcie_port *pp) +static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); @@ -916,7 +916,7 @@ static int tegra_pcie_dw_start_link(struct dw_pcie *pci) { u32 val, offset, speed, tmp; struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; bool retry = true; if (pcie->mode == DW_PCIE_EP_TYPE) { @@ -1212,7 +1212,7 @@ static int tegra_pcie_bpmp_set_pll_state(struct tegra_pcie_dw *pcie, static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie) { - struct pcie_port *pp = &pcie->pci.pp; + struct dw_pcie_rp *pp = &pcie->pci.pp; struct pci_bus *child, *root_bus = NULL; struct pci_dev *pdev; @@ -1443,7 +1443,7 @@ static void tegra_pcie_unconfig_controller(struct tegra_pcie_dw *pcie) static int tegra_pcie_init_controller(struct tegra_pcie_dw *pcie) { struct dw_pcie *pci = &pcie->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; int ret; ret = tegra_pcie_config_controller(pcie, false); @@ -1962,7 +1962,7 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *atu_dma_res; struct tegra_pcie_dw *pcie; - struct pcie_port *pp; + struct dw_pcie_rp *pp; struct dw_pcie *pci; struct phy **phys; char *name; @@ -1980,7 +1980,6 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev) pci->ops = &tegra_dw_pcie_ops; pci->n_fts[0] = N_FTS_VAL; pci->n_fts[1] = FTS_VAL; - pci->version = 0x490A; pp = &pci->pp; pp->num_vectors = MAX_MSI_IRQS; diff --git a/drivers/pci/controller/dwc/pcie-uniphier.c b/drivers/pci/controller/dwc/pcie-uniphier.c index d05be942956e2..b59cf5b91531b 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier.c +++ b/drivers/pci/controller/dwc/pcie-uniphier.c @@ -170,7 +170,7 @@ static void uniphier_pcie_irq_enable(struct uniphier_pcie_priv *priv) static void uniphier_pcie_irq_mask(struct irq_data *d) { - struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); unsigned long flags; @@ -187,7 +187,7 @@ static void uniphier_pcie_irq_mask(struct irq_data *d) static void uniphier_pcie_irq_unmask(struct irq_data *d) { - struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); unsigned long flags; @@ -224,7 +224,7 @@ static const struct irq_domain_ops uniphier_intx_domain_ops = { static void uniphier_pcie_irq_handler(struct irq_desc *desc) { - struct pcie_port *pp = irq_desc_get_handler_data(desc); + struct dw_pcie_rp *pp = irq_desc_get_handler_data(desc); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); struct irq_chip *chip = irq_desc_get_chip(desc); @@ -257,7 +257,7 @@ static void uniphier_pcie_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } -static int uniphier_pcie_config_legacy_irq(struct pcie_port *pp) +static int uniphier_pcie_config_legacy_irq(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); @@ -294,7 +294,7 @@ out_put_node: return ret; } -static int uniphier_pcie_host_init(struct pcie_port *pp) +static int uniphier_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); diff --git a/drivers/pci/controller/dwc/pcie-visconti.c b/drivers/pci/controller/dwc/pcie-visconti.c index a88eab6829bb2..32e6eabd8cec7 100644 --- a/drivers/pci/controller/dwc/pcie-visconti.c +++ b/drivers/pci/controller/dwc/pcie-visconti.c @@ -178,7 +178,7 @@ static void visconti_pcie_stop_link(struct dw_pcie *pci) */ static u64 visconti_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 cpu_addr) { - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; return cpu_addr & ~pp->io_base; } @@ -190,7 +190,7 @@ static const struct dw_pcie_ops dw_pcie_ops = { .stop_link = visconti_pcie_stop_link, }; -static int visconti_pcie_host_init(struct pcie_port *pp) +static int visconti_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct visconti_pcie *pcie = dev_get_drvdata(pci->dev); @@ -278,7 +278,7 @@ static int visconti_add_pcie_port(struct visconti_pcie *pcie, struct platform_device *pdev) { struct dw_pcie *pci = &pcie->pci; - struct pcie_port *pp = &pci->pp; + struct dw_pcie_rp *pp = &pci->pp; struct device *dev = &pdev->dev; pp->irq = platform_get_irq_byname(pdev, "intr"); diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index a5ed779b0a512..51b1c7ad8e4f5 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -52,9 +52,11 @@ struct pci_epf_test { enum pci_barno test_reg_bar; size_t msix_table_offset; struct delayed_work cmd_handler; - struct dma_chan *dma_chan; + struct dma_chan *dma_chan_tx; + struct dma_chan *dma_chan_rx; struct completion transfer_complete; bool dma_supported; + bool dma_private; const struct pci_epc_features *epc_features; }; @@ -96,6 +98,8 @@ static void pci_epf_test_dma_callback(void *param) * @dma_src: The source address of the data transfer. It can be a physical * address given by pci_epc_mem_alloc_addr or DMA mapping APIs. * @len: The size of the data transfer + * @dma_remote: remote RC physical address + * @dir: DMA transfer direction * * Function that uses dmaengine API to transfer data between PCIe EP and remote * PCIe RC. The source and destination address can be a physical address given @@ -105,12 +109,16 @@ static void pci_epf_test_dma_callback(void *param) */ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, dma_addr_t dma_dst, dma_addr_t dma_src, - size_t len) + size_t len, dma_addr_t dma_remote, + enum dma_transfer_direction dir) { + struct dma_chan *chan = (dir == DMA_DEV_TO_MEM) ? + epf_test->dma_chan_tx : epf_test->dma_chan_rx; + dma_addr_t dma_local = (dir == DMA_MEM_TO_DEV) ? dma_src : dma_dst; enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; - struct dma_chan *chan = epf_test->dma_chan; struct pci_epf *epf = epf_test->epf; struct dma_async_tx_descriptor *tx; + struct dma_slave_config sconf = {}; struct device *dev = &epf->dev; dma_cookie_t cookie; int ret; @@ -120,7 +128,22 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, return -EINVAL; } - tx = dmaengine_prep_dma_memcpy(chan, dma_dst, dma_src, len, flags); + if (epf_test->dma_private) { + sconf.direction = dir; + if (dir == DMA_MEM_TO_DEV) + sconf.dst_addr = dma_remote; + else + sconf.src_addr = dma_remote; + + if (dmaengine_slave_config(chan, &sconf)) { + dev_err(dev, "DMA slave config fail\n"); + return -EIO; + } + tx = dmaengine_prep_slave_single(chan, dma_local, len, dir, flags); + } else { + tx = dmaengine_prep_dma_memcpy(chan, dma_dst, dma_src, len, flags); + } + if (!tx) { dev_err(dev, "Failed to prepare DMA memcpy\n"); return -EIO; @@ -148,6 +171,23 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, return 0; } +struct epf_dma_filter { + struct device *dev; + u32 dma_mask; +}; + +static bool epf_dma_filter_fn(struct dma_chan *chan, void *node) +{ + struct epf_dma_filter *filter = node; + struct dma_slave_caps caps; + + memset(&caps, 0, sizeof(caps)); + dma_get_slave_caps(chan, &caps); + + return chan->device->dev == filter->dev + && (filter->dma_mask & caps.directions); +} + /** * pci_epf_test_init_dma_chan() - Function to initialize EPF test DMA channel * @epf_test: the EPF test device that performs data transfer operation @@ -158,10 +198,44 @@ static int pci_epf_test_init_dma_chan(struct pci_epf_test *epf_test) { struct pci_epf *epf = epf_test->epf; struct device *dev = &epf->dev; + struct epf_dma_filter filter; struct dma_chan *dma_chan; dma_cap_mask_t mask; int ret; + filter.dev = epf->epc->dev.parent; + filter.dma_mask = BIT(DMA_DEV_TO_MEM); + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_chan = dma_request_channel(mask, epf_dma_filter_fn, &filter); + if (IS_ERR_OR_NULL(dma_chan)) { + dev_info(dev, "Failed to get private DMA rx channel. Falling back to generic one\n"); + goto fail_back_tx; + } + + epf_test->dma_chan_rx = dma_chan; + + filter.dma_mask = BIT(DMA_MEM_TO_DEV); + dma_chan = dma_request_channel(mask, epf_dma_filter_fn, &filter); + + if (IS_ERR(dma_chan)) { + dev_info(dev, "Failed to get private DMA tx channel. Falling back to generic one\n"); + goto fail_back_rx; + } + + epf_test->dma_chan_tx = dma_chan; + epf_test->dma_private = true; + + init_completion(&epf_test->transfer_complete); + + return 0; + +fail_back_rx: + dma_release_channel(epf_test->dma_chan_rx); + epf_test->dma_chan_tx = NULL; + +fail_back_tx: dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); @@ -174,7 +248,7 @@ static int pci_epf_test_init_dma_chan(struct pci_epf_test *epf_test) } init_completion(&epf_test->transfer_complete); - epf_test->dma_chan = dma_chan; + epf_test->dma_chan_tx = epf_test->dma_chan_rx = dma_chan; return 0; } @@ -190,8 +264,17 @@ static void pci_epf_test_clean_dma_chan(struct pci_epf_test *epf_test) if (!epf_test->dma_supported) return; - dma_release_channel(epf_test->dma_chan); - epf_test->dma_chan = NULL; + dma_release_channel(epf_test->dma_chan_tx); + if (epf_test->dma_chan_tx == epf_test->dma_chan_rx) { + epf_test->dma_chan_tx = NULL; + epf_test->dma_chan_rx = NULL; + return; + } + + dma_release_channel(epf_test->dma_chan_rx); + epf_test->dma_chan_rx = NULL; + + return; } static void pci_epf_test_print_rate(const char *ops, u64 size, @@ -280,8 +363,14 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test) goto err_map_addr; } + if (epf_test->dma_private) { + dev_err(dev, "Cannot transfer data using DMA\n"); + ret = -EINVAL; + goto err_map_addr; + } + ret = pci_epf_test_data_transfer(epf_test, dst_phys_addr, - src_phys_addr, reg->size); + src_phys_addr, reg->size, 0, DMA_MEM_TO_MEM); if (ret) dev_err(dev, "Data transfer failed\n"); } else { @@ -373,7 +462,8 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test) ktime_get_ts64(&start); ret = pci_epf_test_data_transfer(epf_test, dst_phys_addr, - phys_addr, reg->size); + phys_addr, reg->size, + reg->src_addr, DMA_DEV_TO_MEM); if (ret) dev_err(dev, "Data transfer failed\n"); ktime_get_ts64(&end); @@ -463,8 +553,11 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test) } ktime_get_ts64(&start); + ret = pci_epf_test_data_transfer(epf_test, phys_addr, - src_phys_addr, reg->size); + src_phys_addr, reg->size, + reg->dst_addr, + DMA_MEM_TO_DEV); if (ret) dev_err(dev, "Data transfer failed\n"); ktime_get_ts64(&end); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 2bfff2328cf87..e293fea67dfe5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4143,6 +4143,7 @@ unsigned long __weak pci_address_to_pio(phys_addr_t address) * architectures that have memory mapped IO functions defined (and the * PCI_IOBASE value defined) should call this function. */ +#ifndef pci_remap_iospace int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr) { #if defined(PCI_IOBASE) && defined(CONFIG_MMU) @@ -4166,6 +4167,7 @@ int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr) #endif } EXPORT_SYMBOL(pci_remap_iospace); +#endif /** * pci_unmap_iospace - Unmap the memory mapped I/O space diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 2ce636937c6ea..f3d61d4216b68 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -952,12 +952,15 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, resource_size_t min_align = 0; int order; - for (order = 0; order <= max_order; order++) { + for (order = 0; order < max_order; order++) { resource_size_t align1 = 1; + if (!aligns[order]) + continue; + align1 <<= (order + 20); - if (!align) + if (!min_align) min_align = align1; else if (ALIGN(align + min_align, min_align) < align1) min_align = align1 >> 1; @@ -993,7 +996,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, struct list_head *realloc_head) { struct pci_dev *dev; - resource_size_t min_align, align, size, size0, size1; + resource_size_t min_align, align, size, size0, size1, max_align; resource_size_t aligns[18]; /* Alignments from 1MB to 128GB */ int order, max_order; struct resource *b_res = find_bus_resource_of_type(bus, @@ -1073,6 +1076,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, min_align = calculate_mem_align(aligns, max_order); min_align = max(min_align, window_alignment(bus, b_res->flags)); + max_align = 1 << (max_order + 20); + if (min_align >= max_align/2) + max_align = min_align; size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), min_align); add_align = max(min_align, add_align); size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 : @@ -1085,8 +1091,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, b_res->flags = 0; return 0; } - b_res->start = min_align; - b_res->end = size0 + min_align - 1; + b_res->start = max_align; + b_res->end = size0 + max_align - 1; b_res->flags |= IORESOURCE_STARTALIGN; if (bus->self && size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align); diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index a305074c482e8..ddcd1dd130af3 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -919,7 +919,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) if (dws->dma_ops && dws->dma_ops->dma_init) { ret = dws->dma_ops->dma_init(dev, dws); - if (ret) { + if (ret == -EPROBE_DEFER) { + goto err_free_irq; + } else if (ret) { dev_warn(dev, "DMA init failed\n"); } else { master->can_dma = dws->dma_ops->can_dma; @@ -929,7 +931,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) ret = spi_register_controller(master); if (ret) { - dev_err(&master->dev, "problem registering spi master\n"); + dev_err_probe(dev, ret, "problem registering spi master\n"); goto err_dma_exit; } @@ -940,6 +942,7 @@ err_dma_exit: if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); spi_enable_chip(dws, 0); +err_free_irq: free_irq(dws->irq, master); err_free_master: spi_controller_put(master); diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index a09831c62192a..3a8ccdb597153 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -138,15 +138,20 @@ err_exit: static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) { - dws->rxchan = dma_request_slave_channel(dev, "rx"); - if (!dws->rxchan) - return -ENODEV; + int ret; - dws->txchan = dma_request_slave_channel(dev, "tx"); - if (!dws->txchan) { - dma_release_channel(dws->rxchan); + dws->rxchan = dma_request_chan(dev, "rx"); + if (IS_ERR(dws->rxchan)) { + ret = PTR_ERR(dws->rxchan); dws->rxchan = NULL; - return -ENODEV; + goto err_exit; + } + + dws->txchan = dma_request_chan(dev, "tx"); + if (IS_ERR(dws->txchan)) { + ret = PTR_ERR(dws->txchan); + dws->txchan = NULL; + goto free_rxchan; } dws->master->dma_rx = dws->rxchan; @@ -159,6 +164,12 @@ static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) dw_spi_dma_sg_burst_init(dws); return 0; + +free_rxchan: + dma_release_channel(dws->rxchan); + dws->rxchan = NULL; +err_exit: + return ret; } static void dw_spi_dma_exit(struct dw_spi *dws) diff --git a/include/asm-generic/mmu_context.h b/include/asm-generic/mmu_context.h index 91727065bacbf..c72393bd8d3aa 100644 --- a/include/asm-generic/mmu_context.h +++ b/include/asm-generic/mmu_context.h @@ -73,4 +73,14 @@ static inline void deactivate_mm(struct task_struct *tsk, } #endif +/** + * tlb_prefetch - called if by design TLB-prefetching is required + * @addr: Virtual address + */ +#ifndef tlb_prefetch +static inline void tlb_prefetch(unsigned long addr) +{ +} +#endif + #endif /* __ASM_GENERIC_MMU_CONTEXT_H */ diff --git a/include/dt-bindings/ata/ahci.h b/include/dt-bindings/ata/ahci.h new file mode 100644 index 0000000000000..77997b35612ca --- /dev/null +++ b/include/dt-bindings/ata/ahci.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause */ +/* + * This header provides constants for most AHCI bindings. + */ + +#ifndef _DT_BINDINGS_ATA_AHCI_H +#define _DT_BINDINGS_ATA_AHCI_H + +/* Host Bus Adapter generic platform capabilities */ +#define HBA_SSS (1 << 27) +#define HBA_SMPS (1 << 28) + +/* Host Bus Adapter port-specific platform capabilities */ +#define HBA_PORT_HPCP (1 << 18) +#define HBA_PORT_MPSP (1 << 19) +#define HBA_PORT_CPD (1 << 20) +#define HBA_PORT_ESP (1 << 21) +#define HBA_PORT_FBSCP (1 << 22) + +#endif diff --git a/include/dt-bindings/reset/bt1-ccu.h b/include/dt-bindings/reset/bt1-ccu.h index 3578e83026bc8..c691efaa678f7 100644 --- a/include/dt-bindings/reset/bt1-ccu.h +++ b/include/dt-bindings/reset/bt1-ccu.h @@ -21,5 +21,14 @@ #define CCU_SYS_SATA_REF_RST 0 #define CCU_SYS_APB_RST 1 +#define CCU_SYS_DDR_FULL_RST 2 +#define CCU_SYS_DDR_INIT_RST 3 +#define CCU_SYS_PCIE_PCS_PHY_RST 4 +#define CCU_SYS_PCIE_PIPE0_RST 5 +#define CCU_SYS_PCIE_CORE_RST 6 +#define CCU_SYS_PCIE_PWR_RST 7 +#define CCU_SYS_PCIE_STICKY_RST 8 +#define CCU_SYS_PCIE_NSTICKY_RST 9 +#define CCU_SYS_PCIE_HOT_RST 10 #endif /* __DT_BINDINGS_RESET_BT1_CCU_H */ diff --git a/include/dt-bindings/soc/bt1-boot-mode.h b/include/dt-bindings/soc/bt1-boot-mode.h new file mode 100644 index 0000000000000..0e425805bc1d4 --- /dev/null +++ b/include/dt-bindings/soc/bt1-boot-mode.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 Boot Modes + */ +#ifndef __DT_BINDINGS_SOC_BT1_BOOT_MODE_H +#define __DT_BINDINGS_SOC_BT1_BOOT_MODE_H + +#define RCR_BOOT_NORMAL 0x1 +#define RCR_BOOT_LOADER 0x2 +#define RCR_BOOT_RECOVERY 0x3 + +#endif /* __DT_BINDINGS_SOC_BT1_BOOT_MODE_H */ diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index 49e5383d42222..17fa26215292e 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -13,6 +13,7 @@ #include +struct clk; struct device; struct ata_port_info; struct ahci_host_priv; @@ -21,8 +22,12 @@ struct scsi_host_template; int ahci_platform_enable_phys(struct ahci_host_priv *hpriv); void ahci_platform_disable_phys(struct ahci_host_priv *hpriv); +struct clk *ahci_platform_find_clk(struct ahci_host_priv *hpriv, + const char *con_id); int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); +int ahci_platform_deassert_rsts(struct ahci_host_priv *hpriv); +int ahci_platform_assert_rsts(struct ahci_host_priv *hpriv); int ahci_platform_enable_regulators(struct ahci_host_priv *hpriv); void ahci_platform_disable_regulators(struct ahci_host_priv *hpriv); int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); @@ -41,6 +46,7 @@ int ahci_platform_resume_host(struct device *dev); int ahci_platform_suspend(struct device *dev); int ahci_platform_resume(struct device *dev); -#define AHCI_PLATFORM_GET_RESETS 0x01 +#define AHCI_PLATFORM_GET_RESETS BIT(0) +#define AHCI_PLATFORM_RST_TRIGGER BIT(1) #endif /* _AHCI_PLATFORM_H */ diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h index cab6e18773dad..979a83a08378e 100644 --- a/include/linux/dma/edma.h +++ b/include/linux/dma/edma.h @@ -12,19 +12,88 @@ #include #include +#define EDMA_MAX_WR_CH 8 +#define EDMA_MAX_RD_CH 8 + struct dw_edma; +struct dw_edma_region { + u64 paddr; + void __iomem *vaddr; + size_t sz; +}; + +/** + * struct dw_edma_core_ops - platform-specific eDMA methods + * @irq_vector: Get IRQ number of the passed eDMA channel. Note the + * method accepts the channel id in the end-to-end + * numbering with the eDMA write channels being placed + * first in the row. + * @pci_address: Get PCIe bus address corresponding to the passed CPU + * address. Note there is no need in specifying this + * function if the address translation is performed by + * the DW PCIe RP/EP controller with the DW eDMA device in + * subject and DMA_BYPASS isn't set for all the outbound + * iATU windows. That will be done by the controller + * automatically. + */ +struct dw_edma_core_ops { + int (*irq_vector)(struct device *dev, unsigned int nr); + u64 (*pci_address)(struct device *dev, phys_addr_t cpu_addr); +}; + +enum dw_edma_map_format { + EDMA_MF_EDMA_LEGACY = 0x0, + EDMA_MF_EDMA_UNROLL = 0x1, + EDMA_MF_HDMA_COMPAT = 0x5 +}; + +/** + * enum dw_edma_chip_flags - Flags specific to an eDMA chip + * @DW_EDMA_CHIP_LOCAL: eDMA is used locally by an endpoint + */ +enum dw_edma_chip_flags { + DW_EDMA_CHIP_LOCAL = BIT(0), +}; + /** * struct dw_edma_chip - representation of DesignWare eDMA controller hardware * @dev: struct device of the eDMA controller * @id: instance ID - * @irq: irq line + * @nr_irqs: total dma irq number + * @ops DMA channel to IRQ number mapping + * @flags dw_edma_chip_flags + * @reg_base DMA register base address + * @ll_wr_cnt DMA write link list count + * @ll_rd_cnt DMA read link list count + * @rg_region DMA register region + * @ll_region_wr DMA descriptor link list memory for write channel + * @ll_region_rd DMA descriptor link list memory for read channel + * @dt_region_wr DMA data memory for write channel + * @dt_region_rd DMA data memory for read channel + * @mf DMA register map format * @dw: struct dw_edma that is filed by dw_edma_probe() */ struct dw_edma_chip { struct device *dev; - int id; - int irq; + int nr_irqs; + const struct dw_edma_core_ops *ops; + u32 flags; + + void __iomem *reg_base; + + u16 ll_wr_cnt; + u16 ll_rd_cnt; + /* link list address */ + struct dw_edma_region ll_region_wr[EDMA_MAX_WR_CH]; + struct dw_edma_region ll_region_rd[EDMA_MAX_RD_CH]; + + /* data region */ + struct dw_edma_region dt_region_wr[EDMA_MAX_WR_CH]; + struct dw_edma_region dt_region_rd[EDMA_MAX_RD_CH]; + + enum dw_edma_map_format mf; + struct dw_edma *dw; }; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 9000f3ffce8b3..19072be3cd888 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -394,7 +394,7 @@ enum dma_slave_buswidth { * should be read (RX), if the source is memory this argument is * ignored. * @dst_addr: this is the physical address where DMA slave data - * should be written (TX), if the source is memory this argument + * should be written (TX), if the destination is memory this argument * is ignored. * @src_addr_width: this is the width in bytes of the source (RX) * register where DMA data shall be read. If the source diff --git a/include/linux/edac.h b/include/linux/edac.h index 4207d06996a4f..e5747addccae3 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 @@ -197,10 +199,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, @@ -226,20 +230,22 @@ 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_XDR BIT(MEM_XDR) -#define MEM_FLAG_DDR3 BIT(MEM_DDR3) -#define MEM_FLAG_RDDR3 BIT(MEM_RDDR3) -#define MEM_FLAG_LPDDR3 BIT(MEM_LPDDR3) -#define MEM_FLAG_DDR4 BIT(MEM_DDR4) -#define MEM_FLAG_RDDR4 BIT(MEM_RDDR4) -#define MEM_FLAG_LRDDR4 BIT(MEM_LRDDR4) -#define MEM_FLAG_LPDDR4 BIT(MEM_LPDDR4) -#define MEM_FLAG_DDR5 BIT(MEM_DDR5) -#define MEM_FLAG_NVDIMM BIT(MEM_NVDIMM) +#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) +#define MEM_FLAG_LPDDR3 BIT(MEM_LPDDR3) +#define MEM_FLAG_DDR4 BIT(MEM_DDR4) +#define MEM_FLAG_RDDR4 BIT(MEM_RDDR4) +#define MEM_FLAG_LRDDR4 BIT(MEM_LRDDR4) +#define MEM_FLAG_LPDDR4 BIT(MEM_LPDDR4) +#define MEM_FLAG_DDR5 BIT(MEM_DDR5) +#define MEM_FLAG_NVDIMM BIT(MEM_NVDIMM) #define MEM_FLAG_WIO2 BIT(MEM_WIO2) #define MEM_FLAG_HBM2 BIT(MEM_HBM2) diff --git a/include/linux/marvell_phy.h b/include/linux/marvell_phy.h index 0f06c2287b527..38a558d37fdd9 100644 --- a/include/linux/marvell_phy.h +++ b/include/linux/marvell_phy.h @@ -25,6 +25,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 /* Marvel 88E1111 in Finisar SFP module with modified PHY ID */ #define MARVELL_PHY_ID_88E1111_FINISAR 0x01ff0cc0 diff --git a/include/linux/pm.h b/include/linux/pm.h index 1d8209c09686c..d1c19f5b1380f 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -300,47 +300,59 @@ struct dev_pm_ops { int (*runtime_idle)(struct device *dev); }; +#define SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ + .suspend = pm_sleep_ptr(suspend_fn), \ + .resume = pm_sleep_ptr(resume_fn), \ + .freeze = pm_sleep_ptr(suspend_fn), \ + .thaw = pm_sleep_ptr(resume_fn), \ + .poweroff = pm_sleep_ptr(suspend_fn), \ + .restore = pm_sleep_ptr(resume_fn), + +#define LATE_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ + .suspend_late = pm_sleep_ptr(suspend_fn), \ + .resume_early = pm_sleep_ptr(resume_fn), \ + .freeze_late = pm_sleep_ptr(suspend_fn), \ + .thaw_early = pm_sleep_ptr(resume_fn), \ + .poweroff_late = pm_sleep_ptr(suspend_fn), \ + .restore_early = pm_sleep_ptr(resume_fn), + +#define NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ + .suspend_noirq = pm_sleep_ptr(suspend_fn), \ + .resume_noirq = pm_sleep_ptr(resume_fn), \ + .freeze_noirq = pm_sleep_ptr(suspend_fn), \ + .thaw_noirq = pm_sleep_ptr(resume_fn), \ + .poweroff_noirq = pm_sleep_ptr(suspend_fn), \ + .restore_noirq = pm_sleep_ptr(resume_fn), + +#define RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \ + .runtime_suspend = suspend_fn, \ + .runtime_resume = resume_fn, \ + .runtime_idle = idle_fn, + #ifdef CONFIG_PM_SLEEP #define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ - .suspend = suspend_fn, \ - .resume = resume_fn, \ - .freeze = suspend_fn, \ - .thaw = resume_fn, \ - .poweroff = suspend_fn, \ - .restore = resume_fn, + SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #else #define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #endif #ifdef CONFIG_PM_SLEEP #define SET_LATE_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ - .suspend_late = suspend_fn, \ - .resume_early = resume_fn, \ - .freeze_late = suspend_fn, \ - .thaw_early = resume_fn, \ - .poweroff_late = suspend_fn, \ - .restore_early = resume_fn, + LATE_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #else #define SET_LATE_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #endif #ifdef CONFIG_PM_SLEEP #define SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ - .suspend_noirq = suspend_fn, \ - .resume_noirq = resume_fn, \ - .freeze_noirq = suspend_fn, \ - .thaw_noirq = resume_fn, \ - .poweroff_noirq = suspend_fn, \ - .restore_noirq = resume_fn, + NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #else #define SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #endif #ifdef CONFIG_PM #define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \ - .runtime_suspend = suspend_fn, \ - .runtime_resume = resume_fn, \ - .runtime_idle = idle_fn, + RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) #else #define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) #endif @@ -349,9 +361,9 @@ struct dev_pm_ops { * Use this if you want to use the same suspend and resume callbacks for suspend * to RAM and hibernation. */ -#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \ -const struct dev_pm_ops __maybe_unused name = { \ - SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ +#define DEFINE_SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \ +const struct dev_pm_ops name = { \ + SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ } /* @@ -367,17 +379,27 @@ const struct dev_pm_ops __maybe_unused name = { \ * .resume_early(), to the same routines as .runtime_suspend() and * .runtime_resume(), respectively (and analogously for hibernation). */ +#define DEFINE_UNIVERSAL_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \ +static const struct dev_pm_ops name = { \ + SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ + RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \ +} + +/* Deprecated. Use DEFINE_SIMPLE_DEV_PM_OPS() instead. */ +#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \ +const struct dev_pm_ops __maybe_unused name = { \ + SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ +} + +/* Deprecated. Use DEFINE_UNIVERSAL_DEV_PM_OPS() instead. */ #define UNIVERSAL_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \ const struct dev_pm_ops __maybe_unused name = { \ SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \ } -#ifdef CONFIG_PM -#define pm_ptr(_ptr) (_ptr) -#else -#define pm_ptr(_ptr) NULL -#endif +#define pm_ptr(_ptr) PTR_IF(IS_ENABLED(CONFIG_PM), (_ptr)) +#define pm_sleep_ptr(_ptr) PTR_IF(IS_ENABLED(CONFIG_PM_SLEEP), (_ptr)) /* * PM_EVENT_ messages diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 48d015ed21752..277098dd80c72 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -231,6 +231,8 @@ struct plat_stmmacenet_data { void (*exit)(struct platform_device *pdev, void *priv); struct mac_device_info *(*setup)(void *priv); int (*clks_config)(void *priv, bool enabled); + int (*bus_reset)(void *priv); + int (*swr_reset)(void *priv); int (*crosststamp)(ktime_t *device, struct system_counterval_t *system, void *ctx); void (*dump_debug_regs)(void *priv); diff --git a/mm/slab.c b/mm/slab.c index 1bd283e98c58c..f37c6d32b3d1e 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3474,6 +3474,8 @@ void ___cache_free(struct kmem_cache *cachep, void *objp, if (nr_online_nodes > 1 && cache_free_alien(cachep, objp)) return; + tlb_prefetch((unsigned long)ac); + if (ac->avail < ac->limit) { STATS_INC_FREEHIT(cachep); } else {