Merge tag 'drivers-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 1 Sep 2021 22:25:28 +0000 (15:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 1 Sep 2021 22:25:28 +0000 (15:25 -0700)
Pull ARM SoC driver updates from Arnd Bergmann:
 "These are updates for drivers that are tied to a particular SoC,
  including the correspondig device tree bindings:

   - A couple of reset controller changes for unisoc, uniphier, renesas
     and zte platforms

   - memory controller driver fixes for omap and tegra

   - Rockchip io domain driver updates

   - Lots of updates for qualcomm platforms, mostly touching their
     firmware and power management drivers

   - Tegra FUSE and firmware driver updateŃ•

   - Support for virtio transports in the SCMI firmware framework

   - cleanup of ixp4xx drivers, towards enabling multiplatform support
     and bringing it up to date with modern platforms

   - Minor updates for keystone, mediatek, omap, renesas"

* tag 'drivers-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (96 commits)
  reset: simple: remove ZTE details in Kconfig help
  soc: rockchip: io-domain: Remove unneeded semicolon
  soc: rockchip: io-domain: add rk3568 support
  dt-bindings: power: add rk3568-pmu-io-domain support
  bus: ixp4xx: return on error in ixp4xx_exp_probe()
  soc: renesas: Prefer memcpy() over strcpy()
  firmware: tegra: Stop using seq_get_buf()
  soc/tegra: fuse: Enable fuse clock on suspend for Tegra124
  soc/tegra: fuse: Add runtime PM support
  soc/tegra: fuse: Clear fuse->clk on driver probe failure
  soc/tegra: pmc: Prevent racing with cpuilde driver
  soc/tegra: bpmp: Remove unused including <linux/version.h>
  dt-bindings: soc: ti: pruss: Add dma-coherent property
  soc: ti: Remove pm_runtime_irq_safe() usage for smartreflex
  soc: ti: pruss: Enable support for ICSSG subsystems on K3 AM64x SoCs
  dt-bindings: soc: ti: pruss: Update bindings for K3 AM64x SoCs
  firmware: arm_scmi: Use WARN_ON() to check configured transports
  firmware: arm_scmi: Fix boolconv.cocci warnings
  soc: mediatek: mmsys: Fix missing UFOE component in mt8173 table routing
  soc: mediatek: mmsys: add MT8365 support
  ...

93 files changed:
Documentation/devicetree/bindings/ata/intel,ixp4xx-compact-flash.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt
Documentation/devicetree/bindings/firmware/arm,scmi.yaml
Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
Documentation/devicetree/bindings/power/rockchip-io-domain.txt [deleted file]
Documentation/devicetree/bindings/power/rockchip-io-domain.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/reset/qcom,aoss-reset.yaml
Documentation/devicetree/bindings/reset/qcom,pdc-global.yaml
Documentation/devicetree/bindings/reset/renesas,rzg2l-usbphy-ctrl.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/reset/socionext,uniphier-glue-reset.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/reset/uniphier-reset.txt [deleted file]
Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt [deleted file]
Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml
Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml
Documentation/devicetree/bindings/soc/rockchip/grf.yaml
Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml
MAINTAINERS
arch/arm/boot/dts/imx6q.dtsi
arch/arm/boot/dts/imx6qdl.dtsi
arch/arm/mach-omap2/pm34xx.c
arch/arm/mach-tegra/pm.c
arch/arm/mach-tegra/pm.h
arch/arm/mach-tegra/tegra.c
drivers/ata/pata_ixp4xx_cf.c
drivers/bus/Kconfig
drivers/bus/Makefile
drivers/bus/intel-ixp4xx-eb.c [new file with mode: 0644]
drivers/bus/ti-sysc.c
drivers/clocksource/timer-ixp4xx.c
drivers/dma/imx-sdma.c
drivers/firmware/Kconfig
drivers/firmware/Makefile
drivers/firmware/arm_scmi/Kconfig [new file with mode: 0644]
drivers/firmware/arm_scmi/Makefile
drivers/firmware/arm_scmi/common.h
drivers/firmware/arm_scmi/driver.c
drivers/firmware/arm_scmi/mailbox.c
drivers/firmware/arm_scmi/msg.c [new file with mode: 0644]
drivers/firmware/arm_scmi/smc.c
drivers/firmware/arm_scmi/virtio.c [new file with mode: 0644]
drivers/firmware/qcom_scm.c
drivers/firmware/tegra/bpmp-debugfs.c
drivers/iommu/Kconfig
drivers/memory/omap-gpmc.c
drivers/memory/tegra/tegra186.c
drivers/net/wireless/ath/ath10k/Kconfig
drivers/reset/Kconfig
drivers/reset/Makefile
drivers/reset/reset-qcom-pdc.c
drivers/reset/reset-rzg2l-usbphy-ctrl.c [new file with mode: 0644]
drivers/soc/mediatek/mt8173-pm-domains.h
drivers/soc/mediatek/mt8183-mmsys.h
drivers/soc/mediatek/mt8365-mmsys.h [new file with mode: 0644]
drivers/soc/mediatek/mtk-mmsys.c
drivers/soc/mediatek/mtk-mmsys.h
drivers/soc/mediatek/mtk-pm-domains.h
drivers/soc/qcom/cpr.c
drivers/soc/qcom/mdt_loader.c
drivers/soc/qcom/qcom-geni-se.c
drivers/soc/qcom/qcom_aoss.c
drivers/soc/qcom/rpmhpd.c
drivers/soc/qcom/rpmpd.c
drivers/soc/qcom/smd-rpm.c
drivers/soc/qcom/smsm.c
drivers/soc/qcom/socinfo.c
drivers/soc/renesas/Kconfig
drivers/soc/renesas/r8a779a0-sysc.c
drivers/soc/renesas/rcar-sysc.c
drivers/soc/renesas/renesas-soc.c
drivers/soc/rockchip/Kconfig
drivers/soc/rockchip/io-domain.c
drivers/soc/tegra/fuse/fuse-tegra.c
drivers/soc/tegra/fuse/fuse-tegra20.c
drivers/soc/tegra/fuse/fuse-tegra30.c
drivers/soc/tegra/fuse/fuse.h
drivers/soc/tegra/pmc.c
drivers/soc/tegra/powergate-bpmp.c
drivers/soc/ti/pruss.c
drivers/soc/ti/smartreflex.c
drivers/spi/spi-imx.c
drivers/watchdog/Kconfig
drivers/watchdog/ixp4xx_wdt.c
include/dt-bindings/power/qcom-rpmpd.h
include/dt-bindings/reset/qcom,sdm845-pdc.h
include/linux/omap-gpmc.h
include/linux/platform_data/pata_ixp4xx_cf.h
include/linux/power/smartreflex.h
include/linux/qcom-geni-se.h
include/soc/tegra/pm.h
include/uapi/linux/virtio_ids.h
include/uapi/linux/virtio_scmi.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/ata/intel,ixp4xx-compact-flash.yaml b/Documentation/devicetree/bindings/ata/intel,ixp4xx-compact-flash.yaml
new file mode 100644 (file)
index 0000000..52e1860
--- /dev/null
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/ata/intel,ixp4xx-compact-flash.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel IXP4xx CompactFlash Card Controller
+
+maintainers:
+  - Linus Walleij <linus.walleij@linaro.org>
+
+description: |
+  The IXP4xx network processors have a CompactFlash interface that presents
+  a CompactFlash card to the system as a true IDE (parallel ATA) device. The
+  device is always connected to the expansion bus of the IXP4xx SoCs using one
+  or two chip select areas and address translating logic on the board. The
+  node must be placed inside a chip select node on the IXP4xx expansion bus.
+
+properties:
+  compatible:
+    const: intel,ixp4xx-compact-flash
+
+  reg:
+    items:
+      - description: Command interface registers
+      - description: Control interface registers
+
+  interrupts:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+allOf:
+  - $ref: pata-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    bus@c4000000 {
+      compatible = "intel,ixp43x-expansion-bus-controller", "syscon";
+      reg = <0xc4000000 0x1000>;
+      native-endian;
+      #address-cells = <2>;
+      #size-cells = <1>;
+      ranges = <0 0x0 0x50000000 0x01000000>, <1 0x0 0x51000000 0x01000000>;
+      dma-ranges = <0 0x0 0x50000000 0x01000000>, <1 0x0 0x51000000 0x01000000>;
+      ide@1,0 {
+        compatible = "intel,ixp4xx-compact-flash";
+        reg = <1 0x00000000 0x1000>, <1 0x00040000 0x1000>;
+        interrupt-parent = <&gpio0>;
+        interrupts = <12 IRQ_TYPE_EDGE_RISING>;
+      };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml b/Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml
new file mode 100644 (file)
index 0000000..5fb4e7b
--- /dev/null
@@ -0,0 +1,168 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/bus/intel,ixp4xx-expansion-bus-controller.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel IXP4xx Expansion Bus Controller
+
+description: |
+  The IXP4xx expansion bus controller handles access to devices on the
+  memory-mapped expansion bus on the Intel IXP4xx family of system on chips,
+  including IXP42x, IXP43x, IXP45x and IXP46x.
+
+maintainers:
+  - Linus Walleij <linus.walleij@linaro.org>
+
+properties:
+  $nodename:
+    pattern: '^bus@[0-9a-f]+$'
+
+  compatible:
+    items:
+      - enum:
+          - intel,ixp42x-expansion-bus-controller
+          - intel,ixp43x-expansion-bus-controller
+          - intel,ixp45x-expansion-bus-controller
+          - intel,ixp46x-expansion-bus-controller
+      - const: syscon
+
+  reg:
+    description: Control registers for the expansion bus, these are not
+      inside the memory range handled by the expansion bus.
+    maxItems: 1
+
+  native-endian:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description: The IXP4xx has a peculiar MMIO access scheme, as it changes
+      the access pattern for words (swizzling) on the bus depending on whether
+      the SoC is running in big-endian or little-endian mode. Thus the
+      registers must always be accessed using native endianness.
+
+  "#address-cells":
+    description: |
+      The first cell is the chip select number.
+      The second cell is the address offset within the bank.
+    const: 2
+
+  "#size-cells":
+    const: 1
+
+  ranges: true
+  dma-ranges: true
+
+patternProperties:
+  "^.*@[0-7],[0-9a-f]+$":
+    description: Devices attached to chip selects are represented as
+      subnodes.
+    type: object
+
+    properties:
+      intel,ixp4xx-eb-t1:
+        description: Address timing, extend address phase with n cycles.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        maximum: 3
+
+      intel,ixp4xx-eb-t2:
+        description: Setup chip select timing, extend setup phase with n cycles.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        maximum: 3
+
+      intel,ixp4xx-eb-t3:
+        description: Strobe timing, extend strobe phase with n cycles.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        maximum: 15
+
+      intel,ixp4xx-eb-t4:
+        description: Hold timing, extend hold phase with n cycles.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        maximum: 3
+
+      intel,ixp4xx-eb-t5:
+        description: Recovery timing, extend recovery phase with n cycles.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        maximum: 15
+
+      intel,ixp4xx-eb-cycle-type:
+        description: The type of cycles to use on the expansion bus for this
+          chip select. 0 = Intel cycles, 1 = Motorola cycles, 2 = HPI cycles.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2]
+
+      intel,ixp4xx-eb-byte-access-on-halfword:
+        description: Allow byte read access on half word devices.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]
+
+      intel,ixp4xx-eb-hpi-hrdy-pol-high:
+        description: Set HPI HRDY polarity to active high when using HPI.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]
+
+      intel,ixp4xx-eb-mux-address-and-data:
+        description: Multiplex address and data on the data bus.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]
+
+      intel,ixp4xx-eb-ahb-split-transfers:
+        description: Enable AHB split transfers.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]
+
+      intel,ixp4xx-eb-write-enable:
+        description: Enable write cycles.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]
+
+      intel,ixp4xx-eb-byte-access:
+        description: Expansion bus uses only 8 bits. The default is to use
+          16 bits.
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]
+
+required:
+  - compatible
+  - reg
+  - native-endian
+  - "#address-cells"
+  - "#size-cells"
+  - ranges
+  - dma-ranges
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    bus@50000000 {
+        compatible = "intel,ixp42x-expansion-bus-controller", "syscon";
+        reg = <0xc4000000 0x28>;
+        native-endian;
+        #address-cells = <2>;
+        #size-cells = <1>;
+        ranges = <0 0x0 0x50000000 0x01000000>,
+                 <1 0x0 0x51000000 0x01000000>;
+        dma-ranges = <0 0x0 0x50000000 0x01000000>,
+                     <1 0x0 0x51000000 0x01000000>;
+        flash@0,0 {
+            compatible = "intel,ixp4xx-flash", "cfi-flash";
+            bank-width = <2>;
+            reg = <0 0x00000000 0x1000000>;
+            intel,ixp4xx-eb-t3 = <3>;
+            intel,ixp4xx-eb-cycle-type = <0>;
+            intel,ixp4xx-eb-byte-access-on-halfword = <1>;
+            intel,ixp4xx-eb-write-enable = <1>;
+            intel,ixp4xx-eb-byte-access = <0>;
+        };
+        serial@1,0 {
+            compatible = "exar,xr16l2551", "ns8250";
+            reg = <1 0x00000000 0x10>;
+            interrupt-parent = <&gpio0>;
+            interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
+            clock-frequency = <1843200>;
+            intel,ixp4xx-eb-t3 = <3>;
+            intel,ixp4xx-eb-cycle-type = <1>;
+            intel,ixp4xx-eb-write-enable = <1>;
+            intel,ixp4xx-eb-byte-access = <1>;
+        };
+    };
index c9e9740..12c316f 100644 (file)
@@ -9,6 +9,7 @@ Required properties:
       "fsl,imx53-sdma"
       "fsl,imx6q-sdma"
       "fsl,imx7d-sdma"
+      "fsl,imx6ul-sdma"
       "fsl,imx8mq-sdma"
       "fsl,imx8mm-sdma"
       "fsl,imx8mn-sdma"
index cebf6ff..5c4c678 100644 (file)
@@ -34,6 +34,10 @@ properties:
       - description: SCMI compliant firmware with ARM SMC/HVC transport
         items:
           - const: arm,scmi-smc
+      - description: SCMI compliant firmware with SCMI Virtio transport.
+                     The virtio transport only supports a single device.
+        items:
+          - const: arm,scmi-virtio
 
   interrupts:
     description:
@@ -172,6 +176,7 @@ patternProperties:
       Each sub-node represents a protocol supported. If the platform
       supports a dedicated communication channel for a particular protocol,
       then the corresponding transport properties must be present.
+      The virtio transport does not support a dedicated communication channel.
 
     properties:
       reg:
@@ -195,7 +200,6 @@ patternProperties:
 
 required:
   - compatible
-  - shmem
 
 if:
   properties:
@@ -209,6 +213,7 @@ then:
 
   required:
     - mboxes
+    - shmem
 
 else:
   if:
@@ -219,6 +224,7 @@ else:
   then:
     required:
       - arm,smc-id
+      - shmem
 
 examples:
   - |
index 4807b56..239f378 100644 (file)
@@ -30,6 +30,7 @@ properties:
       - qcom,sc8180x-rpmhpd
       - qcom,sdm845-rpmhpd
       - qcom,sdx55-rpmhpd
+      - qcom,sm6115-rpmpd
       - qcom,sm8150-rpmhpd
       - qcom,sm8250-rpmhpd
       - qcom,sm8350-rpmhpd
diff --git a/Documentation/devicetree/bindings/power/rockchip-io-domain.txt b/Documentation/devicetree/bindings/power/rockchip-io-domain.txt
deleted file mode 100644 (file)
index e66fd4e..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-Rockchip SRAM for IO Voltage Domains:
--------------------------------------
-
-IO domain voltages on some Rockchip SoCs are variable but need to be
-kept in sync between the regulators and the SoC using a special
-register.
-
-A specific example using rk3288:
-- If the regulator hooked up to a pin like SDMMC0_VDD is 3.3V then
-  bit 7 of GRF_IO_VSEL needs to be 0.  If the regulator hooked up to
-  that same pin is 1.8V then bit 7 of GRF_IO_VSEL needs to be 1.
-
-Said another way, this driver simply handles keeping bits in the SoC's
-general register file (GRF) in sync with the actual value of a voltage
-hooked up to the pins.
-
-Note that this driver specifically doesn't include:
-- any logic for deciding what voltage we should set regulators to
-- any logic for deciding whether regulators (or internal SoC blocks)
-  should have power or not have power
-
-If there were some other software that had the smarts of making
-decisions about regulators, it would work in conjunction with this
-driver.  When that other software adjusted a regulator's voltage then
-this driver would handle telling the SoC about it.  A good example is
-vqmmc for SD.  In that case the dw_mmc driver simply is told about a
-regulator.  It changes the regulator between 3.3V and 1.8V at the
-right time.  This driver notices the change and makes sure that the
-SoC is on the same page.
-
-
-Required properties:
-- compatible: should be one of:
-  - "rockchip,px30-io-voltage-domain" for px30
-  - "rockchip,px30-pmu-io-voltage-domain" for px30 pmu-domains
-  - "rockchip,rk3188-io-voltage-domain" for rk3188
-  - "rockchip,rk3228-io-voltage-domain" for rk3228
-  - "rockchip,rk3288-io-voltage-domain" for rk3288
-  - "rockchip,rk3328-io-voltage-domain" for rk3328
-  - "rockchip,rk3368-io-voltage-domain" for rk3368
-  - "rockchip,rk3368-pmu-io-voltage-domain" for rk3368 pmu-domains
-  - "rockchip,rk3399-io-voltage-domain" for rk3399
-  - "rockchip,rk3399-pmu-io-voltage-domain" for rk3399 pmu-domains
-  - "rockchip,rv1108-io-voltage-domain" for rv1108
-  - "rockchip,rv1108-pmu-io-voltage-domain" for rv1108 pmu-domains
-
-Deprecated properties:
-- rockchip,grf: phandle to the syscon managing the "general register files"
-    Systems should move the io-domains to a sub-node of the grf simple-mfd.
-
-You specify supplies using the standard regulator bindings by including
-a phandle the relevant regulator.  All specified supplies must be able
-to report their voltage.  The IO Voltage Domain for any non-specified
-supplies will be not be touched.
-
-Possible supplies for PX30:
-- vccio6-supply: The supply connected to VCCIO6.
-- vccio1-supply: The supply connected to VCCIO1.
-- vccio2-supply: The supply connected to VCCIO2.
-- vccio3-supply: The supply connected to VCCIO3.
-- vccio4-supply: The supply connected to VCCIO4.
-- vccio5-supply: The supply connected to VCCIO5.
-- vccio-oscgpi-supply: The supply connected to VCCIO_OSCGPI.
-
-Possible supplies for PX30 pmu-domains:
-- pmuio1-supply: The supply connected to PMUIO1.
-- pmuio2-supply: The supply connected to PMUIO2.
-
-Possible supplies for rk3188:
-- ap0-supply:    The supply connected to AP0_VCC.
-- ap1-supply:    The supply connected to AP1_VCC.
-- cif-supply:    The supply connected to CIF_VCC.
-- flash-supply:  The supply connected to FLASH_VCC.
-- lcdc0-supply:  The supply connected to LCD0_VCC.
-- lcdc1-supply:  The supply connected to LCD1_VCC.
-- vccio0-supply: The supply connected to VCCIO0.
-- vccio1-supply: The supply connected to VCCIO1.
-                 Sometimes also labeled VCCIO1 and VCCIO2.
-
-Possible supplies for rk3228:
-- vccio1-supply: The supply connected to VCCIO1.
-- vccio2-supply: The supply connected to VCCIO2.
-- vccio3-supply: The supply connected to VCCIO3.
-- vccio4-supply: The supply connected to VCCIO4.
-
-Possible supplies for rk3288:
-- audio-supply:  The supply connected to APIO4_VDD.
-- bb-supply:     The supply connected to APIO5_VDD.
-- dvp-supply:    The supply connected to DVPIO_VDD.
-- flash0-supply: The supply connected to FLASH0_VDD.  Typically for eMMC
-- flash1-supply: The supply connected to FLASH1_VDD.  Also known as SDIO1.
-- gpio30-supply: The supply connected to APIO1_VDD.
-- gpio1830       The supply connected to APIO2_VDD.
-- lcdc-supply:   The supply connected to LCDC_VDD.
-- sdcard-supply: The supply connected to SDMMC0_VDD.
-- wifi-supply:   The supply connected to APIO3_VDD.  Also known as SDIO0.
-
-Possible supplies for rk3368:
-- audio-supply:  The supply connected to APIO3_VDD.
-- dvp-supply:    The supply connected to DVPIO_VDD.
-- flash0-supply: The supply connected to FLASH0_VDD.  Typically for eMMC
-- gpio30-supply: The supply connected to APIO1_VDD.
-- gpio1830       The supply connected to APIO4_VDD.
-- sdcard-supply: The supply connected to SDMMC0_VDD.
-- wifi-supply:   The supply connected to APIO2_VDD.  Also known as SDIO0.
-
-Possible supplies for rk3368 pmu-domains:
-- pmu-supply:    The supply connected to PMUIO_VDD.
-- vop-supply:    The supply connected to LCDC_VDD.
-
-Possible supplies for rk3399:
-- bt656-supply:  The supply connected to APIO2_VDD.
-- audio-supply:  The supply connected to APIO5_VDD.
-- sdmmc-supply:  The supply connected to SDMMC0_VDD.
-- gpio1830       The supply connected to APIO4_VDD.
-
-Possible supplies for rk3399 pmu-domains:
-- pmu1830-supply:The supply connected to PMUIO2_VDD.
-
-Example:
-
-       io-domains {
-               compatible = "rockchip,rk3288-io-voltage-domain";
-               rockchip,grf = <&grf>;
-
-               audio-supply = <&vcc18_codec>;
-               bb-supply = <&vcc33_io>;
-               dvp-supply = <&vcc_18>;
-               flash0-supply = <&vcc18_flashio>;
-               gpio1830-supply = <&vcc33_io>;
-               gpio30-supply = <&vcc33_pmuio>;
-               lcdc-supply = <&vcc33_lcd>;
-               sdcard-supply = <&vccio_sd>;
-               wifi-supply = <&vcc18_wl>;
-       };
diff --git a/Documentation/devicetree/bindings/power/rockchip-io-domain.yaml b/Documentation/devicetree/bindings/power/rockchip-io-domain.yaml
new file mode 100644 (file)
index 0000000..1727bf1
--- /dev/null
@@ -0,0 +1,360 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/rockchip-io-domain.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip SRAM for IO Voltage Domains
+
+maintainers:
+  - Heiko Stuebner <heiko@sntech.de>
+
+description: |
+  IO domain voltages on some Rockchip SoCs are variable but need to be
+  kept in sync between the regulators and the SoC using a special
+  register.
+
+  A specific example using rk3288
+    If the regulator hooked up to a pin like SDMMC0_VDD is 3.3V then
+    bit 7 of GRF_IO_VSEL needs to be 0. If the regulator hooked up to
+    that same pin is 1.8V then bit 7 of GRF_IO_VSEL needs to be 1.
+
+  Said another way, this driver simply handles keeping bits in the SoCs
+  General Register File (GRF) in sync with the actual value of a voltage
+  hooked up to the pins.
+
+  Note that this driver specifically does not include
+    any logic for deciding what voltage we should set regulators to
+    any logic for deciding whether regulators (or internal SoC blocks)
+    should have power or not have power
+
+  If there were some other software that had the smarts of making
+  decisions about regulators, it would work in conjunction with this
+  driver. When that other software adjusted a regulators voltage then
+  this driver would handle telling the SoC about it. A good example is
+  vqmmc for SD. In that case the dw_mmc driver simply is told about a
+  regulator. It changes the regulator between 3.3V and 1.8V at the
+  right time. This driver notices the change and makes sure that the
+  SoC is on the same page.
+
+  You specify supplies using the standard regulator bindings by including
+  a phandle the relevant regulator. All specified supplies must be able
+  to report their voltage. The IO Voltage Domain for any non-specified
+  supplies will be not be touched.
+
+properties:
+  compatible:
+    enum:
+      - rockchip,px30-io-voltage-domain
+      - rockchip,px30-pmu-io-voltage-domain
+      - rockchip,rk3188-io-voltage-domain
+      - rockchip,rk3228-io-voltage-domain
+      - rockchip,rk3288-io-voltage-domain
+      - rockchip,rk3328-io-voltage-domain
+      - rockchip,rk3368-io-voltage-domain
+      - rockchip,rk3368-pmu-io-voltage-domain
+      - rockchip,rk3399-io-voltage-domain
+      - rockchip,rk3399-pmu-io-voltage-domain
+      - rockchip,rk3568-pmu-io-voltage-domain
+      - rockchip,rv1108-io-voltage-domain
+      - rockchip,rv1108-pmu-io-voltage-domain
+
+required:
+  - compatible
+
+unevaluatedProperties: false
+
+allOf:
+  - $ref: "#/$defs/px30"
+  - $ref: "#/$defs/px30-pmu"
+  - $ref: "#/$defs/rk3188"
+  - $ref: "#/$defs/rk3228"
+  - $ref: "#/$defs/rk3288"
+  - $ref: "#/$defs/rk3328"
+  - $ref: "#/$defs/rk3368"
+  - $ref: "#/$defs/rk3368-pmu"
+  - $ref: "#/$defs/rk3399"
+  - $ref: "#/$defs/rk3399-pmu"
+  - $ref: "#/$defs/rk3568-pmu"
+  - $ref: "#/$defs/rv1108"
+  - $ref: "#/$defs/rv1108-pmu"
+
+$defs:
+  px30:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,px30-io-voltage-domain
+
+    then:
+      properties:
+        vccio1-supply:
+          description: The supply connected to VCCIO1.
+        vccio2-supply:
+          description: The supply connected to VCCIO2.
+        vccio3-supply:
+          description: The supply connected to VCCIO3.
+        vccio4-supply:
+          description: The supply connected to VCCIO4.
+        vccio5-supply:
+          description: The supply connected to VCCIO5.
+        vccio6-supply:
+          description: The supply connected to VCCIO6.
+        vccio-oscgpi-supply:
+          description: The supply connected to VCCIO_OSCGPI.
+
+  px30-pmu:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,px30-pmu-io-voltage-domain
+
+    then:
+      properties:
+        pmuio1-supply:
+          description: The supply connected to PMUIO1.
+        pmuio2-supply:
+          description: The supply connected to PMUIO2.
+
+  rk3188:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rk3188-io-voltage-domain
+
+    then:
+      properties:
+        ap0-supply:
+          description: The supply connected to AP0_VCC.
+        ap1-supply:
+          description: The supply connected to AP1_VCC.
+        cif-supply:
+          description: The supply connected to CIF_VCC.
+        flash-supply:
+          description: The supply connected to FLASH_VCC.
+        lcdc0-supply:
+          description: The supply connected to LCD0_VCC.
+        lcdc1-supply:
+          description: The supply connected to LCD1_VCC.
+        vccio0-supply:
+          description: The supply connected to VCCIO0.
+        vccio1-supply:
+          description: The supply connected to VCCIO1. Also labeled as VCCIO2.
+
+  rk3228:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rk3228-io-voltage-domain
+
+    then:
+      properties:
+        vccio1-supply:
+          description: The supply connected to VCCIO1.
+        vccio2-supply:
+          description: The supply connected to VCCIO2.
+        vccio3-supply:
+          description: The supply connected to VCCIO3.
+        vccio4-supply:
+          description: The supply connected to VCCIO4.
+
+  rk3288:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rk3288-io-voltage-domain
+
+    then:
+      properties:
+        audio-supply:
+          description: The supply connected to APIO4_VDD.
+        bb-supply:
+          description: The supply connected to APIO5_VDD.
+        dvp-supply:
+          description: The supply connected to DVPIO_VDD.
+        flash0-supply:
+          description: The supply connected to FLASH0_VDD. Typically for eMMC.
+        flash1-supply:
+          description: The supply connected to FLASH1_VDD. Also known as SDIO1.
+        gpio30-supply:
+          description: The supply connected to APIO1_VDD.
+        gpio1830-supply:
+          description: The supply connected to APIO2_VDD.
+        lcdc-supply:
+          description: The supply connected to LCDC_VDD.
+        sdcard-supply:
+          description: The supply connected to SDMMC0_VDD.
+        wifi-supply:
+          description: The supply connected to APIO3_VDD. Also known as SDIO0.
+
+  rk3328:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rk3328-io-voltage-domain
+
+    then:
+      properties:
+        vccio1-supply:
+          description: The supply connected to VCCIO1.
+        vccio2-supply:
+          description: The supply connected to VCCIO2.
+        vccio3-supply:
+          description: The supply connected to VCCIO3.
+        vccio4-supply:
+          description: The supply connected to VCCIO4.
+        vccio5-supply:
+          description: The supply connected to VCCIO5.
+        vccio6-supply:
+          description: The supply connected to VCCIO6.
+        pmuio-supply:
+          description: The supply connected to VCCIO_PMU.
+
+  rk3368:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rk3368-io-voltage-domain
+
+    then:
+      properties:
+        audio-supply:
+          description: The supply connected to APIO3_VDD.
+        dvp-supply:
+          description: The supply connected to DVPIO_VDD.
+        flash0-supply:
+          description: The supply connected to FLASH0_VDD. Typically for eMMC.
+        gpio30-supply:
+          description: The supply connected to APIO1_VDD.
+        gpio1830-supply:
+          description: The supply connected to APIO4_VDD.
+        sdcard-supply:
+          description: The supply connected to SDMMC0_VDD.
+        wifi-supply:
+          description: The supply connected to APIO2_VDD. Also known as SDIO0.
+
+  rk3368-pmu:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rk3368-pmu-io-voltage-domain
+
+    then:
+      properties:
+        pmu-supply:
+          description: The supply connected to PMUIO_VDD.
+        vop-supply:
+          description: The supply connected to LCDC_VDD.
+
+  rk3399:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rk3399-io-voltage-domain
+
+    then:
+      properties:
+        audio-supply:
+          description: The supply connected to APIO5_VDD.
+        bt656-supply:
+          description: The supply connected to APIO2_VDD.
+        gpio1830-supply:
+          description: The supply connected to APIO4_VDD.
+        sdmmc-supply:
+          description: The supply connected to SDMMC0_VDD.
+
+  rk3399-pmu:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rk3399-pmu-io-voltage-domain
+
+    then:
+      properties:
+        pmu1830-supply:
+          description: The supply connected to PMUIO2_VDD.
+
+  rk3568-pmu:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rk3568-pmu-io-voltage-domain
+
+    then:
+      properties:
+        pmuio1-supply:
+          description: The supply connected to PMUIO1.
+        pmuio2-supply:
+          description: The supply connected to PMUIO2.
+        vccio1-supply:
+          description: The supply connected to VCCIO1.
+        vccio2-supply:
+          description: The supply connected to VCCIO2.
+        vccio3-supply:
+          description: The supply connected to VCCIO3.
+        vccio4-supply:
+          description: The supply connected to VCCIO4.
+        vccio5-supply:
+          description: The supply connected to VCCIO5.
+        vccio6-supply:
+          description: The supply connected to VCCIO6.
+        vccio7-supply:
+          description: The supply connected to VCCIO7.
+
+  rv1108:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rv1108-io-voltage-domain
+
+    then:
+      properties:
+        vccio1-supply:
+          description: The supply connected to APIO1_VDD.
+        vccio2-supply:
+          description: The supply connected to APIO2_VDD.
+        vccio3-supply:
+          description: The supply connected to APIO3_VDD.
+        vccio5-supply:
+          description: The supply connected to APIO5_VDD.
+        vccio6-supply:
+          description: The supply connected to APIO6_VDD.
+
+  rv1108-pmu:
+    if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rv1108-pmu-io-voltage-domain
+
+    then:
+      properties:
+        pmu-supply:
+          description: The supply connected to PMUIO_VDD.
+
+examples:
+  - |
+    io-domains {
+      compatible = "rockchip,rk3288-io-voltage-domain";
+      audio-supply = <&vcc18_codec>;
+      bb-supply = <&vcc33_io>;
+      dvp-supply = <&vcc_18>;
+      flash0-supply = <&vcc18_flashio>;
+      gpio1830-supply = <&vcc33_io>;
+      gpio30-supply = <&vcc33_pmuio>;
+      lcdc-supply = <&vcc33_lcd>;
+      sdcard-supply = <&vccio_sd>;
+      wifi-supply = <&vcc18_wl>;
+    };
index e2d85a1..a054757 100644 (file)
@@ -21,6 +21,11 @@ properties:
           - const: "qcom,sc7180-aoss-cc"
           - const: "qcom,sdm845-aoss-cc"
 
+      - description: on SC7280 SoCs the following compatibles must be specified
+        items:
+          - const: "qcom,sc7280-aoss-cc"
+          - const: "qcom,sdm845-aoss-cc"
+
       - description: on SDM845 SoCs the following compatibles must be specified
         items:
           - const: "qcom,sdm845-aoss-cc"
index d7d8cec..831ea8d 100644 (file)
@@ -21,6 +21,10 @@ properties:
           - const: "qcom,sc7180-pdc-global"
           - const: "qcom,sdm845-pdc-global"
 
+      - description: on SC7280 SoCs the following compatibles must be specified
+        items:
+          - const: "qcom,sc7280-pdc-global"
+
       - description: on SDM845 SoCs the following compatibles must be specified
         items:
           - const: "qcom,sdm845-pdc-global"
diff --git a/Documentation/devicetree/bindings/reset/renesas,rzg2l-usbphy-ctrl.yaml b/Documentation/devicetree/bindings/reset/renesas,rzg2l-usbphy-ctrl.yaml
new file mode 100644 (file)
index 0000000..b13514e
--- /dev/null
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/reset/renesas,rzg2l-usbphy-ctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas RZ/G2L USBPHY Control
+
+maintainers:
+  - Biju Das <biju.das.jz@bp.renesas.com>
+
+description:
+  The RZ/G2L USBPHY Control mainly controls reset and power down of the
+  USB/PHY.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - renesas,r9a07g044-usbphy-ctrl # RZ/G2{L,LC}
+      - const: renesas,rzg2l-usbphy-ctrl
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+  '#reset-cells':
+    const: 1
+    description: |
+      The phandle's argument in the reset specifier is the PHY reset associated
+      with the USB port.
+      0 = Port 1 Phy reset
+      1 = Port 2 Phy reset
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - resets
+  - power-domains
+  - '#reset-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/r9a07g044-cpg.h>
+
+    phyrst: usbphy-ctrl@11c40000 {
+        compatible = "renesas,r9a07g044-usbphy-ctrl",
+                     "renesas,rzg2l-usbphy-ctrl";
+        reg = <0x11c40000 0x10000>;
+        clocks = <&cpg CPG_MOD R9A07G044_USB_PCLK>;
+        resets = <&cpg R9A07G044_USB_PRESETN>;
+        power-domains = <&cpg>;
+        #reset-cells = <1>;
+    };
diff --git a/Documentation/devicetree/bindings/reset/socionext,uniphier-glue-reset.yaml b/Documentation/devicetree/bindings/reset/socionext,uniphier-glue-reset.yaml
new file mode 100644 (file)
index 0000000..29e4a90
--- /dev/null
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/reset/socionext,uniphier-glue-reset.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Socionext UniPhier peripheral core reset in glue layer
+
+description: |
+  Some peripheral core reset belongs to its own glue layer. Before using
+  this core reset, it is necessary to control the clocks and resets to
+  enable this layer. These clocks and resets should be described in each
+  property.
+
+maintainers:
+  - Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
+
+properties:
+  compatible:
+    enum:
+      - socionext,uniphier-pro4-usb3-reset
+      - socionext,uniphier-pro5-usb3-reset
+      - socionext,uniphier-pxs2-usb3-reset
+      - socionext,uniphier-ld20-usb3-reset
+      - socionext,uniphier-pxs3-usb3-reset
+      - socionext,uniphier-pro4-ahci-reset
+      - socionext,uniphier-pxs2-ahci-reset
+      - socionext,uniphier-pxs3-ahci-reset
+
+  reg:
+    maxItems: 1
+
+  "#reset-cells":
+    const: 1
+
+  clocks:
+    minItems: 1
+    maxItems: 2
+
+  clock-names:
+    oneOf:
+      - items:           # for Pro4, Pro5
+          - const: gio
+          - const: link
+      - items:           # for others
+          - const: link
+
+  resets:
+    minItems: 1
+    maxItems: 2
+
+  reset-names:
+    oneOf:
+      - items:           # for Pro4, Pro5
+          - const: gio
+          - const: link
+      - items:           # for others
+          - const: link
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - "#reset-cells"
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+
+examples:
+  - |
+    usb-glue@65b00000 {
+        compatible = "simple-mfd";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0 0x65b00000 0x400>;
+
+        usb_rst: reset@0 {
+            compatible = "socionext,uniphier-ld20-usb3-reset";
+            reg = <0x0 0x4>;
+            #reset-cells = <1>;
+            clock-names = "link";
+            clocks = <&sys_clk 14>;
+            reset-names = "link";
+            resets = <&sys_rst 14>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/reset/uniphier-reset.txt b/Documentation/devicetree/bindings/reset/uniphier-reset.txt
deleted file mode 100644 (file)
index 88e06e5..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-UniPhier glue reset controller
-
-
-Peripheral core reset in glue layer
------------------------------------
-
-Some peripheral core reset belongs to its own glue layer. Before using
-this core reset, it is necessary to control the clocks and resets to enable
-this layer. These clocks and resets should be described in each property.
-
-Required properties:
-- compatible: Should be
-    "socionext,uniphier-pro4-usb3-reset" - for Pro4 SoC USB3
-    "socionext,uniphier-pro5-usb3-reset" - for Pro5 SoC USB3
-    "socionext,uniphier-pxs2-usb3-reset" - for PXs2 SoC USB3
-    "socionext,uniphier-ld20-usb3-reset" - for LD20 SoC USB3
-    "socionext,uniphier-pxs3-usb3-reset" - for PXs3 SoC USB3
-    "socionext,uniphier-pro4-ahci-reset" - for Pro4 SoC AHCI
-    "socionext,uniphier-pxs2-ahci-reset" - for PXs2 SoC AHCI
-    "socionext,uniphier-pxs3-ahci-reset" - for PXs3 SoC AHCI
-- #reset-cells: Should be 1.
-- reg: Specifies offset and length of the register set for the device.
-- clocks: A list of phandles to the clock gate for the glue layer.
-       According to the clock-names, appropriate clocks are required.
-- clock-names: Should contain
-    "gio", "link" - for Pro4 and Pro5 SoCs
-    "link"        - for others
-- resets: A list of phandles to the reset control for the glue layer.
-       According to the reset-names, appropriate resets are required.
-- reset-names: Should contain
-    "gio", "link" - for Pro4 and Pro5 SoCs
-    "link"        - for others
-
-Example:
-
-       usb-glue@65b00000 {
-               compatible = "socionext,uniphier-ld20-dwc3-glue",
-                            "simple-mfd";
-               #address-cells = <1>;
-               #size-cells = <1>;
-               ranges = <0 0x65b00000 0x400>;
-
-               usb_rst: reset@0 {
-                       compatible = "socionext,uniphier-ld20-usb3-reset";
-                       reg = <0x0 0x4>;
-                       #reset-cells = <1>;
-                       clock-names = "link";
-                       clocks = <&sys_clk 14>;
-                       reset-names = "link";
-                       resets = <&sys_rst 14>;
-               };
-
-               regulator {
-                       ...
-               };
-
-               phy {
-                       ...
-               };
-               ...
-       };
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt
deleted file mode 100644 (file)
index 783dc81..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-Qualcomm Always-On Subsystem side channel binding
-
-This binding describes the hardware component responsible for side channel
-requests to the always-on subsystem (AOSS), used for certain power management
-requests that is not handled by the standard RPMh interface. Each client in the
-SoC has it's own block of message RAM and IRQ for communication with the AOSS.
-The protocol used to communicate in the message RAM is known as Qualcomm
-Messaging Protocol (QMP)
-
-The AOSS side channel exposes control over a set of resources, used to control
-a set of debug related clocks and to affect the low power state of resources
-related to the secondary subsystems. These resources are exposed as a set of
-power-domains.
-
-- compatible:
-       Usage: required
-       Value type: <string>
-       Definition: must be one of:
-                   "qcom,sc7180-aoss-qmp"
-                   "qcom,sc7280-aoss-qmp"
-                   "qcom,sdm845-aoss-qmp"
-                   "qcom,sm8150-aoss-qmp"
-                   "qcom,sm8250-aoss-qmp"
-                   "qcom,sm8350-aoss-qmp"
-
-- reg:
-       Usage: required
-       Value type: <prop-encoded-array>
-       Definition: the base address and size of the message RAM for this
-                   client's communication with the AOSS
-
-- interrupts:
-       Usage: required
-       Value type: <prop-encoded-array>
-       Definition: should specify the AOSS message IRQ for this client
-
-- mboxes:
-       Usage: required
-       Value type: <prop-encoded-array>
-       Definition: reference to the mailbox representing the outgoing doorbell
-                   in APCS for this client, as described in mailbox/mailbox.txt
-
-- #clock-cells:
-       Usage: optional
-       Value type: <u32>
-       Definition: must be 0
-                   The single clock represents the QDSS clock.
-
-- #power-domain-cells:
-       Usage: optional
-       Value type: <u32>
-       Definition: must be 1
-                   The provided power-domains are:
-                   CDSP state (0), LPASS state (1), modem state (2), SLPI
-                   state (3), SPSS state (4) and Venus state (5).
-
-= SUBNODES
-The AOSS side channel also provides the controls for three cooling devices,
-these are expressed as subnodes of the QMP node. The name of the node is used
-to identify the resource and must therefor be "cx", "mx" or "ebi".
-
-- #cooling-cells:
-       Usage: optional
-       Value type: <u32>
-       Definition: must be 2
-
-= EXAMPLE
-
-The following example represents the AOSS side-channel message RAM and the
-mechanism exposing the power-domains, as found in SDM845.
-
-  aoss_qmp: qmp@c300000 {
-         compatible = "qcom,sdm845-aoss-qmp";
-         reg = <0x0c300000 0x100000>;
-         interrupts = <GIC_SPI 389 IRQ_TYPE_EDGE_RISING>;
-         mboxes = <&apss_shared 0>;
-
-         #power-domain-cells = <1>;
-
-         cx_cdev: cx {
-               #cooling-cells = <2>;
-         };
-
-         mx_cdev: mx {
-               #cooling-cells = <2>;
-         };
-  };
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml
new file mode 100644 (file)
index 0000000..93e4b73
--- /dev/null
@@ -0,0 +1,114 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/qcom/qcom,aoss-qmp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Always-On Subsystem side channel binding
+
+maintainers:
+  - Bjorn Andersson <bjorn.andersson@linaro.org>
+
+description:
+  This binding describes the hardware component responsible for side channel
+  requests to the always-on subsystem (AOSS), used for certain power management
+  requests that is not handled by the standard RPMh interface. Each client in the
+  SoC has it's own block of message RAM and IRQ for communication with the AOSS.
+  The protocol used to communicate in the message RAM is known as Qualcomm
+  Messaging Protocol (QMP)
+
+  The AOSS side channel exposes control over a set of resources, used to control
+  a set of debug related clocks and to affect the low power state of resources
+  related to the secondary subsystems. These resources are exposed as a set of
+  power-domains.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - qcom,sc7180-aoss-qmp
+          - qcom,sc7280-aoss-qmp
+          - qcom,sc8180x-aoss-qmp
+          - qcom,sdm845-aoss-qmp
+          - qcom,sm8150-aoss-qmp
+          - qcom,sm8250-aoss-qmp
+          - qcom,sm8350-aoss-qmp
+      - const: qcom,aoss-qmp
+
+  reg:
+    maxItems: 1
+    description:
+      The base address and size of the message RAM for this client's
+      communication with the AOSS
+
+  interrupts:
+    maxItems: 1
+    description:
+      Should specify the AOSS message IRQ for this client
+
+  mboxes:
+    maxItems: 1
+    description:
+      Reference to the mailbox representing the outgoing doorbell in APCS for
+      this client, as described in mailbox/mailbox.txt
+
+  "#clock-cells":
+    const: 0
+    description:
+      The single clock represents the QDSS clock.
+
+  "#power-domain-cells":
+    const: 1
+    description: |
+        The provided power-domains are:
+        CDSP state (0), LPASS state (1), modem state (2), SLPI
+        state (3), SPSS state (4) and Venus state (5).
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - mboxes
+  - "#clock-cells"
+
+additionalProperties: false
+
+patternProperties:
+  "^(cx|mx|ebi)$":
+    type: object
+    description:
+      The AOSS side channel also provides the controls for three cooling devices,
+      these are expressed as subnodes of the QMP node. The name of the node is
+      used to identify the resource and must therefor be "cx", "mx" or "ebi".
+
+    properties:
+      "#cooling-cells":
+        const: 2
+
+    required:
+      - "#cooling-cells"
+
+    additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    aoss_qmp: qmp@c300000 {
+      compatible = "qcom,sdm845-aoss-qmp", "qcom,aoss-qmp";
+      reg = <0x0c300000 0x100000>;
+      interrupts = <GIC_SPI 389 IRQ_TYPE_EDGE_RISING>;
+      mboxes = <&apss_shared 0>;
+
+      #clock-cells = <0>;
+      #power-domain-cells = <1>;
+
+      cx_cdev: cx {
+        #cooling-cells = <2>;
+      };
+
+      mx_cdev: mx {
+        #cooling-cells = <2>;
+      };
+    };
+...
index 4663c2b..a776cd3 100644 (file)
@@ -51,6 +51,9 @@ properties:
   interconnect-names:
     const: qup-core
 
+  iommus:
+    maxItems: 1
+
 required:
   - compatible
   - reg
index d511f01..cc3fe5e 100644 (file)
@@ -39,6 +39,7 @@ properties:
       - qcom,rpm-msm8996
       - qcom,rpm-msm8998
       - qcom,rpm-sdm660
+      - qcom,rpm-sm6115
       - qcom,rpm-sm6125
       - qcom,rpm-qcs404
 
index 62fa72c..dfebf42 100644 (file)
@@ -15,7 +15,6 @@ properties:
       - items:
           - enum:
               - rockchip,rk3288-sgrf
-              - rockchip,rv1108-pmugrf
               - rockchip,rv1108-usbgrf
           - const: syscon
       - items:
@@ -41,6 +40,7 @@ properties:
               - rockchip,rk3568-grf
               - rockchip,rk3568-pmugrf
               - rockchip,rv1108-grf
+              - rockchip,rv1108-pmugrf
           - const: syscon
           - const: simple-mfd
 
@@ -198,21 +198,28 @@ allOf:
         compatible:
           contains:
             enum:
-              - rockchip,px30-pmugrf
               - rockchip,px30-grf
+              - rockchip,px30-pmugrf
+              - rockchip,rk3188-grf
               - rockchip,rk3228-grf
               - rockchip,rk3288-grf
               - rockchip,rk3328-grf
-              - rockchip,rk3368-pmugrf
               - rockchip,rk3368-grf
-              - rockchip,rk3399-pmugrf
+              - rockchip,rk3368-pmugrf
               - rockchip,rk3399-grf
+              - rockchip,rk3399-pmugrf
+              - rockchip,rk3568-pmugrf
+              - rockchip,rv1108-grf
+              - rockchip,rv1108-pmugrf
 
     then:
       properties:
         io-domains:
-          description:
-            Documentation/devicetree/bindings/power/rockchip-io-domain.txt
+          type: object
+
+          $ref: "/schemas/power/rockchip-io-domain.yaml#"
+
+          unevaluatedProperties: false
 
 examples:
   - |
index 9790617..9d128b9 100644 (file)
@@ -68,6 +68,7 @@ properties:
       - ti,k2g-pruss     # for 66AK2G SoC family
       - ti,am654-icssg   # for K3 AM65x SoC family
       - ti,j721e-icssg   # for K3 J721E SoC family
+      - ti,am642-icssg   # for K3 AM64x SoC family
 
   reg:
     maxItems: 1
@@ -84,6 +85,8 @@ properties:
   dma-ranges:
     maxItems: 1
 
+  dma-coherent: true
+
   power-domains:
     description: |
       This property is as per sci-pm-domain.txt.
@@ -231,8 +234,8 @@ patternProperties:
     description: |
       Industrial Ethernet Peripheral to manage/generate Industrial Ethernet
       functions such as time stamping. Each PRUSS has either 1 IEP (on AM335x,
-      AM437x, AM57xx & 66AK2G SoCs) or 2 IEPs (on K3 AM65x & J721E SoCs ). IEP
-      is used for creating PTP clocks and generating PPS signals.
+      AM437x, AM57xx & 66AK2G SoCs) or 2 IEPs (on K3 AM65x, J721E & AM64x SoCs).
+      IEP is used for creating PTP clocks and generating PPS signals.
 
     type: object
 
@@ -323,17 +326,29 @@ additionalProperties: false
 # - interrupt-controller
 # - pru
 
-if:
-  properties:
-    compatible:
-      contains:
-        enum:
-          - ti,k2g-pruss
-          - ti,am654-icssg
-          - ti,j721e-icssg
-then:
-  required:
-    - power-domains
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - ti,k2g-pruss
+              - ti,am654-icssg
+              - ti,j721e-icssg
+              - ti,am642-icssg
+    then:
+      required:
+        - power-domains
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - ti,k2g-pruss
+    then:
+      required:
+        - dma-coherent
 
 examples:
   - |
index b1a5e77..2170528 100644 (file)
@@ -1501,7 +1501,7 @@ M:        Miquel Raynal <miquel.raynal@bootlin.com@bootlin.com>
 M:     Naga Sureshkumar Relli <nagasure@xilinx.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
-F:     Documentation/devicetree/bindings/mtd/arm,pl353-smc.yaml
+F:     Documentation/devicetree/bindings/memory-controllers/arm,pl353-smc.yaml
 F:     drivers/memory/pl353-smc.c
 
 ARM PRIMECELL CLCD PL110 DRIVER
@@ -2023,10 +2023,12 @@ M:      Krzysztof Halasa <khalasa@piap.pl>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     Documentation/devicetree/bindings/arm/intel-ixp4xx.yaml
+F:     Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml
 F:     Documentation/devicetree/bindings/gpio/intel,ixp4xx-gpio.txt
 F:     Documentation/devicetree/bindings/interrupt-controller/intel,ixp4xx-interrupt.yaml
 F:     Documentation/devicetree/bindings/timer/intel,ixp4xx-timer.yaml
 F:     arch/arm/mach-ixp4xx/
+F:     drivers/bus/intel-ixp4xx-eb.c
 F:     drivers/clocksource/timer-ixp4xx.c
 F:     drivers/crypto/ixp4xx_crypto.c
 F:     drivers/gpio/gpio-ixp4xx.c
@@ -18077,6 +18079,7 @@ F:      drivers/regulator/scmi-regulator.c
 F:     drivers/reset/reset-scmi.c
 F:     include/linux/sc[mp]i_protocol.h
 F:     include/trace/events/scmi.h
+F:     include/uapi/linux/virtio_scmi.h
 
 SYSTEM RESET/SHUTDOWN DRIVERS
 M:     Sebastian Reichel <sre@kernel.org>
index 8d209c1..9caba45 100644 (file)
                                        clocks = <&clks IMX6Q_CLK_ECSPI5>,
                                                 <&clks IMX6Q_CLK_ECSPI5>;
                                        clock-names = "ipg", "per";
-                                       dmas = <&sdma 11 8 1>, <&sdma 12 8 2>;
+                                       dmas = <&sdma 11 7 1>, <&sdma 12 7 2>;
                                        dma-names = "rx", "tx";
                                        status = "disabled";
                                };
index 82e01ce..89c342f 100644 (file)
                                        clocks = <&clks IMX6QDL_CLK_ECSPI1>,
                                                 <&clks IMX6QDL_CLK_ECSPI1>;
                                        clock-names = "ipg", "per";
-                                       dmas = <&sdma 3 8 1>, <&sdma 4 8 2>;
+                                       dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
                                        dma-names = "rx", "tx";
                                        status = "disabled";
                                };
                                        clocks = <&clks IMX6QDL_CLK_ECSPI2>,
                                                 <&clks IMX6QDL_CLK_ECSPI2>;
                                        clock-names = "ipg", "per";
-                                       dmas = <&sdma 5 8 1>, <&sdma 6 8 2>;
+                                       dmas = <&sdma 5 7 1>, <&sdma 6 7 2>;
                                        dma-names = "rx", "tx";
                                        status = "disabled";
                                };
                                        clocks = <&clks IMX6QDL_CLK_ECSPI3>,
                                                 <&clks IMX6QDL_CLK_ECSPI3>;
                                        clock-names = "ipg", "per";
-                                       dmas = <&sdma 7 8 1>, <&sdma 8 8 2>;
+                                       dmas = <&sdma 7 7 1>, <&sdma 8 7 2>;
                                        dma-names = "rx", "tx";
                                        status = "disabled";
                                };
                                        clocks = <&clks IMX6QDL_CLK_ECSPI4>,
                                                 <&clks IMX6QDL_CLK_ECSPI4>;
                                        clock-names = "ipg", "per";
-                                       dmas = <&sdma 9 8 1>, <&sdma 10 8 2>;
+                                       dmas = <&sdma 9 7 1>, <&sdma 10 7 2>;
                                        dma-names = "rx", "tx";
                                        status = "disabled";
                                };
index 71c1d18..d73c7b6 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/omap-gpmc.h>
 
 #include <trace/events/power.h>
 
@@ -81,8 +80,6 @@ static void omap3_core_save_context(void)
 
        /* Save the Interrupt controller context */
        omap_intc_save_context();
-       /* Save the GPMC context */
-       omap3_gpmc_save_context();
        /* Save the system control module context, padconf already save above*/
        omap3_control_save_context();
 }
@@ -91,8 +88,6 @@ static void omap3_core_restore_context(void)
 {
        /* Restore the control module context, padconf restored by h/w */
        omap3_control_restore_context();
-       /* Restore the GPMC context */
-       omap3_gpmc_restore_context();
        /* Restore the interrupt controller context */
        omap_intc_restore_context();
 }
index 6452ebf..b21f51b 100644 (file)
@@ -403,7 +403,7 @@ static const struct platform_suspend_ops tegra_suspend_ops = {
        .enter          = tegra_suspend_enter,
 };
 
-void __init tegra_init_suspend(void)
+void tegra_pm_init_suspend(void)
 {
        enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode();
 
index 81525f5..e63f96d 100644 (file)
@@ -25,10 +25,4 @@ void tegra30_sleep_core_init(void);
 
 extern void (*tegra_tear_down_cpu)(void);
 
-#ifdef CONFIG_PM_SLEEP
-void tegra_init_suspend(void);
-#else
-static inline void tegra_init_suspend(void) {}
-#endif
-
 #endif /* _MACH_TEGRA_PM_H_ */
index c011359..ab5008f 100644 (file)
@@ -84,8 +84,6 @@ static void __init tegra_dt_init(void)
 
 static void __init tegra_dt_init_late(void)
 {
-       tegra_init_suspend();
-
        if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) &&
            of_machine_is_compatible("compal,paz00"))
                tegra_paz00_wifikill_init();
index 5881d64..99c6308 100644 (file)
  */
 
 #include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/libata.h>
 #include <linux/irq.h>
 #include <linux/platform_device.h>
-#include <linux/platform_data/pata_ixp4xx_cf.h>
+#include <linux/regmap.h>
 #include <scsi/scsi_host.h>
 
 #define DRV_NAME       "pata_ixp4xx_cf"
-#define DRV_VERSION    "0.2"
+#define DRV_VERSION    "1.0"
 
-static int ixp4xx_set_mode(struct ata_link *link, struct ata_device **error)
+struct ixp4xx_pata {
+       struct ata_host *host;
+       struct regmap *rmap;
+       u32 cmd_csreg;
+       void __iomem *cmd;
+       void __iomem *ctl;
+};
+
+#define IXP4XX_EXP_TIMING_STRIDE       0x04
+/* The timings for the chipselect is in bits 29..16 */
+#define IXP4XX_EXP_T1_T5_MASK  GENMASK(29, 16)
+#define IXP4XX_EXP_PIO_0_8     0x0a470000
+#define IXP4XX_EXP_PIO_1_8     0x06430000
+#define IXP4XX_EXP_PIO_2_8     0x02410000
+#define IXP4XX_EXP_PIO_3_8     0x00820000
+#define IXP4XX_EXP_PIO_4_8     0x00400000
+#define IXP4XX_EXP_PIO_0_16    0x29640000
+#define IXP4XX_EXP_PIO_1_16    0x05030000
+#define IXP4XX_EXP_PIO_2_16    0x00b20000
+#define IXP4XX_EXP_PIO_3_16    0x00820000
+#define IXP4XX_EXP_PIO_4_16    0x00400000
+#define IXP4XX_EXP_BW_MASK     (BIT(6)|BIT(0))
+#define IXP4XX_EXP_BYTE_RD16   BIT(6) /* Byte reads on half-word devices */
+#define IXP4XX_EXP_BYTE_EN     BIT(0) /* Use 8bit data bus if set */
+
+static void ixp4xx_set_8bit_timing(struct ixp4xx_pata *ixpp, u8 pio_mode)
+{
+       switch (pio_mode) {
+       case XFER_PIO_0:
+               regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                                  IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_0_8);
+               break;
+       case XFER_PIO_1:
+               regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                                  IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_1_8);
+               break;
+       case XFER_PIO_2:
+               regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                                  IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_2_8);
+               break;
+       case XFER_PIO_3:
+               regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                                  IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_3_8);
+               break;
+       case XFER_PIO_4:
+               regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                                  IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_4_8);
+               break;
+       default:
+               break;
+       }
+       regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                          IXP4XX_EXP_BW_MASK, IXP4XX_EXP_BYTE_RD16|IXP4XX_EXP_BYTE_EN);
+}
+
+static void ixp4xx_set_16bit_timing(struct ixp4xx_pata *ixpp, u8 pio_mode)
 {
-       struct ata_device *dev;
-
-       ata_for_each_dev(dev, link, ENABLED) {
-               ata_dev_info(dev, "configured for PIO0\n");
-               dev->pio_mode = XFER_PIO_0;
-               dev->xfer_mode = XFER_PIO_0;
-               dev->xfer_shift = ATA_SHIFT_PIO;
-               dev->flags |= ATA_DFLAG_PIO;
+       switch (pio_mode){
+       case XFER_PIO_0:
+               regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                                  IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_0_16);
+               break;
+       case XFER_PIO_1:
+               regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                                  IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_1_16);
+               break;
+       case XFER_PIO_2:
+               regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                                  IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_2_16);
+               break;
+       case XFER_PIO_3:
+               regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                                  IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_3_16);
+               break;
+       case XFER_PIO_4:
+               regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                                  IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_4_16);
+               break;
+       default:
+               break;
        }
-       return 0;
+       regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
+                          IXP4XX_EXP_BW_MASK, IXP4XX_EXP_BYTE_RD16);
+}
+
+/* This sets up the timing on the chipselect CMD accordingly */
+static void ixp4xx_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+       struct ixp4xx_pata *ixpp = ap->host->private_data;
+
+       ata_dev_printk(adev, KERN_INFO, "configured for PIO%d 8bit\n",
+                      adev->pio_mode - XFER_PIO_0);
+       ixp4xx_set_8bit_timing(ixpp, adev->pio_mode);
 }
 
+
 static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc,
-                               unsigned char *buf, unsigned int buflen, int rw)
+                                         unsigned char *buf, unsigned int buflen, int rw)
 {
        unsigned int i;
        unsigned int words = buflen >> 1;
        u16 *buf16 = (u16 *) buf;
+       struct ata_device *adev = qc->dev;
        struct ata_port *ap = qc->dev->link->ap;
        void __iomem *mmio = ap->ioaddr.data_addr;
-       struct ixp4xx_pata_data *data = dev_get_platdata(ap->host->dev);
+       struct ixp4xx_pata *ixpp = ap->host->private_data;
+       unsigned long flags;
+
+       ata_dev_printk(adev, KERN_DEBUG, "%s %d bytes\n", (rw == READ) ? "READ" : "WRITE",
+                      buflen);
+       spin_lock_irqsave(ap->lock, flags);
 
        /* set the expansion bus in 16bit mode and restore
         * 8 bit mode after the transaction.
         */
-       *data->cs0_cfg &= ~(0x01);
-       udelay(100);
+       ixp4xx_set_16bit_timing(ixpp, adev->pio_mode);
+       udelay(5);
 
        /* Transfer multiple of 2 bytes */
        if (rw == READ)
@@ -76,8 +165,10 @@ static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc,
                words++;
        }
 
-       udelay(100);
-       *data->cs0_cfg |= 0x01;
+       ixp4xx_set_8bit_timing(ixpp, adev->pio_mode);
+       udelay(5);
+
+       spin_unlock_irqrestore(ap->lock, flags);
 
        return words << 1;
 }
@@ -90,79 +181,98 @@ static struct ata_port_operations ixp4xx_port_ops = {
        .inherits               = &ata_sff_port_ops,
        .sff_data_xfer          = ixp4xx_mmio_data_xfer,
        .cable_detect           = ata_cable_40wire,
-       .set_mode               = ixp4xx_set_mode,
+       .set_piomode            = ixp4xx_set_piomode,
+};
+
+static struct ata_port_info ixp4xx_port_info = {
+       .flags          = ATA_FLAG_NO_ATAPI,
+       .pio_mask       = ATA_PIO4,
+       .port_ops       = &ixp4xx_port_ops,
 };
 
 static void ixp4xx_setup_port(struct ata_port *ap,
-                             struct ixp4xx_pata_data *data,
-                             unsigned long raw_cs0, unsigned long raw_cs1)
+                             struct ixp4xx_pata *ixpp,
+                             unsigned long raw_cmd, unsigned long raw_ctl)
 {
        struct ata_ioports *ioaddr = &ap->ioaddr;
-       unsigned long raw_cmd = raw_cs0;
-       unsigned long raw_ctl = raw_cs1 + 0x06;
 
-       ioaddr->cmd_addr        = data->cs0;
-       ioaddr->altstatus_addr  = data->cs1 + 0x06;
-       ioaddr->ctl_addr        = data->cs1 + 0x06;
+       raw_ctl += 0x06;
+       ioaddr->cmd_addr        = ixpp->cmd;
+       ioaddr->altstatus_addr  = ixpp->ctl + 0x06;
+       ioaddr->ctl_addr        = ixpp->ctl + 0x06;
 
        ata_sff_std_ports(ioaddr);
 
-#ifndef __ARMEB__
-
-       /* adjust the addresses to handle the address swizzling of the
-        * ixp4xx in little endian mode.
-        */
-
-       *(unsigned long *)&ioaddr->data_addr            ^= 0x02;
-       *(unsigned long *)&ioaddr->cmd_addr             ^= 0x03;
-       *(unsigned long *)&ioaddr->altstatus_addr       ^= 0x03;
-       *(unsigned long *)&ioaddr->ctl_addr             ^= 0x03;
-       *(unsigned long *)&ioaddr->error_addr           ^= 0x03;
-       *(unsigned long *)&ioaddr->feature_addr         ^= 0x03;
-       *(unsigned long *)&ioaddr->nsect_addr           ^= 0x03;
-       *(unsigned long *)&ioaddr->lbal_addr            ^= 0x03;
-       *(unsigned long *)&ioaddr->lbam_addr            ^= 0x03;
-       *(unsigned long *)&ioaddr->lbah_addr            ^= 0x03;
-       *(unsigned long *)&ioaddr->device_addr          ^= 0x03;
-       *(unsigned long *)&ioaddr->status_addr          ^= 0x03;
-       *(unsigned long *)&ioaddr->command_addr         ^= 0x03;
-
-       raw_cmd ^= 0x03;
-       raw_ctl ^= 0x03;
-#endif
+       if (!IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) {
+               /* adjust the addresses to handle the address swizzling of the
+                * ixp4xx in little endian mode.
+                */
+
+               *(unsigned long *)&ioaddr->data_addr            ^= 0x02;
+               *(unsigned long *)&ioaddr->cmd_addr             ^= 0x03;
+               *(unsigned long *)&ioaddr->altstatus_addr       ^= 0x03;
+               *(unsigned long *)&ioaddr->ctl_addr             ^= 0x03;
+               *(unsigned long *)&ioaddr->error_addr           ^= 0x03;
+               *(unsigned long *)&ioaddr->feature_addr         ^= 0x03;
+               *(unsigned long *)&ioaddr->nsect_addr           ^= 0x03;
+               *(unsigned long *)&ioaddr->lbal_addr            ^= 0x03;
+               *(unsigned long *)&ioaddr->lbam_addr            ^= 0x03;
+               *(unsigned long *)&ioaddr->lbah_addr            ^= 0x03;
+               *(unsigned long *)&ioaddr->device_addr          ^= 0x03;
+               *(unsigned long *)&ioaddr->status_addr          ^= 0x03;
+               *(unsigned long *)&ioaddr->command_addr         ^= 0x03;
+
+               raw_cmd ^= 0x03;
+               raw_ctl ^= 0x03;
+       }
 
        ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", raw_cmd, raw_ctl);
 }
 
 static int ixp4xx_pata_probe(struct platform_device *pdev)
 {
-       struct resource *cs0, *cs1;
-       struct ata_host *host;
-       struct ata_port *ap;
-       struct ixp4xx_pata_data *data = dev_get_platdata(&pdev->dev);
+       struct resource *cmd, *ctl;
+       struct ata_port_info pi = ixp4xx_port_info;
+       const struct ata_port_info *ppi[] = { &pi, NULL };
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct ixp4xx_pata *ixpp;
+       u32 csindex;
        int ret;
        int irq;
 
-       cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       cmd = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 
-       if (!cs0 || !cs1)
+       if (!cmd || !ctl)
                return -EINVAL;
 
-       /* allocate host */
-       host = ata_host_alloc(&pdev->dev, 1);
-       if (!host)
+       ixpp = devm_kzalloc(dev, sizeof(*ixpp), GFP_KERNEL);
+       if (!ixpp)
                return -ENOMEM;
 
-       /* acquire resources and fill host */
-       ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+       ixpp->rmap = syscon_node_to_regmap(np->parent);
+       if (IS_ERR(ixpp->rmap))
+               return dev_err_probe(dev, PTR_ERR(ixpp->rmap), "no regmap\n");
+       /* Inspect our address to figure out what chipselect the CMD is on */
+       ret = of_property_read_u32_index(np, "reg", 0, &csindex);
        if (ret)
-               return ret;
+               return dev_err_probe(dev, ret, "can't inspect CMD address\n");
+       dev_info(dev, "using CS%d for PIO timing configuration\n", csindex);
+       ixpp->cmd_csreg = csindex * IXP4XX_EXP_TIMING_STRIDE;
 
-       data->cs0 = devm_ioremap(&pdev->dev, cs0->start, 0x1000);
-       data->cs1 = devm_ioremap(&pdev->dev, cs1->start, 0x1000);
+       ixpp->host = ata_host_alloc_pinfo(dev, ppi, 1);
+       if (!ixpp->host)
+               return -ENOMEM;
+       ixpp->host->private_data = ixpp;
 
-       if (!data->cs0 || !data->cs1)
+       ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+       if (ret)
+               return ret;
+
+       ixpp->cmd = devm_ioremap_resource(dev, cmd);
+       ixpp->ctl = devm_ioremap_resource(dev, ctl);
+       if (IS_ERR(ixpp->cmd) || IS_ERR(ixpp->ctl))
                return -ENOMEM;
 
        irq = platform_get_irq(pdev, 0);
@@ -173,27 +283,23 @@ static int ixp4xx_pata_probe(struct platform_device *pdev)
        else
                return -EINVAL;
 
-       /* Setup expansion bus chip selects */
-       *data->cs0_cfg = data->cs0_bits;
-       *data->cs1_cfg = data->cs1_bits;
-
-       ap = host->ports[0];
-
-       ap->ops = &ixp4xx_port_ops;
-       ap->pio_mask = ATA_PIO4;
-       ap->flags |= ATA_FLAG_NO_ATAPI;
+       /* Just one port to set up */
+       ixp4xx_setup_port(ixpp->host->ports[0], ixpp, cmd->start, ctl->start);
 
-       ixp4xx_setup_port(ap, data, cs0->start, cs1->start);
+       ata_print_version_once(dev, DRV_VERSION);
 
-       ata_print_version_once(&pdev->dev, DRV_VERSION);
-
-       /* activate host */
-       return ata_host_activate(host, irq, ata_sff_interrupt, 0, &ixp4xx_sht);
+       return ata_host_activate(ixpp->host, irq, ata_sff_interrupt, 0, &ixp4xx_sht);
 }
 
+static const struct of_device_id ixp4xx_pata_of_match[] = {
+       { .compatible = "intel,ixp4xx-compact-flash", },
+       { },
+};
+
 static struct platform_driver ixp4xx_pata_platform_driver = {
        .driver  = {
                .name   = DRV_NAME,
+               .of_match_table = ixp4xx_pata_of_match,
        },
        .probe          = ixp4xx_pata_probe,
        .remove         = ata_platform_remove_one,
index e7f7eee..a5b96f3 100644 (file)
@@ -95,6 +95,17 @@ config IMX_WEIM
          The WEIM(Wireless External Interface Module) works like a bus.
          You can attach many different devices on it, such as NOR, onenand.
 
+config INTEL_IXP4XX_EB
+       bool "Intel IXP4xx expansion bus interface driver"
+       depends on HAS_IOMEM
+       depends on ARCH_IXP4XX || COMPILE_TEST
+       default ARCH_IXP4XX
+       select MFD_SYSCON
+       help
+         Driver for the Intel IXP4xx expansion bus interface. The driver is
+         needed to set up various chip select configuration parameters before
+         devices on the expansion bus can be discovered.
+
 config MIPS_CDMM
        bool "MIPS Common Device Memory Map (CDMM) Driver"
        depends on CPU_MIPSR2 || CPU_MIPSR5
index 397e353..1c29c5e 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_FSL_MC_BUS)      += fsl-mc/
 obj-$(CONFIG_BT1_APB)          += bt1-apb.o
 obj-$(CONFIG_BT1_AXI)          += bt1-axi.o
 obj-$(CONFIG_IMX_WEIM)         += imx-weim.o
+obj-$(CONFIG_INTEL_IXP4XX_EB)  += intel-ixp4xx-eb.o
 obj-$(CONFIG_MIPS_CDMM)                += mips_cdmm.o
 obj-$(CONFIG_MVEBU_MBUS)       += mvebu-mbus.o
 
diff --git a/drivers/bus/intel-ixp4xx-eb.c b/drivers/bus/intel-ixp4xx-eb.c
new file mode 100644 (file)
index 0000000..a438844
--- /dev/null
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel IXP4xx Expansion Bus Controller
+ * Copyright (C) 2021 Linaro Ltd.
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/log2.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define IXP4XX_EXP_NUM_CS              8
+
+#define IXP4XX_EXP_TIMING_CS0          0x00
+#define IXP4XX_EXP_TIMING_CS1          0x04
+#define IXP4XX_EXP_TIMING_CS2          0x08
+#define IXP4XX_EXP_TIMING_CS3          0x0c
+#define IXP4XX_EXP_TIMING_CS4          0x10
+#define IXP4XX_EXP_TIMING_CS5          0x14
+#define IXP4XX_EXP_TIMING_CS6          0x18
+#define IXP4XX_EXP_TIMING_CS7          0x1c
+
+/* Bits inside each CS timing register */
+#define IXP4XX_EXP_TIMING_STRIDE       0x04
+#define IXP4XX_EXP_CS_EN               BIT(31)
+#define IXP456_EXP_PAR_EN              BIT(30) /* Only on IXP45x and IXP46x */
+#define IXP4XX_EXP_T1_MASK             GENMASK(28, 27)
+#define IXP4XX_EXP_T1_SHIFT            28
+#define IXP4XX_EXP_T2_MASK             GENMASK(27, 26)
+#define IXP4XX_EXP_T2_SHIFT            26
+#define IXP4XX_EXP_T3_MASK             GENMASK(25, 22)
+#define IXP4XX_EXP_T3_SHIFT            22
+#define IXP4XX_EXP_T4_MASK             GENMASK(21, 20)
+#define IXP4XX_EXP_T4_SHIFT            20
+#define IXP4XX_EXP_T5_MASK             GENMASK(19, 16)
+#define IXP4XX_EXP_T5_SHIFT            16
+#define IXP4XX_EXP_CYC_TYPE_MASK       GENMASK(15, 14)
+#define IXP4XX_EXP_CYC_TYPE_SHIFT      14
+#define IXP4XX_EXP_SIZE_MASK           GENMASK(13, 10)
+#define IXP4XX_EXP_SIZE_SHIFT          10
+#define IXP4XX_EXP_CNFG_0              BIT(9) /* Always zero */
+#define IXP43X_EXP_SYNC_INTEL          BIT(8) /* Only on IXP43x */
+#define IXP43X_EXP_EXP_CHIP            BIT(7) /* Only on IXP43x */
+#define IXP4XX_EXP_BYTE_RD16           BIT(6)
+#define IXP4XX_EXP_HRDY_POL            BIT(5) /* Only on IXP42x */
+#define IXP4XX_EXP_MUX_EN              BIT(4)
+#define IXP4XX_EXP_SPLT_EN             BIT(3)
+#define IXP4XX_EXP_WORD                        BIT(2) /* Always zero */
+#define IXP4XX_EXP_WR_EN               BIT(1)
+#define IXP4XX_EXP_BYTE_EN             BIT(0)
+#define IXP42X_RESERVED                        (BIT(30)|IXP4XX_EXP_CNFG_0|BIT(8)|BIT(7)|IXP4XX_EXP_WORD)
+#define IXP43X_RESERVED                        (BIT(30)|IXP4XX_EXP_CNFG_0|BIT(5)|IXP4XX_EXP_WORD)
+
+#define IXP4XX_EXP_CNFG0               0x20
+#define IXP4XX_EXP_CNFG0_MEM_MAP       BIT(31)
+#define IXP4XX_EXP_CNFG1               0x24
+
+#define IXP4XX_EXP_BOOT_BASE           0x00000000
+#define IXP4XX_EXP_NORMAL_BASE         0x50000000
+#define IXP4XX_EXP_STRIDE              0x01000000
+
+/* Fuses on the IXP43x */
+#define IXP43X_EXP_UNIT_FUSE_RESET     0x28
+#define IXP43x_EXP_FUSE_SPEED_MASK     GENMASK(23, 22)
+
+/* Number of device tree values in "reg" */
+#define IXP4XX_OF_REG_SIZE             3
+
+struct ixp4xx_eb {
+       struct device *dev;
+       struct regmap *rmap;
+       u32 bus_base;
+       bool is_42x;
+       bool is_43x;
+};
+
+struct ixp4xx_exp_tim_prop {
+       const char *prop;
+       u32 max;
+       u32 mask;
+       u16 shift;
+};
+
+static const struct ixp4xx_exp_tim_prop ixp4xx_exp_tim_props[] = {
+       {
+               .prop = "intel,ixp4xx-eb-t1",
+               .max = 3,
+               .mask = IXP4XX_EXP_T1_MASK,
+               .shift = IXP4XX_EXP_T1_SHIFT,
+       },
+       {
+               .prop = "intel,ixp4xx-eb-t2",
+               .max = 3,
+               .mask = IXP4XX_EXP_T2_MASK,
+               .shift = IXP4XX_EXP_T2_SHIFT,
+       },
+       {
+               .prop = "intel,ixp4xx-eb-t3",
+               .max = 15,
+               .mask = IXP4XX_EXP_T3_MASK,
+               .shift = IXP4XX_EXP_T3_SHIFT,
+       },
+       {
+               .prop = "intel,ixp4xx-eb-t4",
+               .max = 3,
+               .mask = IXP4XX_EXP_T4_MASK,
+               .shift = IXP4XX_EXP_T4_SHIFT,
+       },
+       {
+               .prop = "intel,ixp4xx-eb-t5",
+               .max = 15,
+               .mask = IXP4XX_EXP_T5_MASK,
+               .shift = IXP4XX_EXP_T5_SHIFT,
+       },
+       {
+               .prop = "intel,ixp4xx-eb-byte-access-on-halfword",
+               .max = 1,
+               .mask = IXP4XX_EXP_BYTE_RD16,
+       },
+       {
+               .prop = "intel,ixp4xx-eb-hpi-hrdy-pol-high",
+               .max = 1,
+               .mask = IXP4XX_EXP_HRDY_POL,
+       },
+       {
+               .prop = "intel,ixp4xx-eb-mux-address-and-data",
+               .max = 1,
+               .mask = IXP4XX_EXP_MUX_EN,
+       },
+       {
+               .prop = "intel,ixp4xx-eb-ahb-split-transfers",
+               .max = 1,
+               .mask = IXP4XX_EXP_SPLT_EN,
+       },
+       {
+               .prop = "intel,ixp4xx-eb-write-enable",
+               .max = 1,
+               .mask = IXP4XX_EXP_WR_EN,
+       },
+       {
+               .prop = "intel,ixp4xx-eb-byte-access",
+               .max = 1,
+               .mask = IXP4XX_EXP_BYTE_EN,
+       },
+};
+
+static void ixp4xx_exp_setup_chipselect(struct ixp4xx_eb *eb,
+                                       struct device_node *np,
+                                       u32 cs_index,
+                                       u32 cs_size)
+{
+       u32 cs_cfg;
+       u32 val;
+       u32 cur_cssize;
+       u32 cs_order;
+       int ret;
+       int i;
+
+       if (eb->is_42x && (cs_index > 7)) {
+               dev_err(eb->dev,
+                       "invalid chipselect %u, we only support 0-7\n",
+                       cs_index);
+               return;
+       }
+       if (eb->is_43x && (cs_index > 3)) {
+               dev_err(eb->dev,
+                       "invalid chipselect %u, we only support 0-3\n",
+                       cs_index);
+               return;
+       }
+
+       /* Several chip selects can be joined into one device */
+       if (cs_size > IXP4XX_EXP_STRIDE)
+               cur_cssize = IXP4XX_EXP_STRIDE;
+       else
+               cur_cssize = cs_size;
+
+
+       /*
+        * The following will read/modify/write the configuration for one
+        * chipselect, attempting to leave the boot defaults in place unless
+        * something is explicitly defined.
+        */
+       regmap_read(eb->rmap, IXP4XX_EXP_TIMING_CS0 +
+                   IXP4XX_EXP_TIMING_STRIDE * cs_index, &cs_cfg);
+       dev_info(eb->dev, "CS%d at %#08x, size %#08x, config before: %#08x\n",
+                cs_index, eb->bus_base + IXP4XX_EXP_STRIDE * cs_index,
+                cur_cssize, cs_cfg);
+
+       /* Size set-up first align to 2^9 .. 2^24 */
+       cur_cssize = roundup_pow_of_two(cur_cssize);
+       if (cur_cssize < 512)
+               cur_cssize = 512;
+       cs_order = ilog2(cur_cssize);
+       if (cs_order < 9 || cs_order > 24) {
+               dev_err(eb->dev, "illegal size order %d\n", cs_order);
+               return;
+       }
+       dev_dbg(eb->dev, "CS%d size order: %d\n", cs_index, cs_order);
+       cs_cfg &= ~(IXP4XX_EXP_SIZE_MASK);
+       cs_cfg |= ((cs_order - 9) << IXP4XX_EXP_SIZE_SHIFT);
+
+       for (i = 0; i < ARRAY_SIZE(ixp4xx_exp_tim_props); i++) {
+               const struct ixp4xx_exp_tim_prop *ip = &ixp4xx_exp_tim_props[i];
+
+               /* All are regular u32 values */
+               ret = of_property_read_u32(np, ip->prop, &val);
+               if (ret)
+                       continue;
+
+               /* Handle bools (single bits) first */
+               if (ip->max == 1) {
+                       if (val)
+                               cs_cfg |= ip->mask;
+                       else
+                               cs_cfg &= ~ip->mask;
+                       dev_info(eb->dev, "CS%d %s %s\n", cs_index,
+                                val ? "enabled" : "disabled",
+                                ip->prop);
+                       continue;
+               }
+
+               if (val > ip->max) {
+                       dev_err(eb->dev,
+                               "CS%d too high value for %s: %u, capped at %u\n",
+                               cs_index, ip->prop, val, ip->max);
+                       val = ip->max;
+               }
+               /* This assumes max value fills all the assigned bits (and it does) */
+               cs_cfg &= ~ip->mask;
+               cs_cfg |= (val << ip->shift);
+               dev_info(eb->dev, "CS%d set %s to %u\n", cs_index, ip->prop, val);
+       }
+
+       ret = of_property_read_u32(np, "intel,ixp4xx-eb-cycle-type", &val);
+       if (!ret) {
+               if (val > 3) {
+                       dev_err(eb->dev, "illegal cycle type %d\n", val);
+                       return;
+               }
+               dev_info(eb->dev, "CS%d set cycle type %d\n", cs_index, val);
+               cs_cfg &= ~IXP4XX_EXP_CYC_TYPE_MASK;
+               cs_cfg |= val << IXP4XX_EXP_CYC_TYPE_SHIFT;
+       }
+
+       if (eb->is_42x)
+               cs_cfg &= ~IXP42X_RESERVED;
+       if (eb->is_43x) {
+               cs_cfg &= ~IXP43X_RESERVED;
+               /*
+                * This bit for Intel strata flash is currently unused, but let's
+                * report it if we find one.
+                */
+               if (cs_cfg & IXP43X_EXP_SYNC_INTEL)
+                       dev_info(eb->dev, "claims to be Intel strata flash\n");
+       }
+       cs_cfg |= IXP4XX_EXP_CS_EN;
+
+       regmap_write(eb->rmap,
+                    IXP4XX_EXP_TIMING_CS0 + IXP4XX_EXP_TIMING_STRIDE * cs_index,
+                    cs_cfg);
+       dev_info(eb->dev, "CS%d wrote %#08x into CS config\n", cs_index, cs_cfg);
+
+       /*
+        * If several chip selects are joined together into one big
+        * device area, we call ourselves recursively for each successive
+        * chip select. For a 32MB flash chip this results in two calls
+        * for example.
+        */
+       if (cs_size > IXP4XX_EXP_STRIDE)
+               ixp4xx_exp_setup_chipselect(eb, np,
+                                           cs_index + 1,
+                                           cs_size - IXP4XX_EXP_STRIDE);
+}
+
+static void ixp4xx_exp_setup_child(struct ixp4xx_eb *eb,
+                                  struct device_node *np)
+{
+       u32 cs_sizes[IXP4XX_EXP_NUM_CS];
+       int num_regs;
+       u32 csindex;
+       u32 cssize;
+       int ret;
+       int i;
+
+       num_regs = of_property_count_elems_of_size(np, "reg", IXP4XX_OF_REG_SIZE);
+       if (num_regs <= 0)
+               return;
+       dev_dbg(eb->dev, "child %s has %d register sets\n",
+               of_node_full_name(np), num_regs);
+
+       for (csindex = 0; csindex < IXP4XX_EXP_NUM_CS; csindex++)
+               cs_sizes[csindex] = 0;
+
+       for (i = 0; i < num_regs; i++) {
+               u32 rbase, rsize;
+
+               ret = of_property_read_u32_index(np, "reg",
+                                                i * IXP4XX_OF_REG_SIZE, &csindex);
+               if (ret)
+                       break;
+               ret = of_property_read_u32_index(np, "reg",
+                                                i * IXP4XX_OF_REG_SIZE + 1, &rbase);
+               if (ret)
+                       break;
+               ret = of_property_read_u32_index(np, "reg",
+                                                i * IXP4XX_OF_REG_SIZE + 2, &rsize);
+               if (ret)
+                       break;
+
+               if (csindex >= IXP4XX_EXP_NUM_CS) {
+                       dev_err(eb->dev, "illegal CS %d\n", csindex);
+                       continue;
+               }
+               /*
+                * The memory window always starts from CS base so we need to add
+                * the start and size to get to the size from the start of the CS
+                * base. For example if CS0 is at 0x50000000 and the reg is
+                * <0 0xe40000 0x40000> the size is e80000.
+                *
+                * Roof this if we have several regs setting the same CS.
+                */
+               cssize = rbase + rsize;
+               dev_dbg(eb->dev, "CS%d size %#08x\n", csindex, cssize);
+               if (cs_sizes[csindex] < cssize)
+                       cs_sizes[csindex] = cssize;
+       }
+
+       for (csindex = 0; csindex < IXP4XX_EXP_NUM_CS; csindex++) {
+               cssize = cs_sizes[csindex];
+               if (!cssize)
+                       continue;
+               /* Just this one, so set it up and return */
+               ixp4xx_exp_setup_chipselect(eb, np, csindex, cssize);
+       }
+}
+
+static int ixp4xx_exp_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct ixp4xx_eb *eb;
+       struct device_node *child;
+       bool have_children = false;
+       u32 val;
+       int ret;
+
+       eb = devm_kzalloc(dev, sizeof(*eb), GFP_KERNEL);
+       if (!eb)
+               return -ENOMEM;
+
+       eb->dev = dev;
+       eb->is_42x = of_device_is_compatible(np, "intel,ixp42x-expansion-bus-controller");
+       eb->is_43x = of_device_is_compatible(np, "intel,ixp43x-expansion-bus-controller");
+
+       eb->rmap = syscon_node_to_regmap(np);
+       if (IS_ERR(eb->rmap))
+               return dev_err_probe(dev, PTR_ERR(eb->rmap), "no regmap\n");
+
+       /* We check that the regmap work only on first read */
+       ret = regmap_read(eb->rmap, IXP4XX_EXP_CNFG0, &val);
+       if (ret)
+               return dev_err_probe(dev, ret, "cannot read regmap\n");
+       if (val & IXP4XX_EXP_CNFG0_MEM_MAP)
+               eb->bus_base = IXP4XX_EXP_BOOT_BASE;
+       else
+               eb->bus_base = IXP4XX_EXP_NORMAL_BASE;
+       dev_info(dev, "expansion bus at %08x\n", eb->bus_base);
+
+       if (eb->is_43x) {
+               /* Check some fuses */
+               regmap_read(eb->rmap, IXP43X_EXP_UNIT_FUSE_RESET, &val);
+               switch (FIELD_GET(IXP43x_EXP_FUSE_SPEED_MASK, val)) {
+               case 0:
+                       dev_info(dev, "IXP43x at 533 MHz\n");
+                       break;
+               case 1:
+                       dev_info(dev, "IXP43x at 400 MHz\n");
+                       break;
+               case 2:
+                       dev_info(dev, "IXP43x at 667 MHz\n");
+                       break;
+               default:
+                       dev_info(dev, "IXP43x unknown speed\n");
+                       break;
+               }
+       }
+
+       /* Walk over the child nodes and see what chipselects we use */
+       for_each_available_child_of_node(np, child) {
+               ixp4xx_exp_setup_child(eb, child);
+               /* We have at least one child */
+               have_children = true;
+       }
+
+       if (have_children)
+               return of_platform_default_populate(np, NULL, dev);
+
+       return 0;
+}
+
+static const struct of_device_id ixp4xx_exp_of_match[] = {
+       { .compatible = "intel,ixp42x-expansion-bus-controller", },
+       { .compatible = "intel,ixp43x-expansion-bus-controller", },
+       { .compatible = "intel,ixp45x-expansion-bus-controller", },
+       { .compatible = "intel,ixp46x-expansion-bus-controller", },
+       { }
+};
+
+static struct platform_driver ixp4xx_exp_driver = {
+       .probe = ixp4xx_exp_probe,
+       .driver = {
+               .name = "intel-extbus",
+               .of_match_table = ixp4xx_exp_of_match,
+       },
+};
+module_platform_driver(ixp4xx_exp_driver);
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("Intel IXP4xx external bus driver");
+MODULE_LICENSE("GPL");
index 148a4dd..a51c2a8 100644 (file)
@@ -855,7 +855,7 @@ static int sysc_check_registers(struct sysc *ddata)
 }
 
 /**
- * syc_ioremap - ioremap register space for the interconnect target module
+ * sysc_ioremap - ioremap register space for the interconnect target module
  * @ddata: device driver data
  *
  * Note that the interconnect target module registers can be anywhere
@@ -1446,10 +1446,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
                   SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET),
        SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff,
                   SYSC_QUIRK_LEGACY_IDLE),
-       SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x24, -ENODEV, 0x00000000, 0xffffffff,
-                  SYSC_QUIRK_LEGACY_IDLE),
-       SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x38, -ENODEV, 0x00000000, 0xffffffff,
-                  SYSC_QUIRK_LEGACY_IDLE),
        SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff,
                   SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE),
        SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
@@ -1501,6 +1497,8 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
                   SYSC_MODULE_QUIRK_SGX),
        SYSC_QUIRK("lcdc", 0, 0, 0x54, -ENODEV, 0x4f201000, 0xffffffff,
                   SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
+       SYSC_QUIRK("mcasp", 0, 0, 0x4, -ENODEV, 0x44306302, 0xffffffff,
+                  SYSC_QUIRK_SWSUP_SIDLE),
        SYSC_QUIRK("rtc", 0, 0x74, 0x78, -ENODEV, 0x4eb01908, 0xffff00f0,
                   SYSC_MODULE_QUIRK_RTC_UNLOCK),
        SYSC_QUIRK("tptc", 0, 0, 0x10, -ENODEV, 0x40006c00, 0xffffefff,
@@ -1557,7 +1555,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
        SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0),
        SYSC_QUIRK("iss", 0, 0, 0x10, -ENODEV, 0x40000101, 0xffffffff, 0),
        SYSC_QUIRK("keypad", 0x4a31c000, 0, 0x10, 0x14, 0x00000020, 0xffffffff, 0),
-       SYSC_QUIRK("mcasp", 0, 0, 0x4, -ENODEV, 0x44306302, 0xffffffff, 0),
        SYSC_QUIRK("mcasp", 0, 0, 0x4, -ENODEV, 0x44307b02, 0xffffffff, 0),
        SYSC_QUIRK("mcbsp", 0, -ENODEV, 0x8c, -ENODEV, 0, 0, 0),
        SYSC_QUIRK("mcspi", 0, 0, 0x10, -ENODEV, 0x40300a0b, 0xffff00ff, 0),
@@ -1585,6 +1582,8 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
        SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff, 0),
        SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40000902, 0xffffffff, 0),
        SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40002903, 0xffffffff, 0),
+       SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x24, -ENODEV, 0x00000000, 0xffffffff, 0),
+       SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x38, -ENODEV, 0x00000000, 0xffffffff, 0),
        SYSC_QUIRK("spinlock", 0, 0, 0x10, -ENODEV, 0x50020000, 0xffffffff, 0),
        SYSC_QUIRK("rng", 0, 0x1fe0, 0x1fe4, -ENODEV, 0x00000020, 0xffffffff, 0),
        SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000013, 0xffffffff, 0),
@@ -3115,9 +3114,8 @@ static int sysc_probe(struct platform_device *pdev)
                goto unprepare;
 
        pm_runtime_enable(ddata->dev);
-       error = pm_runtime_get_sync(ddata->dev);
+       error = pm_runtime_resume_and_get(ddata->dev);
        if (error < 0) {
-               pm_runtime_put_noidle(ddata->dev);
                pm_runtime_disable(ddata->dev);
                goto unprepare;
        }
@@ -3175,9 +3173,8 @@ static int sysc_remove(struct platform_device *pdev)
 
        cancel_delayed_work_sync(&ddata->idle_work);
 
-       error = pm_runtime_get_sync(ddata->dev);
+       error = pm_runtime_resume_and_get(ddata->dev);
        if (error < 0) {
-               pm_runtime_put_noidle(ddata->dev);
                pm_runtime_disable(ddata->dev);
                goto unprepare;
        }
index 9396745..cbb1849 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/delay.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/platform_device.h>
 /* Goes away with OF conversion */
 #include <linux/platform_data/timer-ixp4xx.h>
 
@@ -29,9 +30,6 @@
 #define IXP4XX_OSRT1_OFFSET    0x08  /* Timer 1 Reload */
 #define IXP4XX_OST2_OFFSET     0x0C  /* Timer 2 Timestamp */
 #define IXP4XX_OSRT2_OFFSET    0x10  /* Timer 2 Reload */
-#define IXP4XX_OSWT_OFFSET     0x14  /* Watchdog Timer */
-#define IXP4XX_OSWE_OFFSET     0x18  /* Watchdog Enable */
-#define IXP4XX_OSWK_OFFSET     0x1C  /* Watchdog Key */
 #define IXP4XX_OSST_OFFSET     0x20  /* Timer Status */
 
 /*
 #define IXP4XX_OSST_TIMER_1_PEND       0x00000001
 #define IXP4XX_OSST_TIMER_2_PEND       0x00000002
 #define IXP4XX_OSST_TIMER_TS_PEND      0x00000004
-#define IXP4XX_OSST_TIMER_WDOG_PEND    0x00000008
-#define IXP4XX_OSST_TIMER_WARM_RESET   0x00000010
-
-#define        IXP4XX_WDT_KEY                  0x0000482E
-#define        IXP4XX_WDT_RESET_ENABLE         0x00000001
-#define        IXP4XX_WDT_IRQ_ENABLE           0x00000002
-#define        IXP4XX_WDT_COUNT_ENABLE         0x00000004
+/* Remaining registers are for the watchdog and defined in the watchdog driver */
 
 struct ixp4xx_timer {
        void __iomem *base;
-       unsigned int tick_rate;
        u32 latch;
        struct clock_event_device clkevt;
 #ifdef CONFIG_ARM
@@ -181,7 +172,6 @@ static __init int ixp4xx_timer_register(void __iomem *base,
        if (!tmr)
                return -ENOMEM;
        tmr->base = base;
-       tmr->tick_rate = timer_freq;
 
        /*
         * The timer register doesn't allow to specify the two least
@@ -239,6 +229,40 @@ static __init int ixp4xx_timer_register(void __iomem *base,
        return 0;
 }
 
+static struct platform_device ixp4xx_watchdog_device = {
+       .name = "ixp4xx-watchdog",
+       .id = -1,
+};
+
+/*
+ * This probe gets called after the timer is already up and running. The main
+ * function on this platform is to spawn the watchdog device as a child.
+ */
+static int ixp4xx_timer_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+
+       /* Pass the base address as platform data and nothing else */
+       ixp4xx_watchdog_device.dev.platform_data = local_ixp4xx_timer->base;
+       ixp4xx_watchdog_device.dev.parent = dev;
+       return platform_device_register(&ixp4xx_watchdog_device);
+}
+
+static const struct of_device_id ixp4xx_timer_dt_id[] = {
+       { .compatible = "intel,ixp4xx-timer", },
+       { /* sentinel */ },
+};
+
+static struct platform_driver ixp4xx_timer_driver = {
+       .probe  = ixp4xx_timer_probe,
+       .driver = {
+               .name = "ixp4xx-timer",
+               .of_match_table = ixp4xx_timer_dt_id,
+               .suppress_bind_attrs = true,
+       },
+};
+builtin_platform_driver(ixp4xx_timer_driver);
+
 /**
  * ixp4xx_timer_setup() - Timer setup function to be called from boardfiles
  * @timerbase: physical base of timer block
index 8070fd6..cacc725 100644 (file)
@@ -198,12 +198,12 @@ struct sdma_script_start_addrs {
        s32 per_2_firi_addr;
        s32 mcu_2_firi_addr;
        s32 uart_2_per_addr;
-       s32 uart_2_mcu_addr;
+       s32 uart_2_mcu_ram_addr;
        s32 per_2_app_addr;
        s32 mcu_2_app_addr;
        s32 per_2_per_addr;
        s32 uartsh_2_per_addr;
-       s32 uartsh_2_mcu_addr;
+       s32 uartsh_2_mcu_ram_addr;
        s32 per_2_shp_addr;
        s32 mcu_2_shp_addr;
        s32 ata_2_mcu_addr;
@@ -230,6 +230,10 @@ struct sdma_script_start_addrs {
        s32 zcanfd_2_mcu_addr;
        s32 zqspi_2_mcu_addr;
        s32 mcu_2_ecspi_addr;
+       s32 mcu_2_sai_addr;
+       s32 sai_2_mcu_addr;
+       s32 uart_2_mcu_addr;
+       s32 uartsh_2_mcu_addr;
        /* End of v3 array */
        s32 mcu_2_zqspi_addr;
        /* End of v4 array */
@@ -433,9 +437,10 @@ struct sdma_channel {
        unsigned long                   watermark_level;
        u32                             shp_addr, per_addr;
        enum dma_status                 status;
-       bool                            context_loaded;
        struct imx_dma_data             data;
        struct work_struct              terminate_worker;
+       struct list_head                terminated;
+       bool                            is_ram_script;
 };
 
 #define IMX_DMA_SG_LOOP                BIT(0)
@@ -476,6 +481,13 @@ struct sdma_driver_data {
        int num_events;
        struct sdma_script_start_addrs  *script_addrs;
        bool check_ratio;
+       /*
+        * ecspi ERR009165 fixed should be done in sdma script
+        * and it has been fixed in soc from i.mx6ul.
+        * please get more information from the below link:
+        * https://www.nxp.com/docs/en/errata/IMX6DQCE.pdf
+        */
+       bool ecspi_fixed;
 };
 
 struct sdma_engine {
@@ -499,6 +511,7 @@ struct sdma_engine {
        struct sdma_buffer_descriptor   *bd0;
        /* clock ratio for AHB:SDMA core. 1:1 is 1, 2:1 is 0*/
        bool                            clk_ratio;
+       bool                            fw_loaded;
 };
 
 static int sdma_config_write(struct dma_chan *chan,
@@ -595,6 +608,13 @@ static struct sdma_driver_data sdma_imx6q = {
        .script_addrs = &sdma_script_imx6q,
 };
 
+static struct sdma_driver_data sdma_imx6ul = {
+       .chnenbl0 = SDMA_CHNENBL0_IMX35,
+       .num_events = 48,
+       .script_addrs = &sdma_script_imx6q,
+       .ecspi_fixed = true,
+};
+
 static struct sdma_script_start_addrs sdma_script_imx7d = {
        .ap_2_ap_addr = 644,
        .uart_2_mcu_addr = 819,
@@ -628,6 +648,7 @@ static const struct of_device_id sdma_dt_ids[] = {
        { .compatible = "fsl,imx31-sdma", .data = &sdma_imx31, },
        { .compatible = "fsl,imx25-sdma", .data = &sdma_imx25, },
        { .compatible = "fsl,imx7d-sdma", .data = &sdma_imx7d, },
+       { .compatible = "fsl,imx6ul-sdma", .data = &sdma_imx6ul, },
        { .compatible = "fsl,imx8mq-sdma", .data = &sdma_imx8mq, },
        { /* sentinel */ }
 };
@@ -919,6 +940,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
        sdmac->pc_to_device = 0;
        sdmac->device_to_device = 0;
        sdmac->pc_to_pc = 0;
+       sdmac->is_ram_script = false;
 
        switch (peripheral_type) {
        case IMX_DMATYPE_MEMORY:
@@ -945,6 +967,17 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
                emi_2_per = sdma->script_addrs->mcu_2_ata_addr;
                break;
        case IMX_DMATYPE_CSPI:
+               per_2_emi = sdma->script_addrs->app_2_mcu_addr;
+
+               /* Use rom script mcu_2_app if ERR009165 fixed */
+               if (sdmac->sdma->drvdata->ecspi_fixed) {
+                       emi_2_per = sdma->script_addrs->mcu_2_app_addr;
+               } else {
+                       emi_2_per = sdma->script_addrs->mcu_2_ecspi_addr;
+                       sdmac->is_ram_script = true;
+               }
+
+               break;
        case IMX_DMATYPE_EXT:
        case IMX_DMATYPE_SSI:
        case IMX_DMATYPE_SAI:
@@ -954,6 +987,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
        case IMX_DMATYPE_SSI_DUAL:
                per_2_emi = sdma->script_addrs->ssish_2_mcu_addr;
                emi_2_per = sdma->script_addrs->mcu_2_ssish_addr;
+               sdmac->is_ram_script = true;
                break;
        case IMX_DMATYPE_SSI_SP:
        case IMX_DMATYPE_MMC:
@@ -968,6 +1002,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
                per_2_emi = sdma->script_addrs->asrc_2_mcu_addr;
                emi_2_per = sdma->script_addrs->asrc_2_mcu_addr;
                per_2_per = sdma->script_addrs->per_2_per_addr;
+               sdmac->is_ram_script = true;
                break;
        case IMX_DMATYPE_ASRC_SP:
                per_2_emi = sdma->script_addrs->shp_2_mcu_addr;
@@ -1008,9 +1043,6 @@ static int sdma_load_context(struct sdma_channel *sdmac)
        int ret;
        unsigned long flags;
 
-       if (sdmac->context_loaded)
-               return 0;
-
        if (sdmac->direction == DMA_DEV_TO_MEM)
                load_address = sdmac->pc_from_device;
        else if (sdmac->direction == DMA_DEV_TO_DEV)
@@ -1053,8 +1085,6 @@ static int sdma_load_context(struct sdma_channel *sdmac)
 
        spin_unlock_irqrestore(&sdma->channel_0_lock, flags);
 
-       sdmac->context_loaded = true;
-
        return ret;
 }
 
@@ -1078,9 +1108,6 @@ static void sdma_channel_terminate_work(struct work_struct *work)
 {
        struct sdma_channel *sdmac = container_of(work, struct sdma_channel,
                                                  terminate_worker);
-       unsigned long flags;
-       LIST_HEAD(head);
-
        /*
         * According to NXP R&D team a delay of one BD SDMA cost time
         * (maximum is 1ms) should be added after disable of the channel
@@ -1089,11 +1116,7 @@ static void sdma_channel_terminate_work(struct work_struct *work)
         */
        usleep_range(1000, 2000);
 
-       spin_lock_irqsave(&sdmac->vc.lock, flags);
-       vchan_get_all_descriptors(&sdmac->vc, &head);
-       spin_unlock_irqrestore(&sdmac->vc.lock, flags);
-       vchan_dma_desc_free_list(&sdmac->vc, &head);
-       sdmac->context_loaded = false;
+       vchan_dma_desc_free_list(&sdmac->vc, &sdmac->terminated);
 }
 
 static int sdma_terminate_all(struct dma_chan *chan)
@@ -1107,6 +1130,13 @@ static int sdma_terminate_all(struct dma_chan *chan)
 
        if (sdmac->desc) {
                vchan_terminate_vdesc(&sdmac->desc->vd);
+               /*
+                * move out current descriptor into terminated list so that
+                * it could be free in sdma_channel_terminate_work alone
+                * later without potential involving next descriptor raised
+                * up before the last descriptor terminated.
+                */
+               vchan_get_all_descriptors(&sdmac->vc, &sdmac->terminated);
                sdmac->desc = NULL;
                schedule_work(&sdmac->terminate_worker);
        }
@@ -1168,7 +1198,6 @@ static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
 static int sdma_config_channel(struct dma_chan *chan)
 {
        struct sdma_channel *sdmac = to_sdma_chan(chan);
-       int ret;
 
        sdma_disable_channel(chan);
 
@@ -1208,9 +1237,7 @@ static int sdma_config_channel(struct dma_chan *chan)
                sdmac->watermark_level = 0; /* FIXME: M3_BASE_ADDRESS */
        }
 
-       ret = sdma_load_context(sdmac);
-
-       return ret;
+       return 0;
 }
 
 static int sdma_set_channel_priority(struct sdma_channel *sdmac,
@@ -1361,7 +1388,6 @@ static void sdma_free_chan_resources(struct dma_chan *chan)
 
        sdmac->event_id0 = 0;
        sdmac->event_id1 = 0;
-       sdmac->context_loaded = false;
 
        sdma_set_channel_priority(sdmac, 0);
 
@@ -1374,6 +1400,11 @@ static struct sdma_desc *sdma_transfer_init(struct sdma_channel *sdmac,
 {
        struct sdma_desc *desc;
 
+       if (!sdmac->sdma->fw_loaded && sdmac->is_ram_script) {
+               dev_warn_once(sdmac->sdma->dev, "sdma firmware not ready!\n");
+               goto err_out;
+       }
+
        desc = kzalloc((sizeof(*desc)), GFP_NOWAIT);
        if (!desc)
                goto err_out;
@@ -1722,8 +1753,8 @@ static void sdma_issue_pending(struct dma_chan *chan)
 
 #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1        34
 #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2        38
-#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3        41
-#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V4        42
+#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3        45
+#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V4        46
 
 static void sdma_add_scripts(struct sdma_engine *sdma,
                const struct sdma_script_start_addrs *addr)
@@ -1747,6 +1778,19 @@ static void sdma_add_scripts(struct sdma_engine *sdma,
        for (i = 0; i < sdma->script_number; i++)
                if (addr_arr[i] > 0)
                        saddr_arr[i] = addr_arr[i];
+
+       /*
+        * get uart_2_mcu_addr/uartsh_2_mcu_addr rom script specially because
+        * they are now replaced by uart_2_mcu_ram_addr/uartsh_2_mcu_ram_addr
+        * to be compatible with legacy freescale/nxp sdma firmware, and they
+        * are located in the bottom part of sdma_script_start_addrs which are
+        * beyond the SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1.
+        */
+       if (addr->uart_2_mcu_addr)
+               sdma->script_addrs->uart_2_mcu_addr = addr->uart_2_mcu_addr;
+       if (addr->uartsh_2_mcu_addr)
+               sdma->script_addrs->uartsh_2_mcu_addr = addr->uartsh_2_mcu_addr;
+
 }
 
 static void sdma_load_firmware(const struct firmware *fw, void *context)
@@ -1803,6 +1847,8 @@ static void sdma_load_firmware(const struct firmware *fw, void *context)
 
        sdma_add_scripts(sdma, addr);
 
+       sdma->fw_loaded = true;
+
        dev_info(sdma->dev, "loaded firmware %d.%d\n",
                        header->version_major,
                        header->version_minor);
@@ -2086,6 +2132,7 @@ static int sdma_probe(struct platform_device *pdev)
 
                sdmac->channel = i;
                sdmac->vc.desc_free = sdma_desc_free;
+               INIT_LIST_HEAD(&sdmac->terminated);
                INIT_WORK(&sdmac->terminate_worker,
                                sdma_channel_terminate_work);
                /*
index 5d3fd80..220a58c 100644 (file)
@@ -6,39 +6,7 @@
 
 menu "Firmware Drivers"
 
-config ARM_SCMI_PROTOCOL
-       tristate "ARM System Control and Management Interface (SCMI) Message Protocol"
-       depends on ARM || ARM64 || COMPILE_TEST
-       depends on MAILBOX || HAVE_ARM_SMCCC_DISCOVERY
-       help
-         ARM System Control and Management Interface (SCMI) protocol is a
-         set of operating system-independent software interfaces that are
-         used in system management. SCMI is extensible and currently provides
-         interfaces for: Discovery and self-description of the interfaces
-         it supports, Power domain management which is the ability to place
-         a given device or domain into the various power-saving states that
-         it supports, Performance management which is the ability to control
-         the performance of a domain that is composed of compute engines
-         such as application processors and other accelerators, Clock
-         management which is the ability to set and inquire rates on platform
-         managed clocks and Sensor management which is the ability to read
-         sensor data, and be notified of sensor value.
-
-         This protocol library provides interface for all the client drivers
-         making use of the features offered by the SCMI.
-
-config ARM_SCMI_POWER_DOMAIN
-       tristate "SCMI power domain driver"
-       depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
-       default y
-       select PM_GENERIC_DOMAINS if PM
-       help
-         This enables support for the SCMI power domains which can be
-         enabled or disabled via the SCP firmware
-
-         This driver can also be built as a module.  If so, the module
-         will be called scmi_pm_domain. Note this may needed early in boot
-         before rootfs may be available.
+source "drivers/firmware/arm_scmi/Kconfig"
 
 config ARM_SCPI_PROTOCOL
        tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
@@ -235,7 +203,7 @@ config INTEL_STRATIX10_RSU
          Say Y here if you want Intel RSU support.
 
 config QCOM_SCM
-       bool
+       tristate "Qcom SCM driver"
        depends on ARM || ARM64
        depends on HAVE_ARM_SMCCC
        select RESET_CONTROLLER
index 705fabe..5ced067 100644 (file)
@@ -17,7 +17,8 @@ obj-$(CONFIG_ISCSI_IBFT)      += iscsi_ibft.o
 obj-$(CONFIG_FIRMWARE_MEMMAP)  += memmap.o
 obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
 obj-$(CONFIG_FW_CFG_SYSFS)     += qemu_fw_cfg.o
-obj-$(CONFIG_QCOM_SCM)         += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
+obj-$(CONFIG_QCOM_SCM)         += qcom-scm.o
+qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
 obj-$(CONFIG_SYSFB)            += sysfb.o
 obj-$(CONFIG_SYSFB_SIMPLEFB)   += sysfb_simplefb.o
 obj-$(CONFIG_TI_SCI_PROTOCOL)  += ti_sci.o
diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig
new file mode 100644 (file)
index 0000000..7f4d243
--- /dev/null
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "ARM System Control and Management Interface Protocol"
+
+config ARM_SCMI_PROTOCOL
+       tristate "ARM System Control and Management Interface (SCMI) Message Protocol"
+       depends on ARM || ARM64 || COMPILE_TEST
+       help
+         ARM System Control and Management Interface (SCMI) protocol is a
+         set of operating system-independent software interfaces that are
+         used in system management. SCMI is extensible and currently provides
+         interfaces for: Discovery and self-description of the interfaces
+         it supports, Power domain management which is the ability to place
+         a given device or domain into the various power-saving states that
+         it supports, Performance management which is the ability to control
+         the performance of a domain that is composed of compute engines
+         such as application processors and other accelerators, Clock
+         management which is the ability to set and inquire rates on platform
+         managed clocks and Sensor management which is the ability to read
+         sensor data, and be notified of sensor value.
+
+         This protocol library provides interface for all the client drivers
+         making use of the features offered by the SCMI.
+
+if ARM_SCMI_PROTOCOL
+
+config ARM_SCMI_HAVE_TRANSPORT
+       bool
+       help
+         This declares whether at least one SCMI transport has been configured.
+         Used to trigger a build bug when trying to build SCMI without any
+         configured transport.
+
+config ARM_SCMI_HAVE_SHMEM
+       bool
+       help
+         This declares whether a shared memory based transport for SCMI is
+         available.
+
+config ARM_SCMI_HAVE_MSG
+       bool
+       help
+         This declares whether a message passing based transport for SCMI is
+         available.
+
+config ARM_SCMI_TRANSPORT_MAILBOX
+       bool "SCMI transport based on Mailbox"
+       depends on MAILBOX
+       select ARM_SCMI_HAVE_TRANSPORT
+       select ARM_SCMI_HAVE_SHMEM
+       default y
+       help
+         Enable mailbox based transport for SCMI.
+
+         If you want the ARM SCMI PROTOCOL stack to include support for a
+         transport based on mailboxes, answer Y.
+
+config ARM_SCMI_TRANSPORT_SMC
+       bool "SCMI transport based on SMC"
+       depends on HAVE_ARM_SMCCC_DISCOVERY
+       select ARM_SCMI_HAVE_TRANSPORT
+       select ARM_SCMI_HAVE_SHMEM
+       default y
+       help
+         Enable SMC based transport for SCMI.
+
+         If you want the ARM SCMI PROTOCOL stack to include support for a
+         transport based on SMC, answer Y.
+
+config ARM_SCMI_TRANSPORT_VIRTIO
+       bool "SCMI transport based on VirtIO"
+       depends on VIRTIO
+       select ARM_SCMI_HAVE_TRANSPORT
+       select ARM_SCMI_HAVE_MSG
+       help
+         This enables the virtio based transport for SCMI.
+
+         If you want the ARM SCMI PROTOCOL stack to include support for a
+         transport based on VirtIO, answer Y.
+
+endif #ARM_SCMI_PROTOCOL
+
+config ARM_SCMI_POWER_DOMAIN
+       tristate "SCMI power domain driver"
+       depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
+       default y
+       select PM_GENERIC_DOMAINS if PM
+       help
+         This enables support for the SCMI power domains which can be
+         enabled or disabled via the SCP firmware
+
+         This driver can also be built as a module.  If so, the module
+         will be called scmi_pm_domain. Note this may needed early in boot
+         before rootfs may be available.
+
+endmenu
index 6a2ef63..1dcf123 100644 (file)
@@ -1,9 +1,11 @@
 # SPDX-License-Identifier: GPL-2.0-only
 scmi-bus-y = bus.o
 scmi-driver-y = driver.o notify.o
-scmi-transport-y = shmem.o
-scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
-scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
+scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o
+scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_MAILBOX) += mailbox.o
+scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += smc.o
+scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
+scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO) += virtio.o
 scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o
 scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
                    $(scmi-transport-y)
index 8685619..dea1bfb 100644 (file)
 #include <linux/device.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
+#include <linux/hashtable.h>
+#include <linux/list.h>
 #include <linux/module.h>
+#include <linux/refcount.h>
 #include <linux/scmi_protocol.h>
+#include <linux/spinlock.h>
 #include <linux/types.h>
 
 #include <asm/unaligned.h>
@@ -65,11 +69,22 @@ struct scmi_msg_resp_prot_version {
 #define MSG_XTRACT_TOKEN(hdr)  FIELD_GET(MSG_TOKEN_ID_MASK, (hdr))
 #define MSG_TOKEN_MAX          (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1)
 
+/*
+ * Size of @pending_xfers hashtable included in @scmi_xfers_info; ideally, in
+ * order to minimize space and collisions, this should equal max_msg, i.e. the
+ * maximum number of in-flight messages on a specific platform, but such value
+ * is only available at runtime while kernel hashtables are statically sized:
+ * pick instead as a fixed static size the maximum number of entries that can
+ * fit the whole table into one 4k page.
+ */
+#define SCMI_PENDING_XFERS_HT_ORDER_SZ         9
+
 /**
  * struct scmi_msg_hdr - Message(Tx/Rx) header
  *
  * @id: The identifier of the message being sent
  * @protocol_id: The identifier of the protocol used to send @id message
+ * @type: The SCMI type for this message
  * @seq: The token to identify the message. When a message returns, the
  *     platform returns the whole message header unmodified including the
  *     token
@@ -80,6 +95,7 @@ struct scmi_msg_resp_prot_version {
 struct scmi_msg_hdr {
        u8 id;
        u8 protocol_id;
+       u8 type;
        u16 seq;
        u32 status;
        bool poll_completion;
@@ -89,13 +105,14 @@ struct scmi_msg_hdr {
  * pack_scmi_header() - packs and returns 32-bit header
  *
  * @hdr: pointer to header containing all the information on message id,
- *     protocol id and sequence id.
+ *     protocol id, sequence id and type.
  *
  * Return: 32-bit packed message header to be sent to the platform.
  */
 static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr)
 {
        return FIELD_PREP(MSG_ID_MASK, hdr->id) |
+               FIELD_PREP(MSG_TYPE_MASK, hdr->type) |
                FIELD_PREP(MSG_TOKEN_ID_MASK, hdr->seq) |
                FIELD_PREP(MSG_PROTOCOL_ID_MASK, hdr->protocol_id);
 }
@@ -110,6 +127,7 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr)
 {
        hdr->id = MSG_XTRACT_ID(msg_hdr);
        hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr);
+       hdr->type = MSG_XTRACT_TYPE(msg_hdr);
 }
 
 /**
@@ -134,6 +152,27 @@ struct scmi_msg {
  *     buffer for the rx path as we use for the tx path.
  * @done: command message transmit completion event
  * @async_done: pointer to delayed response message received event completion
+ * @pending: True for xfers added to @pending_xfers hashtable
+ * @node: An hlist_node reference used to store this xfer, alternatively, on
+ *       the free list @free_xfers or in the @pending_xfers hashtable
+ * @users: A refcount to track the active users for this xfer.
+ *        This is meant to protect against the possibility that, when a command
+ *        transaction times out concurrently with the reception of a valid
+ *        response message, the xfer could be finally put on the TX path, and
+ *        so vanish, while on the RX path scmi_rx_callback() is still
+ *        processing it: in such a case this refcounting will ensure that, even
+ *        though the timed-out transaction will anyway cause the command
+ *        request to be reported as failed by time-out, the underlying xfer
+ *        cannot be discarded and possibly reused until the last one user on
+ *        the RX path has released it.
+ * @busy: An atomic flag to ensure exclusive write access to this xfer
+ * @state: The current state of this transfer, with states transitions deemed
+ *        valid being:
+ *         - SCMI_XFER_SENT_OK -> SCMI_XFER_RESP_OK [ -> SCMI_XFER_DRESP_OK ]
+ *         - SCMI_XFER_SENT_OK -> SCMI_XFER_DRESP_OK
+ *           (Missing synchronous response is assumed OK and ignored)
+ * @lock: A spinlock to protect state and busy fields.
+ * @priv: A pointer for transport private usage.
  */
 struct scmi_xfer {
        int transfer_id;
@@ -142,8 +181,36 @@ struct scmi_xfer {
        struct scmi_msg rx;
        struct completion done;
        struct completion *async_done;
+       bool pending;
+       struct hlist_node node;
+       refcount_t users;
+#define SCMI_XFER_FREE         0
+#define SCMI_XFER_BUSY         1
+       atomic_t busy;
+#define SCMI_XFER_SENT_OK      0
+#define SCMI_XFER_RESP_OK      1
+#define SCMI_XFER_DRESP_OK     2
+       int state;
+       /* A lock to protect state and busy fields */
+       spinlock_t lock;
+       void *priv;
 };
 
+/*
+ * An helper macro to lookup an xfer from the @pending_xfers hashtable
+ * using the message sequence number token as a key.
+ */
+#define XFER_FIND(__ht, __k)                                   \
+({                                                             \
+       typeof(__k) k_ = __k;                                   \
+       struct scmi_xfer *xfer_ = NULL;                         \
+                                                               \
+       hash_for_each_possible((__ht), xfer_, node, k_)         \
+               if (xfer_->hdr.seq == k_)                       \
+                       break;                                  \
+       xfer_;                                                  \
+})
+
 struct scmi_xfer_ops;
 
 /**
@@ -283,9 +350,13 @@ struct scmi_chan_info {
 /**
  * struct scmi_transport_ops - Structure representing a SCMI transport ops
  *
+ * @link_supplier: Optional callback to add link to a supplier device
  * @chan_available: Callback to check if channel is available or not
  * @chan_setup: Callback to allocate and setup a channel
  * @chan_free: Callback to free a channel
+ * @get_max_msg: Optional callback to provide max_msg dynamically
+ *              Returns the maximum number of messages for the channel type
+ *              (tx or rx) that can be pending simultaneously in the system
  * @send_message: Callback to send a message
  * @mark_txdone: Callback to mark tx as done
  * @fetch_response: Callback to fetch response
@@ -294,10 +365,12 @@ struct scmi_chan_info {
  * @poll_done: Callback to poll transfer status
  */
 struct scmi_transport_ops {
+       int (*link_supplier)(struct device *dev);
        bool (*chan_available)(struct device *dev, int idx);
        int (*chan_setup)(struct scmi_chan_info *cinfo, struct device *dev,
                          bool tx);
        int (*chan_free)(int id, void *p, void *data);
+       unsigned int (*get_max_msg)(struct scmi_chan_info *base_cinfo);
        int (*send_message)(struct scmi_chan_info *cinfo,
                            struct scmi_xfer *xfer);
        void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret);
@@ -317,25 +390,39 @@ struct scmi_device *scmi_child_dev_find(struct device *parent,
 /**
  * struct scmi_desc - Description of SoC integration
  *
+ * @transport_init: An optional function that a transport can provide to
+ *                 initialize some transport-specific setup during SCMI core
+ *                 initialization, so ahead of SCMI core probing.
+ * @transport_exit: An optional function that a transport can provide to
+ *                 de-initialize some transport-specific setup during SCMI core
+ *                 de-initialization, so after SCMI core removal.
  * @ops: Pointer to the transport specific ops structure
  * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
- * @max_msg: Maximum number of messages that can be pending
- *     simultaneously in the system
+ * @max_msg: Maximum number of messages for a channel type (tx or rx) that can
+ *     be pending simultaneously in the system. May be overridden by the
+ *     get_max_msg op.
  * @max_msg_size: Maximum size of data per message that can be handled.
  */
 struct scmi_desc {
+       int (*transport_init)(void);
+       void (*transport_exit)(void);
        const struct scmi_transport_ops *ops;
        int max_rx_timeout_ms;
        int max_msg;
        int max_msg_size;
 };
 
+#ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
 extern const struct scmi_desc scmi_mailbox_desc;
-#ifdef CONFIG_HAVE_ARM_SMCCC_DISCOVERY
+#endif
+#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
 extern const struct scmi_desc scmi_smc_desc;
 #endif
+#ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO
+extern const struct scmi_desc scmi_virtio_desc;
+#endif
 
-void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr);
+void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv);
 void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
 
 /* shmem related declarations */
@@ -352,8 +439,22 @@ void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
 bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
                     struct scmi_xfer *xfer);
 
+/* declarations for message passing transports */
+struct scmi_msg_payld;
+
+/* Maximum overhead of message w.r.t. struct scmi_desc.max_msg_size */
+#define SCMI_MSG_MAX_PROT_OVERHEAD (2 * sizeof(__le32))
+
+size_t msg_response_size(struct scmi_xfer *xfer);
+size_t msg_command_size(struct scmi_xfer *xfer);
+void msg_tx_prepare(struct scmi_msg_payld *msg, struct scmi_xfer *xfer);
+u32 msg_read_header(struct scmi_msg_payld *msg);
+void msg_fetch_response(struct scmi_msg_payld *msg, size_t len,
+                       struct scmi_xfer *xfer);
+void msg_fetch_notification(struct scmi_msg_payld *msg, size_t len,
+                           size_t max_len, struct scmi_xfer *xfer);
+
 void scmi_notification_instance_data_set(const struct scmi_handle *handle,
                                         void *priv);
 void *scmi_notification_instance_data_get(const struct scmi_handle *handle);
-
 #endif /* _SCMI_COMMON_H */
index 9b2e8d4..b406b3f 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/ktime.h>
+#include <linux/hashtable.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
@@ -67,16 +68,23 @@ struct scmi_requested_dev {
 /**
  * struct scmi_xfers_info - Structure to manage transfer information
  *
- * @xfer_block: Preallocated Message array
  * @xfer_alloc_table: Bitmap table for allocated messages.
  *     Index of this bitmap table is also used for message
  *     sequence identifier.
  * @xfer_lock: Protection for message allocation
+ * @max_msg: Maximum number of messages that can be pending
+ * @free_xfers: A free list for available to use xfers. It is initialized with
+ *             a number of xfers equal to the maximum allowed in-flight
+ *             messages.
+ * @pending_xfers: An hashtable, indexed by msg_hdr.seq, used to keep all the
+ *                currently in-flight messages.
  */
 struct scmi_xfers_info {
-       struct scmi_xfer *xfer_block;
        unsigned long *xfer_alloc_table;
        spinlock_t xfer_lock;
+       int max_msg;
+       struct hlist_head free_xfers;
+       DECLARE_HASHTABLE(pending_xfers, SCMI_PENDING_XFERS_HT_ORDER_SZ);
 };
 
 /**
@@ -172,19 +180,6 @@ static inline int scmi_to_linux_errno(int errno)
        return -EIO;
 }
 
-/**
- * scmi_dump_header_dbg() - Helper to dump a message header.
- *
- * @dev: Device pointer corresponding to the SCMI entity
- * @hdr: pointer to header.
- */
-static inline void scmi_dump_header_dbg(struct device *dev,
-                                       struct scmi_msg_hdr *hdr)
-{
-       dev_dbg(dev, "Message ID: %x Sequence ID: %x Protocol: %x\n",
-               hdr->id, hdr->seq, hdr->protocol_id);
-}
-
 void scmi_notification_instance_data_set(const struct scmi_handle *handle,
                                         void *priv)
 {
@@ -204,46 +199,190 @@ void *scmi_notification_instance_data_get(const struct scmi_handle *handle)
        return info->notify_priv;
 }
 
+/**
+ * scmi_xfer_token_set  - Reserve and set new token for the xfer at hand
+ *
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ * @xfer: The xfer to act upon
+ *
+ * Pick the next unused monotonically increasing token and set it into
+ * xfer->hdr.seq: picking a monotonically increasing value avoids immediate
+ * reuse of freshly completed or timed-out xfers, thus mitigating the risk
+ * of incorrect association of a late and expired xfer with a live in-flight
+ * transaction, both happening to re-use the same token identifier.
+ *
+ * Since platform is NOT required to answer our request in-order we should
+ * account for a few rare but possible scenarios:
+ *
+ *  - exactly 'next_token' may be NOT available so pick xfer_id >= next_token
+ *    using find_next_zero_bit() starting from candidate next_token bit
+ *
+ *  - all tokens ahead upto (MSG_TOKEN_ID_MASK - 1) are used in-flight but we
+ *    are plenty of free tokens at start, so try a second pass using
+ *    find_next_zero_bit() and starting from 0.
+ *
+ *  X = used in-flight
+ *
+ * Normal
+ * ------
+ *
+ *             |- xfer_id picked
+ *   -----------+----------------------------------------------------------
+ *   | | |X|X|X| | | | | | ... ... ... ... ... ... ... ... ... ... ...|X|X|
+ *   ----------------------------------------------------------------------
+ *             ^
+ *             |- next_token
+ *
+ * Out-of-order pending at start
+ * -----------------------------
+ *
+ *       |- xfer_id picked, last_token fixed
+ *   -----+----------------------------------------------------------------
+ *   |X|X| | | | |X|X| ... ... ... ... ... ... ... ... ... ... ... ...|X| |
+ *   ----------------------------------------------------------------------
+ *    ^
+ *    |- next_token
+ *
+ *
+ * Out-of-order pending at end
+ * ---------------------------
+ *
+ *       |- xfer_id picked, last_token fixed
+ *   -----+----------------------------------------------------------------
+ *   |X|X| | | | |X|X| ... ... ... ... ... ... ... ... ... ... |X|X|X||X|X|
+ *   ----------------------------------------------------------------------
+ *                                                             ^
+ *                                                             |- next_token
+ *
+ * Context: Assumes to be called with @xfer_lock already acquired.
+ *
+ * Return: 0 on Success or error
+ */
+static int scmi_xfer_token_set(struct scmi_xfers_info *minfo,
+                              struct scmi_xfer *xfer)
+{
+       unsigned long xfer_id, next_token;
+
+       /*
+        * Pick a candidate monotonic token in range [0, MSG_TOKEN_MAX - 1]
+        * using the pre-allocated transfer_id as a base.
+        * Note that the global transfer_id is shared across all message types
+        * so there could be holes in the allocated set of monotonic sequence
+        * numbers, but that is going to limit the effectiveness of the
+        * mitigation only in very rare limit conditions.
+        */
+       next_token = (xfer->transfer_id & (MSG_TOKEN_MAX - 1));
+
+       /* Pick the next available xfer_id >= next_token */
+       xfer_id = find_next_zero_bit(minfo->xfer_alloc_table,
+                                    MSG_TOKEN_MAX, next_token);
+       if (xfer_id == MSG_TOKEN_MAX) {
+               /*
+                * After heavily out-of-order responses, there are no free
+                * tokens ahead, but only at start of xfer_alloc_table so
+                * try again from the beginning.
+                */
+               xfer_id = find_next_zero_bit(minfo->xfer_alloc_table,
+                                            MSG_TOKEN_MAX, 0);
+               /*
+                * Something is wrong if we got here since there can be a
+                * maximum number of (MSG_TOKEN_MAX - 1) in-flight messages
+                * but we have not found any free token [0, MSG_TOKEN_MAX - 1].
+                */
+               if (WARN_ON_ONCE(xfer_id == MSG_TOKEN_MAX))
+                       return -ENOMEM;
+       }
+
+       /* Update +/- last_token accordingly if we skipped some hole */
+       if (xfer_id != next_token)
+               atomic_add((int)(xfer_id - next_token), &transfer_last_id);
+
+       /* Set in-flight */
+       set_bit(xfer_id, minfo->xfer_alloc_table);
+       xfer->hdr.seq = (u16)xfer_id;
+
+       return 0;
+}
+
+/**
+ * scmi_xfer_token_clear  - Release the token
+ *
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ * @xfer: The xfer to act upon
+ */
+static inline void scmi_xfer_token_clear(struct scmi_xfers_info *minfo,
+                                        struct scmi_xfer *xfer)
+{
+       clear_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
+}
+
 /**
  * scmi_xfer_get() - Allocate one message
  *
  * @handle: Pointer to SCMI entity handle
  * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ * @set_pending: If true a monotonic token is picked and the xfer is added to
+ *              the pending hash table.
  *
  * Helper function which is used by various message functions that are
  * exposed to clients of this driver for allocating a message traffic event.
  *
- * This function can sleep depending on pending requests already in the system
- * for the SCMI entity. Further, this also holds a spinlock to maintain
- * integrity of internal data structures.
+ * Picks an xfer from the free list @free_xfers (if any available) and, if
+ * required, sets a monotonically increasing token and stores the inflight xfer
+ * into the @pending_xfers hashtable for later retrieval.
+ *
+ * The successfully initialized xfer is refcounted.
+ *
+ * Context: Holds @xfer_lock while manipulating @xfer_alloc_table and
+ *         @free_xfers.
  *
  * Return: 0 if all went fine, else corresponding error.
  */
 static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
-                                      struct scmi_xfers_info *minfo)
+                                      struct scmi_xfers_info *minfo,
+                                      bool set_pending)
 {
-       u16 xfer_id;
+       int ret;
+       unsigned long flags;
        struct scmi_xfer *xfer;
-       unsigned long flags, bit_pos;
-       struct scmi_info *info = handle_to_scmi_info(handle);
 
-       /* Keep the locked section as small as possible */
        spin_lock_irqsave(&minfo->xfer_lock, flags);
-       bit_pos = find_first_zero_bit(minfo->xfer_alloc_table,
-                                     info->desc->max_msg);
-       if (bit_pos == info->desc->max_msg) {
+       if (hlist_empty(&minfo->free_xfers)) {
                spin_unlock_irqrestore(&minfo->xfer_lock, flags);
                return ERR_PTR(-ENOMEM);
        }
-       set_bit(bit_pos, minfo->xfer_alloc_table);
-       spin_unlock_irqrestore(&minfo->xfer_lock, flags);
 
-       xfer_id = bit_pos;
+       /* grab an xfer from the free_list */
+       xfer = hlist_entry(minfo->free_xfers.first, struct scmi_xfer, node);
+       hlist_del_init(&xfer->node);
 
-       xfer = &minfo->xfer_block[xfer_id];
-       xfer->hdr.seq = xfer_id;
+       /*
+        * Allocate transfer_id early so that can be used also as base for
+        * monotonic sequence number generation if needed.
+        */
        xfer->transfer_id = atomic_inc_return(&transfer_last_id);
 
+       if (set_pending) {
+               /* Pick and set monotonic token */
+               ret = scmi_xfer_token_set(minfo, xfer);
+               if (!ret) {
+                       hash_add(minfo->pending_xfers, &xfer->node,
+                                xfer->hdr.seq);
+                       xfer->pending = true;
+               } else {
+                       dev_err(handle->dev,
+                               "Failed to get monotonic token %d\n", ret);
+                       hlist_add_head(&xfer->node, &minfo->free_xfers);
+                       xfer = ERR_PTR(ret);
+               }
+       }
+
+       if (!IS_ERR(xfer)) {
+               refcount_set(&xfer->users, 1);
+               atomic_set(&xfer->busy, SCMI_XFER_FREE);
+       }
+       spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+
        return xfer;
 }
 
@@ -253,6 +392,9 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
  * @minfo: Pointer to Tx/Rx Message management info based on channel type
  * @xfer: message that was reserved by scmi_xfer_get
  *
+ * After refcount check, possibly release an xfer, clearing the token slot,
+ * removing xfer from @pending_xfers and putting it back into free_xfers.
+ *
  * This holds a spinlock to maintain integrity of internal data structures.
  */
 static void
@@ -260,17 +402,215 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
 {
        unsigned long flags;
 
+       spin_lock_irqsave(&minfo->xfer_lock, flags);
+       if (refcount_dec_and_test(&xfer->users)) {
+               if (xfer->pending) {
+                       scmi_xfer_token_clear(minfo, xfer);
+                       hash_del(&xfer->node);
+                       xfer->pending = false;
+               }
+               hlist_add_head(&xfer->node, &minfo->free_xfers);
+       }
+       spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+}
+
+/**
+ * scmi_xfer_lookup_unlocked  -  Helper to lookup an xfer_id
+ *
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ * @xfer_id: Token ID to lookup in @pending_xfers
+ *
+ * Refcounting is untouched.
+ *
+ * Context: Assumes to be called with @xfer_lock already acquired.
+ *
+ * Return: A valid xfer on Success or error otherwise
+ */
+static struct scmi_xfer *
+scmi_xfer_lookup_unlocked(struct scmi_xfers_info *minfo, u16 xfer_id)
+{
+       struct scmi_xfer *xfer = NULL;
+
+       if (test_bit(xfer_id, minfo->xfer_alloc_table))
+               xfer = XFER_FIND(minfo->pending_xfers, xfer_id);
+
+       return xfer ?: ERR_PTR(-EINVAL);
+}
+
+/**
+ * scmi_msg_response_validate  - Validate message type against state of related
+ * xfer
+ *
+ * @cinfo: A reference to the channel descriptor.
+ * @msg_type: Message type to check
+ * @xfer: A reference to the xfer to validate against @msg_type
+ *
+ * This function checks if @msg_type is congruent with the current state of
+ * a pending @xfer; if an asynchronous delayed response is received before the
+ * related synchronous response (Out-of-Order Delayed Response) the missing
+ * synchronous response is assumed to be OK and completed, carrying on with the
+ * Delayed Response: this is done to address the case in which the underlying
+ * SCMI transport can deliver such out-of-order responses.
+ *
+ * Context: Assumes to be called with xfer->lock already acquired.
+ *
+ * Return: 0 on Success, error otherwise
+ */
+static inline int scmi_msg_response_validate(struct scmi_chan_info *cinfo,
+                                            u8 msg_type,
+                                            struct scmi_xfer *xfer)
+{
        /*
-        * Keep the locked section as small as possible
-        * NOTE: we might escape with smp_mb and no lock here..
-        * but just be conservative and symmetric.
+        * Even if a response was indeed expected on this slot at this point,
+        * a buggy platform could wrongly reply feeding us an unexpected
+        * delayed response we're not prepared to handle: bail-out safely
+        * blaming firmware.
         */
+       if (msg_type == MSG_TYPE_DELAYED_RESP && !xfer->async_done) {
+               dev_err(cinfo->dev,
+                       "Delayed Response for %d not expected! Buggy F/W ?\n",
+                       xfer->hdr.seq);
+               return -EINVAL;
+       }
+
+       switch (xfer->state) {
+       case SCMI_XFER_SENT_OK:
+               if (msg_type == MSG_TYPE_DELAYED_RESP) {
+                       /*
+                        * Delayed Response expected but delivered earlier.
+                        * Assume message RESPONSE was OK and skip state.
+                        */
+                       xfer->hdr.status = SCMI_SUCCESS;
+                       xfer->state = SCMI_XFER_RESP_OK;
+                       complete(&xfer->done);
+                       dev_warn(cinfo->dev,
+                                "Received valid OoO Delayed Response for %d\n",
+                                xfer->hdr.seq);
+               }
+               break;
+       case SCMI_XFER_RESP_OK:
+               if (msg_type != MSG_TYPE_DELAYED_RESP)
+                       return -EINVAL;
+               break;
+       case SCMI_XFER_DRESP_OK:
+               /* No further message expected once in SCMI_XFER_DRESP_OK */
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * scmi_xfer_state_update  - Update xfer state
+ *
+ * @xfer: A reference to the xfer to update
+ * @msg_type: Type of message being processed.
+ *
+ * Note that this message is assumed to have been already successfully validated
+ * by @scmi_msg_response_validate(), so here we just update the state.
+ *
+ * Context: Assumes to be called on an xfer exclusively acquired using the
+ *         busy flag.
+ */
+static inline void scmi_xfer_state_update(struct scmi_xfer *xfer, u8 msg_type)
+{
+       xfer->hdr.type = msg_type;
+
+       /* Unknown command types were already discarded earlier */
+       if (xfer->hdr.type == MSG_TYPE_COMMAND)
+               xfer->state = SCMI_XFER_RESP_OK;
+       else
+               xfer->state = SCMI_XFER_DRESP_OK;
+}
+
+static bool scmi_xfer_acquired(struct scmi_xfer *xfer)
+{
+       int ret;
+
+       ret = atomic_cmpxchg(&xfer->busy, SCMI_XFER_FREE, SCMI_XFER_BUSY);
+
+       return ret == SCMI_XFER_FREE;
+}
+
+/**
+ * scmi_xfer_command_acquire  -  Helper to lookup and acquire a command xfer
+ *
+ * @cinfo: A reference to the channel descriptor.
+ * @msg_hdr: A message header to use as lookup key
+ *
+ * When a valid xfer is found for the sequence number embedded in the provided
+ * msg_hdr, reference counting is properly updated and exclusive access to this
+ * xfer is granted till released with @scmi_xfer_command_release.
+ *
+ * Return: A valid @xfer on Success or error otherwise.
+ */
+static inline struct scmi_xfer *
+scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
+{
+       int ret;
+       unsigned long flags;
+       struct scmi_xfer *xfer;
+       struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
+       struct scmi_xfers_info *minfo = &info->tx_minfo;
+       u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);
+       u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
+
+       /* Are we even expecting this? */
        spin_lock_irqsave(&minfo->xfer_lock, flags);
-       clear_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
+       xfer = scmi_xfer_lookup_unlocked(minfo, xfer_id);
+       if (IS_ERR(xfer)) {
+               dev_err(cinfo->dev,
+                       "Message for %d type %d is not expected!\n",
+                       xfer_id, msg_type);
+               spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+               return xfer;
+       }
+       refcount_inc(&xfer->users);
        spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+
+       spin_lock_irqsave(&xfer->lock, flags);
+       ret = scmi_msg_response_validate(cinfo, msg_type, xfer);
+       /*
+        * If a pending xfer was found which was also in a congruent state with
+        * the received message, acquire exclusive access to it setting the busy
+        * flag.
+        * Spins only on the rare limit condition of concurrent reception of
+        * RESP and DRESP for the same xfer.
+        */
+       if (!ret) {
+               spin_until_cond(scmi_xfer_acquired(xfer));
+               scmi_xfer_state_update(xfer, msg_type);
+       }
+       spin_unlock_irqrestore(&xfer->lock, flags);
+
+       if (ret) {
+               dev_err(cinfo->dev,
+                       "Invalid message type:%d for %d - HDR:0x%X  state:%d\n",
+                       msg_type, xfer_id, msg_hdr, xfer->state);
+               /* On error the refcount incremented above has to be dropped */
+               __scmi_xfer_put(minfo, xfer);
+               xfer = ERR_PTR(-EINVAL);
+       }
+
+       return xfer;
+}
+
+static inline void scmi_xfer_command_release(struct scmi_info *info,
+                                            struct scmi_xfer *xfer)
+{
+       atomic_set(&xfer->busy, SCMI_XFER_FREE);
+       __scmi_xfer_put(&info->tx_minfo, xfer);
+}
+
+static inline void scmi_clear_channel(struct scmi_info *info,
+                                     struct scmi_chan_info *cinfo)
+{
+       if (info->desc->ops->clear_channel)
+               info->desc->ops->clear_channel(cinfo);
 }
 
-static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
+static void scmi_handle_notification(struct scmi_chan_info *cinfo,
+                                    u32 msg_hdr, void *priv)
 {
        struct scmi_xfer *xfer;
        struct device *dev = cinfo->dev;
@@ -279,16 +619,17 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
        ktime_t ts;
 
        ts = ktime_get_boottime();
-       xfer = scmi_xfer_get(cinfo->handle, minfo);
+       xfer = scmi_xfer_get(cinfo->handle, minfo, false);
        if (IS_ERR(xfer)) {
                dev_err(dev, "failed to get free message slot (%ld)\n",
                        PTR_ERR(xfer));
-               info->desc->ops->clear_channel(cinfo);
+               scmi_clear_channel(info, cinfo);
                return;
        }
 
        unpack_scmi_header(msg_hdr, &xfer->hdr);
-       scmi_dump_header_dbg(dev, &xfer->hdr);
+       if (priv)
+               xfer->priv = priv;
        info->desc->ops->fetch_notification(cinfo, info->desc->max_msg_size,
                                            xfer);
        scmi_notify(cinfo->handle, xfer->hdr.protocol_id,
@@ -300,59 +641,41 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
 
        __scmi_xfer_put(minfo, xfer);
 
-       info->desc->ops->clear_channel(cinfo);
+       scmi_clear_channel(info, cinfo);
 }
 
 static void scmi_handle_response(struct scmi_chan_info *cinfo,
-                                u16 xfer_id, u8 msg_type)
+                                u32 msg_hdr, void *priv)
 {
        struct scmi_xfer *xfer;
-       struct device *dev = cinfo->dev;
        struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
-       struct scmi_xfers_info *minfo = &info->tx_minfo;
 
-       /* Are we even expecting this? */
-       if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
-               dev_err(dev, "message for %d is not expected!\n", xfer_id);
-               info->desc->ops->clear_channel(cinfo);
-               return;
-       }
-
-       xfer = &minfo->xfer_block[xfer_id];
-       /*
-        * Even if a response was indeed expected on this slot at this point,
-        * a buggy platform could wrongly reply feeding us an unexpected
-        * delayed response we're not prepared to handle: bail-out safely
-        * blaming firmware.
-        */
-       if (unlikely(msg_type == MSG_TYPE_DELAYED_RESP && !xfer->async_done)) {
-               dev_err(dev,
-                       "Delayed Response for %d not expected! Buggy F/W ?\n",
-                       xfer_id);
-               info->desc->ops->clear_channel(cinfo);
-               /* It was unexpected, so nobody will clear the xfer if not us */
-               __scmi_xfer_put(minfo, xfer);
+       xfer = scmi_xfer_command_acquire(cinfo, msg_hdr);
+       if (IS_ERR(xfer)) {
+               scmi_clear_channel(info, cinfo);
                return;
        }
 
        /* rx.len could be shrunk in the sync do_xfer, so reset to maxsz */
-       if (msg_type == MSG_TYPE_DELAYED_RESP)
+       if (xfer->hdr.type == MSG_TYPE_DELAYED_RESP)
                xfer->rx.len = info->desc->max_msg_size;
 
-       scmi_dump_header_dbg(dev, &xfer->hdr);
-
+       if (priv)
+               xfer->priv = priv;
        info->desc->ops->fetch_response(cinfo, xfer);
 
        trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
                           xfer->hdr.protocol_id, xfer->hdr.seq,
-                          msg_type);
+                          xfer->hdr.type);
 
-       if (msg_type == MSG_TYPE_DELAYED_RESP) {
-               info->desc->ops->clear_channel(cinfo);
+       if (xfer->hdr.type == MSG_TYPE_DELAYED_RESP) {
+               scmi_clear_channel(info, cinfo);
                complete(xfer->async_done);
        } else {
                complete(&xfer->done);
        }
+
+       scmi_xfer_command_release(info, xfer);
 }
 
 /**
@@ -360,6 +683,7 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
  *
  * @cinfo: SCMI channel info
  * @msg_hdr: Message header
+ * @priv: Transport specific private data.
  *
  * Processes one received message to appropriate transfer information and
  * signals completion of the transfer.
@@ -367,18 +691,17 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
  * NOTE: This function will be invoked in IRQ context, hence should be
  * as optimal as possible.
  */
-void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
+void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv)
 {
-       u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
        u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);
 
        switch (msg_type) {
        case MSG_TYPE_NOTIFICATION:
-               scmi_handle_notification(cinfo, msg_hdr);
+               scmi_handle_notification(cinfo, msg_hdr, priv);
                break;
        case MSG_TYPE_COMMAND:
        case MSG_TYPE_DELAYED_RESP:
-               scmi_handle_response(cinfo, xfer_id, msg_type);
+               scmi_handle_response(cinfo, msg_hdr, priv);
                break;
        default:
                WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type);
@@ -390,7 +713,7 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
  * xfer_put() - Release a transmit message
  *
  * @ph: Pointer to SCMI protocol handle
- * @xfer: message that was reserved by scmi_xfer_get
+ * @xfer: message that was reserved by xfer_get_init
  */
 static void xfer_put(const struct scmi_protocol_handle *ph,
                     struct scmi_xfer *xfer)
@@ -408,7 +731,12 @@ static bool scmi_xfer_done_no_timeout(struct scmi_chan_info *cinfo,
 {
        struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
 
+       /*
+        * Poll also on xfer->done so that polling can be forcibly terminated
+        * in case of out-of-order receptions of delayed responses
+        */
        return info->desc->ops->poll_done(cinfo, xfer) ||
+              try_wait_for_completion(&xfer->done) ||
               ktime_after(ktime_get(), stop);
 }
 
@@ -432,6 +760,12 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
        struct device *dev = info->dev;
        struct scmi_chan_info *cinfo;
 
+       if (xfer->hdr.poll_completion && !info->desc->ops->poll_done) {
+               dev_warn_once(dev,
+                             "Polling mode is not supported by transport.\n");
+               return -EINVAL;
+       }
+
        /*
         * Initialise protocol id now from protocol handle to avoid it being
         * overridden by mistake (or malice) by the protocol code mangling with
@@ -448,6 +782,16 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
                              xfer->hdr.protocol_id, xfer->hdr.seq,
                              xfer->hdr.poll_completion);
 
+       xfer->state = SCMI_XFER_SENT_OK;
+       /*
+        * Even though spinlocking is not needed here since no race is possible
+        * on xfer->state due to the monotonically increasing tokens allocation,
+        * we must anyway ensure xfer->state initialization is not re-ordered
+        * after the .send_message() to be sure that on the RX path an early
+        * ISR calling scmi_rx_callback() cannot see an old stale xfer->state.
+        */
+       smp_mb();
+
        ret = info->desc->ops->send_message(cinfo, xfer);
        if (ret < 0) {
                dev_dbg(dev, "Failed to send message %d\n", ret);
@@ -458,11 +802,22 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
                ktime_t stop = ktime_add_ns(ktime_get(), SCMI_MAX_POLL_TO_NS);
 
                spin_until_cond(scmi_xfer_done_no_timeout(cinfo, xfer, stop));
-
-               if (ktime_before(ktime_get(), stop))
-                       info->desc->ops->fetch_response(cinfo, xfer);
-               else
+               if (ktime_before(ktime_get(), stop)) {
+                       unsigned long flags;
+
+                       /*
+                        * Do not fetch_response if an out-of-order delayed
+                        * response is being processed.
+                        */
+                       spin_lock_irqsave(&xfer->lock, flags);
+                       if (xfer->state == SCMI_XFER_SENT_OK) {
+                               info->desc->ops->fetch_response(cinfo, xfer);
+                               xfer->state = SCMI_XFER_RESP_OK;
+                       }
+                       spin_unlock_irqrestore(&xfer->lock, flags);
+               } else {
                        ret = -ETIMEDOUT;
+               }
        } else {
                /* And we wait for the response. */
                timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
@@ -557,7 +912,7 @@ static int xfer_get_init(const struct scmi_protocol_handle *ph,
            tx_size > info->desc->max_msg_size)
                return -ERANGE;
 
-       xfer = scmi_xfer_get(pi->handle, minfo);
+       xfer = scmi_xfer_get(pi->handle, minfo, true);
        if (IS_ERR(xfer)) {
                ret = PTR_ERR(xfer);
                dev_err(dev, "failed to get free message slot(%d)\n", ret);
@@ -566,6 +921,7 @@ static int xfer_get_init(const struct scmi_protocol_handle *ph,
 
        xfer->tx.len = tx_size;
        xfer->rx.len = rx_size ? : info->desc->max_msg_size;
+       xfer->hdr.type = MSG_TYPE_COMMAND;
        xfer->hdr.id = msg_id;
        xfer->hdr.poll_completion = false;
 
@@ -1026,25 +1382,32 @@ static int __scmi_xfer_info_init(struct scmi_info *sinfo,
        const struct scmi_desc *desc = sinfo->desc;
 
        /* Pre-allocated messages, no more than what hdr.seq can support */
-       if (WARN_ON(!desc->max_msg || desc->max_msg > MSG_TOKEN_MAX)) {
+       if (WARN_ON(!info->max_msg || info->max_msg > MSG_TOKEN_MAX)) {
                dev_err(dev,
                        "Invalid maximum messages %d, not in range [1 - %lu]\n",
-                       desc->max_msg, MSG_TOKEN_MAX);
+                       info->max_msg, MSG_TOKEN_MAX);
                return -EINVAL;
        }
 
-       info->xfer_block = devm_kcalloc(dev, desc->max_msg,
-                                       sizeof(*info->xfer_block), GFP_KERNEL);
-       if (!info->xfer_block)
-               return -ENOMEM;
+       hash_init(info->pending_xfers);
 
-       info->xfer_alloc_table = devm_kcalloc(dev, BITS_TO_LONGS(desc->max_msg),
+       /* Allocate a bitmask sized to hold MSG_TOKEN_MAX tokens */
+       info->xfer_alloc_table = devm_kcalloc(dev, BITS_TO_LONGS(MSG_TOKEN_MAX),
                                              sizeof(long), GFP_KERNEL);
        if (!info->xfer_alloc_table)
                return -ENOMEM;
 
-       /* Pre-initialize the buffer pointer to pre-allocated buffers */
-       for (i = 0, xfer = info->xfer_block; i < desc->max_msg; i++, xfer++) {
+       /*
+        * Preallocate a number of xfers equal to max inflight messages,
+        * pre-initialize the buffer pointer to pre-allocated buffers and
+        * attach all of them to the free list
+        */
+       INIT_HLIST_HEAD(&info->free_xfers);
+       for (i = 0; i < info->max_msg; i++) {
+               xfer = devm_kzalloc(dev, sizeof(*xfer), GFP_KERNEL);
+               if (!xfer)
+                       return -ENOMEM;
+
                xfer->rx.buf = devm_kcalloc(dev, sizeof(u8), desc->max_msg_size,
                                            GFP_KERNEL);
                if (!xfer->rx.buf)
@@ -1052,6 +1415,10 @@ static int __scmi_xfer_info_init(struct scmi_info *sinfo,
 
                xfer->tx.buf = xfer->rx.buf;
                init_completion(&xfer->done);
+               spin_lock_init(&xfer->lock);
+
+               /* Add initialized xfer to the free list */
+               hlist_add_head(&xfer->node, &info->free_xfers);
        }
 
        spin_lock_init(&info->xfer_lock);
@@ -1059,10 +1426,40 @@ static int __scmi_xfer_info_init(struct scmi_info *sinfo,
        return 0;
 }
 
+static int scmi_channels_max_msg_configure(struct scmi_info *sinfo)
+{
+       const struct scmi_desc *desc = sinfo->desc;
+
+       if (!desc->ops->get_max_msg) {
+               sinfo->tx_minfo.max_msg = desc->max_msg;
+               sinfo->rx_minfo.max_msg = desc->max_msg;
+       } else {
+               struct scmi_chan_info *base_cinfo;
+
+               base_cinfo = idr_find(&sinfo->tx_idr, SCMI_PROTOCOL_BASE);
+               if (!base_cinfo)
+                       return -EINVAL;
+               sinfo->tx_minfo.max_msg = desc->ops->get_max_msg(base_cinfo);
+
+               /* RX channel is optional so can be skipped */
+               base_cinfo = idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE);
+               if (base_cinfo)
+                       sinfo->rx_minfo.max_msg =
+                               desc->ops->get_max_msg(base_cinfo);
+       }
+
+       return 0;
+}
+
 static int scmi_xfer_info_init(struct scmi_info *sinfo)
 {
-       int ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo);
+       int ret;
+
+       ret = scmi_channels_max_msg_configure(sinfo);
+       if (ret)
+               return ret;
 
+       ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo);
        if (!ret && idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE))
                ret = __scmi_xfer_info_init(sinfo, &sinfo->rx_minfo);
 
@@ -1390,6 +1787,21 @@ void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table)
        mutex_unlock(&scmi_requested_devices_mtx);
 }
 
+static int scmi_cleanup_txrx_channels(struct scmi_info *info)
+{
+       int ret;
+       struct idr *idr = &info->tx_idr;
+
+       ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
+       idr_destroy(&info->tx_idr);
+
+       idr = &info->rx_idr;
+       ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
+       idr_destroy(&info->rx_idr);
+
+       return ret;
+}
+
 static int scmi_probe(struct platform_device *pdev)
 {
        int ret;
@@ -1424,13 +1836,19 @@ static int scmi_probe(struct platform_device *pdev)
        handle->devm_protocol_get = scmi_devm_protocol_get;
        handle->devm_protocol_put = scmi_devm_protocol_put;
 
+       if (desc->ops->link_supplier) {
+               ret = desc->ops->link_supplier(dev);
+               if (ret)
+                       return ret;
+       }
+
        ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE);
        if (ret)
                return ret;
 
        ret = scmi_xfer_info_init(info);
        if (ret)
-               return ret;
+               goto clear_txrx_setup;
 
        if (scmi_notification_init(handle))
                dev_err(dev, "SCMI Notifications NOT available.\n");
@@ -1443,7 +1861,7 @@ static int scmi_probe(struct platform_device *pdev)
        ret = scmi_protocol_acquire(handle, SCMI_PROTOCOL_BASE);
        if (ret) {
                dev_err(dev, "unable to communicate with SCMI\n");
-               return ret;
+               goto notification_exit;
        }
 
        mutex_lock(&scmi_list_mutex);
@@ -1482,6 +1900,12 @@ static int scmi_probe(struct platform_device *pdev)
        }
 
        return 0;
+
+notification_exit:
+       scmi_notification_exit(&info->handle);
+clear_txrx_setup:
+       scmi_cleanup_txrx_channels(info);
+       return ret;
 }
 
 void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id)
@@ -1493,7 +1917,6 @@ static int scmi_remove(struct platform_device *pdev)
 {
        int ret = 0, id;
        struct scmi_info *info = platform_get_drvdata(pdev);
-       struct idr *idr = &info->tx_idr;
        struct device_node *child;
 
        mutex_lock(&scmi_list_mutex);
@@ -1517,14 +1940,7 @@ static int scmi_remove(struct platform_device *pdev)
        idr_destroy(&info->active_protocols);
 
        /* Safe to free channels since no more users */
-       ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
-       idr_destroy(&info->tx_idr);
-
-       idr = &info->rx_idr;
-       ret = idr_for_each(idr, info->desc->ops->chan_free, idr);
-       idr_destroy(&info->rx_idr);
-
-       return ret;
+       return scmi_cleanup_txrx_channels(info);
 }
 
 static ssize_t protocol_version_show(struct device *dev,
@@ -1575,11 +1991,14 @@ ATTRIBUTE_GROUPS(versions);
 
 /* Each compatible listed below must have descriptor associated with it */
 static const struct of_device_id scmi_of_match[] = {
-#ifdef CONFIG_MAILBOX
+#ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
        { .compatible = "arm,scmi", .data = &scmi_mailbox_desc },
 #endif
-#ifdef CONFIG_HAVE_ARM_SMCCC_DISCOVERY
+#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
        { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
+#endif
+#ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO
+       { .compatible = "arm,scmi-virtio", .data = &scmi_virtio_desc},
 #endif
        { /* Sentinel */ },
 };
@@ -1596,10 +2015,69 @@ static struct platform_driver scmi_driver = {
        .remove = scmi_remove,
 };
 
+/**
+ * __scmi_transports_setup  - Common helper to call transport-specific
+ * .init/.exit code if provided.
+ *
+ * @init: A flag to distinguish between init and exit.
+ *
+ * Note that, if provided, we invoke .init/.exit functions for all the
+ * transports currently compiled in.
+ *
+ * Return: 0 on Success.
+ */
+static inline int __scmi_transports_setup(bool init)
+{
+       int ret = 0;
+       const struct of_device_id *trans;
+
+       for (trans = scmi_of_match; trans->data; trans++) {
+               const struct scmi_desc *tdesc = trans->data;
+
+               if ((init && !tdesc->transport_init) ||
+                   (!init && !tdesc->transport_exit))
+                       continue;
+
+               if (init)
+                       ret = tdesc->transport_init();
+               else
+                       tdesc->transport_exit();
+
+               if (ret) {
+                       pr_err("SCMI transport %s FAILED initialization!\n",
+                              trans->compatible);
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int __init scmi_transports_init(void)
+{
+       return __scmi_transports_setup(true);
+}
+
+static void __exit scmi_transports_exit(void)
+{
+       __scmi_transports_setup(false);
+}
+
 static int __init scmi_driver_init(void)
 {
+       int ret;
+
+       /* Bail out if no SCMI transport was configured */
+       if (WARN_ON(!IS_ENABLED(CONFIG_ARM_SCMI_HAVE_TRANSPORT)))
+               return -EINVAL;
+
        scmi_bus_init();
 
+       /* Initialize any compiled-in transport which provided an init/exit */
+       ret = scmi_transports_init();
+       if (ret)
+               return ret;
+
        scmi_base_register();
 
        scmi_clock_register();
@@ -1628,6 +2106,8 @@ static void __exit scmi_driver_exit(void)
 
        scmi_bus_exit();
 
+       scmi_transports_exit();
+
        platform_driver_unregister(&scmi_driver);
 }
 module_exit(scmi_driver_exit);
index e3dcb58..e09eb12 100644 (file)
@@ -43,7 +43,7 @@ static void rx_callback(struct mbox_client *cl, void *m)
 {
        struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl);
 
-       scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem));
+       scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem), NULL);
 }
 
 static bool mailbox_chan_available(struct device *dev, int idx)
diff --git a/drivers/firmware/arm_scmi/msg.c b/drivers/firmware/arm_scmi/msg.c
new file mode 100644 (file)
index 0000000..d33a704
--- /dev/null
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * For transports using message passing.
+ *
+ * Derived from shm.c.
+ *
+ * Copyright (C) 2019-2021 ARM Ltd.
+ * Copyright (C) 2020-2021 OpenSynergy GmbH
+ */
+
+#include <linux/types.h>
+
+#include "common.h"
+
+/*
+ * struct scmi_msg_payld - Transport SDU layout
+ *
+ * The SCMI specification requires all parameters, message headers, return
+ * arguments or any protocol data to be expressed in little endian format only.
+ */
+struct scmi_msg_payld {
+       __le32 msg_header;
+       __le32 msg_payload[];
+};
+
+/**
+ * msg_command_size() - Actual size of transport SDU for command.
+ *
+ * @xfer: message which core has prepared for sending
+ *
+ * Return: transport SDU size.
+ */
+size_t msg_command_size(struct scmi_xfer *xfer)
+{
+       return sizeof(struct scmi_msg_payld) + xfer->tx.len;
+}
+
+/**
+ * msg_response_size() - Maximum size of transport SDU for response.
+ *
+ * @xfer: message which core has prepared for sending
+ *
+ * Return: transport SDU size.
+ */
+size_t msg_response_size(struct scmi_xfer *xfer)
+{
+       return sizeof(struct scmi_msg_payld) + sizeof(__le32) + xfer->rx.len;
+}
+
+/**
+ * msg_tx_prepare() - Set up transport SDU for command.
+ *
+ * @msg: transport SDU for command
+ * @xfer: message which is being sent
+ */
+void msg_tx_prepare(struct scmi_msg_payld *msg, struct scmi_xfer *xfer)
+{
+       msg->msg_header = cpu_to_le32(pack_scmi_header(&xfer->hdr));
+       if (xfer->tx.buf)
+               memcpy(msg->msg_payload, xfer->tx.buf, xfer->tx.len);
+}
+
+/**
+ * msg_read_header() - Read SCMI header from transport SDU.
+ *
+ * @msg: transport SDU
+ *
+ * Return: SCMI header
+ */
+u32 msg_read_header(struct scmi_msg_payld *msg)
+{
+       return le32_to_cpu(msg->msg_header);
+}
+
+/**
+ * msg_fetch_response() - Fetch response SCMI payload from transport SDU.
+ *
+ * @msg: transport SDU with response
+ * @len: transport SDU size
+ * @xfer: message being responded to
+ */
+void msg_fetch_response(struct scmi_msg_payld *msg, size_t len,
+                       struct scmi_xfer *xfer)
+{
+       size_t prefix_len = sizeof(*msg) + sizeof(msg->msg_payload[0]);
+
+       xfer->hdr.status = le32_to_cpu(msg->msg_payload[0]);
+       xfer->rx.len = min_t(size_t, xfer->rx.len,
+                            len >= prefix_len ? len - prefix_len : 0);
+
+       /* Take a copy to the rx buffer.. */
+       memcpy(xfer->rx.buf, &msg->msg_payload[1], xfer->rx.len);
+}
+
+/**
+ * msg_fetch_notification() - Fetch notification payload from transport SDU.
+ *
+ * @msg: transport SDU with notification
+ * @len: transport SDU size
+ * @max_len: maximum SCMI payload size to fetch
+ * @xfer: notification message
+ */
+void msg_fetch_notification(struct scmi_msg_payld *msg, size_t len,
+                           size_t max_len, struct scmi_xfer *xfer)
+{
+       xfer->rx.len = min_t(size_t, max_len,
+                            len >= sizeof(*msg) ? len - sizeof(*msg) : 0);
+
+       /* Take a copy to the rx buffer.. */
+       memcpy(xfer->rx.buf, msg->msg_payload, xfer->rx.len);
+}
index bed5596..4effecc 100644 (file)
@@ -154,7 +154,8 @@ static int smc_send_message(struct scmi_chan_info *cinfo,
        if (scmi_info->irq)
                wait_for_completion(&scmi_info->tx_complete);
 
-       scmi_rx_callback(scmi_info->cinfo, shmem_read_header(scmi_info->shmem));
+       scmi_rx_callback(scmi_info->cinfo,
+                        shmem_read_header(scmi_info->shmem), NULL);
 
        mutex_unlock(&scmi_info->shmem_lock);
 
diff --git a/drivers/firmware/arm_scmi/virtio.c b/drivers/firmware/arm_scmi/virtio.c
new file mode 100644 (file)
index 0000000..224577f
--- /dev/null
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Virtio Transport driver for Arm System Control and Management Interface
+ * (SCMI).
+ *
+ * Copyright (C) 2020-2021 OpenSynergy.
+ * Copyright (C) 2021 ARM Ltd.
+ */
+
+/**
+ * DOC: Theory of Operation
+ *
+ * The scmi-virtio transport implements a driver for the virtio SCMI device.
+ *
+ * There is one Tx channel (virtio cmdq, A2P channel) and at most one Rx
+ * channel (virtio eventq, P2A channel). Each channel is implemented through a
+ * virtqueue. Access to each virtqueue is protected by spinlocks.
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+
+#include <uapi/linux/virtio_ids.h>
+#include <uapi/linux/virtio_scmi.h>
+
+#include "common.h"
+
+#define VIRTIO_SCMI_MAX_MSG_SIZE 128 /* Value may be increased. */
+#define VIRTIO_SCMI_MAX_PDU_SIZE \
+       (VIRTIO_SCMI_MAX_MSG_SIZE + SCMI_MSG_MAX_PROT_OVERHEAD)
+#define DESCRIPTORS_PER_TX_MSG 2
+
+/**
+ * struct scmi_vio_channel - Transport channel information
+ *
+ * @vqueue: Associated virtqueue
+ * @cinfo: SCMI Tx or Rx channel
+ * @free_list: List of unused scmi_vio_msg, maintained for Tx channels only
+ * @is_rx: Whether channel is an Rx channel
+ * @ready: Whether transport user is ready to hear about channel
+ * @max_msg: Maximum number of pending messages for this channel.
+ * @lock: Protects access to all members except ready.
+ * @ready_lock: Protects access to ready. If required, it must be taken before
+ *              lock.
+ */
+struct scmi_vio_channel {
+       struct virtqueue *vqueue;
+       struct scmi_chan_info *cinfo;
+       struct list_head free_list;
+       bool is_rx;
+       bool ready;
+       unsigned int max_msg;
+       /* lock to protect access to all members except ready. */
+       spinlock_t lock;
+       /* lock to rotects access to ready flag. */
+       spinlock_t ready_lock;
+};
+
+/**
+ * struct scmi_vio_msg - Transport PDU information
+ *
+ * @request: SDU used for commands
+ * @input: SDU used for (delayed) responses and notifications
+ * @list: List which scmi_vio_msg may be part of
+ * @rx_len: Input SDU size in bytes, once input has been received
+ */
+struct scmi_vio_msg {
+       struct scmi_msg_payld *request;
+       struct scmi_msg_payld *input;
+       struct list_head list;
+       unsigned int rx_len;
+};
+
+/* Only one SCMI VirtIO device can possibly exist */
+static struct virtio_device *scmi_vdev;
+
+static bool scmi_vio_have_vq_rx(struct virtio_device *vdev)
+{
+       return virtio_has_feature(vdev, VIRTIO_SCMI_F_P2A_CHANNELS);
+}
+
+static int scmi_vio_feed_vq_rx(struct scmi_vio_channel *vioch,
+                              struct scmi_vio_msg *msg)
+{
+       struct scatterlist sg_in;
+       int rc;
+       unsigned long flags;
+
+       sg_init_one(&sg_in, msg->input, VIRTIO_SCMI_MAX_PDU_SIZE);
+
+       spin_lock_irqsave(&vioch->lock, flags);
+
+       rc = virtqueue_add_inbuf(vioch->vqueue, &sg_in, 1, msg, GFP_ATOMIC);
+       if (rc)
+               dev_err_once(vioch->cinfo->dev,
+                            "failed to add to virtqueue (%d)\n", rc);
+       else
+               virtqueue_kick(vioch->vqueue);
+
+       spin_unlock_irqrestore(&vioch->lock, flags);
+
+       return rc;
+}
+
+static void scmi_finalize_message(struct scmi_vio_channel *vioch,
+                                 struct scmi_vio_msg *msg)
+{
+       if (vioch->is_rx) {
+               scmi_vio_feed_vq_rx(vioch, msg);
+       } else {
+               unsigned long flags;
+
+               spin_lock_irqsave(&vioch->lock, flags);
+               list_add(&msg->list, &vioch->free_list);
+               spin_unlock_irqrestore(&vioch->lock, flags);
+       }
+}
+
+static void scmi_vio_complete_cb(struct virtqueue *vqueue)
+{
+       unsigned long ready_flags;
+       unsigned long flags;
+       unsigned int length;
+       struct scmi_vio_channel *vioch;
+       struct scmi_vio_msg *msg;
+       bool cb_enabled = true;
+
+       if (WARN_ON_ONCE(!vqueue->vdev->priv))
+               return;
+       vioch = &((struct scmi_vio_channel *)vqueue->vdev->priv)[vqueue->index];
+
+       for (;;) {
+               spin_lock_irqsave(&vioch->ready_lock, ready_flags);
+
+               if (!vioch->ready) {
+                       if (!cb_enabled)
+                               (void)virtqueue_enable_cb(vqueue);
+                       goto unlock_ready_out;
+               }
+
+               spin_lock_irqsave(&vioch->lock, flags);
+               if (cb_enabled) {
+                       virtqueue_disable_cb(vqueue);
+                       cb_enabled = false;
+               }
+               msg = virtqueue_get_buf(vqueue, &length);
+               if (!msg) {
+                       if (virtqueue_enable_cb(vqueue))
+                               goto unlock_out;
+                       cb_enabled = true;
+               }
+               spin_unlock_irqrestore(&vioch->lock, flags);
+
+               if (msg) {
+                       msg->rx_len = length;
+                       scmi_rx_callback(vioch->cinfo,
+                                        msg_read_header(msg->input), msg);
+
+                       scmi_finalize_message(vioch, msg);
+               }
+
+               spin_unlock_irqrestore(&vioch->ready_lock, ready_flags);
+       }
+
+unlock_out:
+       spin_unlock_irqrestore(&vioch->lock, flags);
+unlock_ready_out:
+       spin_unlock_irqrestore(&vioch->ready_lock, ready_flags);
+}
+
+static const char *const scmi_vio_vqueue_names[] = { "tx", "rx" };
+
+static vq_callback_t *scmi_vio_complete_callbacks[] = {
+       scmi_vio_complete_cb,
+       scmi_vio_complete_cb
+};
+
+static unsigned int virtio_get_max_msg(struct scmi_chan_info *base_cinfo)
+{
+       struct scmi_vio_channel *vioch = base_cinfo->transport_info;
+
+       return vioch->max_msg;
+}
+
+static int virtio_link_supplier(struct device *dev)
+{
+       if (!scmi_vdev) {
+               dev_notice_once(dev,
+                               "Deferring probe after not finding a bound scmi-virtio device\n");
+               return -EPROBE_DEFER;
+       }
+
+       if (!device_link_add(dev, &scmi_vdev->dev,
+                            DL_FLAG_AUTOREMOVE_CONSUMER)) {
+               dev_err(dev, "Adding link to supplier virtio device failed\n");
+               return -ECANCELED;
+       }
+
+       return 0;
+}
+
+static bool virtio_chan_available(struct device *dev, int idx)
+{
+       struct scmi_vio_channel *channels, *vioch = NULL;
+
+       if (WARN_ON_ONCE(!scmi_vdev))
+               return false;
+
+       channels = (struct scmi_vio_channel *)scmi_vdev->priv;
+
+       switch (idx) {
+       case VIRTIO_SCMI_VQ_TX:
+               vioch = &channels[VIRTIO_SCMI_VQ_TX];
+               break;
+       case VIRTIO_SCMI_VQ_RX:
+               if (scmi_vio_have_vq_rx(scmi_vdev))
+                       vioch = &channels[VIRTIO_SCMI_VQ_RX];
+               break;
+       default:
+               return false;
+       }
+
+       return vioch && !vioch->cinfo;
+}
+
+static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
+                            bool tx)
+{
+       unsigned long flags;
+       struct scmi_vio_channel *vioch;
+       int index = tx ? VIRTIO_SCMI_VQ_TX : VIRTIO_SCMI_VQ_RX;
+       int i;
+
+       if (!scmi_vdev)
+               return -EPROBE_DEFER;
+
+       vioch = &((struct scmi_vio_channel *)scmi_vdev->priv)[index];
+
+       for (i = 0; i < vioch->max_msg; i++) {
+               struct scmi_vio_msg *msg;
+
+               msg = devm_kzalloc(cinfo->dev, sizeof(*msg), GFP_KERNEL);
+               if (!msg)
+                       return -ENOMEM;
+
+               if (tx) {
+                       msg->request = devm_kzalloc(cinfo->dev,
+                                                   VIRTIO_SCMI_MAX_PDU_SIZE,
+                                                   GFP_KERNEL);
+                       if (!msg->request)
+                               return -ENOMEM;
+               }
+
+               msg->input = devm_kzalloc(cinfo->dev, VIRTIO_SCMI_MAX_PDU_SIZE,
+                                         GFP_KERNEL);
+               if (!msg->input)
+                       return -ENOMEM;
+
+               if (tx) {
+                       spin_lock_irqsave(&vioch->lock, flags);
+                       list_add_tail(&msg->list, &vioch->free_list);
+                       spin_unlock_irqrestore(&vioch->lock, flags);
+               } else {
+                       scmi_vio_feed_vq_rx(vioch, msg);
+               }
+       }
+
+       spin_lock_irqsave(&vioch->lock, flags);
+       cinfo->transport_info = vioch;
+       /* Indirectly setting channel not available any more */
+       vioch->cinfo = cinfo;
+       spin_unlock_irqrestore(&vioch->lock, flags);
+
+       spin_lock_irqsave(&vioch->ready_lock, flags);
+       vioch->ready = true;
+       spin_unlock_irqrestore(&vioch->ready_lock, flags);
+
+       return 0;
+}
+
+static int virtio_chan_free(int id, void *p, void *data)
+{
+       unsigned long flags;
+       struct scmi_chan_info *cinfo = p;
+       struct scmi_vio_channel *vioch = cinfo->transport_info;
+
+       spin_lock_irqsave(&vioch->ready_lock, flags);
+       vioch->ready = false;
+       spin_unlock_irqrestore(&vioch->ready_lock, flags);
+
+       scmi_free_channel(cinfo, data, id);
+
+       spin_lock_irqsave(&vioch->lock, flags);
+       vioch->cinfo = NULL;
+       spin_unlock_irqrestore(&vioch->lock, flags);
+
+       return 0;
+}
+
+static int virtio_send_message(struct scmi_chan_info *cinfo,
+                              struct scmi_xfer *xfer)
+{
+       struct scmi_vio_channel *vioch = cinfo->transport_info;
+       struct scatterlist sg_out;
+       struct scatterlist sg_in;
+       struct scatterlist *sgs[DESCRIPTORS_PER_TX_MSG] = { &sg_out, &sg_in };
+       unsigned long flags;
+       int rc;
+       struct scmi_vio_msg *msg;
+
+       spin_lock_irqsave(&vioch->lock, flags);
+
+       if (list_empty(&vioch->free_list)) {
+               spin_unlock_irqrestore(&vioch->lock, flags);
+               return -EBUSY;
+       }
+
+       msg = list_first_entry(&vioch->free_list, typeof(*msg), list);
+       list_del(&msg->list);
+
+       msg_tx_prepare(msg->request, xfer);
+
+       sg_init_one(&sg_out, msg->request, msg_command_size(xfer));
+       sg_init_one(&sg_in, msg->input, msg_response_size(xfer));
+
+       rc = virtqueue_add_sgs(vioch->vqueue, sgs, 1, 1, msg, GFP_ATOMIC);
+       if (rc) {
+               list_add(&msg->list, &vioch->free_list);
+               dev_err_once(vioch->cinfo->dev,
+                            "%s() failed to add to virtqueue (%d)\n", __func__,
+                            rc);
+       } else {
+               virtqueue_kick(vioch->vqueue);
+       }
+
+       spin_unlock_irqrestore(&vioch->lock, flags);
+
+       return rc;
+}
+
+static void virtio_fetch_response(struct scmi_chan_info *cinfo,
+                                 struct scmi_xfer *xfer)
+{
+       struct scmi_vio_msg *msg = xfer->priv;
+
+       if (msg) {
+               msg_fetch_response(msg->input, msg->rx_len, xfer);
+               xfer->priv = NULL;
+       }
+}
+
+static void virtio_fetch_notification(struct scmi_chan_info *cinfo,
+                                     size_t max_len, struct scmi_xfer *xfer)
+{
+       struct scmi_vio_msg *msg = xfer->priv;
+
+       if (msg) {
+               msg_fetch_notification(msg->input, msg->rx_len, max_len, xfer);
+               xfer->priv = NULL;
+       }
+}
+
+static const struct scmi_transport_ops scmi_virtio_ops = {
+       .link_supplier = virtio_link_supplier,
+       .chan_available = virtio_chan_available,
+       .chan_setup = virtio_chan_setup,
+       .chan_free = virtio_chan_free,
+       .get_max_msg = virtio_get_max_msg,
+       .send_message = virtio_send_message,
+       .fetch_response = virtio_fetch_response,
+       .fetch_notification = virtio_fetch_notification,
+};
+
+static int scmi_vio_probe(struct virtio_device *vdev)
+{
+       struct device *dev = &vdev->dev;
+       struct scmi_vio_channel *channels;
+       bool have_vq_rx;
+       int vq_cnt;
+       int i;
+       int ret;
+       struct virtqueue *vqs[VIRTIO_SCMI_VQ_MAX_CNT];
+
+       /* Only one SCMI VirtiO device allowed */
+       if (scmi_vdev)
+               return -EINVAL;
+
+       have_vq_rx = scmi_vio_have_vq_rx(vdev);
+       vq_cnt = have_vq_rx ? VIRTIO_SCMI_VQ_MAX_CNT : 1;
+
+       channels = devm_kcalloc(dev, vq_cnt, sizeof(*channels), GFP_KERNEL);
+       if (!channels)
+               return -ENOMEM;
+
+       if (have_vq_rx)
+               channels[VIRTIO_SCMI_VQ_RX].is_rx = true;
+
+       ret = virtio_find_vqs(vdev, vq_cnt, vqs, scmi_vio_complete_callbacks,
+                             scmi_vio_vqueue_names, NULL);
+       if (ret) {
+               dev_err(dev, "Failed to get %d virtqueue(s)\n", vq_cnt);
+               return ret;
+       }
+
+       for (i = 0; i < vq_cnt; i++) {
+               unsigned int sz;
+
+               spin_lock_init(&channels[i].lock);
+               spin_lock_init(&channels[i].ready_lock);
+               INIT_LIST_HEAD(&channels[i].free_list);
+               channels[i].vqueue = vqs[i];
+
+               sz = virtqueue_get_vring_size(channels[i].vqueue);
+               /* Tx messages need multiple descriptors. */
+               if (!channels[i].is_rx)
+                       sz /= DESCRIPTORS_PER_TX_MSG;
+
+               if (sz > MSG_TOKEN_MAX) {
+                       dev_info_once(dev,
+                                     "%s virtqueue could hold %d messages. Only %ld allowed to be pending.\n",
+                                     channels[i].is_rx ? "rx" : "tx",
+                                     sz, MSG_TOKEN_MAX);
+                       sz = MSG_TOKEN_MAX;
+               }
+               channels[i].max_msg = sz;
+       }
+
+       vdev->priv = channels;
+       scmi_vdev = vdev;
+
+       return 0;
+}
+
+static void scmi_vio_remove(struct virtio_device *vdev)
+{
+       vdev->config->reset(vdev);
+       vdev->config->del_vqs(vdev);
+       scmi_vdev = NULL;
+}
+
+static int scmi_vio_validate(struct virtio_device *vdev)
+{
+       if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+               dev_err(&vdev->dev,
+                       "device does not comply with spec version 1.x\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static unsigned int features[] = {
+       VIRTIO_SCMI_F_P2A_CHANNELS,
+};
+
+static const struct virtio_device_id id_table[] = {
+       { VIRTIO_ID_SCMI, VIRTIO_DEV_ANY_ID },
+       { 0 }
+};
+
+static struct virtio_driver virtio_scmi_driver = {
+       .driver.name = "scmi-virtio",
+       .driver.owner = THIS_MODULE,
+       .feature_table = features,
+       .feature_table_size = ARRAY_SIZE(features),
+       .id_table = id_table,
+       .probe = scmi_vio_probe,
+       .remove = scmi_vio_remove,
+       .validate = scmi_vio_validate,
+};
+
+static int __init virtio_scmi_init(void)
+{
+       return register_virtio_driver(&virtio_scmi_driver);
+}
+
+static void __exit virtio_scmi_exit(void)
+{
+       unregister_virtio_driver(&virtio_scmi_driver);
+}
+
+const struct scmi_desc scmi_virtio_desc = {
+       .transport_init = virtio_scmi_init,
+       .transport_exit = virtio_scmi_exit,
+       .ops = &scmi_virtio_ops,
+       .max_rx_timeout_ms = 60000, /* for non-realtime virtio devices */
+       .max_msg = 0, /* overridden by virtio_get_max_msg() */
+       .max_msg_size = VIRTIO_SCMI_MAX_MSG_SIZE,
+};
index 47ea2bd..ced1964 100644 (file)
@@ -71,7 +71,7 @@ static struct qcom_scm_wb_entry qcom_scm_wb[] = {
        { .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 },
 };
 
-static const char *qcom_scm_convention_names[] = {
+static const char * const qcom_scm_convention_names[] = {
        [SMC_CONVENTION_UNKNOWN] = "unknown",
        [SMC_CONVENTION_ARM_32] = "smc arm 32",
        [SMC_CONVENTION_ARM_64] = "smc arm 64",
@@ -331,7 +331,7 @@ int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
                .owner = ARM_SMCCC_OWNER_SIP,
        };
 
-       if (!cpus || (cpus && cpumask_empty(cpus)))
+       if (!cpus || cpumask_empty(cpus))
                return -EINVAL;
 
        for_each_cpu(cpu, cpus) {
@@ -1299,6 +1299,7 @@ static const struct of_device_id qcom_scm_dt_match[] = {
        { .compatible = "qcom,scm" },
        {}
 };
+MODULE_DEVICE_TABLE(of, qcom_scm_dt_match);
 
 static struct platform_driver qcom_scm_driver = {
        .driver = {
@@ -1315,3 +1316,6 @@ static int __init qcom_scm_init(void)
        return platform_driver_register(&qcom_scm_driver);
 }
 subsys_initcall(qcom_scm_init);
+
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. SCM driver");
+MODULE_LICENSE("GPL v2");
index 440d99c..3e9fa4b 100644 (file)
@@ -296,25 +296,61 @@ static int bpmp_debug_show(struct seq_file *m, void *p)
        struct file *file = m->private;
        struct inode *inode = file_inode(file);
        struct tegra_bpmp *bpmp = inode->i_private;
-       char *databuf = NULL;
        char fnamebuf[256];
        const char *filename;
-       uint32_t nbytes = 0;
-       size_t len;
-       int err;
-
-       len = seq_get_buf(m, &databuf);
-       if (!databuf)
-               return -ENOMEM;
+       struct mrq_debug_request req = {
+               .cmd = cpu_to_le32(CMD_DEBUG_READ),
+       };
+       struct mrq_debug_response resp;
+       struct tegra_bpmp_message msg = {
+               .mrq = MRQ_DEBUG,
+               .tx = {
+                       .data = &req,
+                       .size = sizeof(req),
+               },
+               .rx = {
+                       .data = &resp,
+                       .size = sizeof(resp),
+               },
+       };
+       uint32_t fd = 0, len = 0;
+       int remaining, err;
 
        filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));
        if (!filename)
                return -ENOENT;
 
-       err = mrq_debug_read(bpmp, filename, databuf, len, &nbytes);
-       if (!err)
-               seq_commit(m, nbytes);
+       mutex_lock(&bpmp_debug_lock);
+       err = mrq_debug_open(bpmp, filename, &fd, &len, 0);
+       if (err)
+               goto out;
+
+       req.frd.fd = fd;
+       remaining = len;
+
+       while (remaining > 0) {
+               err = tegra_bpmp_transfer(bpmp, &msg);
+               if (err < 0) {
+                       goto close;
+               } else if (msg.rx.ret < 0) {
+                       err = -EINVAL;
+                       goto close;
+               }
 
+               if (resp.frd.readlen > remaining) {
+                       pr_err("%s: read data length invalid\n", __func__);
+                       err = -EINVAL;
+                       goto close;
+               }
+
+               seq_write(m, resp.frd.data, resp.frd.readlen);
+               remaining -= resp.frd.readlen;
+       }
+
+close:
+       err = mrq_debug_close(bpmp, fd);
+out:
+       mutex_unlock(&bpmp_debug_lock);
        return err;
 }
 
index 07b7c25..f61516c 100644 (file)
@@ -253,6 +253,7 @@ config SPAPR_TCE_IOMMU
 config ARM_SMMU
        tristate "ARM Ltd. System MMU (SMMU) Support"
        depends on ARM64 || ARM || (COMPILE_TEST && !GENERIC_ATOMIC64)
+       depends on QCOM_SCM || !QCOM_SCM #if QCOM_SCM=m this can't be =y
        select IOMMU_API
        select IOMMU_IO_PGTABLE_LPAE
        select ARM_DMA_USE_IOMMU if ARM
@@ -382,6 +383,7 @@ config QCOM_IOMMU
        # Note: iommu drivers cannot (yet?) be built as modules
        bool "Qualcomm IOMMU Support"
        depends on ARCH_QCOM || (COMPILE_TEST && !GENERIC_ATOMIC64)
+       depends on QCOM_SCM=y
        select IOMMU_API
        select IOMMU_IO_PGTABLE_LPAE
        select ARM_DMA_USE_IOMMU
index f80c2ea..be0858b 100644 (file)
@@ -9,6 +9,7 @@
  * Copyright (C) 2009 Texas Instruments
  * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
  */
+#include <linux/cpu_pm.h>
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -232,7 +233,10 @@ struct gpmc_device {
        int irq;
        struct irq_chip irq_chip;
        struct gpio_chip gpio_chip;
+       struct notifier_block nb;
+       struct omap3_gpmc_regs context;
        int nirqs;
+       unsigned int is_suspended:1;
 };
 
 static struct irq_domain *gpmc_irq_domain;
@@ -2384,6 +2388,106 @@ static int gpmc_gpio_init(struct gpmc_device *gpmc)
        return 0;
 }
 
+static void omap3_gpmc_save_context(struct gpmc_device *gpmc)
+{
+       struct omap3_gpmc_regs *gpmc_context;
+       int i;
+
+       if (!gpmc || !gpmc_base)
+               return;
+
+       gpmc_context = &gpmc->context;
+
+       gpmc_context->sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
+       gpmc_context->irqenable = gpmc_read_reg(GPMC_IRQENABLE);
+       gpmc_context->timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
+       gpmc_context->config = gpmc_read_reg(GPMC_CONFIG);
+       gpmc_context->prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
+       gpmc_context->prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
+       gpmc_context->prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
+       for (i = 0; i < gpmc_cs_num; i++) {
+               gpmc_context->cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
+               if (gpmc_context->cs_context[i].is_valid) {
+                       gpmc_context->cs_context[i].config1 =
+                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
+                       gpmc_context->cs_context[i].config2 =
+                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
+                       gpmc_context->cs_context[i].config3 =
+                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
+                       gpmc_context->cs_context[i].config4 =
+                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
+                       gpmc_context->cs_context[i].config5 =
+                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
+                       gpmc_context->cs_context[i].config6 =
+                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
+                       gpmc_context->cs_context[i].config7 =
+                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
+               }
+       }
+}
+
+static void omap3_gpmc_restore_context(struct gpmc_device *gpmc)
+{
+       struct omap3_gpmc_regs *gpmc_context;
+       int i;
+
+       if (!gpmc || !gpmc_base)
+               return;
+
+       gpmc_context = &gpmc->context;
+
+       gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context->sysconfig);
+       gpmc_write_reg(GPMC_IRQENABLE, gpmc_context->irqenable);
+       gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context->timeout_ctrl);
+       gpmc_write_reg(GPMC_CONFIG, gpmc_context->config);
+       gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context->prefetch_config1);
+       gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context->prefetch_config2);
+       gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context->prefetch_control);
+       for (i = 0; i < gpmc_cs_num; i++) {
+               if (gpmc_context->cs_context[i].is_valid) {
+                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
+                                         gpmc_context->cs_context[i].config1);
+                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
+                                         gpmc_context->cs_context[i].config2);
+                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
+                                         gpmc_context->cs_context[i].config3);
+                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
+                                         gpmc_context->cs_context[i].config4);
+                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
+                                         gpmc_context->cs_context[i].config5);
+                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
+                                         gpmc_context->cs_context[i].config6);
+                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
+                                         gpmc_context->cs_context[i].config7);
+               } else {
+                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG7, 0);
+               }
+       }
+}
+
+static int omap_gpmc_context_notifier(struct notifier_block *nb,
+                                     unsigned long cmd, void *v)
+{
+       struct gpmc_device *gpmc;
+
+       gpmc = container_of(nb, struct gpmc_device, nb);
+       if (gpmc->is_suspended || pm_runtime_suspended(gpmc->dev))
+               return NOTIFY_OK;
+
+       switch (cmd) {
+       case CPU_CLUSTER_PM_ENTER:
+               omap3_gpmc_save_context(gpmc);
+               break;
+       case CPU_CLUSTER_PM_ENTER_FAILED:       /* No need to restore context */
+               break;
+       case CPU_CLUSTER_PM_EXIT:
+               omap3_gpmc_restore_context(gpmc);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
 static int gpmc_probe(struct platform_device *pdev)
 {
        int rc;
@@ -2472,6 +2576,9 @@ static int gpmc_probe(struct platform_device *pdev)
 
        gpmc_probe_dt_children(pdev);
 
+       gpmc->nb.notifier_call = omap_gpmc_context_notifier;
+       cpu_pm_register_notifier(&gpmc->nb);
+
        return 0;
 
 gpio_init_failed:
@@ -2486,6 +2593,7 @@ static int gpmc_remove(struct platform_device *pdev)
 {
        struct gpmc_device *gpmc = platform_get_drvdata(pdev);
 
+       cpu_pm_unregister_notifier(&gpmc->nb);
        gpmc_free_irq(gpmc);
        gpmc_mem_exit();
        pm_runtime_put_sync(&pdev->dev);
@@ -2497,15 +2605,23 @@ static int gpmc_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM_SLEEP
 static int gpmc_suspend(struct device *dev)
 {
-       omap3_gpmc_save_context();
+       struct gpmc_device *gpmc = dev_get_drvdata(dev);
+
+       omap3_gpmc_save_context(gpmc);
        pm_runtime_put_sync(dev);
+       gpmc->is_suspended = 1;
+
        return 0;
 }
 
 static int gpmc_resume(struct device *dev)
 {
+       struct gpmc_device *gpmc = dev_get_drvdata(dev);
+
        pm_runtime_get_sync(dev);
-       omap3_gpmc_restore_context();
+       omap3_gpmc_restore_context(gpmc);
+       gpmc->is_suspended = 0;
+
        return 0;
 }
 #endif
@@ -2527,74 +2643,3 @@ static __init int gpmc_init(void)
        return platform_driver_register(&gpmc_driver);
 }
 postcore_initcall(gpmc_init);
-
-static struct omap3_gpmc_regs gpmc_context;
-
-void omap3_gpmc_save_context(void)
-{
-       int i;
-
-       if (!gpmc_base)
-               return;
-
-       gpmc_context.sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
-       gpmc_context.irqenable = gpmc_read_reg(GPMC_IRQENABLE);
-       gpmc_context.timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
-       gpmc_context.config = gpmc_read_reg(GPMC_CONFIG);
-       gpmc_context.prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
-       gpmc_context.prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
-       gpmc_context.prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
-       for (i = 0; i < gpmc_cs_num; i++) {
-               gpmc_context.cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
-               if (gpmc_context.cs_context[i].is_valid) {
-                       gpmc_context.cs_context[i].config1 =
-                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
-                       gpmc_context.cs_context[i].config2 =
-                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
-                       gpmc_context.cs_context[i].config3 =
-                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
-                       gpmc_context.cs_context[i].config4 =
-                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
-                       gpmc_context.cs_context[i].config5 =
-                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
-                       gpmc_context.cs_context[i].config6 =
-                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
-                       gpmc_context.cs_context[i].config7 =
-                               gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
-               }
-       }
-}
-
-void omap3_gpmc_restore_context(void)
-{
-       int i;
-
-       if (!gpmc_base)
-               return;
-
-       gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context.sysconfig);
-       gpmc_write_reg(GPMC_IRQENABLE, gpmc_context.irqenable);
-       gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context.timeout_ctrl);
-       gpmc_write_reg(GPMC_CONFIG, gpmc_context.config);
-       gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context.prefetch_config1);
-       gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context.prefetch_config2);
-       gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context.prefetch_control);
-       for (i = 0; i < gpmc_cs_num; i++) {
-               if (gpmc_context.cs_context[i].is_valid) {
-                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
-                               gpmc_context.cs_context[i].config1);
-                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
-                               gpmc_context.cs_context[i].config2);
-                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
-                               gpmc_context.cs_context[i].config3);
-                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
-                               gpmc_context.cs_context[i].config4);
-                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
-                               gpmc_context.cs_context[i].config5);
-                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
-                               gpmc_context.cs_context[i].config6);
-                       gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
-                               gpmc_context.cs_context[i].config7);
-               }
-       }
-}
index e65eac5..3d15388 100644 (file)
@@ -71,6 +71,7 @@ static int tegra186_mc_resume(struct tegra_mc *mc)
        return 0;
 }
 
+#if IS_ENABLED(CONFIG_IOMMU_API)
 static void tegra186_mc_client_sid_override(struct tegra_mc *mc,
                                            const struct tegra_mc_client *client,
                                            unsigned int sid)
@@ -108,6 +109,7 @@ static void tegra186_mc_client_sid_override(struct tegra_mc *mc,
                writel(sid, mc->regs + client->regs.sid.override);
        }
 }
+#endif
 
 static int tegra186_mc_probe_device(struct tegra_mc *mc, struct device *dev)
 {
index 40f91bc..741289e 100644 (file)
@@ -44,6 +44,7 @@ config ATH10K_SNOC
        tristate "Qualcomm ath10k SNOC support"
        depends on ATH10K
        depends on ARCH_QCOM || COMPILE_TEST
+       depends on QCOM_SCM || !QCOM_SCM #if QCOM_SCM=m this can't be =y
        select QCOM_QMI_HELPERS
        help
          This module adds support for integrated WCN3990 chip connected
index 5656cac..be799a5 100644 (file)
@@ -181,6 +181,13 @@ config RESET_RASPBERRYPI
          interfacing with RPi4's co-processor and model these firmware
          initialization routines as reset lines.
 
+config RESET_RZG2L_USBPHY_CTRL
+       tristate "Renesas RZ/G2L USBPHY control driver"
+       depends on ARCH_R9A07G044 || COMPILE_TEST
+       help
+         Support for USBPHY Control found on RZ/G2L family. It mainly
+         controls reset and power down of the USB/PHY.
+
 config RESET_SCMI
        tristate "Reset driver controlled via ARM SCMI interface"
        depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
@@ -207,7 +214,6 @@ config RESET_SIMPLE
           - Realtek SoCs
           - RCC reset controller in STM32 MCUs
           - Allwinner SoCs
-          - ZTE's zx2967 family
           - SiFive FU740 SoCs
 
 config RESET_SOCFPGA
index ea8b8d9..21d46d8 100644 (file)
@@ -25,6 +25,7 @@ obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
 obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
 obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
 obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
+obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
 obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
 obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
 obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
index ab74bcc..f22bb49 100644 (file)
 
 #include <dt-bindings/reset/qcom,sdm845-pdc.h>
 
-#define RPMH_PDC_SYNC_RESET    0x100
+#define RPMH_SDM845_PDC_SYNC_RESET     0x100
+#define RPMH_SC7280_PDC_SYNC_RESET     0x1000
 
 struct qcom_pdc_reset_map {
        u8 bit;
 };
 
+struct qcom_pdc_reset_desc {
+       const struct qcom_pdc_reset_map *resets;
+       size_t num_resets;
+       unsigned int offset;
+};
+
 struct qcom_pdc_reset_data {
        struct reset_controller_dev rcdev;
        struct regmap *regmap;
+       const struct qcom_pdc_reset_desc *desc;
 };
 
-static const struct regmap_config sdm845_pdc_regmap_config = {
+static const struct regmap_config pdc_regmap_config = {
        .name           = "pdc-reset",
        .reg_bits       = 32,
        .reg_stride     = 4,
@@ -44,6 +52,33 @@ static const struct qcom_pdc_reset_map sdm845_pdc_resets[] = {
        [PDC_MODEM_SYNC_RESET] = {9},
 };
 
+static const struct qcom_pdc_reset_desc sdm845_pdc_reset_desc = {
+       .resets = sdm845_pdc_resets,
+       .num_resets = ARRAY_SIZE(sdm845_pdc_resets),
+       .offset = RPMH_SDM845_PDC_SYNC_RESET,
+};
+
+static const struct qcom_pdc_reset_map sc7280_pdc_resets[] = {
+       [PDC_APPS_SYNC_RESET] = {0},
+       [PDC_SP_SYNC_RESET] = {1},
+       [PDC_AUDIO_SYNC_RESET] = {2},
+       [PDC_SENSORS_SYNC_RESET] = {3},
+       [PDC_AOP_SYNC_RESET] = {4},
+       [PDC_DEBUG_SYNC_RESET] = {5},
+       [PDC_GPU_SYNC_RESET] = {6},
+       [PDC_DISPLAY_SYNC_RESET] = {7},
+       [PDC_COMPUTE_SYNC_RESET] = {8},
+       [PDC_MODEM_SYNC_RESET] = {9},
+       [PDC_WLAN_RF_SYNC_RESET] = {10},
+       [PDC_WPSS_SYNC_RESET] = {11},
+};
+
+static const struct qcom_pdc_reset_desc sc7280_pdc_reset_desc = {
+       .resets = sc7280_pdc_resets,
+       .num_resets = ARRAY_SIZE(sc7280_pdc_resets),
+       .offset = RPMH_SC7280_PDC_SYNC_RESET,
+};
+
 static inline struct qcom_pdc_reset_data *to_qcom_pdc_reset_data(
                                struct reset_controller_dev *rcdev)
 {
@@ -54,19 +89,18 @@ static int qcom_pdc_control_assert(struct reset_controller_dev *rcdev,
                                        unsigned long idx)
 {
        struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev);
+       u32 mask = BIT(data->desc->resets[idx].bit);
 
-       return regmap_update_bits(data->regmap, RPMH_PDC_SYNC_RESET,
-                                 BIT(sdm845_pdc_resets[idx].bit),
-                                 BIT(sdm845_pdc_resets[idx].bit));
+       return regmap_update_bits(data->regmap, data->desc->offset, mask, mask);
 }
 
 static int qcom_pdc_control_deassert(struct reset_controller_dev *rcdev,
                                        unsigned long idx)
 {
        struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev);
+       u32 mask = BIT(data->desc->resets[idx].bit);
 
-       return regmap_update_bits(data->regmap, RPMH_PDC_SYNC_RESET,
-                                 BIT(sdm845_pdc_resets[idx].bit), 0);
+       return regmap_update_bits(data->regmap, data->desc->offset, mask, 0);
 }
 
 static const struct reset_control_ops qcom_pdc_reset_ops = {
@@ -76,22 +110,27 @@ static const struct reset_control_ops qcom_pdc_reset_ops = {
 
 static int qcom_pdc_reset_probe(struct platform_device *pdev)
 {
+       const struct qcom_pdc_reset_desc *desc;
        struct qcom_pdc_reset_data *data;
        struct device *dev = &pdev->dev;
        void __iomem *base;
        struct resource *res;
 
+       desc = device_get_match_data(&pdev->dev);
+       if (!desc)
+               return -EINVAL;
+
        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
+       data->desc = desc;
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        base = devm_ioremap_resource(dev, res);
        if (IS_ERR(base))
                return PTR_ERR(base);
 
-       data->regmap = devm_regmap_init_mmio(dev, base,
-                                            &sdm845_pdc_regmap_config);
+       data->regmap = devm_regmap_init_mmio(dev, base, &pdc_regmap_config);
        if (IS_ERR(data->regmap)) {
                dev_err(dev, "Unable to initialize regmap\n");
                return PTR_ERR(data->regmap);
@@ -99,14 +138,15 @@ static int qcom_pdc_reset_probe(struct platform_device *pdev)
 
        data->rcdev.owner = THIS_MODULE;
        data->rcdev.ops = &qcom_pdc_reset_ops;
-       data->rcdev.nr_resets = ARRAY_SIZE(sdm845_pdc_resets);
+       data->rcdev.nr_resets = desc->num_resets;
        data->rcdev.of_node = dev->of_node;
 
        return devm_reset_controller_register(dev, &data->rcdev);
 }
 
 static const struct of_device_id qcom_pdc_reset_of_match[] = {
-       { .compatible = "qcom,sdm845-pdc-global" },
+       { .compatible = "qcom,sc7280-pdc-global", .data = &sc7280_pdc_reset_desc },
+       { .compatible = "qcom,sdm845-pdc-global", .data = &sdm845_pdc_reset_desc },
        {}
 };
 MODULE_DEVICE_TABLE(of, qcom_pdc_reset_of_match);
diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
new file mode 100644 (file)
index 0000000..e0704fd
--- /dev/null
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/G2L USBPHY control driver
+ *
+ * Copyright (C) 2021 Renesas Electronics Corporation
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/reset-controller.h>
+
+#define RESET                  0x000
+
+#define RESET_SEL_PLLRESET     BIT(12)
+#define RESET_PLLRESET         BIT(8)
+
+#define RESET_SEL_P2RESET      BIT(5)
+#define RESET_SEL_P1RESET      BIT(4)
+#define RESET_PHYRST_2         BIT(1)
+#define RESET_PHYRST_1         BIT(0)
+
+#define PHY_RESET_PORT2                (RESET_SEL_P2RESET | RESET_PHYRST_2)
+#define PHY_RESET_PORT1                (RESET_SEL_P1RESET | RESET_PHYRST_1)
+
+#define NUM_PORTS              2
+
+struct rzg2l_usbphy_ctrl_priv {
+       struct reset_controller_dev rcdev;
+       struct reset_control *rstc;
+       void __iomem *base;
+
+       spinlock_t lock;
+};
+
+#define rcdev_to_priv(x)       container_of(x, struct rzg2l_usbphy_ctrl_priv, rcdev)
+
+static int rzg2l_usbphy_ctrl_assert(struct reset_controller_dev *rcdev,
+                                   unsigned long id)
+{
+       struct rzg2l_usbphy_ctrl_priv *priv = rcdev_to_priv(rcdev);
+       u32 port_mask = PHY_RESET_PORT1 | PHY_RESET_PORT2;
+       void __iomem *base = priv->base;
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       val = readl(base + RESET);
+       val |= id ? PHY_RESET_PORT2 : PHY_RESET_PORT1;
+       if (port_mask == (val & port_mask))
+               val |= RESET_PLLRESET;
+       writel(val, base + RESET);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+
+static int rzg2l_usbphy_ctrl_deassert(struct reset_controller_dev *rcdev,
+                                     unsigned long id)
+{
+       struct rzg2l_usbphy_ctrl_priv *priv = rcdev_to_priv(rcdev);
+       void __iomem *base = priv->base;
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       val = readl(base + RESET);
+
+       val |= RESET_SEL_PLLRESET;
+       val &= ~(RESET_PLLRESET | (id ? PHY_RESET_PORT2 : PHY_RESET_PORT1));
+       writel(val, base + RESET);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+
+static int rzg2l_usbphy_ctrl_status(struct reset_controller_dev *rcdev,
+                                   unsigned long id)
+{
+       struct rzg2l_usbphy_ctrl_priv *priv = rcdev_to_priv(rcdev);
+       u32 port_mask;
+
+       port_mask = id ? PHY_RESET_PORT2 : PHY_RESET_PORT1;
+
+       return !!(readl(priv->base + RESET) & port_mask);
+}
+
+static const struct of_device_id rzg2l_usbphy_ctrl_match_table[] = {
+       { .compatible = "renesas,rzg2l-usbphy-ctrl" },
+       { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzg2l_usbphy_ctrl_match_table);
+
+static const struct reset_control_ops rzg2l_usbphy_ctrl_reset_ops = {
+       .assert = rzg2l_usbphy_ctrl_assert,
+       .deassert = rzg2l_usbphy_ctrl_deassert,
+       .status = rzg2l_usbphy_ctrl_status,
+};
+
+static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rzg2l_usbphy_ctrl_priv *priv;
+       unsigned long flags;
+       int error;
+       u32 val;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+       if (IS_ERR(priv->rstc))
+               return dev_err_probe(dev, PTR_ERR(priv->rstc),
+                                    "failed to get reset\n");
+
+       reset_control_deassert(priv->rstc);
+
+       priv->rcdev.ops = &rzg2l_usbphy_ctrl_reset_ops;
+       priv->rcdev.of_reset_n_cells = 1;
+       priv->rcdev.nr_resets = NUM_PORTS;
+       priv->rcdev.of_node = dev->of_node;
+       priv->rcdev.dev = dev;
+
+       error = devm_reset_controller_register(dev, &priv->rcdev);
+       if (error)
+               return error;
+
+       spin_lock_init(&priv->lock);
+       dev_set_drvdata(dev, priv);
+
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_resume_and_get(&pdev->dev);
+
+       /* put pll and phy into reset state */
+       spin_lock_irqsave(&priv->lock, flags);
+       val = readl(priv->base + RESET);
+       val |= RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1;
+       writel(val, priv->base + RESET);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+
+static int rzg2l_usbphy_ctrl_remove(struct platform_device *pdev)
+{
+       struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(&pdev->dev);
+
+       pm_runtime_put(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+       reset_control_assert(priv->rstc);
+
+       return 0;
+}
+
+static struct platform_driver rzg2l_usbphy_ctrl_driver = {
+       .driver = {
+               .name           = "rzg2l_usbphy_ctrl",
+               .of_match_table = rzg2l_usbphy_ctrl_match_table,
+       },
+       .probe  = rzg2l_usbphy_ctrl_probe,
+       .remove = rzg2l_usbphy_ctrl_remove,
+};
+module_platform_driver(rzg2l_usbphy_ctrl_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas RZ/G2L USBPHY Control");
+MODULE_AUTHOR("biju.das.jz@bp.renesas.com>");
index 654c717..714fa92 100644 (file)
@@ -71,6 +71,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8173[] = {
                .ctl_offs = SPM_MFG_ASYNC_PWR_CON,
                .sram_pdn_bits = GENMASK(11, 8),
                .sram_pdn_ack_bits = 0,
+               .caps = MTK_SCPD_DOMAIN_SUPPLY,
        },
        [MT8173_POWER_DOMAIN_MFG_2D] = {
                .name = "mfg_2d",
index 579dfc8..9dee485 100644 (file)
 static const struct mtk_mmsys_routes mmsys_mt8183_routing_table[] = {
        {
                DDP_COMPONENT_OVL0, DDP_COMPONENT_OVL_2L0,
-               MT8183_DISP_OVL0_MOUT_EN, MT8183_OVL0_MOUT_EN_OVL0_2L
+               MT8183_DISP_OVL0_MOUT_EN, MT8183_OVL0_MOUT_EN_OVL0_2L,
+               MT8183_OVL0_MOUT_EN_OVL0_2L
        }, {
                DDP_COMPONENT_OVL_2L0, DDP_COMPONENT_RDMA0,
-               MT8183_DISP_OVL0_2L_MOUT_EN, MT8183_OVL0_2L_MOUT_EN_DISP_PATH0
+               MT8183_DISP_OVL0_2L_MOUT_EN, MT8183_OVL0_2L_MOUT_EN_DISP_PATH0,
+               MT8183_OVL0_2L_MOUT_EN_DISP_PATH0
        }, {
                DDP_COMPONENT_OVL_2L1, DDP_COMPONENT_RDMA1,
-               MT8183_DISP_OVL1_2L_MOUT_EN, MT8183_OVL1_2L_MOUT_EN_RDMA1
+               MT8183_DISP_OVL1_2L_MOUT_EN, MT8183_OVL1_2L_MOUT_EN_RDMA1,
+               MT8183_OVL1_2L_MOUT_EN_RDMA1
        }, {
                DDP_COMPONENT_DITHER, DDP_COMPONENT_DSI0,
-               MT8183_DISP_DITHER0_MOUT_EN, MT8183_DITHER0_MOUT_IN_DSI0
+               MT8183_DISP_DITHER0_MOUT_EN, MT8183_DITHER0_MOUT_IN_DSI0,
+               MT8183_DITHER0_MOUT_IN_DSI0
        }, {
                DDP_COMPONENT_OVL_2L0, DDP_COMPONENT_RDMA0,
-               MT8183_DISP_PATH0_SEL_IN, MT8183_DISP_PATH0_SEL_IN_OVL0_2L
+               MT8183_DISP_PATH0_SEL_IN, MT8183_DISP_PATH0_SEL_IN_OVL0_2L,
+               MT8183_DISP_PATH0_SEL_IN_OVL0_2L
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
-               MT8183_DISP_DPI0_SEL_IN, MT8183_DPI0_SEL_IN_RDMA1
+               MT8183_DISP_DPI0_SEL_IN, MT8183_DPI0_SEL_IN_RDMA1,
+               MT8183_DPI0_SEL_IN_RDMA1
        }, {
                DDP_COMPONENT_RDMA0, DDP_COMPONENT_COLOR0,
-               MT8183_DISP_RDMA0_SOUT_SEL_IN, MT8183_RDMA0_SOUT_COLOR0
+               MT8183_DISP_RDMA0_SOUT_SEL_IN, MT8183_RDMA0_SOUT_COLOR0,
+               MT8183_RDMA0_SOUT_COLOR0
        }
 };
 
diff --git a/drivers/soc/mediatek/mt8365-mmsys.h b/drivers/soc/mediatek/mt8365-mmsys.h
new file mode 100644 (file)
index 0000000..690e3fe
--- /dev/null
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __SOC_MEDIATEK_MT8365_MMSYS_H
+#define __SOC_MEDIATEK_MT8365_MMSYS_H
+
+#define MT8365_DISP_REG_CONFIG_DISP_OVL0_MOUT_EN       0xf3c
+#define MT8365_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL     0xf4c
+#define MT8365_DISP_REG_CONFIG_DISP_DITHER0_MOUT_EN    0xf50
+#define MT8365_DISP_REG_CONFIG_DISP_RDMA0_SEL_IN       0xf54
+#define MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN  0xf60
+#define MT8365_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN      0xf64
+#define MT8365_DISP_REG_CONFIG_DISP_DSI0_SEL_IN                0xf68
+
+#define MT8365_RDMA0_SOUT_COLOR0                       0x1
+#define MT8365_DITHER_MOUT_EN_DSI0                     0x1
+#define MT8365_DSI0_SEL_IN_DITHER                      0x1
+#define MT8365_RDMA0_SEL_IN_OVL0                       0x0
+#define MT8365_RDMA0_RSZ0_SEL_IN_RDMA0                 0x0
+#define MT8365_DISP_COLOR_SEL_IN_COLOR0                        0x0
+#define MT8365_OVL0_MOUT_PATH0_SEL                     BIT(0)
+
+static const struct mtk_mmsys_routes mt8365_mmsys_routing_table[] = {
+       {
+               DDP_COMPONENT_OVL0, DDP_COMPONENT_RDMA0,
+               MT8365_DISP_REG_CONFIG_DISP_OVL0_MOUT_EN,
+               MT8365_OVL0_MOUT_PATH0_SEL, MT8365_OVL0_MOUT_PATH0_SEL
+       },
+       {
+               DDP_COMPONENT_OVL0, DDP_COMPONENT_RDMA0,
+               MT8365_DISP_REG_CONFIG_DISP_RDMA0_SEL_IN,
+               MT8365_RDMA0_SEL_IN_OVL0, MT8365_RDMA0_SEL_IN_OVL0
+       },
+       {
+               DDP_COMPONENT_RDMA0, DDP_COMPONENT_COLOR0,
+               MT8365_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL,
+               MT8365_RDMA0_SOUT_COLOR0, MT8365_RDMA0_SOUT_COLOR0
+       },
+       {
+               DDP_COMPONENT_COLOR0, DDP_COMPONENT_CCORR,
+               MT8365_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN,
+               MT8365_DISP_COLOR_SEL_IN_COLOR0,MT8365_DISP_COLOR_SEL_IN_COLOR0
+       },
+       {
+               DDP_COMPONENT_DITHER, DDP_COMPONENT_DSI0,
+               MT8365_DISP_REG_CONFIG_DISP_DITHER0_MOUT_EN,
+               MT8365_DITHER_MOUT_EN_DSI0, MT8365_DITHER_MOUT_EN_DSI0
+       },
+       {
+               DDP_COMPONENT_DITHER, DDP_COMPONENT_DSI0,
+               MT8365_DISP_REG_CONFIG_DISP_DSI0_SEL_IN,
+               MT8365_DSI0_SEL_IN_DITHER, MT8365_DSI0_SEL_IN_DITHER
+       },
+       {
+               DDP_COMPONENT_RDMA0, DDP_COMPONENT_COLOR0,
+               MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN,
+               MT8365_RDMA0_RSZ0_SEL_IN_RDMA0, MT8365_RDMA0_RSZ0_SEL_IN_RDMA0
+       },
+};
+
+#endif /* __SOC_MEDIATEK_MT8365_MMSYS_H */
index 080660e..a78e88f 100644 (file)
@@ -13,6 +13,7 @@
 #include "mtk-mmsys.h"
 #include "mt8167-mmsys.h"
 #include "mt8183-mmsys.h"
+#include "mt8365-mmsys.h"
 
 static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
        .clk_driver = "clk-mt2701-mm",
@@ -52,6 +53,12 @@ static const struct mtk_mmsys_driver_data mt8183_mmsys_driver_data = {
        .num_routes = ARRAY_SIZE(mmsys_mt8183_routing_table),
 };
 
+static const struct mtk_mmsys_driver_data mt8365_mmsys_driver_data = {
+       .clk_driver = "clk-mt8365-mm",
+       .routes = mt8365_mmsys_routing_table,
+       .num_routes = ARRAY_SIZE(mt8365_mmsys_routing_table),
+};
+
 struct mtk_mmsys {
        void __iomem *regs;
        const struct mtk_mmsys_driver_data *data;
@@ -68,7 +75,9 @@ void mtk_mmsys_ddp_connect(struct device *dev,
 
        for (i = 0; i < mmsys->data->num_routes; i++)
                if (cur == routes[i].from_comp && next == routes[i].to_comp) {
-                       reg = readl_relaxed(mmsys->regs + routes[i].addr) | routes[i].val;
+                       reg = readl_relaxed(mmsys->regs + routes[i].addr);
+                       reg &= ~routes[i].mask;
+                       reg |= routes[i].val;
                        writel_relaxed(reg, mmsys->regs + routes[i].addr);
                }
 }
@@ -85,7 +94,8 @@ void mtk_mmsys_ddp_disconnect(struct device *dev,
 
        for (i = 0; i < mmsys->data->num_routes; i++)
                if (cur == routes[i].from_comp && next == routes[i].to_comp) {
-                       reg = readl_relaxed(mmsys->regs + routes[i].addr) & ~routes[i].val;
+                       reg = readl_relaxed(mmsys->regs + routes[i].addr);
+                       reg &= ~routes[i].mask;
                        writel_relaxed(reg, mmsys->regs + routes[i].addr);
                }
 }
@@ -157,6 +167,10 @@ static const struct of_device_id of_match_mtk_mmsys[] = {
                .compatible = "mediatek,mt8183-mmsys",
                .data = &mt8183_mmsys_driver_data,
        },
+       {
+               .compatible = "mediatek,mt8365-mmsys",
+               .data = &mt8365_mmsys_driver_data,
+       },
        { }
 };
 
index a760a34..9e2b81b 100644 (file)
 #define RDMA0_SOUT_DSI1                                0x1
 #define RDMA0_SOUT_DSI2                                0x4
 #define RDMA0_SOUT_DSI3                                0x5
+#define RDMA0_SOUT_MASK                                0x7
 #define RDMA1_SOUT_DPI0                                0x2
 #define RDMA1_SOUT_DPI1                                0x3
 #define RDMA1_SOUT_DSI1                                0x1
 #define RDMA1_SOUT_DSI2                                0x4
 #define RDMA1_SOUT_DSI3                                0x5
+#define RDMA1_SOUT_MASK                                0x7
 #define RDMA2_SOUT_DPI0                                0x2
 #define RDMA2_SOUT_DPI1                                0x3
 #define RDMA2_SOUT_DSI1                                0x1
 #define RDMA2_SOUT_DSI2                                0x4
 #define RDMA2_SOUT_DSI3                                0x5
+#define RDMA2_SOUT_MASK                                0x7
 #define DPI0_SEL_IN_RDMA1                      0x1
 #define DPI0_SEL_IN_RDMA2                      0x3
+#define DPI0_SEL_IN_MASK                       0x3
 #define DPI1_SEL_IN_RDMA1                      (0x1 << 8)
 #define DPI1_SEL_IN_RDMA2                      (0x3 << 8)
+#define DPI1_SEL_IN_MASK                       (0x3 << 8)
 #define DSI0_SEL_IN_RDMA1                      0x1
 #define DSI0_SEL_IN_RDMA2                      0x4
+#define DSI0_SEL_IN_MASK                       0x7
 #define DSI1_SEL_IN_RDMA1                      0x1
 #define DSI1_SEL_IN_RDMA2                      0x4
+#define DSI1_SEL_IN_MASK                       0x7
 #define DSI2_SEL_IN_RDMA1                      (0x1 << 16)
 #define DSI2_SEL_IN_RDMA2                      (0x4 << 16)
+#define DSI2_SEL_IN_MASK                       (0x7 << 16)
 #define DSI3_SEL_IN_RDMA1                      (0x1 << 16)
 #define DSI3_SEL_IN_RDMA2                      (0x4 << 16)
+#define DSI3_SEL_IN_MASK                       (0x7 << 16)
 #define COLOR1_SEL_IN_OVL1                     0x1
 
 #define OVL_MOUT_EN_RDMA                       0x1
 #define BLS_TO_DSI_RDMA1_TO_DPI1               0x8
 #define BLS_TO_DPI_RDMA1_TO_DSI                        0x2
+#define BLS_RDMA1_DSI_DPI_MASK                 0xf
 #define DSI_SEL_IN_BLS                         0x0
 #define DPI_SEL_IN_BLS                         0x0
+#define DPI_SEL_IN_MASK                                0x1
 #define DSI_SEL_IN_RDMA                                0x1
+#define DSI_SEL_IN_MASK                                0x1
 
 struct mtk_mmsys_routes {
        u32 from_comp;
        u32 to_comp;
        u32 addr;
+       u32 mask;
        u32 val;
 };
 
@@ -91,124 +104,168 @@ struct mtk_mmsys_driver_data {
 static const struct mtk_mmsys_routes mmsys_default_routing_table[] = {
        {
                DDP_COMPONENT_BLS, DDP_COMPONENT_DSI0,
-               DISP_REG_CONFIG_OUT_SEL, BLS_TO_DSI_RDMA1_TO_DPI1
+               DISP_REG_CONFIG_OUT_SEL, BLS_RDMA1_DSI_DPI_MASK,
+               BLS_TO_DSI_RDMA1_TO_DPI1
        }, {
                DDP_COMPONENT_BLS, DDP_COMPONENT_DSI0,
-               DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_BLS
+               DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_MASK,
+               DSI_SEL_IN_BLS
        }, {
                DDP_COMPONENT_BLS, DDP_COMPONENT_DPI0,
-               DISP_REG_CONFIG_OUT_SEL, BLS_TO_DPI_RDMA1_TO_DSI
+               DISP_REG_CONFIG_OUT_SEL, BLS_RDMA1_DSI_DPI_MASK,
+               BLS_TO_DPI_RDMA1_TO_DSI
        }, {
                DDP_COMPONENT_BLS, DDP_COMPONENT_DPI0,
-               DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_RDMA
+               DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_MASK,
+               DSI_SEL_IN_RDMA
        }, {
                DDP_COMPONENT_BLS, DDP_COMPONENT_DPI0,
-               DISP_REG_CONFIG_DPI_SEL, DPI_SEL_IN_BLS
+               DISP_REG_CONFIG_DPI_SEL, DPI_SEL_IN_MASK,
+               DPI_SEL_IN_BLS
        }, {
                DDP_COMPONENT_GAMMA, DDP_COMPONENT_RDMA1,
-               DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN, GAMMA_MOUT_EN_RDMA1
+               DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN, GAMMA_MOUT_EN_RDMA1,
+               GAMMA_MOUT_EN_RDMA1
        }, {
                DDP_COMPONENT_OD0, DDP_COMPONENT_RDMA0,
-               DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD_MOUT_EN_RDMA0
+               DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD_MOUT_EN_RDMA0,
+               OD_MOUT_EN_RDMA0
        }, {
                DDP_COMPONENT_OD1, DDP_COMPONENT_RDMA1,
-               DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD1_MOUT_EN_RDMA1
+               DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD1_MOUT_EN_RDMA1,
+               OD1_MOUT_EN_RDMA1
        }, {
                DDP_COMPONENT_OVL0, DDP_COMPONENT_COLOR0,
-               DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, OVL0_MOUT_EN_COLOR0
+               DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, OVL0_MOUT_EN_COLOR0,
+               OVL0_MOUT_EN_COLOR0
        }, {
                DDP_COMPONENT_OVL0, DDP_COMPONENT_COLOR0,
-               DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, COLOR0_SEL_IN_OVL0
+               DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, COLOR0_SEL_IN_OVL0,
+               COLOR0_SEL_IN_OVL0
        }, {
                DDP_COMPONENT_OVL0, DDP_COMPONENT_RDMA0,
-               DISP_REG_CONFIG_DISP_OVL_MOUT_EN, OVL_MOUT_EN_RDMA
+               DISP_REG_CONFIG_DISP_OVL_MOUT_EN, OVL_MOUT_EN_RDMA,
+               OVL_MOUT_EN_RDMA
        }, {
                DDP_COMPONENT_OVL1, DDP_COMPONENT_COLOR1,
-               DISP_REG_CONFIG_DISP_OVL1_MOUT_EN, OVL1_MOUT_EN_COLOR1
+               DISP_REG_CONFIG_DISP_OVL1_MOUT_EN, OVL1_MOUT_EN_COLOR1,
+               OVL1_MOUT_EN_COLOR1
        }, {
                DDP_COMPONENT_OVL1, DDP_COMPONENT_COLOR1,
-               DISP_REG_CONFIG_DISP_COLOR1_SEL_IN, COLOR1_SEL_IN_OVL1
+               DISP_REG_CONFIG_DISP_COLOR1_SEL_IN, COLOR1_SEL_IN_OVL1,
+               COLOR1_SEL_IN_OVL1
        }, {
                DDP_COMPONENT_RDMA0, DDP_COMPONENT_DPI0,
-               DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_DPI0
+               DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK,
+               RDMA0_SOUT_DPI0
        }, {
                DDP_COMPONENT_RDMA0, DDP_COMPONENT_DPI1,
-               DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_DPI1
+               DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK,
+               RDMA0_SOUT_DPI1
        }, {
                DDP_COMPONENT_RDMA0, DDP_COMPONENT_DSI1,
-               DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_DSI1
+               DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK,
+               RDMA0_SOUT_DSI1
        }, {
                DDP_COMPONENT_RDMA0, DDP_COMPONENT_DSI2,
-               DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_DSI2
+               DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK,
+               RDMA0_SOUT_DSI2
        }, {
                DDP_COMPONENT_RDMA0, DDP_COMPONENT_DSI3,
-               DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_DSI3
+               DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK,
+               RDMA0_SOUT_DSI3
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
-               DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_DPI0
+               DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK,
+               RDMA1_SOUT_DPI0
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
-               DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_RDMA1
+               DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_MASK,
+               DPI0_SEL_IN_RDMA1
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI1,
-               DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_DPI1
+               DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK,
+               RDMA1_SOUT_DPI1
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI1,
-               DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_RDMA1
+               DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_MASK,
+               DPI1_SEL_IN_RDMA1
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI0,
-               DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_RDMA1
+               DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_MASK,
+               DSI0_SEL_IN_RDMA1
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI1,
-               DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_DSI1
+               DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK,
+               RDMA1_SOUT_DSI1
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI1,
-               DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_RDMA1
+               DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_MASK,
+               DSI1_SEL_IN_RDMA1
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI2,
-               DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_DSI2
+               DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK,
+               RDMA1_SOUT_DSI2
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI2,
-               DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_RDMA1
+               DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_MASK,
+               DSI2_SEL_IN_RDMA1
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI3,
-               DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_DSI3
+               DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK,
+               RDMA1_SOUT_DSI3
        }, {
                DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI3,
-               DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_RDMA1
+               DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_MASK,
+               DSI3_SEL_IN_RDMA1
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI0,
-               DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_DPI0
+               DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK,
+               RDMA2_SOUT_DPI0
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI0,
-               DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_RDMA2
+               DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_MASK,
+               DPI0_SEL_IN_RDMA2
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI1,
-               DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_DPI1
+               DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK,
+               RDMA2_SOUT_DPI1
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI1,
-               DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_RDMA2
+               DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_MASK,
+               DPI1_SEL_IN_RDMA2
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI0,
-               DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_RDMA2
+               DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_MASK,
+               DSI0_SEL_IN_RDMA2
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI1,
-               DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_DSI1
+               DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK,
+               RDMA2_SOUT_DSI1
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI1,
-               DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_RDMA2
+               DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_MASK,
+               DSI1_SEL_IN_RDMA2
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI2,
-               DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_DSI2
+               DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK,
+               RDMA2_SOUT_DSI2
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI2,
-               DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_RDMA2
+               DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_MASK,
+               DSI2_SEL_IN_RDMA2
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI3,
-               DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_DSI3
+               DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK,
+               RDMA2_SOUT_DSI3
        }, {
                DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI3,
-               DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_RDMA2
+               DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_MASK,
+               DSI3_SEL_IN_RDMA2
+       }, {
+               DDP_COMPONENT_UFOE, DDP_COMPONENT_DSI0,
+               DISP_REG_CONFIG_DISP_UFOE_MOUT_EN, UFOE_MOUT_EN_DSI0,
+               UFOE_MOUT_EN_DSI0
        }
 };
 
index 21a4e11..c5ac649 100644 (file)
@@ -60,7 +60,7 @@
 #define BUS_PROT_UPDATE_TOPAXI(_mask)                          \
                BUS_PROT_UPDATE(_mask,                          \
                                INFRA_TOPAXI_PROTECTEN,         \
-                               INFRA_TOPAXI_PROTECTEN_CLR,     \
+                               INFRA_TOPAXI_PROTECTEN,         \
                                INFRA_TOPAXI_PROTECTSTA1)
 
 struct scpsys_bus_prot_data {
index b24cc77..4ce8e81 100644 (file)
@@ -801,38 +801,6 @@ unlock:
        return ret;
 }
 
-static int cpr_read_efuse(struct device *dev, const char *cname, u32 *data)
-{
-       struct nvmem_cell *cell;
-       ssize_t len;
-       char *ret;
-       int i;
-
-       *data = 0;
-
-       cell = nvmem_cell_get(dev, cname);
-       if (IS_ERR(cell)) {
-               if (PTR_ERR(cell) != -EPROBE_DEFER)
-                       dev_err(dev, "undefined cell %s\n", cname);
-               return PTR_ERR(cell);
-       }
-
-       ret = nvmem_cell_read(cell, &len);
-       nvmem_cell_put(cell);
-       if (IS_ERR(ret)) {
-               dev_err(dev, "can't read cell %s\n", cname);
-               return PTR_ERR(ret);
-       }
-
-       for (i = 0; i < len; i++)
-               *data |= ret[i] << (8 * i);
-
-       kfree(ret);
-       dev_dbg(dev, "efuse read(%s) = %x, bytes %zd\n", cname, *data, len);
-
-       return 0;
-}
-
 static int
 cpr_populate_ring_osc_idx(struct cpr_drv *drv)
 {
@@ -843,8 +811,7 @@ cpr_populate_ring_osc_idx(struct cpr_drv *drv)
        int ret;
 
        for (; fuse < end; fuse++, fuses++) {
-               ret = cpr_read_efuse(drv->dev, fuses->ring_osc,
-                                    &data);
+               ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->ring_osc, &data);
                if (ret)
                        return ret;
                fuse->ring_osc_idx = data;
@@ -863,7 +830,7 @@ static int cpr_read_fuse_uV(const struct cpr_desc *desc,
        u32 bits = 0;
        int ret;
 
-       ret = cpr_read_efuse(drv->dev, init_v_efuse, &bits);
+       ret = nvmem_cell_read_variable_le_u32(drv->dev, init_v_efuse, &bits);
        if (ret)
                return ret;
 
@@ -932,7 +899,7 @@ static int cpr_fuse_corner_init(struct cpr_drv *drv)
                }
 
                /* Populate target quotient by scaling */
-               ret = cpr_read_efuse(drv->dev, fuses->quotient, &fuse->quot);
+               ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->quotient, &fuse->quot);
                if (ret)
                        return ret;
 
@@ -1001,7 +968,7 @@ static int cpr_calculate_scaling(const char *quot_offset,
        prev_fuse = fuse - 1;
 
        if (quot_offset) {
-               ret = cpr_read_efuse(drv->dev, quot_offset, &quot_diff);
+               ret = nvmem_cell_read_variable_le_u32(drv->dev, quot_offset, &quot_diff);
                if (ret)
                        return ret;
 
@@ -1701,7 +1668,7 @@ static int cpr_probe(struct platform_device *pdev)
         * initialized after attaching to the power domain,
         * since it depends on the CPU's OPP table.
         */
-       ret = cpr_read_efuse(dev, "cpr_fuse_revision", &cpr_rev);
+       ret = nvmem_cell_read_variable_le_u32(dev, "cpr_fuse_revision", &cpr_rev);
        if (ret)
                return ret;
 
index eba7f76..bda170d 100644 (file)
@@ -166,6 +166,8 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
                metadata = qcom_mdt_read_metadata(fw, &metadata_len);
                if (IS_ERR(metadata)) {
                        ret = PTR_ERR(metadata);
+                       dev_err(dev, "error %d reading firmware %s metadata\n",
+                               ret, fw_name);
                        goto out;
                }
 
@@ -173,7 +175,9 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
 
                kfree(metadata);
                if (ret) {
-                       dev_err(dev, "invalid firmware metadata\n");
+                       /* Invalid firmware metadata */
+                       dev_err(dev, "error %d initializing firmware %s\n",
+                               ret, fw_name);
                        goto out;
                }
        }
@@ -199,7 +203,9 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
                        ret = qcom_scm_pas_mem_setup(pas_id, mem_phys,
                                                     max_addr - min_addr);
                        if (ret) {
-                               dev_err(dev, "unable to setup relocation\n");
+                               /* Unable to set up relocation */
+                               dev_err(dev, "error %d setting up firmware %s\n",
+                                       ret, fw_name);
                                goto out;
                        }
                }
@@ -243,9 +249,8 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
                if (phdr->p_filesz && phdr->p_offset < fw->size) {
                        /* Firmware is large enough to be non-split */
                        if (phdr->p_offset + phdr->p_filesz > fw->size) {
-                               dev_err(dev,
-                                       "failed to load segment %d from truncated file %s\n",
-                                       i, firmware);
+                               dev_err(dev, "file %s segment %d would be truncated\n",
+                                       fw_name, i);
                                ret = -EINVAL;
                                break;
                        }
@@ -257,7 +262,8 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
                        ret = request_firmware_into_buf(&seg_fw, fw_name, dev,
                                                        ptr, phdr->p_filesz);
                        if (ret) {
-                               dev_err(dev, "failed to load %s\n", fw_name);
+                               dev_err(dev, "error %d loading %s\n",
+                                       ret, fw_name);
                                break;
                        }
 
index 5bdfb15..7d649d2 100644 (file)
@@ -104,7 +104,6 @@ static const char * const icc_path_names[] = {"qup-core", "qup-config",
 #define GENI_OUTPUT_CTRL               0x24
 #define GENI_CGC_CTRL                  0x28
 #define GENI_CLK_CTRL_RO               0x60
-#define GENI_IF_DISABLE_RO             0x64
 #define GENI_FW_S_REVISION_RO          0x6c
 #define SE_GENI_BYTE_GRAN              0x254
 #define SE_GENI_TX_PACKING_CFG0                0x260
@@ -322,6 +321,30 @@ static void geni_se_select_dma_mode(struct geni_se *se)
                writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN);
 }
 
+static void geni_se_select_gpi_mode(struct geni_se *se)
+{
+       u32 val;
+
+       geni_se_irq_clear(se);
+
+       writel(0, se->base + SE_IRQ_EN);
+
+       val = readl(se->base + SE_GENI_S_IRQ_EN);
+       val &= ~S_CMD_DONE_EN;
+       writel(val, se->base + SE_GENI_S_IRQ_EN);
+
+       val = readl(se->base + SE_GENI_M_IRQ_EN);
+       val &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN |
+                M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
+       writel(val, se->base + SE_GENI_M_IRQ_EN);
+
+       writel(GENI_DMA_MODE_EN, se->base + SE_GENI_DMA_MODE_EN);
+
+       val = readl(se->base + SE_GSI_EVENT_EN);
+       val |= (DMA_RX_EVENT_EN | DMA_TX_EVENT_EN | GENI_M_EVENT_EN | GENI_S_EVENT_EN);
+       writel(val, se->base + SE_GSI_EVENT_EN);
+}
+
 /**
  * geni_se_select_mode() - Select the serial engine transfer mode
  * @se:                Pointer to the concerned serial engine.
@@ -329,7 +352,7 @@ static void geni_se_select_dma_mode(struct geni_se *se)
  */
 void geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode)
 {
-       WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA);
+       WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA && mode != GENI_GPI_DMA);
 
        switch (mode) {
        case GENI_SE_FIFO:
@@ -338,6 +361,9 @@ void geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode)
        case GENI_SE_DMA:
                geni_se_select_dma_mode(se);
                break;
+       case GENI_GPI_DMA:
+               geni_se_select_gpi_mode(se);
+               break;
        case GENI_SE_INVALID:
        default:
                break;
index 934fcc4..536c3e4 100644 (file)
@@ -476,12 +476,12 @@ static int qmp_cooling_device_add(struct qmp *qmp,
 static int qmp_cooling_devices_register(struct qmp *qmp)
 {
        struct device_node *np, *child;
-       int count = QMP_NUM_COOLING_RESOURCES;
+       int count = 0;
        int ret;
 
        np = qmp->dev->of_node;
 
-       qmp->cooling_devs = devm_kcalloc(qmp->dev, count,
+       qmp->cooling_devs = devm_kcalloc(qmp->dev, QMP_NUM_COOLING_RESOURCES,
                                         sizeof(*qmp->cooling_devs),
                                         GFP_KERNEL);
 
@@ -497,12 +497,16 @@ static int qmp_cooling_devices_register(struct qmp *qmp)
                        goto unroll;
        }
 
+       if (!count)
+               devm_kfree(qmp->dev, qmp->cooling_devs);
+
        return 0;
 
 unroll:
        while (--count >= 0)
                thermal_cooling_device_unregister
                        (qmp->cooling_devs[count].cdev);
+       devm_kfree(qmp->dev, qmp->cooling_devs);
 
        return ret;
 }
@@ -602,6 +606,7 @@ static const struct of_device_id qmp_dt_match[] = {
        { .compatible = "qcom,sm8150-aoss-qmp", },
        { .compatible = "qcom,sm8250-aoss-qmp", },
        { .compatible = "qcom,sm8350-aoss-qmp", },
+       { .compatible = "qcom,aoss-qmp", },
        {}
 };
 MODULE_DEVICE_TABLE(of, qmp_dt_match);
index 2daa17b..fa209b4 100644 (file)
@@ -403,12 +403,11 @@ static int rpmhpd_power_on(struct generic_pm_domain *domain)
 static int rpmhpd_power_off(struct generic_pm_domain *domain)
 {
        struct rpmhpd *pd = domain_to_rpmhpd(domain);
-       int ret = 0;
+       int ret;
 
        mutex_lock(&rpmhpd_lock);
 
-       ret = rpmhpd_aggregate_corner(pd, pd->level[0]);
-
+       ret = rpmhpd_aggregate_corner(pd, 0);
        if (!ret)
                pd->enabled = false;
 
index 0b532a8..dbf494e 100644 (file)
@@ -346,6 +346,33 @@ static const struct rpmpd_desc sdm660_desc = {
        .max_state = RPM_SMD_LEVEL_TURBO,
 };
 
+/* sm4250/6115 RPM Power domains */
+DEFINE_RPMPD_PAIR(sm6115, vddcx, vddcx_ao, RWCX, LEVEL, 0);
+DEFINE_RPMPD_VFL(sm6115, vddcx_vfl, RWCX, 0);
+
+DEFINE_RPMPD_PAIR(sm6115, vddmx, vddmx_ao, RWMX, LEVEL, 0);
+DEFINE_RPMPD_VFL(sm6115, vddmx_vfl, RWMX, 0);
+
+DEFINE_RPMPD_LEVEL(sm6115, vdd_lpi_cx, RWLC, 0);
+DEFINE_RPMPD_LEVEL(sm6115, vdd_lpi_mx, RWLM, 0);
+
+static struct rpmpd *sm6115_rpmpds[] = {
+       [SM6115_VDDCX] =                &sm6115_vddcx,
+       [SM6115_VDDCX_AO] =             &sm6115_vddcx_ao,
+       [SM6115_VDDCX_VFL] =            &sm6115_vddcx_vfl,
+       [SM6115_VDDMX] =                &sm6115_vddmx,
+       [SM6115_VDDMX_AO] =             &sm6115_vddmx_ao,
+       [SM6115_VDDMX_VFL] =            &sm6115_vddmx_vfl,
+       [SM6115_VDD_LPI_CX] =           &sm6115_vdd_lpi_cx,
+       [SM6115_VDD_LPI_MX] =           &sm6115_vdd_lpi_mx,
+};
+
+static const struct rpmpd_desc sm6115_desc = {
+       .rpmpds = sm6115_rpmpds,
+       .num_pds = ARRAY_SIZE(sm6115_rpmpds),
+       .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR,
+};
+
 static const struct of_device_id rpmpd_match_table[] = {
        { .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc },
        { .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc },
@@ -356,6 +383,7 @@ static const struct of_device_id rpmpd_match_table[] = {
        { .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc },
        { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc },
        { .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc },
+       { .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc },
        { }
 };
 MODULE_DEVICE_TABLE(of, rpmpd_match_table);
index bc0be1d..dfdd4f2 100644 (file)
@@ -242,6 +242,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = {
        { .compatible = "qcom,rpm-msm8996" },
        { .compatible = "qcom,rpm-msm8998" },
        { .compatible = "qcom,rpm-sdm660" },
+       { .compatible = "qcom,rpm-sm6115" },
        { .compatible = "qcom,rpm-sm6125" },
        { .compatible = "qcom,rpm-qcs404" },
        {}
index 1d3d5e3..ef15d01 100644 (file)
@@ -109,7 +109,7 @@ struct smsm_entry {
        DECLARE_BITMAP(irq_enabled, 32);
        DECLARE_BITMAP(irq_rising, 32);
        DECLARE_BITMAP(irq_falling, 32);
-       u32 last_value;
+       unsigned long last_value;
 
        u32 *remote_state;
        u32 *subscription;
@@ -204,8 +204,7 @@ static irqreturn_t smsm_intr(int irq, void *data)
        u32 val;
 
        val = readl(entry->remote_state);
-       changed = val ^ entry->last_value;
-       entry->last_value = val;
+       changed = val ^ xchg(&entry->last_value, val);
 
        for_each_set_bit(i, entry->irq_enabled, 32) {
                if (!(changed & BIT(i)))
@@ -264,6 +263,12 @@ static void smsm_unmask_irq(struct irq_data *irqd)
        struct qcom_smsm *smsm = entry->smsm;
        u32 val;
 
+       /* Make sure our last cached state is up-to-date */
+       if (readl(entry->remote_state) & BIT(irq))
+               set_bit(irq, &entry->last_value);
+       else
+               clear_bit(irq, &entry->last_value);
+
        set_bit(irq, entry->irq_enabled);
 
        if (entry->subscription) {
@@ -299,11 +304,28 @@ static int smsm_set_irq_type(struct irq_data *irqd, unsigned int type)
        return 0;
 }
 
+static int smsm_get_irqchip_state(struct irq_data *irqd,
+                                 enum irqchip_irq_state which, bool *state)
+{
+       struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
+       irq_hw_number_t irq = irqd_to_hwirq(irqd);
+       u32 val;
+
+       if (which != IRQCHIP_STATE_LINE_LEVEL)
+               return -EINVAL;
+
+       val = readl(entry->remote_state);
+       *state = !!(val & BIT(irq));
+
+       return 0;
+}
+
 static struct irq_chip smsm_irq_chip = {
        .name           = "smsm",
        .irq_mask       = smsm_mask_irq,
        .irq_unmask     = smsm_unmask_irq,
        .irq_set_type   = smsm_set_irq_type,
+       .irq_get_irqchip_state = smsm_get_irqchip_state,
 };
 
 /**
index b2f049f..9faf483 100644 (file)
@@ -417,8 +417,8 @@ QCOM_OPEN(chip_id, qcom_show_chip_id);
 static int show_image_##type(struct seq_file *seq, void *p)              \
 {                                                                \
        struct smem_image_version *image_version = seq->private;  \
-       seq_puts(seq, image_version->type);                       \
-       seq_putc(seq, '\n');                                      \
+       if (image_version->type[0] != '\0')                       \
+               seq_printf(seq, "%s\n", image_version->type);     \
        return 0;                                                 \
 }                                                                \
 static int open_image_##type(struct inode *inode, struct file *file)     \
index 71b44c3..07e0ecd 100644 (file)
@@ -208,6 +208,7 @@ config ARCH_R8A77951
        help
          This enables support for the Renesas R-Car H3 SoC (revisions 2.0 and
          later).
+         This includes different gradings like R-Car H3e-2G.
 
 config ARCH_R8A77965
        bool "ARM64 Platform support for R-Car M3-N"
@@ -229,6 +230,7 @@ config ARCH_R8A77961
        select SYSC_R8A77961
        help
          This enables support for the Renesas R-Car M3-W+ SoC.
+         This includes different gradings like R-Car M3e-2G.
 
 config ARCH_R8A77980
        bool "ARM64 Platform support for R-Car V3H"
index d464ffa..7410b9f 100644 (file)
@@ -404,19 +404,21 @@ static int __init r8a779a0_sysc_pd_init(void)
        for (i = 0; i < info->num_areas; i++) {
                const struct r8a779a0_sysc_area *area = &info->areas[i];
                struct r8a779a0_sysc_pd *pd;
+               size_t n;
 
                if (!area->name) {
                        /* Skip NULLified area */
                        continue;
                }
 
-               pd = kzalloc(sizeof(*pd) + strlen(area->name) + 1, GFP_KERNEL);
+               n = strlen(area->name) + 1;
+               pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL);
                if (!pd) {
                        error = -ENOMEM;
                        goto out_put;
                }
 
-               strcpy(pd->name, area->name);
+               memcpy(pd->name, area->name, n);
                pd->genpd.name = pd->name;
                pd->pdr = area->pdr;
                pd->flags = area->flags;
index 53387a7..b0a80de 100644 (file)
@@ -396,19 +396,21 @@ static int __init rcar_sysc_pd_init(void)
        for (i = 0; i < info->num_areas; i++) {
                const struct rcar_sysc_area *area = &info->areas[i];
                struct rcar_sysc_pd *pd;
+               size_t n;
 
                if (!area->name) {
                        /* Skip NULLified area */
                        continue;
                }
 
-               pd = kzalloc(sizeof(*pd) + strlen(area->name) + 1, GFP_KERNEL);
+               n = strlen(area->name) + 1;
+               pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL);
                if (!pd) {
                        error = -ENOMEM;
                        goto out_put;
                }
 
-               strcpy(pd->name, area->name);
+               memcpy(pd->name, area->name, n);
                pd->genpd.name = pd->name;
                pd->ch.chan_offs = area->chan_offs;
                pd->ch.chan_bit = area->chan_bit;
index 8310fce..dab9f5a 100644 (file)
@@ -284,11 +284,15 @@ static const struct of_device_id renesas_socs[] __initconst = {
 #if defined(CONFIG_ARCH_R8A77950) || defined(CONFIG_ARCH_R8A77951)
        { .compatible = "renesas,r8a7795",      .data = &soc_rcar_h3 },
 #endif
+#ifdef CONFIG_ARCH_R8A77951
+       { .compatible = "renesas,r8a779m1",     .data = &soc_rcar_h3 },
+#endif
 #ifdef CONFIG_ARCH_R8A77960
        { .compatible = "renesas,r8a7796",      .data = &soc_rcar_m3_w },
 #endif
 #ifdef CONFIG_ARCH_R8A77961
        { .compatible = "renesas,r8a77961",     .data = &soc_rcar_m3_w },
+       { .compatible = "renesas,r8a779m3",     .data = &soc_rcar_m3_w },
 #endif
 #ifdef CONFIG_ARCH_R8A77965
        { .compatible = "renesas,r8a77965",     .data = &soc_rcar_m3_n },
index 2c13bf4..25eb2c1 100644 (file)
@@ -6,8 +6,8 @@ if ARCH_ROCKCHIP || COMPILE_TEST
 #
 
 config ROCKCHIP_GRF
-       bool
-       default y
+       bool "Rockchip General Register Files support" if COMPILE_TEST
+       default y if ARCH_ROCKCHIP
        help
          The General Register Files are a central component providing
          special additional settings registers for a lot of soc-components.
index cf8182f..9df513d 100644 (file)
 #define RK3399_PMUGRF_CON0_VSEL                BIT(8)
 #define RK3399_PMUGRF_VSEL_SUPPLY_NUM  9
 
-struct rockchip_iodomain;
+#define RK3568_PMU_GRF_IO_VSEL0                (0x0140)
+#define RK3568_PMU_GRF_IO_VSEL1                (0x0144)
+#define RK3568_PMU_GRF_IO_VSEL2                (0x0148)
 
-struct rockchip_iodomain_soc_data {
-       int grf_offset;
-       const char *supply_names[MAX_SUPPLIES];
-       void (*init)(struct rockchip_iodomain *iod);
-};
+struct rockchip_iodomain;
 
 struct rockchip_iodomain_supply {
        struct rockchip_iodomain *iod;
@@ -66,13 +64,62 @@ struct rockchip_iodomain_supply {
        int idx;
 };
 
+struct rockchip_iodomain_soc_data {
+       int grf_offset;
+       const char *supply_names[MAX_SUPPLIES];
+       void (*init)(struct rockchip_iodomain *iod);
+       int (*write)(struct rockchip_iodomain_supply *supply, int uV);
+};
+
 struct rockchip_iodomain {
        struct device *dev;
        struct regmap *grf;
        const struct rockchip_iodomain_soc_data *soc_data;
        struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
+       int (*write)(struct rockchip_iodomain_supply *supply, int uV);
 };
 
+static int rk3568_iodomain_write(struct rockchip_iodomain_supply *supply, int uV)
+{
+       struct rockchip_iodomain *iod = supply->iod;
+       u32 is_3v3 = uV > MAX_VOLTAGE_1_8;
+       u32 val0, val1;
+       int b;
+
+       switch (supply->idx) {
+       case 0: /* pmuio1 */
+               break;
+       case 1: /* pmuio2 */
+               b = supply->idx;
+               val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
+               b = supply->idx + 4;
+               val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
+
+               regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val0);
+               regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val1);
+               break;
+       case 3: /* vccio2 */
+               break;
+       case 2: /* vccio1 */
+       case 4: /* vccio3 */
+       case 5: /* vccio4 */
+       case 6: /* vccio5 */
+       case 7: /* vccio6 */
+       case 8: /* vccio7 */
+               b = supply->idx - 1;
+               val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
+               val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
+
+               regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL0, val0);
+               regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL1, val1);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply,
                                   int uV)
 {
@@ -136,7 +183,7 @@ static int rockchip_iodomain_notify(struct notifier_block *nb,
                        return NOTIFY_BAD;
        }
 
-       ret = rockchip_iodomain_write(supply, uV);
+       ret = supply->iod->write(supply, uV);
        if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
                return NOTIFY_BAD;
 
@@ -398,6 +445,22 @@ static const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = {
        .init = rk3399_pmu_iodomain_init,
 };
 
+static const struct rockchip_iodomain_soc_data soc_data_rk3568_pmu = {
+       .grf_offset = 0x140,
+       .supply_names = {
+               "pmuio1",
+               "pmuio2",
+               "vccio1",
+               "vccio2",
+               "vccio3",
+               "vccio4",
+               "vccio5",
+               "vccio6",
+               "vccio7",
+       },
+       .write = rk3568_iodomain_write,
+};
+
 static const struct rockchip_iodomain_soc_data soc_data_rv1108 = {
        .grf_offset = 0x404,
        .supply_names = {
@@ -469,6 +532,10 @@ static const struct of_device_id rockchip_iodomain_match[] = {
                .compatible = "rockchip,rk3399-pmu-io-voltage-domain",
                .data = &soc_data_rk3399_pmu
        },
+       {
+               .compatible = "rockchip,rk3568-pmu-io-voltage-domain",
+               .data = &soc_data_rk3568_pmu
+       },
        {
                .compatible = "rockchip,rv1108-io-voltage-domain",
                .data = &soc_data_rv1108
@@ -502,6 +569,11 @@ static int rockchip_iodomain_probe(struct platform_device *pdev)
        match = of_match_node(rockchip_iodomain_match, np);
        iod->soc_data = match->data;
 
+       if (iod->soc_data->write)
+               iod->write = iod->soc_data->write;
+       else
+               iod->write = rockchip_iodomain_write;
+
        parent = pdev->dev.parent;
        if (parent && parent->of_node) {
                iod->grf = syscon_node_to_regmap(parent->of_node);
@@ -562,7 +634,7 @@ static int rockchip_iodomain_probe(struct platform_device *pdev)
                supply->reg = reg;
                supply->nb.notifier_call = rockchip_iodomain_notify;
 
-               ret = rockchip_iodomain_write(supply, uV);
+               ret = iod->write(supply, uV);
                if (ret) {
                        supply->reg = NULL;
                        goto unreg_notify;
index 3d9da3d..f215181 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/sys_soc.h>
 
@@ -210,6 +211,8 @@ static int tegra_fuse_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, fuse);
        fuse->dev = &pdev->dev;
 
+       pm_runtime_enable(&pdev->dev);
+
        if (fuse->soc->probe) {
                err = fuse->soc->probe(fuse);
                if (err < 0)
@@ -246,14 +249,71 @@ static int tegra_fuse_probe(struct platform_device *pdev)
        return 0;
 
 restore:
+       fuse->clk = NULL;
        fuse->base = base;
+       pm_runtime_disable(&pdev->dev);
        return err;
 }
 
+static int __maybe_unused tegra_fuse_runtime_resume(struct device *dev)
+{
+       int err;
+
+       err = clk_prepare_enable(fuse->clk);
+       if (err < 0) {
+               dev_err(dev, "failed to enable FUSE clock: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused tegra_fuse_runtime_suspend(struct device *dev)
+{
+       clk_disable_unprepare(fuse->clk);
+
+       return 0;
+}
+
+static int __maybe_unused tegra_fuse_suspend(struct device *dev)
+{
+       int ret;
+
+       /*
+        * Critical for RAM re-repair operation, which must occur on resume
+        * from LP1 system suspend and as part of CCPLEX cluster switching.
+        */
+       if (fuse->soc->clk_suspend_on)
+               ret = pm_runtime_resume_and_get(dev);
+       else
+               ret = pm_runtime_force_suspend(dev);
+
+       return ret;
+}
+
+static int __maybe_unused tegra_fuse_resume(struct device *dev)
+{
+       int ret = 0;
+
+       if (fuse->soc->clk_suspend_on)
+               pm_runtime_put(dev);
+       else
+   &n