Devicetree
 help / color / mirror / Atom feed
* [PATCH 5/6] bus: cdmm: Add MIPS R5 arch support
From: Serge Semin @ 2020-05-26 14:02 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
	Rob Herring, Arnd Bergmann, Jason Cooper, Marc Zyngier,
	Rafael J. Wysocki, Daniel Lezcano, James Hogan, linux-mips,
	devicetree, linux-kernel
In-Reply-To: <20200526140243.21946-1-Sergey.Semin@baikalelectronics.ru>

CDMM may be available not only on MIPS R2 architectures, but also on
newer MIPS R5 chips. For instance our P5600 chip has one. Let's mark
the CDMM bus being supported for that MIPS arch too.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
---
 drivers/bus/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 6d4e4497b59b..971c07bc92d4 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -58,7 +58,7 @@ config IMX_WEIM
 
 config MIPS_CDMM
 	bool "MIPS Common Device Memory Map (CDMM) Driver"
-	depends on CPU_MIPSR2
+	depends on CPU_MIPSR2 || CPU_MIPSR5
 	help
 	  Driver needed for the MIPS Common Device Memory Map bus in MIPS
 	  cores. This bus is for per-CPU tightly coupled devices such as the
-- 
2.26.2


^ permalink raw reply related

* [PATCH 2/6] dt-bindings: interrupt-controller: Convert mti,gic to DT schema
From: Serge Semin @ 2020-05-26 14:02 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman,
	Jason Cooper, Marc Zyngier, Rob Herring
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
	Arnd Bergmann, Rafael J. Wysocki, Daniel Lezcano, James Hogan,
	linux-mips, devicetree, linux-kernel, Rob Herring
In-Reply-To: <20200526140243.21946-1-Sergey.Semin@baikalelectronics.ru>

Modern device tree bindings are supposed to be created as YAML-files
in accordance with DT schema. This commit replaces MIPS GIC legacy bare
text binding with YAML file. As before the binding file states that the
corresponding dts node is supposed to be compatible with MIPS Global
Interrupt Controller indicated by the "mti,gic" compatible string and
to provide a mandatory interrupt-controller and '#interrupt-cells'
properties. There might be optional registers memory range,
"mti,reserved-cpu-vectors" and "mti,reserved-ipi-vectors" properties
specified.

MIPS GIC also includes a free-running global timer, per-CPU count/compare
timers, and a watchdog. Since currently the GIC Timer is only supported the
DT schema expects an IRQ and clock-phandler charged timer sub-node with
"mti,mips-gic-timer" compatible string.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>

---

I don't really know who is the corresponding driver maintainer, so I
added Paul to the maintainers property since he used to be looking for the
MIPS arch and Thomas looking after it now. Any idea what email should be
specified there instead?

Changelog prev:
- Since timer sub-node has no unit-address, the node shouldn't be named
  with one. So alter the MIPS GIC bindings to have a pure "timer"
  sub-node.
- Discard allOf: [ $ref: /schemas/interrupt-controller.yaml# ].
- Since it's a conversion patch use GPL-2.0-only SPDX header.
---
 .../interrupt-controller/mips-gic.txt         |  67 --------
 .../interrupt-controller/mti,gic.yaml         | 148 ++++++++++++++++++
 2 files changed, 148 insertions(+), 67 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/interrupt-controller/mips-gic.txt
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/mti,gic.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/mips-gic.txt b/Documentation/devicetree/bindings/interrupt-controller/mips-gic.txt
deleted file mode 100644
index 173595305e26..000000000000
--- a/Documentation/devicetree/bindings/interrupt-controller/mips-gic.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-MIPS Global Interrupt Controller (GIC)
-
-The MIPS GIC routes external interrupts to individual VPEs and IRQ pins.
-It also supports local (per-processor) interrupts and software-generated
-interrupts which can be used as IPIs.  The GIC also includes a free-running
-global timer, per-CPU count/compare timers, and a watchdog.
-
-Required properties:
-- compatible : Should be "mti,gic".
-- interrupt-controller : Identifies the node as an interrupt controller
-- #interrupt-cells : Specifies the number of cells needed to encode an
-  interrupt specifier.  Should be 3.
-  - The first cell is the type of interrupt, local or shared.
-    See <include/dt-bindings/interrupt-controller/mips-gic.h>.
-  - The second cell is the GIC interrupt number.
-  - The third cell encodes the interrupt flags.
-    See <include/dt-bindings/interrupt-controller/irq.h> for a list of valid
-    flags.
-
-Optional properties:
-- reg : Base address and length of the GIC registers.  If not present,
-  the base address reported by the hardware GCR_GIC_BASE will be used.
-- mti,reserved-cpu-vectors : Specifies the list of CPU interrupt vectors
-  to which the GIC may not route interrupts.  Valid values are 2 - 7.
-  This property is ignored if the CPU is started in EIC mode.
-- mti,reserved-ipi-vectors : Specifies the range of GIC interrupts that are
-  reserved for IPIs.
-  It accepts 2 values, the 1st is the starting interrupt and the 2nd is the size
-  of the reserved range.
-  If not specified, the driver will allocate the last 2 * number of VPEs in the
-  system.
-
-Required properties for timer sub-node:
-- compatible : Should be "mti,gic-timer".
-- interrupts : Interrupt for the GIC local timer.
-
-Optional properties for timer sub-node:
-- clocks : GIC timer operating clock.
-- clock-frequency : Clock frequency at which the GIC timers operate.
-
-Note that one of clocks or clock-frequency must be specified.
-
-Example:
-
-	gic: interrupt-controller@1bdc0000 {
-		compatible = "mti,gic";
-		reg = <0x1bdc0000 0x20000>;
-
-		interrupt-controller;
-		#interrupt-cells = <3>;
-
-		mti,reserved-cpu-vectors = <7>;
-		mti,reserved-ipi-vectors = <40 8>;
-
-		timer {
-			compatible = "mti,gic-timer";
-			interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
-			clock-frequency = <50000000>;
-		};
-	};
-
-	uart@18101400 {
-		...
-		interrupt-parent = <&gic>;
-		interrupts = <GIC_SHARED 24 IRQ_TYPE_LEVEL_HIGH>;
-		...
-	};
diff --git a/Documentation/devicetree/bindings/interrupt-controller/mti,gic.yaml b/Documentation/devicetree/bindings/interrupt-controller/mti,gic.yaml
new file mode 100644
index 000000000000..9f0eb3addac4
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/mti,gic.yaml
@@ -0,0 +1,148 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/mti,gic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MIPS Global Interrupt Controller
+
+maintainers:
+  - Paul Burton <paulburton@kernel.org>
+  - Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+
+description: |
+  The MIPS GIC routes external interrupts to individual VPEs and IRQ pins.
+  It also supports local (per-processor) interrupts and software-generated
+  interrupts which can be used as IPIs. The GIC also includes a free-running
+  global timer, per-CPU count/compare timers, and a watchdog.
+
+properties:
+  compatible:
+    const: mti,gic
+
+  "#interrupt-cells":
+    const: 3
+    description: |
+      The 1st cell is the type of interrupt: local or shared defined in the
+      file 'dt-bindings/interrupt-controller/mips-gic.h'. The 2nd cell is the
+      GIC interrupt number. The 3d cell encodes the interrupt flags setting up
+      the IRQ trigger modes, which are defined in the file
+      'dt-bindings/interrupt-controller/irq.h'.
+
+  reg:
+    description: |
+      Base address and length of the GIC registers space. If not present,
+      the base address reported by the hardware GCR_GIC_BASE will be used.
+    maxItems: 1
+
+  interrupt-controller: true
+
+  mti,reserved-cpu-vectors:
+    description: |
+      Specifies the list of CPU interrupt vectors to which the GIC may not
+      route interrupts. This property is ignored if the CPU is started in EIC
+      mode.
+    allOf:
+      - $ref: /schemas/types.yaml#definitions/uint32-array
+      - minItems: 1
+        maxItems: 6
+        uniqueItems: true
+        items:
+          minimum: 2
+          maximum: 7
+
+  mti,reserved-ipi-vectors:
+    description: |
+      Specifies the range of GIC interrupts that are reserved for IPIs.
+      It accepts two values: the 1st is the starting interrupt and the 2nd is
+      the size of the reserved range. If not specified, the driver will
+      allocate the last (2 * number of VPEs in the system).
+    allOf:
+      - $ref: /schemas/types.yaml#definitions/uint32-array
+      - items:
+          - minimum: 0
+            maximum: 254
+          - minimum: 2
+            maximum: 254
+
+  timer:
+    type: object
+    description: |
+      MIPS GIC includes a free-running global timer, per-CPU count/compare
+      timers, and a watchdog. Currently only the GIC Timer is supported.
+    properties:
+      compatible:
+        const: mti,gic-timer
+
+      interrupts:
+        description: |
+          Interrupt for the GIC local timer, so normally it's suppose to be of
+          <GIC_LOCAL X IRQ_TYPE_NONE> format.
+        maxItems: 1
+
+      clocks:
+        maxItems: 1
+
+      clock-frequency: true
+
+    required:
+      - compatible
+      - interrupts
+
+    oneOf:
+      - required:
+          - clocks
+      - required:
+          - clock-frequency
+
+    additionalProperties: false
+
+unevaluatedProperties: false
+
+required:
+  - compatible
+  - "#interrupt-cells"
+  - interrupt-controller
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/mips-gic.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    interrupt-controller@1bdc0000 {
+      compatible = "mti,gic";
+      reg = <0x1bdc0000 0x20000>;
+      interrupt-controller;
+      #interrupt-cells = <3>;
+      mti,reserved-cpu-vectors = <7>;
+      mti,reserved-ipi-vectors = <40 8>;
+
+      timer {
+        compatible = "mti,gic-timer";
+        interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+        clock-frequency = <50000000>;
+      };
+    };
+  - |
+    #include <dt-bindings/interrupt-controller/mips-gic.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    interrupt-controller@1bdc0000 {
+      compatible = "mti,gic";
+      reg = <0x1bdc0000 0x20000>;
+      interrupt-controller;
+      #interrupt-cells = <3>;
+
+      timer {
+        compatible = "mti,gic-timer";
+        interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+        clocks = <&cpu_pll>;
+      };
+    };
+  - |
+    interrupt-controller {
+      compatible = "mti,gic";
+      interrupt-controller;
+      #interrupt-cells = <3>;
+    };
+...
-- 
2.26.2


^ permalink raw reply related

* [PATCH 1/6] dt-bindings: power: Convert mti,mips-cpc to DT schema
From: Serge Semin @ 2020-05-26 14:02 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman,
	Paul Burton, Rob Herring
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
	Arnd Bergmann, Jason Cooper, Marc Zyngier, Rafael J. Wysocki,
	Daniel Lezcano, James Hogan, linux-mips, devicetree, linux-kernel,
	Rob Herring
In-Reply-To: <20200526140243.21946-1-Sergey.Semin@baikalelectronics.ru>

It's a Cluster Power Controller embedded into the MIPS IP cores.
Currently the corresponding dts node is supposed to have compatible
and reg properties.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>

---

Changelog prev:
- Reword the changelog summary - use shorter version.
- Lowercase the example hex'es.
---
 .../bindings/power/mti,mips-cpc.txt           |  8 -----
 .../bindings/power/mti,mips-cpc.yaml          | 35 +++++++++++++++++++
 2 files changed, 35 insertions(+), 8 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/power/mti,mips-cpc.txt
 create mode 100644 Documentation/devicetree/bindings/power/mti,mips-cpc.yaml

diff --git a/Documentation/devicetree/bindings/power/mti,mips-cpc.txt b/Documentation/devicetree/bindings/power/mti,mips-cpc.txt
deleted file mode 100644
index c6b82511ae8a..000000000000
--- a/Documentation/devicetree/bindings/power/mti,mips-cpc.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Binding for MIPS Cluster Power Controller (CPC).
-
-This binding allows a system to specify where the CPC registers are
-located.
-
-Required properties:
-compatible : Should be "mti,mips-cpc".
-regs: Should describe the address & size of the CPC register region.
diff --git a/Documentation/devicetree/bindings/power/mti,mips-cpc.yaml b/Documentation/devicetree/bindings/power/mti,mips-cpc.yaml
new file mode 100644
index 000000000000..9cd92a57130c
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/mti,mips-cpc.yaml
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/mti,mips-cpc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MIPS Cluster Power Controller
+
+description: |
+  Defines a location of the MIPS Cluster Power Controller registers.
+
+maintainers:
+  - Paul Burton <paulburton@kernel.org>
+
+properties:
+  compatible:
+    const: mti,mips-cpc
+
+  reg:
+    description: |
+      Base address and size of an unoccupied memory region, which will be
+      used to map the MIPS CPC registers block.
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+examples:
+  - |
+    cpc@1bde0000 {
+      compatible = "mti,mips-cpc";
+      reg = <0 0x1bde0000 0 0x8000>;
+    };
+...
-- 
2.26.2


^ permalink raw reply related

* [PATCH v3 1/2] dt-bindings: power: reset: Unrequire regmap property in syscon-reboot node
From: Serge Semin @ 2020-05-26 13:51 UTC (permalink / raw)
  To: Sebastian Reichel, Rob Herring
  Cc: Serge Semin, Serge Semin, Rob Herring, Alexey Malahov,
	Thomas Bogendoerfer, Arnd Bergmann, linux-mips, linux-pm,
	devicetree, linux-kernel
In-Reply-To: <20200526135102.21236-1-Sergey.Semin@baikalelectronics.ru>

Since normally syscon-reboot block is supposed to be a part of a system
controller, lets mark the regmap property as deprecated and recommend the
syscon-reboot node to be a sub-node of SYSCON.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: linux-mips@vger.kernel.org

---

Changelog v2:
- This is a new patch created as a result of the discussion:
  https://lore.kernel.org/linux-pm/20200306130402.1F4F0803079F@mail.baikalelectronics.ru/
---
 .../bindings/power/reset/syscon-reboot.yaml       | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml b/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml
index b80772cb9f06..da2509724812 100644
--- a/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml
+++ b/Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml
@@ -12,9 +12,12 @@ maintainers:
 description: |+
   This is a generic reset driver using syscon to map the reset register.
   The reset is generally performed with a write to the reset register
-  defined by the register map pointed by syscon reference plus the offset
-  with the value and mask defined in the reboot node.
-  Default will be little endian mode, 32 bit access only.
+  defined by the SYSCON register map base plus the offset with the value and
+  mask defined in the reboot node. Default will be little endian mode, 32 bit
+  access only. The SYSCON registers map is normally retrieved from the
+  parental dt-node. So the SYSCON reboot node should be represented as a
+  sub-node of a "syscon", "simple-mfd" node. Though the regmap property
+  pointing to the system controller node is also supported.
 
 properties:
   compatible:
@@ -30,7 +33,10 @@ properties:
 
   regmap:
     $ref: /schemas/types.yaml#/definitions/phandle
-    description: Phandle to the register map node.
+    deprecated: true
+    description: |
+      Phandle to the register map node. This property is deprecated in favor of
+      the syscon-reboot node been a child of a system controller node.
 
   value:
     $ref: /schemas/types.yaml#/definitions/uint32
@@ -38,7 +44,6 @@ properties:
 
 required:
   - compatible
-  - regmap
   - offset
 
 additionalProperties: false
-- 
2.26.2


^ permalink raw reply related

* [PATCH RESEND v3 0/2] syscon: Alter syscon and reboot drivers
From: Serge Semin @ 2020-05-26 13:50 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Maxim Kaurkin,
	Pavel Parkhomenko, Ramil Zaripov, Ekaterina Skachko, Vadim Vlasov,
	Alexey Kolotnikov, Thomas Bogendoerfer, Arnd Bergmann,
	Rob Herring, linux-mips, devicetree, linux-pm, linux-kernel

This is a small patchset about tuning the syscon infrastructure a bit.
As it's going to be general in the framework of the Baikal-T1 SoC support
integration into the kernel, we suggest to replace the legacy text-based
syscon-reboot-mode dts-bindings file with yaml-based one. Then seeing a
syscon reboot block is normally expected to be a part of a system
controller and based on the discussion
https://lore.kernel.org/linux-pm/20200306130402.1F4F0803079F@mail.baikalelectronics.ru/
we decided to alter the syscon reboot driver so one would also try to fetch
the syscon registers map from a parental DT node. regmap property is left
supported although it's marked as deprecated from now.

This patchset is rebased and tested on the mainline Linux kernel 5.7-rc4:
0e698dfa2822 ("Linux 5.7-rc4")
tag: v5.7-rc4

Changelog v2:
- Add Sebastian' Acked-by tag to patch 1.
- Use a shorter summary describing the bindings modification patches.
- Our corporate email server doesn't change Message-Id anymore, so the patchset
  is resubmitted being in the cover-letter-threaded format.
- Discard patch with syscon "-endian" property support. As Rob said It shall be
  in the common dt-schema.
- Replace patches of adding a regmap property support to the syscon-reboot-mode
  with patches making syscon-reboot a sub-node of a system controller node.
- Mark regmap property as deprecated from now.

Link: https://lore.kernel.org/linux-pm/20200507233846.11548-1-Sergey.Semin@baikalelectronics.ru/
Changelog v3:
- Discard the commit 6acd3ecd88ff ("dt-bindings: power: reset: Convert
  syscon-reboot-mode to DT schema") since it has been merged in by Sebatian.
- Add Rob's Reviewed-by tag to the patch "dt-bindings: power: reset: Unrequire
  regmap property in syscon-reboot node"

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Maxim Kaurkin <Maxim.Kaurkin@baikalelectronics.ru>
Cc: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
Cc: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Cc: Ekaterina Skachko <Ekaterina.Skachko@baikalelectronics.ru>
Cc: Vadim Vlasov <V.Vlasov@baikalelectronics.ru>
Cc: Alexey Kolotnikov <Alexey.Kolotnikov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-pm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

Serge Semin (2):
  dt-bindings: power: reset: Unrequire regmap property in syscon-reboot
    node
  power: reset: syscon-reboot: Add parental syscon support

 .../bindings/power/reset/syscon-reboot.yaml       | 15 ++++++++++-----
 drivers/power/reset/syscon-reboot.c               |  7 +++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

-- 
2.26.2


^ permalink raw reply

* [PATCH v3 2/2] power: reset: syscon-reboot: Add parental syscon support
From: Serge Semin @ 2020-05-26 13:51 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Thomas Bogendoerfer,
	Arnd Bergmann, Rob Herring, linux-mips, devicetree, linux-pm,
	linux-kernel
In-Reply-To: <20200526135102.21236-1-Sergey.Semin@baikalelectronics.ru>

Since normally syscon-reboot block is supposed to be a part of a system
controller, lets look for the syscon regmap in a parental DT node if
regmap property isn't specified. DT binding from now considers the regmap
property as deprecated.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: devicetree@vger.kernel.org

---

Changelog v2:
- This is a new patch created as a result of the discussion:
  https://lore.kernel.org/linux-pm/20200306130402.1F4F0803079F@mail.baikalelectronics.ru/

Changelog v3:
- Resend
---
 drivers/power/reset/syscon-reboot.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c
index 62fbba0df971..510e363381ca 100644
--- a/drivers/power/reset/syscon-reboot.c
+++ b/drivers/power/reset/syscon-reboot.c
@@ -51,8 +51,11 @@ static int syscon_reboot_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap");
-	if (IS_ERR(ctx->map))
-		return PTR_ERR(ctx->map);
+	if (IS_ERR(ctx->map)) {
+		ctx->map = syscon_node_to_regmap(dev->parent->of_node);
+		if (IS_ERR(ctx->map))
+			return PTR_ERR(ctx->map);
+	}
 
 	if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset))
 		return -EINVAL;
-- 
2.26.2


^ permalink raw reply related

* Re: [PATCH 1/2] hwmon: pwm-fan: Add profile support and add remove module support
From: Guenter Roeck @ 2020-05-26 13:42 UTC (permalink / raw)
  To: Sandipan Patra
  Cc: Thierry Reding, Jonathan Hunter, u.kleine-koenig@pengutronix.de,
	kamil@wypas.org, jdelvare@suse.com, robh+dt@kernel.org,
	Bibek Basu, Bitan Biswas, linux-pwm@vger.kernel.org,
	linux-hwmon@vger.kernel.org, devicetree@vger.kernel.org,
	linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <BYAPR12MB30145EC4578F64EAD1233357ADB00@BYAPR12MB3014.namprd12.prod.outlook.com>

On Tue, May 26, 2020 at 12:08:14PM +0000, Sandipan Patra wrote:
> Hi,
> 
> 
> > -----Original Message-----
> > From: Guenter Roeck <groeck7@gmail.com> On Behalf Of Guenter Roeck
> > Sent: Tuesday, May 26, 2020 5:12 PM
> > To: Sandipan Patra <spatra@nvidia.com>; Thierry Reding
> > <treding@nvidia.com>; Jonathan Hunter <jonathanh@nvidia.com>; u.kleine-
> > koenig@pengutronix.de; kamil@wypas.org; jdelvare@suse.com;
> > robh+dt@kernel.org
> > Cc: Bibek Basu <bbasu@nvidia.com>; Bitan Biswas <bbiswas@nvidia.com>;
> > linux-pwm@vger.kernel.org; linux-hwmon@vger.kernel.org;
> > devicetree@vger.kernel.org; linux-tegra@vger.kernel.org; linux-
> > kernel@vger.kernel.org
> > Subject: Re: [PATCH 1/2] hwmon: pwm-fan: Add profile support and add remove
> > module support
> > 
> > External email: Use caution opening links or attachments
> > 
> > 
> > On 5/25/20 10:06 PM, Sandipan Patra wrote:
> > > This change has 2 parts:
> > > 1. Add support for profiles mode settings.
> > >     This allows different fan settings for trip point temp/hyst/pwm.
> > >     T194 has multiple fan-profiles support.
> > >
> > > 2. Add pwm-fan remove support. This is essential since the config is
> > >     tristate capable.
> > >
> > > Signed-off-by: Sandipan Patra <spatra@nvidia.com>
> > > ---
> > >  drivers/hwmon/pwm-fan.c | 112
> > > ++++++++++++++++++++++++++++++++++++++++++------
> > >  1 file changed, 100 insertions(+), 12 deletions(-)
> > >
> > > diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index
> > > 30b7b3e..26db589 100644
> > > --- a/drivers/hwmon/pwm-fan.c
> > > +++ b/drivers/hwmon/pwm-fan.c
> > 
> > [ ... ]
> > 
> > >
> > > +static int pwm_fan_remove(struct platform_device *pdev) {
> > > +     struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
> > > +     struct pwm_args args;
> > > +
> > > +     if (!ctx)
> > > +             return -EINVAL;
> > > +
> > > +     if (IS_ENABLED(CONFIG_THERMAL))
> > > +             thermal_cooling_device_unregister(ctx->cdev);
> > > +
> > > +     pwm_get_args(ctx->pwm, &args);
> > > +     pwm_config(ctx->pwm, 0, args.period);
> > > +     pwm_disable(ctx->pwm);
> > > +
> > > +     return 0;
> > > +}
> > > +
> > 
> > I don't think you actually tested this. I would suggest to make yourself familiar
> > with 'devm' functions and their use, and then resubmit.
> > 
> 
> Thanks Guenter.
> I missed to mention about devm while unregistering the cooling device.
> That would definitely cause a mistake in code. I am noting it for further patch.
> 
For that part, I am extremely surprised that it is not handled by the
thermal subsystem. Does each thermal driver need this kind of code ?

> For a better clarity, I will push next version of this patch to handle only multiple profiles support.
> "remove fan module" will be supported by a separate patch altogether.
> 

I asked you to look into "devm" functionality. I ask you again.
If you still think that a remove function is needed, I will require
detailed reasoning. Please be prepared to explain why devm functions
do not work for this driver.

Guenter

^ permalink raw reply

