- 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
--- /dev/null
+# 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 <hdegoede@redhat.com>
+ - Damien Le Moal <damien.lemoal@opensource.wdc.com>
+
+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
+
+...
+++ /dev/null
-* 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 : <interrupt mapping for SATA IRQ>
-- reg : <registers mapping>
-
-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 = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
- 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>;;
- };
- };
--- /dev/null
+# 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 <hdegoede@redhat.com>
+ - Jens Axboe <axboe@kernel.dk>
+
+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 <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/berlin2q.h>
+ #include <dt-bindings/ata/ahci.h>
+
+ sata@f7e90000 {
+ compatible = "marvell,berlin2q-ahci", "generic-ahci";
+ reg = <0xf7e90000 0x1000>;
+ interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&chip CLKID_SATA>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hba-cap = <HBA_SMPS>;
+
+ 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)>;
+ };
+ };
--- /dev/null
+# 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 <fancer.lancer@gmail.com>
+
+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>;
+ };
+ };
+...
+++ /dev/null
-* 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>;
- };
- };
--- /dev/null
+# 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 <f.fainelli@gmail.com>
+
+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>;
+ };
+ };
"#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.
...
--- /dev/null
+# 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 <fancer.lancer@gmail.com>
+
+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 ]
+
+...
--- /dev/null
+# 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 <fancer.lancer@gmail.com>
+
+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 <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/ata/ahci.h>
+
+ sata@122f0000 {
+ compatible = "snps,dwc-ahci";
+ reg = <0x122F0000 0x1ff>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
+
+ 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 = <HBA_PORT_FBSCP>;
+
+ snps,tx-ts-max = <512>;
+ snps,rx-ts-max = <512>;
+ };
+ };
+
+...
maintainers:
- Chanwoo Choi <cw00.choi@samsung.com>
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
- Tomasz Figa <tomasz.figa@gmail.com>
maintainers:
- Chanwoo Choi <cw00.choi@samsung.com>
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
- Tomasz Figa <tomasz.figa@gmail.com>
maintainers:
- Chanwoo Choi <cw00.choi@samsung.com>
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
- Tomasz Figa <tomasz.figa@gmail.com>
maintainers:
- Chanwoo Choi <cw00.choi@samsung.com>
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
- Tomasz Figa <tomasz.figa@gmail.com>
maintainers:
- Chanwoo Choi <cw00.choi@samsung.com>
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
- Tomasz Figa <tomasz.figa@gmail.com>
maintainers:
- Chanwoo Choi <cw00.choi@samsung.com>
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
The Samsung Exynos542x SoC has a NoC (Network on Chip) Probe for NoC bus.
maintainers:
- Chanwoo Choi <cw00.choi@samsung.com>
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
The Samsung Exynos SoC has PPMU (Platform Performance Monitoring Unit) for
properties:
data-lines:
+ $ref: /schemas/types.yaml#/definitions/uint32
enum: [ 16, 18, 24 ]
port@1:
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:
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:
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:
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:
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:
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:
init-delay:
description: delay after initialization sequence [ms]
+ $ref: /schemas/types.yaml#/definitions/uint32
panel-width-mm:
description: physical panel width [mm]
properties:
compatible:
- const: snps,dma-spear1340
+ oneOf:
+ - const: snps,dma-spear1340
+ - const: baikal,bt1-dmac
"#dma-cells":
minimum: 3
const: 2
registers-number:
+ $ref: /schemas/types.yaml#/definitions/uint32
description: Number of daisy-chained shift registers
enable-gpios:
- gpio-controller
- interrupt-controller
- "#interrupt-cells"
- - interrupt-parent
additionalProperties: false
type: boolean
function-row-physmap:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 15
description: |
title: Samsung Exynos SoC Interrupt Combiner Controller
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
Samsung's Exynos4 architecture includes a interrupt combiner controller which
- ti,mbox-num-fifos
allOf:
- - if:
- properties:
- compatible:
- enum:
- - ti,am654-mailbox
- then:
- required:
- - interrupt-parent
-
- if:
properties:
compatible:
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.
title: DDR PHY Front End (DPFE) for Broadcom STB
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Markus Mayer <mmayer@broadcom.com>
properties:
maintainers:
- Jan Luebbe <jlu@pengutronix.de>
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
properties:
compatible:
title: Qualcomm Atheros AR7xxx/AR9xxx DDR controller
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
The DDR controller of the AR7xxx and AR9xxx families provides an interface to
title: H8/300 bus controller
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Yoshinori Sato <ysato@users.sourceforge.jp>
properties:
Controller device
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Lukasz Luba <lukasz.luba@arm.com>
description: |
--- /dev/null
+# 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 <krzk@kernel.org>
+ - Manish Narani <manish.narani@xilinx.com>
+ - Michal Simek <michal.simek@xilinx.com>
+
+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";
+ };
+...
+++ /dev/null
-# 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 <krzysztof.kozlowski@canonical.com>
- - Manish Narani <manish.narani@xilinx.com>
- - Michal Simek <michal.simek@xilinx.com>
-
-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>;
- };
- };
maintainers:
- Bartosz Golaszewski <bgolaszewski@baylibre.com>
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
Documentation:
--- /dev/null
+# 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 <krzk@kernel.org>
+ - Manish Narani <manish.narani@xilinx.com>
+ - Michal Simek <michal.simek@xilinx.com>
+
+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>;
+ };
+...
--- /dev/null
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/mfd/baikal,bt1-boot-con.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Baikal-T1 SoC Boot Controller
+
+maintainers:
+ - Serge Semin <fancer.lancer@gmail.com>
+
+description:
+ Baikal-T1 SoC is equipped with a Boot Controller which is responsible for
+ the SoC proper boot up procedure. Depending on the external pin state the
+ system can boot up either from the internal ROM or from the externally attached
+ SPI flash (at least of 16MB) or from the internal SRAM (the 64KB of executional
+ code is pre-loaded from the external SPI flash).
+
+allOf:
+ - $ref: /schemas/mfd/syscon.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: baikal,bt1-boot-con
+ - const: syscon
+ - const: simple-mfd
+
+ reg:
+ items:
+ - description:
+ Baikal-T1 Boot Controller CSR space. It doesn't include many
+ settings':' corrent boot mode, SPI controller access mux, SRAM
+ access mux and device ID.
+ - description: Mirrored first 4MB of the boot SPI flash memory
+
+ reg-names:
+ items:
+ - const: boot
+ - const: mirror
+
+ 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";
+ };
+ };
+...
--- /dev/null
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/mfd/baikal,bt1-sys-con.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Baikal-T1 SoC System Controller
+
+maintainers:
+ - Serge Semin <fancer.lancer@gmail.com>
+
+description:
+ Baikal-T1 SoC is equipped with a System Controller which is responsible for
+ the SoC components setting up and consists of the next sub-blocks':'
+ PLL/AXI-bus/System devices Clocks Control Units, P5600 CM2 L2-RAM controller,
+ CPU cores reboot flag, persistent across reboots register, indirectly
+ accessible DW APB I2C controller, Boot Controller with a pre-installed memory
+ mapped firmware and a resource limited DW APB SSI, which is also can be used
+ to transparently access an external SPI flash by means of a dedicated IO
+ memory region.
+
+allOf:
+ - $ref: /schemas/mfd/syscon.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: baikal,bt1-sys-con
+ - const: syscon
+ - const: simple-mfd
+
+ reg:
+ description:
+ Baikal-T1 System Controller CSR space. It includes CCU (Clock Control
+ Unit), L2 settings, Reboot flag and Reboot tolerant register, System I2C
+ controller CSRs.
+ maxItems: 1
+
+ reg-names:
+ const: sys
+
+ 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>;
+ };
+ };
+...
- '#gpio-cells'
- interrupt-controller
- '#interrupt-cells'
- - interrupt-parent
- interrupts
- AVDD-supply
- DBVDD1-supply
minItems: 2
maxItems: 4 # Should be enough
- reg:
- maxItems: 1
+ reg: true
reg-io-width:
description: |
- cypress,cy7c1019dv33-10zsxi
- arm,vexpress-psram
- const: mtd-ram
+ - items:
+ - enum:
+ - baikal,bt1-int-rom
+ - const: mtd-rom
- enum:
- cfi-flash
- jedec-flash
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
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:
maxItems: 1
bus_freq:
+ $ref: /schemas/types.yaml#/definitions/uint32
maximum: 2500000
description: MDIO Bus frequency
--- /dev/null
+# 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 <fancer.lancer@gmail.com>
+
+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>;
+ };
+...
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:
- fsl,imx6qp-pcie
- fsl,imx7d-pcie
- fsl,imx8mq-pcie
+ - fsl,imx8mm-pcie
+ - fsl,imx8mp-pcie
reg:
items:
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
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
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
--- /dev/null
+# 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 <shawn.lin@rock-chips.com>
+ - Simon Xue <xxm@rock-chips.com>
+ - Heiko Stuebner <heiko@sntech.de>
+
+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>;
+ };
+ };
+...
- const: config
- const: mgmt
+ dma-coherent: true
+
num-lanes:
const: 8
- num-lanes
- interrupts
- interrupt-names
- - interrupt-parent
- interrupt-map-mask
- interrupt-map
- - clock-names
- clocks
- resets
- pwren-gpios
<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>;
--- /dev/null
+# 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 <jingoohan1@gmail.com>
+ - Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+
+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
+
+...
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>;
};
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
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>;
};
properties:
compatible:
- const: socionext,uniphier-pro5-pcie-ep
+ enum:
+ - socionext,uniphier-pro5-pcie-ep
+ - socionext,uniphier-nx1-pcie-ep
reg:
minItems: 4
- 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
- resets
- reset-names
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
--- /dev/null
+# 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 <hayashi.kunihiko@socionext.com>
+
+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>;
+ };
+ };
- const: mpu
interrupts:
- maxItems: 1
+ maxItems: 2
clocks:
items:
#interrupt-cells = <1>;
ranges = <0x81000000 0 0x40000000 0 0x40000000 0 0x00010000>,
<0x82000000 0 0x50000000 0 0x50000000 0 0x20000000>;
- interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "intr";
+ interrupts = <GIC_SPI 211 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>;
+ 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
+++ /dev/null
-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>;
- };
- };
- reg-names
- "#interrupt-cells"
- interrupts
- - interrupt-parent
- interrupt-map
- interrupt-map-mask
- bus-range
input-schmitt-disable: true
input-polarity-invert:
+ type: boolean
description:
Enable or disable pin input polarity inversion.
output-low: true
output-polarity-invert:
+ type: boolean
description:
Enable or disable pin output polarity inversion.
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.
cm-poll-mode:
description: polling mode
+ $ref: /schemas/types.yaml#/definitions/uint32
default: 0
enum:
- 0 # disabled
title: Samsung Exynos SoC True Random Number Generator
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Łukasz Stelmach <l.stelmach@samsung.com>
properties:
maxItems: 1
clock-error-detect:
+ type: boolean
description: If set enable the clock detection management
required:
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.
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
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
reflect the purpose of the node. Unit address (@<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(@.*)?"
- arm,juno-sram-ns
- atmel,sama5d2-securam
- rockchip,rk3288-pmu-sram
+ - baikal,bt1-sram
reg:
maxItems: 1
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
- samsung,exynos4210-sysram
- samsung,exynos4210-sysram-ns
- socionext,milbeaut-smp-sram
+ - baikal,bt1-boot-sram
reg:
description:
- compatible
- reg
-if:
- properties:
- compatible:
- contains:
- const: rockchip,rk3288-pmu-sram
-
-else:
- required:
- - "#address-cells"
- - "#size-cells"
- - ranges
-
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
F: include/linux/backlight.h
F: include/linux/pwm_backlight.h
+BAIKAL-T1 PVT HARDWARE MONITOR DRIVER
+M: Serge Semin <fancer.lancer@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
+F: drivers/hwmon/bt1-pvt.*
+
BATMAN ADVANCED
M: Marek Lindner <mareklindner@neomailbox.ch>
M: Simon Wunderlich <sw@simonwunderlich.de>
F: drivers/ata/libahci_platform.c
F: include/linux/ahci_platform.h
+LIBATA SATA AHCI SYNOPSYS DWC CONTROLLER DRIVER
+M: Serge Semin <fancer.lancer@gmail.com>
+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 <mikpelinux@gmail.com>
L: linux-ide@vger.kernel.org
F: arch/mips/
F: drivers/platform/mips/
+MIPS/BAIKAL-T1 PLATFORM
+M: Serge Semin <fancer.lancer@gmail.com>
+L: linux-mips@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/bus/baikal,bt1-*
+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 <paulburton@kernel.org>
L: linux-mips@vger.kernel.org
NTB IDT DRIVER
M: Serge Semin <fancer.lancer@gmail.com>
-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 <dave.jiang@intel.com>
F: Documentation/devicetree/bindings/pci/axis,artpec*
F: drivers/pci/controller/dwc/*artpec*
+PCIE DRIVER FOR BAIKAL-T1
+M: Serge Semin <fancer.lancer@gmail.com>
+L: linux-pci@vger.kernel.org
+S: 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 <rric@kernel.org>
L: linux-pci@vger.kernel.org
M: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
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
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/
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
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"
#
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
#
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
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
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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ * Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
+ * Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 early printk
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/serial_reg.h>
+
+#include <asm/mach-baikal-t1/memory.h>
+
+#define BT1_UART_BASE(_id) \
+ (void *)KSEG1ADDR(CONCATENATE(BT1_UART, CONCATENATE(_id, _BASE)))
+
+void prom_putchar(char c)
+{
+ void __iomem *uart_base = BT1_UART_BASE(CONFIG_BT1_EARLY_UART);
+ unsigned int timeout = 50000;
+ int status, bits;
+
+ bits = UART_LSR_TEMT | UART_LSR_THRE;
+
+ do {
+ status = __raw_readl(uart_base + (UART_LSR << 2));
+
+ if (--timeout == 0)
+ break;
+ } while ((status & bits) != bits);
+
+ if (timeout)
+ __raw_writel(c, uart_base + (UART_TX << 2));
+}
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ * Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
+ * Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 platform initialization
+ */
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/libfdt.h>
+#include <linux/limits.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_clk.h>
+#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/swiotlb.h>
+#include <linux/sys_soc.h>
+
+#include <asm/bootinfo.h>
+#include <asm/cpu-info.h>
+#include <asm/io.h>
+#include <asm/mips-cm.h>
+#include <asm/mips-cpc.h>
+#include <asm/mipsregs.h>
+#include <asm/pci.h>
+#include <asm/pgtable.h>
+#include <asm/prom.h>
+#include <asm/setup.h>
+#include <asm/smp-ops.h>
+#include <asm/smp.h>
+#include <asm/time.h>
+
+#include <asm/mach-baikal-t1/memory.h>
+
+static __initdata const void *fdt;
+
+/*
+ * The following configuration have been used to synthesize the Baikal-T1
+ * MIPS Warroir P5600 core:
+ * 1) SI_EVAReset = 0 - boot in legacy (not EVA) memory layout mode after
+ * reset.
+ * 2) SI_UseExceptionBase = 0 - core uses legacy BEV mode, which selects
+ * 0xBFC00000 to be exception vector by default after reset.
+ * 3) SI_ExceptionBase[31:12] = 0xBFC00000 - externally set default exception
+ * SI_ExceptionBasePA[31:29] = 0x0 base address. It is used when
+ * CP0.CONFIG5.K = 1.
+ * 4) SI_EICPresent = 0 - even though GIC is always attached to the cores,
+ * this pin is hardwaired to the state of the
+ * GIC_VX_CTL_EIC bit.
+ */
+
+/*
+ * Redefine the MIPS CDMM phys base method to be used at the earliest boot
+ * stage before DT is parsed.
+ */
+#ifdef CONFIG_MIPS_EJTAG_FDC_EARLYCON
+
+phys_addr_t mips_cdmm_phys_base(void)
+{
+ return BT1_P5600_CDMM_BASE;
+}
+
+#endif /* CONFIG_MIPS_EJTAG_FDC_EARLYCON */
+
+/*
+ * We have to redefine the L2-sync phys base method, since the default
+ * region overlaps the Baikal-T1 boot memory following the CM2 GCRs.
+ */
+phys_addr_t mips_cm_l2sync_phys_base(void)
+{
+ return BT1_P5600_GCR_L2SYNC_BASE;
+}
+
+void __init *plat_get_fdt(void)
+{
+ const char *str;
+
+ /* Return already found fdt. */
+ if (fdt)
+ return (void *)fdt;
+
+ /*
+ * Generic method will search for appended, 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 */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 IRQ initialization
+ */
+#include <linux/irqchip.h>
+
+#include <asm/mipsregs.h>
+#include <asm/mips-gic.h>
+#include <asm/irq_cpu.h>
+#include <asm/irq.h>
+
+int get_c0_fdc_int(void)
+{
+ return gic_get_c0_fdc_int();
+}
+
+int get_c0_perfcount_int(void)
+{
+ return gic_get_c0_perfcount_int();
+}
+
+unsigned int get_c0_compare_int(void)
+{
+ return gic_get_c0_compare_int();
+}
+
+/*
+ * If CP0.Cause.IV == 1 and cpu_has_veic = 1 the next method isn't supposed
+ * to be called ever. Otherwise we just handle a vectored interrupt, which was
+ * routed to the generic exception vector.
+ */
+#if !defined(CONFIG_IRQ_MIPS_CPU)
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ extern unsigned long vi_handlers[];
+ unsigned int cause = (read_c0_cause() & CAUSEF_IP) >> CAUSEB_IP2;
+ void (*isr)(void) = (void *)vi_handlers[cause];
+
+ if (cause && isr)
+ isr();
+ else if (cause && !isr)
+ panic("Vectored interrupt %u handler is empty\n", cause);
+ else
+ spurious_interrupt();
+}
+
+#endif /* !CONFIG_IRQ_MIPS_CPU */
+
+void __init arch_init_irq(void)
+{
+ if (!cpu_has_veic)
+ mips_cpu_irq_init();
+
+ irqchip_init();
+}
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
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+#include "../../baikal-t1/early_printk.c"
# 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
--- /dev/null
+# 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))
--- /dev/null
+// 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 = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&sata1 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ 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";
+};
--- /dev/null
+// 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";
+ };
+};
--- /dev/null
+// 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 = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&sata1 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ 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";
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Edelweiss EM416 Mezzanine Card device tree
+ */
+
+#include <dt-bindings/net/mscc-phy-vsc8531.h>
+
+#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 = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&sata1 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ 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_LINK_1000_ACTIVITY>;
+ vsc8531,led-1-mode = <VSC8531_LINK_100_ACTIVITY>;
+ };
+};
+
+&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_LINK_1000_ACTIVITY>;
+ vsc8531,led-1-mode = <VSC8531_LINK_100_ACTIVITY>;
+ };
+};
+
+&gmac1 {
+ mac-address = [ 00 26 58 79 00 06 ];
+
+ phy-handle = <&gmac1_phy>;
+
+ status = "okay";
+};
+
+&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";
+};
--- /dev/null
+// 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 = <HBA_PORT_FBSCP>;
+
+ status = "okay";
+};
+
+&sata1 {
+ hba-port-cap = <HBA_PORT_FBSCP>;
+
+ 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";
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal Electronics KR/KX4 SFI Mezzanine Card device tree
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ 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>;
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 SoC overclocking device tree
+ */
+
+#include <dt-bindings/clock/bt1-ccu.h>
+
+/*
+ * 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>;
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 SoC generic device tree
+ */
+
+#include <dt-bindings/dma/dw-dmac.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/mips-gic.h>
+#include <dt-bindings/thermal/thermal.h>
+#include <dt-bindings/ata/ahci.h>
+
+#include <dt-bindings/clock/bt1-ccu.h>
+#include <dt-bindings/reset/bt1-ccu.h>
+#include <dt-bindings/soc/bt1-boot-mode.h>
+
+/ {
+ model = "Baikal-T1 SoC";
+ compatible = "baikal,bt1";
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ interrupt-parent = <&gic>;
+
+ aliases {
+ serial0 = &uart0;
+ serial1 = &uart1;
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
+ i2c2 = &i2c2;
+ spi0 = &spi0;
+ spi1 = &spi1;
+ spi2 = &spi2;
+ 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 = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+
+ 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 = <GIC_SHARED 127 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_axi CCU_AXI_MAIN_CLK>;
+ clock-names = "aclk";
+
+ resets = <&ccu_axi CCU_AXI_MAIN_RST>;
+ reset-names = "arst";
+
+ syscon = <&syscon>;
+
+ /*
+ * Note the (dma-)ranges mapping must be 64K aligned due to
+ * iATU constraints (lowest 16 bits aren't writable). Also
+ * note that we have to split the MEM-range up into two so
+ * one of them would be 256MB-aligned as some of the PCIe
+ * peripherals require. It can be done since AXI-interconnect
+ * doesn't permit the PCIe-master to access the MMIO-range
+ * anyway, so we can freely use the memory range above
+ * 0x1bfc0000 locally within the PCIe space.
+ */
+ pcie: pcie@1f052000 {
+ compatible = "baikal,bt1-pcie";
+ device_type = "pci";
+ reg = <0 0x1f052000 0 0x1000>,
+ <0 0x1f053000 0 0x1000>,
+ <0 0x1bdb0000 0 0x10000>;
+ reg-names = "dbi", "dbi2", "config";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges = <0x82000000 0 0x08000000 0 0x08000000 0 0x03da0000>, /* mem */
+ <0x82000000 0 0x10000000 0 0x0bda0000 0 0x10000000>, /* mem */
+ <0x81000000 0 0x0bda0000 0 0x1bda0000 0 0x00010000>; /* io */
+ bus-range = <0x0 0xff>;
+
+ interrupts = <GIC_SHARED 80 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 81 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 82 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 83 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 84 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 85 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 86 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 87 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 88 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 89 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 90 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 91 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 92 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 93 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dma0", "dma1", "dma2", "dma3",
+ "dma4", "dma5", "dma6", "dma7",
+ "msi", "aer", "pme", "hp", "bw_mg",
+ "l_eq";
+
+ /* 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 = <GIC_SHARED 56 IRQ_TYPE_LEVEL_HIGH>;
+
+ /* Clock rate up to 200MHz */
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "hclk";
+
+ dma-channels = <8>;
+ dma-requests = <12>;
+ dma-masters = <2>;
+
+ chan_allocation_order = <0>;
+ chan_priority = <0>;
+ block_size = <4095>;
+ data-width = <16 4>;
+ multi-block = <0 0 0 0 0 0 0 0>;
+ snps,max-burst-len = <16 16 4 4 4 4 4 4>;
+
+ status = "disabled";
+ };
+
+ mc: memory-controller@1f042000 {
+ compatible = "baikal,bt1-ddrc";
+ reg = <0 0x1f042000 0 0x1000>,
+ <0 0x1f043000 0 0x1000>;
+ reg-names = "umctl2", "phy";
+
+ interrupts = <GIC_SHARED 96 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 97 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 98 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 99 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dfi_e", "ecc_ce", "ecc_ue", "ecc_sbr";
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_DDR_CLK>,
+ <&ccu_pll CCU_DDR_PLL>;
+ clock-names = "pclk", "aclk", "core";
+
+ resets = <&ccu_axi CCU_AXI_DDR_RST>,
+ <&ccu_sys CCU_SYS_DDR_INIT_RST>;
+ reset-names = "arst", "core";
+
+ status = "disabled";
+ };
+
+ /*
+ * DWC AHCI SATA controller has been configured with 32-bits
+ * AMBA Master Address Bus width. Make sure any buffer
+ * allocated above that limit is bounced down to the permitted
+ * memory space before being passed to the device.
+ */
+ sata: sata@1f050000 {
+ compatible = "baikal,bt1-ahci";
+ reg = <0 0x1f050000 0 0x2000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 64 IRQ_TYPE_LEVEL_HIGH>;
+
+ /* Using an external 100MHz clock source is preferable */
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_SATA_CLK>,
+ <&ccu_sys CCU_SYS_SATA_REF_CLK>;
+ clock-names = "pclk", "aclk", "ref";
+
+ resets = <&ccu_axi CCU_AXI_SATA_RST>,
+ <&ccu_sys CCU_SYS_SATA_REF_RST>;
+ reset-names = "arst", "ref";
+
+ ports-implemented = <0x3>;
+
+ status = "disabled";
+
+ sata0: sata-port@0 {
+ reg = <0>;
+
+ snps,tx-ts-max = <16>;
+ snps,rx-ts-max = <16>;
+
+ status = "disabled";
+ };
+
+ sata1: sata-port@1 {
+ reg = <1>;
+
+ snps,tx-ts-max = <16>;
+ snps,rx-ts-max = <16>;
+
+ status = "disabled";
+ };
+ };
+
+ xgmac: ethernet@1f054000 {
+ compatible = "baikal,bt1-xgmac";
+ reg = <0 0x1f054000 0 0x4000>,
+ <0 0x1f05d000 0 0x1000>;
+ reg-names = "stmmaceth", "xpcs";
+
+ interrupts = <GIC_SHARED 74 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 75 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 76 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 77 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 78 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 79 IRQ_TYPE_LEVEL_HIGH>;
+ 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 = <GIC_SHARED 104 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_HWA_CLK>,
+ <&ccu_sys CCU_SYS_HWA_CLK>;
+ clock-names = "pclk", "aclk", "ref";
+
+ resets = <&ccu_axi CCU_AXI_HWA_RST>;
+ reset-names = "arst";
+
+ status = "disabled";
+ };
+
+ 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 = <GIC_SHARED 79 IRQ_TYPE_LEVEL_HIGH>;
+
+ 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 = <GIC_SHARED 68 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "host";
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_USB_CLK>,
+ <&ccu_sys CCU_SYS_USB_CLK>;
+ clock-names = "pclk", "bus_early", "ref";
+
+ resets = <&ccu_axi CCU_AXI_USB_RST>;
+ reset-names = "arst";
+
+ dr_mode = "host";
+ phy_type = "ulpi";
+ maximum-speed = "high-speed";
+
+ snps,incr-burst-type-adjustment = <1 4 8 16>;
+
+ status = "disabled";
+ };
+
+ gmac0: ethernet@1f05e000 {
+ compatible = "baikal,bt1-gmac";
+ reg = <0 0x1f05e000 0 0x2000>;
+ #address-cells = <1>;
+ #size-cells = <2>;
+ dma-ranges = <0 0 0 0x1 0>;
+
+ interrupts = <GIC_SHARED 72 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "macirq";
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_GMAC0_CLK>,
+ <&ccu_sys CCU_SYS_GMAC0_TX_CLK>,
+ <&ccu_sys CCU_SYS_GMAC0_PTP_CLK>;
+ clock-names = "pclk", "stmmaceth", "tx", "ptp_ref";
+
+ resets = <&ccu_axi CCU_AXI_GMAC0_RST>;
+ reset-names = "stmmaceth";
+
+ /* DW GMAC is configured to export 1xGPI and 1xGPO */
+ ngpios = <2>;
+
+ 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 = <GIC_SHARED 73 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "macirq";
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_axi CCU_AXI_GMAC1_CLK>,
+ <&ccu_sys CCU_SYS_GMAC1_TX_CLK>,
+ <&ccu_sys CCU_SYS_GMAC1_PTP_CLK>;
+ clock-names = "pclk", "stmmaceth", "tx", "ptp_ref";
+
+ resets = <&ccu_axi CCU_AXI_GMAC1_RST>;
+ reset-names = "stmmaceth";
+
+ /* DW GMAC is configured to export 1xGPI and 1xGPO */
+ ngpios = <2>;
+
+ 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 = <GIC_SHARED 16 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "pclk";
+
+ resets = <&ccu_sys CCU_SYS_APB_RST>;
+ reset-names = "prst";
+
+ syscon: syscon@1f04d000 {
+ compatible = "baikal,bt1-sys-con", "syscon", "simple-mfd";
+ reg = <0x1f04d000 0x1000>;
+ reg-names = "sys";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ little-endian;
+ reg-io-width = <4>;
+
+ ccu_pll: clock-controller@1f04d000 {
+ compatible = "baikal,bt1-ccu-pll";
+ reg = <0x1f04d000 0x028>;
+ #clock-cells = <1>;
+
+ clocks = <&ref_clk>;
+ clock-names = "ref_clk";
+ };
+
+ ccu_axi: clock-controller@1f04d030 {
+ compatible = "baikal,bt1-ccu-axi";
+ reg = <0x1f04d030 0x030>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+
+ clocks = <&ccu_pll CCU_SATA_PLL>,
+ <&ccu_pll CCU_PCIE_PLL>,
+ <&ccu_pll CCU_ETH_PLL>;
+ clock-names = "sata_clk", "pcie_clk", "eth_clk";
+ };
+
+ ccu_sys: clock-controller@1f04d060 {
+ compatible = "baikal,bt1-ccu-sys";
+ reg = <0x1f04d060 0x0a0>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+
+ clocks = <&ref_clk>,
+ <&ccu_pll CCU_SATA_PLL>,
+ <&ccu_pll CCU_PCIE_PLL>,
+ <&ccu_pll CCU_ETH_PLL>;
+ clock-names = "ref_clk", "sata_clk", "pcie_clk",
+ "eth_clk";
+ };
+
+ l2: l2@1f04d028 {
+ compatible = "baikal,bt1-l2-ctl";
+ reg = <0x1f04d028 0x004>;
+
+ baikal,l2-ws-latency = <0>;
+ baikal,l2-tag-latency = <0>;
+ baikal,l2-data-latency = <1>;
+
+ status = "disabled";
+ };
+
+ reboot {
+ compatible = "syscon-reboot";
+ offset = <0x118>;
+
+ mask = <0x1>;
+ value = <0x1>;
+
+ status = "disabled";
+ };
+
+ reboot-mode {
+ compatible = "syscon-reboot-mode";
+ offset = <0x154>;
+
+ mode-normal = <RCR_BOOT_NORMAL>;
+ mode-loader = <RCR_BOOT_LOADER>;
+ mode-recovery = <RCR_BOOT_RECOVERY>;
+ };
+
+ i2c0: i2c@1f04d100 {
+ compatible = "baikal,bt1-sys-i2c";
+ reg = <0x1f04d100 0x010>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 32 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-frequency = <400000>;
+
+ status = "disabled";
+ };
+ };
+
+ bootcon: syscon@1f040000 {
+ compatible = "baikal,bt1-boot-con", "syscon", "simple-mfd";
+ reg = <0x1f040000 0x1000>,
+ <0x1fc00000 0x400000>;
+ reg-names = "boot", "mirror";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ little-endian;
+ reg-io-width = <4>;
+
+ boot_mux: mux-controller {
+ compatible = "mmio-mux";
+ #mux-control-cells = <1>;
+
+ mux-reg-masks = <0x0 0x100>, <0x4 0x1>;
+ idle-states = <0x1>, <0x0>;
+ };
+
+ int_rom: rom@1bfc0000 {
+ compatible = "baikal,bt1-int-rom", "mtd-rom";
+ reg = <0x1bfc0000 0x10000>;
+
+ no-unaligned-direct-access;
+ bank-width = <4>;
+
+ status = "disabled";
+ };
+
+ /*
+ * Note that using the dirmap region stalls the APB bus
+ * until an IO operation is finished. It may cause
+ * significant lags in concurrent access to the system
+ * MMIO, since each SPI flash dword read operation takes
+ * at least 2.56 us to be finished (cmd + addr + data).
+ */
+ spi0: spi@1f040100 {
+ compatible = "baikal,bt1-sys-ssi";
+ reg = <0x1f040100 0x900>,
+ <0x1c000000 0x1000000>;
+ reg-names = "config", "map";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mux-controls = <&boot_mux 0>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ssi_clk";
+
+ status = "disabled";
+ };
+ };
+
+ gpio0: gpio@1f044000 {
+ compatible = "snps,dw-apb-gpio";
+ reg = <0x1f044000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_sys CCU_SYS_GPIO_CLK>;
+ clock-names = "bus", "db";
+
+ status = "disabled";
+
+ port0: gpio-port@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ reg = <0>;
+
+ interrupts = <GIC_SHARED 19 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpios = <32>;
+ };
+ };
+
+ gpio1: gpio@1f045000 {
+ compatible = "snps,dw-apb-gpio";
+ reg = <0x1f045000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>,
+ <&ccu_sys CCU_SYS_GPIO_CLK>;
+ clock-names = "bus", "db";
+
+ status = "disabled";
+
+ port1: gpio-port@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ reg = <0>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpios = <3>;
+ };
+ };
+
+ i2c1: i2c@1f046000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x1f046000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 33 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_I2C1_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ref", "pclk";
+ clock-frequency = <400000>;
+
+ dmas = <&dma 4 0 1 0xff>, <&dma 5 0 1 0xff>;
+ dma-names = "tx", "rx";
+
+ status = "disabled";
+ };
+
+ i2c2: i2c@1f047000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x1f047000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 34 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_I2C2_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ref", "pclk";
+ clock-frequency = <400000>;
+
+ dmas = <&dma 6 0 1 0xff>, <&dma 7 0 1 0xff>;
+ dma-names = "tx", "rx";
+
+ status = "disabled";
+ };
+
+ timer_dw0: timer@1f049000 {
+ compatible = "snps,dw-apb-timer";
+ reg = <0x1f049000 0x14>;
+
+ interrupts = <GIC_SHARED 24 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_TIMER0_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "timer", "pclk";
+
+ status = "disabled";
+ };
+
+ timer_dw1: timer@1f049014 {
+ compatible = "snps,dw-apb-timer";
+ reg = <0x1f049014 0x14>;
+
+ interrupts = <GIC_SHARED 25 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_TIMER1_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "timer", "pclk";
+
+ status = "disabled";
+ };
+
+ timer_dw2: timer@1f049028 {
+ compatible = "snps,dw-apb-timer";
+ reg = <0x1f049028 0x14>;
+
+ interrupts = <GIC_SHARED 26 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_TIMER2_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "timer", "pclk";
+
+ status = "disabled";
+ };
+
+ uart0: serial@1f04a000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x1f04a000 0x1000>;
+
+ interrupts = <GIC_SHARED 48 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_UART_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "baudclk", "apb_pclk";
+
+ dmas = <&dma 0 0 1 0xff>, <&dma 1 0 1 0xff>;
+ dma-names = "tx", "rx";
+
+ dcd-override;
+ dsr-override;
+ cts-override;
+ ri-override;
+
+ /* earlycon settings. */
+ reg-io-width = <4>;
+ reg-shift = <2>;
+
+ status = "disabled";
+ };
+
+ uart1: serial@1f04b000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x1f04b000 0x1000>;
+
+ interrupts = <GIC_SHARED 49 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_UART_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "baudclk", "apb_pclk";
+
+ dmas = <&dma 2 0 1 0xff>, <&dma 3 0 1 0xff>;
+ dma-names = "tx", "rx";
+
+ /* earlycon settings. */
+ reg-io-width = <4>;
+ reg-shift = <2>;
+
+ status = "disabled";
+ };
+
+ wdt: watchdog@1f04c000 {
+ compatible = "snps,dw-wdt";
+ reg = <0x1f04c000 0x1000>;
+
+ interrupts = <GIC_SHARED 17 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_WDT_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "tclk", "pclk";
+
+ /* Adjust ref-clock rate for better TOPs granularity */
+ assigned-clocks = <&ccu_sys CCU_SYS_WDT_CLK>;
+ assigned-clock-rates = <65534>;
+
+ snps,watchdog-tops = <0x000000ff 0x000001ff 0x000003ff
+ 0x000007ff 0x0000ffff 0x0001ffff
+ 0x0003ffff 0x0007ffff 0x000fffff
+ 0x001fffff 0x003fffff 0x007fffff
+ 0x00ffffff 0x01ffffff 0x03ffffff
+ 0x07ffffff>;
+
+ status = "disabled";
+ };
+
+ /*
+ * It's highly recommended to use all DW APB SSI controllers
+ * with GPIO-based CS, due to the native CS being automatically
+ * asserted/de-asserted on transmissions. Such HW design isn't
+ * that suitable for the kernel SPI subsystem, so GPIO-based CS
+ * will help to prevent very nasty, hard-to-fix errors.
+ */
+ spi1: spi@1f04e000 {
+ compatible = "snps,dw-apb-ssi";
+ reg = <0x1f04e000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 40 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ssi_clk";
+
+ /*
+ * Make sure Rx DMA channels have higher priority. Note
+ * also that first two DW DMAC channels aren't suitable
+ * for the well-balanced Tx and Rx SPI transfers.
+ */
+ dmas = <&dma 8 0 1 0xe0>, <&dma 9 0 1 0x1c>;
+ dma-names = "tx", "rx";
+
+ reg-io-width = <4>;
+
+ status = "disabled";
+ };
+
+ spi2: spi@1f04f000 {
+ compatible = "snps,dw-apb-ssi";
+ reg = <0x1f04f000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <GIC_SHARED 41 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ssi_clk";
+
+ /*
+ * Make sure Rx DMA channels have higher priority. Note
+ * also that first two DW DMAC channels aren't suitable
+ * for the well-balanced Tx and Rx SPI transfers.
+ */
+ dmas = <&dma 10 0 1 0xe0>, <&dma 11 0 1 0x1c>;
+ dma-names = "tx", "rx";
+
+ reg-io-width = <4>;
+
+ status = "disabled";
+ };
+
+ pvt: temperature-sensor@1f200000 {
+ compatible = "baikal,bt1-pvt";
+ reg = <0x1f200000 0x1000>;
+ #thermal-sensor-cells = <0>;
+
+ interrupts = <GIC_SHARED 31 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&ccu_sys CCU_SYS_PVT_CLK>,
+ <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "ref", "pclk";
+
+ status = "disabled";
+ };
+
+ efuse: efuse@1f201000 {
+ compatible = "baikal,bt1-efuse";
+ reg = <0x1f201000 0x1000>;
+
+ clocks = <&ccu_sys CCU_SYS_APB_CLK>;
+ clock-names = "pclk";
+
+ status = "disabled";
+ };
+ };
+};
--- /dev/null
+// 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 = <HBA_PORT_FBSCP>;
+
+ 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";
+};
--- /dev/null
+// 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>;
+ };
+};
--- /dev/null
+// 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>;
+ };
+};
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
*/
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()]
#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) \
volatile type *__mem; \
type __val; \
\
- if (barrier) \
+ if (!(relax && IS_ENABLED(CONFIG_STRONG_UC_ORDERING))) \
iobarrier_rw(); \
else \
war_io_reorder_wmb(); \
\
__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)) \
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(); \
\
BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long)); \
\
- if (barrier) \
+ if (!(relax && IS_ENABLED(CONFIG_STRONG_UC_ORDERING))) \
iobarrier_rw(); \
\
__val = *__addr; \
#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) \
\
#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) \
#define __BUILDIO(bwlq, type) \
\
-__BUILD_MEMORY_SINGLE(____raw_, bwlq, type, 1, 0, 0)
+__BUILD_MEMORY_SINGLE(____raw_, bwlq, type, 0, 0)
__BUILDIO(q, u64)
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);
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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 <irq.h>
+
+#endif /* __ASM_MACH_BAIKAL_T1_IRQ_H__ */
--- /dev/null
+/* 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 <asm/regdef.h>
+#include <asm/mipsregs.h>
+
+ /*
+ * 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__ */
--- /dev/null
+/* 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 <linux/sizes.h>
+#include <asm/addrspace.h>
+
+#define BT1_LOMEM_BASE 0x00000000
+#define BT1_LOMEM_SIZE SZ_128M
+
+#define BT1_MMIO_START BT1_PCIE_MAP_BASE
+
+#define BT1_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__ */
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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 <asm/mach-generic/spaces.h>
+
+#endif /* __ASM_MACH_BAIKAL_T1_SPACES_H__ */
#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 <asm/mach-generic/spaces.h>
#endif
#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)
res = read_##name(); \
new = res | set; \
write_##name(new); \
+ _ehb(); \
\
return res; \
} \
res = read_##name(); \
new = res & ~clear; \
write_##name(new); \
+ _ehb(); \
\
return res; \
} \
new = res & ~change; \
new |= (val & change); \
write_##name(new); \
+ _ehb(); \
\
return res; \
}
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 <asm-generic/mmu_context.h>
#endif /* _ASM_MMU_CONTEXT_H */
#ifdef CONFIG_KEXEC
void (*kexec_nonboot_cpu)(void);
#endif
-};
+} __no_randomize_layout;
extern void register_smp_ops(const struct plat_smp_ops *ops);
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
}
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;
{
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
struct opal_resp_tok toks[MAX_TOKS];
};
+/* Presumably DMA-able buffers must be cache-aligned */
struct opal_dev {
bool supported;
bool mbr_enabled;
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;
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
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
{
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;
}
/*
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);
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,
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 */
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 */
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 */
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 */
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 */
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);
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)
struct ahci_host_priv *hpriv;
void __iomem *pwrdn_reg;
struct resource *res;
- struct clk *clk;
u32 mpy;
int rc;
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;
* 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;
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DWC AHCI SATA Platform driver
+ *
+ * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
+ */
+
+#include <linux/ahci_platform.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/libata.h>
+#include <linux/log2.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+#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 <Sergey.Semin@baikalelectronics.ru>");
+MODULE_LICENSE("GPL");
SYS_CFG_SATA_EN);
}
- of_property_read_u32(np, "ports-implemented", &hpriv->force_port_map);
-
return 0;
}
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;
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", },
{},
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) {
* http://www.intel.com/technology/serialata/pdf/rev1_1.pdf
*/
+#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/module.h>
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);
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;
*/
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;
}
/* 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)
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)
* Anton Vorontsov <avorontsov@ru.mvista.com>
*/
+#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/gfp.h>
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);
* 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
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);
{
ahci_platform_disable_phys(hpriv);
- reset_control_assert(hpriv->rsts);
+ ahci_platform_assert_rsts(hpriv);
ahci_platform_disable_clks(hpriv);
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
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
* 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)
*
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))
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");
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);
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;
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)
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) {
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)
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) {
config CLK_BT1_CCU_DIV
bool "Baikal-T1 CCU Dividers support"
- select RESET_CONTROLLER
select MFD_SYSCON
default MIPS_BAIKAL_T1
help
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
# 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
#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
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)
{
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 {
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)
};
continue;
}
+ if (!strcmp("div_buf", name))
+ continue;
+
bits[didx] = ccu_div_bits[bidx];
bits[didx].div = div;
&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);
#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 */
.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,
} 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;
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;
#include <linux/bits.h>
#include <linux/of.h>
+/*
+ * 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)
* 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
};
void ccu_div_hw_unregister(struct ccu_div *div);
-int ccu_div_reset_domain(struct ccu_div *div);
-
#endif /* __CLK_BT1_CCU_DIV_H__ */
#include <linux/bits.h>
#include <linux/of.h>
+/*
+ * 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.
* @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;
struct regmap *sys_regs;
struct device_node *np;
unsigned long flags;
+ unsigned long features;
};
/*
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ * Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 CCU Resets interface driver
+ */
+
+#define pr_fmt(fmt) "bt1-ccu-rst: " fmt
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/printk.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/reset/bt1-ccu.h>
+
+#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);
+}
--- /dev/null
+/* 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 <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+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__ */
#define pr_fmt(fmt) "bt1-ccu-div: " fmt
#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <dt-bindings/clock/bt1-ccu.h>
-#include <dt-bindings/reset/bt1-ccu.h>
#include "ccu-div.h"
+#include "ccu-rst.h"
#define CCU_AXI_MAIN_BASE 0x030
#define CCU_AXI_DDR_BASE 0x034
.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, \
.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;
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;
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
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",
"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",
"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;
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));
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;
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;
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;
}
}
}
- 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;
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 <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 CCU Dividers clock driver");
+MODULE_LICENSE("GPL");
#define pr_fmt(fmt) "bt1-ccu-pll: " fmt
#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
#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)
const char *parent_name;
unsigned int base;
unsigned long flags;
+ unsigned long features;
};
/*
*/
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 {
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);
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;
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;
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])) {
}
}
+ 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;
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 <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 CCU PLL clock driver");
+MODULE_LICENSE("GPL");
.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 = {
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;
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);
*/
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) {
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)
{
{
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
int err = 0;
- LIST_HEAD(head);
if (!chan->configured) {
/* Do nothing */
{
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) {
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;
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++) {
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;
* 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;
* 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);
- }
}
}
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;
}
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);
}
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;
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)
(*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;
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) {
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);
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);
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;
}
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);
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;
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;
}
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);
#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,
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;
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;
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 {
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,
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;
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 */
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);
}
/* 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];
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;
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];
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;
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)) {
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) {
#include <linux/bitfield.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+
#include "dw-edma-core.h"
#include "dw-edma-v0-core.h"
#include "dw-edma-v0-regs.h"
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) \
SET_32(dw, rd_##name, value); \
} while (0)
-#ifdef CONFIG_64BIT
-
#define SET_64(dw, name, value) \
writeq(value, &(__dw_regs(dw)->name))
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))
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)
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;
{
u32 value;
- if (dw->mf == EDMA_MF_EDMA_LEGACY) {
+ if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) {
u32 viewport_sel;
unsigned long flags;
#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);
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);
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;
}
#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)
{
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,
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)));
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)));
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;
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++;
}
/* 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);
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,
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,
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 */
}
/* 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);
}
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 */
#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 {
}
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);
}
#include <linux/dma/edma.h>
#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 */
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.
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
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
#include <linux/edac.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
+#include <linux/idr.h>
+#include <linux/of.h>
+
#include <asm/page.h>
#include "edac_mc.h"
#include "edac_module.h"
/* 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.
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)
[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",
unsigned int pos[EDAC_MAX_LAYERS];
unsigned int row, chn, idx;
int layer;
- void *p;
/*
* Allocate and fill the dimm structs
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];
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;
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
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
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) {
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;
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)
#define _EDAC_MC_H_
#include <linux/kernel.h>
+#include <linux/limits.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/edac.h>
+/* Generate MC identifier automatically */
+#define EDAC_AUTO_MC_NUM UINT_MAX
+
#if PAGE_SHIFT < 20
#define PAGES_TO_MiB(pages) ((pages) >> (20 - PAGE_SHIFT))
#define MiB_TO_PAGES(mb) ((mb) << (20 - PAGE_SHIFT))
+// 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 <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
#include <linux/edac.h>
+#include <linux/fs.h>
+#include <linux/log2.h>
#include <linux/module.h>
+#include <linux/pfn.h>
#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/sizes.h>
+#include <linux/spinlock.h>
+#include <linux/units.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include "edac_module.h"
-/* Number of cs_rows needed per memory controller */
-#define SYNPS_EDAC_NR_CSROWS 1
-
/* Number of channels per memory controller */
-#define SYNPS_EDAC_NR_CHANS 1
-
-/* Granularity of reported error in bytes */
-#define SYNPS_EDAC_ERR_GRAIN 1
-
-#define SYNPS_EDAC_MSG_SIZE 256
-
-#define SYNPS_EDAC_MOD_STRING "synps_edac"
-#define SYNPS_EDAC_MOD_VER "1"
-
-/* Synopsys DDR memory controller registers that are relevant to ECC */
-#define CTRL_OFST 0x0
-#define T_ZQ_OFST 0xA4
+#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
#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
#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.
* 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) {
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");
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Zynq DDR ECC Driver
+ * This driver is based on ppc4xx_edac.c drivers
+ *
+ * Copyright (C) 2012 - 2014 Xilinx, Inc.
+ */
+
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "edac_module.h"
+
+/* Number of cs_rows needed per memory controller */
+#define ZYNQ_EDAC_NR_CSROWS 1
+
+/* Number of channels per memory controller */
+#define ZYNQ_EDAC_NR_CHANS 1
+
+/* Granularity of reported error in bytes */
+#define ZYNQ_EDAC_ERR_GRAIN 1
+
+#define ZYNQ_EDAC_MSG_SIZE 256
+
+#define ZYNQ_EDAC_MOD_STRING "zynq_edac"
+#define ZYNQ_EDAC_MOD_VER "1"
+
+/* Zynq DDR memory controller ECC registers */
+#define ZYNQ_CTRL_OFST 0x0
+#define ZYNQ_T_ZQ_OFST 0xA4
+
+/* ECC control register */
+#define ZYNQ_ECC_CTRL_OFST 0xC4
+/* ECC log register */
+#define ZYNQ_CE_LOG_OFST 0xC8
+/* ECC address register */
+#define ZYNQ_CE_ADDR_OFST 0xCC
+/* ECC data[31:0] register */
+#define ZYNQ_CE_DATA_31_0_OFST 0xD0
+
+/* Uncorrectable error info registers */
+#define ZYNQ_UE_LOG_OFST 0xDC
+#define ZYNQ_UE_ADDR_OFST 0xE0
+#define ZYNQ_UE_DATA_31_0_OFST 0xE4
+
+#define ZYNQ_STAT_OFST 0xF0
+#define ZYNQ_SCRUB_OFST 0xF4
+
+/* Control register bit field definitions */
+#define ZYNQ_CTRL_BW_MASK 0xC
+#define ZYNQ_CTRL_BW_SHIFT 2
+
+#define ZYNQ_DDRCTL_WDTH_16 1
+#define ZYNQ_DDRCTL_WDTH_32 0
+
+/* ZQ register bit field definitions */
+#define ZYNQ_T_ZQ_DDRMODE_MASK 0x2
+
+/* ECC control register bit field definitions */
+#define ZYNQ_ECC_CTRL_CLR_CE_ERR 0x2
+#define ZYNQ_ECC_CTRL_CLR_UE_ERR 0x1
+
+/* ECC correctable/uncorrectable error log register definitions */
+#define ZYNQ_LOG_VALID 0x1
+#define ZYNQ_CE_LOG_BITPOS_MASK 0xFE
+#define ZYNQ_CE_LOG_BITPOS_SHIFT 1
+
+/* ECC correctable/uncorrectable error address register definitions */
+#define ZYNQ_ADDR_COL_MASK 0xFFF
+#define ZYNQ_ADDR_ROW_MASK 0xFFFF000
+#define ZYNQ_ADDR_ROW_SHIFT 12
+#define ZYNQ_ADDR_BANK_MASK 0x70000000
+#define ZYNQ_ADDR_BANK_SHIFT 28
+
+/* ECC statistic register definitions */
+#define ZYNQ_STAT_UECNT_MASK 0xFF
+#define ZYNQ_STAT_CECNT_MASK 0xFF00
+#define ZYNQ_STAT_CECNT_SHIFT 8
+
+/* ECC scrub register definitions */
+#define ZYNQ_SCRUB_MODE_MASK 0x7
+#define ZYNQ_SCRUB_MODE_SECDED 0x4
+
+/**
+ * struct zynq_ecc_error_info - ECC error log information.
+ * @row: Row number.
+ * @col: Column number.
+ * @bank: Bank number.
+ * @bitpos: Bit position.
+ * @data: Data causing the error.
+ */
+struct zynq_ecc_error_info {
+ u32 row;
+ u32 col;
+ u32 bank;
+ u32 bitpos;
+ u32 data;
+};
+
+/**
+ * struct zynq_ecc_status - ECC status information to report.
+ * @ce_cnt: Correctable error count.
+ * @ue_cnt: Uncorrectable error count.
+ * @ceinfo: Correctable error log information.
+ * @ueinfo: Uncorrectable error log information.
+ */
+struct zynq_ecc_status {
+ u32 ce_cnt;
+ u32 ue_cnt;
+ struct zynq_ecc_error_info ceinfo;
+ struct zynq_ecc_error_info ueinfo;
+};
+
+/**
+ * struct zynq_edac_priv - DDR memory controller private instance data.
+ * @baseaddr: Base address of the DDR controller.
+ * @message: Buffer for framing the event specific info.
+ * @stat: ECC status information.
+ */
+struct zynq_edac_priv {
+ void __iomem *baseaddr;
+ char message[ZYNQ_EDAC_MSG_SIZE];
+ struct zynq_ecc_status stat;
+};
+
+/**
+ * zynq_get_error_info - Get the current ECC error info.
+ * @priv: DDR memory controller private instance data.
+ *
+ * Return: one if there is no error, otherwise zero.
+ */
+static int zynq_get_error_info(struct zynq_edac_priv *priv)
+{
+ struct zynq_ecc_status *p;
+ u32 regval, clearval = 0;
+ void __iomem *base;
+
+ base = priv->baseaddr;
+ p = &priv->stat;
+
+ regval = readl(base + ZYNQ_STAT_OFST);
+ if (!regval)
+ return 1;
+
+ p->ce_cnt = (regval & ZYNQ_STAT_CECNT_MASK) >> ZYNQ_STAT_CECNT_SHIFT;
+ p->ue_cnt = regval & ZYNQ_STAT_UECNT_MASK;
+
+ regval = readl(base + ZYNQ_CE_LOG_OFST);
+ if (!(p->ce_cnt && (regval & ZYNQ_LOG_VALID)))
+ goto ue_err;
+
+ p->ceinfo.bitpos = (regval & ZYNQ_CE_LOG_BITPOS_MASK) >> ZYNQ_CE_LOG_BITPOS_SHIFT;
+ regval = readl(base + ZYNQ_CE_ADDR_OFST);
+ p->ceinfo.row = (regval & ZYNQ_ADDR_ROW_MASK) >> ZYNQ_ADDR_ROW_SHIFT;
+ p->ceinfo.col = regval & ZYNQ_ADDR_COL_MASK;
+ p->ceinfo.bank = (regval & ZYNQ_ADDR_BANK_MASK) >> ZYNQ_ADDR_BANK_SHIFT;
+ p->ceinfo.data = readl(base + ZYNQ_CE_DATA_31_0_OFST);
+ edac_dbg(3, "CE bit position: %d data: %d\n", p->ceinfo.bitpos,
+ p->ceinfo.data);
+ clearval = ZYNQ_ECC_CTRL_CLR_CE_ERR;
+
+ue_err:
+ regval = readl(base + ZYNQ_UE_LOG_OFST);
+ if (!(p->ue_cnt && (regval & ZYNQ_LOG_VALID)))
+ goto out;
+
+ regval = readl(base + ZYNQ_UE_ADDR_OFST);
+ p->ueinfo.row = (regval & ZYNQ_ADDR_ROW_MASK) >> ZYNQ_ADDR_ROW_SHIFT;
+ p->ueinfo.col = regval & ZYNQ_ADDR_COL_MASK;
+ p->ueinfo.bank = (regval & ZYNQ_ADDR_BANK_MASK) >> ZYNQ_ADDR_BANK_SHIFT;
+ p->ueinfo.data = readl(base + ZYNQ_UE_DATA_31_0_OFST);
+ clearval |= ZYNQ_ECC_CTRL_CLR_UE_ERR;
+
+out:
+ writel(clearval, base + ZYNQ_ECC_CTRL_OFST);
+ writel(0x0, base + ZYNQ_ECC_CTRL_OFST);
+
+ return 0;
+}
+
+/**
+ * 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");
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
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
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;
--- /dev/null
+/*
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * 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 <COPYRIGHT HOLDER> 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 <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/mdio.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/of_mdio.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+#include <linux/clk.h>
+
+#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;
+}
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
+#include <linux/of_mdio.h>
#include <linux/clk.h>
#include <linux/property.h>
#include <linux/acpi.h>
: 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;
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;
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;
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)
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:
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);
#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,
.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",
static const struct of_device_id xgbe_of_match[] = {
{ .compatible = "amd,xgbe-seattle-v1a",
.data = &xgbe_v1 },
+ { .compatible = "baikal,bt1-xgmac",
+ .data = &xgbe_v3 },
{},
};
};
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;
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;
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);
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
# 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
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Baikal-T1 GMAC driver
+ *
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ */
+#include <linux/clk.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stmmac.h>
+
+#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 <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 GMAC glue driver");
+MODULE_LICENSE("GPL v2");
{
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.
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;
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) {
if (!mdio_bus_data)
return 0;
- new_bus = mdiobus_alloc();
+ priv->mii = new_bus = mdiobus_alloc();
if (!new_bus)
return -ENOMEM;
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++) {
goto no_phy_found;
}
-bus_register_done:
- priv->mii = new_bus;
-
return 0;
no_phy_found:
int x = mcast_bins;
switch (x) {
+ case 0:
case HASH_TABLE_SIZE:
case 128:
case 256:
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;
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;
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
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
--- /dev/null
+// 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 <linux/module.h>
+#include <linux/phy.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mdio.h>
+#include <linux/marvell_phy.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+
+/* 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");
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);
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;
.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",
#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;
};
*/
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)
pci_bus_for_each_resource(bus, r, i) {
resource_size_t min_used = min;
+ resource_size_t res_align = align;
if (!r)
continue;
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
/* 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;
}
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
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
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
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
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.
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
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
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
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);
.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;
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;
{
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;
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;
{
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);
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);
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;
{
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);
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);
{
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;
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)
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 {
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;
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;
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;
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;
.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);
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;
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;
*/
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);
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;
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);
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[] = {
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;
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);
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)
{
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[] = {
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);
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);
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;
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;
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);
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);
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;
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) {
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);
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ * Vadim Vlasov <Vadim.Vlasov@baikalelectronics.ru>
+ * Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 PCIe controller driver
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+
+#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 <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 PCIe driver");
+MODULE_LICENSE("GPL");
#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
-#include "../../pci.h"
-
void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
{
struct pci_epc *epc = ep->epc;
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)
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;
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;
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) {
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;
__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;
}
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;
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);
}
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)
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*
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);
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)
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),
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");
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)
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);
#include <linux/pci_regs.h>
#include <linux/platform_device.h>
-#include "../../pci.h"
#include "pcie-designware.h"
static struct pci_ops dw_pcie_ops;
};
/* 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;
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);
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;
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;
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;
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;
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;
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);
.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);
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);
}
}
-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;
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;
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 {
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;
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 ||
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;
+ }
}
}
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;
}
}
- /* 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 */
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
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;
}
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 = {
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)
.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.
/* 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]);
* 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);
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);
#include <linux/platform_device.h>
#include <linux/resource.h>
#include <linux/types.h>
-#include <linux/regmap.h>
#include "pcie-designware.h"
struct dw_plat_pcie {
struct dw_pcie *pci;
- struct regmap *regmap;
enum dw_pcie_device_mode mode;
};
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);
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;
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);
return -ENOMEM;
pci->dev = dev;
- pci->ops = &dw_pcie_ops;
dw_plat_pcie->pci = pci;
dw_plat_pcie->mode = mode;
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 = {
* Author: Jingoo Han <jg1.han@samsung.com>
*/
+#include <linux/align.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/dma/edma.h>
+#include <linux/gpio/consumer.h>
+#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/sizes.h>
#include <linux/types.h>
#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
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)
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;
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);
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)
{
}
-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);
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;
#define _PCIE_DESIGNWARE_H
#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
#include <linux/dma-mapping.h>
+#include <linux/dma/edma.h>
+#include <linux/gpio/consumer.h>
#include <linux/irq.h>
#include <linux/msi.h>
#include <linux/pci.h>
+#include <linux/reset.h>
#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
+/* 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
#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
#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)
#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)
#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
* 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
#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,
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;
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,
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)
#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);
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)
{
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;
}
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);
{
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);
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);
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);
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);
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);
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);
{
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;
#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;
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);
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);
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);
.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;
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);
};
static const struct of_device_id of_intel_pcie_match[] = {
- { .compatible = "intel,lgm-pcie", .data = &pcie_data },
+ { .compatible = "intel,lgm-pcie" },
{}
};
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
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;
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;
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);
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;
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);
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);
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;
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);
}
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,
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);
{
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;
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);
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);
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);
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);
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);
{
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) {
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;
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);
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;
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;
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;
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;
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);
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);
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);
*/
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;
}
.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);
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");
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;
};
* @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
*/
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;
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;
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
{
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);
}
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;
}
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,
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 {
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);
}
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);
* 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)
#endif
}
EXPORT_SYMBOL(pci_remap_iospace);
+#endif
/**
* pci_unmap_iospace - Unmap the memory mapped I/O space
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;
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,
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 :
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);
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;
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;
}
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);
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;
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)
}
#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 */
--- /dev/null
+/* 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
#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 */
--- /dev/null
+/* 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 */
#include <linux/compiler.h>
+struct clk;
struct device;
struct ata_port_info;
struct ahci_host_priv;
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);
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 */
#include <linux/device.h>
#include <linux/dmaengine.h>
+#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;
};
* 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
* 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
* 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
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,
#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)
#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
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
* 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) \
}
/*
* .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
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);
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 {