All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/4] Add IOMMU driver for rk356x
@ 2021-05-21  8:36 ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

This series adds the IOMMU driver for rk356x SoC.
Since a new compatible is needed to distinguish this second version of 
IOMMU hardware block from the first one, it is an opportunity to convert
the binding to DT schema.

version 5:
 - Add internal ops inside the driver to be able to add variants.
 - Add support of v2 variant.
 - Stop using 'version' field
 - Use GENMASK macro

version 4:
 - Add description for reg items
 - Remove useless interrupt-names properties
 - Add description for interrupts items
 - Remove interrupt-names properties from DST files

version 3:
 - Rename compatible with soc prefix
 - Rebase on v5.12 tag

version 2:
 - Fix iommu-cells typo in rk322x.dtsi
 - Change maintainer
 - Change reg maxItems
 - Add power-domains property
Add IOMMU driver for rk356x

This series adds the IOMMU driver for rk356x SoC.
Since a new compatible is needed to distinguish this second version of 
IOMMU hardware block from the first one, it is an opportunity to convert
the binding to DT schema.

version 4:
 - Add description for reg items
 - Remove useless interrupt-names properties
 - Add description for interrupts items
 - Remove interrupt-names properties from DST files

version 3:
 - Rename compatible with soc prefix
 - Rebase on v5.12 tag

version 2:
 - Fix iommu-cells typo in rk322x.dtsi
 - Change maintainer
 - Change reg maxItems
 - Add power-domains property

 
Benjamin Gaignard (4):
  dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
  dt-bindings: iommu: rockchip: Add compatible for v2
  iommu: rockchip: Add internal ops to handle variants
  iommu: rockchip: Add support for iommu v2

 .../bindings/iommu/rockchip,iommu.txt         |  38 -----
 .../bindings/iommu/rockchip,iommu.yaml        |  85 ++++++++++
 drivers/iommu/rockchip-iommu.c                | 147 +++++++++++++++---
 3 files changed, 213 insertions(+), 57 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
 create mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml

-- 
2.25.1


^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 0/4] Add IOMMU driver for rk356x
@ 2021-05-21  8:36 ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

This series adds the IOMMU driver for rk356x SoC.
Since a new compatible is needed to distinguish this second version of 
IOMMU hardware block from the first one, it is an opportunity to convert
the binding to DT schema.

version 5:
 - Add internal ops inside the driver to be able to add variants.
 - Add support of v2 variant.
 - Stop using 'version' field
 - Use GENMASK macro

version 4:
 - Add description for reg items
 - Remove useless interrupt-names properties
 - Add description for interrupts items
 - Remove interrupt-names properties from DST files

version 3:
 - Rename compatible with soc prefix
 - Rebase on v5.12 tag

version 2:
 - Fix iommu-cells typo in rk322x.dtsi
 - Change maintainer
 - Change reg maxItems
 - Add power-domains property
Add IOMMU driver for rk356x

This series adds the IOMMU driver for rk356x SoC.
Since a new compatible is needed to distinguish this second version of 
IOMMU hardware block from the first one, it is an opportunity to convert
the binding to DT schema.

version 4:
 - Add description for reg items
 - Remove useless interrupt-names properties
 - Add description for interrupts items
 - Remove interrupt-names properties from DST files

version 3:
 - Rename compatible with soc prefix
 - Rebase on v5.12 tag

version 2:
 - Fix iommu-cells typo in rk322x.dtsi
 - Change maintainer
 - Change reg maxItems
 - Add power-domains property

 
Benjamin Gaignard (4):
  dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
  dt-bindings: iommu: rockchip: Add compatible for v2
  iommu: rockchip: Add internal ops to handle variants
  iommu: rockchip: Add support for iommu v2

 .../bindings/iommu/rockchip,iommu.txt         |  38 -----
 .../bindings/iommu/rockchip,iommu.yaml        |  85 ++++++++++
 drivers/iommu/rockchip-iommu.c                | 147 +++++++++++++++---
 3 files changed, 213 insertions(+), 57 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
 create mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml

-- 
2.25.1


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 0/4] Add IOMMU driver for rk356x
@ 2021-05-21  8:36 ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: devicetree, Benjamin Gaignard, linux-kernel, linux-rockchip,
	iommu, kernel, linux-arm-kernel

This series adds the IOMMU driver for rk356x SoC.
Since a new compatible is needed to distinguish this second version of 
IOMMU hardware block from the first one, it is an opportunity to convert
the binding to DT schema.

version 5:
 - Add internal ops inside the driver to be able to add variants.
 - Add support of v2 variant.
 - Stop using 'version' field
 - Use GENMASK macro

version 4:
 - Add description for reg items
 - Remove useless interrupt-names properties
 - Add description for interrupts items
 - Remove interrupt-names properties from DST files

version 3:
 - Rename compatible with soc prefix
 - Rebase on v5.12 tag

version 2:
 - Fix iommu-cells typo in rk322x.dtsi
 - Change maintainer
 - Change reg maxItems
 - Add power-domains property
Add IOMMU driver for rk356x

This series adds the IOMMU driver for rk356x SoC.
Since a new compatible is needed to distinguish this second version of 
IOMMU hardware block from the first one, it is an opportunity to convert
the binding to DT schema.

version 4:
 - Add description for reg items
 - Remove useless interrupt-names properties
 - Add description for interrupts items
 - Remove interrupt-names properties from DST files

version 3:
 - Rename compatible with soc prefix
 - Rebase on v5.12 tag

version 2:
 - Fix iommu-cells typo in rk322x.dtsi
 - Change maintainer
 - Change reg maxItems
 - Add power-domains property

 
Benjamin Gaignard (4):
  dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
  dt-bindings: iommu: rockchip: Add compatible for v2
  iommu: rockchip: Add internal ops to handle variants
  iommu: rockchip: Add support for iommu v2

 .../bindings/iommu/rockchip,iommu.txt         |  38 -----
 .../bindings/iommu/rockchip,iommu.yaml        |  85 ++++++++++
 drivers/iommu/rockchip-iommu.c                | 147 +++++++++++++++---
 3 files changed, 213 insertions(+), 57 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
 create mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml

-- 
2.25.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 0/4] Add IOMMU driver for rk356x
@ 2021-05-21  8:36 ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

This series adds the IOMMU driver for rk356x SoC.
Since a new compatible is needed to distinguish this second version of 
IOMMU hardware block from the first one, it is an opportunity to convert
the binding to DT schema.

version 5:
 - Add internal ops inside the driver to be able to add variants.
 - Add support of v2 variant.
 - Stop using 'version' field
 - Use GENMASK macro

version 4:
 - Add description for reg items
 - Remove useless interrupt-names properties
 - Add description for interrupts items
 - Remove interrupt-names properties from DST files

version 3:
 - Rename compatible with soc prefix
 - Rebase on v5.12 tag

version 2:
 - Fix iommu-cells typo in rk322x.dtsi
 - Change maintainer
 - Change reg maxItems
 - Add power-domains property
Add IOMMU driver for rk356x

This series adds the IOMMU driver for rk356x SoC.
Since a new compatible is needed to distinguish this second version of 
IOMMU hardware block from the first one, it is an opportunity to convert
the binding to DT schema.

version 4:
 - Add description for reg items
 - Remove useless interrupt-names properties
 - Add description for interrupts items
 - Remove interrupt-names properties from DST files

version 3:
 - Rename compatible with soc prefix
 - Rebase on v5.12 tag

version 2:
 - Fix iommu-cells typo in rk322x.dtsi
 - Change maintainer
 - Change reg maxItems
 - Add power-domains property

 
Benjamin Gaignard (4):
  dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
  dt-bindings: iommu: rockchip: Add compatible for v2
  iommu: rockchip: Add internal ops to handle variants
  iommu: rockchip: Add support for iommu v2

 .../bindings/iommu/rockchip,iommu.txt         |  38 -----
 .../bindings/iommu/rockchip,iommu.yaml        |  85 ++++++++++
 drivers/iommu/rockchip-iommu.c                | 147 +++++++++++++++---
 3 files changed, 213 insertions(+), 57 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
 create mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml

-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 1/4] dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
  2021-05-21  8:36 ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21  8:36   ` Benjamin Gaignard
  -1 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Convert Rockchip IOMMU to DT schema

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/iommu/rockchip,iommu.txt         | 38 ---------
 .../bindings/iommu/rockchip,iommu.yaml        | 80 +++++++++++++++++++
 2 files changed, 80 insertions(+), 38 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
 create mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml

diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt b/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
deleted file mode 100644
index 6ecefea1c6f9..000000000000
--- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-Rockchip IOMMU
-==============
-
-A Rockchip DRM iommu translates io virtual addresses to physical addresses for
-its master device.  Each slave device is bound to a single master device, and
-shares its clocks, power domain and irq.
-
-Required properties:
-- compatible      : Should be "rockchip,iommu"
-- reg             : Address space for the configuration registers
-- interrupts      : Interrupt specifier for the IOMMU instance
-- interrupt-names : Interrupt name for the IOMMU instance
-- #iommu-cells    : Should be <0>.  This indicates the iommu is a
-                    "single-master" device, and needs no additional information
-                    to associate with its master device.  See:
-                    Documentation/devicetree/bindings/iommu/iommu.txt
-- clocks          : A list of clocks required for the IOMMU to be accessible by
-                    the host CPU.
-- clock-names     : Should contain the following:
-	"iface" - Main peripheral bus clock (PCLK/HCL) (required)
-	"aclk"  - AXI bus clock (required)
-
-Optional properties:
-- rockchip,disable-mmu-reset : Don't use the mmu reset operation.
-			       Some mmu instances may produce unexpected results
-			       when the reset operation is used.
-
-Example:
-
-	vopl_mmu: iommu@ff940300 {
-		compatible = "rockchip,iommu";
-		reg = <0xff940300 0x100>;
-		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
-		interrupt-names = "vopl_mmu";
-		clocks = <&cru ACLK_VOP1>, <&cru HCLK_VOP1>;
-		clock-names = "aclk", "iface";
-		#iommu-cells = <0>;
-	};
diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
new file mode 100644
index 000000000000..099fc2578b54
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iommu/rockchip,iommu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip IOMMU
+
+maintainers:
+  - Heiko Stuebner <heiko@sntech.de>
+
+description: |+
+  A Rockchip DRM iommu translates io virtual addresses to physical addresses for
+  its master device. Each slave device is bound to a single master device and
+  shares its clocks, power domain and irq.
+
+  For information on assigning IOMMU controller to its peripheral devices,
+  see generic IOMMU bindings.
+
+properties:
+  compatible:
+    const: rockchip,iommu
+
+  reg:
+    items:
+      - description: configuration registers for MMU instance 0
+      - description: configuration registers for MMU instance 1
+    minItems: 1
+    maxItems: 2
+
+  interrupts:
+    items:
+      - description: interruption for MMU instance 0
+      - description: interruption for MMU instance 1
+    minItems: 1
+    maxItems: 2
+
+  clocks:
+    items:
+      - description: Core clock
+      - description: Interface clock
+
+  clock-names:
+    items:
+      - const: aclk
+      - const: iface
+
+  "#iommu-cells":
+    const: 0
+
+  rockchip,disable-mmu-reset:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description: |
+      Do not use the mmu reset operation.
+      Some mmu instances may produce unexpected results
+      when the reset operation is used.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - "#iommu-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/rk3399-cru.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    vopl_mmu: iommu@ff940300 {
+      compatible = "rockchip,iommu";
+      reg = <0xff940300 0x100>;
+      interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&cru ACLK_VOP1>, <&cru HCLK_VOP1>;
+      clock-names = "aclk", "iface";
+      #iommu-cells = <0>;
+    };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 1/4] dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Convert Rockchip IOMMU to DT schema

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/iommu/rockchip,iommu.txt         | 38 ---------
 .../bindings/iommu/rockchip,iommu.yaml        | 80 +++++++++++++++++++
 2 files changed, 80 insertions(+), 38 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
 create mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml

diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt b/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
deleted file mode 100644
index 6ecefea1c6f9..000000000000
--- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-Rockchip IOMMU
-==============
-
-A Rockchip DRM iommu translates io virtual addresses to physical addresses for
-its master device.  Each slave device is bound to a single master device, and
-shares its clocks, power domain and irq.
-
-Required properties:
-- compatible      : Should be "rockchip,iommu"
-- reg             : Address space for the configuration registers
-- interrupts      : Interrupt specifier for the IOMMU instance
-- interrupt-names : Interrupt name for the IOMMU instance
-- #iommu-cells    : Should be <0>.  This indicates the iommu is a
-                    "single-master" device, and needs no additional information
-                    to associate with its master device.  See:
-                    Documentation/devicetree/bindings/iommu/iommu.txt
-- clocks          : A list of clocks required for the IOMMU to be accessible by
-                    the host CPU.
-- clock-names     : Should contain the following:
-	"iface" - Main peripheral bus clock (PCLK/HCL) (required)
-	"aclk"  - AXI bus clock (required)
-
-Optional properties:
-- rockchip,disable-mmu-reset : Don't use the mmu reset operation.
-			       Some mmu instances may produce unexpected results
-			       when the reset operation is used.
-
-Example:
-
-	vopl_mmu: iommu@ff940300 {
-		compatible = "rockchip,iommu";
-		reg = <0xff940300 0x100>;
-		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
-		interrupt-names = "vopl_mmu";
-		clocks = <&cru ACLK_VOP1>, <&cru HCLK_VOP1>;
-		clock-names = "aclk", "iface";
-		#iommu-cells = <0>;
-	};
diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
new file mode 100644
index 000000000000..099fc2578b54
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iommu/rockchip,iommu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip IOMMU
+
+maintainers:
+  - Heiko Stuebner <heiko@sntech.de>
+
+description: |+
+  A Rockchip DRM iommu translates io virtual addresses to physical addresses for
+  its master device. Each slave device is bound to a single master device and
+  shares its clocks, power domain and irq.
+
+  For information on assigning IOMMU controller to its peripheral devices,
+  see generic IOMMU bindings.
+
+properties:
+  compatible:
+    const: rockchip,iommu
+
+  reg:
+    items:
+      - description: configuration registers for MMU instance 0
+      - description: configuration registers for MMU instance 1
+    minItems: 1
+    maxItems: 2
+
+  interrupts:
+    items:
+      - description: interruption for MMU instance 0
+      - description: interruption for MMU instance 1
+    minItems: 1
+    maxItems: 2
+
+  clocks:
+    items:
+      - description: Core clock
+      - description: Interface clock
+
+  clock-names:
+    items:
+      - const: aclk
+      - const: iface
+
+  "#iommu-cells":
+    const: 0
+
+  rockchip,disable-mmu-reset:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description: |
+      Do not use the mmu reset operation.
+      Some mmu instances may produce unexpected results
+      when the reset operation is used.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - "#iommu-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/rk3399-cru.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    vopl_mmu: iommu@ff940300 {
+      compatible = "rockchip,iommu";
+      reg = <0xff940300 0x100>;
+      interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&cru ACLK_VOP1>, <&cru HCLK_VOP1>;
+      clock-names = "aclk", "iface";
+      #iommu-cells = <0>;
+    };
-- 
2.25.1


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 1/4] dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: devicetree, Benjamin Gaignard, Rob Herring, linux-kernel,
	linux-rockchip, iommu, kernel, linux-arm-kernel

Convert Rockchip IOMMU to DT schema

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/iommu/rockchip,iommu.txt         | 38 ---------
 .../bindings/iommu/rockchip,iommu.yaml        | 80 +++++++++++++++++++
 2 files changed, 80 insertions(+), 38 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
 create mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml

diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt b/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
deleted file mode 100644
index 6ecefea1c6f9..000000000000
--- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-Rockchip IOMMU
-==============
-
-A Rockchip DRM iommu translates io virtual addresses to physical addresses for
-its master device.  Each slave device is bound to a single master device, and
-shares its clocks, power domain and irq.
-
-Required properties:
-- compatible      : Should be "rockchip,iommu"
-- reg             : Address space for the configuration registers
-- interrupts      : Interrupt specifier for the IOMMU instance
-- interrupt-names : Interrupt name for the IOMMU instance
-- #iommu-cells    : Should be <0>.  This indicates the iommu is a
-                    "single-master" device, and needs no additional information
-                    to associate with its master device.  See:
-                    Documentation/devicetree/bindings/iommu/iommu.txt
-- clocks          : A list of clocks required for the IOMMU to be accessible by
-                    the host CPU.
-- clock-names     : Should contain the following:
-	"iface" - Main peripheral bus clock (PCLK/HCL) (required)
-	"aclk"  - AXI bus clock (required)
-
-Optional properties:
-- rockchip,disable-mmu-reset : Don't use the mmu reset operation.
-			       Some mmu instances may produce unexpected results
-			       when the reset operation is used.
-
-Example:
-
-	vopl_mmu: iommu@ff940300 {
-		compatible = "rockchip,iommu";
-		reg = <0xff940300 0x100>;
-		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
-		interrupt-names = "vopl_mmu";
-		clocks = <&cru ACLK_VOP1>, <&cru HCLK_VOP1>;
-		clock-names = "aclk", "iface";
-		#iommu-cells = <0>;
-	};
diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
new file mode 100644
index 000000000000..099fc2578b54
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iommu/rockchip,iommu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip IOMMU
+
+maintainers:
+  - Heiko Stuebner <heiko@sntech.de>
+
+description: |+
+  A Rockchip DRM iommu translates io virtual addresses to physical addresses for
+  its master device. Each slave device is bound to a single master device and
+  shares its clocks, power domain and irq.
+
+  For information on assigning IOMMU controller to its peripheral devices,
+  see generic IOMMU bindings.
+
+properties:
+  compatible:
+    const: rockchip,iommu
+
+  reg:
+    items:
+      - description: configuration registers for MMU instance 0
+      - description: configuration registers for MMU instance 1
+    minItems: 1
+    maxItems: 2
+
+  interrupts:
+    items:
+      - description: interruption for MMU instance 0
+      - description: interruption for MMU instance 1
+    minItems: 1
+    maxItems: 2
+
+  clocks:
+    items:
+      - description: Core clock
+      - description: Interface clock
+
+  clock-names:
+    items:
+      - const: aclk
+      - const: iface
+
+  "#iommu-cells":
+    const: 0
+
+  rockchip,disable-mmu-reset:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description: |
+      Do not use the mmu reset operation.
+      Some mmu instances may produce unexpected results
+      when the reset operation is used.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - "#iommu-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/rk3399-cru.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    vopl_mmu: iommu@ff940300 {
+      compatible = "rockchip,iommu";
+      reg = <0xff940300 0x100>;
+      interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&cru ACLK_VOP1>, <&cru HCLK_VOP1>;
+      clock-names = "aclk", "iface";
+      #iommu-cells = <0>;
+    };
-- 
2.25.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 1/4] dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Convert Rockchip IOMMU to DT schema

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/iommu/rockchip,iommu.txt         | 38 ---------
 .../bindings/iommu/rockchip,iommu.yaml        | 80 +++++++++++++++++++
 2 files changed, 80 insertions(+), 38 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
 create mode 100644 Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml

diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt b/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
deleted file mode 100644
index 6ecefea1c6f9..000000000000
--- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-Rockchip IOMMU
-==============
-
-A Rockchip DRM iommu translates io virtual addresses to physical addresses for
-its master device.  Each slave device is bound to a single master device, and
-shares its clocks, power domain and irq.
-
-Required properties:
-- compatible      : Should be "rockchip,iommu"
-- reg             : Address space for the configuration registers
-- interrupts      : Interrupt specifier for the IOMMU instance
-- interrupt-names : Interrupt name for the IOMMU instance
-- #iommu-cells    : Should be <0>.  This indicates the iommu is a
-                    "single-master" device, and needs no additional information
-                    to associate with its master device.  See:
-                    Documentation/devicetree/bindings/iommu/iommu.txt
-- clocks          : A list of clocks required for the IOMMU to be accessible by
-                    the host CPU.
-- clock-names     : Should contain the following:
-	"iface" - Main peripheral bus clock (PCLK/HCL) (required)
-	"aclk"  - AXI bus clock (required)
-
-Optional properties:
-- rockchip,disable-mmu-reset : Don't use the mmu reset operation.
-			       Some mmu instances may produce unexpected results
-			       when the reset operation is used.
-
-Example:
-
-	vopl_mmu: iommu@ff940300 {
-		compatible = "rockchip,iommu";
-		reg = <0xff940300 0x100>;
-		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
-		interrupt-names = "vopl_mmu";
-		clocks = <&cru ACLK_VOP1>, <&cru HCLK_VOP1>;
-		clock-names = "aclk", "iface";
-		#iommu-cells = <0>;
-	};
diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
new file mode 100644
index 000000000000..099fc2578b54
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iommu/rockchip,iommu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip IOMMU
+
+maintainers:
+  - Heiko Stuebner <heiko@sntech.de>
+
+description: |+
+  A Rockchip DRM iommu translates io virtual addresses to physical addresses for
+  its master device. Each slave device is bound to a single master device and
+  shares its clocks, power domain and irq.
+
+  For information on assigning IOMMU controller to its peripheral devices,
+  see generic IOMMU bindings.
+
+properties:
+  compatible:
+    const: rockchip,iommu
+
+  reg:
+    items:
+      - description: configuration registers for MMU instance 0
+      - description: configuration registers for MMU instance 1
+    minItems: 1
+    maxItems: 2
+
+  interrupts:
+    items:
+      - description: interruption for MMU instance 0
+      - description: interruption for MMU instance 1
+    minItems: 1
+    maxItems: 2
+
+  clocks:
+    items:
+      - description: Core clock
+      - description: Interface clock
+
+  clock-names:
+    items:
+      - const: aclk
+      - const: iface
+
+  "#iommu-cells":
+    const: 0
+
+  rockchip,disable-mmu-reset:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description: |
+      Do not use the mmu reset operation.
+      Some mmu instances may produce unexpected results
+      when the reset operation is used.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - "#iommu-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/rk3399-cru.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    vopl_mmu: iommu@ff940300 {
+      compatible = "rockchip,iommu";
+      reg = <0xff940300 0x100>;
+      interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&cru ACLK_VOP1>, <&cru HCLK_VOP1>;
+      clock-names = "aclk", "iface";
+      #iommu-cells = <0>;
+    };
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
  2021-05-21  8:36 ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21  8:36   ` Benjamin Gaignard
  -1 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Add compatible for the second version of IOMMU hardware block.
RK356x IOMMU can also be link to a power domain.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
index 099fc2578b54..d2e28a9e3545 100644
--- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
+++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
@@ -19,7 +19,9 @@ description: |+
 
 properties:
   compatible:
-    const: rockchip,iommu
+    enum:
+      - rockchip,iommu
+      - rockchip,rk3568-iommu
 
   reg:
     items:
@@ -48,6 +50,9 @@ properties:
   "#iommu-cells":
     const: 0
 
+  power-domains:
+    maxItems: 1
+
   rockchip,disable-mmu-reset:
     $ref: /schemas/types.yaml#/definitions/flag
     description: |
-- 
2.25.1


^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Add compatible for the second version of IOMMU hardware block.
RK356x IOMMU can also be link to a power domain.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
index 099fc2578b54..d2e28a9e3545 100644
--- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
+++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
@@ -19,7 +19,9 @@ description: |+
 
 properties:
   compatible:
-    const: rockchip,iommu
+    enum:
+      - rockchip,iommu
+      - rockchip,rk3568-iommu
 
   reg:
     items:
@@ -48,6 +50,9 @@ properties:
   "#iommu-cells":
     const: 0
 
+  power-domains:
+    maxItems: 1
+
   rockchip,disable-mmu-reset:
     $ref: /schemas/types.yaml#/definitions/flag
     description: |
-- 
2.25.1


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: devicetree, Benjamin Gaignard, Rob Herring, linux-kernel,
	linux-rockchip, iommu, kernel, linux-arm-kernel

Add compatible for the second version of IOMMU hardware block.
RK356x IOMMU can also be link to a power domain.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
index 099fc2578b54..d2e28a9e3545 100644
--- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
+++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
@@ -19,7 +19,9 @@ description: |+
 
 properties:
   compatible:
-    const: rockchip,iommu
+    enum:
+      - rockchip,iommu
+      - rockchip,rk3568-iommu
 
   reg:
     items:
@@ -48,6 +50,9 @@ properties:
   "#iommu-cells":
     const: 0
 
+  power-domains:
+    maxItems: 1
+
   rockchip,disable-mmu-reset:
     $ref: /schemas/types.yaml#/definitions/flag
     description: |
-- 
2.25.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Add compatible for the second version of IOMMU hardware block.
RK356x IOMMU can also be link to a power domain.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
index 099fc2578b54..d2e28a9e3545 100644
--- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
+++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
@@ -19,7 +19,9 @@ description: |+
 
 properties:
   compatible:
-    const: rockchip,iommu
+    enum:
+      - rockchip,iommu
+      - rockchip,rk3568-iommu
 
   reg:
     items:
@@ -48,6 +50,9 @@ properties:
   "#iommu-cells":
     const: 0
 
+  power-domains:
+    maxItems: 1
+
   rockchip,disable-mmu-reset:
     $ref: /schemas/types.yaml#/definitions/flag
     description: |
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
  2021-05-21  8:36 ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21  8:36   ` Benjamin Gaignard
  -1 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

Add internal ops to be able to handle incoming variant v2.
The goal is to keep the overall structure of the framework but
to allow to add the evolution of this hardware block.

The ops are global for a SoC because iommu domains are not
attached to a specific devices if they are for a virtuel device like
drm. Use a global variable shouldn't be since SoC usually doesn't
embedded different versions of the iommu hardware block.
If that happen one day a WARN_ON will be displayed at probe time.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
---
version 5:
 - Use of_device_get_match_data()
 - Add internal ops inside the driver

 drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
 1 file changed, 50 insertions(+), 19 deletions(-)

diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 7a2932772fdf..e7b9bcf174b1 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -19,6 +19,7 @@
 #include <linux/iopoll.h>
 #include <linux/list.h>
 #include <linux/mm.h>
+#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
@@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
 	"aclk", "iface",
 };
 
+struct rk_iommu_ops {
+	phys_addr_t (*pt_address)(u32 dte);
+	u32 (*mk_dtentries)(dma_addr_t pt_dma);
+	u32 (*mk_ptentries)(phys_addr_t page, int prot);
+	phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
+	u32 pt_address_mask;
+};
+
 struct rk_iommu {
 	struct device *dev;
 	void __iomem **bases;
@@ -116,6 +125,7 @@ struct rk_iommudata {
 };
 
 static struct device *dma_dev;
+static const struct rk_iommu_ops *rk_ops;
 
 static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
 				  unsigned int count)
@@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
 #define RK_PTE_PAGE_READABLE      BIT(1)
 #define RK_PTE_PAGE_VALID         BIT(0)
 
-static inline phys_addr_t rk_pte_page_address(u32 pte)
-{
-	return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
-}
-
 static inline bool rk_pte_is_page_valid(u32 pte)
 {
 	return pte & RK_PTE_PAGE_VALID;
@@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
 		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
 
 		dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
-		if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
+		if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
 			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
 			return -EFAULT;
 		}
@@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
 	return 0;
 }
 
+static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
+{
+	return addr;
+}
+
 static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 {
 	void __iomem *base = iommu->bases[index];
@@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 	page_offset = rk_iova_page_offset(iova);
 
 	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
-	mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
+	mmu_dte_addr_phys = rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
 
 	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
 	dte_addr = phys_to_virt(dte_addr_phys);
@@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 	if (!rk_dte_is_pt_valid(dte))
 		goto print_it;
 
-	pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
+	pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
 	pte_addr = phys_to_virt(pte_addr_phys);
 	pte = *pte_addr;
 
 	if (!rk_pte_is_page_valid(pte))
 		goto print_it;
 
-	page_addr_phys = rk_pte_page_address(pte) + page_offset;
+	page_addr_phys = rk_ops->pt_address(pte) + page_offset;
 	page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
 
 print_it:
@@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
 	if (!rk_dte_is_pt_valid(dte))
 		goto out;
 
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	page_table = (u32 *)phys_to_virt(pt_phys);
 	pte = page_table[rk_iova_pte_index(iova)];
 	if (!rk_pte_is_page_valid(pte))
 		goto out;
 
-	phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
+	phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
 out:
 	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
 
@@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	dte = rk_mk_dte(pt_dma);
+	dte = rk_ops->mk_dtentries(pt_dma);
 	*dte_addr = dte;
 
 	rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
 	rk_table_flush(rk_domain,
 		       rk_domain->dt_dma + dte_index * sizeof(u32), 1);
 done:
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	return (u32 *)phys_to_virt(pt_phys);
 }
 
@@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 		if (rk_pte_is_page_valid(pte))
 			goto unwind;
 
-		pte_addr[pte_count] = rk_mk_pte(paddr, prot);
+		pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
 
 		paddr += SPAGE_SIZE;
 	}
@@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 			    pte_count * SPAGE_SIZE);
 
 	iova += pte_count * SPAGE_SIZE;
-	page_phys = rk_pte_page_address(pte_addr[pte_count]);
+	page_phys = rk_ops->pt_address(pte_addr[pte_count]);
 	pr_err("iova: %pad already mapped to %pa cannot remap to phys: %pa prot: %#x\n",
 	       &iova, &page_phys, &paddr, prot);
 
@@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
 	dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
 	pte_index = rk_iova_pte_index(iova);
 	pte_addr = &page_table[pte_index];
-	pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
+
+	pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
 	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
 				paddr, size, prot);
 
@@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
 		return 0;
 	}
 
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
 	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
 	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size);
@@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
 	for (i = 0; i < NUM_DT_ENTRIES; i++) {
 		u32 dte = rk_domain->dt[i];
 		if (rk_dte_is_pt_valid(dte)) {
-			phys_addr_t pt_phys = rk_dte_pt_address(dte);
+			phys_addr_t pt_phys = rk_ops->pt_address(dte);
 			u32 *page_table = phys_to_virt(pt_phys);
 			dma_unmap_single(dma_dev, pt_phys,
 					 SPAGE_SIZE, DMA_TO_DEVICE);
@@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct platform_device *pdev)
 	iommu->dev = dev;
 	iommu->num_mmu = 0;
 
+	if (!rk_ops)
+		rk_ops = of_device_get_match_data(dev);
+
+	/*
+	 * That should not happen unless different versions of the
+	 * hardware block are embedded the same SoC
+	 */
+	WARN_ON(rk_ops != of_device_get_match_data(dev));
+
 	iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
 				    GFP_KERNEL);
 	if (!iommu->bases)
@@ -1277,10 +1297,21 @@ static const struct dev_pm_ops rk_iommu_pm_ops = {
 				pm_runtime_force_resume)
 };
 
+static struct rk_iommu_ops iommu_data_ops_v1 = {
+	.pt_address = &rk_dte_pt_address,
+	.mk_dtentries = &rk_mk_dte,
+	.mk_ptentries = &rk_mk_pte,
+	.dte_addr_phys = &rk_dte_addr_phys,
+	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
+};
+
 static const struct of_device_id rk_iommu_dt_ids[] = {
-	{ .compatible = "rockchip,iommu" },
+	{	.compatible = "rockchip,iommu",
+		.data = &iommu_data_ops_v1,
+	},
 	{ /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
 
 static struct platform_driver rk_iommu_driver = {
 	.probe = rk_iommu_probe,
-- 
2.25.1


^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

Add internal ops to be able to handle incoming variant v2.
The goal is to keep the overall structure of the framework but
to allow to add the evolution of this hardware block.

The ops are global for a SoC because iommu domains are not
attached to a specific devices if they are for a virtuel device like
drm. Use a global variable shouldn't be since SoC usually doesn't
embedded different versions of the iommu hardware block.
If that happen one day a WARN_ON will be displayed at probe time.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
---
version 5:
 - Use of_device_get_match_data()
 - Add internal ops inside the driver

 drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
 1 file changed, 50 insertions(+), 19 deletions(-)

diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 7a2932772fdf..e7b9bcf174b1 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -19,6 +19,7 @@
 #include <linux/iopoll.h>
 #include <linux/list.h>
 #include <linux/mm.h>
+#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
@@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
 	"aclk", "iface",
 };
 
+struct rk_iommu_ops {
+	phys_addr_t (*pt_address)(u32 dte);
+	u32 (*mk_dtentries)(dma_addr_t pt_dma);
+	u32 (*mk_ptentries)(phys_addr_t page, int prot);
+	phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
+	u32 pt_address_mask;
+};
+
 struct rk_iommu {
 	struct device *dev;
 	void __iomem **bases;
@@ -116,6 +125,7 @@ struct rk_iommudata {
 };
 
 static struct device *dma_dev;
+static const struct rk_iommu_ops *rk_ops;
 
 static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
 				  unsigned int count)
@@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
 #define RK_PTE_PAGE_READABLE      BIT(1)
 #define RK_PTE_PAGE_VALID         BIT(0)
 
-static inline phys_addr_t rk_pte_page_address(u32 pte)
-{
-	return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
-}
-
 static inline bool rk_pte_is_page_valid(u32 pte)
 {
 	return pte & RK_PTE_PAGE_VALID;
@@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
 		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
 
 		dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
-		if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
+		if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
 			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
 			return -EFAULT;
 		}
@@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
 	return 0;
 }
 
+static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
+{
+	return addr;
+}
+
 static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 {
 	void __iomem *base = iommu->bases[index];
@@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 	page_offset = rk_iova_page_offset(iova);
 
 	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
-	mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
+	mmu_dte_addr_phys = rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
 
 	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
 	dte_addr = phys_to_virt(dte_addr_phys);
@@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 	if (!rk_dte_is_pt_valid(dte))
 		goto print_it;
 
-	pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
+	pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
 	pte_addr = phys_to_virt(pte_addr_phys);
 	pte = *pte_addr;
 
 	if (!rk_pte_is_page_valid(pte))
 		goto print_it;
 
-	page_addr_phys = rk_pte_page_address(pte) + page_offset;
+	page_addr_phys = rk_ops->pt_address(pte) + page_offset;
 	page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
 
 print_it:
@@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
 	if (!rk_dte_is_pt_valid(dte))
 		goto out;
 
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	page_table = (u32 *)phys_to_virt(pt_phys);
 	pte = page_table[rk_iova_pte_index(iova)];
 	if (!rk_pte_is_page_valid(pte))
 		goto out;
 
-	phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
+	phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
 out:
 	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
 
@@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	dte = rk_mk_dte(pt_dma);
+	dte = rk_ops->mk_dtentries(pt_dma);
 	*dte_addr = dte;
 
 	rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
 	rk_table_flush(rk_domain,
 		       rk_domain->dt_dma + dte_index * sizeof(u32), 1);
 done:
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	return (u32 *)phys_to_virt(pt_phys);
 }
 
@@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 		if (rk_pte_is_page_valid(pte))
 			goto unwind;
 
-		pte_addr[pte_count] = rk_mk_pte(paddr, prot);
+		pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
 
 		paddr += SPAGE_SIZE;
 	}
@@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 			    pte_count * SPAGE_SIZE);
 
 	iova += pte_count * SPAGE_SIZE;
-	page_phys = rk_pte_page_address(pte_addr[pte_count]);
+	page_phys = rk_ops->pt_address(pte_addr[pte_count]);
 	pr_err("iova: %pad already mapped to %pa cannot remap to phys: %pa prot: %#x\n",
 	       &iova, &page_phys, &paddr, prot);
 
@@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
 	dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
 	pte_index = rk_iova_pte_index(iova);
 	pte_addr = &page_table[pte_index];
-	pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
+
+	pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
 	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
 				paddr, size, prot);
 
@@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
 		return 0;
 	}
 
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
 	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
 	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size);
@@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
 	for (i = 0; i < NUM_DT_ENTRIES; i++) {
 		u32 dte = rk_domain->dt[i];
 		if (rk_dte_is_pt_valid(dte)) {
-			phys_addr_t pt_phys = rk_dte_pt_address(dte);
+			phys_addr_t pt_phys = rk_ops->pt_address(dte);
 			u32 *page_table = phys_to_virt(pt_phys);
 			dma_unmap_single(dma_dev, pt_phys,
 					 SPAGE_SIZE, DMA_TO_DEVICE);
@@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct platform_device *pdev)
 	iommu->dev = dev;
 	iommu->num_mmu = 0;
 
+	if (!rk_ops)
+		rk_ops = of_device_get_match_data(dev);
+
+	/*
+	 * That should not happen unless different versions of the
+	 * hardware block are embedded the same SoC
+	 */
+	WARN_ON(rk_ops != of_device_get_match_data(dev));
+
 	iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
 				    GFP_KERNEL);
 	if (!iommu->bases)
@@ -1277,10 +1297,21 @@ static const struct dev_pm_ops rk_iommu_pm_ops = {
 				pm_runtime_force_resume)
 };
 
+static struct rk_iommu_ops iommu_data_ops_v1 = {
+	.pt_address = &rk_dte_pt_address,
+	.mk_dtentries = &rk_mk_dte,
+	.mk_ptentries = &rk_mk_pte,
+	.dte_addr_phys = &rk_dte_addr_phys,
+	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
+};
+
 static const struct of_device_id rk_iommu_dt_ids[] = {
-	{ .compatible = "rockchip,iommu" },
+	{	.compatible = "rockchip,iommu",
+		.data = &iommu_data_ops_v1,
+	},
 	{ /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
 
 static struct platform_driver rk_iommu_driver = {
 	.probe = rk_iommu_probe,
-- 
2.25.1


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: devicetree, Benjamin Gaignard, linux-kernel, linux-rockchip,
	iommu, kernel, linux-arm-kernel

Add internal ops to be able to handle incoming variant v2.
The goal is to keep the overall structure of the framework but
to allow to add the evolution of this hardware block.

The ops are global for a SoC because iommu domains are not
attached to a specific devices if they are for a virtuel device like
drm. Use a global variable shouldn't be since SoC usually doesn't
embedded different versions of the iommu hardware block.
If that happen one day a WARN_ON will be displayed at probe time.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
---
version 5:
 - Use of_device_get_match_data()
 - Add internal ops inside the driver

 drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
 1 file changed, 50 insertions(+), 19 deletions(-)

diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 7a2932772fdf..e7b9bcf174b1 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -19,6 +19,7 @@
 #include <linux/iopoll.h>
 #include <linux/list.h>
 #include <linux/mm.h>
+#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
@@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
 	"aclk", "iface",
 };
 
+struct rk_iommu_ops {
+	phys_addr_t (*pt_address)(u32 dte);
+	u32 (*mk_dtentries)(dma_addr_t pt_dma);
+	u32 (*mk_ptentries)(phys_addr_t page, int prot);
+	phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
+	u32 pt_address_mask;
+};
+
 struct rk_iommu {
 	struct device *dev;
 	void __iomem **bases;
@@ -116,6 +125,7 @@ struct rk_iommudata {
 };
 
 static struct device *dma_dev;
+static const struct rk_iommu_ops *rk_ops;
 
 static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
 				  unsigned int count)
@@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
 #define RK_PTE_PAGE_READABLE      BIT(1)
 #define RK_PTE_PAGE_VALID         BIT(0)
 
-static inline phys_addr_t rk_pte_page_address(u32 pte)
-{
-	return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
-}
-
 static inline bool rk_pte_is_page_valid(u32 pte)
 {
 	return pte & RK_PTE_PAGE_VALID;
@@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
 		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
 
 		dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
-		if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
+		if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
 			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
 			return -EFAULT;
 		}
@@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
 	return 0;
 }
 
+static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
+{
+	return addr;
+}
+
 static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 {
 	void __iomem *base = iommu->bases[index];
@@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 	page_offset = rk_iova_page_offset(iova);
 
 	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
-	mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
+	mmu_dte_addr_phys = rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
 
 	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
 	dte_addr = phys_to_virt(dte_addr_phys);
@@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 	if (!rk_dte_is_pt_valid(dte))
 		goto print_it;
 
-	pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
+	pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
 	pte_addr = phys_to_virt(pte_addr_phys);
 	pte = *pte_addr;
 
 	if (!rk_pte_is_page_valid(pte))
 		goto print_it;
 
-	page_addr_phys = rk_pte_page_address(pte) + page_offset;
+	page_addr_phys = rk_ops->pt_address(pte) + page_offset;
 	page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
 
 print_it:
@@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
 	if (!rk_dte_is_pt_valid(dte))
 		goto out;
 
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	page_table = (u32 *)phys_to_virt(pt_phys);
 	pte = page_table[rk_iova_pte_index(iova)];
 	if (!rk_pte_is_page_valid(pte))
 		goto out;
 
-	phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
+	phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
 out:
 	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
 
@@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	dte = rk_mk_dte(pt_dma);
+	dte = rk_ops->mk_dtentries(pt_dma);
 	*dte_addr = dte;
 
 	rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
 	rk_table_flush(rk_domain,
 		       rk_domain->dt_dma + dte_index * sizeof(u32), 1);
 done:
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	return (u32 *)phys_to_virt(pt_phys);
 }
 
@@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 		if (rk_pte_is_page_valid(pte))
 			goto unwind;
 
-		pte_addr[pte_count] = rk_mk_pte(paddr, prot);
+		pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
 
 		paddr += SPAGE_SIZE;
 	}
@@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 			    pte_count * SPAGE_SIZE);
 
 	iova += pte_count * SPAGE_SIZE;
-	page_phys = rk_pte_page_address(pte_addr[pte_count]);
+	page_phys = rk_ops->pt_address(pte_addr[pte_count]);
 	pr_err("iova: %pad already mapped to %pa cannot remap to phys: %pa prot: %#x\n",
 	       &iova, &page_phys, &paddr, prot);
 
@@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
 	dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
 	pte_index = rk_iova_pte_index(iova);
 	pte_addr = &page_table[pte_index];
-	pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
+
+	pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
 	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
 				paddr, size, prot);
 
@@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
 		return 0;
 	}
 
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
 	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
 	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size);
@@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
 	for (i = 0; i < NUM_DT_ENTRIES; i++) {
 		u32 dte = rk_domain->dt[i];
 		if (rk_dte_is_pt_valid(dte)) {
-			phys_addr_t pt_phys = rk_dte_pt_address(dte);
+			phys_addr_t pt_phys = rk_ops->pt_address(dte);
 			u32 *page_table = phys_to_virt(pt_phys);
 			dma_unmap_single(dma_dev, pt_phys,
 					 SPAGE_SIZE, DMA_TO_DEVICE);
@@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct platform_device *pdev)
 	iommu->dev = dev;
 	iommu->num_mmu = 0;
 
+	if (!rk_ops)
+		rk_ops = of_device_get_match_data(dev);
+
+	/*
+	 * That should not happen unless different versions of the
+	 * hardware block are embedded the same SoC
+	 */
+	WARN_ON(rk_ops != of_device_get_match_data(dev));
+
 	iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
 				    GFP_KERNEL);
 	if (!iommu->bases)
@@ -1277,10 +1297,21 @@ static const struct dev_pm_ops rk_iommu_pm_ops = {
 				pm_runtime_force_resume)
 };
 
+static struct rk_iommu_ops iommu_data_ops_v1 = {
+	.pt_address = &rk_dte_pt_address,
+	.mk_dtentries = &rk_mk_dte,
+	.mk_ptentries = &rk_mk_pte,
+	.dte_addr_phys = &rk_dte_addr_phys,
+	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
+};
+
 static const struct of_device_id rk_iommu_dt_ids[] = {
-	{ .compatible = "rockchip,iommu" },
+	{	.compatible = "rockchip,iommu",
+		.data = &iommu_data_ops_v1,
+	},
 	{ /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
 
 static struct platform_driver rk_iommu_driver = {
 	.probe = rk_iommu_probe,
-- 
2.25.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

Add internal ops to be able to handle incoming variant v2.
The goal is to keep the overall structure of the framework but
to allow to add the evolution of this hardware block.

The ops are global for a SoC because iommu domains are not
attached to a specific devices if they are for a virtuel device like
drm. Use a global variable shouldn't be since SoC usually doesn't
embedded different versions of the iommu hardware block.
If that happen one day a WARN_ON will be displayed at probe time.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
---
version 5:
 - Use of_device_get_match_data()
 - Add internal ops inside the driver

 drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
 1 file changed, 50 insertions(+), 19 deletions(-)

diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 7a2932772fdf..e7b9bcf174b1 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -19,6 +19,7 @@
 #include <linux/iopoll.h>
 #include <linux/list.h>
 #include <linux/mm.h>
+#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
@@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
 	"aclk", "iface",
 };
 
+struct rk_iommu_ops {
+	phys_addr_t (*pt_address)(u32 dte);
+	u32 (*mk_dtentries)(dma_addr_t pt_dma);
+	u32 (*mk_ptentries)(phys_addr_t page, int prot);
+	phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
+	u32 pt_address_mask;
+};
+
 struct rk_iommu {
 	struct device *dev;
 	void __iomem **bases;
@@ -116,6 +125,7 @@ struct rk_iommudata {
 };
 
 static struct device *dma_dev;
+static const struct rk_iommu_ops *rk_ops;
 
 static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
 				  unsigned int count)
@@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
 #define RK_PTE_PAGE_READABLE      BIT(1)
 #define RK_PTE_PAGE_VALID         BIT(0)
 
-static inline phys_addr_t rk_pte_page_address(u32 pte)
-{
-	return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
-}
-
 static inline bool rk_pte_is_page_valid(u32 pte)
 {
 	return pte & RK_PTE_PAGE_VALID;
@@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
 		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
 
 		dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
-		if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
+		if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
 			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
 			return -EFAULT;
 		}
@@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
 	return 0;
 }
 
+static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
+{
+	return addr;
+}
+
 static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 {
 	void __iomem *base = iommu->bases[index];
@@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 	page_offset = rk_iova_page_offset(iova);
 
 	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
-	mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
+	mmu_dte_addr_phys = rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
 
 	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
 	dte_addr = phys_to_virt(dte_addr_phys);
@@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 	if (!rk_dte_is_pt_valid(dte))
 		goto print_it;
 
-	pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
+	pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
 	pte_addr = phys_to_virt(pte_addr_phys);
 	pte = *pte_addr;
 
 	if (!rk_pte_is_page_valid(pte))
 		goto print_it;
 
-	page_addr_phys = rk_pte_page_address(pte) + page_offset;
+	page_addr_phys = rk_ops->pt_address(pte) + page_offset;
 	page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
 
 print_it:
@@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
 	if (!rk_dte_is_pt_valid(dte))
 		goto out;
 
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	page_table = (u32 *)phys_to_virt(pt_phys);
 	pte = page_table[rk_iova_pte_index(iova)];
 	if (!rk_pte_is_page_valid(pte))
 		goto out;
 
-	phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
+	phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
 out:
 	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
 
@@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	dte = rk_mk_dte(pt_dma);
+	dte = rk_ops->mk_dtentries(pt_dma);
 	*dte_addr = dte;
 
 	rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
 	rk_table_flush(rk_domain,
 		       rk_domain->dt_dma + dte_index * sizeof(u32), 1);
 done:
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	return (u32 *)phys_to_virt(pt_phys);
 }
 
@@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 		if (rk_pte_is_page_valid(pte))
 			goto unwind;
 
-		pte_addr[pte_count] = rk_mk_pte(paddr, prot);
+		pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
 
 		paddr += SPAGE_SIZE;
 	}
@@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 			    pte_count * SPAGE_SIZE);
 
 	iova += pte_count * SPAGE_SIZE;
-	page_phys = rk_pte_page_address(pte_addr[pte_count]);
+	page_phys = rk_ops->pt_address(pte_addr[pte_count]);
 	pr_err("iova: %pad already mapped to %pa cannot remap to phys: %pa prot: %#x\n",
 	       &iova, &page_phys, &paddr, prot);
 
@@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
 	dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
 	pte_index = rk_iova_pte_index(iova);
 	pte_addr = &page_table[pte_index];
-	pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
+
+	pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
 	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
 				paddr, size, prot);
 
@@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
 		return 0;
 	}
 
-	pt_phys = rk_dte_pt_address(dte);
+	pt_phys = rk_ops->pt_address(dte);
 	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
 	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
 	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size);
@@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
 	for (i = 0; i < NUM_DT_ENTRIES; i++) {
 		u32 dte = rk_domain->dt[i];
 		if (rk_dte_is_pt_valid(dte)) {
-			phys_addr_t pt_phys = rk_dte_pt_address(dte);
+			phys_addr_t pt_phys = rk_ops->pt_address(dte);
 			u32 *page_table = phys_to_virt(pt_phys);
 			dma_unmap_single(dma_dev, pt_phys,
 					 SPAGE_SIZE, DMA_TO_DEVICE);
@@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct platform_device *pdev)
 	iommu->dev = dev;
 	iommu->num_mmu = 0;
 
+	if (!rk_ops)
+		rk_ops = of_device_get_match_data(dev);
+
+	/*
+	 * That should not happen unless different versions of the
+	 * hardware block are embedded the same SoC
+	 */
+	WARN_ON(rk_ops != of_device_get_match_data(dev));
+
 	iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
 				    GFP_KERNEL);
 	if (!iommu->bases)
@@ -1277,10 +1297,21 @@ static const struct dev_pm_ops rk_iommu_pm_ops = {
 				pm_runtime_force_resume)
 };
 
+static struct rk_iommu_ops iommu_data_ops_v1 = {
+	.pt_address = &rk_dte_pt_address,
+	.mk_dtentries = &rk_mk_dte,
+	.mk_ptentries = &rk_mk_pte,
+	.dte_addr_phys = &rk_dte_addr_phys,
+	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
+};
+
 static const struct of_device_id rk_iommu_dt_ids[] = {
-	{ .compatible = "rockchip,iommu" },
+	{	.compatible = "rockchip,iommu",
+		.data = &iommu_data_ops_v1,
+	},
 	{ /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
 
 static struct platform_driver rk_iommu_driver = {
 	.probe = rk_iommu_probe,
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
  2021-05-21  8:36 ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21  8:36   ` Benjamin Gaignard
  -1 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

This second version of the hardware block has a different bits
mapping for page table entries.
Add the ops matching to this new mapping.
Define a new compatible to distinguish it from the first version.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
---
version 5:
 - Use internal ops to support v2 hardware block
 - Use GENMASK macro.
 - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
   because I believe that is more readable like this.
 - Do not duplicate code.

 drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index e7b9bcf174b1..23253a2f269e 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
 	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
 }
 
+/*
+ * In v2:
+ * 31:12 - PT address bit 31:0
+ * 11: 8 - PT address bit 35:32
+ *  7: 4 - PT address bit 39:36
+ *  3: 1 - Reserved
+ *     0 - 1 if PT @ PT address is valid
+ */
+#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
+#define DTE_HI_MASK1	GENMASK(11, 8)
+#define DTE_HI_MASK2	GENMASK(7, 4)
+#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
+#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */
+#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
+#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
+
+static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
+{
+	u64 dte_v2 = dte;
+
+	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
+		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
+		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
+
+	return (phys_addr_t)dte_v2;
+}
+
 static inline bool rk_dte_is_pt_valid(u32 dte)
 {
 	return dte & RK_DTE_PT_VALID;
@@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
 	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
 }
 
+static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
+{
+	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
+		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
+		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
+
+	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
+}
+
 /*
  * Each PTE has a Page address, some flags and a valid bit:
  * +---------------------+---+-------+-+
@@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
 	return page | flags | RK_PTE_PAGE_VALID;
 }
 
+/*
+ * In v2:
+ * 31:12 - Page address bit 31:0
+ *  11:9 - Page address bit 34:32
+ *   8:4 - Page address bit 39:35
+ *     3 - Security
+ *     2 - Readable
+ *     1 - Writable
+ *     0 - 1 if Page @ Page address is valid
+ */
+#define RK_PTE_PAGE_READABLE_V2      BIT(2)
+#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
+
+static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
+{
+	u32 flags = 0;
+
+	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
+	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
+
+	return rk_mk_dte_v2(page) | flags ;
+}
+
 static u32 rk_mk_pte_invalid(u32 pte)
 {
 	return pte & ~RK_PTE_PAGE_VALID;
@@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
 	return addr;
 }
 
+#define DT_HI_MASK GENMASK_ULL(39, 32)
+#define DT_SHIFT   28
+
+static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
+{
+	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
+}
+
 static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 {
 	void __iomem *base = iommu->bases[index];
@@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
 	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
 };
 
+static struct rk_iommu_ops iommu_data_ops_v2 = {
+	.pt_address = &rk_dte_pt_address_v2,
+	.mk_dtentries = &rk_mk_dte_v2,
+	.mk_ptentries = &rk_mk_pte_v2,
+	.dte_addr_phys = &rk_dte_addr_phys_v2,
+	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
+};
+
 static const struct of_device_id rk_iommu_dt_ids[] = {
 	{	.compatible = "rockchip,iommu",
 		.data = &iommu_data_ops_v1,
 	},
+	{	.compatible = "rockchip,rk3568-iommu",
+		.data = &iommu_data_ops_v2,
+	},
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

This second version of the hardware block has a different bits
mapping for page table entries.
Add the ops matching to this new mapping.
Define a new compatible to distinguish it from the first version.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
---
version 5:
 - Use internal ops to support v2 hardware block
 - Use GENMASK macro.
 - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
   because I believe that is more readable like this.
 - Do not duplicate code.

 drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index e7b9bcf174b1..23253a2f269e 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
 	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
 }
 
+/*
+ * In v2:
+ * 31:12 - PT address bit 31:0
+ * 11: 8 - PT address bit 35:32
+ *  7: 4 - PT address bit 39:36
+ *  3: 1 - Reserved
+ *     0 - 1 if PT @ PT address is valid
+ */
+#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
+#define DTE_HI_MASK1	GENMASK(11, 8)
+#define DTE_HI_MASK2	GENMASK(7, 4)
+#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
+#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */
+#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
+#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
+
+static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
+{
+	u64 dte_v2 = dte;
+
+	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
+		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
+		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
+
+	return (phys_addr_t)dte_v2;
+}
+
 static inline bool rk_dte_is_pt_valid(u32 dte)
 {
 	return dte & RK_DTE_PT_VALID;
@@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
 	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
 }
 
+static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
+{
+	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
+		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
+		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
+
+	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
+}
+
 /*
  * Each PTE has a Page address, some flags and a valid bit:
  * +---------------------+---+-------+-+
@@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
 	return page | flags | RK_PTE_PAGE_VALID;
 }
 
+/*
+ * In v2:
+ * 31:12 - Page address bit 31:0
+ *  11:9 - Page address bit 34:32
+ *   8:4 - Page address bit 39:35
+ *     3 - Security
+ *     2 - Readable
+ *     1 - Writable
+ *     0 - 1 if Page @ Page address is valid
+ */
+#define RK_PTE_PAGE_READABLE_V2      BIT(2)
+#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
+
+static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
+{
+	u32 flags = 0;
+
+	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
+	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
+
+	return rk_mk_dte_v2(page) | flags ;
+}
+
 static u32 rk_mk_pte_invalid(u32 pte)
 {
 	return pte & ~RK_PTE_PAGE_VALID;
@@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
 	return addr;
 }
 
+#define DT_HI_MASK GENMASK_ULL(39, 32)
+#define DT_SHIFT   28
+
+static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
+{
+	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
+}
+
 static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 {
 	void __iomem *base = iommu->bases[index];
@@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
 	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
 };
 
+static struct rk_iommu_ops iommu_data_ops_v2 = {
+	.pt_address = &rk_dte_pt_address_v2,
+	.mk_dtentries = &rk_mk_dte_v2,
+	.mk_ptentries = &rk_mk_pte_v2,
+	.dte_addr_phys = &rk_dte_addr_phys_v2,
+	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
+};
+
 static const struct of_device_id rk_iommu_dt_ids[] = {
 	{	.compatible = "rockchip,iommu",
 		.data = &iommu_data_ops_v1,
 	},
+	{	.compatible = "rockchip,rk3568-iommu",
+		.data = &iommu_data_ops_v2,
+	},
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
-- 
2.25.1


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: devicetree, Benjamin Gaignard, linux-kernel, linux-rockchip,
	iommu, kernel, linux-arm-kernel

This second version of the hardware block has a different bits
mapping for page table entries.
Add the ops matching to this new mapping.
Define a new compatible to distinguish it from the first version.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
---
version 5:
 - Use internal ops to support v2 hardware block
 - Use GENMASK macro.
 - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
   because I believe that is more readable like this.
 - Do not duplicate code.

 drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index e7b9bcf174b1..23253a2f269e 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
 	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
 }
 
+/*
+ * In v2:
+ * 31:12 - PT address bit 31:0
+ * 11: 8 - PT address bit 35:32
+ *  7: 4 - PT address bit 39:36
+ *  3: 1 - Reserved
+ *     0 - 1 if PT @ PT address is valid
+ */
+#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
+#define DTE_HI_MASK1	GENMASK(11, 8)
+#define DTE_HI_MASK2	GENMASK(7, 4)
+#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
+#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */
+#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
+#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
+
+static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
+{
+	u64 dte_v2 = dte;
+
+	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
+		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
+		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
+
+	return (phys_addr_t)dte_v2;
+}
+
 static inline bool rk_dte_is_pt_valid(u32 dte)
 {
 	return dte & RK_DTE_PT_VALID;
@@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
 	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
 }
 
+static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
+{
+	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
+		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
+		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
+
+	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
+}
+
 /*
  * Each PTE has a Page address, some flags and a valid bit:
  * +---------------------+---+-------+-+
@@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
 	return page | flags | RK_PTE_PAGE_VALID;
 }
 
+/*
+ * In v2:
+ * 31:12 - Page address bit 31:0
+ *  11:9 - Page address bit 34:32
+ *   8:4 - Page address bit 39:35
+ *     3 - Security
+ *     2 - Readable
+ *     1 - Writable
+ *     0 - 1 if Page @ Page address is valid
+ */
+#define RK_PTE_PAGE_READABLE_V2      BIT(2)
+#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
+
+static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
+{
+	u32 flags = 0;
+
+	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
+	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
+
+	return rk_mk_dte_v2(page) | flags ;
+}
+
 static u32 rk_mk_pte_invalid(u32 pte)
 {
 	return pte & ~RK_PTE_PAGE_VALID;
@@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
 	return addr;
 }
 
+#define DT_HI_MASK GENMASK_ULL(39, 32)
+#define DT_SHIFT   28
+
+static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
+{
+	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
+}
+
 static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 {
 	void __iomem *base = iommu->bases[index];
@@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
 	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
 };
 
+static struct rk_iommu_ops iommu_data_ops_v2 = {
+	.pt_address = &rk_dte_pt_address_v2,
+	.mk_dtentries = &rk_mk_dte_v2,
+	.mk_ptentries = &rk_mk_pte_v2,
+	.dte_addr_phys = &rk_dte_addr_phys_v2,
+	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
+};
+
 static const struct of_device_id rk_iommu_dt_ids[] = {
 	{	.compatible = "rockchip,iommu",
 		.data = &iommu_data_ops_v1,
 	},
+	{	.compatible = "rockchip,rk3568-iommu",
+		.data = &iommu_data_ops_v2,
+	},
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
-- 
2.25.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
@ 2021-05-21  8:36   ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21  8:36 UTC (permalink / raw)
  To: joro, will, robh+dt, heiko, xxm, robin.murphy
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

This second version of the hardware block has a different bits
mapping for page table entries.
Add the ops matching to this new mapping.
Define a new compatible to distinguish it from the first version.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
---
version 5:
 - Use internal ops to support v2 hardware block
 - Use GENMASK macro.
 - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
   because I believe that is more readable like this.
 - Do not duplicate code.

 drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index e7b9bcf174b1..23253a2f269e 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
 	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
 }
 
+/*
+ * In v2:
+ * 31:12 - PT address bit 31:0
+ * 11: 8 - PT address bit 35:32
+ *  7: 4 - PT address bit 39:36
+ *  3: 1 - Reserved
+ *     0 - 1 if PT @ PT address is valid
+ */
+#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
+#define DTE_HI_MASK1	GENMASK(11, 8)
+#define DTE_HI_MASK2	GENMASK(7, 4)
+#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
+#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */
+#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
+#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
+
+static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
+{
+	u64 dte_v2 = dte;
+
+	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
+		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
+		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
+
+	return (phys_addr_t)dte_v2;
+}
+
 static inline bool rk_dte_is_pt_valid(u32 dte)
 {
 	return dte & RK_DTE_PT_VALID;
@@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
 	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
 }
 
+static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
+{
+	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
+		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
+		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
+
+	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
+}
+
 /*
  * Each PTE has a Page address, some flags and a valid bit:
  * +---------------------+---+-------+-+
@@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
 	return page | flags | RK_PTE_PAGE_VALID;
 }
 
+/*
+ * In v2:
+ * 31:12 - Page address bit 31:0
+ *  11:9 - Page address bit 34:32
+ *   8:4 - Page address bit 39:35
+ *     3 - Security
+ *     2 - Readable
+ *     1 - Writable
+ *     0 - 1 if Page @ Page address is valid
+ */
+#define RK_PTE_PAGE_READABLE_V2      BIT(2)
+#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
+
+static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
+{
+	u32 flags = 0;
+
+	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
+	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
+
+	return rk_mk_dte_v2(page) | flags ;
+}
+
 static u32 rk_mk_pte_invalid(u32 pte)
 {
 	return pte & ~RK_PTE_PAGE_VALID;
@@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
 	return addr;
 }
 
+#define DT_HI_MASK GENMASK_ULL(39, 32)
+#define DT_SHIFT   28
+
+static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
+{
+	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
+}
+
 static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 {
 	void __iomem *base = iommu->bases[index];
@@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
 	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
 };
 
+static struct rk_iommu_ops iommu_data_ops_v2 = {
+	.pt_address = &rk_dte_pt_address_v2,
+	.mk_dtentries = &rk_mk_dte_v2,
+	.mk_ptentries = &rk_mk_pte_v2,
+	.dte_addr_phys = &rk_dte_addr_phys_v2,
+	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
+};
+
 static const struct of_device_id rk_iommu_dt_ids[] = {
 	{	.compatible = "rockchip,iommu",
 		.data = &iommu_data_ops_v1,
 	},
+	{	.compatible = "rockchip,rk3568-iommu",
+		.data = &iommu_data_ops_v2,
+	},
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
  2021-05-21  8:36   ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21  9:16     ` Heiko Stübner
  -1 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:16 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

Am Freitag, 21. Mai 2021, 10:36:36 CEST schrieb Benjamin Gaignard:
> Add internal ops to be able to handle incoming variant v2.
> The goal is to keep the overall structure of the framework but
> to allow to add the evolution of this hardware block.
> 
> The ops are global for a SoC because iommu domains are not
> attached to a specific devices if they are for a virtuel device like
> drm. Use a global variable shouldn't be since SoC usually doesn't
> embedded different versions of the iommu hardware block.
> If that happen one day a WARN_ON will be displayed at probe time.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>




^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21  9:16     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:16 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

Am Freitag, 21. Mai 2021, 10:36:36 CEST schrieb Benjamin Gaignard:
> Add internal ops to be able to handle incoming variant v2.
> The goal is to keep the overall structure of the framework but
> to allow to add the evolution of this hardware block.
> 
> The ops are global for a SoC because iommu domains are not
> attached to a specific devices if they are for a virtuel device like
> drm. Use a global variable shouldn't be since SoC usually doesn't
> embedded different versions of the iommu hardware block.
> If that happen one day a WARN_ON will be displayed at probe time.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>




_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21  9:16     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:16 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: devicetree, Benjamin Gaignard, linux-kernel, linux-rockchip,
	iommu, kernel, linux-arm-kernel

Am Freitag, 21. Mai 2021, 10:36:36 CEST schrieb Benjamin Gaignard:
> Add internal ops to be able to handle incoming variant v2.
> The goal is to keep the overall structure of the framework but
> to allow to add the evolution of this hardware block.
> 
> The ops are global for a SoC because iommu domains are not
> attached to a specific devices if they are for a virtuel device like
> drm. Use a global variable shouldn't be since SoC usually doesn't
> embedded different versions of the iommu hardware block.
> If that happen one day a WARN_ON will be displayed at probe time.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>



_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21  9:16     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:16 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

Am Freitag, 21. Mai 2021, 10:36:36 CEST schrieb Benjamin Gaignard:
> Add internal ops to be able to handle incoming variant v2.
> The goal is to keep the overall structure of the framework but
> to allow to add the evolution of this hardware block.
> 
> The ops are global for a SoC because iommu domains are not
> attached to a specific devices if they are for a virtuel device like
> drm. Use a global variable shouldn't be since SoC usually doesn't
> embedded different versions of the iommu hardware block.
> If that happen one day a WARN_ON will be displayed at probe time.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>




_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
  2021-05-21  8:36   ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21  9:17     ` Heiko Stübner
  -1 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:17 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

Am Freitag, 21. Mai 2021, 10:36:37 CEST schrieb Benjamin Gaignard:
> This second version of the hardware block has a different bits
> mapping for page table entries.
> Add the ops matching to this new mapping.
> Define a new compatible to distinguish it from the first version.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>

> ---
> version 5:
>  - Use internal ops to support v2 hardware block
>  - Use GENMASK macro.
>  - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
>    because I believe that is more readable like this.
>  - Do not duplicate code.
> 
>  drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
>  1 file changed, 78 insertions(+)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index e7b9bcf174b1..23253a2f269e 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
>  	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
>  }
>  
> +/*
> + * In v2:
> + * 31:12 - PT address bit 31:0
> + * 11: 8 - PT address bit 35:32
> + *  7: 4 - PT address bit 39:36
> + *  3: 1 - Reserved
> + *     0 - 1 if PT @ PT address is valid
> + */
> +#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
> +#define DTE_HI_MASK1	GENMASK(11, 8)
> +#define DTE_HI_MASK2	GENMASK(7, 4)
> +#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
> +#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */
> +#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
> +#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
> +
> +static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
> +{
> +	u64 dte_v2 = dte;
> +
> +	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
> +		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
> +		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
> +
> +	return (phys_addr_t)dte_v2;
> +}
> +
>  static inline bool rk_dte_is_pt_valid(u32 dte)
>  {
>  	return dte & RK_DTE_PT_VALID;
> @@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>  	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
>  }
>  
> +static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
> +{
> +	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
> +		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
> +		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
> +
> +	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
> +}
> +
>  /*
>   * Each PTE has a Page address, some flags and a valid bit:
>   * +---------------------+---+-------+-+
> @@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
>  	return page | flags | RK_PTE_PAGE_VALID;
>  }
>  
> +/*
> + * In v2:
> + * 31:12 - Page address bit 31:0
> + *  11:9 - Page address bit 34:32
> + *   8:4 - Page address bit 39:35
> + *     3 - Security
> + *     2 - Readable
> + *     1 - Writable
> + *     0 - 1 if Page @ Page address is valid
> + */
> +#define RK_PTE_PAGE_READABLE_V2      BIT(2)
> +#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
> +
> +static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
> +{
> +	u32 flags = 0;
> +
> +	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
> +	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
> +
> +	return rk_mk_dte_v2(page) | flags ;
> +}
> +
>  static u32 rk_mk_pte_invalid(u32 pte)
>  {
>  	return pte & ~RK_PTE_PAGE_VALID;
> @@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>  	return addr;
>  }
>  
> +#define DT_HI_MASK GENMASK_ULL(39, 32)
> +#define DT_SHIFT   28
> +
> +static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
> +{
> +	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
> +}
> +
>  static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>  {
>  	void __iomem *base = iommu->bases[index];
> @@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
>  	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>  };
>  
> +static struct rk_iommu_ops iommu_data_ops_v2 = {
> +	.pt_address = &rk_dte_pt_address_v2,
> +	.mk_dtentries = &rk_mk_dte_v2,
> +	.mk_ptentries = &rk_mk_pte_v2,
> +	.dte_addr_phys = &rk_dte_addr_phys_v2,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
> +};
> +
>  static const struct of_device_id rk_iommu_dt_ids[] = {
>  	{	.compatible = "rockchip,iommu",
>  		.data = &iommu_data_ops_v1,
>  	},
> +	{	.compatible = "rockchip,rk3568-iommu",
> +		.data = &iommu_data_ops_v2,
> +	},
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
> 





^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
@ 2021-05-21  9:17     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:17 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

Am Freitag, 21. Mai 2021, 10:36:37 CEST schrieb Benjamin Gaignard:
> This second version of the hardware block has a different bits
> mapping for page table entries.
> Add the ops matching to this new mapping.
> Define a new compatible to distinguish it from the first version.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>

> ---
> version 5:
>  - Use internal ops to support v2 hardware block
>  - Use GENMASK macro.
>  - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
>    because I believe that is more readable like this.
>  - Do not duplicate code.
> 
>  drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
>  1 file changed, 78 insertions(+)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index e7b9bcf174b1..23253a2f269e 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
>  	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
>  }
>  
> +/*
> + * In v2:
> + * 31:12 - PT address bit 31:0
> + * 11: 8 - PT address bit 35:32
> + *  7: 4 - PT address bit 39:36
> + *  3: 1 - Reserved
> + *     0 - 1 if PT @ PT address is valid
> + */
> +#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
> +#define DTE_HI_MASK1	GENMASK(11, 8)
> +#define DTE_HI_MASK2	GENMASK(7, 4)
> +#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
> +#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */
> +#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
> +#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
> +
> +static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
> +{
> +	u64 dte_v2 = dte;
> +
> +	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
> +		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
> +		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
> +
> +	return (phys_addr_t)dte_v2;
> +}
> +
>  static inline bool rk_dte_is_pt_valid(u32 dte)
>  {
>  	return dte & RK_DTE_PT_VALID;
> @@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>  	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
>  }
>  
> +static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
> +{
> +	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
> +		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
> +		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
> +
> +	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
> +}
> +
>  /*
>   * Each PTE has a Page address, some flags and a valid bit:
>   * +---------------------+---+-------+-+
> @@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
>  	return page | flags | RK_PTE_PAGE_VALID;
>  }
>  
> +/*
> + * In v2:
> + * 31:12 - Page address bit 31:0
> + *  11:9 - Page address bit 34:32
> + *   8:4 - Page address bit 39:35
> + *     3 - Security
> + *     2 - Readable
> + *     1 - Writable
> + *     0 - 1 if Page @ Page address is valid
> + */
> +#define RK_PTE_PAGE_READABLE_V2      BIT(2)
> +#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
> +
> +static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
> +{
> +	u32 flags = 0;
> +
> +	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
> +	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
> +
> +	return rk_mk_dte_v2(page) | flags ;
> +}
> +
>  static u32 rk_mk_pte_invalid(u32 pte)
>  {
>  	return pte & ~RK_PTE_PAGE_VALID;
> @@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>  	return addr;
>  }
>  
> +#define DT_HI_MASK GENMASK_ULL(39, 32)
> +#define DT_SHIFT   28
> +
> +static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
> +{
> +	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
> +}
> +
>  static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>  {
>  	void __iomem *base = iommu->bases[index];
> @@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
>  	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>  };
>  
> +static struct rk_iommu_ops iommu_data_ops_v2 = {
> +	.pt_address = &rk_dte_pt_address_v2,
> +	.mk_dtentries = &rk_mk_dte_v2,
> +	.mk_ptentries = &rk_mk_pte_v2,
> +	.dte_addr_phys = &rk_dte_addr_phys_v2,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
> +};
> +
>  static const struct of_device_id rk_iommu_dt_ids[] = {
>  	{	.compatible = "rockchip,iommu",
>  		.data = &iommu_data_ops_v1,
>  	},
> +	{	.compatible = "rockchip,rk3568-iommu",
> +		.data = &iommu_data_ops_v2,
> +	},
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
> 





_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
@ 2021-05-21  9:17     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:17 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: devicetree, Benjamin Gaignard, linux-kernel, linux-rockchip,
	iommu, kernel, linux-arm-kernel

Am Freitag, 21. Mai 2021, 10:36:37 CEST schrieb Benjamin Gaignard:
> This second version of the hardware block has a different bits
> mapping for page table entries.
> Add the ops matching to this new mapping.
> Define a new compatible to distinguish it from the first version.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>

> ---
> version 5:
>  - Use internal ops to support v2 hardware block
>  - Use GENMASK macro.
>  - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
>    because I believe that is more readable like this.
>  - Do not duplicate code.
> 
>  drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
>  1 file changed, 78 insertions(+)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index e7b9bcf174b1..23253a2f269e 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
>  	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
>  }
>  
> +/*
> + * In v2:
> + * 31:12 - PT address bit 31:0
> + * 11: 8 - PT address bit 35:32
> + *  7: 4 - PT address bit 39:36
> + *  3: 1 - Reserved
> + *     0 - 1 if PT @ PT address is valid
> + */
> +#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
> +#define DTE_HI_MASK1	GENMASK(11, 8)
> +#define DTE_HI_MASK2	GENMASK(7, 4)
> +#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
> +#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */
> +#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
> +#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
> +
> +static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
> +{
> +	u64 dte_v2 = dte;
> +
> +	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
> +		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
> +		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
> +
> +	return (phys_addr_t)dte_v2;
> +}
> +
>  static inline bool rk_dte_is_pt_valid(u32 dte)
>  {
>  	return dte & RK_DTE_PT_VALID;
> @@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>  	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
>  }
>  
> +static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
> +{
> +	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
> +		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
> +		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
> +
> +	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
> +}
> +
>  /*
>   * Each PTE has a Page address, some flags and a valid bit:
>   * +---------------------+---+-------+-+
> @@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
>  	return page | flags | RK_PTE_PAGE_VALID;
>  }
>  
> +/*
> + * In v2:
> + * 31:12 - Page address bit 31:0
> + *  11:9 - Page address bit 34:32
> + *   8:4 - Page address bit 39:35
> + *     3 - Security
> + *     2 - Readable
> + *     1 - Writable
> + *     0 - 1 if Page @ Page address is valid
> + */
> +#define RK_PTE_PAGE_READABLE_V2      BIT(2)
> +#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
> +
> +static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
> +{
> +	u32 flags = 0;
> +
> +	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
> +	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
> +
> +	return rk_mk_dte_v2(page) | flags ;
> +}
> +
>  static u32 rk_mk_pte_invalid(u32 pte)
>  {
>  	return pte & ~RK_PTE_PAGE_VALID;
> @@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>  	return addr;
>  }
>  
> +#define DT_HI_MASK GENMASK_ULL(39, 32)
> +#define DT_SHIFT   28
> +
> +static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
> +{
> +	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
> +}
> +
>  static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>  {
>  	void __iomem *base = iommu->bases[index];
> @@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
>  	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>  };
>  
> +static struct rk_iommu_ops iommu_data_ops_v2 = {
> +	.pt_address = &rk_dte_pt_address_v2,
> +	.mk_dtentries = &rk_mk_dte_v2,
> +	.mk_ptentries = &rk_mk_pte_v2,
> +	.dte_addr_phys = &rk_dte_addr_phys_v2,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
> +};
> +
>  static const struct of_device_id rk_iommu_dt_ids[] = {
>  	{	.compatible = "rockchip,iommu",
>  		.data = &iommu_data_ops_v1,
>  	},
> +	{	.compatible = "rockchip,rk3568-iommu",
> +		.data = &iommu_data_ops_v2,
> +	},
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
> 




_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
@ 2021-05-21  9:17     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:17 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard

Am Freitag, 21. Mai 2021, 10:36:37 CEST schrieb Benjamin Gaignard:
> This second version of the hardware block has a different bits
> mapping for page table entries.
> Add the ops matching to this new mapping.
> Define a new compatible to distinguish it from the first version.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>

> ---
> version 5:
>  - Use internal ops to support v2 hardware block
>  - Use GENMASK macro.
>  - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
>    because I believe that is more readable like this.
>  - Do not duplicate code.
> 
>  drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
>  1 file changed, 78 insertions(+)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index e7b9bcf174b1..23253a2f269e 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
>  	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
>  }
>  
> +/*
> + * In v2:
> + * 31:12 - PT address bit 31:0
> + * 11: 8 - PT address bit 35:32
> + *  7: 4 - PT address bit 39:36
> + *  3: 1 - Reserved
> + *     0 - 1 if PT @ PT address is valid
> + */
> +#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
> +#define DTE_HI_MASK1	GENMASK(11, 8)
> +#define DTE_HI_MASK2	GENMASK(7, 4)
> +#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
> +#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */
> +#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
> +#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
> +
> +static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
> +{
> +	u64 dte_v2 = dte;
> +
> +	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
> +		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
> +		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
> +
> +	return (phys_addr_t)dte_v2;
> +}
> +
>  static inline bool rk_dte_is_pt_valid(u32 dte)
>  {
>  	return dte & RK_DTE_PT_VALID;
> @@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>  	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
>  }
>  
> +static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
> +{
> +	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
> +		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
> +		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
> +
> +	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
> +}
> +
>  /*
>   * Each PTE has a Page address, some flags and a valid bit:
>   * +---------------------+---+-------+-+
> @@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
>  	return page | flags | RK_PTE_PAGE_VALID;
>  }
>  
> +/*
> + * In v2:
> + * 31:12 - Page address bit 31:0
> + *  11:9 - Page address bit 34:32
> + *   8:4 - Page address bit 39:35
> + *     3 - Security
> + *     2 - Readable
> + *     1 - Writable
> + *     0 - 1 if Page @ Page address is valid
> + */
> +#define RK_PTE_PAGE_READABLE_V2      BIT(2)
> +#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
> +
> +static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
> +{
> +	u32 flags = 0;
> +
> +	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
> +	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
> +
> +	return rk_mk_dte_v2(page) | flags ;
> +}
> +
>  static u32 rk_mk_pte_invalid(u32 pte)
>  {
>  	return pte & ~RK_PTE_PAGE_VALID;
> @@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>  	return addr;
>  }
>  
> +#define DT_HI_MASK GENMASK_ULL(39, 32)
> +#define DT_SHIFT   28
> +
> +static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
> +{
> +	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
> +}
> +
>  static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>  {
>  	void __iomem *base = iommu->bases[index];
> @@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
>  	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>  };
>  
> +static struct rk_iommu_ops iommu_data_ops_v2 = {
> +	.pt_address = &rk_dte_pt_address_v2,
> +	.mk_dtentries = &rk_mk_dte_v2,
> +	.mk_ptentries = &rk_mk_pte_v2,
> +	.dte_addr_phys = &rk_dte_addr_phys_v2,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
> +};
> +
>  static const struct of_device_id rk_iommu_dt_ids[] = {
>  	{	.compatible = "rockchip,iommu",
>  		.data = &iommu_data_ops_v1,
>  	},
> +	{	.compatible = "rockchip,rk3568-iommu",
> +		.data = &iommu_data_ops_v2,
> +	},
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
> 





_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
  2021-05-21  8:36   ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21  9:18     ` Heiko Stübner
  -1 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:18 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Am Freitag, 21. Mai 2021, 10:36:35 CEST schrieb Benjamin Gaignard:
> Add compatible for the second version of IOMMU hardware block.
> RK356x IOMMU can also be link to a power domain.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>

> ---
>  .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> index 099fc2578b54..d2e28a9e3545 100644
> --- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> +++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> @@ -19,7 +19,9 @@ description: |+
>  
>  properties:
>    compatible:
> -    const: rockchip,iommu
> +    enum:
> +      - rockchip,iommu
> +      - rockchip,rk3568-iommu
>  
>    reg:
>      items:
> @@ -48,6 +50,9 @@ properties:
>    "#iommu-cells":
>      const: 0
>  
> +  power-domains:
> +    maxItems: 1
> +
>    rockchip,disable-mmu-reset:
>      $ref: /schemas/types.yaml#/definitions/flag
>      description: |
> 





^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
@ 2021-05-21  9:18     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:18 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Am Freitag, 21. Mai 2021, 10:36:35 CEST schrieb Benjamin Gaignard:
> Add compatible for the second version of IOMMU hardware block.
> RK356x IOMMU can also be link to a power domain.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>

> ---
>  .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> index 099fc2578b54..d2e28a9e3545 100644
> --- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> +++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> @@ -19,7 +19,9 @@ description: |+
>  
>  properties:
>    compatible:
> -    const: rockchip,iommu
> +    enum:
> +      - rockchip,iommu
> +      - rockchip,rk3568-iommu
>  
>    reg:
>      items:
> @@ -48,6 +50,9 @@ properties:
>    "#iommu-cells":
>      const: 0
>  
> +  power-domains:
> +    maxItems: 1
> +
>    rockchip,disable-mmu-reset:
>      $ref: /schemas/types.yaml#/definitions/flag
>      description: |
> 





_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
@ 2021-05-21  9:18     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:18 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: devicetree, Benjamin Gaignard, Rob Herring, linux-kernel,
	linux-rockchip, iommu, kernel, linux-arm-kernel

Am Freitag, 21. Mai 2021, 10:36:35 CEST schrieb Benjamin Gaignard:
> Add compatible for the second version of IOMMU hardware block.
> RK356x IOMMU can also be link to a power domain.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>

> ---
>  .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> index 099fc2578b54..d2e28a9e3545 100644
> --- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> +++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> @@ -19,7 +19,9 @@ description: |+
>  
>  properties:
>    compatible:
> -    const: rockchip,iommu
> +    enum:
> +      - rockchip,iommu
> +      - rockchip,rk3568-iommu
>  
>    reg:
>      items:
> @@ -48,6 +50,9 @@ properties:
>    "#iommu-cells":
>      const: 0
>  
> +  power-domains:
> +    maxItems: 1
> +
>    rockchip,disable-mmu-reset:
>      $ref: /schemas/types.yaml#/definitions/flag
>      description: |
> 




_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
@ 2021-05-21  9:18     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:18 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Am Freitag, 21. Mai 2021, 10:36:35 CEST schrieb Benjamin Gaignard:
> Add compatible for the second version of IOMMU hardware block.
> RK356x IOMMU can also be link to a power domain.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>

> ---
>  .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> index 099fc2578b54..d2e28a9e3545 100644
> --- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> +++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> @@ -19,7 +19,9 @@ description: |+
>  
>  properties:
>    compatible:
> -    const: rockchip,iommu
> +    enum:
> +      - rockchip,iommu
> +      - rockchip,rk3568-iommu
>  
>    reg:
>      items:
> @@ -48,6 +50,9 @@ properties:
>    "#iommu-cells":
>      const: 0
>  
> +  power-domains:
> +    maxItems: 1
> +
>    rockchip,disable-mmu-reset:
>      $ref: /schemas/types.yaml#/definitions/flag
>      description: |
> 





_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 1/4] dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
  2021-05-21  8:36   ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21  9:18     ` Heiko Stübner
  -1 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:18 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Am Freitag, 21. Mai 2021, 10:36:34 CEST schrieb Benjamin Gaignard:
> Convert Rockchip IOMMU to DT schema
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>




^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 1/4] dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
@ 2021-05-21  9:18     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:18 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Am Freitag, 21. Mai 2021, 10:36:34 CEST schrieb Benjamin Gaignard:
> Convert Rockchip IOMMU to DT schema
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>




_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 1/4] dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
@ 2021-05-21  9:18     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:18 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: devicetree, Benjamin Gaignard, Rob Herring, linux-kernel,
	linux-rockchip, iommu, kernel, linux-arm-kernel

Am Freitag, 21. Mai 2021, 10:36:34 CEST schrieb Benjamin Gaignard:
> Convert Rockchip IOMMU to DT schema
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>



_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 1/4] dt-bindings: iommu: rockchip: Convert IOMMU to DT schema
@ 2021-05-21  9:18     ` Heiko Stübner
  0 siblings, 0 replies; 56+ messages in thread
From: Heiko Stübner @ 2021-05-21  9:18 UTC (permalink / raw)
  To: joro, will, robh+dt, xxm, robin.murphy, Benjamin Gaignard
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Benjamin Gaignard, Rob Herring

Am Freitag, 21. Mai 2021, 10:36:34 CEST schrieb Benjamin Gaignard:
> Convert Rockchip IOMMU to DT schema
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Reviewed-by: Heiko Stuebner <heiko@sntech.de>




_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
  2021-05-21  8:36   ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21 11:52     ` Robin Murphy
  -1 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 11:52 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Rob Herring

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> Add compatible for the second version of IOMMU hardware block.
> RK356x IOMMU can also be link to a power domain.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---
>   .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
>   1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> index 099fc2578b54..d2e28a9e3545 100644
> --- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> +++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> @@ -19,7 +19,9 @@ description: |+
>   
>   properties:
>     compatible:
> -    const: rockchip,iommu
> +    enum:
> +      - rockchip,iommu
> +      - rockchip,rk3568-iommu
>   
>     reg:
>       items:
> @@ -48,6 +50,9 @@ properties:
>     "#iommu-cells":
>       const: 0
>   
> +  power-domains:
> +    maxItems: 1
> +

Nit: power domains are already present on various IOMMU nodes since 
RK3288 - it feels like strictly this should be in patch #1 to fix the 
existing binding as part of the conversion, but on the other hand I 
can't really imagine anyone caring *that* much about dtscheck bisecting 
cleanly :)

Robin.

>     rockchip,disable-mmu-reset:
>       $ref: /schemas/types.yaml#/definitions/flag
>       description: |
> 

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
@ 2021-05-21 11:52     ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 11:52 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Rob Herring

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> Add compatible for the second version of IOMMU hardware block.
> RK356x IOMMU can also be link to a power domain.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---
>   .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
>   1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> index 099fc2578b54..d2e28a9e3545 100644
> --- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> +++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> @@ -19,7 +19,9 @@ description: |+
>   
>   properties:
>     compatible:
> -    const: rockchip,iommu
> +    enum:
> +      - rockchip,iommu
> +      - rockchip,rk3568-iommu
>   
>     reg:
>       items:
> @@ -48,6 +50,9 @@ properties:
>     "#iommu-cells":
>       const: 0
>   
> +  power-domains:
> +    maxItems: 1
> +

Nit: power domains are already present on various IOMMU nodes since 
RK3288 - it feels like strictly this should be in patch #1 to fix the 
existing binding as part of the conversion, but on the other hand I 
can't really imagine anyone caring *that* much about dtscheck bisecting 
cleanly :)

Robin.

>     rockchip,disable-mmu-reset:
>       $ref: /schemas/types.yaml#/definitions/flag
>       description: |
> 

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
@ 2021-05-21 11:52     ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 11:52 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: devicetree, Rob Herring, linux-kernel, linux-rockchip, iommu,
	kernel, linux-arm-kernel

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> Add compatible for the second version of IOMMU hardware block.
> RK356x IOMMU can also be link to a power domain.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---
>   .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
>   1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> index 099fc2578b54..d2e28a9e3545 100644
> --- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> +++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> @@ -19,7 +19,9 @@ description: |+
>   
>   properties:
>     compatible:
> -    const: rockchip,iommu
> +    enum:
> +      - rockchip,iommu
> +      - rockchip,rk3568-iommu
>   
>     reg:
>       items:
> @@ -48,6 +50,9 @@ properties:
>     "#iommu-cells":
>       const: 0
>   
> +  power-domains:
> +    maxItems: 1
> +

Nit: power domains are already present on various IOMMU nodes since 
RK3288 - it feels like strictly this should be in patch #1 to fix the 
existing binding as part of the conversion, but on the other hand I 
can't really imagine anyone caring *that* much about dtscheck bisecting 
cleanly :)

Robin.

>     rockchip,disable-mmu-reset:
>       $ref: /schemas/types.yaml#/definitions/flag
>       description: |
> 
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2
@ 2021-05-21 11:52     ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 11:52 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel, Rob Herring

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> Add compatible for the second version of IOMMU hardware block.
> RK356x IOMMU can also be link to a power domain.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---
>   .../devicetree/bindings/iommu/rockchip,iommu.yaml          | 7 ++++++-
>   1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> index 099fc2578b54..d2e28a9e3545 100644
> --- a/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> +++ b/Documentation/devicetree/bindings/iommu/rockchip,iommu.yaml
> @@ -19,7 +19,9 @@ description: |+
>   
>   properties:
>     compatible:
> -    const: rockchip,iommu
> +    enum:
> +      - rockchip,iommu
> +      - rockchip,rk3568-iommu
>   
>     reg:
>       items:
> @@ -48,6 +50,9 @@ properties:
>     "#iommu-cells":
>       const: 0
>   
> +  power-domains:
> +    maxItems: 1
> +

Nit: power domains are already present on various IOMMU nodes since 
RK3288 - it feels like strictly this should be in patch #1 to fix the 
existing binding as part of the conversion, but on the other hand I 
can't really imagine anyone caring *that* much about dtscheck bisecting 
cleanly :)

Robin.

>     rockchip,disable-mmu-reset:
>       $ref: /schemas/types.yaml#/definitions/flag
>       description: |
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
  2021-05-21  8:36   ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21 12:58     ` Robin Murphy
  -1 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 12:58 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> Add internal ops to be able to handle incoming variant v2.
> The goal is to keep the overall structure of the framework but
> to allow to add the evolution of this hardware block.
> 
> The ops are global for a SoC because iommu domains are not
> attached to a specific devices if they are for a virtuel device like
> drm. Use a global variable shouldn't be since SoC usually doesn't
> embedded different versions of the iommu hardware block.
> If that happen one day a WARN_ON will be displayed at probe time.

IMO it would be a grievous error if such a "virtual device" ever gets 
near the IOMMU API, so personally I wouldn't use that as a justification 
for anything :)

FWIW you should be OK to handle things on a per-instance basis, it just 
means you have to defer some of the domain setup to .attach_dev time, 
like various other drivers do. That said, there's nothing wrong with the 
global if we do expect instances to be consistent across any given 
Rockchip SoC (and my gut feeling is that we probably should).

> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
> version 5:
>   - Use of_device_get_match_data()
>   - Add internal ops inside the driver
> 
>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>   1 file changed, 50 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index 7a2932772fdf..e7b9bcf174b1 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -19,6 +19,7 @@
>   #include <linux/iopoll.h>
>   #include <linux/list.h>
>   #include <linux/mm.h>
> +#include <linux/module.h>

This seems to be an unrelated and unnecessary change.

>   #include <linux/init.h>
>   #include <linux/of.h>
>   #include <linux/of_iommu.h>
> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>   	"aclk", "iface",
>   };
>   
> +struct rk_iommu_ops {
> +	phys_addr_t (*pt_address)(u32 dte);
> +	u32 (*mk_dtentries)(dma_addr_t pt_dma);
> +	u32 (*mk_ptentries)(phys_addr_t page, int prot);
> +	phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
> +	u32 pt_address_mask;
> +};
> +
>   struct rk_iommu {
>   	struct device *dev;
>   	void __iomem **bases;
> @@ -116,6 +125,7 @@ struct rk_iommudata {
>   };
>   
>   static struct device *dma_dev;
> +static const struct rk_iommu_ops *rk_ops;
>   
>   static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
>   				  unsigned int count)
> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>   #define RK_PTE_PAGE_READABLE      BIT(1)
>   #define RK_PTE_PAGE_VALID         BIT(0)
>   
> -static inline phys_addr_t rk_pte_page_address(u32 pte)
> -{
> -	return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
> -}
> -
>   static inline bool rk_pte_is_page_valid(u32 pte)
>   {
>   	return pte & RK_PTE_PAGE_VALID;
> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
>   		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
>   
>   		dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
> -		if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
> +		if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {

Nit: might it make more sense to do something like:

		dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
		rk_iommu_write(... dte_addr)
		if (rk_iommu_read(...) != dte_addr)

so that you don't need to bother defining ->pt_address_mask for just 
this one sanity-check?

>   			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
>   			return -EFAULT;
>   		}
> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
>   	return 0;
>   }
>   
> +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)

The argument type here should be u32, since it's a DTE, not a physical 
address...

> +{
> +	return addr;
> +}
> +
>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   {
>   	void __iomem *base = iommu->bases[index];
> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   	page_offset = rk_iova_page_offset(iova);
>   
>   	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
> -	mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
> +	mmu_dte_addr_phys = rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);

...and the cast here should not be here, since it *is* the conversion 
that the called function is supposed to be performing.

>   	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>   	dte_addr = phys_to_virt(dte_addr_phys);
> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   	if (!rk_dte_is_pt_valid(dte))
>   		goto print_it;
>   
> -	pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
> +	pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>   	pte_addr = phys_to_virt(pte_addr_phys);
>   	pte = *pte_addr;
>   
>   	if (!rk_pte_is_page_valid(pte))
>   		goto print_it;
>   
> -	page_addr_phys = rk_pte_page_address(pte) + page_offset;
> +	page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>   	page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>   
>   print_it:
> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
>   	if (!rk_dte_is_pt_valid(dte))
>   		goto out;
>   
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	page_table = (u32 *)phys_to_virt(pt_phys);
>   	pte = page_table[rk_iova_pte_index(iova)];
>   	if (!rk_pte_is_page_valid(pte))
>   		goto out;
>   
> -	phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
> +	phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>   out:
>   	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>   
> @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
>   		return ERR_PTR(-ENOMEM);
>   	}
>   
> -	dte = rk_mk_dte(pt_dma);
> +	dte = rk_ops->mk_dtentries(pt_dma);
>   	*dte_addr = dte;
>   
>   	rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>   	rk_table_flush(rk_domain,
>   		       rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>   done:
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	return (u32 *)phys_to_virt(pt_phys);
>   }
>   
> @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
>   		if (rk_pte_is_page_valid(pte))
>   			goto unwind;
>   
> -		pte_addr[pte_count] = rk_mk_pte(paddr, prot);
> +		pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>   
>   		paddr += SPAGE_SIZE;
>   	}
> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
>   			    pte_count * SPAGE_SIZE);
>   
>   	iova += pte_count * SPAGE_SIZE;
> -	page_phys = rk_pte_page_address(pte_addr[pte_count]);
> +	page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>   	pr_err("iova: %pad already mapped to %pa cannot remap to phys: %pa prot: %#x\n",
>   	       &iova, &page_phys, &paddr, prot);
>   
> @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
>   	dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>   	pte_index = rk_iova_pte_index(iova);
>   	pte_addr = &page_table[pte_index];
> -	pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
> +
> +	pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>   	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>   				paddr, size, prot);
>   
> @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
>   		return 0;
>   	}
>   
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>   	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>   	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size);
> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
>   	for (i = 0; i < NUM_DT_ENTRIES; i++) {
>   		u32 dte = rk_domain->dt[i];
>   		if (rk_dte_is_pt_valid(dte)) {
> -			phys_addr_t pt_phys = rk_dte_pt_address(dte);
> +			phys_addr_t pt_phys = rk_ops->pt_address(dte);
>   			u32 *page_table = phys_to_virt(pt_phys);
>   			dma_unmap_single(dma_dev, pt_phys,
>   					 SPAGE_SIZE, DMA_TO_DEVICE);
> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct platform_device *pdev)
>   	iommu->dev = dev;
>   	iommu->num_mmu = 0;
>   
> +	if (!rk_ops)
> +		rk_ops = of_device_get_match_data(dev);
> +
> +	/*
> +	 * That should not happen unless different versions of the
> +	 * hardware block are embedded the same SoC
> +	 */
> +	WARN_ON(rk_ops != of_device_get_match_data(dev));

Nit: calling of_device_get_match_data() twice seems rather untidy - how 
about something like:

	ops = of_device_get_match_data(dev);
	if (!rk_ops)
		rk_ops = ops;
	else if (WARN_ON(rk_ops != ops))
		return -EINVAL;

Either way I think it would be good to treat unexpected inconsistentcy 
as an actual error, rather than second-guessing the DT and carrying on 
under the assumption the device is something other than it claimed to be.

> +
>   	iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>   				    GFP_KERNEL);
>   	if (!iommu->bases)
> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops rk_iommu_pm_ops = {
>   				pm_runtime_force_resume)
>   };
>   
> +static struct rk_iommu_ops iommu_data_ops_v1 = {
> +	.pt_address = &rk_dte_pt_address,
> +	.mk_dtentries = &rk_mk_dte,
> +	.mk_ptentries = &rk_mk_pte,
> +	.dte_addr_phys = &rk_dte_addr_phys,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
> +};
> +
>   static const struct of_device_id rk_iommu_dt_ids[] = {
> -	{ .compatible = "rockchip,iommu" },
> +	{	.compatible = "rockchip,iommu",
> +		.data = &iommu_data_ops_v1,
> +	},
>   	{ /* sentinel */ }
>   };
> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);

As before, unrelated and unnecessary since this driver is still bool in 
the Kconfig. If you do want to support modular builds you'll also need 
to ensure rk_iommu_ops.owner is set, but do it all as a separate patch 
please.

Thanks,
Robin.

>   
>   static struct platform_driver rk_iommu_driver = {
>   	.probe = rk_iommu_probe,
> 

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21 12:58     ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 12:58 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> Add internal ops to be able to handle incoming variant v2.
> The goal is to keep the overall structure of the framework but
> to allow to add the evolution of this hardware block.
> 
> The ops are global for a SoC because iommu domains are not
> attached to a specific devices if they are for a virtuel device like
> drm. Use a global variable shouldn't be since SoC usually doesn't
> embedded different versions of the iommu hardware block.
> If that happen one day a WARN_ON will be displayed at probe time.

IMO it would be a grievous error if such a "virtual device" ever gets 
near the IOMMU API, so personally I wouldn't use that as a justification 
for anything :)

FWIW you should be OK to handle things on a per-instance basis, it just 
means you have to defer some of the domain setup to .attach_dev time, 
like various other drivers do. That said, there's nothing wrong with the 
global if we do expect instances to be consistent across any given 
Rockchip SoC (and my gut feeling is that we probably should).

> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
> version 5:
>   - Use of_device_get_match_data()
>   - Add internal ops inside the driver
> 
>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>   1 file changed, 50 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index 7a2932772fdf..e7b9bcf174b1 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -19,6 +19,7 @@
>   #include <linux/iopoll.h>
>   #include <linux/list.h>
>   #include <linux/mm.h>
> +#include <linux/module.h>

This seems to be an unrelated and unnecessary change.

>   #include <linux/init.h>
>   #include <linux/of.h>
>   #include <linux/of_iommu.h>
> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>   	"aclk", "iface",
>   };
>   
> +struct rk_iommu_ops {
> +	phys_addr_t (*pt_address)(u32 dte);
> +	u32 (*mk_dtentries)(dma_addr_t pt_dma);
> +	u32 (*mk_ptentries)(phys_addr_t page, int prot);
> +	phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
> +	u32 pt_address_mask;
> +};
> +
>   struct rk_iommu {
>   	struct device *dev;
>   	void __iomem **bases;
> @@ -116,6 +125,7 @@ struct rk_iommudata {
>   };
>   
>   static struct device *dma_dev;
> +static const struct rk_iommu_ops *rk_ops;
>   
>   static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
>   				  unsigned int count)
> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>   #define RK_PTE_PAGE_READABLE      BIT(1)
>   #define RK_PTE_PAGE_VALID         BIT(0)
>   
> -static inline phys_addr_t rk_pte_page_address(u32 pte)
> -{
> -	return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
> -}
> -
>   static inline bool rk_pte_is_page_valid(u32 pte)
>   {
>   	return pte & RK_PTE_PAGE_VALID;
> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
>   		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
>   
>   		dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
> -		if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
> +		if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {

Nit: might it make more sense to do something like:

		dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
		rk_iommu_write(... dte_addr)
		if (rk_iommu_read(...) != dte_addr)

so that you don't need to bother defining ->pt_address_mask for just 
this one sanity-check?

>   			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
>   			return -EFAULT;
>   		}
> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
>   	return 0;
>   }
>   
> +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)

The argument type here should be u32, since it's a DTE, not a physical 
address...

> +{
> +	return addr;
> +}
> +
>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   {
>   	void __iomem *base = iommu->bases[index];
> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   	page_offset = rk_iova_page_offset(iova);
>   
>   	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
> -	mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
> +	mmu_dte_addr_phys = rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);

...and the cast here should not be here, since it *is* the conversion 
that the called function is supposed to be performing.

>   	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>   	dte_addr = phys_to_virt(dte_addr_phys);
> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   	if (!rk_dte_is_pt_valid(dte))
>   		goto print_it;
>   
> -	pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
> +	pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>   	pte_addr = phys_to_virt(pte_addr_phys);
>   	pte = *pte_addr;
>   
>   	if (!rk_pte_is_page_valid(pte))
>   		goto print_it;
>   
> -	page_addr_phys = rk_pte_page_address(pte) + page_offset;
> +	page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>   	page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>   
>   print_it:
> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
>   	if (!rk_dte_is_pt_valid(dte))
>   		goto out;
>   
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	page_table = (u32 *)phys_to_virt(pt_phys);
>   	pte = page_table[rk_iova_pte_index(iova)];
>   	if (!rk_pte_is_page_valid(pte))
>   		goto out;
>   
> -	phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
> +	phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>   out:
>   	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>   
> @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
>   		return ERR_PTR(-ENOMEM);
>   	}
>   
> -	dte = rk_mk_dte(pt_dma);
> +	dte = rk_ops->mk_dtentries(pt_dma);
>   	*dte_addr = dte;
>   
>   	rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>   	rk_table_flush(rk_domain,
>   		       rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>   done:
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	return (u32 *)phys_to_virt(pt_phys);
>   }
>   
> @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
>   		if (rk_pte_is_page_valid(pte))
>   			goto unwind;
>   
> -		pte_addr[pte_count] = rk_mk_pte(paddr, prot);
> +		pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>   
>   		paddr += SPAGE_SIZE;
>   	}
> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
>   			    pte_count * SPAGE_SIZE);
>   
>   	iova += pte_count * SPAGE_SIZE;
> -	page_phys = rk_pte_page_address(pte_addr[pte_count]);
> +	page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>   	pr_err("iova: %pad already mapped to %pa cannot remap to phys: %pa prot: %#x\n",
>   	       &iova, &page_phys, &paddr, prot);
>   
> @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
>   	dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>   	pte_index = rk_iova_pte_index(iova);
>   	pte_addr = &page_table[pte_index];
> -	pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
> +
> +	pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>   	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>   				paddr, size, prot);
>   
> @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
>   		return 0;
>   	}
>   
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>   	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>   	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size);
> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
>   	for (i = 0; i < NUM_DT_ENTRIES; i++) {
>   		u32 dte = rk_domain->dt[i];
>   		if (rk_dte_is_pt_valid(dte)) {
> -			phys_addr_t pt_phys = rk_dte_pt_address(dte);
> +			phys_addr_t pt_phys = rk_ops->pt_address(dte);
>   			u32 *page_table = phys_to_virt(pt_phys);
>   			dma_unmap_single(dma_dev, pt_phys,
>   					 SPAGE_SIZE, DMA_TO_DEVICE);
> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct platform_device *pdev)
>   	iommu->dev = dev;
>   	iommu->num_mmu = 0;
>   
> +	if (!rk_ops)
> +		rk_ops = of_device_get_match_data(dev);
> +
> +	/*
> +	 * That should not happen unless different versions of the
> +	 * hardware block are embedded the same SoC
> +	 */
> +	WARN_ON(rk_ops != of_device_get_match_data(dev));

Nit: calling of_device_get_match_data() twice seems rather untidy - how 
about something like:

	ops = of_device_get_match_data(dev);
	if (!rk_ops)
		rk_ops = ops;
	else if (WARN_ON(rk_ops != ops))
		return -EINVAL;

Either way I think it would be good to treat unexpected inconsistentcy 
as an actual error, rather than second-guessing the DT and carrying on 
under the assumption the device is something other than it claimed to be.

> +
>   	iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>   				    GFP_KERNEL);
>   	if (!iommu->bases)
> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops rk_iommu_pm_ops = {
>   				pm_runtime_force_resume)
>   };
>   
> +static struct rk_iommu_ops iommu_data_ops_v1 = {
> +	.pt_address = &rk_dte_pt_address,
> +	.mk_dtentries = &rk_mk_dte,
> +	.mk_ptentries = &rk_mk_pte,
> +	.dte_addr_phys = &rk_dte_addr_phys,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
> +};
> +
>   static const struct of_device_id rk_iommu_dt_ids[] = {
> -	{ .compatible = "rockchip,iommu" },
> +	{	.compatible = "rockchip,iommu",
> +		.data = &iommu_data_ops_v1,
> +	},
>   	{ /* sentinel */ }
>   };
> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);

As before, unrelated and unnecessary since this driver is still bool in 
the Kconfig. If you do want to support modular builds you'll also need 
to ensure rk_iommu_ops.owner is set, but do it all as a separate patch 
please.

Thanks,
Robin.

>   
>   static struct platform_driver rk_iommu_driver = {
>   	.probe = rk_iommu_probe,
> 

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21 12:58     ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 12:58 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: devicetree, linux-kernel, linux-rockchip, iommu, kernel,
	linux-arm-kernel

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> Add internal ops to be able to handle incoming variant v2.
> The goal is to keep the overall structure of the framework but
> to allow to add the evolution of this hardware block.
> 
> The ops are global for a SoC because iommu domains are not
> attached to a specific devices if they are for a virtuel device like
> drm. Use a global variable shouldn't be since SoC usually doesn't
> embedded different versions of the iommu hardware block.
> If that happen one day a WARN_ON will be displayed at probe time.

IMO it would be a grievous error if such a "virtual device" ever gets 
near the IOMMU API, so personally I wouldn't use that as a justification 
for anything :)

FWIW you should be OK to handle things on a per-instance basis, it just 
means you have to defer some of the domain setup to .attach_dev time, 
like various other drivers do. That said, there's nothing wrong with the 
global if we do expect instances to be consistent across any given 
Rockchip SoC (and my gut feeling is that we probably should).

> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
> version 5:
>   - Use of_device_get_match_data()
>   - Add internal ops inside the driver
> 
>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>   1 file changed, 50 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index 7a2932772fdf..e7b9bcf174b1 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -19,6 +19,7 @@
>   #include <linux/iopoll.h>
>   #include <linux/list.h>
>   #include <linux/mm.h>
> +#include <linux/module.h>

This seems to be an unrelated and unnecessary change.

>   #include <linux/init.h>
>   #include <linux/of.h>
>   #include <linux/of_iommu.h>
> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>   	"aclk", "iface",
>   };
>   
> +struct rk_iommu_ops {
> +	phys_addr_t (*pt_address)(u32 dte);
> +	u32 (*mk_dtentries)(dma_addr_t pt_dma);
> +	u32 (*mk_ptentries)(phys_addr_t page, int prot);
> +	phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
> +	u32 pt_address_mask;
> +};
> +
>   struct rk_iommu {
>   	struct device *dev;
>   	void __iomem **bases;
> @@ -116,6 +125,7 @@ struct rk_iommudata {
>   };
>   
>   static struct device *dma_dev;
> +static const struct rk_iommu_ops *rk_ops;
>   
>   static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
>   				  unsigned int count)
> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>   #define RK_PTE_PAGE_READABLE      BIT(1)
>   #define RK_PTE_PAGE_VALID         BIT(0)
>   
> -static inline phys_addr_t rk_pte_page_address(u32 pte)
> -{
> -	return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
> -}
> -
>   static inline bool rk_pte_is_page_valid(u32 pte)
>   {
>   	return pte & RK_PTE_PAGE_VALID;
> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
>   		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
>   
>   		dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
> -		if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
> +		if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {

Nit: might it make more sense to do something like:

		dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
		rk_iommu_write(... dte_addr)
		if (rk_iommu_read(...) != dte_addr)

so that you don't need to bother defining ->pt_address_mask for just 
this one sanity-check?

>   			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
>   			return -EFAULT;
>   		}
> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
>   	return 0;
>   }
>   
> +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)

The argument type here should be u32, since it's a DTE, not a physical 
address...

> +{
> +	return addr;
> +}
> +
>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   {
>   	void __iomem *base = iommu->bases[index];
> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   	page_offset = rk_iova_page_offset(iova);
>   
>   	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
> -	mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
> +	mmu_dte_addr_phys = rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);

...and the cast here should not be here, since it *is* the conversion 
that the called function is supposed to be performing.

>   	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>   	dte_addr = phys_to_virt(dte_addr_phys);
> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   	if (!rk_dte_is_pt_valid(dte))
>   		goto print_it;
>   
> -	pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
> +	pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>   	pte_addr = phys_to_virt(pte_addr_phys);
>   	pte = *pte_addr;
>   
>   	if (!rk_pte_is_page_valid(pte))
>   		goto print_it;
>   
> -	page_addr_phys = rk_pte_page_address(pte) + page_offset;
> +	page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>   	page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>   
>   print_it:
> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
>   	if (!rk_dte_is_pt_valid(dte))
>   		goto out;
>   
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	page_table = (u32 *)phys_to_virt(pt_phys);
>   	pte = page_table[rk_iova_pte_index(iova)];
>   	if (!rk_pte_is_page_valid(pte))
>   		goto out;
>   
> -	phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
> +	phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>   out:
>   	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>   
> @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
>   		return ERR_PTR(-ENOMEM);
>   	}
>   
> -	dte = rk_mk_dte(pt_dma);
> +	dte = rk_ops->mk_dtentries(pt_dma);
>   	*dte_addr = dte;
>   
>   	rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>   	rk_table_flush(rk_domain,
>   		       rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>   done:
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	return (u32 *)phys_to_virt(pt_phys);
>   }
>   
> @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
>   		if (rk_pte_is_page_valid(pte))
>   			goto unwind;
>   
> -		pte_addr[pte_count] = rk_mk_pte(paddr, prot);
> +		pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>   
>   		paddr += SPAGE_SIZE;
>   	}
> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
>   			    pte_count * SPAGE_SIZE);
>   
>   	iova += pte_count * SPAGE_SIZE;
> -	page_phys = rk_pte_page_address(pte_addr[pte_count]);
> +	page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>   	pr_err("iova: %pad already mapped to %pa cannot remap to phys: %pa prot: %#x\n",
>   	       &iova, &page_phys, &paddr, prot);
>   
> @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
>   	dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>   	pte_index = rk_iova_pte_index(iova);
>   	pte_addr = &page_table[pte_index];
> -	pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
> +
> +	pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>   	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>   				paddr, size, prot);
>   
> @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
>   		return 0;
>   	}
>   
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>   	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>   	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size);
> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
>   	for (i = 0; i < NUM_DT_ENTRIES; i++) {
>   		u32 dte = rk_domain->dt[i];
>   		if (rk_dte_is_pt_valid(dte)) {
> -			phys_addr_t pt_phys = rk_dte_pt_address(dte);
> +			phys_addr_t pt_phys = rk_ops->pt_address(dte);
>   			u32 *page_table = phys_to_virt(pt_phys);
>   			dma_unmap_single(dma_dev, pt_phys,
>   					 SPAGE_SIZE, DMA_TO_DEVICE);
> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct platform_device *pdev)
>   	iommu->dev = dev;
>   	iommu->num_mmu = 0;
>   
> +	if (!rk_ops)
> +		rk_ops = of_device_get_match_data(dev);
> +
> +	/*
> +	 * That should not happen unless different versions of the
> +	 * hardware block are embedded the same SoC
> +	 */
> +	WARN_ON(rk_ops != of_device_get_match_data(dev));

Nit: calling of_device_get_match_data() twice seems rather untidy - how 
about something like:

	ops = of_device_get_match_data(dev);
	if (!rk_ops)
		rk_ops = ops;
	else if (WARN_ON(rk_ops != ops))
		return -EINVAL;

Either way I think it would be good to treat unexpected inconsistentcy 
as an actual error, rather than second-guessing the DT and carrying on 
under the assumption the device is something other than it claimed to be.

> +
>   	iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>   				    GFP_KERNEL);
>   	if (!iommu->bases)
> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops rk_iommu_pm_ops = {
>   				pm_runtime_force_resume)
>   };
>   
> +static struct rk_iommu_ops iommu_data_ops_v1 = {
> +	.pt_address = &rk_dte_pt_address,
> +	.mk_dtentries = &rk_mk_dte,
> +	.mk_ptentries = &rk_mk_pte,
> +	.dte_addr_phys = &rk_dte_addr_phys,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
> +};
> +
>   static const struct of_device_id rk_iommu_dt_ids[] = {
> -	{ .compatible = "rockchip,iommu" },
> +	{	.compatible = "rockchip,iommu",
> +		.data = &iommu_data_ops_v1,
> +	},
>   	{ /* sentinel */ }
>   };
> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);

As before, unrelated and unnecessary since this driver is still bool in 
the Kconfig. If you do want to support modular builds you'll also need 
to ensure rk_iommu_ops.owner is set, but do it all as a separate patch 
please.

Thanks,
Robin.

>   
>   static struct platform_driver rk_iommu_driver = {
>   	.probe = rk_iommu_probe,
> 
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21 12:58     ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 12:58 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> Add internal ops to be able to handle incoming variant v2.
> The goal is to keep the overall structure of the framework but
> to allow to add the evolution of this hardware block.
> 
> The ops are global for a SoC because iommu domains are not
> attached to a specific devices if they are for a virtuel device like
> drm. Use a global variable shouldn't be since SoC usually doesn't
> embedded different versions of the iommu hardware block.
> If that happen one day a WARN_ON will be displayed at probe time.

IMO it would be a grievous error if such a "virtual device" ever gets 
near the IOMMU API, so personally I wouldn't use that as a justification 
for anything :)

FWIW you should be OK to handle things on a per-instance basis, it just 
means you have to defer some of the domain setup to .attach_dev time, 
like various other drivers do. That said, there's nothing wrong with the 
global if we do expect instances to be consistent across any given 
Rockchip SoC (and my gut feeling is that we probably should).

> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
> version 5:
>   - Use of_device_get_match_data()
>   - Add internal ops inside the driver
> 
>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>   1 file changed, 50 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index 7a2932772fdf..e7b9bcf174b1 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -19,6 +19,7 @@
>   #include <linux/iopoll.h>
>   #include <linux/list.h>
>   #include <linux/mm.h>
> +#include <linux/module.h>

This seems to be an unrelated and unnecessary change.

>   #include <linux/init.h>
>   #include <linux/of.h>
>   #include <linux/of_iommu.h>
> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>   	"aclk", "iface",
>   };
>   
> +struct rk_iommu_ops {
> +	phys_addr_t (*pt_address)(u32 dte);
> +	u32 (*mk_dtentries)(dma_addr_t pt_dma);
> +	u32 (*mk_ptentries)(phys_addr_t page, int prot);
> +	phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
> +	u32 pt_address_mask;
> +};
> +
>   struct rk_iommu {
>   	struct device *dev;
>   	void __iomem **bases;
> @@ -116,6 +125,7 @@ struct rk_iommudata {
>   };
>   
>   static struct device *dma_dev;
> +static const struct rk_iommu_ops *rk_ops;
>   
>   static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
>   				  unsigned int count)
> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>   #define RK_PTE_PAGE_READABLE      BIT(1)
>   #define RK_PTE_PAGE_VALID         BIT(0)
>   
> -static inline phys_addr_t rk_pte_page_address(u32 pte)
> -{
> -	return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
> -}
> -
>   static inline bool rk_pte_is_page_valid(u32 pte)
>   {
>   	return pte & RK_PTE_PAGE_VALID;
> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
>   		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
>   
>   		dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
> -		if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
> +		if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {

Nit: might it make more sense to do something like:

		dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
		rk_iommu_write(... dte_addr)
		if (rk_iommu_read(...) != dte_addr)

so that you don't need to bother defining ->pt_address_mask for just 
this one sanity-check?

>   			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
>   			return -EFAULT;
>   		}
> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
>   	return 0;
>   }
>   
> +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)

The argument type here should be u32, since it's a DTE, not a physical 
address...

> +{
> +	return addr;
> +}
> +
>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   {
>   	void __iomem *base = iommu->bases[index];
> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   	page_offset = rk_iova_page_offset(iova);
>   
>   	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
> -	mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
> +	mmu_dte_addr_phys = rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);

...and the cast here should not be here, since it *is* the conversion 
that the called function is supposed to be performing.

>   	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>   	dte_addr = phys_to_virt(dte_addr_phys);
> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   	if (!rk_dte_is_pt_valid(dte))
>   		goto print_it;
>   
> -	pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
> +	pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>   	pte_addr = phys_to_virt(pte_addr_phys);
>   	pte = *pte_addr;
>   
>   	if (!rk_pte_is_page_valid(pte))
>   		goto print_it;
>   
> -	page_addr_phys = rk_pte_page_address(pte) + page_offset;
> +	page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>   	page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>   
>   print_it:
> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
>   	if (!rk_dte_is_pt_valid(dte))
>   		goto out;
>   
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	page_table = (u32 *)phys_to_virt(pt_phys);
>   	pte = page_table[rk_iova_pte_index(iova)];
>   	if (!rk_pte_is_page_valid(pte))
>   		goto out;
>   
> -	phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
> +	phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>   out:
>   	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>   
> @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
>   		return ERR_PTR(-ENOMEM);
>   	}
>   
> -	dte = rk_mk_dte(pt_dma);
> +	dte = rk_ops->mk_dtentries(pt_dma);
>   	*dte_addr = dte;
>   
>   	rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>   	rk_table_flush(rk_domain,
>   		       rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>   done:
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	return (u32 *)phys_to_virt(pt_phys);
>   }
>   
> @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
>   		if (rk_pte_is_page_valid(pte))
>   			goto unwind;
>   
> -		pte_addr[pte_count] = rk_mk_pte(paddr, prot);
> +		pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>   
>   		paddr += SPAGE_SIZE;
>   	}
> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
>   			    pte_count * SPAGE_SIZE);
>   
>   	iova += pte_count * SPAGE_SIZE;
> -	page_phys = rk_pte_page_address(pte_addr[pte_count]);
> +	page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>   	pr_err("iova: %pad already mapped to %pa cannot remap to phys: %pa prot: %#x\n",
>   	       &iova, &page_phys, &paddr, prot);
>   
> @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
>   	dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>   	pte_index = rk_iova_pte_index(iova);
>   	pte_addr = &page_table[pte_index];
> -	pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
> +
> +	pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>   	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>   				paddr, size, prot);
>   
> @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
>   		return 0;
>   	}
>   
> -	pt_phys = rk_dte_pt_address(dte);
> +	pt_phys = rk_ops->pt_address(dte);
>   	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>   	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>   	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size);
> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
>   	for (i = 0; i < NUM_DT_ENTRIES; i++) {
>   		u32 dte = rk_domain->dt[i];
>   		if (rk_dte_is_pt_valid(dte)) {
> -			phys_addr_t pt_phys = rk_dte_pt_address(dte);
> +			phys_addr_t pt_phys = rk_ops->pt_address(dte);
>   			u32 *page_table = phys_to_virt(pt_phys);
>   			dma_unmap_single(dma_dev, pt_phys,
>   					 SPAGE_SIZE, DMA_TO_DEVICE);
> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct platform_device *pdev)
>   	iommu->dev = dev;
>   	iommu->num_mmu = 0;
>   
> +	if (!rk_ops)
> +		rk_ops = of_device_get_match_data(dev);
> +
> +	/*
> +	 * That should not happen unless different versions of the
> +	 * hardware block are embedded the same SoC
> +	 */
> +	WARN_ON(rk_ops != of_device_get_match_data(dev));

Nit: calling of_device_get_match_data() twice seems rather untidy - how 
about something like:

	ops = of_device_get_match_data(dev);
	if (!rk_ops)
		rk_ops = ops;
	else if (WARN_ON(rk_ops != ops))
		return -EINVAL;

Either way I think it would be good to treat unexpected inconsistentcy 
as an actual error, rather than second-guessing the DT and carrying on 
under the assumption the device is something other than it claimed to be.

> +
>   	iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>   				    GFP_KERNEL);
>   	if (!iommu->bases)
> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops rk_iommu_pm_ops = {
>   				pm_runtime_force_resume)
>   };
>   
> +static struct rk_iommu_ops iommu_data_ops_v1 = {
> +	.pt_address = &rk_dte_pt_address,
> +	.mk_dtentries = &rk_mk_dte,
> +	.mk_ptentries = &rk_mk_pte,
> +	.dte_addr_phys = &rk_dte_addr_phys,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
> +};
> +
>   static const struct of_device_id rk_iommu_dt_ids[] = {
> -	{ .compatible = "rockchip,iommu" },
> +	{	.compatible = "rockchip,iommu",
> +		.data = &iommu_data_ops_v1,
> +	},
>   	{ /* sentinel */ }
>   };
> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);

As before, unrelated and unnecessary since this driver is still bool in 
the Kconfig. If you do want to support modular builds you'll also need 
to ensure rk_iommu_ops.owner is set, but do it all as a separate patch 
please.

Thanks,
Robin.

>   
>   static struct platform_driver rk_iommu_driver = {
>   	.probe = rk_iommu_probe,
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
  2021-05-21 12:58     ` Robin Murphy
  (?)
  (?)
@ 2021-05-21 13:38       ` Benjamin Gaignard
  -1 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21 13:38 UTC (permalink / raw)
  To: Robin Murphy, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel


Le 21/05/2021 à 14:58, Robin Murphy a écrit :
> On 2021-05-21 09:36, Benjamin Gaignard wrote:
>> Add internal ops to be able to handle incoming variant v2.
>> The goal is to keep the overall structure of the framework but
>> to allow to add the evolution of this hardware block.
>>
>> The ops are global for a SoC because iommu domains are not
>> attached to a specific devices if they are for a virtuel device like
>> drm. Use a global variable shouldn't be since SoC usually doesn't
>> embedded different versions of the iommu hardware block.
>> If that happen one day a WARN_ON will be displayed at probe time.
>
> IMO it would be a grievous error if such a "virtual device" ever gets 
> near the IOMMU API, so personally I wouldn't use that as a 
> justification for anything :)
>
> FWIW you should be OK to handle things on a per-instance basis, it 
> just means you have to defer some of the domain setup to .attach_dev 
> time, like various other drivers do. That said, there's nothing wrong 
> with the global if we do expect instances to be consistent across any 
> given Rockchip SoC (and my gut feeling is that we probably should).

I have tried that solution first but drm device appear to but such "virtual device" so I had to use the global.

I send a v6 to fix your others remarks.

Thanks for your advice.

Benjamin

>
>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
>> ---
>> version 5:
>>   - Use of_device_get_match_data()
>>   - Add internal ops inside the driver
>>
>>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>>   1 file changed, 50 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/iommu/rockchip-iommu.c 
>> b/drivers/iommu/rockchip-iommu.c
>> index 7a2932772fdf..e7b9bcf174b1 100644
>> --- a/drivers/iommu/rockchip-iommu.c
>> +++ b/drivers/iommu/rockchip-iommu.c
>> @@ -19,6 +19,7 @@
>>   #include <linux/iopoll.h>
>>   #include <linux/list.h>
>>   #include <linux/mm.h>
>> +#include <linux/module.h>
>
> This seems to be an unrelated and unnecessary change.
>
>>   #include <linux/init.h>
>>   #include <linux/of.h>
>>   #include <linux/of_iommu.h>
>> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>>       "aclk", "iface",
>>   };
>>   +struct rk_iommu_ops {
>> +    phys_addr_t (*pt_address)(u32 dte);
>> +    u32 (*mk_dtentries)(dma_addr_t pt_dma);
>> +    u32 (*mk_ptentries)(phys_addr_t page, int prot);
>> +    phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
>> +    u32 pt_address_mask;
>> +};
>> +
>>   struct rk_iommu {
>>       struct device *dev;
>>       void __iomem **bases;
>> @@ -116,6 +125,7 @@ struct rk_iommudata {
>>   };
>>     static struct device *dma_dev;
>> +static const struct rk_iommu_ops *rk_ops;
>>     static inline void rk_table_flush(struct rk_iommu_domain *dom, 
>> dma_addr_t dma,
>>                     unsigned int count)
>> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>>   #define RK_PTE_PAGE_READABLE      BIT(1)
>>   #define RK_PTE_PAGE_VALID         BIT(0)
>>   -static inline phys_addr_t rk_pte_page_address(u32 pte)
>> -{
>> -    return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
>> -}
>> -
>>   static inline bool rk_pte_is_page_valid(u32 pte)
>>   {
>>       return pte & RK_PTE_PAGE_VALID;
>> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu 
>> *iommu)
>>           rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 
>> DTE_ADDR_DUMMY);
>>             dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
>> -        if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
>> +        if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
>
> Nit: might it make more sense to do something like:
>
>         dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
>         rk_iommu_write(... dte_addr)
>         if (rk_iommu_read(...) != dte_addr)
>
> so that you don't need to bother defining ->pt_address_mask for just 
> this one sanity-check?
>
>>               dev_err(iommu->dev, "Error during raw reset. 
>> MMU_DTE_ADDR is not functioning\n");
>>               return -EFAULT;
>>           }
>> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu 
>> *iommu)
>>       return 0;
>>   }
>>   +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>
> The argument type here should be u32, since it's a DTE, not a physical 
> address...
>
>> +{
>> +    return addr;
>> +}
>> +
>>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t 
>> iova)
>>   {
>>       void __iomem *base = iommu->bases[index];
>> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int 
>> index, dma_addr_t iova)
>>       page_offset = rk_iova_page_offset(iova);
>>         mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
>> -    mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
>> +    mmu_dte_addr_phys = 
>> rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
>
> ...and the cast here should not be here, since it *is* the conversion 
> that the called function is supposed to be performing.
>
>>       dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>>       dte_addr = phys_to_virt(dte_addr_phys);
>> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, 
>> int index, dma_addr_t iova)
>>       if (!rk_dte_is_pt_valid(dte))
>>           goto print_it;
>>   -    pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
>> +    pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>>       pte_addr = phys_to_virt(pte_addr_phys);
>>       pte = *pte_addr;
>>         if (!rk_pte_is_page_valid(pte))
>>           goto print_it;
>>   -    page_addr_phys = rk_pte_page_address(pte) + page_offset;
>> +    page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>>       page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>>     print_it:
>> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct 
>> iommu_domain *domain,
>>       if (!rk_dte_is_pt_valid(dte))
>>           goto out;
>>   -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       page_table = (u32 *)phys_to_virt(pt_phys);
>>       pte = page_table[rk_iova_pte_index(iova)];
>>       if (!rk_pte_is_page_valid(pte))
>>           goto out;
>>   -    phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
>> +    phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>>   out:
>>       spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>>   @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct 
>> rk_iommu_domain *rk_domain,
>>           return ERR_PTR(-ENOMEM);
>>       }
>>   -    dte = rk_mk_dte(pt_dma);
>> +    dte = rk_ops->mk_dtentries(pt_dma);
>>       *dte_addr = dte;
>>         rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>>       rk_table_flush(rk_domain,
>>                  rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>>   done:
>> -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       return (u32 *)phys_to_virt(pt_phys);
>>   }
>>   @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct 
>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>           if (rk_pte_is_page_valid(pte))
>>               goto unwind;
>>   -        pte_addr[pte_count] = rk_mk_pte(paddr, prot);
>> +        pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>>             paddr += SPAGE_SIZE;
>>       }
>> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct 
>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>                   pte_count * SPAGE_SIZE);
>>         iova += pte_count * SPAGE_SIZE;
>> -    page_phys = rk_pte_page_address(pte_addr[pte_count]);
>> +    page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>>       pr_err("iova: %pad already mapped to %pa cannot remap to phys: 
>> %pa prot: %#x\n",
>>              &iova, &page_phys, &paddr, prot);
>>   @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain 
>> *domain, unsigned long _iova,
>>       dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>>       pte_index = rk_iova_pte_index(iova);
>>       pte_addr = &page_table[pte_index];
>> -    pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
>> +
>> +    pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>>       ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>>                   paddr, size, prot);
>>   @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct 
>> iommu_domain *domain, unsigned long _iova,
>>           return 0;
>>       }
>>   -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>>       pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>>       unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, 
>> size);
>> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct 
>> iommu_domain *domain)
>>       for (i = 0; i < NUM_DT_ENTRIES; i++) {
>>           u32 dte = rk_domain->dt[i];
>>           if (rk_dte_is_pt_valid(dte)) {
>> -            phys_addr_t pt_phys = rk_dte_pt_address(dte);
>> +            phys_addr_t pt_phys = rk_ops->pt_address(dte);
>>               u32 *page_table = phys_to_virt(pt_phys);
>>               dma_unmap_single(dma_dev, pt_phys,
>>                        SPAGE_SIZE, DMA_TO_DEVICE);
>> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct 
>> platform_device *pdev)
>>       iommu->dev = dev;
>>       iommu->num_mmu = 0;
>>   +    if (!rk_ops)
>> +        rk_ops = of_device_get_match_data(dev);
>> +
>> +    /*
>> +     * That should not happen unless different versions of the
>> +     * hardware block are embedded the same SoC
>> +     */
>> +    WARN_ON(rk_ops != of_device_get_match_data(dev));
>
> Nit: calling of_device_get_match_data() twice seems rather untidy - 
> how about something like:
>
>     ops = of_device_get_match_data(dev);
>     if (!rk_ops)
>         rk_ops = ops;
>     else if (WARN_ON(rk_ops != ops))
>         return -EINVAL;
>
> Either way I think it would be good to treat unexpected inconsistentcy 
> as an actual error, rather than second-guessing the DT and carrying on 
> under the assumption the device is something other than it claimed to be.
>
>> +
>>       iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>>                       GFP_KERNEL);
>>       if (!iommu->bases)
>> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops 
>> rk_iommu_pm_ops = {
>>                   pm_runtime_force_resume)
>>   };
>>   +static struct rk_iommu_ops iommu_data_ops_v1 = {
>> +    .pt_address = &rk_dte_pt_address,
>> +    .mk_dtentries = &rk_mk_dte,
>> +    .mk_ptentries = &rk_mk_pte,
>> +    .dte_addr_phys = &rk_dte_addr_phys,
>> +    .pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>> +};
>> +
>>   static const struct of_device_id rk_iommu_dt_ids[] = {
>> -    { .compatible = "rockchip,iommu" },
>> +    {    .compatible = "rockchip,iommu",
>> +        .data = &iommu_data_ops_v1,
>> +    },
>>       { /* sentinel */ }
>>   };
>> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
>
> As before, unrelated and unnecessary since this driver is still bool 
> in the Kconfig. If you do want to support modular builds you'll also 
> need to ensure rk_iommu_ops.owner is set, but do it all as a separate 
> patch please.
>
> Thanks,
> Robin.
>
>>     static struct platform_driver rk_iommu_driver = {
>>       .probe = rk_iommu_probe,
>>
>

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21 13:38       ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21 13:38 UTC (permalink / raw)
  To: Robin Murphy, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel


Le 21/05/2021 à 14:58, Robin Murphy a écrit :
> On 2021-05-21 09:36, Benjamin Gaignard wrote:
>> Add internal ops to be able to handle incoming variant v2.
>> The goal is to keep the overall structure of the framework but
>> to allow to add the evolution of this hardware block.
>>
>> The ops are global for a SoC because iommu domains are not
>> attached to a specific devices if they are for a virtuel device like
>> drm. Use a global variable shouldn't be since SoC usually doesn't
>> embedded different versions of the iommu hardware block.
>> If that happen one day a WARN_ON will be displayed at probe time.
>
> IMO it would be a grievous error if such a "virtual device" ever gets 
> near the IOMMU API, so personally I wouldn't use that as a 
> justification for anything :)
>
> FWIW you should be OK to handle things on a per-instance basis, it 
> just means you have to defer some of the domain setup to .attach_dev 
> time, like various other drivers do. That said, there's nothing wrong 
> with the global if we do expect instances to be consistent across any 
> given Rockchip SoC (and my gut feeling is that we probably should).

I have tried that solution first but drm device appear to but such "virtual device" so I had to use the global.

I send a v6 to fix your others remarks.

Thanks for your advice.

Benjamin

>
>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
>> ---
>> version 5:
>>   - Use of_device_get_match_data()
>>   - Add internal ops inside the driver
>>
>>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>>   1 file changed, 50 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/iommu/rockchip-iommu.c 
>> b/drivers/iommu/rockchip-iommu.c
>> index 7a2932772fdf..e7b9bcf174b1 100644
>> --- a/drivers/iommu/rockchip-iommu.c
>> +++ b/drivers/iommu/rockchip-iommu.c
>> @@ -19,6 +19,7 @@
>>   #include <linux/iopoll.h>
>>   #include <linux/list.h>
>>   #include <linux/mm.h>
>> +#include <linux/module.h>
>
> This seems to be an unrelated and unnecessary change.
>
>>   #include <linux/init.h>
>>   #include <linux/of.h>
>>   #include <linux/of_iommu.h>
>> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>>       "aclk", "iface",
>>   };
>>   +struct rk_iommu_ops {
>> +    phys_addr_t (*pt_address)(u32 dte);
>> +    u32 (*mk_dtentries)(dma_addr_t pt_dma);
>> +    u32 (*mk_ptentries)(phys_addr_t page, int prot);
>> +    phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
>> +    u32 pt_address_mask;
>> +};
>> +
>>   struct rk_iommu {
>>       struct device *dev;
>>       void __iomem **bases;
>> @@ -116,6 +125,7 @@ struct rk_iommudata {
>>   };
>>     static struct device *dma_dev;
>> +static const struct rk_iommu_ops *rk_ops;
>>     static inline void rk_table_flush(struct rk_iommu_domain *dom, 
>> dma_addr_t dma,
>>                     unsigned int count)
>> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>>   #define RK_PTE_PAGE_READABLE      BIT(1)
>>   #define RK_PTE_PAGE_VALID         BIT(0)
>>   -static inline phys_addr_t rk_pte_page_address(u32 pte)
>> -{
>> -    return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
>> -}
>> -
>>   static inline bool rk_pte_is_page_valid(u32 pte)
>>   {
>>       return pte & RK_PTE_PAGE_VALID;
>> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu 
>> *iommu)
>>           rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 
>> DTE_ADDR_DUMMY);
>>             dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
>> -        if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
>> +        if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
>
> Nit: might it make more sense to do something like:
>
>         dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
>         rk_iommu_write(... dte_addr)
>         if (rk_iommu_read(...) != dte_addr)
>
> so that you don't need to bother defining ->pt_address_mask for just 
> this one sanity-check?
>
>>               dev_err(iommu->dev, "Error during raw reset. 
>> MMU_DTE_ADDR is not functioning\n");
>>               return -EFAULT;
>>           }
>> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu 
>> *iommu)
>>       return 0;
>>   }
>>   +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>
> The argument type here should be u32, since it's a DTE, not a physical 
> address...
>
>> +{
>> +    return addr;
>> +}
>> +
>>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t 
>> iova)
>>   {
>>       void __iomem *base = iommu->bases[index];
>> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int 
>> index, dma_addr_t iova)
>>       page_offset = rk_iova_page_offset(iova);
>>         mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
>> -    mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
>> +    mmu_dte_addr_phys = 
>> rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
>
> ...and the cast here should not be here, since it *is* the conversion 
> that the called function is supposed to be performing.
>
>>       dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>>       dte_addr = phys_to_virt(dte_addr_phys);
>> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, 
>> int index, dma_addr_t iova)
>>       if (!rk_dte_is_pt_valid(dte))
>>           goto print_it;
>>   -    pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
>> +    pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>>       pte_addr = phys_to_virt(pte_addr_phys);
>>       pte = *pte_addr;
>>         if (!rk_pte_is_page_valid(pte))
>>           goto print_it;
>>   -    page_addr_phys = rk_pte_page_address(pte) + page_offset;
>> +    page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>>       page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>>     print_it:
>> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct 
>> iommu_domain *domain,
>>       if (!rk_dte_is_pt_valid(dte))
>>           goto out;
>>   -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       page_table = (u32 *)phys_to_virt(pt_phys);
>>       pte = page_table[rk_iova_pte_index(iova)];
>>       if (!rk_pte_is_page_valid(pte))
>>           goto out;
>>   -    phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
>> +    phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>>   out:
>>       spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>>   @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct 
>> rk_iommu_domain *rk_domain,
>>           return ERR_PTR(-ENOMEM);
>>       }
>>   -    dte = rk_mk_dte(pt_dma);
>> +    dte = rk_ops->mk_dtentries(pt_dma);
>>       *dte_addr = dte;
>>         rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>>       rk_table_flush(rk_domain,
>>                  rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>>   done:
>> -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       return (u32 *)phys_to_virt(pt_phys);
>>   }
>>   @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct 
>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>           if (rk_pte_is_page_valid(pte))
>>               goto unwind;
>>   -        pte_addr[pte_count] = rk_mk_pte(paddr, prot);
>> +        pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>>             paddr += SPAGE_SIZE;
>>       }
>> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct 
>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>                   pte_count * SPAGE_SIZE);
>>         iova += pte_count * SPAGE_SIZE;
>> -    page_phys = rk_pte_page_address(pte_addr[pte_count]);
>> +    page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>>       pr_err("iova: %pad already mapped to %pa cannot remap to phys: 
>> %pa prot: %#x\n",
>>              &iova, &page_phys, &paddr, prot);
>>   @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain 
>> *domain, unsigned long _iova,
>>       dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>>       pte_index = rk_iova_pte_index(iova);
>>       pte_addr = &page_table[pte_index];
>> -    pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
>> +
>> +    pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>>       ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>>                   paddr, size, prot);
>>   @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct 
>> iommu_domain *domain, unsigned long _iova,
>>           return 0;
>>       }
>>   -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>>       pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>>       unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, 
>> size);
>> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct 
>> iommu_domain *domain)
>>       for (i = 0; i < NUM_DT_ENTRIES; i++) {
>>           u32 dte = rk_domain->dt[i];
>>           if (rk_dte_is_pt_valid(dte)) {
>> -            phys_addr_t pt_phys = rk_dte_pt_address(dte);
>> +            phys_addr_t pt_phys = rk_ops->pt_address(dte);
>>               u32 *page_table = phys_to_virt(pt_phys);
>>               dma_unmap_single(dma_dev, pt_phys,
>>                        SPAGE_SIZE, DMA_TO_DEVICE);
>> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct 
>> platform_device *pdev)
>>       iommu->dev = dev;
>>       iommu->num_mmu = 0;
>>   +    if (!rk_ops)
>> +        rk_ops = of_device_get_match_data(dev);
>> +
>> +    /*
>> +     * That should not happen unless different versions of the
>> +     * hardware block are embedded the same SoC
>> +     */
>> +    WARN_ON(rk_ops != of_device_get_match_data(dev));
>
> Nit: calling of_device_get_match_data() twice seems rather untidy - 
> how about something like:
>
>     ops = of_device_get_match_data(dev);
>     if (!rk_ops)
>         rk_ops = ops;
>     else if (WARN_ON(rk_ops != ops))
>         return -EINVAL;
>
> Either way I think it would be good to treat unexpected inconsistentcy 
> as an actual error, rather than second-guessing the DT and carrying on 
> under the assumption the device is something other than it claimed to be.
>
>> +
>>       iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>>                       GFP_KERNEL);
>>       if (!iommu->bases)
>> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops 
>> rk_iommu_pm_ops = {
>>                   pm_runtime_force_resume)
>>   };
>>   +static struct rk_iommu_ops iommu_data_ops_v1 = {
>> +    .pt_address = &rk_dte_pt_address,
>> +    .mk_dtentries = &rk_mk_dte,
>> +    .mk_ptentries = &rk_mk_pte,
>> +    .dte_addr_phys = &rk_dte_addr_phys,
>> +    .pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>> +};
>> +
>>   static const struct of_device_id rk_iommu_dt_ids[] = {
>> -    { .compatible = "rockchip,iommu" },
>> +    {    .compatible = "rockchip,iommu",
>> +        .data = &iommu_data_ops_v1,
>> +    },
>>       { /* sentinel */ }
>>   };
>> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
>
> As before, unrelated and unnecessary since this driver is still bool 
> in the Kconfig. If you do want to support modular builds you'll also 
> need to ensure rk_iommu_ops.owner is set, but do it all as a separate 
> patch please.
>
> Thanks,
> Robin.
>
>>     static struct platform_driver rk_iommu_driver = {
>>       .probe = rk_iommu_probe,
>>
>

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21 13:38       ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21 13:38 UTC (permalink / raw)
  To: Robin Murphy, joro, will, robh+dt, heiko, xxm
  Cc: devicetree, linux-kernel, linux-rockchip, iommu, kernel,
	linux-arm-kernel


Le 21/05/2021 à 14:58, Robin Murphy a écrit :
> On 2021-05-21 09:36, Benjamin Gaignard wrote:
>> Add internal ops to be able to handle incoming variant v2.
>> The goal is to keep the overall structure of the framework but
>> to allow to add the evolution of this hardware block.
>>
>> The ops are global for a SoC because iommu domains are not
>> attached to a specific devices if they are for a virtuel device like
>> drm. Use a global variable shouldn't be since SoC usually doesn't
>> embedded different versions of the iommu hardware block.
>> If that happen one day a WARN_ON will be displayed at probe time.
>
> IMO it would be a grievous error if such a "virtual device" ever gets 
> near the IOMMU API, so personally I wouldn't use that as a 
> justification for anything :)
>
> FWIW you should be OK to handle things on a per-instance basis, it 
> just means you have to defer some of the domain setup to .attach_dev 
> time, like various other drivers do. That said, there's nothing wrong 
> with the global if we do expect instances to be consistent across any 
> given Rockchip SoC (and my gut feeling is that we probably should).

I have tried that solution first but drm device appear to but such "virtual device" so I had to use the global.

I send a v6 to fix your others remarks.

Thanks for your advice.

Benjamin

>
>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
>> ---
>> version 5:
>>   - Use of_device_get_match_data()
>>   - Add internal ops inside the driver
>>
>>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>>   1 file changed, 50 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/iommu/rockchip-iommu.c 
>> b/drivers/iommu/rockchip-iommu.c
>> index 7a2932772fdf..e7b9bcf174b1 100644
>> --- a/drivers/iommu/rockchip-iommu.c
>> +++ b/drivers/iommu/rockchip-iommu.c
>> @@ -19,6 +19,7 @@
>>   #include <linux/iopoll.h>
>>   #include <linux/list.h>
>>   #include <linux/mm.h>
>> +#include <linux/module.h>
>
> This seems to be an unrelated and unnecessary change.
>
>>   #include <linux/init.h>
>>   #include <linux/of.h>
>>   #include <linux/of_iommu.h>
>> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>>       "aclk", "iface",
>>   };
>>   +struct rk_iommu_ops {
>> +    phys_addr_t (*pt_address)(u32 dte);
>> +    u32 (*mk_dtentries)(dma_addr_t pt_dma);
>> +    u32 (*mk_ptentries)(phys_addr_t page, int prot);
>> +    phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
>> +    u32 pt_address_mask;
>> +};
>> +
>>   struct rk_iommu {
>>       struct device *dev;
>>       void __iomem **bases;
>> @@ -116,6 +125,7 @@ struct rk_iommudata {
>>   };
>>     static struct device *dma_dev;
>> +static const struct rk_iommu_ops *rk_ops;
>>     static inline void rk_table_flush(struct rk_iommu_domain *dom, 
>> dma_addr_t dma,
>>                     unsigned int count)
>> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>>   #define RK_PTE_PAGE_READABLE      BIT(1)
>>   #define RK_PTE_PAGE_VALID         BIT(0)
>>   -static inline phys_addr_t rk_pte_page_address(u32 pte)
>> -{
>> -    return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
>> -}
>> -
>>   static inline bool rk_pte_is_page_valid(u32 pte)
>>   {
>>       return pte & RK_PTE_PAGE_VALID;
>> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu 
>> *iommu)
>>           rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 
>> DTE_ADDR_DUMMY);
>>             dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
>> -        if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
>> +        if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
>
> Nit: might it make more sense to do something like:
>
>         dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
>         rk_iommu_write(... dte_addr)
>         if (rk_iommu_read(...) != dte_addr)
>
> so that you don't need to bother defining ->pt_address_mask for just 
> this one sanity-check?
>
>>               dev_err(iommu->dev, "Error during raw reset. 
>> MMU_DTE_ADDR is not functioning\n");
>>               return -EFAULT;
>>           }
>> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu 
>> *iommu)
>>       return 0;
>>   }
>>   +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>
> The argument type here should be u32, since it's a DTE, not a physical 
> address...
>
>> +{
>> +    return addr;
>> +}
>> +
>>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t 
>> iova)
>>   {
>>       void __iomem *base = iommu->bases[index];
>> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int 
>> index, dma_addr_t iova)
>>       page_offset = rk_iova_page_offset(iova);
>>         mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
>> -    mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
>> +    mmu_dte_addr_phys = 
>> rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
>
> ...and the cast here should not be here, since it *is* the conversion 
> that the called function is supposed to be performing.
>
>>       dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>>       dte_addr = phys_to_virt(dte_addr_phys);
>> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, 
>> int index, dma_addr_t iova)
>>       if (!rk_dte_is_pt_valid(dte))
>>           goto print_it;
>>   -    pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
>> +    pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>>       pte_addr = phys_to_virt(pte_addr_phys);
>>       pte = *pte_addr;
>>         if (!rk_pte_is_page_valid(pte))
>>           goto print_it;
>>   -    page_addr_phys = rk_pte_page_address(pte) + page_offset;
>> +    page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>>       page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>>     print_it:
>> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct 
>> iommu_domain *domain,
>>       if (!rk_dte_is_pt_valid(dte))
>>           goto out;
>>   -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       page_table = (u32 *)phys_to_virt(pt_phys);
>>       pte = page_table[rk_iova_pte_index(iova)];
>>       if (!rk_pte_is_page_valid(pte))
>>           goto out;
>>   -    phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
>> +    phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>>   out:
>>       spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>>   @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct 
>> rk_iommu_domain *rk_domain,
>>           return ERR_PTR(-ENOMEM);
>>       }
>>   -    dte = rk_mk_dte(pt_dma);
>> +    dte = rk_ops->mk_dtentries(pt_dma);
>>       *dte_addr = dte;
>>         rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>>       rk_table_flush(rk_domain,
>>                  rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>>   done:
>> -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       return (u32 *)phys_to_virt(pt_phys);
>>   }
>>   @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct 
>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>           if (rk_pte_is_page_valid(pte))
>>               goto unwind;
>>   -        pte_addr[pte_count] = rk_mk_pte(paddr, prot);
>> +        pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>>             paddr += SPAGE_SIZE;
>>       }
>> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct 
>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>                   pte_count * SPAGE_SIZE);
>>         iova += pte_count * SPAGE_SIZE;
>> -    page_phys = rk_pte_page_address(pte_addr[pte_count]);
>> +    page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>>       pr_err("iova: %pad already mapped to %pa cannot remap to phys: 
>> %pa prot: %#x\n",
>>              &iova, &page_phys, &paddr, prot);
>>   @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain 
>> *domain, unsigned long _iova,
>>       dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>>       pte_index = rk_iova_pte_index(iova);
>>       pte_addr = &page_table[pte_index];
>> -    pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
>> +
>> +    pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>>       ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>>                   paddr, size, prot);
>>   @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct 
>> iommu_domain *domain, unsigned long _iova,
>>           return 0;
>>       }
>>   -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>>       pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>>       unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, 
>> size);
>> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct 
>> iommu_domain *domain)
>>       for (i = 0; i < NUM_DT_ENTRIES; i++) {
>>           u32 dte = rk_domain->dt[i];
>>           if (rk_dte_is_pt_valid(dte)) {
>> -            phys_addr_t pt_phys = rk_dte_pt_address(dte);
>> +            phys_addr_t pt_phys = rk_ops->pt_address(dte);
>>               u32 *page_table = phys_to_virt(pt_phys);
>>               dma_unmap_single(dma_dev, pt_phys,
>>                        SPAGE_SIZE, DMA_TO_DEVICE);
>> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct 
>> platform_device *pdev)
>>       iommu->dev = dev;
>>       iommu->num_mmu = 0;
>>   +    if (!rk_ops)
>> +        rk_ops = of_device_get_match_data(dev);
>> +
>> +    /*
>> +     * That should not happen unless different versions of the
>> +     * hardware block are embedded the same SoC
>> +     */
>> +    WARN_ON(rk_ops != of_device_get_match_data(dev));
>
> Nit: calling of_device_get_match_data() twice seems rather untidy - 
> how about something like:
>
>     ops = of_device_get_match_data(dev);
>     if (!rk_ops)
>         rk_ops = ops;
>     else if (WARN_ON(rk_ops != ops))
>         return -EINVAL;
>
> Either way I think it would be good to treat unexpected inconsistentcy 
> as an actual error, rather than second-guessing the DT and carrying on 
> under the assumption the device is something other than it claimed to be.
>
>> +
>>       iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>>                       GFP_KERNEL);
>>       if (!iommu->bases)
>> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops 
>> rk_iommu_pm_ops = {
>>                   pm_runtime_force_resume)
>>   };
>>   +static struct rk_iommu_ops iommu_data_ops_v1 = {
>> +    .pt_address = &rk_dte_pt_address,
>> +    .mk_dtentries = &rk_mk_dte,
>> +    .mk_ptentries = &rk_mk_pte,
>> +    .dte_addr_phys = &rk_dte_addr_phys,
>> +    .pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>> +};
>> +
>>   static const struct of_device_id rk_iommu_dt_ids[] = {
>> -    { .compatible = "rockchip,iommu" },
>> +    {    .compatible = "rockchip,iommu",
>> +        .data = &iommu_data_ops_v1,
>> +    },
>>       { /* sentinel */ }
>>   };
>> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
>
> As before, unrelated and unnecessary since this driver is still bool 
> in the Kconfig. If you do want to support modular builds you'll also 
> need to ensure rk_iommu_ops.owner is set, but do it all as a separate 
> patch please.
>
> Thanks,
> Robin.
>
>>     static struct platform_driver rk_iommu_driver = {
>>       .probe = rk_iommu_probe,
>>
>
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21 13:38       ` Benjamin Gaignard
  0 siblings, 0 replies; 56+ messages in thread
From: Benjamin Gaignard @ 2021-05-21 13:38 UTC (permalink / raw)
  To: Robin Murphy, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel


Le 21/05/2021 à 14:58, Robin Murphy a écrit :
> On 2021-05-21 09:36, Benjamin Gaignard wrote:
>> Add internal ops to be able to handle incoming variant v2.
>> The goal is to keep the overall structure of the framework but
>> to allow to add the evolution of this hardware block.
>>
>> The ops are global for a SoC because iommu domains are not
>> attached to a specific devices if they are for a virtuel device like
>> drm. Use a global variable shouldn't be since SoC usually doesn't
>> embedded different versions of the iommu hardware block.
>> If that happen one day a WARN_ON will be displayed at probe time.
>
> IMO it would be a grievous error if such a "virtual device" ever gets 
> near the IOMMU API, so personally I wouldn't use that as a 
> justification for anything :)
>
> FWIW you should be OK to handle things on a per-instance basis, it 
> just means you have to defer some of the domain setup to .attach_dev 
> time, like various other drivers do. That said, there's nothing wrong 
> with the global if we do expect instances to be consistent across any 
> given Rockchip SoC (and my gut feeling is that we probably should).

I have tried that solution first but drm device appear to but such "virtual device" so I had to use the global.

I send a v6 to fix your others remarks.

Thanks for your advice.

Benjamin

>
>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
>> ---
>> version 5:
>>   - Use of_device_get_match_data()
>>   - Add internal ops inside the driver
>>
>>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>>   1 file changed, 50 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/iommu/rockchip-iommu.c 
>> b/drivers/iommu/rockchip-iommu.c
>> index 7a2932772fdf..e7b9bcf174b1 100644
>> --- a/drivers/iommu/rockchip-iommu.c
>> +++ b/drivers/iommu/rockchip-iommu.c
>> @@ -19,6 +19,7 @@
>>   #include <linux/iopoll.h>
>>   #include <linux/list.h>
>>   #include <linux/mm.h>
>> +#include <linux/module.h>
>
> This seems to be an unrelated and unnecessary change.
>
>>   #include <linux/init.h>
>>   #include <linux/of.h>
>>   #include <linux/of_iommu.h>
>> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>>       "aclk", "iface",
>>   };
>>   +struct rk_iommu_ops {
>> +    phys_addr_t (*pt_address)(u32 dte);
>> +    u32 (*mk_dtentries)(dma_addr_t pt_dma);
>> +    u32 (*mk_ptentries)(phys_addr_t page, int prot);
>> +    phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
>> +    u32 pt_address_mask;
>> +};
>> +
>>   struct rk_iommu {
>>       struct device *dev;
>>       void __iomem **bases;
>> @@ -116,6 +125,7 @@ struct rk_iommudata {
>>   };
>>     static struct device *dma_dev;
>> +static const struct rk_iommu_ops *rk_ops;
>>     static inline void rk_table_flush(struct rk_iommu_domain *dom, 
>> dma_addr_t dma,
>>                     unsigned int count)
>> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>>   #define RK_PTE_PAGE_READABLE      BIT(1)
>>   #define RK_PTE_PAGE_VALID         BIT(0)
>>   -static inline phys_addr_t rk_pte_page_address(u32 pte)
>> -{
>> -    return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
>> -}
>> -
>>   static inline bool rk_pte_is_page_valid(u32 pte)
>>   {
>>       return pte & RK_PTE_PAGE_VALID;
>> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu 
>> *iommu)
>>           rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 
>> DTE_ADDR_DUMMY);
>>             dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
>> -        if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
>> +        if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
>
> Nit: might it make more sense to do something like:
>
>         dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
>         rk_iommu_write(... dte_addr)
>         if (rk_iommu_read(...) != dte_addr)
>
> so that you don't need to bother defining ->pt_address_mask for just 
> this one sanity-check?
>
>>               dev_err(iommu->dev, "Error during raw reset. 
>> MMU_DTE_ADDR is not functioning\n");
>>               return -EFAULT;
>>           }
>> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu 
>> *iommu)
>>       return 0;
>>   }
>>   +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>
> The argument type here should be u32, since it's a DTE, not a physical 
> address...
>
>> +{
>> +    return addr;
>> +}
>> +
>>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t 
>> iova)
>>   {
>>       void __iomem *base = iommu->bases[index];
>> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int 
>> index, dma_addr_t iova)
>>       page_offset = rk_iova_page_offset(iova);
>>         mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
>> -    mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
>> +    mmu_dte_addr_phys = 
>> rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
>
> ...and the cast here should not be here, since it *is* the conversion 
> that the called function is supposed to be performing.
>
>>       dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>>       dte_addr = phys_to_virt(dte_addr_phys);
>> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, 
>> int index, dma_addr_t iova)
>>       if (!rk_dte_is_pt_valid(dte))
>>           goto print_it;
>>   -    pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
>> +    pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>>       pte_addr = phys_to_virt(pte_addr_phys);
>>       pte = *pte_addr;
>>         if (!rk_pte_is_page_valid(pte))
>>           goto print_it;
>>   -    page_addr_phys = rk_pte_page_address(pte) + page_offset;
>> +    page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>>       page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>>     print_it:
>> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct 
>> iommu_domain *domain,
>>       if (!rk_dte_is_pt_valid(dte))
>>           goto out;
>>   -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       page_table = (u32 *)phys_to_virt(pt_phys);
>>       pte = page_table[rk_iova_pte_index(iova)];
>>       if (!rk_pte_is_page_valid(pte))
>>           goto out;
>>   -    phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
>> +    phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>>   out:
>>       spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>>   @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct 
>> rk_iommu_domain *rk_domain,
>>           return ERR_PTR(-ENOMEM);
>>       }
>>   -    dte = rk_mk_dte(pt_dma);
>> +    dte = rk_ops->mk_dtentries(pt_dma);
>>       *dte_addr = dte;
>>         rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>>       rk_table_flush(rk_domain,
>>                  rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>>   done:
>> -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       return (u32 *)phys_to_virt(pt_phys);
>>   }
>>   @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct 
>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>           if (rk_pte_is_page_valid(pte))
>>               goto unwind;
>>   -        pte_addr[pte_count] = rk_mk_pte(paddr, prot);
>> +        pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>>             paddr += SPAGE_SIZE;
>>       }
>> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct 
>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>                   pte_count * SPAGE_SIZE);
>>         iova += pte_count * SPAGE_SIZE;
>> -    page_phys = rk_pte_page_address(pte_addr[pte_count]);
>> +    page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>>       pr_err("iova: %pad already mapped to %pa cannot remap to phys: 
>> %pa prot: %#x\n",
>>              &iova, &page_phys, &paddr, prot);
>>   @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain 
>> *domain, unsigned long _iova,
>>       dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>>       pte_index = rk_iova_pte_index(iova);
>>       pte_addr = &page_table[pte_index];
>> -    pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
>> +
>> +    pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>>       ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>>                   paddr, size, prot);
>>   @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct 
>> iommu_domain *domain, unsigned long _iova,
>>           return 0;
>>       }
>>   -    pt_phys = rk_dte_pt_address(dte);
>> +    pt_phys = rk_ops->pt_address(dte);
>>       pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>>       pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>>       unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, 
>> size);
>> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct 
>> iommu_domain *domain)
>>       for (i = 0; i < NUM_DT_ENTRIES; i++) {
>>           u32 dte = rk_domain->dt[i];
>>           if (rk_dte_is_pt_valid(dte)) {
>> -            phys_addr_t pt_phys = rk_dte_pt_address(dte);
>> +            phys_addr_t pt_phys = rk_ops->pt_address(dte);
>>               u32 *page_table = phys_to_virt(pt_phys);
>>               dma_unmap_single(dma_dev, pt_phys,
>>                        SPAGE_SIZE, DMA_TO_DEVICE);
>> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct 
>> platform_device *pdev)
>>       iommu->dev = dev;
>>       iommu->num_mmu = 0;
>>   +    if (!rk_ops)
>> +        rk_ops = of_device_get_match_data(dev);
>> +
>> +    /*
>> +     * That should not happen unless different versions of the
>> +     * hardware block are embedded the same SoC
>> +     */
>> +    WARN_ON(rk_ops != of_device_get_match_data(dev));
>
> Nit: calling of_device_get_match_data() twice seems rather untidy - 
> how about something like:
>
>     ops = of_device_get_match_data(dev);
>     if (!rk_ops)
>         rk_ops = ops;
>     else if (WARN_ON(rk_ops != ops))
>         return -EINVAL;
>
> Either way I think it would be good to treat unexpected inconsistentcy 
> as an actual error, rather than second-guessing the DT and carrying on 
> under the assumption the device is something other than it claimed to be.
>
>> +
>>       iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>>                       GFP_KERNEL);
>>       if (!iommu->bases)
>> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops 
>> rk_iommu_pm_ops = {
>>                   pm_runtime_force_resume)
>>   };
>>   +static struct rk_iommu_ops iommu_data_ops_v1 = {
>> +    .pt_address = &rk_dte_pt_address,
>> +    .mk_dtentries = &rk_mk_dte,
>> +    .mk_ptentries = &rk_mk_pte,
>> +    .dte_addr_phys = &rk_dte_addr_phys,
>> +    .pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>> +};
>> +
>>   static const struct of_device_id rk_iommu_dt_ids[] = {
>> -    { .compatible = "rockchip,iommu" },
>> +    {    .compatible = "rockchip,iommu",
>> +        .data = &iommu_data_ops_v1,
>> +    },
>>       { /* sentinel */ }
>>   };
>> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
>
> As before, unrelated and unnecessary since this driver is still bool 
> in the Kconfig. If you do want to support modular builds you'll also 
> need to ensure rk_iommu_ops.owner is set, but do it all as a separate 
> patch please.
>
> Thanks,
> Robin.
>
>>     static struct platform_driver rk_iommu_driver = {
>>       .probe = rk_iommu_probe,
>>
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
  2021-05-21  8:36   ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21 13:42     ` Robin Murphy
  -1 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 13:42 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> This second version of the hardware block has a different bits
> mapping for page table entries.
> Add the ops matching to this new mapping.
> Define a new compatible to distinguish it from the first version.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
> version 5:
>   - Use internal ops to support v2 hardware block
>   - Use GENMASK macro.
>   - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
>     because I believe that is more readable like this.
>   - Do not duplicate code.
> 
>   drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
>   1 file changed, 78 insertions(+)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index e7b9bcf174b1..23253a2f269e 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
>   	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
>   }
>   
> +/*
> + * In v2:
> + * 31:12 - PT address bit 31:0
> + * 11: 8 - PT address bit 35:32
> + *  7: 4 - PT address bit 39:36
> + *  3: 1 - Reserved
> + *     0 - 1 if PT @ PT address is valid
> + */
> +#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
> +#define DTE_HI_MASK1	GENMASK(11, 8)
> +#define DTE_HI_MASK2	GENMASK(7, 4)
> +#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
> +#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */

Nit: no harm in doing "#define DTE_HI_SHIFT1 (32 - 8)" etc. for maximum 
clarity if you want.

> +#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
> +#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
> +
> +static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
> +{
> +	u64 dte_v2 = dte;
> +
> +	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
> +		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
> +		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
> +
> +	return (phys_addr_t)dte_v2;
> +}
> +
>   static inline bool rk_dte_is_pt_valid(u32 dte)
>   {
>   	return dte & RK_DTE_PT_VALID;
> @@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>   	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
>   }
>   
> +static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
> +{
> +	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
> +		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
> +		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
> +
> +	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
> +}
> +
>   /*
>    * Each PTE has a Page address, some flags and a valid bit:
>    * +---------------------+---+-------+-+
> @@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
>   	return page | flags | RK_PTE_PAGE_VALID;
>   }
>   
> +/*
> + * In v2:
> + * 31:12 - Page address bit 31:0
> + *  11:9 - Page address bit 34:32
> + *   8:4 - Page address bit 39:35
> + *     3 - Security
> + *     2 - Readable
> + *     1 - Writable
> + *     0 - 1 if Page @ Page address is valid
> + */
> +#define RK_PTE_PAGE_READABLE_V2      BIT(2)
> +#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
> +
> +static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
> +{
> +	u32 flags = 0;
> +
> +	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
> +	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
> +
> +	return rk_mk_dte_v2(page) | flags ;
> +}
> +
>   static u32 rk_mk_pte_invalid(u32 pte)
>   {
>   	return pte & ~RK_PTE_PAGE_VALID;
> @@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>   	return addr;
>   }
>   
> +#define DT_HI_MASK GENMASK_ULL(39, 32)
> +#define DT_SHIFT   28
> +
> +static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
> +{
> +	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
> +}

Are we missing something overall? AFAICS the DT_HI_MASK bits of 
RK_MMU_DTE_ADDR will never actually be used, since rk_iommu_enable() 
just writes the value of dt_dma directly...

> +
>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   {
>   	void __iomem *base = iommu->bases[index];
> @@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
>   	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>   };
>   
> +static struct rk_iommu_ops iommu_data_ops_v2 = {
> +	.pt_address = &rk_dte_pt_address_v2,
> +	.mk_dtentries = &rk_mk_dte_v2,
> +	.mk_ptentries = &rk_mk_pte_v2,
> +	.dte_addr_phys = &rk_dte_addr_phys_v2,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
> +};
> +
>   static const struct of_device_id rk_iommu_dt_ids[] = {
>   	{	.compatible = "rockchip,iommu",
>   		.data = &iommu_data_ops_v1,
>   	},
> +	{	.compatible = "rockchip,rk3568-iommu",
> +		.data = &iommu_data_ops_v2,
> +	},
>   	{ /* sentinel */ }
>   };
>   MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
> 

...and I'll bet the reason it appears to work is that we also never 
actually set the IOMMU device's DMA masks anywhere, so what happens in 
practice is that even if pagetable pages are allocated above 32 bits 
they'll just get bounced by the DMA mapping ops and gradually fill up 
the SWIOTLB buffer. That's something you're liable to have a bad time 
with under real-world usage ;)

The overall cleanup is *so* much better though, thanks for that!

Robin.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
@ 2021-05-21 13:42     ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 13:42 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> This second version of the hardware block has a different bits
> mapping for page table entries.
> Add the ops matching to this new mapping.
> Define a new compatible to distinguish it from the first version.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
> version 5:
>   - Use internal ops to support v2 hardware block
>   - Use GENMASK macro.
>   - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
>     because I believe that is more readable like this.
>   - Do not duplicate code.
> 
>   drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
>   1 file changed, 78 insertions(+)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index e7b9bcf174b1..23253a2f269e 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
>   	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
>   }
>   
> +/*
> + * In v2:
> + * 31:12 - PT address bit 31:0
> + * 11: 8 - PT address bit 35:32
> + *  7: 4 - PT address bit 39:36
> + *  3: 1 - Reserved
> + *     0 - 1 if PT @ PT address is valid
> + */
> +#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
> +#define DTE_HI_MASK1	GENMASK(11, 8)
> +#define DTE_HI_MASK2	GENMASK(7, 4)
> +#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
> +#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */

Nit: no harm in doing "#define DTE_HI_SHIFT1 (32 - 8)" etc. for maximum 
clarity if you want.

> +#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
> +#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
> +
> +static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
> +{
> +	u64 dte_v2 = dte;
> +
> +	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
> +		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
> +		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
> +
> +	return (phys_addr_t)dte_v2;
> +}
> +
>   static inline bool rk_dte_is_pt_valid(u32 dte)
>   {
>   	return dte & RK_DTE_PT_VALID;
> @@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>   	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
>   }
>   
> +static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
> +{
> +	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
> +		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
> +		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
> +
> +	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
> +}
> +
>   /*
>    * Each PTE has a Page address, some flags and a valid bit:
>    * +---------------------+---+-------+-+
> @@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
>   	return page | flags | RK_PTE_PAGE_VALID;
>   }
>   
> +/*
> + * In v2:
> + * 31:12 - Page address bit 31:0
> + *  11:9 - Page address bit 34:32
> + *   8:4 - Page address bit 39:35
> + *     3 - Security
> + *     2 - Readable
> + *     1 - Writable
> + *     0 - 1 if Page @ Page address is valid
> + */
> +#define RK_PTE_PAGE_READABLE_V2      BIT(2)
> +#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
> +
> +static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
> +{
> +	u32 flags = 0;
> +
> +	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
> +	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
> +
> +	return rk_mk_dte_v2(page) | flags ;
> +}
> +
>   static u32 rk_mk_pte_invalid(u32 pte)
>   {
>   	return pte & ~RK_PTE_PAGE_VALID;
> @@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>   	return addr;
>   }
>   
> +#define DT_HI_MASK GENMASK_ULL(39, 32)
> +#define DT_SHIFT   28
> +
> +static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
> +{
> +	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
> +}

Are we missing something overall? AFAICS the DT_HI_MASK bits of 
RK_MMU_DTE_ADDR will never actually be used, since rk_iommu_enable() 
just writes the value of dt_dma directly...

> +
>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   {
>   	void __iomem *base = iommu->bases[index];
> @@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
>   	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>   };
>   
> +static struct rk_iommu_ops iommu_data_ops_v2 = {
> +	.pt_address = &rk_dte_pt_address_v2,
> +	.mk_dtentries = &rk_mk_dte_v2,
> +	.mk_ptentries = &rk_mk_pte_v2,
> +	.dte_addr_phys = &rk_dte_addr_phys_v2,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
> +};
> +
>   static const struct of_device_id rk_iommu_dt_ids[] = {
>   	{	.compatible = "rockchip,iommu",
>   		.data = &iommu_data_ops_v1,
>   	},
> +	{	.compatible = "rockchip,rk3568-iommu",
> +		.data = &iommu_data_ops_v2,
> +	},
>   	{ /* sentinel */ }
>   };
>   MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
> 

...and I'll bet the reason it appears to work is that we also never 
actually set the IOMMU device's DMA masks anywhere, so what happens in 
practice is that even if pagetable pages are allocated above 32 bits 
they'll just get bounced by the DMA mapping ops and gradually fill up 
the SWIOTLB buffer. That's something you're liable to have a bad time 
with under real-world usage ;)

The overall cleanup is *so* much better though, thanks for that!

Robin.

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
@ 2021-05-21 13:42     ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 13:42 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: devicetree, linux-kernel, linux-rockchip, iommu, kernel,
	linux-arm-kernel

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> This second version of the hardware block has a different bits
> mapping for page table entries.
> Add the ops matching to this new mapping.
> Define a new compatible to distinguish it from the first version.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
> version 5:
>   - Use internal ops to support v2 hardware block
>   - Use GENMASK macro.
>   - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
>     because I believe that is more readable like this.
>   - Do not duplicate code.
> 
>   drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
>   1 file changed, 78 insertions(+)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index e7b9bcf174b1..23253a2f269e 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
>   	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
>   }
>   
> +/*
> + * In v2:
> + * 31:12 - PT address bit 31:0
> + * 11: 8 - PT address bit 35:32
> + *  7: 4 - PT address bit 39:36
> + *  3: 1 - Reserved
> + *     0 - 1 if PT @ PT address is valid
> + */
> +#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
> +#define DTE_HI_MASK1	GENMASK(11, 8)
> +#define DTE_HI_MASK2	GENMASK(7, 4)
> +#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
> +#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */

Nit: no harm in doing "#define DTE_HI_SHIFT1 (32 - 8)" etc. for maximum 
clarity if you want.

> +#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
> +#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
> +
> +static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
> +{
> +	u64 dte_v2 = dte;
> +
> +	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
> +		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
> +		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
> +
> +	return (phys_addr_t)dte_v2;
> +}
> +
>   static inline bool rk_dte_is_pt_valid(u32 dte)
>   {
>   	return dte & RK_DTE_PT_VALID;
> @@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>   	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
>   }
>   
> +static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
> +{
> +	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
> +		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
> +		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
> +
> +	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
> +}
> +
>   /*
>    * Each PTE has a Page address, some flags and a valid bit:
>    * +---------------------+---+-------+-+
> @@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
>   	return page | flags | RK_PTE_PAGE_VALID;
>   }
>   
> +/*
> + * In v2:
> + * 31:12 - Page address bit 31:0
> + *  11:9 - Page address bit 34:32
> + *   8:4 - Page address bit 39:35
> + *     3 - Security
> + *     2 - Readable
> + *     1 - Writable
> + *     0 - 1 if Page @ Page address is valid
> + */
> +#define RK_PTE_PAGE_READABLE_V2      BIT(2)
> +#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
> +
> +static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
> +{
> +	u32 flags = 0;
> +
> +	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
> +	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
> +
> +	return rk_mk_dte_v2(page) | flags ;
> +}
> +
>   static u32 rk_mk_pte_invalid(u32 pte)
>   {
>   	return pte & ~RK_PTE_PAGE_VALID;
> @@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>   	return addr;
>   }
>   
> +#define DT_HI_MASK GENMASK_ULL(39, 32)
> +#define DT_SHIFT   28
> +
> +static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
> +{
> +	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
> +}

Are we missing something overall? AFAICS the DT_HI_MASK bits of 
RK_MMU_DTE_ADDR will never actually be used, since rk_iommu_enable() 
just writes the value of dt_dma directly...

> +
>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   {
>   	void __iomem *base = iommu->bases[index];
> @@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
>   	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>   };
>   
> +static struct rk_iommu_ops iommu_data_ops_v2 = {
> +	.pt_address = &rk_dte_pt_address_v2,
> +	.mk_dtentries = &rk_mk_dte_v2,
> +	.mk_ptentries = &rk_mk_pte_v2,
> +	.dte_addr_phys = &rk_dte_addr_phys_v2,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
> +};
> +
>   static const struct of_device_id rk_iommu_dt_ids[] = {
>   	{	.compatible = "rockchip,iommu",
>   		.data = &iommu_data_ops_v1,
>   	},
> +	{	.compatible = "rockchip,rk3568-iommu",
> +		.data = &iommu_data_ops_v2,
> +	},
>   	{ /* sentinel */ }
>   };
>   MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
> 

...and I'll bet the reason it appears to work is that we also never 
actually set the IOMMU device's DMA masks anywhere, so what happens in 
practice is that even if pagetable pages are allocated above 32 bits 
they'll just get bounced by the DMA mapping ops and gradually fill up 
the SWIOTLB buffer. That's something you're liable to have a bad time 
with under real-world usage ;)

The overall cleanup is *so* much better though, thanks for that!

Robin.
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2
@ 2021-05-21 13:42     ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 13:42 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel

On 2021-05-21 09:36, Benjamin Gaignard wrote:
> This second version of the hardware block has a different bits
> mapping for page table entries.
> Add the ops matching to this new mapping.
> Define a new compatible to distinguish it from the first version.
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
> version 5:
>   - Use internal ops to support v2 hardware block
>   - Use GENMASK macro.
>   - Keep rk_dte_pt_address() and rk_dte_pt_address_v2() separated
>     because I believe that is more readable like this.
>   - Do not duplicate code.
> 
>   drivers/iommu/rockchip-iommu.c | 78 ++++++++++++++++++++++++++++++++++
>   1 file changed, 78 insertions(+)
> 
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index e7b9bcf174b1..23253a2f269e 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -189,6 +189,33 @@ static inline phys_addr_t rk_dte_pt_address(u32 dte)
>   	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
>   }
>   
> +/*
> + * In v2:
> + * 31:12 - PT address bit 31:0
> + * 11: 8 - PT address bit 35:32
> + *  7: 4 - PT address bit 39:36
> + *  3: 1 - Reserved
> + *     0 - 1 if PT @ PT address is valid
> + */
> +#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
> +#define DTE_HI_MASK1	GENMASK(11, 8)
> +#define DTE_HI_MASK2	GENMASK(7, 4)
> +#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
> +#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */

Nit: no harm in doing "#define DTE_HI_SHIFT1 (32 - 8)" etc. for maximum 
clarity if you want.

> +#define PAGE_DESC_HI_MASK1	GENMASK_ULL(39, 36)
> +#define PAGE_DESC_HI_MASK2	GENMASK_ULL(35, 32)
> +
> +static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
> +{
> +	u64 dte_v2 = dte;
> +
> +	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
> +		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
> +		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
> +
> +	return (phys_addr_t)dte_v2;
> +}
> +
>   static inline bool rk_dte_is_pt_valid(u32 dte)
>   {
>   	return dte & RK_DTE_PT_VALID;
> @@ -199,6 +226,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>   	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
>   }
>   
> +static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
> +{
> +	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
> +		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
> +		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
> +
> +	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
> +}
> +
>   /*
>    * Each PTE has a Page address, some flags and a valid bit:
>    * +---------------------+---+-------+-+
> @@ -240,6 +276,29 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
>   	return page | flags | RK_PTE_PAGE_VALID;
>   }
>   
> +/*
> + * In v2:
> + * 31:12 - Page address bit 31:0
> + *  11:9 - Page address bit 34:32
> + *   8:4 - Page address bit 39:35
> + *     3 - Security
> + *     2 - Readable
> + *     1 - Writable
> + *     0 - 1 if Page @ Page address is valid
> + */
> +#define RK_PTE_PAGE_READABLE_V2      BIT(2)
> +#define RK_PTE_PAGE_WRITABLE_V2      BIT(1)
> +
> +static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
> +{
> +	u32 flags = 0;
> +
> +	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
> +	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
> +
> +	return rk_mk_dte_v2(page) | flags ;
> +}
> +
>   static u32 rk_mk_pte_invalid(u32 pte)
>   {
>   	return pte & ~RK_PTE_PAGE_VALID;
> @@ -480,6 +539,14 @@ static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>   	return addr;
>   }
>   
> +#define DT_HI_MASK GENMASK_ULL(39, 32)
> +#define DT_SHIFT   28
> +
> +static inline phys_addr_t rk_dte_addr_phys_v2(phys_addr_t addr)
> +{
> +	return (addr & RK_DTE_PT_ADDRESS_MASK) | ((addr & DT_HI_MASK) << DT_SHIFT);
> +}

Are we missing something overall? AFAICS the DT_HI_MASK bits of 
RK_MMU_DTE_ADDR will never actually be used, since rk_iommu_enable() 
just writes the value of dt_dma directly...

> +
>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
>   {
>   	void __iomem *base = iommu->bases[index];
> @@ -1305,10 +1372,21 @@ static struct rk_iommu_ops iommu_data_ops_v1 = {
>   	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>   };
>   
> +static struct rk_iommu_ops iommu_data_ops_v2 = {
> +	.pt_address = &rk_dte_pt_address_v2,
> +	.mk_dtentries = &rk_mk_dte_v2,
> +	.mk_ptentries = &rk_mk_pte_v2,
> +	.dte_addr_phys = &rk_dte_addr_phys_v2,
> +	.pt_address_mask = RK_DTE_PT_ADDRESS_MASK_V2,
> +};
> +
>   static const struct of_device_id rk_iommu_dt_ids[] = {
>   	{	.compatible = "rockchip,iommu",
>   		.data = &iommu_data_ops_v1,
>   	},
> +	{	.compatible = "rockchip,rk3568-iommu",
> +		.data = &iommu_data_ops_v2,
> +	},
>   	{ /* sentinel */ }
>   };
>   MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
> 

...and I'll bet the reason it appears to work is that we also never 
actually set the IOMMU device's DMA masks anywhere, so what happens in 
practice is that even if pagetable pages are allocated above 32 bits 
they'll just get bounced by the DMA mapping ops and gradually fill up 
the SWIOTLB buffer. That's something you're liable to have a bad time 
with under real-world usage ;)

The overall cleanup is *so* much better though, thanks for that!

Robin.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
  2021-05-21 13:38       ` Benjamin Gaignard
  (?)
  (?)
@ 2021-05-21 14:44         ` Robin Murphy
  -1 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 14:44 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel

On 2021-05-21 14:38, Benjamin Gaignard wrote:
> 
> Le 21/05/2021 à 14:58, Robin Murphy a écrit :
>> On 2021-05-21 09:36, Benjamin Gaignard wrote:
>>> Add internal ops to be able to handle incoming variant v2.
>>> The goal is to keep the overall structure of the framework but
>>> to allow to add the evolution of this hardware block.
>>>
>>> The ops are global for a SoC because iommu domains are not
>>> attached to a specific devices if they are for a virtuel device like
>>> drm. Use a global variable shouldn't be since SoC usually doesn't
>>> embedded different versions of the iommu hardware block.
>>> If that happen one day a WARN_ON will be displayed at probe time.
>>
>> IMO it would be a grievous error if such a "virtual device" ever gets 
>> near the IOMMU API, so personally I wouldn't use that as a 
>> justification for anything :)
>>
>> FWIW you should be OK to handle things on a per-instance basis, it 
>> just means you have to defer some of the domain setup to .attach_dev 
>> time, like various other drivers do. That said, there's nothing wrong 
>> with the global if we do expect instances to be consistent across any 
>> given Rockchip SoC (and my gut feeling is that we probably should).
> 
> I have tried that solution first but drm device appear to but such 
> "virtual device" so I had to use the global.

Hmm, the "rockchip,display-subsystem" node is not associated with an 
IOMMU, and shouldn't even be passed to the DMA API either, because it's 
not a real piece of DMA capable hardware. Whatever the DRM stack is 
doing above, it should only be the actual VOP devices that we see down 
here. If not, that's indicative of something being wrong elsewhere. Like 
I say though, I think it's fine to use global ops simply on the 
expectation that that's how the new SOCs are going to be.

In fact this reminds me, I think I started writing a patch somewhere to 
clean up the virtual device mess for rockchip-drm (IIRC I could see no 
reason why we can't just allocate the DRM device from the VOP driver, 
similar to what exynos-drm does). Maybe I should dig that up again...

> I send a v6 to fix your others remarks.

I guess I'll wait for v7 now then, since I got sidetracked before 
sending my review of patch #4 (heck, I've just spent the last half hour 
doing something else in the middle of writing this!) ;)

Cheers,
Robin.

> 
> Thanks for your advice.
> 
> Benjamin
> 
>>
>>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
>>> ---
>>> version 5:
>>>   - Use of_device_get_match_data()
>>>   - Add internal ops inside the driver
>>>
>>>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>>>   1 file changed, 50 insertions(+), 19 deletions(-)
>>>
>>> diff --git a/drivers/iommu/rockchip-iommu.c 
>>> b/drivers/iommu/rockchip-iommu.c
>>> index 7a2932772fdf..e7b9bcf174b1 100644
>>> --- a/drivers/iommu/rockchip-iommu.c
>>> +++ b/drivers/iommu/rockchip-iommu.c
>>> @@ -19,6 +19,7 @@
>>>   #include <linux/iopoll.h>
>>>   #include <linux/list.h>
>>>   #include <linux/mm.h>
>>> +#include <linux/module.h>
>>
>> This seems to be an unrelated and unnecessary change.
>>
>>>   #include <linux/init.h>
>>>   #include <linux/of.h>
>>>   #include <linux/of_iommu.h>
>>> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>>>       "aclk", "iface",
>>>   };
>>>   +struct rk_iommu_ops {
>>> +    phys_addr_t (*pt_address)(u32 dte);
>>> +    u32 (*mk_dtentries)(dma_addr_t pt_dma);
>>> +    u32 (*mk_ptentries)(phys_addr_t page, int prot);
>>> +    phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
>>> +    u32 pt_address_mask;
>>> +};
>>> +
>>>   struct rk_iommu {
>>>       struct device *dev;
>>>       void __iomem **bases;
>>> @@ -116,6 +125,7 @@ struct rk_iommudata {
>>>   };
>>>     static struct device *dma_dev;
>>> +static const struct rk_iommu_ops *rk_ops;
>>>     static inline void rk_table_flush(struct rk_iommu_domain *dom, 
>>> dma_addr_t dma,
>>>                     unsigned int count)
>>> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>>>   #define RK_PTE_PAGE_READABLE      BIT(1)
>>>   #define RK_PTE_PAGE_VALID         BIT(0)
>>>   -static inline phys_addr_t rk_pte_page_address(u32 pte)
>>> -{
>>> -    return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
>>> -}
>>> -
>>>   static inline bool rk_pte_is_page_valid(u32 pte)
>>>   {
>>>       return pte & RK_PTE_PAGE_VALID;
>>> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu 
>>> *iommu)
>>>           rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 
>>> DTE_ADDR_DUMMY);
>>>             dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
>>> -        if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
>>> +        if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
>>
>> Nit: might it make more sense to do something like:
>>
>>         dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
>>         rk_iommu_write(... dte_addr)
>>         if (rk_iommu_read(...) != dte_addr)
>>
>> so that you don't need to bother defining ->pt_address_mask for just 
>> this one sanity-check?
>>
>>>               dev_err(iommu->dev, "Error during raw reset. 
>>> MMU_DTE_ADDR is not functioning\n");
>>>               return -EFAULT;
>>>           }
>>> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu 
>>> *iommu)
>>>       return 0;
>>>   }
>>>   +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>>
>> The argument type here should be u32, since it's a DTE, not a physical 
>> address...
>>
>>> +{
>>> +    return addr;
>>> +}
>>> +
>>>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t 
>>> iova)
>>>   {
>>>       void __iomem *base = iommu->bases[index];
>>> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int 
>>> index, dma_addr_t iova)
>>>       page_offset = rk_iova_page_offset(iova);
>>>         mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
>>> -    mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
>>> +    mmu_dte_addr_phys = 
>>> rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
>>
>> ...and the cast here should not be here, since it *is* the conversion 
>> that the called function is supposed to be performing.
>>
>>>       dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>>>       dte_addr = phys_to_virt(dte_addr_phys);
>>> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, 
>>> int index, dma_addr_t iova)
>>>       if (!rk_dte_is_pt_valid(dte))
>>>           goto print_it;
>>>   -    pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
>>> +    pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>>>       pte_addr = phys_to_virt(pte_addr_phys);
>>>       pte = *pte_addr;
>>>         if (!rk_pte_is_page_valid(pte))
>>>           goto print_it;
>>>   -    page_addr_phys = rk_pte_page_address(pte) + page_offset;
>>> +    page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>>>       page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>>>     print_it:
>>> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct 
>>> iommu_domain *domain,
>>>       if (!rk_dte_is_pt_valid(dte))
>>>           goto out;
>>>   -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       page_table = (u32 *)phys_to_virt(pt_phys);
>>>       pte = page_table[rk_iova_pte_index(iova)];
>>>       if (!rk_pte_is_page_valid(pte))
>>>           goto out;
>>>   -    phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
>>> +    phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>>>   out:
>>>       spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>>>   @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct 
>>> rk_iommu_domain *rk_domain,
>>>           return ERR_PTR(-ENOMEM);
>>>       }
>>>   -    dte = rk_mk_dte(pt_dma);
>>> +    dte = rk_ops->mk_dtentries(pt_dma);
>>>       *dte_addr = dte;
>>>         rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>>>       rk_table_flush(rk_domain,
>>>                  rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>>>   done:
>>> -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       return (u32 *)phys_to_virt(pt_phys);
>>>   }
>>>   @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct 
>>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>>           if (rk_pte_is_page_valid(pte))
>>>               goto unwind;
>>>   -        pte_addr[pte_count] = rk_mk_pte(paddr, prot);
>>> +        pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>>>             paddr += SPAGE_SIZE;
>>>       }
>>> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct 
>>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>>                   pte_count * SPAGE_SIZE);
>>>         iova += pte_count * SPAGE_SIZE;
>>> -    page_phys = rk_pte_page_address(pte_addr[pte_count]);
>>> +    page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>>>       pr_err("iova: %pad already mapped to %pa cannot remap to phys: 
>>> %pa prot: %#x\n",
>>>              &iova, &page_phys, &paddr, prot);
>>>   @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain 
>>> *domain, unsigned long _iova,
>>>       dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>>>       pte_index = rk_iova_pte_index(iova);
>>>       pte_addr = &page_table[pte_index];
>>> -    pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
>>> +
>>> +    pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>>>       ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>>>                   paddr, size, prot);
>>>   @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct 
>>> iommu_domain *domain, unsigned long _iova,
>>>           return 0;
>>>       }
>>>   -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>>>       pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>>>       unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, 
>>> size);
>>> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct 
>>> iommu_domain *domain)
>>>       for (i = 0; i < NUM_DT_ENTRIES; i++) {
>>>           u32 dte = rk_domain->dt[i];
>>>           if (rk_dte_is_pt_valid(dte)) {
>>> -            phys_addr_t pt_phys = rk_dte_pt_address(dte);
>>> +            phys_addr_t pt_phys = rk_ops->pt_address(dte);
>>>               u32 *page_table = phys_to_virt(pt_phys);
>>>               dma_unmap_single(dma_dev, pt_phys,
>>>                        SPAGE_SIZE, DMA_TO_DEVICE);
>>> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct 
>>> platform_device *pdev)
>>>       iommu->dev = dev;
>>>       iommu->num_mmu = 0;
>>>   +    if (!rk_ops)
>>> +        rk_ops = of_device_get_match_data(dev);
>>> +
>>> +    /*
>>> +     * That should not happen unless different versions of the
>>> +     * hardware block are embedded the same SoC
>>> +     */
>>> +    WARN_ON(rk_ops != of_device_get_match_data(dev));
>>
>> Nit: calling of_device_get_match_data() twice seems rather untidy - 
>> how about something like:
>>
>>     ops = of_device_get_match_data(dev);
>>     if (!rk_ops)
>>         rk_ops = ops;
>>     else if (WARN_ON(rk_ops != ops))
>>         return -EINVAL;
>>
>> Either way I think it would be good to treat unexpected inconsistentcy 
>> as an actual error, rather than second-guessing the DT and carrying on 
>> under the assumption the device is something other than it claimed to be.
>>
>>> +
>>>       iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>>>                       GFP_KERNEL);
>>>       if (!iommu->bases)
>>> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops 
>>> rk_iommu_pm_ops = {
>>>                   pm_runtime_force_resume)
>>>   };
>>>   +static struct rk_iommu_ops iommu_data_ops_v1 = {
>>> +    .pt_address = &rk_dte_pt_address,
>>> +    .mk_dtentries = &rk_mk_dte,
>>> +    .mk_ptentries = &rk_mk_pte,
>>> +    .dte_addr_phys = &rk_dte_addr_phys,
>>> +    .pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>>> +};
>>> +
>>>   static const struct of_device_id rk_iommu_dt_ids[] = {
>>> -    { .compatible = "rockchip,iommu" },
>>> +    {    .compatible = "rockchip,iommu",
>>> +        .data = &iommu_data_ops_v1,
>>> +    },
>>>       { /* sentinel */ }
>>>   };
>>> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
>>
>> As before, unrelated and unnecessary since this driver is still bool 
>> in the Kconfig. If you do want to support modular builds you'll also 
>> need to ensure rk_iommu_ops.owner is set, but do it all as a separate 
>> patch please.
>>
>> Thanks,
>> Robin.
>>
>>>     static struct platform_driver rk_iommu_driver = {
>>>       .probe = rk_iommu_probe,
>>>
>>

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21 14:44         ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 14:44 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel

On 2021-05-21 14:38, Benjamin Gaignard wrote:
> 
> Le 21/05/2021 à 14:58, Robin Murphy a écrit :
>> On 2021-05-21 09:36, Benjamin Gaignard wrote:
>>> Add internal ops to be able to handle incoming variant v2.
>>> The goal is to keep the overall structure of the framework but
>>> to allow to add the evolution of this hardware block.
>>>
>>> The ops are global for a SoC because iommu domains are not
>>> attached to a specific devices if they are for a virtuel device like
>>> drm. Use a global variable shouldn't be since SoC usually doesn't
>>> embedded different versions of the iommu hardware block.
>>> If that happen one day a WARN_ON will be displayed at probe time.
>>
>> IMO it would be a grievous error if such a "virtual device" ever gets 
>> near the IOMMU API, so personally I wouldn't use that as a 
>> justification for anything :)
>>
>> FWIW you should be OK to handle things on a per-instance basis, it 
>> just means you have to defer some of the domain setup to .attach_dev 
>> time, like various other drivers do. That said, there's nothing wrong 
>> with the global if we do expect instances to be consistent across any 
>> given Rockchip SoC (and my gut feeling is that we probably should).
> 
> I have tried that solution first but drm device appear to but such 
> "virtual device" so I had to use the global.

Hmm, the "rockchip,display-subsystem" node is not associated with an 
IOMMU, and shouldn't even be passed to the DMA API either, because it's 
not a real piece of DMA capable hardware. Whatever the DRM stack is 
doing above, it should only be the actual VOP devices that we see down 
here. If not, that's indicative of something being wrong elsewhere. Like 
I say though, I think it's fine to use global ops simply on the 
expectation that that's how the new SOCs are going to be.

In fact this reminds me, I think I started writing a patch somewhere to 
clean up the virtual device mess for rockchip-drm (IIRC I could see no 
reason why we can't just allocate the DRM device from the VOP driver, 
similar to what exynos-drm does). Maybe I should dig that up again...

> I send a v6 to fix your others remarks.

I guess I'll wait for v7 now then, since I got sidetracked before 
sending my review of patch #4 (heck, I've just spent the last half hour 
doing something else in the middle of writing this!) ;)

Cheers,
Robin.

> 
> Thanks for your advice.
> 
> Benjamin
> 
>>
>>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
>>> ---
>>> version 5:
>>>   - Use of_device_get_match_data()
>>>   - Add internal ops inside the driver
>>>
>>>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>>>   1 file changed, 50 insertions(+), 19 deletions(-)
>>>
>>> diff --git a/drivers/iommu/rockchip-iommu.c 
>>> b/drivers/iommu/rockchip-iommu.c
>>> index 7a2932772fdf..e7b9bcf174b1 100644
>>> --- a/drivers/iommu/rockchip-iommu.c
>>> +++ b/drivers/iommu/rockchip-iommu.c
>>> @@ -19,6 +19,7 @@
>>>   #include <linux/iopoll.h>
>>>   #include <linux/list.h>
>>>   #include <linux/mm.h>
>>> +#include <linux/module.h>
>>
>> This seems to be an unrelated and unnecessary change.
>>
>>>   #include <linux/init.h>
>>>   #include <linux/of.h>
>>>   #include <linux/of_iommu.h>
>>> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>>>       "aclk", "iface",
>>>   };
>>>   +struct rk_iommu_ops {
>>> +    phys_addr_t (*pt_address)(u32 dte);
>>> +    u32 (*mk_dtentries)(dma_addr_t pt_dma);
>>> +    u32 (*mk_ptentries)(phys_addr_t page, int prot);
>>> +    phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
>>> +    u32 pt_address_mask;
>>> +};
>>> +
>>>   struct rk_iommu {
>>>       struct device *dev;
>>>       void __iomem **bases;
>>> @@ -116,6 +125,7 @@ struct rk_iommudata {
>>>   };
>>>     static struct device *dma_dev;
>>> +static const struct rk_iommu_ops *rk_ops;
>>>     static inline void rk_table_flush(struct rk_iommu_domain *dom, 
>>> dma_addr_t dma,
>>>                     unsigned int count)
>>> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>>>   #define RK_PTE_PAGE_READABLE      BIT(1)
>>>   #define RK_PTE_PAGE_VALID         BIT(0)
>>>   -static inline phys_addr_t rk_pte_page_address(u32 pte)
>>> -{
>>> -    return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
>>> -}
>>> -
>>>   static inline bool rk_pte_is_page_valid(u32 pte)
>>>   {
>>>       return pte & RK_PTE_PAGE_VALID;
>>> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu 
>>> *iommu)
>>>           rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 
>>> DTE_ADDR_DUMMY);
>>>             dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
>>> -        if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
>>> +        if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
>>
>> Nit: might it make more sense to do something like:
>>
>>         dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
>>         rk_iommu_write(... dte_addr)
>>         if (rk_iommu_read(...) != dte_addr)
>>
>> so that you don't need to bother defining ->pt_address_mask for just 
>> this one sanity-check?
>>
>>>               dev_err(iommu->dev, "Error during raw reset. 
>>> MMU_DTE_ADDR is not functioning\n");
>>>               return -EFAULT;
>>>           }
>>> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu 
>>> *iommu)
>>>       return 0;
>>>   }
>>>   +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>>
>> The argument type here should be u32, since it's a DTE, not a physical 
>> address...
>>
>>> +{
>>> +    return addr;
>>> +}
>>> +
>>>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t 
>>> iova)
>>>   {
>>>       void __iomem *base = iommu->bases[index];
>>> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int 
>>> index, dma_addr_t iova)
>>>       page_offset = rk_iova_page_offset(iova);
>>>         mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
>>> -    mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
>>> +    mmu_dte_addr_phys = 
>>> rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
>>
>> ...and the cast here should not be here, since it *is* the conversion 
>> that the called function is supposed to be performing.
>>
>>>       dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>>>       dte_addr = phys_to_virt(dte_addr_phys);
>>> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, 
>>> int index, dma_addr_t iova)
>>>       if (!rk_dte_is_pt_valid(dte))
>>>           goto print_it;
>>>   -    pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
>>> +    pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>>>       pte_addr = phys_to_virt(pte_addr_phys);
>>>       pte = *pte_addr;
>>>         if (!rk_pte_is_page_valid(pte))
>>>           goto print_it;
>>>   -    page_addr_phys = rk_pte_page_address(pte) + page_offset;
>>> +    page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>>>       page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>>>     print_it:
>>> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct 
>>> iommu_domain *domain,
>>>       if (!rk_dte_is_pt_valid(dte))
>>>           goto out;
>>>   -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       page_table = (u32 *)phys_to_virt(pt_phys);
>>>       pte = page_table[rk_iova_pte_index(iova)];
>>>       if (!rk_pte_is_page_valid(pte))
>>>           goto out;
>>>   -    phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
>>> +    phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>>>   out:
>>>       spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>>>   @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct 
>>> rk_iommu_domain *rk_domain,
>>>           return ERR_PTR(-ENOMEM);
>>>       }
>>>   -    dte = rk_mk_dte(pt_dma);
>>> +    dte = rk_ops->mk_dtentries(pt_dma);
>>>       *dte_addr = dte;
>>>         rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>>>       rk_table_flush(rk_domain,
>>>                  rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>>>   done:
>>> -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       return (u32 *)phys_to_virt(pt_phys);
>>>   }
>>>   @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct 
>>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>>           if (rk_pte_is_page_valid(pte))
>>>               goto unwind;
>>>   -        pte_addr[pte_count] = rk_mk_pte(paddr, prot);
>>> +        pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>>>             paddr += SPAGE_SIZE;
>>>       }
>>> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct 
>>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>>                   pte_count * SPAGE_SIZE);
>>>         iova += pte_count * SPAGE_SIZE;
>>> -    page_phys = rk_pte_page_address(pte_addr[pte_count]);
>>> +    page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>>>       pr_err("iova: %pad already mapped to %pa cannot remap to phys: 
>>> %pa prot: %#x\n",
>>>              &iova, &page_phys, &paddr, prot);
>>>   @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain 
>>> *domain, unsigned long _iova,
>>>       dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>>>       pte_index = rk_iova_pte_index(iova);
>>>       pte_addr = &page_table[pte_index];
>>> -    pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
>>> +
>>> +    pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>>>       ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>>>                   paddr, size, prot);
>>>   @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct 
>>> iommu_domain *domain, unsigned long _iova,
>>>           return 0;
>>>       }
>>>   -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>>>       pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>>>       unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, 
>>> size);
>>> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct 
>>> iommu_domain *domain)
>>>       for (i = 0; i < NUM_DT_ENTRIES; i++) {
>>>           u32 dte = rk_domain->dt[i];
>>>           if (rk_dte_is_pt_valid(dte)) {
>>> -            phys_addr_t pt_phys = rk_dte_pt_address(dte);
>>> +            phys_addr_t pt_phys = rk_ops->pt_address(dte);
>>>               u32 *page_table = phys_to_virt(pt_phys);
>>>               dma_unmap_single(dma_dev, pt_phys,
>>>                        SPAGE_SIZE, DMA_TO_DEVICE);
>>> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct 
>>> platform_device *pdev)
>>>       iommu->dev = dev;
>>>       iommu->num_mmu = 0;
>>>   +    if (!rk_ops)
>>> +        rk_ops = of_device_get_match_data(dev);
>>> +
>>> +    /*
>>> +     * That should not happen unless different versions of the
>>> +     * hardware block are embedded the same SoC
>>> +     */
>>> +    WARN_ON(rk_ops != of_device_get_match_data(dev));
>>
>> Nit: calling of_device_get_match_data() twice seems rather untidy - 
>> how about something like:
>>
>>     ops = of_device_get_match_data(dev);
>>     if (!rk_ops)
>>         rk_ops = ops;
>>     else if (WARN_ON(rk_ops != ops))
>>         return -EINVAL;
>>
>> Either way I think it would be good to treat unexpected inconsistentcy 
>> as an actual error, rather than second-guessing the DT and carrying on 
>> under the assumption the device is something other than it claimed to be.
>>
>>> +
>>>       iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>>>                       GFP_KERNEL);
>>>       if (!iommu->bases)
>>> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops 
>>> rk_iommu_pm_ops = {
>>>                   pm_runtime_force_resume)
>>>   };
>>>   +static struct rk_iommu_ops iommu_data_ops_v1 = {
>>> +    .pt_address = &rk_dte_pt_address,
>>> +    .mk_dtentries = &rk_mk_dte,
>>> +    .mk_ptentries = &rk_mk_pte,
>>> +    .dte_addr_phys = &rk_dte_addr_phys,
>>> +    .pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>>> +};
>>> +
>>>   static const struct of_device_id rk_iommu_dt_ids[] = {
>>> -    { .compatible = "rockchip,iommu" },
>>> +    {    .compatible = "rockchip,iommu",
>>> +        .data = &iommu_data_ops_v1,
>>> +    },
>>>       { /* sentinel */ }
>>>   };
>>> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
>>
>> As before, unrelated and unnecessary since this driver is still bool 
>> in the Kconfig. If you do want to support modular builds you'll also 
>> need to ensure rk_iommu_ops.owner is set, but do it all as a separate 
>> patch please.
>>
>> Thanks,
>> Robin.
>>
>>>     static struct platform_driver rk_iommu_driver = {
>>>       .probe = rk_iommu_probe,
>>>
>>

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21 14:44         ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 14:44 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: devicetree, linux-kernel, linux-rockchip, iommu, kernel,
	linux-arm-kernel

On 2021-05-21 14:38, Benjamin Gaignard wrote:
> 
> Le 21/05/2021 à 14:58, Robin Murphy a écrit :
>> On 2021-05-21 09:36, Benjamin Gaignard wrote:
>>> Add internal ops to be able to handle incoming variant v2.
>>> The goal is to keep the overall structure of the framework but
>>> to allow to add the evolution of this hardware block.
>>>
>>> The ops are global for a SoC because iommu domains are not
>>> attached to a specific devices if they are for a virtuel device like
>>> drm. Use a global variable shouldn't be since SoC usually doesn't
>>> embedded different versions of the iommu hardware block.
>>> If that happen one day a WARN_ON will be displayed at probe time.
>>
>> IMO it would be a grievous error if such a "virtual device" ever gets 
>> near the IOMMU API, so personally I wouldn't use that as a 
>> justification for anything :)
>>
>> FWIW you should be OK to handle things on a per-instance basis, it 
>> just means you have to defer some of the domain setup to .attach_dev 
>> time, like various other drivers do. That said, there's nothing wrong 
>> with the global if we do expect instances to be consistent across any 
>> given Rockchip SoC (and my gut feeling is that we probably should).
> 
> I have tried that solution first but drm device appear to but such 
> "virtual device" so I had to use the global.

Hmm, the "rockchip,display-subsystem" node is not associated with an 
IOMMU, and shouldn't even be passed to the DMA API either, because it's 
not a real piece of DMA capable hardware. Whatever the DRM stack is 
doing above, it should only be the actual VOP devices that we see down 
here. If not, that's indicative of something being wrong elsewhere. Like 
I say though, I think it's fine to use global ops simply on the 
expectation that that's how the new SOCs are going to be.

In fact this reminds me, I think I started writing a patch somewhere to 
clean up the virtual device mess for rockchip-drm (IIRC I could see no 
reason why we can't just allocate the DRM device from the VOP driver, 
similar to what exynos-drm does). Maybe I should dig that up again...

> I send a v6 to fix your others remarks.

I guess I'll wait for v7 now then, since I got sidetracked before 
sending my review of patch #4 (heck, I've just spent the last half hour 
doing something else in the middle of writing this!) ;)

Cheers,
Robin.

> 
> Thanks for your advice.
> 
> Benjamin
> 
>>
>>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
>>> ---
>>> version 5:
>>>   - Use of_device_get_match_data()
>>>   - Add internal ops inside the driver
>>>
>>>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>>>   1 file changed, 50 insertions(+), 19 deletions(-)
>>>
>>> diff --git a/drivers/iommu/rockchip-iommu.c 
>>> b/drivers/iommu/rockchip-iommu.c
>>> index 7a2932772fdf..e7b9bcf174b1 100644
>>> --- a/drivers/iommu/rockchip-iommu.c
>>> +++ b/drivers/iommu/rockchip-iommu.c
>>> @@ -19,6 +19,7 @@
>>>   #include <linux/iopoll.h>
>>>   #include <linux/list.h>
>>>   #include <linux/mm.h>
>>> +#include <linux/module.h>
>>
>> This seems to be an unrelated and unnecessary change.
>>
>>>   #include <linux/init.h>
>>>   #include <linux/of.h>
>>>   #include <linux/of_iommu.h>
>>> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>>>       "aclk", "iface",
>>>   };
>>>   +struct rk_iommu_ops {
>>> +    phys_addr_t (*pt_address)(u32 dte);
>>> +    u32 (*mk_dtentries)(dma_addr_t pt_dma);
>>> +    u32 (*mk_ptentries)(phys_addr_t page, int prot);
>>> +    phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
>>> +    u32 pt_address_mask;
>>> +};
>>> +
>>>   struct rk_iommu {
>>>       struct device *dev;
>>>       void __iomem **bases;
>>> @@ -116,6 +125,7 @@ struct rk_iommudata {
>>>   };
>>>     static struct device *dma_dev;
>>> +static const struct rk_iommu_ops *rk_ops;
>>>     static inline void rk_table_flush(struct rk_iommu_domain *dom, 
>>> dma_addr_t dma,
>>>                     unsigned int count)
>>> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>>>   #define RK_PTE_PAGE_READABLE      BIT(1)
>>>   #define RK_PTE_PAGE_VALID         BIT(0)
>>>   -static inline phys_addr_t rk_pte_page_address(u32 pte)
>>> -{
>>> -    return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
>>> -}
>>> -
>>>   static inline bool rk_pte_is_page_valid(u32 pte)
>>>   {
>>>       return pte & RK_PTE_PAGE_VALID;
>>> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu 
>>> *iommu)
>>>           rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 
>>> DTE_ADDR_DUMMY);
>>>             dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
>>> -        if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
>>> +        if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
>>
>> Nit: might it make more sense to do something like:
>>
>>         dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
>>         rk_iommu_write(... dte_addr)
>>         if (rk_iommu_read(...) != dte_addr)
>>
>> so that you don't need to bother defining ->pt_address_mask for just 
>> this one sanity-check?
>>
>>>               dev_err(iommu->dev, "Error during raw reset. 
>>> MMU_DTE_ADDR is not functioning\n");
>>>               return -EFAULT;
>>>           }
>>> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu 
>>> *iommu)
>>>       return 0;
>>>   }
>>>   +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>>
>> The argument type here should be u32, since it's a DTE, not a physical 
>> address...
>>
>>> +{
>>> +    return addr;
>>> +}
>>> +
>>>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t 
>>> iova)
>>>   {
>>>       void __iomem *base = iommu->bases[index];
>>> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int 
>>> index, dma_addr_t iova)
>>>       page_offset = rk_iova_page_offset(iova);
>>>         mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
>>> -    mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
>>> +    mmu_dte_addr_phys = 
>>> rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
>>
>> ...and the cast here should not be here, since it *is* the conversion 
>> that the called function is supposed to be performing.
>>
>>>       dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>>>       dte_addr = phys_to_virt(dte_addr_phys);
>>> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, 
>>> int index, dma_addr_t iova)
>>>       if (!rk_dte_is_pt_valid(dte))
>>>           goto print_it;
>>>   -    pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
>>> +    pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>>>       pte_addr = phys_to_virt(pte_addr_phys);
>>>       pte = *pte_addr;
>>>         if (!rk_pte_is_page_valid(pte))
>>>           goto print_it;
>>>   -    page_addr_phys = rk_pte_page_address(pte) + page_offset;
>>> +    page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>>>       page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>>>     print_it:
>>> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct 
>>> iommu_domain *domain,
>>>       if (!rk_dte_is_pt_valid(dte))
>>>           goto out;
>>>   -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       page_table = (u32 *)phys_to_virt(pt_phys);
>>>       pte = page_table[rk_iova_pte_index(iova)];
>>>       if (!rk_pte_is_page_valid(pte))
>>>           goto out;
>>>   -    phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
>>> +    phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>>>   out:
>>>       spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>>>   @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct 
>>> rk_iommu_domain *rk_domain,
>>>           return ERR_PTR(-ENOMEM);
>>>       }
>>>   -    dte = rk_mk_dte(pt_dma);
>>> +    dte = rk_ops->mk_dtentries(pt_dma);
>>>       *dte_addr = dte;
>>>         rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>>>       rk_table_flush(rk_domain,
>>>                  rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>>>   done:
>>> -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       return (u32 *)phys_to_virt(pt_phys);
>>>   }
>>>   @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct 
>>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>>           if (rk_pte_is_page_valid(pte))
>>>               goto unwind;
>>>   -        pte_addr[pte_count] = rk_mk_pte(paddr, prot);
>>> +        pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>>>             paddr += SPAGE_SIZE;
>>>       }
>>> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct 
>>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>>                   pte_count * SPAGE_SIZE);
>>>         iova += pte_count * SPAGE_SIZE;
>>> -    page_phys = rk_pte_page_address(pte_addr[pte_count]);
>>> +    page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>>>       pr_err("iova: %pad already mapped to %pa cannot remap to phys: 
>>> %pa prot: %#x\n",
>>>              &iova, &page_phys, &paddr, prot);
>>>   @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain 
>>> *domain, unsigned long _iova,
>>>       dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>>>       pte_index = rk_iova_pte_index(iova);
>>>       pte_addr = &page_table[pte_index];
>>> -    pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
>>> +
>>> +    pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>>>       ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>>>                   paddr, size, prot);
>>>   @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct 
>>> iommu_domain *domain, unsigned long _iova,
>>>           return 0;
>>>       }
>>>   -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>>>       pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>>>       unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, 
>>> size);
>>> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct 
>>> iommu_domain *domain)
>>>       for (i = 0; i < NUM_DT_ENTRIES; i++) {
>>>           u32 dte = rk_domain->dt[i];
>>>           if (rk_dte_is_pt_valid(dte)) {
>>> -            phys_addr_t pt_phys = rk_dte_pt_address(dte);
>>> +            phys_addr_t pt_phys = rk_ops->pt_address(dte);
>>>               u32 *page_table = phys_to_virt(pt_phys);
>>>               dma_unmap_single(dma_dev, pt_phys,
>>>                        SPAGE_SIZE, DMA_TO_DEVICE);
>>> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct 
>>> platform_device *pdev)
>>>       iommu->dev = dev;
>>>       iommu->num_mmu = 0;
>>>   +    if (!rk_ops)
>>> +        rk_ops = of_device_get_match_data(dev);
>>> +
>>> +    /*
>>> +     * That should not happen unless different versions of the
>>> +     * hardware block are embedded the same SoC
>>> +     */
>>> +    WARN_ON(rk_ops != of_device_get_match_data(dev));
>>
>> Nit: calling of_device_get_match_data() twice seems rather untidy - 
>> how about something like:
>>
>>     ops = of_device_get_match_data(dev);
>>     if (!rk_ops)
>>         rk_ops = ops;
>>     else if (WARN_ON(rk_ops != ops))
>>         return -EINVAL;
>>
>> Either way I think it would be good to treat unexpected inconsistentcy 
>> as an actual error, rather than second-guessing the DT and carrying on 
>> under the assumption the device is something other than it claimed to be.
>>
>>> +
>>>       iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>>>                       GFP_KERNEL);
>>>       if (!iommu->bases)
>>> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops 
>>> rk_iommu_pm_ops = {
>>>                   pm_runtime_force_resume)
>>>   };
>>>   +static struct rk_iommu_ops iommu_data_ops_v1 = {
>>> +    .pt_address = &rk_dte_pt_address,
>>> +    .mk_dtentries = &rk_mk_dte,
>>> +    .mk_ptentries = &rk_mk_pte,
>>> +    .dte_addr_phys = &rk_dte_addr_phys,
>>> +    .pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>>> +};
>>> +
>>>   static const struct of_device_id rk_iommu_dt_ids[] = {
>>> -    { .compatible = "rockchip,iommu" },
>>> +    {    .compatible = "rockchip,iommu",
>>> +        .data = &iommu_data_ops_v1,
>>> +    },
>>>       { /* sentinel */ }
>>>   };
>>> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
>>
>> As before, unrelated and unnecessary since this driver is still bool 
>> in the Kconfig. If you do want to support modular builds you'll also 
>> need to ensure rk_iommu_ops.owner is set, but do it all as a separate 
>> patch please.
>>
>> Thanks,
>> Robin.
>>
>>>     static struct platform_driver rk_iommu_driver = {
>>>       .probe = rk_iommu_probe,
>>>
>>
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants
@ 2021-05-21 14:44         ` Robin Murphy
  0 siblings, 0 replies; 56+ messages in thread
From: Robin Murphy @ 2021-05-21 14:44 UTC (permalink / raw)
  To: Benjamin Gaignard, joro, will, robh+dt, heiko, xxm
  Cc: iommu, devicetree, linux-arm-kernel, linux-rockchip,
	linux-kernel, kernel

On 2021-05-21 14:38, Benjamin Gaignard wrote:
> 
> Le 21/05/2021 à 14:58, Robin Murphy a écrit :
>> On 2021-05-21 09:36, Benjamin Gaignard wrote:
>>> Add internal ops to be able to handle incoming variant v2.
>>> The goal is to keep the overall structure of the framework but
>>> to allow to add the evolution of this hardware block.
>>>
>>> The ops are global for a SoC because iommu domains are not
>>> attached to a specific devices if they are for a virtuel device like
>>> drm. Use a global variable shouldn't be since SoC usually doesn't
>>> embedded different versions of the iommu hardware block.
>>> If that happen one day a WARN_ON will be displayed at probe time.
>>
>> IMO it would be a grievous error if such a "virtual device" ever gets 
>> near the IOMMU API, so personally I wouldn't use that as a 
>> justification for anything :)
>>
>> FWIW you should be OK to handle things on a per-instance basis, it 
>> just means you have to defer some of the domain setup to .attach_dev 
>> time, like various other drivers do. That said, there's nothing wrong 
>> with the global if we do expect instances to be consistent across any 
>> given Rockchip SoC (and my gut feeling is that we probably should).
> 
> I have tried that solution first but drm device appear to but such 
> "virtual device" so I had to use the global.

Hmm, the "rockchip,display-subsystem" node is not associated with an 
IOMMU, and shouldn't even be passed to the DMA API either, because it's 
not a real piece of DMA capable hardware. Whatever the DRM stack is 
doing above, it should only be the actual VOP devices that we see down 
here. If not, that's indicative of something being wrong elsewhere. Like 
I say though, I think it's fine to use global ops simply on the 
expectation that that's how the new SOCs are going to be.

In fact this reminds me, I think I started writing a patch somewhere to 
clean up the virtual device mess for rockchip-drm (IIRC I could see no 
reason why we can't just allocate the DRM device from the VOP driver, 
similar to what exynos-drm does). Maybe I should dig that up again...

> I send a v6 to fix your others remarks.

I guess I'll wait for v7 now then, since I got sidetracked before 
sending my review of patch #4 (heck, I've just spent the last half hour 
doing something else in the middle of writing this!) ;)

Cheers,
Robin.

> 
> Thanks for your advice.
> 
> Benjamin
> 
>>
>>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
>>> ---
>>> version 5:
>>>   - Use of_device_get_match_data()
>>>   - Add internal ops inside the driver
>>>
>>>   drivers/iommu/rockchip-iommu.c | 69 ++++++++++++++++++++++++----------
>>>   1 file changed, 50 insertions(+), 19 deletions(-)
>>>
>>> diff --git a/drivers/iommu/rockchip-iommu.c 
>>> b/drivers/iommu/rockchip-iommu.c
>>> index 7a2932772fdf..e7b9bcf174b1 100644
>>> --- a/drivers/iommu/rockchip-iommu.c
>>> +++ b/drivers/iommu/rockchip-iommu.c
>>> @@ -19,6 +19,7 @@
>>>   #include <linux/iopoll.h>
>>>   #include <linux/list.h>
>>>   #include <linux/mm.h>
>>> +#include <linux/module.h>
>>
>> This seems to be an unrelated and unnecessary change.
>>
>>>   #include <linux/init.h>
>>>   #include <linux/of.h>
>>>   #include <linux/of_iommu.h>
>>> @@ -96,6 +97,14 @@ static const char * const rk_iommu_clocks[] = {
>>>       "aclk", "iface",
>>>   };
>>>   +struct rk_iommu_ops {
>>> +    phys_addr_t (*pt_address)(u32 dte);
>>> +    u32 (*mk_dtentries)(dma_addr_t pt_dma);
>>> +    u32 (*mk_ptentries)(phys_addr_t page, int prot);
>>> +    phys_addr_t (*dte_addr_phys)(phys_addr_t addr);
>>> +    u32 pt_address_mask;
>>> +};
>>> +
>>>   struct rk_iommu {
>>>       struct device *dev;
>>>       void __iomem **bases;
>>> @@ -116,6 +125,7 @@ struct rk_iommudata {
>>>   };
>>>     static struct device *dma_dev;
>>> +static const struct rk_iommu_ops *rk_ops;
>>>     static inline void rk_table_flush(struct rk_iommu_domain *dom, 
>>> dma_addr_t dma,
>>>                     unsigned int count)
>>> @@ -215,11 +225,6 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
>>>   #define RK_PTE_PAGE_READABLE      BIT(1)
>>>   #define RK_PTE_PAGE_VALID         BIT(0)
>>>   -static inline phys_addr_t rk_pte_page_address(u32 pte)
>>> -{
>>> -    return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
>>> -}
>>> -
>>>   static inline bool rk_pte_is_page_valid(u32 pte)
>>>   {
>>>       return pte & RK_PTE_PAGE_VALID;
>>> @@ -451,7 +456,7 @@ static int rk_iommu_force_reset(struct rk_iommu 
>>> *iommu)
>>>           rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 
>>> DTE_ADDR_DUMMY);
>>>             dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
>>> -        if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
>>> +        if (dte_addr != (DTE_ADDR_DUMMY & rk_ops->pt_address_mask)) {
>>
>> Nit: might it make more sense to do something like:
>>
>>         dte_addr = rk_ops->pt_address(... DTE_ADDR_DUMMY);
>>         rk_iommu_write(... dte_addr)
>>         if (rk_iommu_read(...) != dte_addr)
>>
>> so that you don't need to bother defining ->pt_address_mask for just 
>> this one sanity-check?
>>
>>>               dev_err(iommu->dev, "Error during raw reset. 
>>> MMU_DTE_ADDR is not functioning\n");
>>>               return -EFAULT;
>>>           }
>>> @@ -470,6 +475,11 @@ static int rk_iommu_force_reset(struct rk_iommu 
>>> *iommu)
>>>       return 0;
>>>   }
>>>   +static inline phys_addr_t rk_dte_addr_phys(phys_addr_t addr)
>>
>> The argument type here should be u32, since it's a DTE, not a physical 
>> address...
>>
>>> +{
>>> +    return addr;
>>> +}
>>> +
>>>   static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t 
>>> iova)
>>>   {
>>>       void __iomem *base = iommu->bases[index];
>>> @@ -489,7 +499,7 @@ static void log_iova(struct rk_iommu *iommu, int 
>>> index, dma_addr_t iova)
>>>       page_offset = rk_iova_page_offset(iova);
>>>         mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
>>> -    mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
>>> +    mmu_dte_addr_phys = 
>>> rk_ops->dte_addr_phys((phys_addr_t)mmu_dte_addr);
>>
>> ...and the cast here should not be here, since it *is* the conversion 
>> that the called function is supposed to be performing.
>>
>>>       dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>>>       dte_addr = phys_to_virt(dte_addr_phys);
>>> @@ -498,14 +508,14 @@ static void log_iova(struct rk_iommu *iommu, 
>>> int index, dma_addr_t iova)
>>>       if (!rk_dte_is_pt_valid(dte))
>>>           goto print_it;
>>>   -    pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
>>> +    pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>>>       pte_addr = phys_to_virt(pte_addr_phys);
>>>       pte = *pte_addr;
>>>         if (!rk_pte_is_page_valid(pte))
>>>           goto print_it;
>>>   -    page_addr_phys = rk_pte_page_address(pte) + page_offset;
>>> +    page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>>>       page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>>>     print_it:
>>> @@ -601,13 +611,13 @@ static phys_addr_t rk_iommu_iova_to_phys(struct 
>>> iommu_domain *domain,
>>>       if (!rk_dte_is_pt_valid(dte))
>>>           goto out;
>>>   -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       page_table = (u32 *)phys_to_virt(pt_phys);
>>>       pte = page_table[rk_iova_pte_index(iova)];
>>>       if (!rk_pte_is_page_valid(pte))
>>>           goto out;
>>>   -    phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
>>> +    phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>>>   out:
>>>       spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>>>   @@ -679,14 +689,14 @@ static u32 *rk_dte_get_page_table(struct 
>>> rk_iommu_domain *rk_domain,
>>>           return ERR_PTR(-ENOMEM);
>>>       }
>>>   -    dte = rk_mk_dte(pt_dma);
>>> +    dte = rk_ops->mk_dtentries(pt_dma);
>>>       *dte_addr = dte;
>>>         rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
>>>       rk_table_flush(rk_domain,
>>>                  rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>>>   done:
>>> -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       return (u32 *)phys_to_virt(pt_phys);
>>>   }
>>>   @@ -728,7 +738,7 @@ static int rk_iommu_map_iova(struct 
>>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>>           if (rk_pte_is_page_valid(pte))
>>>               goto unwind;
>>>   -        pte_addr[pte_count] = rk_mk_pte(paddr, prot);
>>> +        pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>>>             paddr += SPAGE_SIZE;
>>>       }
>>> @@ -750,7 +760,7 @@ static int rk_iommu_map_iova(struct 
>>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>>                   pte_count * SPAGE_SIZE);
>>>         iova += pte_count * SPAGE_SIZE;
>>> -    page_phys = rk_pte_page_address(pte_addr[pte_count]);
>>> +    page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>>>       pr_err("iova: %pad already mapped to %pa cannot remap to phys: 
>>> %pa prot: %#x\n",
>>>              &iova, &page_phys, &paddr, prot);
>>>   @@ -785,7 +795,8 @@ static int rk_iommu_map(struct iommu_domain 
>>> *domain, unsigned long _iova,
>>>       dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
>>>       pte_index = rk_iova_pte_index(iova);
>>>       pte_addr = &page_table[pte_index];
>>> -    pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
>>> +
>>> +    pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
>>>       ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>>>                   paddr, size, prot);
>>>   @@ -821,7 +832,7 @@ static size_t rk_iommu_unmap(struct 
>>> iommu_domain *domain, unsigned long _iova,
>>>           return 0;
>>>       }
>>>   -    pt_phys = rk_dte_pt_address(dte);
>>> +    pt_phys = rk_ops->pt_address(dte);
>>>       pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
>>>       pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>>>       unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, 
>>> size);
>>> @@ -1037,7 +1048,7 @@ static void rk_iommu_domain_free(struct 
>>> iommu_domain *domain)
>>>       for (i = 0; i < NUM_DT_ENTRIES; i++) {
>>>           u32 dte = rk_domain->dt[i];
>>>           if (rk_dte_is_pt_valid(dte)) {
>>> -            phys_addr_t pt_phys = rk_dte_pt_address(dte);
>>> +            phys_addr_t pt_phys = rk_ops->pt_address(dte);
>>>               u32 *page_table = phys_to_virt(pt_phys);
>>>               dma_unmap_single(dma_dev, pt_phys,
>>>                        SPAGE_SIZE, DMA_TO_DEVICE);
>>> @@ -1138,6 +1149,15 @@ static int rk_iommu_probe(struct 
>>> platform_device *pdev)
>>>       iommu->dev = dev;
>>>       iommu->num_mmu = 0;
>>>   +    if (!rk_ops)
>>> +        rk_ops = of_device_get_match_data(dev);
>>> +
>>> +    /*
>>> +     * That should not happen unless different versions of the
>>> +     * hardware block are embedded the same SoC
>>> +     */
>>> +    WARN_ON(rk_ops != of_device_get_match_data(dev));
>>
>> Nit: calling of_device_get_match_data() twice seems rather untidy - 
>> how about something like:
>>
>>     ops = of_device_get_match_data(dev);
>>     if (!rk_ops)
>>         rk_ops = ops;
>>     else if (WARN_ON(rk_ops != ops))
>>         return -EINVAL;
>>
>> Either way I think it would be good to treat unexpected inconsistentcy 
>> as an actual error, rather than second-guessing the DT and carrying on 
>> under the assumption the device is something other than it claimed to be.
>>
>>> +
>>>       iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
>>>                       GFP_KERNEL);
>>>       if (!iommu->bases)
>>> @@ -1277,10 +1297,21 @@ static const struct dev_pm_ops 
>>> rk_iommu_pm_ops = {
>>>                   pm_runtime_force_resume)
>>>   };
>>>   +static struct rk_iommu_ops iommu_data_ops_v1 = {
>>> +    .pt_address = &rk_dte_pt_address,
>>> +    .mk_dtentries = &rk_mk_dte,
>>> +    .mk_ptentries = &rk_mk_pte,
>>> +    .dte_addr_phys = &rk_dte_addr_phys,
>>> +    .pt_address_mask = RK_DTE_PT_ADDRESS_MASK,
>>> +};
>>> +
>>>   static const struct of_device_id rk_iommu_dt_ids[] = {
>>> -    { .compatible = "rockchip,iommu" },
>>> +    {    .compatible = "rockchip,iommu",
>>> +        .data = &iommu_data_ops_v1,
>>> +    },
>>>       { /* sentinel */ }
>>>   };
>>> +MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
>>
>> As before, unrelated and unnecessary since this driver is still bool 
>> in the Kconfig. If you do want to support modular builds you'll also 
>> need to ensure rk_iommu_ops.owner is set, but do it all as a separate 
>> patch please.
>>
>> Thanks,
>> Robin.
>>
>>>     static struct platform_driver rk_iommu_driver = {
>>>       .probe = rk_iommu_probe,
>>>
>>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 56+ messages in thread

end of thread, other threads:[~2021-05-21 14:46 UTC | newest]

Thread overview: 56+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-21  8:36 [PATCH v5 0/4] Add IOMMU driver for rk356x Benjamin Gaignard
2021-05-21  8:36 ` Benjamin Gaignard
2021-05-21  8:36 ` Benjamin Gaignard
2021-05-21  8:36 ` Benjamin Gaignard
2021-05-21  8:36 ` [PATCH v5 1/4] dt-bindings: iommu: rockchip: Convert IOMMU to DT schema Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  9:18   ` Heiko Stübner
2021-05-21  9:18     ` Heiko Stübner
2021-05-21  9:18     ` Heiko Stübner
2021-05-21  9:18     ` Heiko Stübner
2021-05-21  8:36 ` [PATCH v5 2/4] dt-bindings: iommu: rockchip: Add compatible for v2 Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  9:18   ` Heiko Stübner
2021-05-21  9:18     ` Heiko Stübner
2021-05-21  9:18     ` Heiko Stübner
2021-05-21  9:18     ` Heiko Stübner
2021-05-21 11:52   ` Robin Murphy
2021-05-21 11:52     ` Robin Murphy
2021-05-21 11:52     ` Robin Murphy
2021-05-21 11:52     ` Robin Murphy
2021-05-21  8:36 ` [PATCH v5 3/4] iommu: rockchip: Add internal ops to handle variants Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  9:16   ` Heiko Stübner
2021-05-21  9:16     ` Heiko Stübner
2021-05-21  9:16     ` Heiko Stübner
2021-05-21  9:16     ` Heiko Stübner
2021-05-21 12:58   ` Robin Murphy
2021-05-21 12:58     ` Robin Murphy
2021-05-21 12:58     ` Robin Murphy
2021-05-21 12:58     ` Robin Murphy
2021-05-21 13:38     ` Benjamin Gaignard
2021-05-21 13:38       ` Benjamin Gaignard
2021-05-21 13:38       ` Benjamin Gaignard
2021-05-21 13:38       ` Benjamin Gaignard
2021-05-21 14:44       ` Robin Murphy
2021-05-21 14:44         ` Robin Murphy
2021-05-21 14:44         ` Robin Murphy
2021-05-21 14:44         ` Robin Murphy
2021-05-21  8:36 ` [PATCH v5 4/4] iommu: rockchip: Add support for iommu v2 Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  8:36   ` Benjamin Gaignard
2021-05-21  9:17   ` Heiko Stübner
2021-05-21  9:17     ` Heiko Stübner
2021-05-21  9:17     ` Heiko Stübner
2021-05-21  9:17     ` Heiko Stübner
2021-05-21 13:42   ` Robin Murphy
2021-05-21 13:42     ` Robin Murphy
2021-05-21 13:42     ` Robin Murphy
2021-05-21 13:42     ` Robin Murphy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.