* [PATCH v3 3/3] hwmon: Add Baikal-T1 PVT sensor driver
From: Serge Semin @ 2020-05-26 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck, Jonathan Corbet
  Cc: Serge Semin, Serge Semin, Maxim Kaurkin, Alexey Malahov,
	Thomas Bogendoerfer, Arnd Bergmann, Rob Herring, linux-mips,
	devicetree, linux-hwmon, linux-doc, linux-kernel
In-Reply-To: <20200526133823.20466-1-Sergey.Semin@baikalelectronics.ru>

Baikal-T1 SoC provides an embedded process, voltage and temperature
sensor to monitor an internal SoC environment (chip temperature, supply
voltage and process monitor) and on time detect critical situations,
which may cause the system instability and even damages. The IP-block
is based on the Analog Bits PVT sensor, but is equipped with a
dedicated control wrapper, which provides a MMIO registers-based access
to the sensor core functionality (APB3-bus based) and exposes an
additional functions like thresholds/data ready interrupts, its status
and masks, measurements timeout. All of these is used to create a hwmon
driver being added to the kernel by this commit.

The driver implements support for the hardware monitoring capabilities
of Baikal-T1 process, voltage and temperature sensors. PVT IP-core
consists of one temperature and four voltage sensors, each of which is
implemented as a dedicated hwmon channel config.

The driver can optionally provide the hwmon alarms for each sensor the
PVT controller supports. The alarms functionality is made compile-time
configurable due to the hardware interface implementation peculiarity,
which is connected with an ability to convert data from only one sensor
at a time. Additional limitation is that the controller performs the
thresholds checking synchronously with the data conversion procedure.
Due to these limitations in order to have the hwmon alarms
automatically detected the driver code must switch from one sensor to
another, read converted data and manually check the threshold status
bits. Depending on the measurements timeout settings this design may
cause additional burden on the system performance. By default if the
alarms kernel config is disabled the data conversion is performed by
the driver on demand when read operation is requested via corresponding
_input-file.

Co-developed-by: Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
Signed-off-by: Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: devicetree@vger.kernel.org

---

Changelog v2:
- Discard handwritten IO-access wrappers. Use normal readl/writel instead.
- Use generic FIELD_{GET,PREP} macros instead of handwritten ones.
- Since the driver depends on the OF config we can remove of_match_ptr()
  macro utilization.
- Don't print error-message if no platform IRQ found. Just return an error.
- Remove probe-status info string printout.

Changelog v3:
- Add bt1-pvt into the Documentation/hwmon/index.rst file.
- Discard explicit "default n" from the SENSORS_BT1_PVT_ALARMS config.
- Use "depends on SENSORS_BT1_PVT" statement instead of if-endif kbuild
  config clause.
- Alphabetically order the include macro operators.
- Discard unneeded include macro in the header file.
- Use new generic interface of the hwmon alarms notifications introduced
  in the first patch (based on hwmon_notify_event()).
- Add more descriptive information regarding the temp1_trim attribute.
- Discard setting the platforms device private data by using
  platform_set_drvdata(). It's redundant since unused in the driver.
- Pass "pvt" hwmon name instead of dev_name(dev) to
  devm_hwmon_device_register_with_info().
- Add "baikal,pvt-temp-trim-millicelsius" temperature trim DT property
  support.
- Discard kernel log warnings printed from the ISR when either min or
  max threshold levels are crossed.
- Discard CONFIG_OF dependency since there is non at compile-time.
---
 Documentation/hwmon/bt1-pvt.rst |  116 ++++
 Documentation/hwmon/index.rst   |    1 +
 drivers/hwmon/Kconfig           |   25 +
 drivers/hwmon/Makefile          |    1 +
 drivers/hwmon/bt1-pvt.c         | 1155 +++++++++++++++++++++++++++++++
 drivers/hwmon/bt1-pvt.h         |  244 +++++++
 6 files changed, 1542 insertions(+)
 create mode 100644 Documentation/hwmon/bt1-pvt.rst
 create mode 100644 drivers/hwmon/bt1-pvt.c
 create mode 100644 drivers/hwmon/bt1-pvt.h

diff --git a/Documentation/hwmon/bt1-pvt.rst b/Documentation/hwmon/bt1-pvt.rst
new file mode 100644
index 000000000000..f5f47891d87a
--- /dev/null
+++ b/Documentation/hwmon/bt1-pvt.rst
@@ -0,0 +1,116 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Kernel driver bt1-pvt
+=====================
+
+Supported chips:
+
+  * Baikal-T1 PVT sensor (in SoC)
+
+    Prefix: 'bt1-pvt'
+
+    Addresses scanned: -
+
+    Datasheet: Provided by BAIKAL ELECTRONICS upon request and under NDA
+
+Authors:
+    Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
+    Serge Semin <Sergey.Semin@baikalelectronics.ru>
+
+Description
+-----------
+
+This driver implements support for the hardware monitoring capabilities of the
+embedded into Baikal-T1 process, voltage and temperature sensors. PVT IP-core
+consists of one temperature and four voltage sensors, which can be used to
+monitor the chip internal environment like heating, supply voltage and
+transistors performance. The driver can optionally provide the hwmon alarms
+for each sensor the PVT controller supports. The alarms functionality is made
+compile-time configurable due to the hardware interface implementation
+peculiarity, which is connected with an ability to convert data from only one
+sensor at a time. Additional limitation is that the controller performs the
+thresholds checking synchronously with the data conversion procedure. Due to
+these in order to have the hwmon alarms automatically detected the driver code
+must switch from one sensor to another, read converted data and manually check
+the threshold status bits. Depending on the measurements timeout settings
+(update_interval sysfs node value) this design may cause additional burden on
+the system performance. So in case if alarms are unnecessary in your system
+design it's recommended to have them disabled to prevent the PVT IRQs being
+periodically raised to get the data cache/alarms status up to date. By default
+in alarm-less configuration the data conversion is performed by the driver
+on demand when read operation is requested via corresponding _input-file.
+
+Temperature Monitoring
+----------------------
+
+Temperature is measured with 10-bit resolution and reported in millidegree
+Celsius. The driver performs all the scaling by itself therefore reports true
+temperatures that don't need any user-space adjustments. While the data
+translation formulae isn't linear, which gives us non-linear discreteness,
+it's close to one, but giving a bit better accuracy for higher temperatures.
+The temperature input is mapped as follows (the last column indicates the input
+ranges)::
+
+	temp1: CPU embedded diode	-48.38C - +147.438C
+
+In case if the alarms kernel config is enabled in the driver the temperature input
+has associated min and max limits which trigger an alarm when crossed.
+
+Voltage Monitoring
+------------------
+
+The voltage inputs are also sampled with 10-bit resolution and reported in
+millivolts. But in this case the data translation formulae is linear, which
+provides a constant measurements discreteness. The data scaling is also
+performed by the driver, so returning true millivolts. The voltage inputs are
+mapped as follows (the last column indicates the input ranges)::
+
+	in0: VDD		(processor core)		0.62V - 1.168V
+	in1: Low-Vt		(low voltage threshold)		0.62V - 1.168V
+	in2: High-Vt		(high voltage threshold)	0.62V - 1.168V
+	in3: Standard-Vt	(standard voltage threshold)	0.62V - 1.168V
+
+In case if the alarms configis enabled in the driver the voltage inputs
+have associated min and max limits which trigger an alarm when crossed.
+
+Sysfs Attributes
+----------------
+
+Following is a list of all sysfs attributes that the driver provides, their
+permissions and a short description:
+
+=============================== ======= =======================================
+Name				Perm	Description
+=============================== ======= =======================================
+update_interval			RW	Measurements update interval per
+					sensor.
+temp1_type			RO	Sensor type (always 1 as CPU embedded
+					diode).
+temp1_label			RO	CPU Core Temperature sensor.
+temp1_input			RO	Measured temperature in millidegree
+					Celsius.
+temp1_min			RW	Low limit for temp input.
+temp1_max			RW	High limit for temp input.
+temp1_min_alarm			RO	Temperature input alarm. Returns 1 if
+					temperature input went below min limit,
+					0 otherwise.
+temp1_max_alarm			RO	Temperature input alarm. Returns 1 if
+					temperature input went above max limit,
+					0 otherwise.
+temp1_trim			RW	Temperature sensor trimming factor in
+					millidegree Celsius. It can be used to
+					manually adjust the temperature
+					measurements within 7.130 degrees
+					Celsius.
+in[0-3]_label			RO	CPU Voltage sensor (either core or
+					low/high/standard thresholds).
+in[0-3]_input			RO	Measured voltage in millivolts.
+in[0-3]_min			RW	Low limit for voltage input.
+in[0-3]_max			RW	High limit for voltage input.
+in[0-3]_min_alarm		RO	Voltage input alarm. Returns 1 if
+					voltage input went below min limit,
+					0 otherwise.
+in[0-3]_max_alarm		RO	Voltage input alarm. Returns 1 if
+					voltage input went above max limit,
+					0 otherwise.
+=============================== ======= =======================================
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 8ef62fd39787..3f322d2f1d20 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -43,6 +43,7 @@ Hardware Monitoring Kernel Drivers
    asc7621
    aspeed-pwm-tacho
    bel-pfe
+   bt1-pvt
    coretemp
    da9052
    da9055
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 4c62f900bf7e..d1c6c5d46c08 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -404,6 +404,31 @@ config SENSORS_ATXP1
 	  This driver can also be built as a module. If so, the module
 	  will be called atxp1.
 
+config SENSORS_BT1_PVT
+	tristate "Baikal-T1 Process, Voltage, Temperature sensor driver"
+	depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+	help
+	  If you say yes here you get support for Baikal-T1 PVT sensor
+	  embedded into the SoC.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called bt1-pvt.
+
+config SENSORS_BT1_PVT_ALARMS
+	bool "Enable Baikal-T1 PVT sensor alarms"
+	depends on SENSORS_BT1_PVT
+	help
+	  Baikal-T1 PVT IP-block provides threshold registers for each
+	  supported sensor. But the corresponding interrupts might be
+	  generated by the thresholds comparator only in synchronization with
+	  a data conversion. Additionally there is only one sensor data can
+	  be converted at a time. All of these makes the interface impossible
+	  to be used for the hwmon alarms implementation without periodic
+	  switch between the PVT sensors. By default the data conversion is
+	  performed on demand from the user-space. If this config is enabled
+	  the data conversion will be periodically performed and the data will be
+	  saved in the internal driver cache.
+
 config SENSORS_DRIVETEMP
 	tristate "Hard disk drives with temperature sensors"
 	depends on SCSI && ATA
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b0b9c8e57176..408b86a5e71d 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_SENSORS_ASC7621)	+= asc7621.o
 obj-$(CONFIG_SENSORS_ASPEED)	+= aspeed-pwm-tacho.o
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o
+obj-$(CONFIG_SENSORS_BT1_PVT)	+= bt1-pvt.o
 obj-$(CONFIG_SENSORS_CORETEMP)	+= coretemp.o
 obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
 obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o
diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c
new file mode 100644
index 000000000000..a85cb4785bf6
--- /dev/null
+++ b/drivers/hwmon/bt1-pvt.c
@@ -0,0 +1,1155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ *   Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
+ *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 Process, Voltage, Temperature sensor driver
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/seqlock.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#include "bt1-pvt.h"
+
+/*
+ * For the sake of the code simplification we created the sensors info table
+ * with the sensor names, activation modes, threshold registers base address
+ * and the thresholds bit fields.
+ */
+static const struct pvt_sensor_info pvt_info[] = {
+	PVT_SENSOR_INFO(0, "CPU Core Temperature", hwmon_temp, TEMP, TTHRES),
+	PVT_SENSOR_INFO(0, "CPU Core Voltage", hwmon_in, VOLT, VTHRES),
+	PVT_SENSOR_INFO(1, "CPU Core Low-Vt", hwmon_in, LVT, LTHRES),
+	PVT_SENSOR_INFO(2, "CPU Core High-Vt", hwmon_in, HVT, HTHRES),
+	PVT_SENSOR_INFO(3, "CPU Core Standard-Vt", hwmon_in, SVT, STHRES),
+};
+
+/*
+ * The original translation formulae of the temperature (in degrees of Celsius)
+ * to PVT data and vice-versa are following:
+ * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) +
+ *     1.7204e2,
+ * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) +
+ *     3.1020e-1*(N^1) - 4.838e1,
+ * where T = [-48.380, 147.438]C and N = [0, 1023].
+ * They must be accordingly altered to be suitable for the integer arithmetics.
+ * The technique is called 'factor redistribution', which just makes sure the
+ * multiplications and divisions are made so to have a result of the operations
+ * within the integer numbers limit. In addition we need to translate the
+ * formulae to accept millidegrees of Celsius. Here what they look like after
+ * the alterations:
+ * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T +
+ *     17204e2) / 1e4,
+ * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D -
+ *     48380,
+ * where T = [-48380, 147438] mC and N = [0, 1023].
+ */
+const static struct pvt_poly poly_temp_to_N = {
+	.total_divider = 10000,
+	.terms = {
+		{4, 18322, 10000, 10000},
+		{3, 2343, 10000, 10},
+		{2, 87018, 10000, 10},
+		{1, 39269, 1000, 1},
+		{0, 1720400, 1, 1}
+	}
+};
+
+const static struct pvt_poly poly_N_to_temp = {
+	.total_divider = 1,
+	.terms = {
+		{4, -16743, 1000, 1},
+		{3, 81542, 1000, 1},
+		{2, -182010, 1000, 1},
+		{1, 310200, 1000, 1},
+		{0, -48380, 1, 1}
+	}
+};
+
+/*
+ * Similar alterations are performed for the voltage conversion equations.
+ * The original formulae are:
+ * N = 1.8658e3*V - 1.1572e3,
+ * V = (N + 1.1572e3) / 1.8658e3,
+ * where V = [0.620, 1.168] V and N = [0, 1023].
+ * After the optimization they looks as follows:
+ * N = (18658e-3*V - 11572) / 10,
+ * V = N * 10^5 / 18658 + 11572 * 10^4 / 18658.
+ */
+const static struct pvt_poly poly_volt_to_N = {
+	.total_divider = 10,
+	.terms = {
+		{1, 18658, 1000, 1},
+		{0, -11572, 1, 1}
+	}
+};
+
+const static struct pvt_poly poly_N_to_volt = {
+	.total_divider = 10,
+	.terms = {
+		{1, 100000, 18658, 1},
+		{0, 115720000, 1, 18658}
+	}
+};
+
+/*
+ * Here is the polynomial calculation function, which performs the
+ * redistributed terms calculations. It's pretty straightforward. We walk
+ * over each degree term up to the free one, and perform the redistributed
+ * multiplication of the term coefficient, its divider (as for the rationale
+ * fraction representation), data power and the rational fraction divider
+ * leftover. Then all of this is collected in a total sum variable, which
+ * value is normalized by the total divider before being returned.
+ */
+static long pvt_calc_poly(const struct pvt_poly *poly, long data)
+{
+	const struct pvt_poly_term *term = poly->terms;
+	long tmp, ret = 0;
+	int deg;
+
+	do {
+		tmp = term->coef;
+		for (deg = 0; deg < term->deg; ++deg)
+			tmp = mult_frac(tmp, data, term->divider);
+		ret += tmp / term->divider_leftover;
+	} while ((term++)->deg);
+
+	return ret / poly->total_divider;
+}
+
+static inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data)
+{
+	u32 old;
+
+	old = readl_relaxed(reg);
+	writel((old & ~mask) | (data & mask), reg);
+
+	return old & mask;
+}
+
+/*
+ * Baikal-T1 PVT mode can be updated only when the controller is disabled.
+ * So first we disable it, then set the new mode together with the controller
+ * getting back enabled. The same concerns the temperature trim and
+ * measurements timeout. If it is necessary the interface mutex is supposed
+ * to be locked at the time the operations are performed.
+ */
+static inline void pvt_set_mode(struct pvt_hwmon *pvt, u32 mode)
+{
+	u32 old;
+
+	mode = FIELD_PREP(PVT_CTRL_MODE_MASK, mode);
+
+	old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN,
+		   mode | old);
+}
+
+static inline u32 pvt_calc_trim(unsigned int temp)
+{
+	temp = clamp_val(temp, 0, PVT_TRIM_TEMP);
+
+	return DIV_ROUND_UP(temp, PVT_TRIM_STEP);
+}
+
+static inline void pvt_set_trim(struct pvt_hwmon *pvt, u32 trim)
+{
+	u32 old;
+
+	trim = FIELD_PREP(PVT_CTRL_TRIM_MASK, trim);
+
+	old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN,
+		   trim | old);
+}
+
+static inline void pvt_set_tout(struct pvt_hwmon *pvt, u32 tout)
+{
+	u32 old;
+
+	old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	writel(tout, pvt->regs + PVT_TTIMEOUT);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, old);
+}
+
+/*
+ * This driver can optionally provide the hwmon alarms for each sensor the PVT
+ * controller supports. The alarms functionality is made compile-time
+ * configurable due to the hardware interface implementation peculiarity
+ * described further in this comment. So in case if alarms are unnecessary in
+ * your system design it's recommended to have them disabled to prevent the PVT
+ * IRQs being periodically raised to get the data cache/alarms status up to
+ * date.
+ *
+ * Baikal-T1 PVT embedded controller is based on the Analog Bits PVT sensor,
+ * but is equipped with a dedicated control wrapper. It exposes the PVT
+ * sub-block registers space via the APB3 bus. In addition the wrapper provides
+ * a common interrupt vector of the sensors conversion completion events and
+ * threshold value alarms. Alas the wrapper interface hasn't been fully thought
+ * through. There is only one sensor can be activated at a time, for which the
+ * thresholds comparator is enabled right after the data conversion is
+ * completed. Due to this if alarms need to be implemented for all available
+ * sensors we can't just set the thresholds and enable the interrupts. We need
+ * to enable the sensors one after another and let the controller to detect
+ * the alarms by itself at each conversion. This also makes pointless to handle
+ * the alarms interrupts, since in occasion they happen synchronously with
+ * data conversion completion. The best driver design would be to have the
+ * completion interrupts enabled only and keep the converted value in the
+ * driver data cache. This solution is implemented if hwmon alarms are enabled
+ * in this driver. In case if the alarms are disabled, the conversion is
+ * performed on demand at the time a sensors input file is read.
+ */
+
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+
+#define pvt_hard_isr NULL
+
+static irqreturn_t pvt_soft_isr(int irq, void *data)
+{
+	const struct pvt_sensor_info *info;
+	struct pvt_hwmon *pvt = data;
+	struct pvt_cache *cache;
+	u32 val, thres_sts, old;
+
+	/*
+	 * DVALID bit will be cleared by reading the data. We need to save the
+	 * status before the next conversion happens. Threshold events will be
+	 * handled a bit later.
+	 */
+	thres_sts = readl(pvt->regs + PVT_RAW_INTR_STAT);
+
+	/*
+	 * Then lets recharge the PVT interface with the next sampling mode.
+	 * Lock the interface mutex to serialize trim, timeouts and alarm
+	 * thresholds settings.
+	 */
+	cache = &pvt->cache[pvt->sensor];
+	info = &pvt_info[pvt->sensor];
+	pvt->sensor = (pvt->sensor == PVT_SENSOR_LAST) ?
+		      PVT_SENSOR_FIRST : (pvt->sensor + 1);
+
+	/*
+	 * For some reason we have to mask the interrupt before changing the
+	 * mode, otherwise sometimes the temperature mode doesn't get
+	 * activated even though the actual mode in the ctrl register
+	 * corresponds to one. Then we read the data. By doing so we also
+	 * recharge the data conversion. After this the mode corresponding
+	 * to the next sensor in the row is set. Finally we enable the
+	 * interrupts back.
+	 */
+	mutex_lock(&pvt->iface_mtx);
+
+	old = pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+			 PVT_INTR_DVALID);
+
+	val = readl(pvt->regs + PVT_DATA);
+
+	pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
+
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, old);
+
+	mutex_unlock(&pvt->iface_mtx);
+
+	/*
+	 * We can now update the data cache with data just retrieved from the
+	 * sensor. Lock write-seqlock to make sure the reader has a coherent
+	 * data.
+	 */
+	write_seqlock(&cache->data_seqlock);
+
+	cache->data = FIELD_GET(PVT_DATA_DATA_MASK, val);
+
+	write_sequnlock(&cache->data_seqlock);
+
+	/*
+	 * While PVT core is doing the next mode data conversion, we'll check
+	 * whether the alarms were triggered for the current sensor. Note that
+	 * according to the documentation only one threshold IRQ status can be
+	 * set at a time, that's why if-else statement is utilized.
+	 */
+	if ((thres_sts & info->thres_sts_lo) ^ cache->thres_sts_lo) {
+		WRITE_ONCE(cache->thres_sts_lo, thres_sts & info->thres_sts_lo);
+		hwmon_notify_event(pvt->hwmon, info->type, info->attr_min_alarm,
+				   info->channel);
+	} else if ((thres_sts & info->thres_sts_hi) ^ cache->thres_sts_hi) {
+		WRITE_ONCE(cache->thres_sts_hi, thres_sts & info->thres_sts_hi);
+		hwmon_notify_event(pvt->hwmon, info->type, info->attr_max_alarm,
+				   info->channel);
+	}
+
+	return IRQ_HANDLED;
+}
+
+inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type)
+{
+	return 0644;
+}
+
+inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type)
+{
+	return 0444;
+}
+
+static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			 long *val)
+{
+	struct pvt_cache *cache = &pvt->cache[type];
+	unsigned int seq;
+	u32 data;
+
+	do {
+		seq = read_seqbegin(&cache->data_seqlock);
+		data = cache->data;
+	} while (read_seqretry(&cache->data_seqlock, seq));
+
+	if (type == PVT_TEMP)
+		*val = pvt_calc_poly(&poly_N_to_temp, data);
+	else
+		*val = pvt_calc_poly(&poly_N_to_volt, data);
+
+	return 0;
+}
+
+static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			  bool is_low, long *val)
+{
+	u32 data;
+
+	/* No need in serialization, since it is just read from MMIO. */
+	data = readl(pvt->regs + pvt_info[type].thres_base);
+
+	if (is_low)
+		data = FIELD_GET(PVT_THRES_LO_MASK, data);
+	else
+		data = FIELD_GET(PVT_THRES_HI_MASK, data);
+
+	if (type == PVT_TEMP)
+		*val = pvt_calc_poly(&poly_N_to_temp, data);
+	else
+		*val = pvt_calc_poly(&poly_N_to_volt, data);
+
+	return 0;
+}
+
+static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			   bool is_low, long val)
+{
+	u32 data, limit, mask;
+	int ret;
+
+	if (type == PVT_TEMP) {
+		val = clamp(val, PVT_TEMP_MIN, PVT_TEMP_MAX);
+		data = pvt_calc_poly(&poly_temp_to_N, val);
+	} else {
+		val = clamp(val, PVT_VOLT_MIN, PVT_VOLT_MAX);
+		data = pvt_calc_poly(&poly_volt_to_N, val);
+	}
+
+	/* Serialize limit update, since a part of the register is changed. */
+	ret = mutex_lock_interruptible(&pvt->iface_mtx);
+	if (ret)
+		return ret;
+
+	/* Make sure the upper and lower ranges don't intersect. */
+	limit = readl(pvt->regs + pvt_info[type].thres_base);
+	if (is_low) {
+		limit = FIELD_GET(PVT_THRES_HI_MASK, limit);
+		data = clamp_val(data, PVT_DATA_MIN, limit);
+		data = FIELD_PREP(PVT_THRES_LO_MASK, data);
+		mask = PVT_THRES_LO_MASK;
+	} else {
+		limit = FIELD_GET(PVT_THRES_LO_MASK, limit);
+		data = clamp_val(data, limit, PVT_DATA_MAX);
+		data = FIELD_PREP(PVT_THRES_HI_MASK, data);
+		mask = PVT_THRES_HI_MASK;
+	}
+
+	pvt_update(pvt->regs + pvt_info[type].thres_base, mask, data);
+
+	mutex_unlock(&pvt->iface_mtx);
+
+	return 0;
+}
+
+static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			  bool is_low, long *val)
+{
+	if (is_low)
+		*val = !!READ_ONCE(pvt->cache[type].thres_sts_lo);
+	else
+		*val = !!READ_ONCE(pvt->cache[type].thres_sts_hi);
+
+	return 0;
+}
+
+static const struct hwmon_channel_info *pvt_channel_info[] = {
+	HWMON_CHANNEL_INFO(chip,
+			   HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
+	HWMON_CHANNEL_INFO(temp,
+			   HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL |
+			   HWMON_T_MIN | HWMON_T_MIN_ALARM |
+			   HWMON_T_MAX | HWMON_T_MAX_ALARM),
+	HWMON_CHANNEL_INFO(in,
+			   HWMON_I_INPUT | HWMON_I_LABEL |
+			   HWMON_I_MIN | HWMON_I_MIN_ALARM |
+			   HWMON_I_MAX | HWMON_I_MAX_ALARM,
+			   HWMON_I_INPUT | HWMON_I_LABEL |
+			   HWMON_I_MIN | HWMON_I_MIN_ALARM |
+			   HWMON_I_MAX | HWMON_I_MAX_ALARM,
+			   HWMON_I_INPUT | HWMON_I_LABEL |
+			   HWMON_I_MIN | HWMON_I_MIN_ALARM |
+			   HWMON_I_MAX | HWMON_I_MAX_ALARM,
+			   HWMON_I_INPUT | HWMON_I_LABEL |
+			   HWMON_I_MIN | HWMON_I_MIN_ALARM |
+			   HWMON_I_MAX | HWMON_I_MAX_ALARM),
+	NULL
+};
+
+#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static irqreturn_t pvt_hard_isr(int irq, void *data)
+{
+	struct pvt_hwmon *pvt = data;
+	struct pvt_cache *cache;
+	u32 val;
+
+	/*
+	 * Mask the DVALID interrupt so after exiting from the handler a
+	 * repeated conversion wouldn't happen.
+	 */
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+		   PVT_INTR_DVALID);
+
+	/*
+	 * Nothing special for alarm-less driver. Just read the data, update
+	 * the cache and notify a waiter of this event.
+	 */
+	val = readl(pvt->regs + PVT_DATA);
+	if (!(val & PVT_DATA_VALID)) {
+		dev_err(pvt->dev, "Got IRQ when data isn't valid\n");
+		return IRQ_HANDLED;
+	}
+
+	cache = &pvt->cache[pvt->sensor];
+
+	WRITE_ONCE(cache->data, FIELD_GET(PVT_DATA_DATA_MASK, val));
+
+	complete(&cache->conversion);
+
+	return IRQ_HANDLED;
+}
+
+#define pvt_soft_isr NULL
+
+inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type)
+{
+	return 0;
+}
+
+inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type)
+{
+	return 0;
+}
+
+static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			 long *val)
+{
+	struct pvt_cache *cache = &pvt->cache[type];
+	u32 data;
+	int ret;
+
+	/*
+	 * Lock PVT conversion interface until data cache is updated. The
+	 * data read procedure is following: set the requested PVT sensor
+	 * mode, enable IRQ and conversion, wait until conversion is finished,
+	 * then disable conversion and IRQ, and read the cached data.
+	 */
+	ret = mutex_lock_interruptible(&pvt->iface_mtx);
+	if (ret)
+		return ret;
+
+	pvt->sensor = type;
+	pvt_set_mode(pvt, pvt_info[type].mode);
+
+	/*
+	 * Unmask the DVALID interrupt and enable the sensors conversions.
+	 * Do the reverse procedure when conversion is done.
+	 */
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
+
+	wait_for_completion(&cache->conversion);
+
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+		   PVT_INTR_DVALID);
+
+	data = READ_ONCE(cache->data);
+
+	mutex_unlock(&pvt->iface_mtx);
+
+	if (type == PVT_TEMP)
+		*val = pvt_calc_poly(&poly_N_to_temp, data);
+	else
+		*val = pvt_calc_poly(&poly_N_to_volt, data);
+
+	return 0;
+}
+
+static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			  bool is_low, long *val)
+{
+	return -EOPNOTSUPP;
+}
+
+static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			   bool is_low, long val)
+{
+	return -EOPNOTSUPP;
+}
+
+static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+			  bool is_low, long *val)
+{
+	return -EOPNOTSUPP;
+}
+
+static const struct hwmon_channel_info *pvt_channel_info[] = {
+	HWMON_CHANNEL_INFO(chip,
+			   HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
+	HWMON_CHANNEL_INFO(temp,
+			   HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL),
+	HWMON_CHANNEL_INFO(in,
+			   HWMON_I_INPUT | HWMON_I_LABEL,
+			   HWMON_I_INPUT | HWMON_I_LABEL,
+			   HWMON_I_INPUT | HWMON_I_LABEL,
+			   HWMON_I_INPUT | HWMON_I_LABEL),
+	NULL
+};
+
+#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static inline bool pvt_hwmon_channel_is_valid(enum hwmon_sensor_types type,
+					      int ch)
+{
+	switch (type) {
+	case hwmon_temp:
+		if (ch < 0 || ch >= PVT_TEMP_CHS)
+			return false;
+		break;
+	case hwmon_in:
+		if (ch < 0 || ch >= PVT_VOLT_CHS)
+			return false;
+		break;
+	default:
+		break;
+	}
+
+	/* The rest of the types are independent from the channel number. */
+	return true;
+}
+
+static umode_t pvt_hwmon_is_visible(const void *data,
+				    enum hwmon_sensor_types type,
+				    u32 attr, int ch)
+{
+	if (!pvt_hwmon_channel_is_valid(type, ch))
+		return 0;
+
+	switch (type) {
+	case hwmon_chip:
+		switch (attr) {
+		case hwmon_chip_update_interval:
+			return 0644;
+		}
+		break;
+	case hwmon_temp:
+		switch (attr) {
+		case hwmon_temp_input:
+		case hwmon_temp_type:
+		case hwmon_temp_label:
+			return 0444;
+		case hwmon_temp_min:
+		case hwmon_temp_max:
+			return pvt_limit_is_visible(ch);
+		case hwmon_temp_min_alarm:
+		case hwmon_temp_max_alarm:
+			return pvt_alarm_is_visible(ch);
+		}
+		break;
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_input:
+		case hwmon_in_label:
+			return 0444;
+		case hwmon_in_min:
+		case hwmon_in_max:
+			return pvt_limit_is_visible(PVT_VOLT + ch);
+		case hwmon_in_min_alarm:
+		case hwmon_in_max_alarm:
+			return pvt_alarm_is_visible(PVT_VOLT + ch);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int pvt_read_timeout(struct pvt_hwmon *pvt, long *val)
+{
+	unsigned long rate;
+	ktime_t kt;
+	u32 data;
+
+	rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk);
+	if (!rate)
+		return -ENODEV;
+
+	/*
+	 * Don't bother with mutex here, since we just read data from MMIO.
+	 * We also have to scale the ticks timeout up to compensate the
+	 * ms-ns-data translations.
+	 */
+	data = readl(pvt->regs + PVT_TTIMEOUT) + 1;
+
+	/*
+	 * Calculate ref-clock based delay (Ttotal) between two consecutive
+	 * data samples of the same sensor. So we first must calculate the
+	 * delay introduced by the internal ref-clock timer (Tref * Fclk).
+	 * Then add the constant timeout cuased by each conversion latency
+	 * (Tmin). The basic formulae for each conversion is following:
+	 *   Ttotal = Tref * Fclk + Tmin
+	 * Note if alarms are enabled the sensors are polled one after
+	 * another, so in order to have the delay being applicable for each
+	 * sensor the requested value must be equally redistirbuted.
+	 */
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+	kt = ktime_set(PVT_SENSORS_NUM * (u64)data, 0);
+	kt = ktime_divns(kt, rate);
+	kt = ktime_add_ns(kt, PVT_SENSORS_NUM * PVT_TOUT_MIN);
+#else
+	kt = ktime_set(data, 0);
+	kt = ktime_divns(kt, rate);
+	kt = ktime_add_ns(kt, PVT_TOUT_MIN);
+#endif
+
+	/* Return the result in msec as hwmon sysfs interface requires. */
+	*val = ktime_to_ms(kt);
+
+	return 0;
+}
+
+static int pvt_write_timeout(struct pvt_hwmon *pvt, long val)
+{
+	unsigned long rate;
+	ktime_t kt;
+	u32 data;
+	int ret;
+
+	rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk);
+	if (!rate)
+		return -ENODEV;
+
+	/*
+	 * If alarms are enabled, the requested timeout must be divided
+	 * between all available sensors to have the requested delay
+	 * applicable to each individual sensor.
+	 */
+	kt = ms_to_ktime(val);
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+	kt = ktime_divns(kt, PVT_SENSORS_NUM);
+#endif
+
+	/*
+	 * Subtract a constant lag, which always persists due to the limited
+	 * PVT sampling rate. Make sure the timeout is not negative.
+	 */
+	kt = ktime_sub_ns(kt, PVT_TOUT_MIN);
+	if (ktime_to_ns(kt) < 0)
+		kt = ktime_set(0, 0);
+
+	/*
+	 * Finally recalculate the timeout in terms of the reference clock
+	 * period.
+	 */
+	data = ktime_divns(kt * rate, NSEC_PER_SEC);
+
+	/*
+	 * Update the measurements delay, but lock the interface first, since
+	 * we have to disable PVT in order to have the new delay actually
+	 * updated.
+	 */
+	ret = mutex_lock_interruptible(&pvt->iface_mtx);
+	if (ret)
+		return ret;
+
+	pvt_set_tout(pvt, data);
+
+	mutex_unlock(&pvt->iface_mtx);
+
+	return 0;
+}
+
+static int pvt_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+			  u32 attr, int ch, long *val)
+{
+	struct pvt_hwmon *pvt = dev_get_drvdata(dev);
+
+	if (!pvt_hwmon_channel_is_valid(type, ch))
+		return -EINVAL;
+
+	switch (type) {
+	case hwmon_chip:
+		switch (attr) {
+		case hwmon_chip_update_interval:
+			return pvt_read_timeout(pvt, val);
+		}
+		break;
+	case hwmon_temp:
+		switch (attr) {
+		case hwmon_temp_input:
+			return pvt_read_data(pvt, ch, val);
+		case hwmon_temp_type:
+			*val = 1;
+			return 0;
+		case hwmon_temp_min:
+			return pvt_read_limit(pvt, ch, true, val);
+		case hwmon_temp_max:
+			return pvt_read_limit(pvt, ch, false, val);
+		case hwmon_temp_min_alarm:
+			return pvt_read_alarm(pvt, ch, true, val);
+		case hwmon_temp_max_alarm:
+			return pvt_read_alarm(pvt, ch, false, val);
+		}
+		break;
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_input:
+			return pvt_read_data(pvt, PVT_VOLT + ch, val);
+		case hwmon_in_min:
+			return pvt_read_limit(pvt, PVT_VOLT + ch, true, val);
+		case hwmon_in_max:
+			return pvt_read_limit(pvt, PVT_VOLT + ch, false, val);
+		case hwmon_in_min_alarm:
+			return pvt_read_alarm(pvt, PVT_VOLT + ch, true, val);
+		case hwmon_in_max_alarm:
+			return pvt_read_alarm(pvt, PVT_VOLT + ch, false, val);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int pvt_hwmon_read_string(struct device *dev,
+				 enum hwmon_sensor_types type,
+				 u32 attr, int ch, const char **str)
+{
+	if (!pvt_hwmon_channel_is_valid(type, ch))
+		return -EINVAL;
+
+	switch (type) {
+	case hwmon_temp:
+		switch (attr) {
+		case hwmon_temp_label:
+			*str = pvt_info[ch].label;
+			return 0;
+		}
+		break;
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_label:
+			*str = pvt_info[PVT_VOLT + ch].label;
+			return 0;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int pvt_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+		    u32 attr, int ch, long val)
+{
+	struct pvt_hwmon *pvt = dev_get_drvdata(dev);
+
+	if (!pvt_hwmon_channel_is_valid(type, ch))
+		return -EINVAL;
+
+	switch (type) {
+	case hwmon_chip:
+		switch (attr) {
+		case hwmon_chip_update_interval:
+			return pvt_write_timeout(pvt, val);
+		}
+		break;
+	case hwmon_temp:
+		switch (attr) {
+		case hwmon_temp_min:
+			return pvt_write_limit(pvt, ch, true, val);
+		case hwmon_temp_max:
+			return pvt_write_limit(pvt, ch, false, val);
+		}
+		break;
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_min:
+			return pvt_write_limit(pvt, PVT_VOLT + ch, true, val);
+		case hwmon_in_max:
+			return pvt_write_limit(pvt, PVT_VOLT + ch, false, val);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops pvt_hwmon_ops = {
+	.is_visible = pvt_hwmon_is_visible,
+	.read = pvt_hwmon_read,
+	.read_string = pvt_hwmon_read_string,
+	.write = pvt_hwmon_write
+};
+
+static const struct hwmon_chip_info pvt_hwmon_info = {
+	.ops = &pvt_hwmon_ops,
+	.info = pvt_channel_info
+};
+
+static ssize_t pvt_hwmon_temp_trim_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct pvt_hwmon *pvt = dev_get_drvdata(dev);
+	u32 data;
+
+	data = readl(pvt->regs + PVT_CTRL);
+	data = FIELD_GET(PVT_CTRL_TRIM_MASK, data);
+
+	return scnprintf(buf, PAGE_SIZE, "%lu\n", data * PVT_TRIM_STEP);
+}
+
+static ssize_t pvt_hwmon_temp_trim_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct pvt_hwmon *pvt = dev_get_drvdata(dev);
+	unsigned int temp;
+	u32 trim;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &temp);
+	if (ret)
+		return ret;
+
+	/*
+	 * Serialize trim update, since a part of the register is changed and
+	 * the controller is supposed to be disabled during this operation.
+	 */
+	ret = mutex_lock_interruptible(&pvt->iface_mtx);
+	if (ret)
+		return ret;
+
+	trim = pvt_calc_trim(temp);
+	pvt_set_trim(pvt, trim);
+
+	mutex_unlock(&pvt->iface_mtx);
+
+	return count;
+}
+static SENSOR_DEVICE_ATTR_RW(temp1_trim, pvt_hwmon_temp_trim, 0);
+
+static struct attribute *pvt_hwmon_attrs[] = {
+	&sensor_dev_attr_temp1_trim.dev_attr.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(pvt_hwmon);
+
+static void pvt_clear_data(void *data)
+{
+	struct pvt_hwmon *pvt = data;
+#if !defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+	int idx;
+
+	for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
+		complete_all(&pvt->cache[idx].conversion);
+#endif
+
+	mutex_destroy(&pvt->iface_mtx);
+}
+
+static struct pvt_hwmon *pvt_create_data(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct pvt_hwmon *pvt;
+	int ret, idx;
+
+	pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL);
+	if (!pvt)
+		return ERR_PTR(-ENOMEM);
+
+	ret = devm_add_action(dev, pvt_clear_data, pvt);
+	if (ret) {
+		dev_err(dev, "Can't add PVT data clear action\n");
+		return ERR_PTR(ret);
+	}
+
+	pvt->dev = dev;
+	pvt->sensor = PVT_SENSOR_FIRST;
+	mutex_init(&pvt->iface_mtx);
+
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+	for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
+		seqlock_init(&pvt->cache[idx].data_seqlock);
+#else
+	for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
+		init_completion(&pvt->cache[idx].conversion);
+#endif
+
+	return pvt;
+}
+
+static int pvt_request_regs(struct pvt_hwmon *pvt)
+{
+	struct platform_device *pdev = to_platform_device(pvt->dev);
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(pvt->dev, "Couldn't find PVT memresource\n");
+		return -EINVAL;
+	}
+
+	pvt->regs = devm_ioremap_resource(pvt->dev, res);
+	if (IS_ERR(pvt->regs)) {
+		dev_err(pvt->dev, "Couldn't map PVT registers\n");
+		return PTR_ERR(pvt->regs);
+	}
+
+	return 0;
+}
+
+static void pvt_disable_clks(void *data)
+{
+	struct pvt_hwmon *pvt = data;
+
+	clk_bulk_disable_unprepare(PVT_CLOCK_NUM, pvt->clks);
+}
+
+static int pvt_request_clks(struct pvt_hwmon *pvt)
+{
+	int ret;
+
+	pvt->clks[PVT_CLOCK_APB].id = "pclk";
+	pvt->clks[PVT_CLOCK_REF].id = "ref";
+
+	ret = devm_clk_bulk_get(pvt->dev, PVT_CLOCK_NUM, pvt->clks);
+	if (ret) {
+		dev_err(pvt->dev, "Couldn't get PVT clocks descriptors\n");
+		return ret;
+	}
+
+	ret = clk_bulk_prepare_enable(PVT_CLOCK_NUM, pvt->clks);
+	if (ret) {
+		dev_err(pvt->dev, "Couldn't enable the PVT clocks\n");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(pvt->dev, pvt_disable_clks, pvt);
+	if (ret) {
+		dev_err(pvt->dev, "Can't add PVT clocks disable action\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void pvt_init_iface(struct pvt_hwmon *pvt)
+{
+	unsigned int temp;
+	u32 trim;
+
+	/*
+	 * Make sure all interrupts and controller are disabled so not to
+	 * accidentally have ISR executed before the driver data is fully
+	 * initialized. Clear the IRQ status as well.
+	 */
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	readl(pvt->regs + PVT_CLR_INTR);
+	readl(pvt->regs + PVT_DATA);
+
+	/* Setup default sensor mode, timeout and temperature trim. */
+	pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
+	pvt_set_tout(pvt, PVT_TOUT_DEF);
+
+	trim = PVT_TRIM_DEF;
+	if (!of_property_read_u32(pvt->dev->of_node,
+	     "baikal,pvt-temp-trim-millicelsius", &temp))
+		trim = pvt_calc_trim(temp);
+
+	pvt_set_trim(pvt, trim);
+}
+
+static int pvt_request_irq(struct pvt_hwmon *pvt)
+{
+	struct platform_device *pdev = to_platform_device(pvt->dev);
+	int ret;
+
+	pvt->irq = platform_get_irq(pdev, 0);
+	if (pvt->irq < 0)
+		return pvt->irq;
+
+	ret = devm_request_threaded_irq(pvt->dev, pvt->irq,
+					pvt_hard_isr, pvt_soft_isr,
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+					IRQF_SHARED | IRQF_TRIGGER_HIGH |
+					IRQF_ONESHOT,
+#else
+					IRQF_SHARED | IRQF_TRIGGER_HIGH,
+#endif
+					"pvt", pvt);
+	if (ret) {
+		dev_err(pvt->dev, "Couldn't request PVT IRQ\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int pvt_create_hwmon(struct pvt_hwmon *pvt)
+{
+	pvt->hwmon = devm_hwmon_device_register_with_info(pvt->dev, "pvt", pvt,
+		&pvt_hwmon_info, pvt_hwmon_groups);
+	if (IS_ERR(pvt->hwmon)) {
+		dev_err(pvt->dev, "Couldn't create hwmon device\n");
+		return PTR_ERR(pvt->hwmon);
+	}
+
+	return 0;
+}
+
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+
+static void pvt_disable_iface(void *data)
+{
+	struct pvt_hwmon *pvt = data;
+
+	mutex_lock(&pvt->iface_mtx);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+		   PVT_INTR_DVALID);
+	mutex_unlock(&pvt->iface_mtx);
+}
+
+static int pvt_enable_iface(struct pvt_hwmon *pvt)
+{
+	int ret;
+
+	ret = devm_add_action(pvt->dev, pvt_disable_iface, pvt);
+	if (ret) {
+		dev_err(pvt->dev, "Can't add PVT disable interface action\n");
+		return ret;
+	}
+
+	/*
+	 * Enable sensors data conversion and IRQ. We need to lock the
+	 * interface mutex since hwmon has just been created and the
+	 * corresponding sysfs files are accessible from user-space,
+	 * which theoretically may cause races.
+	 */
+	mutex_lock(&pvt->iface_mtx);
+	pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
+	pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
+	mutex_unlock(&pvt->iface_mtx);
+
+	return 0;
+}
+
+#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static int pvt_enable_iface(struct pvt_hwmon *pvt)
+{
+	return 0;
+}
+
+#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static int pvt_probe(struct platform_device *pdev)
+{
+	struct pvt_hwmon *pvt;
+	int ret;
+
+	pvt = pvt_create_data(pdev);
+	if (IS_ERR(pvt))
+		return PTR_ERR(pvt);
+
+	ret = pvt_request_regs(pvt);
+	if (ret)
+		return ret;
+
+	ret = pvt_request_clks(pvt);
+	if (ret)
+		return ret;
+
+	pvt_init_iface(pvt);
+
+	ret = pvt_request_irq(pvt);
+	if (ret)
+		return ret;
+
+	ret = pvt_create_hwmon(pvt);
+	if (ret)
+		return ret;
+
+	ret = pvt_enable_iface(pvt);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct of_device_id pvt_of_match[] = {
+	{ .compatible = "baikal,bt1-pvt" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pvt_of_match);
+
+static struct platform_driver pvt_driver = {
+	.probe = pvt_probe,
+	.driver = {
+		.name = "bt1-pvt",
+		.of_match_table = pvt_of_match
+	}
+};
+module_platform_driver(pvt_driver);
+
+MODULE_AUTHOR("Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 PVT driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/bt1-pvt.h b/drivers/hwmon/bt1-pvt.h
new file mode 100644
index 000000000000..5eac73e94885
--- /dev/null
+++ b/drivers/hwmon/bt1-pvt.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 Process, Voltage, Temperature sensor driver
+ */
+#ifndef __HWMON_BT1_PVT_H__
+#define __HWMON_BT1_PVT_H__
+
+#include <linux/completion.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/seqlock.h>
+
+/* Baikal-T1 PVT registers and their bitfields */
+#define PVT_CTRL			0x00
+#define PVT_CTRL_EN			BIT(0)
+#define PVT_CTRL_MODE_FLD		1
+#define PVT_CTRL_MODE_MASK		GENMASK(3, PVT_CTRL_MODE_FLD)
+#define PVT_CTRL_MODE_TEMP		0x0
+#define PVT_CTRL_MODE_VOLT		0x1
+#define PVT_CTRL_MODE_LVT		0x2
+#define PVT_CTRL_MODE_HVT		0x4
+#define PVT_CTRL_MODE_SVT		0x6
+#define PVT_CTRL_TRIM_FLD		4
+#define PVT_CTRL_TRIM_MASK		GENMASK(8, PVT_CTRL_TRIM_FLD)
+#define PVT_DATA			0x04
+#define PVT_DATA_VALID			BIT(10)
+#define PVT_DATA_DATA_FLD		0
+#define PVT_DATA_DATA_MASK		GENMASK(9, PVT_DATA_DATA_FLD)
+#define PVT_TTHRES			0x08
+#define PVT_VTHRES			0x0C
+#define PVT_LTHRES			0x10
+#define PVT_HTHRES			0x14
+#define PVT_STHRES			0x18
+#define PVT_THRES_LO_FLD		0
+#define PVT_THRES_LO_MASK		GENMASK(9, PVT_THRES_LO_FLD)
+#define PVT_THRES_HI_FLD		10
+#define PVT_THRES_HI_MASK		GENMASK(19, PVT_THRES_HI_FLD)
+#define PVT_TTIMEOUT			0x1C
+#define PVT_INTR_STAT			0x20
+#define PVT_INTR_MASK			0x24
+#define PVT_RAW_INTR_STAT		0x28
+#define PVT_INTR_DVALID			BIT(0)
+#define PVT_INTR_TTHRES_LO		BIT(1)
+#define PVT_INTR_TTHRES_HI		BIT(2)
+#define PVT_INTR_VTHRES_LO		BIT(3)
+#define PVT_INTR_VTHRES_HI		BIT(4)
+#define PVT_INTR_LTHRES_LO		BIT(5)
+#define PVT_INTR_LTHRES_HI		BIT(6)
+#define PVT_INTR_HTHRES_LO		BIT(7)
+#define PVT_INTR_HTHRES_HI		BIT(8)
+#define PVT_INTR_STHRES_LO		BIT(9)
+#define PVT_INTR_STHRES_HI		BIT(10)
+#define PVT_INTR_ALL			GENMASK(10, 0)
+#define PVT_CLR_INTR			0x2C
+
+/*
+ * PVT sensors-related limits and default values
+ * @PVT_TEMP_MIN: Minimal temperature in millidegrees of Celsius.
+ * @PVT_TEMP_MAX: Maximal temperature in millidegrees of Celsius.
+ * @PVT_TEMP_CHS: Number of temperature hwmon channels.
+ * @PVT_VOLT_MIN: Minimal voltage in mV.
+ * @PVT_VOLT_MAX: Maximal voltage in mV.
+ * @PVT_VOLT_CHS: Number of voltage hwmon channels.
+ * @PVT_DATA_MIN: Minimal PVT raw data value.
+ * @PVT_DATA_MAX: Maximal PVT raw data value.
+ * @PVT_TRIM_MIN: Minimal temperature sensor trim value.
+ * @PVT_TRIM_MAX: Maximal temperature sensor trim value.
+ * @PVT_TRIM_DEF: Default temperature sensor trim value (set a proper value
+ *		  when one is determined for Baikal-T1 SoC).
+ * @PVT_TRIM_TEMP: Maximum temperature encoded by the trim factor.
+ * @PVT_TRIM_STEP: Temperature stride corresponding to the trim value.
+ * @PVT_TOUT_MIN: Minimal timeout between samples in nanoseconds.
+ * @PVT_TOUT_DEF: Default data measurements timeout. In case if alarms are
+ *		  activated the PVT IRQ is enabled to be raised after each
+ *		  conversion in order to have the thresholds checked and the
+ *		  converted value cached. Too frequent conversions may cause
+ *		  the system CPU overload. Lets set the 50ms delay between
+ *		  them by default to prevent this.
+ */
+#define PVT_TEMP_MIN		-48380L
+#define PVT_TEMP_MAX		147438L
+#define PVT_TEMP_CHS		1
+#define PVT_VOLT_MIN		620L
+#define PVT_VOLT_MAX		1168L
+#define PVT_VOLT_CHS		4
+#define PVT_DATA_MIN		0
+#define PVT_DATA_MAX		(PVT_DATA_DATA_MASK >> PVT_DATA_DATA_FLD)
+#define PVT_TRIM_MIN		0
+#define PVT_TRIM_MAX		(PVT_CTRL_TRIM_MASK >> PVT_CTRL_TRIM_FLD)
+#define PVT_TRIM_TEMP		7130
+#define PVT_TRIM_STEP		(PVT_TRIM_TEMP / PVT_TRIM_MAX)
+#define PVT_TRIM_DEF		0
+#define PVT_TOUT_MIN		(NSEC_PER_SEC / 3000)
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+# define PVT_TOUT_DEF		60000
+#else
+# define PVT_TOUT_DEF		0
+#endif
+
+/*
+ * enum pvt_sensor_type - Baikal-T1 PVT sensor types (correspond to each PVT
+ *			  sampling mode)
+ * @PVT_SENSOR*: helpers to traverse the sensors in loops.
+ * @PVT_TEMP: PVT Temperature sensor.
+ * @PVT_VOLT: PVT Voltage sensor.
+ * @PVT_LVT: PVT Low-Voltage threshold sensor.
+ * @PVT_HVT: PVT High-Voltage threshold sensor.
+ * @PVT_SVT: PVT Standard-Voltage threshold sensor.
+ */
+enum pvt_sensor_type {
+	PVT_SENSOR_FIRST,
+	PVT_TEMP = PVT_SENSOR_FIRST,
+	PVT_VOLT,
+	PVT_LVT,
+	PVT_HVT,
+	PVT_SVT,
+	PVT_SENSOR_LAST = PVT_SVT,
+	PVT_SENSORS_NUM
+};
+
+/*
+ * enum pvt_clock_type - Baikal-T1 PVT clocks.
+ * @PVT_CLOCK_APB: APB clock.
+ * @PVT_CLOCK_REF: PVT reference clock.
+ */
+enum pvt_clock_type {
+	PVT_CLOCK_APB,
+	PVT_CLOCK_REF,
+	PVT_CLOCK_NUM
+};
+
+/*
+ * struct pvt_sensor_info - Baikal-T1 PVT sensor informational structure
+ * @channel: Sensor channel ID.
+ * @label: hwmon sensor label.
+ * @mode: PVT mode corresponding to the channel.
+ * @thres_base: upper and lower threshold values of the sensor.
+ * @thres_sts_lo: low threshold status bitfield.
+ * @thres_sts_hi: high threshold status bitfield.
+ * @type: Sensor type.
+ * @attr_min_alarm: Min alarm attribute ID.
+ * @attr_min_alarm: Max alarm attribute ID.
+ */
+struct pvt_sensor_info {
+	int channel;
+	const char *label;
+	u32 mode;
+	unsigned long thres_base;
+	u32 thres_sts_lo;
+	u32 thres_sts_hi;
+	enum hwmon_sensor_types type;
+	u32 attr_min_alarm;
+	u32 attr_max_alarm;
+};
+
+#define PVT_SENSOR_INFO(_ch, _label, _type, _mode, _thres)	\
+	{							\
+		.channel = _ch,					\
+		.label = _label,				\
+		.mode = PVT_CTRL_MODE_ ##_mode,			\
+		.thres_base = PVT_ ##_thres,			\
+		.thres_sts_lo = PVT_INTR_ ##_thres## _LO,	\
+		.thres_sts_hi = PVT_INTR_ ##_thres## _HI,	\
+		.type = _type,					\
+		.attr_min_alarm = _type## _min,			\
+		.attr_max_alarm = _type## _max,			\
+	}
+
+/*
+ * struct pvt_cache - PVT sensors data cache
+ * @data: data cache in raw format.
+ * @thres_sts_lo: low threshold status saved on the previous data conversion.
+ * @thres_sts_hi: high threshold status saved on the previous data conversion.
+ * @data_seqlock: cached data seq-lock.
+ * @conversion: data conversion completion.
+ */
+struct pvt_cache {
+	u32 data;
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+	seqlock_t data_seqlock;
+	u32 thres_sts_lo;
+	u32 thres_sts_hi;
+#else
+	struct completion conversion;
+#endif
+};
+
+/*
+ * struct pvt_hwmon - Baikal-T1 PVT private data
+ * @dev: device structure of the PVT platform device.
+ * @hwmon: hwmon device structure.
+ * @regs: pointer to the Baikal-T1 PVT registers region.
+ * @irq: PVT events IRQ number.
+ * @clks: Array of the PVT clocks descriptor (APB/ref clocks).
+ * @ref_clk: Pointer to the reference clocks descriptor.
+ * @iface_mtx: Generic interface mutex (used to lock the alarm registers
+ *	       when the alarms enabled, or the data conversion interface
+ *	       if alarms are disabled).
+ * @sensor: current PVT sensor the data conversion is being performed for.
+ * @cache: data cache descriptor.
+ */
+struct pvt_hwmon {
+	struct device *dev;
+	struct device *hwmon;
+
+	void __iomem *regs;
+	int irq;
+
+	struct clk_bulk_data clks[PVT_CLOCK_NUM];
+
+	struct mutex iface_mtx;
+	enum pvt_sensor_type sensor;
+	struct pvt_cache cache[PVT_SENSORS_NUM];
+};
+
+/*
+ * struct pvt_poly_term - a term descriptor of the PVT data translation
+ *			  polynomial
+ * @deg: degree of the term.
+ * @coef: multiplication factor of the term.
+ * @divider: distributed divider per each degree.
+ * @divider_leftover: divider leftover, which couldn't be redistributed.
+ */
+struct pvt_poly_term {
+	unsigned int deg;
+	long coef;
+	long divider;
+	long divider_leftover;
+};
+
+/*
+ * struct pvt_poly - PVT data translation polynomial descriptor
+ * @total_divider: total data divider.
+ * @terms: polynomial terms up to a free one.
+ */
+struct pvt_poly {
+	long total_divider;
+	struct pvt_poly_term terms[];
+};
+
+#endif /* __HWMON_BT1_PVT_H__ */
-- 
2.26.2


^ permalink raw reply related

* [PATCH v3 2/3] hwmon: Add notification support
From: Serge Semin @ 2020-05-26 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Serge Semin, Serge Semin, Maxim Kaurkin, Alexey Malahov,
	Thomas Bogendoerfer, Arnd Bergmann, Rob Herring, linux-mips,
	devicetree, linux-hwmon, linux-kernel
In-Reply-To: <20200526133823.20466-1-Sergey.Semin@baikalelectronics.ru>

From: Guenter Roeck <linux@roeck-us.net>

For hwmon drivers using the hwmon_device_register_with_info() API, it
is desirable to have a generic notification mechanism available. This
mechanism can be used to notify userspace as well as the thermal
subsystem if the driver experiences any events, such as warning or
critical alarms.

Implement hwmon_notify_event() to provide this mechanism. The function
generates a sysfs event and a udev event. If the device is registered
with the thermal subsystem and the event is associated with a temperature
sensor, also notify the thermal subsystem that a thermal event occurred.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Maxim Kaurkin <Maxim.Kaurkin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: devicetree@vger.kernel.org

---

Guenter: I have no plan to push this since there are currently no users
(and thus no means to test the code). However, I want to make sure that
the patch is available if/when needed.
Serge: There is a user now. It's Baikal-T1 PVT driver. So the patch can
be merged in.

Changelog v2: Skipped

Changelog v3:
- Fix merge conflicts found in struct hwmon_thermal_data and in
  hwmon_thermal_add_sensors().
- Use hwmon_attr_base() to get the hwmon attribute base index.
- Use the base value to get the sysfs-attribute name.
  hwmon_thermal_notify() is supposed to be called with normal index.
---
 drivers/hwmon/hwmon.c | 69 +++++++++++++++++++++++++++++++++++++++++--
 include/linux/hwmon.h |  3 ++
 2 files changed, 69 insertions(+), 3 deletions(-)

diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 6a30fb453f7a..e9bc33c18a33 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -15,6 +15,7 @@
 #include <linux/gfp.h>
 #include <linux/hwmon.h>
 #include <linux/idr.h>
+#include <linux/list.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
@@ -31,7 +32,7 @@ struct hwmon_device {
 	const char *name;
 	struct device dev;
 	const struct hwmon_chip_info *chip;
-
+	struct list_head tzdata;
 	struct attribute_group group;
 	const struct attribute_group **groups;
 };
@@ -55,12 +56,12 @@ struct hwmon_device_attribute {
 
 /*
  * Thermal zone information
- * In addition to the reference to the hwmon device,
- * also provides the sensor index.
  */
 struct hwmon_thermal_data {
+	struct list_head node;		/* hwmon tzdata list entry */
 	struct device *dev;		/* Reference to hwmon device */
 	int index;			/* sensor index */
+	struct thermal_zone_device *tzd;/* thermal zone device */
 };
 
 static ssize_t
@@ -156,10 +157,17 @@ static const struct thermal_zone_of_device_ops hwmon_thermal_ops = {
 	.get_temp = hwmon_thermal_get_temp,
 };
 
+static void hwmon_thermal_remove_sensor(void *data)
+{
+	list_del(data);
+}
+
 static int hwmon_thermal_add_sensor(struct device *dev, int index)
 {
+	struct hwmon_device *hwdev = to_hwmon_device(dev);
 	struct hwmon_thermal_data *tdata;
 	struct thermal_zone_device *tzd;
+	int err;
 
 	tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL);
 	if (!tdata)
@@ -177,13 +185,37 @@ static int hwmon_thermal_add_sensor(struct device *dev, int index)
 	if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV))
 		return PTR_ERR(tzd);
 
+	err = devm_add_action(dev, hwmon_thermal_remove_sensor, &tdata->node);
+	if (err)
+		return err;
+
+	tdata->tzd = tzd;
+	list_add(&tdata->node, &hwdev->tzdata);
+
 	return 0;
 }
+
+static void hwmon_thermal_notify(struct device *dev, int index)
+{
+	struct hwmon_device *hwdev = to_hwmon_device(dev);
+	struct hwmon_thermal_data *tzdata;
+
+	list_for_each_entry(tzdata, &hwdev->tzdata, node) {
+		if (tzdata->index == index) {
+			thermal_zone_device_update(tzdata->tzd,
+						   THERMAL_EVENT_UNSPECIFIED);
+		}
+	}
+}
+
 #else
 static int hwmon_thermal_add_sensor(struct device *dev, int index)
 {
 	return 0;
 }
+
+static void hwmon_thermal_notify(struct device *dev, int index) { }
+
 #endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */
 
 static int hwmon_attr_base(enum hwmon_sensor_types type)
@@ -511,6 +543,35 @@ static const int __templates_size[] = {
 	[hwmon_intrusion] = ARRAY_SIZE(hwmon_intrusion_attr_templates),
 };
 
+int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
+		       u32 attr, int channel)
+{
+	char sattr[MAX_SYSFS_ATTR_NAME_LENGTH];
+	const char * const *templates;
+	const char *template;
+	int base;
+
+	if (type >= ARRAY_SIZE(__templates))
+		return -EINVAL;
+	if (attr >= __templates_size[type])
+		return -EINVAL;
+
+	templates = __templates[type];
+	template = templates[attr];
+
+	base = hwmon_attr_base(type);
+
+	scnprintf(sattr, MAX_SYSFS_ATTR_NAME_LENGTH, template, base + channel);
+	sysfs_notify(&dev->kobj, NULL, sattr);
+	kobject_uevent(&dev->kobj, KOBJ_CHANGE);
+
+	if (type == hwmon_temp)
+		hwmon_thermal_notify(dev, channel);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hwmon_notify_event);
+
 static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info)
 {
 	int i, n;
@@ -661,6 +722,8 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
 	if (err)
 		goto free_hwmon;
 
+	INIT_LIST_HEAD(&hwdev->tzdata);
+
 	if (dev && dev->of_node && chip && chip->ops->read &&
 	    chip->info[0]->type == hwmon_chip &&
 	    (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {
diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index 5e609f25878c..363d4a814aa1 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -436,6 +436,9 @@ devm_hwmon_device_register_with_info(struct device *dev,
 void hwmon_device_unregister(struct device *dev);
 void devm_hwmon_device_unregister(struct device *dev);
 
+int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
+		       u32 attr, int channel);
+
 /**
  * hwmon_is_bad_char - Is the char invalid in a hwmon name
  * @ch: the char to be considered
-- 
2.26.2


^ permalink raw reply related

* [PATCH v3 0/3] hwmon: Add Baikal-T1 SoC Process, Voltage and Temp sensor support
From: Serge Semin @ 2020-05-26 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Serge Semin, Serge Semin, Maxim Kaurkin, Maxim Kaurkin,
	Alexey Malahov, Pavel Parkhomenko, Ramil Zaripov,
	Ekaterina Skachko, Vadim Vlasov, Alexey Kolotnikov,
	Thomas Bogendoerfer, Arnd Bergmann, Rob Herring, linux-mips,
	linux-hwmon, devicetree, linux-kernel

In order to keep track of Baikal-T1 SoC power consumption and make sure
the chip heating is within the normal temperature limits, there is
a dedicated hardware monitor sensor embedded into the SoC. It is based
on the Analog Bits PVT sensor but equipped with a vendor-specific control
wrapper, which ease an access to the sensors functionality. Fist of all it
provides an accessed to the sampled Temperature, Voltage and
Low/Standard/High Voltage thresholds. In addition the wrapper generates
an interrupt in case if one enabled for alarm thresholds or data ready
event. All of these functionality is implemented in the Baikal-T1 PVT
driver submitted within this patchset. Naturally there is also a patch,
which creates a corresponding yaml-based dt-binding file for the sensor.

This patchset is rebased and tested on the mainline Linux kernel 5.6-rc4:
base-commit: 0e698dfa2822 ("Linux 5.7-rc4")
tag: v5.7-rc4

Note new vendor prefix for Baikal-T1 PVT device will be added in the
framework of the next patchset:
https://lkml.org/lkml/2020/5/6/1047

Changelog v2:
- Don't use a multi-arg clock phandle reference in the examples dt-bindings
  property. Thus reundant include pre-processor statement can be removed.
- Rearrange the SoBs with adding Maxim' co-development tag.
- Lowercase the node-name in the dt-schema example.
- Add dual license header to the dt-bindings file.
- Replace "additionalProperties: false" property with
  "unevaluatedProperties: false".
- Discard label definition from the binding example.
- Discard handwritten IO-access wrappers. Use normal readl/writel instead.
- Use generic FIELD_{GET,PREP} macros instead of handwritten ones.
- Since the driver depends on the OF config we can remove of_match_ptr()
  macro utilization.
- Don't print error-message if no platform IRQ found. Just return an error.
- Remove probe-status info string printout.
- Our corporate email server doesn't change Message-Id anymore, so the patchset
  is resubmitted being in the cover-letter-threaded format.

Link: https://lore.kernel.org/linux-hwmon/20200510103211.27905-1-Sergey.Semin@baikalelectronics.ru/
Changelog v3:
- Add bt1-pvt into the Documentation/hwmon/index.rst file.
- Discard explicit "default n" from the SENSORS_BT1_PVT_ALARMS config.
- Use "depends on SENSORS_BT1_PVT" statement instead of if-endif kbuild
  config clause.
- Alphabetically order the include macro operators.
- Discard unneeded include macro in the header file.
- Use new generic interface of the hwmon alarms notifications introduced
  in the first patch (based on hwmon_notify_event()).
- Add more descriptive information regarding the temp1_trim attribute.
- Discard setting the platforms device private data by using
  platform_set_drvdata(). It's redundant since unused in the driver.
- Pass "pvt" hwmon name instead of dev_name(dev) to
  devm_hwmon_device_register_with_info().
- Add "baikal,pvt-temp-trim-millicelsius" temperature trim DT property
  support.
- Discard kernel log warnings printed from the ISR when either min or
  max threshold levels are crossed.
- Discard CONFIG_OF dependency since there is non at compile-time.

Co-developed-by: Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
Signed-off-by: Maxim Kaurkin <Maxim.Kaurkin@baikalelectronics.ru>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
Cc: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Cc: Ekaterina Skachko <Ekaterina.Skachko@baikalelectronics.ru>
Cc: Vadim Vlasov <V.Vlasov@baikalelectronics.ru>
Cc: Alexey Kolotnikov <Alexey.Kolotnikov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: linux-hwmon@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

Guenter Roeck (1):
  hwmon: Add notification support

Serge Semin (2):
  dt-bindings: hwmon: Add Baikal-T1 PVT sensor binding
  hwmon: Add Baikal-T1 PVT sensor driver

 .../bindings/hwmon/baikal,bt1-pvt.yaml        |  107 ++
 Documentation/hwmon/bt1-pvt.rst               |  116 ++
 Documentation/hwmon/index.rst                 |    1 +
 drivers/hwmon/Kconfig                         |   25 +
 drivers/hwmon/Makefile                        |    1 +
 drivers/hwmon/bt1-pvt.c                       | 1155 +++++++++++++++++
 drivers/hwmon/bt1-pvt.h                       |  244 ++++
 drivers/hwmon/hwmon.c                         |   69 +-
 include/linux/hwmon.h                         |    3 +
 9 files changed, 1718 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
 create mode 100644 Documentation/hwmon/bt1-pvt.rst
 create mode 100644 drivers/hwmon/bt1-pvt.c
 create mode 100644 drivers/hwmon/bt1-pvt.h

-- 
2.26.2


^ permalink raw reply

* [PATCH v3 1/3] dt-bindings: hwmon: Add Baikal-T1 PVT sensor binding
From: Serge Semin @ 2020-05-26 13:38 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck, Rob Herring
  Cc: Serge Semin, Serge Semin, Maxim Kaurkin, Rob Herring,
	Alexey Malahov, Thomas Bogendoerfer, Arnd Bergmann, linux-mips,
	linux-hwmon, devicetree, linux-kernel
In-Reply-To: <20200526133823.20466-1-Sergey.Semin@baikalelectronics.ru>

Baikal-T1 SoC is equipped with an embedded process, voltage and
temperature sensor to monitor the chip internal environment like
temperature, supply voltage and transistors performance.

This bindings describes the external Baikal-T1 PVT control interfaces
like MMIO registers space, interrupt request number and clocks source.
These are then used by the corresponding hwmon device driver to
implement the sysfs files-based access to the sensors functionality.

Co-developed-by: Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
Signed-off-by: Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: linux-mips@vger.kernel.org

---

Changelog v2:
- Lowercase the node-name in the example.
- Add dual-license header.
- Don't use a multi-arg clock phandle reference in the examples dt-binding
  property. Thus reundant include pre-processor statement can be removed.
- Replace "additionalProperties: false" property with
  "unevaluatedProperties: false".
- Discard label definition from the example.
- Align settings of the "#thermal-sensor-cells" property with two spaces
  ahead of the property line start.

Changelog v3:
- Add "baikal,pvt-temp-trim-millicelsius" property support.
---
 .../bindings/hwmon/baikal,bt1-pvt.yaml        | 107 ++++++++++++++++++
 1 file changed, 107 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml b/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
new file mode 100644
index 000000000000..6c3a44ec3684
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/baikal,bt1-pvt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 PVT Sensor
+
+maintainers:
+  - Serge Semin <fancer.lancer@gmail.com>
+
+description: |
+  Baikal-T1 SoC provides an embedded process, voltage and temperature
+  sensor to monitor an internal SoC environment (chip temperature, supply
+  voltage and process monitor) and on time detect critical situations,
+  which may cause the system instability and even damages. The IP-block
+  is based on the Analog Bits PVT sensor, but is equipped with a dedicated
+  control wrapper, which provides a MMIO registers-based access to the
+  sensor core functionality (APB3-bus based) and exposes an additional
+  functions like thresholds/data ready interrupts, its status and masks,
+  measurements timeout. Its internal structure is depicted on the next
+  diagram:
+
+     Analog Bits core                     Bakal-T1 PVT control block
+  +--------------------+                  +------------------------+
+  | Temperature sensor |-+         +------| Sensors control        |
+  |--------------------| |<---En---|      |------------------------|
+  | Voltage sensor     |-|<--Mode--| +--->| Sampled data           |
+  |--------------------| |<--Trim--+ |    |------------------------|
+  | Low-Vt sensor      |-|           | +--| Thresholds comparator  |
+  |--------------------| |---Data----| |  |------------------------|
+  | High-Vt sensor     |-|           | +->| Interrupts status      |
+  |--------------------| |--Valid--+-+ |  |------------------------|
+  | Standard-Vt sensor |-+         +---+--| Interrupts mask        |
+  +--------------------+                  |------------------------|
+           ^                              | Interrupts timeout     |
+           |                              +------------------------+
+           |                                        ^  ^
+  Rclk-----+----------------------------------------+  |
+  APB3-------------------------------------------------+
+
+  This bindings describes the external Baikal-T1 PVT control interfaces
+  like MMIO registers space, interrupt request number and clocks source.
+  These are then used by the corresponding hwmon device driver to
+  implement the sysfs files-based access to the sensors functionality.
+
+properties:
+  compatible:
+    const: baikal,bt1-pvt
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: PVT reference clock
+      - description: APB3 interface clock
+
+  clock-names:
+    items:
+      - const: ref
+      - const: pclk
+
+  "#thermal-sensor-cells":
+    description: Baikal-T1 can be referenced as the CPU thermal-sensor
+    const: 0
+
+  baikal,pvt-temp-trim-millicelsius:
+    description: |
+      Temperature sensor trimming factor. It can be used to manually adjust the
+      temperature measurements within 7.130 degrees Celsius.
+    maxItems: 1
+    items:
+      default: 0
+      minimum: 0
+      maximum: 7130
+
+unevaluatedProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/mips-gic.h>
+
+    pvt@1f200000 {
+      compatible = "baikal,bt1-pvt";
+      reg = <0x1f200000 0x1000>;
+      #thermal-sensor-cells = <0>;
+
+      interrupts = <GIC_SHARED 31 IRQ_TYPE_LEVEL_HIGH>;
+
+      baikal,pvt-temp-trim-millicelsius = <1000>;
+
+      clocks = <&ccu_sys>, <&ccu_sys>;
+      clock-names = "ref", "pclk";
+    };
+...
-- 
2.26.2


^ permalink raw reply related

* Re: [PATCH v4 1/2] dt-bindings: PCI: Add UniPhier PCIe endpoint controller description
From: Lorenzo Pieralisi @ 2020-05-26 13:34 UTC (permalink / raw)
  To: Kunihiko Hayashi, robh+dt
  Cc: Bjorn Helgaas, Masahiro Yamada, linux-pci, devicetree,
	linux-arm-kernel, linux-kernel, Masami Hiramatsu, Jassi Brar
In-Reply-To: <1589457801-12796-2-git-send-email-hayashi.kunihiko@socionext.com>

On Thu, May 14, 2020 at 09:03:20PM +0900, Kunihiko Hayashi wrote:
> Add DT bindings for PCIe controller implemented in UniPhier SoCs
> when configured in endpoint mode. This controller is based on
> the DesignWare PCIe core.
> 
> Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
> ---
>  .../bindings/pci/socionext,uniphier-pcie-ep.yaml   | 92 ++++++++++++++++++++++
>  MAINTAINERS                                        |  2 +-
>  2 files changed, 93 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml

Hi Rob,

are you OK with this patch ? Please let me know, I'd like to pull
the series, thanks.

Lorenzo

> diff --git a/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml
> new file mode 100644
> index 0000000..f0558b9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml
> @@ -0,0 +1,92 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/pci/socionext,uniphier-pcie-ep.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Socionext UniPhier PCIe endpoint controller
> +
> +description: |
> +  UniPhier PCIe endpoint controller is based on the Synopsys DesignWare
> +  PCI core. It shares common features with the PCIe DesignWare core and
> +  inherits common properties defined in
> +  Documentation/devicetree/bindings/pci/designware-pcie.txt.
> +
> +maintainers:
> +  - Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
> +
> +allOf:
> +  - $ref: "pci-ep.yaml#"
> +
> +properties:
> +  compatible:
> +    const: socionext,uniphier-pro5-pcie-ep
> +
> +  reg:
> +    maxItems: 4
> +
> +  reg-names:
> +    items:
> +      - const: dbi
> +      - const: dbi2
> +      - const: link
> +      - const: addr_space
> +
> +  clocks:
> +    maxItems: 2
> +
> +  clock-names:
> +    items:
> +      - const: gio
> +      - const: link
> +
> +  resets:
> +    maxItems: 2
> +
> +  reset-names:
> +    items:
> +      - const: gio
> +      - const: link
> +
> +  num-ib-windows:
> +    const: 16
> +
> +  num-ob-windows:
> +    const: 16
> +
> +  num-lanes: true
> +
> +  phys:
> +    maxItems: 1
> +
> +  phy-names:
> +    const: pcie-phy
> +
> +required:
> +  - compatible
> +  - reg
> +  - reg-names
> +  - clocks
> +  - clock-names
> +  - resets
> +  - reset-names
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    pcie_ep: pcie-ep@66000000 {
> +        compatible = "socionext,uniphier-pro5-pcie-ep";
> +        reg-names = "dbi", "dbi2", "link", "addr_space";
> +        reg = <0x66000000 0x1000>, <0x66001000 0x1000>,
> +              <0x66010000 0x10000>, <0x67000000 0x400000>;
> +        clock-names = "gio", "link";
> +        clocks = <&sys_clk 12>, <&sys_clk 24>;
> +        reset-names = "gio", "link";
> +        resets = <&sys_rst 12>, <&sys_rst 24>;
> +        num-ib-windows = <16>;
> +        num-ob-windows = <16>;
> +        num-lanes = <4>;
> +        phy-names = "pcie-phy";
> +        phys = <&pcie_phy>;
> +    };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 92657a1..7f26748 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -13211,7 +13211,7 @@ PCIE DRIVER FOR SOCIONEXT UNIPHIER
>  M:	Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
>  L:	linux-pci@vger.kernel.org
>  S:	Maintained
> -F:	Documentation/devicetree/bindings/pci/uniphier-pcie.txt
> +F:	Documentation/devicetree/bindings/pci/uniphier-pcie*
>  F:	drivers/pci/controller/dwc/pcie-uniphier.c
>  
>  PCIE DRIVER FOR ST SPEAR13XX
> -- 
> 2.7.4
> 

^ permalink raw reply

* Re: [PATCHv3 1/7] dt-bindings: crypto: Add TI SA2UL crypto accelerator documentation
From: Tero Kristo @ 2020-05-26 13:16 UTC (permalink / raw)
  To: herbert, davem, linux-crypto; +Cc: Keerthy, Rob Herring, devicetree
In-Reply-To: <20200514125005.23641-1-t-kristo@ti.com>

On 14/05/2020 15:50, Tero Kristo wrote:
> From: Keerthy <j-keerthy@ti.com>
> 
> The Security Accelerator Ultra Lite (SA2UL) subsystem provides hardware
> cryptographic acceleration for the following use cases:
> 
> * Encryption and authentication for secure boot
> * Encryption and authentication of content in applications
>    requiring DRM (digital rights management) and
>    content/asset protection
> 
> SA2UL provides support for number of different cryptographic algorithms
> including SHA1, SHA256, SHA512, AES, 3DES, and various combinations of
> the previous for AEAD use.
> 
> Cc: Rob Herring <robh@kernel.org>
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Keerthy <j-keerthy@ti.com>
> [t-kristo@ti.com: converted documentation to yaml]
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
> ---
> v3:
>    - fixed a typo in rng child node regex

Rob, any comments on this one?

I did post the patch for converting omap-rng (the child node) to schema 
also.

-Tero

> 
>   .../devicetree/bindings/crypto/ti,sa2ul.yaml  | 76 +++++++++++++++++++
>   1 file changed, 76 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/crypto/ti,sa2ul.yaml
> 
> diff --git a/Documentation/devicetree/bindings/crypto/ti,sa2ul.yaml b/Documentation/devicetree/bindings/crypto/ti,sa2ul.yaml
> new file mode 100644
> index 000000000000..27bb3a7e2b87
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/crypto/ti,sa2ul.yaml
> @@ -0,0 +1,76 @@
> +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/crypto/ti,sa2ul.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: K3 SoC SA2UL crypto module
> +
> +maintainers:
> +  - Tero Kristo <t-kristo@ti.com>
> +
> +properties:
> +  compatible:
> +    enum:
> +      - ti,j721e-sa2ul
> +      - ti,am654-sa2ul
> +
> +  reg:
> +    maxItems: 1
> +
> +  power-domains:
> +    maxItems: 1
> +
> +  dmas:
> +    items:
> +      - description: TX DMA Channel
> +      - description: RX DMA Channel #1
> +      - description: RX DMA Channel #2
> +
> +  dma-names:
> +    items:
> +      - const: tx
> +      - const: rx1
> +      - const: rx2
> +
> +  dma-coherent: true
> +
> +  "#address-cells":
> +    const: 2
> +
> +  "#size-cells":
> +    const: 2
> +
> +  ranges:
> +    description:
> +      Address translation for the possible RNG child node for SA2UL
> +
> +patternProperties:
> +  "^rng@[a-f0-9]+$":
> +    type: object
> +    description:
> +      Child RNG node for SA2UL
> +
> +required:
> +  - compatible
> +  - reg
> +  - power-domains
> +  - dmas
> +  - dma-names
> +  - dma-coherent
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/soc/ti,sci_pm_domain.h>
> +
> +    main_crypto: crypto@4e00000 {
> +        compatible = "ti,j721-sa2ul";
> +        reg = <0x0 0x4e00000 0x0 0x1200>;
> +        power-domains = <&k3_pds 264 TI_SCI_PD_EXCLUSIVE>;
> +        dmas = <&main_udmap 0xc000>, <&main_udmap 0x4000>,
> +               <&main_udmap 0x4001>;
> +        dma-names = "tx", "rx1", "rx2";
> +        dma-coherent;
> +    };
> 

--
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

^ permalink raw reply

* Re: [PATCH v3 0/6] bus/memory: Add Baikal-T1 SoC APB/AXI/L2 drivers
From: Serge Semin @ 2020-05-26 13:08 UTC (permalink / raw)
  To: Rob Herring
  Cc: Thomas Bogendoerfer, Greg Kroah-Hartman, Arnd Bergmann,
	Alexey Malahov, Maxim Kaurkin, Pavel Parkhomenko, Ramil Zaripov,
	Ekaterina Skachko, Vadim Vlasov, Alexey Kolotnikov, Paul Burton,
	Olof Johansson, linux-mips, soc, devicetree, linux-kernel
In-Reply-To: <20200526125928.17096-1-Sergey.Semin@baikalelectronics.ru>

Rob,
Could you pay attention to this patchset? There is only one DT binding left
without your tag:
[PATCH v3 3/6] dt-bindings: memory: Add Baikal-T1 L2-cache Control Block binding
After we get it, Arnd will merge the series into the soc repository.

Thanks
-Sergey


On Tue, May 26, 2020 at 03:59:22PM +0300, Serge Semin wrote:
> Baikal-T1 SoC CPU is based on two MIPS Warrior P5600 cores. Their main
> memory Non-Coherent IO interface is connected to the OCP2AXI bridge, which
> in turn is then connected to the DW AMBA 3 AXI Interconnect (so called
> Main Interconnect) with nine masters and four slaves ports. Main
> Interconnect is responsible for the AXI-bus traffic arbitration (QoS) and
> its routing from one component to another. In addition there is a Errors
> Handler Block (EHB) accesible by means of the Baikal-T1 SoC System
> Controller responsible to detect AXI protocol errors and device not
> responding situations built on top the interconnect. Baikal-T1 AXI-bus
> driver included in this patchset will be responsible for working with that
> functionality, though currently it doesn't support QoS tuning. Instead
> it's capable of detecting the error events, reporting an info about them
> to the system log, injecting artificial errors to test the driver
> functionality. Since AXI Interconnect doesn't provide a way to find out
> which devices are connected to it, so its DT node is supposed to be
> compatible with "simple-bus" driver, while sub-nodes shall represent the
> masters attached to the bus.
> 
> One of the AXI Interconnect slaves is an AXI-APB bridge used to access the
> Baikal-T1 SoC subsystems CSRs. MMIO request from CPU and DMAC masters are
> routed there if they are detected to be within [0x08000000 0x1FFFFFFF]
> range of the physical memory. In case if an attempted APB transaction
> stays with no response for a pre-defined time it will be detected by the
> APB-bus Errors Handler Block (EHB), which will raise an interrupt, then
> the bus gets freed for a next operation. The APB-bus driver provides the
> interrupt handler to detect the erroneous address, update an errors
> counter and prints an error message about the faulty address. The counter
> and the APB-bus operations timeout can be accessed via corresponding sysfs
> nodes. A dedicated sysfs-node can be also used to artificially cause the
> bus errors described above. Since APB-bus is a platform bus, it doesn't
> provide a way to detect slave devices connected to it, so similarly to the
> AXI-bus it's also supposed to be compatible with "simple-bus" driver.
> 
> Aside from PCIe/SATA/DDR/I2C/EHB/CPU/reboot specific settings the
> Baikal-T1 System Controller provides a MIPS P5600 CM2 L2-cache tuning
> block. It is responsible for the setting up the Tag/Data/WS L2-to-RAM
> latencies. The last small patch in this patchset provides a driver and
> DT-schema-based binding for the described device. So that the latencies
> can be tuned up by means of dedicated DT properties and sysfs nodes.
> 
> This patchset is rebased and tested on the mainline Linux kernel 5.7-rc4:
> 0e698dfa2822 ("Linux 5.7-rc4")
> tag: v5.7-rc4
> 
> Changelog v2 (AXI/APB bus):
> - Assign dual GPL/BSD licenses to the bindings.
> - Use single lined copyright headers in the bindings.
> - Replace "additionalProperties: false" property with
>   "unevaluatedProperties: false" in the bindings.
> - Don't use a multi-arg clock phandle reference in DT binding examples.
>   Thus remove includes from there.
> - Fix some commit message and Kconfig help text spelling.
> - Move drivers from soc to the bus subsystem.
> - Convert a simple EHB drivers to the Baikal-T1 AXI and APB bus ones.
> - Convert APB bus driver to using regmap MMIO API.
> - Use syscon regmap to access the AXI-bus erroneous address.
> - Add reset line support.
> - Add Main Interconnect clock support to the AXI-bus driver.
> - Remove probe-status info string printout.
> - Discard of_match_ptr() macro utilization.
> - Don't print error-message if no platform IRQ found. Just return an error.
> - Use generic FIELD_{GET,PREP} macros instead of handwritten ones in the
>   AXI-bus driver.
> 
> Changelog v2 (l2 driver):
> - Fix some commit message and Kconfig help text spelling.
> - Move the driver to the memory subsystem.
> - Assign dual GPL/BSD license to the DT binding.
> - Use single lined copyright header in the binding.
> - Discard reg property and syscon compatible string.
> - Move "allOf" restrictions to the root level of the properties.
> - The DT node is supposed to be a child of the Baikal-T1 system controller
>   node. So regmap will be fetched from there.
> - Use generic FIELD_{GET,PREP} macro.
> - Remove probe-status info string printout.
> - Since the driver depends on the OF config we can remove of_match_ptr()
>   macro utilization.
> 
> Changelog v3:
> - Combine l2 and AXI/APB bus patches in a single patchset.
> - Retrieve AXI-bus QoS registers by resource name "qos".
> - Discard CONFIG_OF dependency since there is none at compile-time.
> - Add syscon EHB registers range to the AXI-bus reg property as optional
>   entry.
> - Fix invalid of_property_read_u32() return value test in the l2-ctl
>   driver.
> - Get the reg property back into the l2-ctl DT bindings even though the
>   driver is using the parental syscon regmap.
> - The l2-ctl DT schema will live separately from the system controller,
>   but the corresponding sub-node of the later DT schema will $ref this one.
> - Set non-default latencies in the l2-ctl DT example.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
> Cc: Maxim Kaurkin <Maxim.Kaurkin@baikalelectronics.ru>
> Cc: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
> Cc: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
> Cc: Ekaterina Skachko <Ekaterina.Skachko@baikalelectronics.ru>
> Cc: Vadim Vlasov <V.Vlasov@baikalelectronics.ru>
> Cc: Alexey Kolotnikov <Alexey.Kolotnikov@baikalelectronics.ru>
> Cc: Paul Burton <paulburton@kernel.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Olof Johansson <olof@lixom.net>
> Cc: linux-mips@vger.kernel.org
> Cc: soc@kernel.org
> Cc: devicetree@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> 
> Serge Semin (6):
>   dt-bindings: bus: Add Baikal-T1 AXI-bus binding
>   dt-bindings: bus: Add Baikal-T1 APB-bus binding
>   dt-bindings: memory: Add Baikal-T1 L2-cache Control Block binding
>   bus: Add Baikal-T1 AXI-bus driver
>   bus: Add Baikal-T1 APB-bus driver
>   memory: Add Baikal-T1 L2-cache Control Block driver
> 
>  .../bindings/bus/baikal,bt1-apb.yaml          |  90 ++++
>  .../bindings/bus/baikal,bt1-axi.yaml          | 107 +++++
>  .../memory-controllers/baikal,bt1-l2-ctl.yaml |  63 +++
>  drivers/bus/Kconfig                           |  30 ++
>  drivers/bus/Makefile                          |   2 +
>  drivers/bus/bt1-apb.c                         | 421 ++++++++++++++++++
>  drivers/bus/bt1-axi.c                         | 318 +++++++++++++
>  drivers/memory/Kconfig                        |  11 +
>  drivers/memory/Makefile                       |   1 +
>  drivers/memory/bt1-l2-ctl.c                   | 322 ++++++++++++++
>  10 files changed, 1365 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/bus/baikal,bt1-apb.yaml
>  create mode 100644 Documentation/devicetree/bindings/bus/baikal,bt1-axi.yaml
>  create mode 100644 Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml
>  create mode 100644 drivers/bus/bt1-apb.c
>  create mode 100644 drivers/bus/bt1-axi.c
>  create mode 100644 drivers/memory/bt1-l2-ctl.c
> 
> -- 
> 2.26.2
> 

^ permalink raw reply

* [PATCH v3 2/6] dt-bindings: bus: Add Baikal-T1 APB-bus binding
From: Serge Semin @ 2020-05-26 12:59 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Arnd Bergmann,
	Rob Herring
  Cc: Serge Semin, Serge Semin, Rob Herring, Alexey Malahov,
	Paul Burton, Olof Johansson, linux-mips, soc, devicetree,
	linux-kernel
In-Reply-To: <20200526125928.17096-1-Sergey.Semin@baikalelectronics.ru>

Baikal-T1 CPU or DMAC MMIO requests are handled by the AMBA 3 AXI
Interconnect which routes them to the AXI-APB bridge, which in turn
serializes accesses and routes them to the corresponding APB slave device.
This binding describes the AXI-APB bridge considered as the APB-bus. It is
supposed to be compatible with "be,bt1-apb" and "simple-bus" drivers,
should be equipped with EHB MMIO region and a region with no slave device
mapped, interrupts line number, APB reference clock and domain reset line.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: linux-mips@vger.kernel.org
Cc: soc@kernel.org

---

Changelog v2:
- Move driver to the bus subsystem.
- Don't use a multi-arg clock phandle reference in the examples dt-bindings
  property. Thus redundant include statement can be removed.
- Use dual GPL/BSD license.
- Use single lined copyright header.
- Lowercase the unit-address.
- Convert a dedicated EHB block binding to the Baikal-T1 APB-bus one.
- Add APB reference clock and reset support.
- Replace "additionalProperties: false" property with
  "unevaluatedProperties: false".
- Add reg-names property.
---
 .../bindings/bus/baikal,bt1-apb.yaml          | 90 +++++++++++++++++++
 1 file changed, 90 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/bus/baikal,bt1-apb.yaml

diff --git a/Documentation/devicetree/bindings/bus/baikal,bt1-apb.yaml b/Documentation/devicetree/bindings/bus/baikal,bt1-apb.yaml
new file mode 100644
index 000000000000..d6a3b71ea835
--- /dev/null
+++ b/Documentation/devicetree/bindings/bus/baikal,bt1-apb.yaml
@@ -0,0 +1,90 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/bus/baikal,bt1-apb.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 APB-bus
+
+maintainers:
+  - Serge Semin <fancer.lancer@gmail.com>
+
+description: |
+  Baikal-T1 CPU or DMAC MMIO requests are handled by the AMBA 3 AXI Interconnect
+  which routes them to the AXI-APB bridge. This interface is a single master
+  multiple slaves bus in turn serializing IO accesses and routing them to the
+  addressed APB slave devices. In case of any APB protocol collisions, slave
+  device not responding on timeout an IRQ is raised with an erroneous address
+  reported to the APB terminator (APB Errors Handler Block).
+
+allOf:
+ - $ref: /schemas/simple-bus.yaml#
+
+properties:
+  compatible:
+    contains:
+      const: baikal,bt1-apb
+
+  reg:
+    items:
+      - description: APB EHB MMIO registers
+      - description: APB MMIO region with no any device mapped
+
+  reg-names:
+    items:
+      - const: ehb
+      - const: nodev
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: APB reference clock
+
+  clock-names:
+    items:
+      - const: pclk
+
+  resets:
+    items:
+      - description: APB domain reset line
+
+  reset-names:
+    items:
+      - const: prst
+
+unevaluatedProperties: false
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - clocks
+  - clock-names
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/mips-gic.h>
+
+    bus@1f059000 {
+      compatible = "baikal,bt1-apb", "simple-bus";
+      reg = <0 0x1f059000 0 0x1000>,
+            <0 0x1d000000 0 0x2040000>;
+      reg-names = "ehb", "nodev";
+      #address-cells = <1>;
+      #size-cells = <1>;
+
+      ranges;
+
+      interrupts = <GIC_SHARED 16 IRQ_TYPE_LEVEL_HIGH>;
+
+      clocks = <&ccu_sys 1>;
+      clock-names = "pclk";
+
+      resets = <&ccu_sys 1>;
+      reset-names = "prst";
+    };
+...
-- 
2.26.2


^ permalink raw reply related

* [PATCH v3 1/6] dt-bindings: bus: Add Baikal-T1 AXI-bus binding
From: Serge Semin @ 2020-05-26 12:59 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Arnd Bergmann,
	Rob Herring
  Cc: Serge Semin, Serge Semin, Rob Herring, Alexey Malahov,
	Paul Burton, Olof Johansson, linux-mips, soc, devicetree,
	linux-kernel
In-Reply-To: <20200526125928.17096-1-Sergey.Semin@baikalelectronics.ru>

AXI3-bus is the main communication bus connecting all high-speed
peripheral IP-cores with RAM controller and with MIPS P5600 cores on
Baikal-T1 SoC. This binding describes the DW AMBA 3 AXI Inteconnect
and Errors Handler Block synthesized on top of it, which are
responsible for the AXI-bus traffic arbitration and errors reporting
upstream to CPU. Baikal-T1 AXI-bus DT node is supposed to be compatible
with "be,bt1-axi" and "simple-bus" drivers, should have reg property with
AXI-bus QOS registers space, syscon phandle reference to the Baikal-T1
System Controller, IRQ line declared, AXI Interconnect reference clock and
reset line.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: linux-mips@vger.kernel.org
Cc: soc@kernel.org

---

Changelog v2:
- Move driver to the bus subsystem.
- Use dual GPL/BSD license.
- Use single lined copyright header.
- Lowercase the unit-address.
- Convert a simple EHB block binding to the Baikal-T1 AXI-bus one with
  interconnect capabilities support.
- Replace "additionalProperties: false" property with
  "unevaluatedProperties: false".
- Add AXI reference clock and reset support.
- Add syscon phandle reference to the Baikal-T1 System Controller node.

Changelog v3:
- Add syscon EHB registers range to the reg property as optional entry.
---
 .../bindings/bus/baikal,bt1-axi.yaml          | 107 ++++++++++++++++++
 1 file changed, 107 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/bus/baikal,bt1-axi.yaml

diff --git a/Documentation/devicetree/bindings/bus/baikal,bt1-axi.yaml b/Documentation/devicetree/bindings/bus/baikal,bt1-axi.yaml
new file mode 100644
index 000000000000..203bc0e5346b
--- /dev/null
+++ b/Documentation/devicetree/bindings/bus/baikal,bt1-axi.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/bus/baikal,bt1-axi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 AXI-bus
+
+maintainers:
+  - Serge Semin <fancer.lancer@gmail.com>
+
+description: |
+  AXI3-bus is the main communication bus of Baikal-T1 SoC connecting all
+  high-speed peripheral IP-cores with RAM controller and with MIPS P5600
+  cores. Traffic arbitration is done by means of DW AXI Interconnect (so
+  called AXI Main Interconnect) routing IO requests from one block to
+  another: from CPU to SoC peripherals and between some SoC peripherals
+  (mostly between peripheral devices and RAM, but also between DMA and
+  some peripherals). In case of any protocol error, device not responding
+  an IRQ is raised and a faulty situation is reported to the AXI EHB
+  (Errors Handler Block) embedded on top of the DW AXI Interconnect and
+  accessible by means of the Baikal-T1 System Controller.
+
+allOf:
+ - $ref: /schemas/simple-bus.yaml#
+
+properties:
+  compatible:
+    contains:
+      const: baikal,bt1-axi
+
+  reg:
+    minItems: 1
+    items:
+      - description: Synopsys DesignWare AXI Interconnect QoS registers
+      - description: AXI EHB MMIO system controller registers
+
+  reg-names:
+    minItems: 1
+    items:
+      - const: qos
+      - const: ehb
+
+  '#interconnect-cells':
+    const: 1
+
+  syscon:
+    $ref: /schemas/types.yaml#definitions/phandle
+    description: Phandle to the Baikal-T1 System Controller DT node
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Main Interconnect uplink reference clock
+
+  clock-names:
+    items:
+      - const: aclk
+
+  resets:
+    items:
+      - description: Main Interconnect reset line
+
+  reset-names:
+    items:
+      - const: arst
+
+unevaluatedProperties: false
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - syscon
+  - interrupts
+  - clocks
+  - clock-names
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/mips-gic.h>
+
+    bus@1f05a000 {
+      compatible = "baikal,bt1-axi", "simple-bus";
+      reg = <0 0x1f05a000 0 0x1000>,
+            <0 0x1f04d110 0 0x8>;
+      reg-names = "qos", "ehb";
+      #address-cells = <1>;
+      #size-cells = <1>;
+      #interconnect-cells = <1>;
+
+      syscon = <&syscon>;
+
+      ranges;
+
+      interrupts = <GIC_SHARED 127 IRQ_TYPE_LEVEL_HIGH>;
+
+      clocks = <&ccu_axi 0>;
+      clock-names = "aclk";
+
+      resets = <&ccu_axi 0>;
+      reset-names = "arst";
+    };
+...
-- 
2.26.2


^ permalink raw reply related

* [PATCH v3 3/6] dt-bindings: memory: Add Baikal-T1 L2-cache Control Block binding
From: Serge Semin @ 2020-05-26 12:59 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Arnd Bergmann,
	Rob Herring
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
	Olof Johansson, linux-mips, soc, devicetree, linux-kernel
In-Reply-To: <20200526125928.17096-1-Sergey.Semin@baikalelectronics.ru>

There is a single register provided by the SoC system controller,
which can be used to tune the L2-cache RAM up. It only provides a way
to change the L2-RAM access latencies. So aside from "be,bt1-l2-ctl"
compatible string the device node can be optionally equipped with the
properties of Tag/Data/WS latencies.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: linux-mips@vger.kernel.org
Cc: soc@kernel.org

---

Changelog v2:
- Move driver to the memory subsystem.
- Use dual GPL/BSD license.
- Use single lined copyright header.
- Move "allOf" restrictions to the root level of the properties.
- Discard syscon compatible string and reg property.
- The DT node is supposed to be a child of the Baikal-T1 system controller
  node.

Changelog v3:
- Get the reg property back even though the driver is using the parental
  syscon regmap.
- The DT schema will live separately from the system controller, but the
  corresponding sub-node of the later DT schema will $ref this one.
- Set non-default latencies in the example.
---
 .../memory-controllers/baikal,bt1-l2-ctl.yaml | 63 +++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml

diff --git a/Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml b/Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml
new file mode 100644
index 000000000000..1fca282f64a2
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/memory-controllers/baikal,bt1-l2-ctl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 L2-cache Control Block
+
+maintainers:
+  - Serge Semin <fancer.lancer@gmail.com>
+
+description: |
+  By means of the System Controller Baikal-T1 SoC exposes a few settings to
+  tune the MIPS P5600 CM2 L2 cache performance up. In particular it's possible
+  to change the Tag, Data and Way-select RAM access latencies. Baikal-T1
+  L2-cache controller block is responsible for the tuning. Its DT node is
+  supposed to be a child of the system controller.
+
+properties:
+  compatible:
+    const: baikal,bt1-l2-ctl
+
+  reg:
+    maxItems: 1
+
+  baikal,l2-ws-latency:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: Cycles of latency for Way-select RAM accesses
+    default: 0
+    minimum: 0
+    maximum: 3
+
+  baikal,l2-tag-latency:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: Cycles of latency for Tag RAM accesses
+    default: 0
+    minimum: 0
+    maximum: 3
+
+  baikal,l2-data-latency:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: Cycles of latency for Data RAM accesses
+    default: 1
+    minimum: 0
+    maximum: 3
+
+additionalProperties: false
+
+required:
+  - compatible
+
+examples:
+  - |
+    l2@1f04d028 {
+      compatible = "baikal,bt1-l2-ctl";
+      reg = <0x1f04d028 0x004>;
+
+      baikal,l2-ws-latency = <1>;
+      baikal,l2-tag-latency = <1>;
+      baikal,l2-data-latency = <2>;
+    };
+...
-- 
2.26.2


^ permalink raw reply related

* [PATCH v3 4/6] bus: Add Baikal-T1 AXI-bus driver
From: Serge Semin @ 2020-05-26 12:59 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Arnd Bergmann
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
	Olof Johansson, Rob Herring, linux-mips, soc, devicetree,
	linux-kernel
In-Reply-To: <20200526125928.17096-1-Sergey.Semin@baikalelectronics.ru>

AXI3-bus is the main communication bus connecting all high-speed
peripheral IP-cores with RAM controller and MIPS P5600 cores on Baikal-T1
SoC. Bus traffic arbitration is done by means of DW AMBA 3 AXI
Interconnect (so called AXI Main Interconnect) routing IO requests from
one SoC block to another. This driver provides a way to detect any bus
protocol errors and device not responding situations by means of an
embedded on top of the interconnect errors handler block (EHB). AXI
Interconnect QoS arbitration tuning is currently unsupported.
The bus doesn't provide a way to detect the interconnected devices,
so they are supposed to be statically defined like by means of the
simple-bus sub-nodes.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: soc@kernel.org
Cc: devicetree@vger.kernel.org

---

Changelog v2:
- Fix commit message and Kconfig help text spelling.
- Move driver from soc to the bus subsystem.
- Convert a simple EHB driver to the Baikal-T1 AXI-bus one.
- Use syscon regmap to access the AXI-bus erroneous address.
- Add interconnect reset line support.
- Remove probe-status info string printout.
- Use generic FIELD_{GET,PREP} macros instead of handwritten ones.
- Since the driver depends on the OF config we can remove of_match_ptr()
  macro utilization.
- Don't print error-message if no platform IRQ found. Just return an error.
- Select MFD_SYSCON config.

Changelog v3:
- Retrieve QoS registers by resource name "qos".
- Discard CONFIG_OF dependency since there is none at compile-time.
---
 drivers/bus/Kconfig   |  15 ++
 drivers/bus/Makefile  |   1 +
 drivers/bus/bt1-axi.c | 318 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 334 insertions(+)
 create mode 100644 drivers/bus/bt1-axi.c

diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 6d4e4497b59b..1080dd26e268 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -29,6 +29,21 @@ config BRCMSTB_GISB_ARB
 	  arbiter. This driver provides timeout and target abort error handling
 	  and internal bus master decoding.
 
+config BT1_AXI
+	tristate "Baikal-T1 AXI-bus driver"
+	depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+	select MFD_SYSCON
+	help
+	  AXI3-bus is the main communication bus connecting all high-speed
+	  peripheral IP-cores with RAM controller and with MIPS P5600 cores on
+	  Baikal-T1 SoC. Traffic arbitration is done by means of DW AMBA 3 AXI
+	  Interconnect (so called AXI Main Interconnect) routing IO requests
+	  from one SoC block to another. This driver provides a way to detect
+	  any bus protocol errors and device not responding situations by
+	  means of an embedded on top of the interconnect errors handler
+	  block (EHB). AXI Interconnect QoS arbitration tuning is currently
+	  unsupported.
+
 config MOXTET
 	tristate "CZ.NIC Turris Mox module configuration bus"
 	depends on SPI_MASTER && OF
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 05f32cd694a4..eaa25d171ebd 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MOXTET)		+= moxtet.o
 # DPAA2 fsl-mc bus
 obj-$(CONFIG_FSL_MC_BUS)	+= fsl-mc/
 
+obj-$(CONFIG_BT1_AXI)		+= bt1-axi.o
 obj-$(CONFIG_IMX_WEIM)		+= imx-weim.o
 obj-$(CONFIG_MIPS_CDMM)		+= mips_cdmm.o
 obj-$(CONFIG_MVEBU_MBUS) 	+= mvebu-mbus.o
diff --git a/drivers/bus/bt1-axi.c b/drivers/bus/bt1-axi.c
new file mode 100644
index 000000000000..a52a5b9bbaf5
--- /dev/null
+++ b/drivers/bus/bt1-axi.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 AXI-bus driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/atomic.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/interrupt.h>
+#include <linux/nmi.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/sysfs.h>
+
+#define BT1_AXI_WERRL			0x110
+#define BT1_AXI_WERRH			0x114
+#define BT1_AXI_WERRH_TYPE		BIT(23)
+#define BT1_AXI_WERRH_ADDR_FLD		24
+#define BT1_AXI_WERRH_ADDR_MASK		GENMASK(31, BT1_AXI_WERRH_ADDR_FLD)
+
+/*
+ * struct bt1_axi - Baikal-T1 AXI-bus private data
+ * @dev: Pointer to the device structure.
+ * @qos_regs: AXI Interconnect QoS tuning registers.
+ * @sys_regs: Baikal-T1 System Controller registers map.
+ * @irq: Errors IRQ number.
+ * @aclk: AXI reference clock.
+ * @arst: AXI Interconnect reset line.
+ * @count: Number of errors detected.
+ */
+struct bt1_axi {
+	struct device *dev;
+
+	void __iomem *qos_regs;
+	struct regmap *sys_regs;
+	int irq;
+
+	struct clk *aclk;
+
+	struct reset_control *arst;
+
+	atomic_t count;
+};
+
+static irqreturn_t bt1_axi_isr(int irq, void *data)
+{
+	struct bt1_axi *axi = data;
+	u32 low = 0, high = 0;
+
+	regmap_read(axi->sys_regs, BT1_AXI_WERRL, &low);
+	regmap_read(axi->sys_regs, BT1_AXI_WERRH, &high);
+
+	dev_crit_ratelimited(axi->dev,
+		"AXI-bus fault %d: %s at 0x%x%08x\n",
+		atomic_inc_return(&axi->count),
+		high & BT1_AXI_WERRH_TYPE ? "no slave" : "slave protocol error",
+		high, low);
+
+	/*
+	 * Print backtrace on each CPU. This might be pointless if the fault
+	 * has happened on the same CPU as the IRQ handler is executed or
+	 * the other core proceeded further execution despite the error.
+	 * But if it's not, by looking at the trace we would get straight to
+	 * the cause of the problem.
+	 */
+	trigger_all_cpu_backtrace();
+
+	return IRQ_HANDLED;
+}
+
+static void bt1_axi_clear_data(void *data)
+{
+	struct bt1_axi *axi = data;
+	struct platform_device *pdev = to_platform_device(axi->dev);
+
+	platform_set_drvdata(pdev, NULL);
+}
+
+static struct bt1_axi *bt1_axi_create_data(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct bt1_axi *axi;
+	int ret;
+
+	axi = devm_kzalloc(dev, sizeof(*axi), GFP_KERNEL);
+	if (!axi)
+		return ERR_PTR(-ENOMEM);
+
+	ret = devm_add_action(dev, bt1_axi_clear_data, axi);
+	if (ret) {
+		dev_err(dev, "Can't add AXI EHB data clear action\n");
+		return ERR_PTR(ret);
+	}
+
+	axi->dev = dev;
+	atomic_set(&axi->count, 0);
+	platform_set_drvdata(pdev, axi);
+
+	return axi;
+}
+
+static int bt1_axi_request_regs(struct bt1_axi *axi)
+{
+	struct platform_device *pdev = to_platform_device(axi->dev);
+	struct device *dev = axi->dev;
+
+	axi->sys_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
+	if (IS_ERR(axi->sys_regs)) {
+		dev_err(dev, "Couldn't find syscon registers\n");
+		return PTR_ERR(axi->sys_regs);
+	}
+
+	axi->qos_regs = devm_platform_ioremap_resource_byname(pdev, "qos");
+	if (IS_ERR(axi->qos_regs)) {
+		dev_err(dev, "Couldn't map AXI-bus QoS registers\n");
+		return PTR_ERR(axi->qos_regs);
+	}
+
+	return 0;
+}
+
+static int bt1_axi_request_rst(struct bt1_axi *axi)
+{
+	int ret;
+
+	axi->arst = devm_reset_control_get_optional_exclusive(axi->dev, "arst");
+	if (IS_ERR(axi->arst)) {
+		dev_warn(axi->dev, "Couldn't get reset control line\n");
+		return PTR_ERR(axi->arst);
+	}
+
+	ret = reset_control_deassert(axi->arst);
+	if (ret)
+		dev_err(axi->dev, "Failed to deassert the reset line\n");
+
+	return ret;
+}
+
+static void bt1_axi_disable_clk(void *data)
+{
+	struct bt1_axi *axi = data;
+
+	clk_disable_unprepare(axi->aclk);
+}
+
+static int bt1_axi_request_clk(struct bt1_axi *axi)
+{
+	int ret;
+
+	axi->aclk = devm_clk_get(axi->dev, "aclk");
+	if (IS_ERR(axi->aclk)) {
+		dev_err(axi->dev, "Couldn't get AXI Interconnect clock\n");
+		return PTR_ERR(axi->aclk);
+	}
+
+	ret = clk_prepare_enable(axi->aclk);
+	if (ret) {
+		dev_err(axi->dev, "Couldn't enable the AXI clock\n");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(axi->dev, bt1_axi_disable_clk, axi);
+	if (ret) {
+		dev_err(axi->dev, "Can't add AXI clock disable action\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int bt1_axi_request_irq(struct bt1_axi *axi)
+{
+	struct platform_device *pdev = to_platform_device(axi->dev);
+	int ret;
+
+	axi->irq = platform_get_irq(pdev, 0);
+	if (axi->irq < 0)
+		return axi->irq;
+
+	ret = devm_request_irq(axi->dev, axi->irq, bt1_axi_isr, IRQF_SHARED,
+			       "bt1-axi", axi);
+	if (ret) {
+		dev_err(axi->dev, "Couldn't request AXI EHB IRQ\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static ssize_t count_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	struct bt1_axi *axi = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&axi->count));
+}
+static DEVICE_ATTR_RO(count);
+
+static int inject_error_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "Error injection: bus unaligned\n");
+}
+
+static int inject_error_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *data, size_t count)
+{
+	struct bt1_axi *axi = dev_get_drvdata(dev);
+
+	/*
+	 * Performing unaligned read from the memory will cause the CM2 bus
+	 * error while unaligned writing - the AXI bus write error handled
+	 * by this driver.
+	 */
+	if (!strncmp(data, "bus", 3))
+		readb(axi->qos_regs);
+	else if (!strncmp(data, "unaligned", 9))
+		writeb(0, axi->qos_regs);
+	else
+		return -EINVAL;
+
+	return count;
+}
+static DEVICE_ATTR_RW(inject_error);
+
+static struct attribute *bt1_axi_sysfs_attrs[] = {
+	&dev_attr_count.attr,
+	&dev_attr_inject_error.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(bt1_axi_sysfs);
+
+static void bt1_axi_remove_sysfs(void *data)
+{
+	struct bt1_axi *axi = data;
+
+	device_remove_groups(axi->dev, bt1_axi_sysfs_groups);
+}
+
+static int bt1_axi_init_sysfs(struct bt1_axi *axi)
+{
+	int ret;
+
+	ret = device_add_groups(axi->dev, bt1_axi_sysfs_groups);
+	if (ret) {
+		dev_err(axi->dev, "Failed to add sysfs files group\n");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(axi->dev, bt1_axi_remove_sysfs, axi);
+	if (ret)
+		dev_err(axi->dev, "Can't add AXI EHB sysfs remove action\n");
+
+	return ret;
+}
+
+static int bt1_axi_probe(struct platform_device *pdev)
+{
+	struct bt1_axi *axi;
+	int ret;
+
+	axi = bt1_axi_create_data(pdev);
+	if (IS_ERR(axi))
+		return PTR_ERR(axi);
+
+	ret = bt1_axi_request_regs(axi);
+	if (ret)
+		return ret;
+
+	ret = bt1_axi_request_rst(axi);
+	if (ret)
+		return ret;
+
+	ret = bt1_axi_request_clk(axi);
+	if (ret)
+		return ret;
+
+	ret = bt1_axi_request_irq(axi);
+	if (ret)
+		return ret;
+
+	ret = bt1_axi_init_sysfs(axi);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct of_device_id bt1_axi_of_match[] = {
+	{ .compatible = "baikal,bt1-axi" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, bt1_axi_of_match);
+
+static struct platform_driver bt1_axi_driver = {
+	.probe = bt1_axi_probe,
+	.driver = {
+		.name = "bt1-axi",
+		.of_match_table = bt1_axi_of_match
+	}
+};
+module_platform_driver(bt1_axi_driver);
+
+MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 AXI-bus driver");
+MODULE_LICENSE("GPL v2");
-- 
2.26.2


^ permalink raw reply related

* [PATCH v3 5/6] bus: Add Baikal-T1 APB-bus driver
From: Serge Semin @ 2020-05-26 12:59 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Arnd Bergmann
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
	Olof Johansson, Rob Herring, linux-mips, soc, devicetree,
	linux-kernel
In-Reply-To: <20200526125928.17096-1-Sergey.Semin@baikalelectronics.ru>

Baikal-T1 AXI-APB bridge is used to access the SoC subsystem CSRs.
IO requests are routed to this bus by means of the DW AMBA 3 AXI
Interconnect. In case if an attempted APB transaction stays with no
response for a pre-defined time an interrupt occurs and the bus gets
freed for a next operation. This driver provides the interrupt handler
to detect the erroneous address, prints an error message about the
address fault, updates an errors counter. The counter and the APB-bus
operations timeout can be accessed via corresponding sysfs nodes.
A dedicated sysfs-node can be also used to artificially cause the
bus errors described above.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: soc@kernel.org
Cc: devicetree@vger.kernel.org

---

Changelog v2:
- Fix commit message and Kconfig help text spelling.
- Move driver from soc to the bus subsystem.
- Convert a simple EHB driver to the Baikal-T1 AXI-bus one.
- Convert registers MMIO to the regmap.
- Add reset line support.
- Remove probe-status info string printout.
- Since the driver depends on the OF config we can remove of_match_ptr()
  macro utilization.
- Don't print error-message if no platform IRQ found. Just return an error.

Changelog v3:
- Discard CONFIG_OF dependency since there is none at compile-time.
---
 drivers/bus/Kconfig   |  15 ++
 drivers/bus/Makefile  |   1 +
 drivers/bus/bt1-apb.c | 421 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 437 insertions(+)
 create mode 100644 drivers/bus/bt1-apb.c

diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 1080dd26e268..030f0e59f193 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -29,6 +29,21 @@ config BRCMSTB_GISB_ARB
 	  arbiter. This driver provides timeout and target abort error handling
 	  and internal bus master decoding.
 
+config BT1_APB
+	tristate "Baikal-T1 APB-bus driver"
+	depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+	select REGMAP_MMIO
+	help
+	  Baikal-T1 AXI-APB bridge is used to access the SoC subsystem CSRs.
+	  IO requests are routed to this bus by means of the DW AMBA 3 AXI
+	  Interconnect. In case of any APB protocol collisions, slave device
+	  not responding on timeout an IRQ is raised with an erroneous address
+	  reported to the APB terminator (APB Errors Handler Block). This
+	  driver provides the interrupt handler to detect the erroneous
+	  address, prints an error message about the address fault, updates an
+	  errors counter. The counter and the APB-bus operations timeout can be
+	  accessed via corresponding sysfs nodes.
+
 config BT1_AXI
 	tristate "Baikal-T1 AXI-bus driver"
 	depends on MIPS_BAIKAL_T1 || COMPILE_TEST
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index eaa25d171ebd..08ccbfaf7705 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MOXTET)		+= moxtet.o
 # DPAA2 fsl-mc bus
 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_MIPS_CDMM)		+= mips_cdmm.o
diff --git a/drivers/bus/bt1-apb.c b/drivers/bus/bt1-apb.c
new file mode 100644
index 000000000000..295cfa83aa23
--- /dev/null
+++ b/drivers/bus/bt1-apb.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 APB-bus driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/atomic.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/nmi.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/time64.h>
+#include <linux/clk.h>
+#include <linux/sysfs.h>
+
+#define APB_EHB_ISR			0x00
+#define APB_EHB_ISR_PENDING		BIT(0)
+#define APB_EHB_ISR_MASK		BIT(1)
+#define APB_EHB_ADDR			0x04
+#define APB_EHB_TIMEOUT			0x08
+
+#define APB_EHB_TIMEOUT_MIN		0x000003FFU
+#define APB_EHB_TIMEOUT_MAX		0xFFFFFFFFU
+
+/*
+ * struct bt1_apb - Baikal-T1 APB EHB private data
+ * @dev: Pointer to the device structure.
+ * @regs: APB EHB registers map.
+ * @res: No-device error injection memory region.
+ * @irq: Errors IRQ number.
+ * @rate: APB-bus reference clock rate.
+ * @pclk: APB-reference clock.
+ * @prst: APB domain reset line.
+ * @count: Number of errors detected.
+ */
+struct bt1_apb {
+	struct device *dev;
+
+	struct regmap *regs;
+	void __iomem *res;
+	int irq;
+
+	unsigned long rate;
+	struct clk *pclk;
+
+	struct reset_control *prst;
+
+	atomic_t count;
+};
+
+static const struct regmap_config bt1_apb_regmap_cfg = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = APB_EHB_TIMEOUT,
+	.fast_io = true
+};
+
+static inline unsigned long bt1_apb_n_to_timeout_us(struct bt1_apb *apb, u32 n)
+{
+	u64 timeout = (u64)n * USEC_PER_SEC;
+
+	do_div(timeout, apb->rate);
+
+	return timeout;
+
+}
+
+static inline unsigned long bt1_apb_timeout_to_n_us(struct bt1_apb *apb,
+						    unsigned long timeout)
+{
+	u64 n = (u64)timeout * apb->rate;
+
+	do_div(n, USEC_PER_SEC);
+
+	return n;
+
+}
+
+static irqreturn_t bt1_apb_isr(int irq, void *data)
+{
+	struct bt1_apb *apb = data;
+	u32 addr = 0;
+
+	regmap_read(apb->regs, APB_EHB_ADDR, &addr);
+
+	dev_crit_ratelimited(apb->dev,
+		"APB-bus fault %d: Slave access timeout at 0x%08x\n",
+		atomic_inc_return(&apb->count),
+		addr);
+
+	/*
+	 * Print backtrace on each CPU. This might be pointless if the fault
+	 * has happened on the same CPU as the IRQ handler is executed or
+	 * the other core proceeded further execution despite the error.
+	 * But if it's not, by looking at the trace we would get straight to
+	 * the cause of the problem.
+	 */
+	trigger_all_cpu_backtrace();
+
+	regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING, 0);
+
+	return IRQ_HANDLED;
+}
+
+static void bt1_apb_clear_data(void *data)
+{
+	struct bt1_apb *apb = data;
+	struct platform_device *pdev = to_platform_device(apb->dev);
+
+	platform_set_drvdata(pdev, NULL);
+}
+
+static struct bt1_apb *bt1_apb_create_data(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct bt1_apb *apb;
+	int ret;
+
+	apb = devm_kzalloc(dev, sizeof(*apb), GFP_KERNEL);
+	if (!apb)
+		return ERR_PTR(-ENOMEM);
+
+	ret = devm_add_action(dev, bt1_apb_clear_data, apb);
+	if (ret) {
+		dev_err(dev, "Can't add APB EHB data clear action\n");
+		return ERR_PTR(ret);
+	}
+
+	apb->dev = dev;
+	atomic_set(&apb->count, 0);
+	platform_set_drvdata(pdev, apb);
+
+	return apb;
+}
+
+static int bt1_apb_request_regs(struct bt1_apb *apb)
+{
+	struct platform_device *pdev = to_platform_device(apb->dev);
+	void __iomem *regs;
+
+	regs = devm_platform_ioremap_resource_byname(pdev, "ehb");
+	if (IS_ERR(regs)) {
+		dev_err(apb->dev, "Couldn't map APB EHB registers\n");
+		return PTR_ERR(regs);
+	}
+
+	apb->regs = devm_regmap_init_mmio(apb->dev, regs, &bt1_apb_regmap_cfg);
+	if (IS_ERR(apb->regs)) {
+		dev_err(apb->dev, "Couldn't create APB EHB regmap\n");
+		return PTR_ERR(apb->regs);
+	}
+
+	apb->res = devm_platform_ioremap_resource_byname(pdev, "nodev");
+	if (IS_ERR(apb->res)) {
+		dev_err(apb->dev, "Couldn't map reserved region\n");
+		return PTR_ERR(apb->res);
+	}
+
+	return 0;
+}
+
+static int bt1_apb_request_rst(struct bt1_apb *apb)
+{
+	int ret;
+
+	apb->prst = devm_reset_control_get_optional_exclusive(apb->dev, "prst");
+	if (IS_ERR(apb->prst)) {
+		dev_warn(apb->dev, "Couldn't get reset control line\n");
+		return PTR_ERR(apb->prst);
+	}
+
+	ret = reset_control_deassert(apb->prst);
+	if (ret)
+		dev_err(apb->dev, "Failed to deassert the reset line\n");
+
+	return ret;
+}
+
+static void bt1_apb_disable_clk(void *data)
+{
+	struct bt1_apb *apb = data;
+
+	clk_disable_unprepare(apb->pclk);
+}
+
+static int bt1_apb_request_clk(struct bt1_apb *apb)
+{
+	int ret;
+
+	apb->pclk = devm_clk_get(apb->dev, "pclk");
+	if (IS_ERR(apb->pclk)) {
+		dev_err(apb->dev, "Couldn't get APB clock descriptor\n");
+		return PTR_ERR(apb->pclk);
+	}
+
+	ret = clk_prepare_enable(apb->pclk);
+	if (ret) {
+		dev_err(apb->dev, "Couldn't enable the APB clock\n");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(apb->dev, bt1_apb_disable_clk, apb);
+	if (ret) {
+		dev_err(apb->dev, "Can't add APB EHB clocks disable action\n");
+		return ret;
+	}
+
+	apb->rate = clk_get_rate(apb->pclk);
+	if (!apb->rate) {
+		dev_err(apb->dev, "Invalid clock rate\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void bt1_apb_clear_irq(void *data)
+{
+	struct bt1_apb *apb = data;
+
+	regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_MASK, 0);
+}
+
+static int bt1_apb_request_irq(struct bt1_apb *apb)
+{
+	struct platform_device *pdev = to_platform_device(apb->dev);
+	int ret;
+
+	apb->irq = platform_get_irq(pdev, 0);
+	if (apb->irq < 0)
+		return apb->irq;
+
+	ret = devm_request_irq(apb->dev, apb->irq, bt1_apb_isr, IRQF_SHARED,
+			       "bt1-apb", apb);
+	if (ret) {
+		dev_err(apb->dev, "Couldn't request APB EHB IRQ\n");
+		return ret;
+	}
+
+	ret = devm_add_action(apb->dev, bt1_apb_clear_irq, apb);
+	if (ret) {
+		dev_err(apb->dev, "Can't add APB EHB IRQs clear action\n");
+		return ret;
+	}
+
+	/* Unmask IRQ and clear it' pending flag. */
+	regmap_update_bits(apb->regs, APB_EHB_ISR,
+			   APB_EHB_ISR_PENDING | APB_EHB_ISR_MASK,
+			   APB_EHB_ISR_MASK);
+
+	return 0;
+}
+
+static ssize_t count_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct bt1_apb *apb = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&apb->count));
+}
+static DEVICE_ATTR_RO(count);
+
+static ssize_t timeout_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct bt1_apb *apb = dev_get_drvdata(dev);
+	unsigned long timeout;
+	int ret;
+	u32 n;
+
+	ret = regmap_read(apb->regs, APB_EHB_TIMEOUT, &n);
+	if (ret)
+		return ret;
+
+	timeout = bt1_apb_n_to_timeout_us(apb, n);
+
+	return scnprintf(buf, PAGE_SIZE, "%lu\n", timeout);
+}
+
+static ssize_t timeout_store(struct device *dev,
+			     struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct bt1_apb *apb = dev_get_drvdata(dev);
+	unsigned long timeout;
+	int ret;
+	u32 n;
+
+	if (kstrtoul(buf, 0, &timeout) < 0)
+		return -EINVAL;
+
+	n = bt1_apb_timeout_to_n_us(apb, timeout);
+	n = clamp(n, APB_EHB_TIMEOUT_MIN, APB_EHB_TIMEOUT_MAX);
+
+	ret = regmap_write(apb->regs, APB_EHB_TIMEOUT, n);
+
+	return ret ?: count;
+}
+static DEVICE_ATTR_RW(timeout);
+
+static int inject_error_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "Error injection: nodev irq\n");
+}
+
+static int inject_error_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *data, size_t count)
+{
+	struct bt1_apb *apb = dev_get_drvdata(dev);
+
+	/*
+	 * Either dummy read from the unmapped address in the APB IO area
+	 * or manually set the IRQ status.
+	 */
+	if (!strncmp(data, "nodev", 5))
+		readl(apb->res);
+	else if (!strncmp(data, "irq", 3))
+		regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING,
+				   APB_EHB_ISR_PENDING);
+	else
+		return -EINVAL;
+
+	return count;
+}
+static DEVICE_ATTR_RW(inject_error);
+
+static struct attribute *bt1_apb_sysfs_attrs[] = {
+	&dev_attr_count.attr,
+	&dev_attr_timeout.attr,
+	&dev_attr_inject_error.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(bt1_apb_sysfs);
+
+static void bt1_apb_remove_sysfs(void *data)
+{
+	struct bt1_apb *apb = data;
+
+	device_remove_groups(apb->dev, bt1_apb_sysfs_groups);
+}
+
+static int bt1_apb_init_sysfs(struct bt1_apb *apb)
+{
+	int ret;
+
+	ret = device_add_groups(apb->dev, bt1_apb_sysfs_groups);
+	if (ret) {
+		dev_err(apb->dev, "Failed to create EHB APB sysfs nodes\n");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(apb->dev, bt1_apb_remove_sysfs, apb);
+	if (ret)
+		dev_err(apb->dev, "Can't add APB EHB sysfs remove action\n");
+
+	return ret;
+}
+
+static int bt1_apb_probe(struct platform_device *pdev)
+{
+	struct bt1_apb *apb;
+	int ret;
+
+	apb = bt1_apb_create_data(pdev);
+	if (IS_ERR(apb))
+		return PTR_ERR(apb);
+
+	ret = bt1_apb_request_regs(apb);
+	if (ret)
+		return ret;
+
+	ret = bt1_apb_request_rst(apb);
+	if (ret)
+		return ret;
+
+	ret = bt1_apb_request_clk(apb);
+	if (ret)
+		return ret;
+
+	ret = bt1_apb_request_irq(apb);
+	if (ret)
+		return ret;
+
+	ret = bt1_apb_init_sysfs(apb);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct of_device_id bt1_apb_of_match[] = {
+	{ .compatible = "baikal,bt1-apb" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, bt1_apb_of_match);
+
+static struct platform_driver bt1_apb_driver = {
+	.probe = bt1_apb_probe,
+	.driver = {
+		.name = "bt1-apb",
+		.of_match_table = bt1_apb_of_match
+	}
+};
+module_platform_driver(bt1_apb_driver);
+
+MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 APB-bus driver");
+MODULE_LICENSE("GPL v2");
-- 
2.26.2


^ permalink raw reply related

* [PATCH v3 6/6] memory: Add Baikal-T1 L2-cache Control Block driver
From: Serge Semin @ 2020-05-26 12:59 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Arnd Bergmann
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
	Olof Johansson, Rob Herring, linux-mips, soc, devicetree,
	linux-kernel
In-Reply-To: <20200526125928.17096-1-Sergey.Semin@baikalelectronics.ru>

Baikal-T1 SoC provides a way to tune the MIPS P5600 CM2 L2-cache
performance up. It can be done by changing the L2-RAM Data/Tag/WS
latencies in a dedicated register exposed by the system controller.
The driver added by this commit provides a dts properties-based and
sysfs-based interface for it. The device DT node is supposed to be a
child of Baikal-T1 System Controller node.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: soc@kernel.org
Cc: devicetree@vger.kernel.org

---

Changelog v2:
- Move driver from soc to memory subsystem.
- Get regmap from the parental syscon DT node.
- Use generic FIELD_{GET,PREP} macro.
- Remove probe-status info string printout.
- Since the driver depends on the OF config we can remove of_match_ptr()
  macro utilization.
- Select MFD_SYSCON config.

Changelog v3:
- Discard CONFIG_OF dependency since there is non at compile-time.
- Fix invalid of_property_read_u32() return value test.
---
 drivers/memory/Kconfig      |  11 ++
 drivers/memory/Makefile     |   1 +
 drivers/memory/bt1-l2-ctl.c | 322 ++++++++++++++++++++++++++++++++++++
 3 files changed, 334 insertions(+)
 create mode 100644 drivers/memory/bt1-l2-ctl.c

diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 9bddca292330..04368ee2a809 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -46,6 +46,17 @@ config ATMEL_EBI
 	  tree is used. This bus supports NANDs, external ethernet controller,
 	  SRAMs, ATA devices, etc.
 
+config BT1_L2_CTL
+	bool "Baikal-T1 CM2 L2-RAM Cache Control Block"
+	depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+	select MFD_SYSCON
+	help
+	  Baikal-T1 CPU is based on the MIPS P5600 Warrior IP-core. The CPU
+	  resides Coherency Manager v2 with embedded 1MB L2-cache. It's
+	  possible to tune the L2 cache performance up by setting the data,
+	  tags and way-select latencies of RAM access. This driver provides a
+	  dt properties-based and sysfs interface for it.
+
 config TI_AEMIF
 	tristate "Texas Instruments AEMIF driver"
 	depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 27b493435e61..6d7e3e64ba62 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_ARM_PL172_MPMC)	+= pl172.o
 obj-$(CONFIG_ATMEL_SDRAMC)	+= atmel-sdramc.o
 obj-$(CONFIG_ATMEL_EBI)		+= atmel-ebi.o
 obj-$(CONFIG_ARCH_BRCMSTB)	+= brcmstb_dpfe.o
+obj-$(CONFIG_BT1_L2_CTL)	+= bt1-l2-ctl.o
 obj-$(CONFIG_TI_AEMIF)		+= ti-aemif.o
 obj-$(CONFIG_TI_EMIF)		+= emif.o
 obj-$(CONFIG_OMAP_GPMC)		+= omap-gpmc.o
diff --git a/drivers/memory/bt1-l2-ctl.c b/drivers/memory/bt1-l2-ctl.c
new file mode 100644
index 000000000000..633fea6a4edf
--- /dev/null
+++ b/drivers/memory/bt1-l2-ctl.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 CM2 L2-cache Control Block driver.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/sysfs.h>
+#include <linux/of.h>
+
+#define L2_CTL_REG			0x028
+#define L2_CTL_DATA_STALL_FLD		0
+#define L2_CTL_DATA_STALL_MASK		GENMASK(1, L2_CTL_DATA_STALL_FLD)
+#define L2_CTL_TAG_STALL_FLD		2
+#define L2_CTL_TAG_STALL_MASK		GENMASK(3, L2_CTL_TAG_STALL_FLD)
+#define L2_CTL_WS_STALL_FLD		4
+#define L2_CTL_WS_STALL_MASK		GENMASK(5, L2_CTL_WS_STALL_FLD)
+#define L2_CTL_SET_CLKRATIO		BIT(13)
+#define L2_CTL_CLKRATIO_LOCK		BIT(31)
+
+#define L2_CTL_STALL_MIN		0
+#define L2_CTL_STALL_MAX		3
+#define L2_CTL_STALL_SET_DELAY_US	1
+#define L2_CTL_STALL_SET_TOUT_US	1000
+
+/*
+ * struct l2_ctl - Baikal-T1 L2 Control block private data.
+ * @dev: Pointer to the device structure.
+ * @sys_regs: Baikal-T1 System Controller registers map.
+ */
+struct l2_ctl {
+	struct device *dev;
+
+	struct regmap *sys_regs;
+};
+
+/*
+ * enum l2_ctl_stall - Baikal-T1 L2-cache-RAM stall identifier.
+ * @L2_WSSTALL: Way-select latency.
+ * @L2_TAGSTALL: Tag latency.
+ * @L2_DATASTALL: Data latency.
+ */
+enum l2_ctl_stall {
+	L2_WS_STALL,
+	L2_TAG_STALL,
+	L2_DATA_STALL
+};
+
+/*
+ * struct l2_ctl_device_attribute - Baikal-T1 L2-cache device attribute.
+ * @dev_attr: Actual sysfs device attribute.
+ * @id: L2-cache stall field identifier.
+ */
+struct l2_ctl_device_attribute {
+	struct device_attribute dev_attr;
+	enum l2_ctl_stall id;
+};
+#define to_l2_ctl_dev_attr(_dev_attr) \
+	container_of(_dev_attr, struct l2_ctl_device_attribute, dev_attr)
+
+#define L2_CTL_ATTR_RW(_name, _prefix, _id) \
+	struct l2_ctl_device_attribute l2_ctl_attr_##_name = \
+		{ __ATTR(_name, 0644, _prefix##_show, _prefix##_store),	_id }
+
+static int l2_ctl_get_latency(struct l2_ctl *l2, enum l2_ctl_stall id, u32 *val)
+{
+	u32 data = 0;
+	int ret;
+
+	ret = regmap_read(l2->sys_regs, L2_CTL_REG, &data);
+	if (ret)
+		return ret;
+
+	switch (id) {
+	case L2_WS_STALL:
+		*val = FIELD_GET(L2_CTL_WS_STALL_MASK, data);
+		break;
+	case L2_TAG_STALL:
+		*val = FIELD_GET(L2_CTL_TAG_STALL_MASK, data);
+		break;
+	case L2_DATA_STALL:
+		*val = FIELD_GET(L2_CTL_DATA_STALL_MASK, data);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int l2_ctl_set_latency(struct l2_ctl *l2, enum l2_ctl_stall id, u32 val)
+{
+	u32 mask = 0, data = 0;
+	int ret;
+
+	val = clamp_val(val, L2_CTL_STALL_MIN, L2_CTL_STALL_MAX);
+
+	switch (id) {
+	case L2_WS_STALL:
+		data = FIELD_PREP(L2_CTL_WS_STALL_MASK, val);
+		mask = L2_CTL_WS_STALL_MASK;
+		break;
+	case L2_TAG_STALL:
+		data = FIELD_PREP(L2_CTL_TAG_STALL_MASK, val);
+		mask = L2_CTL_TAG_STALL_MASK;
+		break;
+	case L2_DATA_STALL:
+		data = FIELD_PREP(L2_CTL_DATA_STALL_MASK, val);
+		mask = L2_CTL_DATA_STALL_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	data |= L2_CTL_SET_CLKRATIO;
+	mask |= L2_CTL_SET_CLKRATIO;
+
+	ret = regmap_update_bits(l2->sys_regs, L2_CTL_REG, mask, data);
+	if (ret)
+		return ret;
+
+	return regmap_read_poll_timeout(l2->sys_regs, L2_CTL_REG, data,
+					data & L2_CTL_CLKRATIO_LOCK,
+					L2_CTL_STALL_SET_DELAY_US,
+					L2_CTL_STALL_SET_TOUT_US);
+}
+
+static void l2_ctl_clear_data(void *data)
+{
+	struct l2_ctl *l2 = data;
+	struct platform_device *pdev = to_platform_device(l2->dev);
+
+	platform_set_drvdata(pdev, NULL);
+}
+
+static struct l2_ctl *l2_ctl_create_data(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct l2_ctl *l2;
+	int ret;
+
+	l2 = devm_kzalloc(dev, sizeof(*l2), GFP_KERNEL);
+	if (!l2)
+		return ERR_PTR(-ENOMEM);
+
+	ret = devm_add_action(dev, l2_ctl_clear_data, l2);
+	if (ret) {
+		dev_err(dev, "Can't add L2 CTL data clear action\n");
+		return ERR_PTR(ret);
+	}
+
+	l2->dev = dev;
+	platform_set_drvdata(pdev, l2);
+
+	return l2;
+}
+
+static int l2_ctl_find_sys_regs(struct l2_ctl *l2)
+{
+	l2->sys_regs = syscon_node_to_regmap(l2->dev->of_node->parent);
+	if (IS_ERR(l2->sys_regs)) {
+		dev_err(l2->dev, "Couldn't get L2 CTL register map\n");
+		return PTR_ERR(l2->sys_regs);
+	}
+
+	return 0;
+}
+
+static int l2_ctl_of_parse_property(struct l2_ctl *l2, enum l2_ctl_stall id,
+				    const char *propname)
+{
+	int ret = 0;
+	u32 data;
+
+	if (!of_property_read_u32(l2->dev->of_node, propname, &data)) {
+		ret = l2_ctl_set_latency(l2, id, data);
+		if (ret)
+			dev_err(l2->dev, "Invalid value of '%s'\n", propname);
+	}
+
+	return ret;
+}
+
+static int l2_ctl_of_parse(struct l2_ctl *l2)
+{
+	int ret;
+
+	ret = l2_ctl_of_parse_property(l2, L2_WS_STALL, "baikal,l2-ws-latency");
+	if (ret)
+		return ret;
+
+	ret = l2_ctl_of_parse_property(l2, L2_TAG_STALL, "baikal,l2-tag-latency");
+	if (ret)
+		return ret;
+
+	return l2_ctl_of_parse_property(l2, L2_DATA_STALL,
+					"baikal,l2-data-latency");
+}
+
+static ssize_t l2_ctl_latency_show(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct l2_ctl_device_attribute *devattr = to_l2_ctl_dev_attr(attr);
+	struct l2_ctl *l2 = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	ret = l2_ctl_get_latency(l2, devattr->id, &data);
+	if (ret)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", data);
+}
+
+static ssize_t l2_ctl_latency_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct l2_ctl_device_attribute *devattr = to_l2_ctl_dev_attr(attr);
+	struct l2_ctl *l2 = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	if (kstrtouint(buf, 0, &data) < 0)
+		return -EINVAL;
+
+	ret = l2_ctl_set_latency(l2, devattr->id, data);
+	if (ret)
+		return ret;
+
+	return count;
+}
+static L2_CTL_ATTR_RW(l2_ws_latency, l2_ctl_latency, L2_WS_STALL);
+static L2_CTL_ATTR_RW(l2_tag_latency, l2_ctl_latency, L2_TAG_STALL);
+static L2_CTL_ATTR_RW(l2_data_latency, l2_ctl_latency, L2_DATA_STALL);
+
+static struct attribute *l2_ctl_sysfs_attrs[] = {
+	&l2_ctl_attr_l2_ws_latency.dev_attr.attr,
+	&l2_ctl_attr_l2_tag_latency.dev_attr.attr,
+	&l2_ctl_attr_l2_data_latency.dev_attr.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(l2_ctl_sysfs);
+
+static void l2_ctl_remove_sysfs(void *data)
+{
+	struct l2_ctl *l2 = data;
+
+	device_remove_groups(l2->dev, l2_ctl_sysfs_groups);
+}
+
+static int l2_ctl_init_sysfs(struct l2_ctl *l2)
+{
+	int ret;
+
+	ret = device_add_groups(l2->dev, l2_ctl_sysfs_groups);
+	if (ret) {
+		dev_err(l2->dev, "Failed to create L2 CTL sysfs nodes\n");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(l2->dev, l2_ctl_remove_sysfs, l2);
+	if (ret)
+		dev_err(l2->dev, "Can't add L2 CTL sysfs remove action\n");
+
+	return ret;
+}
+
+static int l2_ctl_probe(struct platform_device *pdev)
+{
+	struct l2_ctl *l2;
+	int ret;
+
+	l2 = l2_ctl_create_data(pdev);
+	if (IS_ERR(l2))
+		return PTR_ERR(l2);
+
+	ret = l2_ctl_find_sys_regs(l2);
+	if (ret)
+		return ret;
+
+	ret = l2_ctl_of_parse(l2);
+	if (ret)
+		return ret;
+
+	ret = l2_ctl_init_sysfs(l2);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct of_device_id l2_ctl_of_match[] = {
+	{ .compatible = "baikal,bt1-l2-ctl" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, l2_ctl_of_match);
+
+static struct platform_driver l2_ctl_driver = {
+	.probe = l2_ctl_probe,
+	.driver = {
+		.name = "bt1-l2-ctl",
+		.of_match_table = l2_ctl_of_match
+	}
+};
+module_platform_driver(l2_ctl_driver);
+
+MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 L2-cache driver");
+MODULE_LICENSE("GPL v2");
-- 
2.26.2


^ permalink raw reply related

* [PATCH v3 0/6] bus/memory: Add Baikal-T1 SoC APB/AXI/L2 drivers
From: Serge Semin @ 2020-05-26 12:59 UTC (permalink / raw)
  To: Thomas Bogendoerfer, Greg Kroah-Hartman, Arnd Bergmann
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Maxim Kaurkin,
	Pavel Parkhomenko, Ramil Zaripov, Ekaterina Skachko, Vadim Vlasov,
	Alexey Kolotnikov, Paul Burton, Rob Herring, Olof Johansson,
	linux-mips, soc, devicetree, linux-kernel

Baikal-T1 SoC CPU is based on two MIPS Warrior P5600 cores. Their main
memory Non-Coherent IO interface is connected to the OCP2AXI bridge, which
in turn is then connected to the DW AMBA 3 AXI Interconnect (so called
Main Interconnect) with nine masters and four slaves ports. Main
Interconnect is responsible for the AXI-bus traffic arbitration (QoS) and
its routing from one component to another. In addition there is a Errors
Handler Block (EHB) accesible by means of the Baikal-T1 SoC System
Controller responsible to detect AXI protocol errors and device not
responding situations built on top the interconnect. Baikal-T1 AXI-bus
driver included in this patchset will be responsible for working with that
functionality, though currently it doesn't support QoS tuning. Instead
it's capable of detecting the error events, reporting an info about them
to the system log, injecting artificial errors to test the driver
functionality. Since AXI Interconnect doesn't provide a way to find out
which devices are connected to it, so its DT node is supposed to be
compatible with "simple-bus" driver, while sub-nodes shall represent the
masters attached to the bus.

One of the AXI Interconnect slaves is an AXI-APB bridge used to access the
Baikal-T1 SoC subsystems CSRs. MMIO request from CPU and DMAC masters are
routed there if they are detected to be within [0x08000000 0x1FFFFFFF]
range of the physical memory. In case if an attempted APB transaction
stays with no response for a pre-defined time it will be detected by the
APB-bus Errors Handler Block (EHB), which will raise an interrupt, then
the bus gets freed for a next operation. The APB-bus driver provides the
interrupt handler to detect the erroneous address, update an errors
counter and prints an error message about the faulty address. The counter
and the APB-bus operations timeout can be accessed via corresponding sysfs
nodes. A dedicated sysfs-node can be also used to artificially cause the
bus errors described above. Since APB-bus is a platform bus, it doesn't
provide a way to detect slave devices connected to it, so similarly to the
AXI-bus it's also supposed to be compatible with "simple-bus" driver.

Aside from PCIe/SATA/DDR/I2C/EHB/CPU/reboot specific settings the
Baikal-T1 System Controller provides a MIPS P5600 CM2 L2-cache tuning
block. It is responsible for the setting up the Tag/Data/WS L2-to-RAM
latencies. The last small patch in this patchset provides a driver and
DT-schema-based binding for the described device. So that the latencies
can be tuned up by means of dedicated DT properties and sysfs nodes.

This patchset is rebased and tested on the mainline Linux kernel 5.7-rc4:
0e698dfa2822 ("Linux 5.7-rc4")
tag: v5.7-rc4

Changelog v2 (AXI/APB bus):
- Assign dual GPL/BSD licenses to the bindings.
- Use single lined copyright headers in the bindings.
- Replace "additionalProperties: false" property with
  "unevaluatedProperties: false" in the bindings.
- Don't use a multi-arg clock phandle reference in DT binding examples.
  Thus remove includes from there.
- Fix some commit message and Kconfig help text spelling.
- Move drivers from soc to the bus subsystem.
- Convert a simple EHB drivers to the Baikal-T1 AXI and APB bus ones.
- Convert APB bus driver to using regmap MMIO API.
- Use syscon regmap to access the AXI-bus erroneous address.
- Add reset line support.
- Add Main Interconnect clock support to the AXI-bus driver.
- Remove probe-status info string printout.
- Discard of_match_ptr() macro utilization.
- Don't print error-message if no platform IRQ found. Just return an error.
- Use generic FIELD_{GET,PREP} macros instead of handwritten ones in the
  AXI-bus driver.

Changelog v2 (l2 driver):
- Fix some commit message and Kconfig help text spelling.
- Move the driver to the memory subsystem.
- Assign dual GPL/BSD license to the DT binding.
- Use single lined copyright header in the binding.
- Discard reg property and syscon compatible string.
- Move "allOf" restrictions to the root level of the properties.
- The DT node is supposed to be a child of the Baikal-T1 system controller
  node. So regmap will be fetched from there.
- Use generic FIELD_{GET,PREP} macro.
- Remove probe-status info string printout.
- Since the driver depends on the OF config we can remove of_match_ptr()
  macro utilization.

Changelog v3:
- Combine l2 and AXI/APB bus patches in a single patchset.
- Retrieve AXI-bus QoS registers by resource name "qos".
- Discard CONFIG_OF dependency since there is none at compile-time.
- Add syscon EHB registers range to the AXI-bus reg property as optional
  entry.
- Fix invalid of_property_read_u32() return value test in the l2-ctl
  driver.
- Get the reg property back into the l2-ctl DT bindings even though the
  driver is using the parental syscon regmap.
- The l2-ctl DT schema will live separately from the system controller,
  but the corresponding sub-node of the later DT schema will $ref this one.
- Set non-default latencies in the l2-ctl DT example.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Maxim Kaurkin <Maxim.Kaurkin@baikalelectronics.ru>
Cc: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
Cc: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Cc: Ekaterina Skachko <Ekaterina.Skachko@baikalelectronics.ru>
Cc: Vadim Vlasov <V.Vlasov@baikalelectronics.ru>
Cc: Alexey Kolotnikov <Alexey.Kolotnikov@baikalelectronics.ru>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: linux-mips@vger.kernel.org
Cc: soc@kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

Serge Semin (6):
  dt-bindings: bus: Add Baikal-T1 AXI-bus binding
  dt-bindings: bus: Add Baikal-T1 APB-bus binding
  dt-bindings: memory: Add Baikal-T1 L2-cache Control Block binding
  bus: Add Baikal-T1 AXI-bus driver
  bus: Add Baikal-T1 APB-bus driver
  memory: Add Baikal-T1 L2-cache Control Block driver

 .../bindings/bus/baikal,bt1-apb.yaml          |  90 ++++
 .../bindings/bus/baikal,bt1-axi.yaml          | 107 +++++
 .../memory-controllers/baikal,bt1-l2-ctl.yaml |  63 +++
 drivers/bus/Kconfig                           |  30 ++
 drivers/bus/Makefile                          |   2 +
 drivers/bus/bt1-apb.c                         | 421 ++++++++++++++++++
 drivers/bus/bt1-axi.c                         | 318 +++++++++++++
 drivers/memory/Kconfig                        |  11 +
 drivers/memory/Makefile                       |   1 +
 drivers/memory/bt1-l2-ctl.c                   | 322 ++++++++++++++
 10 files changed, 1365 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/bus/baikal,bt1-apb.yaml
 create mode 100644 Documentation/devicetree/bindings/bus/baikal,bt1-axi.yaml
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/baikal,bt1-l2-ctl.yaml
 create mode 100644 drivers/bus/bt1-apb.c
 create mode 100644 drivers/bus/bt1-axi.c
 create mode 100644 drivers/memory/bt1-l2-ctl.c

-- 
2.26.2


^ permalink raw reply

* [PATCH V7 7/7] arm64: dts: sc7180: Add interconnect for QUP and QSPI
From: Akash Asthana @ 2020-05-26 12:54 UTC (permalink / raw)
  To: gregkh, agross, bjorn.andersson, wsa, broonie, mark.rutland,
	robh+dt
  Cc: linux-i2c, linux-spi, devicetree, swboyd, mgautam, linux-arm-msm,
	linux-serial, mka, dianders, msavaliy, evgreen, Akash Asthana
In-Reply-To: <1590497690-29035-1-git-send-email-akashast@codeaurora.org>

Add interconnect ports for GENI QUPs and QSPI to set bus capabilities.

Signed-off-by: Akash Asthana <akashast@codeaurora.org>
---
Changes in V2:
 - As per Bjorn's comment, ignoring 80 char limit in defining interconnects
   paths.

Changes in V3:
 - No change.

Change in V4:
 - No change.

Changes in V5:
 - No change.

Chnages in V6:
 - No change.

Changes in V7:
 - No change.

 arch/arm64/boot/dts/qcom/sc7180.dtsi | 127 +++++++++++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sc7180.dtsi b/arch/arm64/boot/dts/qcom/sc7180.dtsi
index 31b9217..3624bde 100644
--- a/arch/arm64/boot/dts/qcom/sc7180.dtsi
+++ b/arch/arm64/boot/dts/qcom/sc7180.dtsi
@@ -547,6 +547,8 @@
 			#size-cells = <2>;
 			ranges;
 			iommus = <&apps_smmu 0x43 0x0>;
+			interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>;
+			interconnect-names = "qup-core";
 			status = "disabled";
 
 			i2c0: i2c@880000 {
@@ -559,6 +561,11 @@
 				interrupts = <GIC_SPI 601 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>,
+						<&aggre1_noc MASTER_QUP_0 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -572,6 +579,9 @@
 				interrupts = <GIC_SPI 601 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -583,6 +593,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart0_default>;
 				interrupts = <GIC_SPI 601 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -596,6 +609,11 @@
 				interrupts = <GIC_SPI 602 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>,
+						<&aggre1_noc MASTER_QUP_0 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -609,6 +627,9 @@
 				interrupts = <GIC_SPI 602 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -620,6 +641,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart1_default>;
 				interrupts = <GIC_SPI 602 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -633,6 +657,11 @@
 				interrupts = <GIC_SPI 603 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>,
+						<&aggre1_noc MASTER_QUP_0 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -644,6 +673,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart2_default>;
 				interrupts = <GIC_SPI 603 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -657,6 +689,11 @@
 				interrupts = <GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>,
+						<&aggre1_noc MASTER_QUP_0 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -670,6 +707,9 @@
 				interrupts = <GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -681,6 +721,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart3_default>;
 				interrupts = <GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -694,6 +737,11 @@
 				interrupts = <GIC_SPI 605 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>,
+						<&aggre1_noc MASTER_QUP_0 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -705,6 +753,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart4_default>;
 				interrupts = <GIC_SPI 605 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -718,6 +769,11 @@
 				interrupts = <GIC_SPI 606 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>,
+						<&aggre1_noc MASTER_QUP_0 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -731,6 +787,9 @@
 				interrupts = <GIC_SPI 606 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -742,6 +801,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart5_default>;
 				interrupts = <GIC_SPI 606 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_0 &qup_virt SLAVE_QUP_CORE_0>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_0>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 		};
@@ -756,6 +818,8 @@
 			#size-cells = <2>;
 			ranges;
 			iommus = <&apps_smmu 0x4c3 0x0>;
+			interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>;
+			interconnect-names = "qup-core";
 			status = "disabled";
 
 			i2c6: i2c@a80000 {
@@ -768,6 +832,11 @@
 				interrupts = <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>,
+						<&aggre2_noc MASTER_QUP_1 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -781,6 +850,9 @@
 				interrupts = <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -792,6 +864,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart6_default>;
 				interrupts = <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -805,6 +880,11 @@
 				interrupts = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>,
+						<&aggre2_noc MASTER_QUP_1 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -816,6 +896,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart7_default>;
 				interrupts = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -829,6 +912,11 @@
 				interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>,
+						<&aggre2_noc MASTER_QUP_1 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -842,6 +930,9 @@
 				interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -853,6 +944,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart8_default>;
 				interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -866,6 +960,11 @@
 				interrupts = <GIC_SPI 356 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>,
+						<&aggre2_noc MASTER_QUP_1 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -877,6 +976,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart9_default>;
 				interrupts = <GIC_SPI 356 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -890,6 +992,11 @@
 				interrupts = <GIC_SPI 357 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>,
+						<&aggre2_noc MASTER_QUP_1 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -903,6 +1010,9 @@
 				interrupts = <GIC_SPI 357 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -914,6 +1024,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart10_default>;
 				interrupts = <GIC_SPI 357 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -927,6 +1040,11 @@
 				interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>,
+						<&aggre2_noc MASTER_QUP_1 &mc_virt SLAVE_EBI1>;
+				interconnect-names = "qup-core", "qup-config",
+							"qup-memory";
 				status = "disabled";
 			};
 
@@ -940,6 +1058,9 @@
 				interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 
@@ -951,6 +1072,9 @@
 				pinctrl-names = "default";
 				pinctrl-0 = <&qup_uart11_default>;
 				interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
+				interconnects = <&qup_virt MASTER_QUP_CORE_1 &qup_virt SLAVE_QUP_CORE_1>,
+						<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_QUP_1>;
+				interconnect-names = "qup-core", "qup-config";
 				status = "disabled";
 			};
 		};
@@ -2132,6 +2256,9 @@
 			clocks = <&gcc GCC_QSPI_CNOC_PERIPH_AHB_CLK>,
 				 <&gcc GCC_QSPI_CORE_CLK>;
 			clock-names = "iface", "core";
+			interconnects = <&gem_noc MASTER_APPSS_PROC
+					&config_noc SLAVE_QSPI_0>;
+			interconnect-names = "qspi-config";
 			status = "disabled";
 		};
 
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project

^ permalink raw reply related

* [PATCH V7 6/7] spi: spi-qcom-qspi: Add interconnect support
From: Akash Asthana @ 2020-05-26 12:54 UTC (permalink / raw)
  To: gregkh, agross, bjorn.andersson, wsa, broonie, mark.rutland,
	robh+dt
  Cc: linux-i2c, linux-spi, devicetree, swboyd, mgautam, linux-arm-msm,
	linux-serial, mka, dianders, msavaliy, evgreen, Akash Asthana
In-Reply-To: <1590497690-29035-1-git-send-email-akashast@codeaurora.org>

Get the interconnect paths for QSPI device and vote according to the
current bus speed of the driver.

Signed-off-by: Akash Asthana <akashast@codeaurora.org>
---
Changes in V2:
 - As per Bjorn's comment, introduced and using devm_of_icc_get API for getting
   path handle
 - As per Matthias comment, added error handling for icc_set_bw call

Changes in V3:
 - No Change.

Changes in V4:
 - As per Mark's comment move peak_bw guess as twice of avg_bw if
   nothing mentioned explicitly to ICC core.

Changes in V5:
 - Add icc_enable/disable to power on/off call.
 - Save some non-zero avg/peak value to ICC core by calling geni_icc_set_bw
   from probe so that when resume/icc_enable is called NOC are running at
   some non-zero value.

Changes in V6:
 - As per Matthias's comment made print statement consistent across driver

Changes in V7:
 - As per Matthias's comment removed usage of peak_bw variable because we don't
   have explicit peak requirement, we were voting peak = avg and this can be
   tracked using single variable for avg bw.
 - As per Matthias's comment improved print log.

 drivers/spi/spi-qcom-qspi.c | 57 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c
index 3c4f83b..092ac27 100644
--- a/drivers/spi/spi-qcom-qspi.c
+++ b/drivers/spi/spi-qcom-qspi.c
@@ -2,6 +2,7 @@
 // Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
 
 #include <linux/clk.h>
+#include <linux/interconnect.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/module.h>
@@ -139,7 +140,9 @@ struct qcom_qspi {
 	struct device *dev;
 	struct clk_bulk_data *clks;
 	struct qspi_xfer xfer;
-	/* Lock to protect xfer and IRQ accessed registers */
+	struct icc_path *icc_path_cpu_to_qspi;
+	unsigned int avg_bw_cpu;
+	/* Lock to protect data accessed by IRQs */
 	spinlock_t lock;
 };
 
@@ -241,6 +244,19 @@ static int qcom_qspi_transfer_one(struct spi_master *master,
 		return ret;
 	}
 
+	/*
+	 * Set BW quota for CPU as driver supports FIFO mode only.
+	 * We don't have explicit peak requirement so keep it equal to avg_bw.
+	 */
+	ctrl->avg_bw_cpu = Bps_to_icc(speed_hz);
+	ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, ctrl->avg_bw_cpu,
+		ctrl->avg_bw_cpu);
+	if (ret) {
+		dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
 	spin_lock_irqsave(&ctrl->lock, flags);
 
 	/* We are half duplex, so either rx or tx will be set */
@@ -458,6 +474,29 @@ static int qcom_qspi_probe(struct platform_device *pdev)
 	if (ret)
 		goto exit_probe_master_put;
 
+	ctrl->icc_path_cpu_to_qspi = devm_of_icc_get(dev, "qspi-config");
+	if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) {
+		ret = PTR_ERR(ctrl->icc_path_cpu_to_qspi);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "Failed to get cpu path: %d\n", ret);
+		goto exit_probe_master_put;
+	}
+	/* Set BW vote for register access */
+	ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000),
+				Bps_to_icc(1000));
+	if (ret) {
+		dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n",
+				__func__, ret);
+		goto exit_probe_master_put;
+	}
+
+	ret = icc_disable(ctrl->icc_path_cpu_to_qspi);
+	if (ret) {
+		dev_err(ctrl->dev, "%s: ICC disable failed for cpu: %d\n",
+				__func__, ret);
+		goto exit_probe_master_put;
+	}
+
 	ret = platform_get_irq(pdev, 0);
 	if (ret < 0)
 		goto exit_probe_master_put;
@@ -511,9 +550,17 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev)
 {
 	struct spi_master *master = dev_get_drvdata(dev);
 	struct qcom_qspi *ctrl = spi_master_get_devdata(master);
+	int ret;
 
 	clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks);
 
+	ret = icc_disable(ctrl->icc_path_cpu_to_qspi);
+	if (ret) {
+		dev_err_ratelimited(ctrl->dev, "%s: ICC disable failed for cpu: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
 	return 0;
 }
 
@@ -521,6 +568,14 @@ static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev)
 {
 	struct spi_master *master = dev_get_drvdata(dev);
 	struct qcom_qspi *ctrl = spi_master_get_devdata(master);
+	int ret;
+
+	ret = icc_enable(ctrl->icc_path_cpu_to_qspi);
+	if (ret) {
+		dev_err_ratelimited(ctrl->dev, "%s: ICC enable failed for cpu: %d\n",
+			__func__, ret);
+		return ret;
+	}
 
 	return clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks);
 }
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project

^ permalink raw reply related

* [PATCH V7 5/7] tty: serial: qcom_geni_serial: Add interconnect support
From: Akash Asthana @ 2020-05-26 12:54 UTC (permalink / raw)
  To: gregkh, agross, bjorn.andersson, wsa, broonie, mark.rutland,
	robh+dt
  Cc: linux-i2c, linux-spi, devicetree, swboyd, mgautam, linux-arm-msm,
	linux-serial, mka, dianders, msavaliy, evgreen, Akash Asthana
In-Reply-To: <1590497690-29035-1-git-send-email-akashast@codeaurora.org>

Get the interconnect paths for Uart based Serial Engine device
and vote according to the baud rate requirement of the driver.

Signed-off-by: Akash Asthana <akashast@codeaurora.org>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
---
Changes in V2:
 - As per Bjorn's comment, removed se == NULL check from geni_serial_icc_get
 - As per Bjorn's comment, removed code to set se->icc_path* to NULL in failure
 - As per Bjorn's comment, introduced and using devm_of_icc_get API for getting
   path handle
 - As per Matthias comment, added error handling for icc_set_bw call

Changes in V3:
 - As per Matthias comment, use common library APIs defined in geni-se
   driver for ICC functionality.

Changes in V4:
 - As per Mark's comment move peak_bw guess as twice of avg_bw if
   nothing mentioned explicitly to ICC core.
 - As per Matthias's comment select core clock BW based on baud rate.
   If it's less than 115200 go for GENI_DEFAULT_BW else CORE_2X_50_MHZ

Changes in V5:
 - Add icc_enable/disable to power on/off call.
 - Save some non-zero avg/peak value to ICC core by calling geni_icc_set_bw
   from probe so that when resume/icc_enable is called NOC are running at
   some non-zero value. No need to call icc_disable after BW vote because
   console devices are expected to be in active state from the probe itself
   and qcom_geni_serial_pm(STATE_OFF) will be called for non-console ones.

Changes in V6:
 - No change

Changes in V7:
 - As per Matthias's comment removed usage of peak_bw variable because we don't
   have explicit peak requirement, we were voting peak = avg and this can be
   tracked using single variable for avg bw.

 drivers/tty/serial/qcom_geni_serial.c | 31 ++++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index a2b1b6d..aa975ed 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -944,6 +944,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
 	struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
 	unsigned long clk_rate;
 	u32 ver, sampling_rate;
+	unsigned int avg_bw_core;
 
 	qcom_geni_serial_stop_rx(uport);
 	/* baud rate */
@@ -965,6 +966,16 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
 	ser_clk_cfg = SER_CLK_EN;
 	ser_clk_cfg |= clk_div << CLK_DIV_SHFT;
 
+	/*
+	 * Bump up BW vote on CPU and CORE path as driver supports FIFO mode
+	 * only.
+	 */
+	avg_bw_core = (baud > 115200) ? Bps_to_icc(CORE_2X_50_MHZ)
+						: GENI_DEFAULT_BW;
+	port->se.icc_paths[GENI_TO_CORE].avg_bw = avg_bw_core;
+	port->se.icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(baud);
+	geni_icc_set_bw(&port->se);
+
 	/* parity */
 	tx_trans_cfg = readl(uport->membase + SE_UART_TX_TRANS_CFG);
 	tx_parity_cfg = readl(uport->membase + SE_UART_TX_PARITY_CFG);
@@ -1234,11 +1245,14 @@ static void qcom_geni_serial_pm(struct uart_port *uport,
 	if (old_state == UART_PM_STATE_UNDEFINED)
 		old_state = UART_PM_STATE_OFF;
 
-	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
+	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) {
+		geni_icc_enable(&port->se);
 		geni_se_resources_on(&port->se);
-	else if (new_state == UART_PM_STATE_OFF &&
-			old_state == UART_PM_STATE_ON)
+	} else if (new_state == UART_PM_STATE_OFF &&
+			old_state == UART_PM_STATE_ON) {
 		geni_se_resources_off(&port->se);
+		geni_icc_disable(&port->se);
+	}
 }
 
 static const struct uart_ops qcom_geni_console_pops = {
@@ -1336,6 +1350,17 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
 			return -ENOMEM;
 	}
 
+	ret = geni_icc_get(&port->se, NULL);
+	if (ret)
+		return ret;
+	port->se.icc_paths[GENI_TO_CORE].avg_bw = GENI_DEFAULT_BW;
+	port->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
+
+	/* Set BW for register access */
+	ret = geni_icc_set_bw(&port->se);
+	if (ret)
+		return ret;
+
 	port->name = devm_kasprintf(uport->dev, GFP_KERNEL,
 			"qcom_geni_serial_%s%d",
 			uart_console(uport) ? "console" : "uart", uport->line);
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project

^ permalink raw reply related

* [PATCH V7 4/7] spi: spi-geni-qcom: Add interconnect support
From: Akash Asthana @ 2020-05-26 12:54 UTC (permalink / raw)
  To: gregkh, agross, bjorn.andersson, wsa, broonie, mark.rutland,
	robh+dt
  Cc: linux-i2c, linux-spi, devicetree, swboyd, mgautam, linux-arm-msm,
	linux-serial, mka, dianders, msavaliy, evgreen, Akash Asthana
In-Reply-To: <1590497690-29035-1-git-send-email-akashast@codeaurora.org>

Get the interconnect paths for SPI based Serial Engine device
and vote according to the current bus speed of the driver.

Signed-off-by: Akash Asthana <akashast@codeaurora.org>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
---
Changes in V2:
 - As per Bjorn's comment, removed se == NULL check from geni_spi_icc_get
 - As per Bjorn's comment, removed code to set se->icc_path* to NULL in failure
 - As per Bjorn's comment, introduced and using devm_of_icc_get API for getting
   path handle
 - As per Matthias comment, added error handling for icc_set_bw call

Changes in V3:
 - As per Matthias's comment, use helper ICC function from geni-se driver.

Changes in V4:
 - Move peak_bw guess as twice of avg_bw if nothing mentioned explicitly
   to ICC core.

Changes in V5:
 - Use icc_enable/disable in power on/off call.
 - Save some non-zero avg/peak value to ICC core by calling geni_icc_set_bw
   from probe so that when resume/icc_enable is called NOC are running at
   some non-zero value. No need to call icc_disable after BW vote because
   device will resume and suspend before probe return and will leave ICC in
   disabled state.

Changes in V6:
 - No change

Changes in V7:
 - As per Matthias's comment removed usage of peak_bw variable because we don't
   have explicit peak requirement, we were voting peak = avg and this can be
   tracked using single variable for avg bw.

 drivers/spi/spi-geni-qcom.c | 29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index c397242..2ace5c5 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -234,6 +234,12 @@ static int setup_fifo_params(struct spi_device *spi_slv,
 		return ret;
 	}
 
+	/* Set BW quota for CPU as driver supports FIFO mode only. */
+	se->icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(mas->cur_speed_hz);
+	ret = geni_icc_set_bw(se);
+	if (ret)
+		return ret;
+
 	clk_sel = idx & CLK_SEL_MSK;
 	m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN;
 	spi_setup_word_len(mas, spi_slv->mode, spi_slv->bits_per_word);
@@ -578,6 +584,17 @@ static int spi_geni_probe(struct platform_device *pdev)
 	spin_lock_init(&mas->lock);
 	pm_runtime_enable(dev);
 
+	ret = geni_icc_get(&mas->se, NULL);
+	if (ret)
+		goto spi_geni_probe_runtime_disable;
+	/* Set the bus quota to a reasonable value for register access */
+	mas->se.icc_paths[GENI_TO_CORE].avg_bw = Bps_to_icc(CORE_2X_50_MHZ);
+	mas->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
+
+	ret = geni_icc_set_bw(&mas->se);
+	if (ret)
+		goto spi_geni_probe_runtime_disable;
+
 	ret = spi_geni_init(mas);
 	if (ret)
 		goto spi_geni_probe_runtime_disable;
@@ -616,14 +633,24 @@ static int __maybe_unused spi_geni_runtime_suspend(struct device *dev)
 {
 	struct spi_master *spi = dev_get_drvdata(dev);
 	struct spi_geni_master *mas = spi_master_get_devdata(spi);
+	int ret;
+
+	ret = geni_se_resources_off(&mas->se);
+	if (ret)
+		return ret;
 
-	return geni_se_resources_off(&mas->se);
+	return geni_icc_disable(&mas->se);
 }
 
 static int __maybe_unused spi_geni_runtime_resume(struct device *dev)
 {
 	struct spi_master *spi = dev_get_drvdata(dev);
 	struct spi_geni_master *mas = spi_master_get_devdata(spi);
+	int ret;
+
+	ret = geni_icc_enable(&mas->se);
+	if (ret)
+		return ret;
 
 	return geni_se_resources_on(&mas->se);
 }
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox