devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code
@ 2025-06-23 12:56 Conor Dooley
  2025-06-23 12:56 ` [PATCH v3 1/9] dt-bindings: mfd: syscon document the control-scb syscon on PolarFire SoC Conor Dooley
                   ` (8 more replies)
  0 siblings, 9 replies; 14+ messages in thread
From: Conor Dooley @ 2025-06-23 12:56 UTC (permalink / raw)
  To: sboyd
  Cc: conor, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

From: Conor Dooley <conor.dooley@microchip.com>

Yo,

Stephen - I would really like to know if what I have done with the
regmap clock is what you were asking for (a there's a link to that below
to remind yourself). I've been trying to get you to look at this for a
while, even just to affirm that I am on the right track!

Cheers,
Conor.

v3 changes:
- drop simple-mfd (for now) from syscon node

v2 cover letter:

Here's something that I've been mulling over for a while, since I
started to understand how devicetree stuff was "meant" to be done.
There'd been little reason to actually press forward with it, because it
is fairly disruptive. I've finally opted to do it, because a user has
come along with a hwmon driver that needs to access the same register
region as the mailbox and the author is not keen on using the aux bus,
and because I do not want the new pic64gx SoC that's based on PolarFire
SoC to use bindings etc that I know to be incorrect.

Given backwards compatibility needs to be maintained, this patch series
isn't the prettiest thing I have ever written. The reset driver needs to
retain support for the auxiliary bus, which looks a bit mess, but not
much can be done there. The mailbox and clock drivers both have to have
an "old probe" function to handle the old layout. Thankfully in the
clock driver, regmap support can be used to identically
handle both old and new devicetree formats - but using a regmap in the
mailbox driver was only really possible for the new format, so the code
there is unfortunately a bit of an if/else mess that I'm both not proud
of, nor really sure is worth "improving".

The series should be pretty splitable per subsystem, only the dts change
has some sort of dependency, but I'll not be applying that till
everything else is in Linus' tree, so that's not a big deal.

I don't really want this stuff in stable, hence a lack of cc: stable
anywhere here, since what's currently in the tree works fine for the
currently supported hardware.

AFAIK, the only other project affected here is U-Boot, which I have
already modified to support the new format.

I previously submitted this as an RFC, only to Lee and the dt list, in
order to get some feedback on the syscon/mfd bindings:
https://lore.kernel.org/all/20240815-shindig-bunny-fd42792d638a@spud/
I'm not really going to bother with a proper changelog, since that was
submitted with lots of WIP code to get answers to some questions. The
main change was "removing" some of the child nodes of the syscons.

And as a "real" series where discussion lead to me dropping use of the
amlogic clk-regmap support:
https://lore.kernel.org/linux-clk/20241002-private-unequal-33cfa6101338@spud/
As a result of that, I've implemented what I think Stephen was asking
for - but I'm not at all sure that it is..

CC: Conor Dooley <conor.dooley@microchip.com>
CC: Daire McNamara <daire.mcnamara@microchip.com>
CC: pierre-henry.moussay@microchip.com
CC: valentina.fernandezalanis@microchip.com
CC: Michael Turquette <mturquette@baylibre.com>
CC: Stephen Boyd <sboyd@kernel.org>
CC: Rob Herring <robh@kernel.org>
CC: Krzysztof Kozlowski <krzk+dt@kernel.org>
CC: Jassi Brar <jassisinghbrar@gmail.com>
CC: Lee Jones <lee@kernel.org>
CC: Paul Walmsley <paul.walmsley@sifive.com>
CC: Palmer Dabbelt <palmer@dabbelt.com>
CC: Philipp Zabel <p.zabel@pengutronix.de>
CC: linux-riscv@lists.infradead.org
CC: linux-clk@vger.kernel.org
CC: devicetree@vger.kernel.org
CC: linux-kernel@vger.kernel.org

Conor Dooley (9):
  dt-bindings: mfd: syscon document the control-scb syscon on PolarFire
    SoC
  dt-bindings: soc: microchip: document the simple-mfd syscon on
    PolarFire SoC
  soc: microchip: add mfd drivers for two syscon regions on PolarFire
    SoC
  reset: mpfs: add non-auxiliary bus probing
  dt-bindings: clk: microchip: mpfs: remove first reg region
  riscv: dts: microchip: fix mailbox description
  riscv: dts: microchip: convert clock and reset to use syscon
  clk: divider, gate: create regmap-backed copies of gate and divider
    clocks
  clk: microchip: mpfs: use regmap clock types

 .../bindings/clock/microchip,mpfs-clkcfg.yaml |  36 ++-
 .../devicetree/bindings/mfd/syscon.yaml       |   2 +
 .../microchip,mpfs-mss-top-sysreg.yaml        |  47 +++
 arch/riscv/boot/dts/microchip/mpfs.dtsi       |  34 ++-
 drivers/clk/Kconfig                           |   8 +
 drivers/clk/Makefile                          |   2 +
 drivers/clk/clk-divider-regmap.c              | 271 ++++++++++++++++++
 drivers/clk/clk-gate-regmap.c                 | 254 ++++++++++++++++
 drivers/clk/microchip/Kconfig                 |   4 +
 drivers/clk/microchip/clk-mpfs.c              | 151 ++++++----
 drivers/reset/reset-mpfs.c                    |  81 ++++--
 drivers/soc/microchip/Kconfig                 |  13 +
 drivers/soc/microchip/Makefile                |   1 +
 drivers/soc/microchip/mpfs-control-scb.c      |  45 +++
 drivers/soc/microchip/mpfs-mss-top-sysreg.c   |  48 ++++
 include/linux/clk-provider.h                  | 120 ++++++++
 16 files changed, 1023 insertions(+), 94 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml
 create mode 100644 drivers/clk/clk-divider-regmap.c
 create mode 100644 drivers/clk/clk-gate-regmap.c
 create mode 100644 drivers/soc/microchip/mpfs-control-scb.c
 create mode 100644 drivers/soc/microchip/mpfs-mss-top-sysreg.c

-- 
2.45.2


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

* [PATCH v3 1/9] dt-bindings: mfd: syscon document the control-scb syscon on PolarFire SoC
  2025-06-23 12:56 [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code Conor Dooley
@ 2025-06-23 12:56 ` Conor Dooley
  2025-06-23 12:56 ` [PATCH v3 2/9] dt-bindings: soc: microchip: document the simple-mfd " Conor Dooley
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Conor Dooley @ 2025-06-23 12:56 UTC (permalink / raw)
  To: sboyd
  Cc: conor, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel, Krzysztof Kozlowski

From: Conor Dooley <conor.dooley@microchip.com>

The "control-scb" region, contains the "tvs" temperature and voltage
sensors and the control/status registers for the system controller's
mailbox. The mailbox has a dedicated node, so there's no need for a
child node describing it, looking the syscon up by compatible is
sufficient.

Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
v2:
add the control-scb syscon here too, since it doesn't have any children.
---
 Documentation/devicetree/bindings/mfd/syscon.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml
index 27672adeb1fed..d18be50dd7127 100644
--- a/Documentation/devicetree/bindings/mfd/syscon.yaml
+++ b/Documentation/devicetree/bindings/mfd/syscon.yaml
@@ -90,6 +90,7 @@ select:
           - mediatek,mt8173-pctl-a-syscfg
           - mediatek,mt8365-syscfg
           - microchip,lan966x-cpu-syscon
+          - microchip,mpfs-control-scb
           - microchip,mpfs-sysreg-scb
           - microchip,sam9x60-sfr
           - microchip,sama7d65-ddr3phy
@@ -197,6 +198,7 @@ properties:
           - mediatek,mt8365-infracfg-nao
           - mediatek,mt8365-syscfg
           - microchip,lan966x-cpu-syscon
+          - microchip,mpfs-control-scb
           - microchip,mpfs-sysreg-scb
           - microchip,sam9x60-sfr
           - microchip,sama7d65-ddr3phy
-- 
2.45.2


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

* [PATCH v3 2/9] dt-bindings: soc: microchip: document the simple-mfd syscon on PolarFire SoC
  2025-06-23 12:56 [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code Conor Dooley
  2025-06-23 12:56 ` [PATCH v3 1/9] dt-bindings: mfd: syscon document the control-scb syscon on PolarFire SoC Conor Dooley
@ 2025-06-23 12:56 ` Conor Dooley
  2025-06-27  7:04   ` Krzysztof Kozlowski
  2025-06-23 12:56 ` [PATCH v3 3/9] soc: microchip: add mfd drivers for two syscon regions " Conor Dooley
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 14+ messages in thread
From: Conor Dooley @ 2025-06-23 12:56 UTC (permalink / raw)
  To: sboyd
  Cc: conor, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

From: Conor Dooley <conor.dooley@microchip.com>

"mss-top-sysreg" contains clocks, pinctrl, resets, an interrupt controller
and more. At this point, only the reset controller child is described as
that's all that is described by the existing bindings.
The clock controller already has a dedicated node, and will retain it as
there are other clock regions, so like the mailbox, a compatible-based
lookup of the syscon is sufficient to keep the clock driver working as
before, so no child is needed. There's also an interrupt multiplexing
service provided by this syscon, for which there is work in progress at
[1].

Link: https://lore.kernel.org/linux-gpio/20240723-uncouple-enforcer-7c48e4a4fefe@wendy/ [1]
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
v3:
- drop simple-mfd at Krzysztof's request since the child nodes do not
  yet exist.

v2:
- clean up various minor comments from Rob on mpfs-mss-top-sysreg
- remove mpfs-control-scb from this patch
---
 .../microchip,mpfs-mss-top-sysreg.yaml        | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml

diff --git a/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml
new file mode 100644
index 0000000000000..1ab691db87950
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip PolarFire SoC Microprocessor Subsystem (MSS) sysreg register region
+
+maintainers:
+  - Conor Dooley <conor.dooley@microchip.com>
+
+description:
+  An wide assortment of registers that control elements of the MSS on PolarFire
+  SoC, including pinmuxing, resets and clocks among others.
+
+properties:
+  compatible:
+    items:
+      - const: microchip,mpfs-mss-top-sysreg
+      - const: syscon
+
+  reg:
+    maxItems: 1
+
+  '#reset-cells':
+    description:
+      The AHB/AXI peripherals on the PolarFire SoC have reset support, so
+      from CLK_ENVM to CLK_CFM. The reset consumer should specify the
+      desired peripheral via the clock ID in its "resets" phandle cell.
+      See include/dt-bindings/clock/microchip,mpfs-clock.h for the full list
+      of PolarFire clock/reset IDs.
+    const: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    syscon@20002000 {
+      compatible = "microchip,mpfs-mss-top-sysreg", "syscon";
+      reg = <0x20002000 0x1000>;
+      #reset-cells = <1>;
+    };
+
-- 
2.45.2


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

* [PATCH v3 3/9] soc: microchip: add mfd drivers for two syscon regions on PolarFire SoC
  2025-06-23 12:56 [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code Conor Dooley
  2025-06-23 12:56 ` [PATCH v3 1/9] dt-bindings: mfd: syscon document the control-scb syscon on PolarFire SoC Conor Dooley
  2025-06-23 12:56 ` [PATCH v3 2/9] dt-bindings: soc: microchip: document the simple-mfd " Conor Dooley
@ 2025-06-23 12:56 ` Conor Dooley
  2025-06-23 12:56 ` [PATCH v3 4/9] reset: mpfs: add non-auxiliary bus probing Conor Dooley
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Conor Dooley @ 2025-06-23 12:56 UTC (permalink / raw)
  To: sboyd
  Cc: conor, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

From: Conor Dooley <conor.dooley@microchip.com>

The control-scb and mss-top-sysreg regions on PolarFire SoC both fulfill
multiple purposes. The former is used for mailbox functions in addition
to the temperature & voltage sensor while the latter is used for clocks,
resets, interrupt muxing and pinctrl.

Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
 drivers/soc/microchip/Kconfig               | 13 ++++++
 drivers/soc/microchip/Makefile              |  1 +
 drivers/soc/microchip/mpfs-control-scb.c    | 45 +++++++++++++++++++
 drivers/soc/microchip/mpfs-mss-top-sysreg.c | 48 +++++++++++++++++++++
 4 files changed, 107 insertions(+)
 create mode 100644 drivers/soc/microchip/mpfs-control-scb.c
 create mode 100644 drivers/soc/microchip/mpfs-mss-top-sysreg.c

diff --git a/drivers/soc/microchip/Kconfig b/drivers/soc/microchip/Kconfig
index 19f4b576f822b..31d188311e05f 100644
--- a/drivers/soc/microchip/Kconfig
+++ b/drivers/soc/microchip/Kconfig
@@ -9,3 +9,16 @@ config POLARFIRE_SOC_SYS_CTRL
 	  module will be called mpfs_system_controller.
 
 	  If unsure, say N.
+
+config POLARFIRE_SOC_SYSCONS
+	bool "PolarFire SoC (MPFS) syscon drivers"
+	default y
+	depends on ARCH_MICROCHIP
+	select MFD_CORE
+	help
+	  These drivers add support for the syscons on PolarFire SoC (MPFS).
+	  Without these drivers core parts of the kernel such as clocks
+	  and resets will not function correctly.
+
+	  If unsure, and on a PolarFire SoC, say y.
+
diff --git a/drivers/soc/microchip/Makefile b/drivers/soc/microchip/Makefile
index 14489919fe4b3..1a3a1594b089b 100644
--- a/drivers/soc/microchip/Makefile
+++ b/drivers/soc/microchip/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_POLARFIRE_SOC_SYS_CTRL)	+= mpfs-sys-controller.o
+obj-$(CONFIG_POLARFIRE_SOC_SYSCONS)	+= mpfs-control-scb.o mpfs-mss-top-sysreg.o
diff --git a/drivers/soc/microchip/mpfs-control-scb.c b/drivers/soc/microchip/mpfs-control-scb.c
new file mode 100644
index 0000000000000..d1a8e79c232e3
--- /dev/null
+++ b/drivers/soc/microchip/mpfs-control-scb.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/array_size.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+static const struct mfd_cell mpfs_control_scb_devs[] = {
+	{ .name = "mpfs-tvs", },
+};
+
+static int mpfs_control_scb_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE, mpfs_control_scb_devs,
+			      1, NULL, 0, NULL);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct of_device_id mpfs_control_scb_of_match[] = {
+	{.compatible = "microchip,mpfs-control-scb", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mpfs_control_scb_of_match);
+
+static struct platform_driver mpfs_control_scb_driver = {
+	.driver = {
+		.name = "mpfs-control-scb",
+		.of_match_table = mpfs_control_scb_of_match,
+	},
+	.probe = mpfs_control_scb_probe,
+};
+module_platform_driver(mpfs_control_scb_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_DESCRIPTION("PolarFire SoC control scb driver");
diff --git a/drivers/soc/microchip/mpfs-mss-top-sysreg.c b/drivers/soc/microchip/mpfs-mss-top-sysreg.c
new file mode 100644
index 0000000000000..9b2e7b84cdba2
--- /dev/null
+++ b/drivers/soc/microchip/mpfs-mss-top-sysreg.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/array_size.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+static const struct mfd_cell mpfs_mss_top_sysreg_devs[] = {
+	{ .name = "mpfs-reset", },
+};
+
+static int mpfs_mss_top_sysreg_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE, mpfs_mss_top_sysreg_devs,
+			      1, NULL, 0, NULL);
+	if (ret)
+		return ret;
+
+	if (devm_of_platform_populate(dev))
+		dev_err(dev, "Error populating children\n");
+
+	return 0;
+}
+
+static const struct of_device_id mpfs_mss_top_sysreg_of_match[] = {
+	{.compatible = "microchip,mpfs-mss-top-sysreg", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mpfs_mss_top_sysreg_of_match);
+
+static struct platform_driver mpfs_mss_top_sysreg_driver = {
+	.driver = {
+		.name = "mpfs-mss-top-sysreg",
+		.of_match_table = mpfs_mss_top_sysreg_of_match,
+	},
+	.probe = mpfs_mss_top_sysreg_probe,
+};
+module_platform_driver(mpfs_mss_top_sysreg_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_DESCRIPTION("PolarFire SoC mss top sysreg driver");
-- 
2.45.2


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

* [PATCH v3 4/9] reset: mpfs: add non-auxiliary bus probing
  2025-06-23 12:56 [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code Conor Dooley
                   ` (2 preceding siblings ...)
  2025-06-23 12:56 ` [PATCH v3 3/9] soc: microchip: add mfd drivers for two syscon regions " Conor Dooley
@ 2025-06-23 12:56 ` Conor Dooley
  2025-06-27 16:01   ` Philipp Zabel
  2025-06-23 12:56 ` [PATCH v3 5/9] dt-bindings: clk: microchip: mpfs: remove first reg region Conor Dooley
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 14+ messages in thread
From: Conor Dooley @ 2025-06-23 12:56 UTC (permalink / raw)
  To: sboyd
  Cc: conor, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

From: Conor Dooley <conor.dooley@microchip.com>

While the auxiliary bus was a nice bandaid, and meant that re-writing
the representation of the clock regions in devicetree was not required,
it has run its course. The "mss_top_sysreg" region that contains the
clock and reset regions, also contains pinctrl and an interrupt
controller, so the time has come rewrite the devicetree and probe the
reset controller from an mfd devicetree node, rather than implement
those drivers using the auxiliary bus. Wanting to avoid propagating this
naive/incorrect description of the hardware to the new pic64gx SoC is a
major motivating factor here.

Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
v2:
Implement the request to use regmap_update_bits(). I found that I then
hated the read/write helpers since they were just bloat, so I ripped
them out. I replaced the regular spin_lock_irqsave() stuff with a
guard(spinlock_irqsave), since that's a simpler way of handling the two
different paths through such a trivial pair of functions.
---
 drivers/reset/reset-mpfs.c | 81 ++++++++++++++++++++++++++++++--------
 1 file changed, 65 insertions(+), 16 deletions(-)

diff --git a/drivers/reset/reset-mpfs.c b/drivers/reset/reset-mpfs.c
index 574e59db83a4f..9c3e996f3a099 100644
--- a/drivers/reset/reset-mpfs.c
+++ b/drivers/reset/reset-mpfs.c
@@ -7,12 +7,15 @@
  *
  */
 #include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
 #include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/regmap.h>
 #include <linux/reset-controller.h>
 #include <dt-bindings/clock/microchip,mpfs-clock.h>
 #include <soc/microchip/mpfs.h>
@@ -27,11 +30,14 @@
 #define MPFS_SLEEP_MIN_US	100
 #define MPFS_SLEEP_MAX_US	200
 
+#define REG_SUBBLK_RESET_CR	0x88u
+
 /* block concurrent access to the soft reset register */
 static DEFINE_SPINLOCK(mpfs_reset_lock);
 
 struct mpfs_reset {
 	void __iomem *base;
+	struct regmap *regmap;
 	struct reset_controller_dev rcdev;
 };
 
@@ -46,41 +52,50 @@ static inline struct mpfs_reset *to_mpfs_reset(struct reset_controller_dev *rcde
 static int mpfs_assert(struct reset_controller_dev *rcdev, unsigned long id)
 {
 	struct mpfs_reset *rst = to_mpfs_reset(rcdev);
-	unsigned long flags;
 	u32 reg;
 
-	spin_lock_irqsave(&mpfs_reset_lock, flags);
+	guard(spinlock_irqsave)(&mpfs_reset_lock);
+
+	if (rst->regmap) {
+		regmap_update_bits(rst->regmap, REG_SUBBLK_RESET_CR, BIT(id), BIT(id));
+		return 0;
+	}
 
 	reg = readl(rst->base);
 	reg |= BIT(id);
 	writel(reg, rst->base);
 
-	spin_unlock_irqrestore(&mpfs_reset_lock, flags);
-
 	return 0;
 }
 
 static int mpfs_deassert(struct reset_controller_dev *rcdev, unsigned long id)
 {
 	struct mpfs_reset *rst = to_mpfs_reset(rcdev);
-	unsigned long flags;
 	u32 reg;
 
-	spin_lock_irqsave(&mpfs_reset_lock, flags);
+	guard(spinlock_irqsave)(&mpfs_reset_lock);
+
+	if (rst->regmap) {
+		regmap_update_bits(rst->regmap, REG_SUBBLK_RESET_CR, BIT(id), 0);
+		return 0;
+	}
 
 	reg = readl(rst->base);
 	reg &= ~BIT(id);
 	writel(reg, rst->base);
 
-	spin_unlock_irqrestore(&mpfs_reset_lock, flags);
-
 	return 0;
 }
 
 static int mpfs_status(struct reset_controller_dev *rcdev, unsigned long id)
 {
 	struct mpfs_reset *rst = to_mpfs_reset(rcdev);
-	u32 reg = readl(rst->base);
+	u32 reg;
+
+	if (rst->regmap)
+		regmap_read(rst->regmap, REG_SUBBLK_RESET_CR, &reg);
+	else
+		reg = readl(rst->base);
 
 	/*
 	 * It is safe to return here as MPFS_NUM_RESETS makes sure the sign bit
@@ -130,11 +145,45 @@ static int mpfs_reset_xlate(struct reset_controller_dev *rcdev,
 	return index - MPFS_PERIPH_OFFSET;
 }
 
-static int mpfs_reset_probe(struct auxiliary_device *adev,
-			    const struct auxiliary_device_id *id)
+static int mpfs_reset_mfd_probe(struct platform_device *pdev)
 {
-	struct device *dev = &adev->dev;
 	struct reset_controller_dev *rcdev;
+	struct device *dev = &pdev->dev;
+	struct mpfs_reset *rst;
+
+	rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL);
+	if (!rst)
+		return -ENOMEM;
+
+	rcdev = &rst->rcdev;
+	rcdev->dev = dev;
+	rcdev->ops = &mpfs_reset_ops;
+
+	rcdev->of_node = pdev->dev.parent->of_node;
+	rcdev->of_reset_n_cells = 1;
+	rcdev->of_xlate = mpfs_reset_xlate;
+	rcdev->nr_resets = MPFS_NUM_RESETS;
+
+	rst->regmap = device_node_to_regmap(pdev->dev.parent->of_node);
+	if (IS_ERR(rst->regmap))
+		dev_err_probe(dev, PTR_ERR(rst->regmap), "Failed to find syscon regmap\n");
+
+	return devm_reset_controller_register(dev, rcdev);
+}
+
+static struct platform_driver mpfs_reset_mfd_driver = {
+	.probe		= mpfs_reset_mfd_probe,
+	.driver = {
+		.name = "mpfs-reset",
+	},
+};
+module_platform_driver(mpfs_reset_mfd_driver);
+
+static int mpfs_reset_adev_probe(struct auxiliary_device *adev,
+				 const struct auxiliary_device_id *id)
+{
+	struct reset_controller_dev *rcdev;
+	struct device *dev = &adev->dev;
 	struct mpfs_reset *rst;
 
 	rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL);
@@ -145,8 +194,8 @@ static int mpfs_reset_probe(struct auxiliary_device *adev,
 
 	rcdev = &rst->rcdev;
 	rcdev->dev = dev;
-	rcdev->dev->parent = dev->parent;
 	rcdev->ops = &mpfs_reset_ops;
+
 	rcdev->of_node = dev->parent->of_node;
 	rcdev->of_reset_n_cells = 1;
 	rcdev->of_xlate = mpfs_reset_xlate;
@@ -222,12 +271,12 @@ static const struct auxiliary_device_id mpfs_reset_ids[] = {
 };
 MODULE_DEVICE_TABLE(auxiliary, mpfs_reset_ids);
 
-static struct auxiliary_driver mpfs_reset_driver = {
-	.probe		= mpfs_reset_probe,
+static struct auxiliary_driver mpfs_reset_aux_driver = {
+	.probe		= mpfs_reset_adev_probe,
 	.id_table	= mpfs_reset_ids,
 };
 
-module_auxiliary_driver(mpfs_reset_driver);
+module_auxiliary_driver(mpfs_reset_aux_driver);
 
 MODULE_DESCRIPTION("Microchip PolarFire SoC Reset Driver");
 MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
-- 
2.45.2


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

* [PATCH v3 5/9] dt-bindings: clk: microchip: mpfs: remove first reg region
  2025-06-23 12:56 [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code Conor Dooley
                   ` (3 preceding siblings ...)
  2025-06-23 12:56 ` [PATCH v3 4/9] reset: mpfs: add non-auxiliary bus probing Conor Dooley
@ 2025-06-23 12:56 ` Conor Dooley
  2025-06-23 12:56 ` [PATCH v3 6/9] riscv: dts: microchip: fix mailbox description Conor Dooley
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Conor Dooley @ 2025-06-23 12:56 UTC (permalink / raw)
  To: sboyd
  Cc: conor, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

From: Conor Dooley <conor.dooley@microchip.com>

The first reg region in this binding is not exclusively for clocks, as
evidenced by the dual role of this device as a reset controller at
present. The first region is however better described by a simple-mfd
syscon, but this would have require a significant re-write of the
devicetree for the platform, so the easy way out was chosen when reset
support was first introduced. The region doesn't just contain clock and
reset registers, it also contains pinctrl and interrupt controller
functionality, so drop the region from the clock binding so that it can
be described instead by a simple-mfd syscon rather than propagate this
incorrect description of the hardware to the new pic64gx SoC.

Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
 .../bindings/clock/microchip,mpfs-clkcfg.yaml | 36 +++++++++++--------
 1 file changed, 22 insertions(+), 14 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/microchip,mpfs-clkcfg.yaml b/Documentation/devicetree/bindings/clock/microchip,mpfs-clkcfg.yaml
index e4e1c31267d2a..ee4f31596d978 100644
--- a/Documentation/devicetree/bindings/clock/microchip,mpfs-clkcfg.yaml
+++ b/Documentation/devicetree/bindings/clock/microchip,mpfs-clkcfg.yaml
@@ -22,16 +22,23 @@ properties:
     const: microchip,mpfs-clkcfg
 
   reg:
-    items:
-      - description: |
-          clock config registers:
-          These registers contain enable, reset & divider tables for the, cpu,
-          axi, ahb and rtc/mtimer reference clocks as well as enable and reset
-          for the peripheral clocks.
-      - description: |
-          mss pll dri registers:
-          Block of registers responsible for dynamic reconfiguration of the mss
-          pll
+    oneOf:
+      - items:
+          - description: |
+              clock config registers:
+              These registers contain enable, reset & divider tables for the, cpu,
+              axi, ahb and rtc/mtimer reference clocks as well as enable and reset
+              for the peripheral clocks.
+          - description: |
+              mss pll dri registers:
+              Block of registers responsible for dynamic reconfiguration of the mss
+              pll
+        deprecated: true
+      - items:
+          - description: |
+              mss pll dri registers:
+              Block of registers responsible for dynamic reconfiguration of the mss
+              pll
 
   clocks:
     maxItems: 1
@@ -69,11 +76,12 @@ examples:
   - |
     #include <dt-bindings/clock/microchip,mpfs-clock.h>
     soc {
-            #address-cells = <2>;
-            #size-cells = <2>;
-            clkcfg: clock-controller@20002000 {
+            #address-cells = <1>;
+            #size-cells = <1>;
+
+            clkcfg: clock-controller@3E001000 {
                 compatible = "microchip,mpfs-clkcfg";
-                reg = <0x0 0x20002000 0x0 0x1000>, <0x0 0x3E001000 0x0 0x1000>;
+                reg = <0x3E001000 0x1000>;
                 clocks = <&ref>;
                 #clock-cells = <1>;
         };
-- 
2.45.2


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

* [PATCH v3 6/9] riscv: dts: microchip: fix mailbox description
  2025-06-23 12:56 [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code Conor Dooley
                   ` (4 preceding siblings ...)
  2025-06-23 12:56 ` [PATCH v3 5/9] dt-bindings: clk: microchip: mpfs: remove first reg region Conor Dooley
@ 2025-06-23 12:56 ` Conor Dooley
  2025-06-23 12:56 ` [PATCH v3 7/9] riscv: dts: microchip: convert clock and reset to use syscon Conor Dooley
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Conor Dooley @ 2025-06-23 12:56 UTC (permalink / raw)
  To: sboyd
  Cc: conor, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

From: Conor Dooley <conor.dooley@microchip.com>

When the binding for the mailbox on PolarFire SoC was originally
written, and later modified, mistakes were made - and the precise
nature of the later modification should have been a giveaway, but alas
I was naive at the time.

A more correct modelling of the hardware is to use two syscons and have
a single reg entry for the mailbox, containing the mailbox region. The
two syscons contain the general control/status registers for the mailbox
and the interrupt related registers respectively. The reason for two
syscons is that the same mailbox is present on the non-SoC version of
the FPGA, which has no interrupt controller, and the shared part of the
rtl was unchanged between devices.

Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
 arch/riscv/boot/dts/microchip/mpfs.dtsi | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/arch/riscv/boot/dts/microchip/mpfs.dtsi b/arch/riscv/boot/dts/microchip/mpfs.dtsi
index 9883ca3554c50..f9d6bf08e7170 100644
--- a/arch/riscv/boot/dts/microchip/mpfs.dtsi
+++ b/arch/riscv/boot/dts/microchip/mpfs.dtsi
@@ -259,6 +259,11 @@ clkcfg: clkcfg@20002000 {
 			#reset-cells = <1>;
 		};
 
+		sysreg_scb: syscon@20003000 {
+			compatible = "microchip,mpfs-sysreg-scb", "syscon";
+			reg = <0x0 0x20003000 0x0 0x1000>;
+		};
+
 		ccc_se: clock-controller@38010000 {
 			compatible = "microchip,mpfs-ccc";
 			reg = <0x0 0x38010000 0x0 0x1000>, <0x0 0x38020000 0x0 0x1000>,
@@ -521,10 +526,14 @@ usb: usb@20201000 {
 			status = "disabled";
 		};
 
-		mbox: mailbox@37020000 {
+		control_scb: syscon@37020000 {
+			compatible = "microchip,mpfs-control-scb", "syscon";
+			reg = <0x0 0x37020000 0x0 0x100>;
+		};
+
+		mbox: mailbox@37020800 {
 			compatible = "microchip,mpfs-mailbox";
-			reg = <0x0 0x37020000 0x0 0x58>, <0x0 0x2000318C 0x0 0x40>,
-			      <0x0 0x37020800 0x0 0x100>;
+			reg = <0x0 0x37020800 0x0 0x1000>;
 			interrupt-parent = <&plic>;
 			interrupts = <96>;
 			#mbox-cells = <1>;
-- 
2.45.2


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

* [PATCH v3 7/9] riscv: dts: microchip: convert clock and reset to use syscon
  2025-06-23 12:56 [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code Conor Dooley
                   ` (5 preceding siblings ...)
  2025-06-23 12:56 ` [PATCH v3 6/9] riscv: dts: microchip: fix mailbox description Conor Dooley
@ 2025-06-23 12:56 ` Conor Dooley
  2025-06-23 12:56 ` [PATCH v3 8/9] clk: divider, gate: create regmap-backed copies of gate and divider clocks Conor Dooley
  2025-06-23 12:56 ` [PATCH v3 9/9] clk: microchip: mpfs: use regmap clock types Conor Dooley
  8 siblings, 0 replies; 14+ messages in thread
From: Conor Dooley @ 2025-06-23 12:56 UTC (permalink / raw)
  To: sboyd
  Cc: conor, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

From: Conor Dooley <conor.dooley@microchip.com>

The "subblock" clocks and reset registers on PolarFire SoC are located
in the mss-top-sysreg region, alongside pinctrl and interrupt control
functionality. Re-write the devicetree to describe the sys explicitly,
as its own node, rather than as a region of the clock node.
Correspondingly, the phandles to the reset controller must be updated to
the new provider. The drivers will continue to support the old way of
doing things.

Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
 arch/riscv/boot/dts/microchip/mpfs.dtsi | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/arch/riscv/boot/dts/microchip/mpfs.dtsi b/arch/riscv/boot/dts/microchip/mpfs.dtsi
index f9d6bf08e7170..5c2963e269b83 100644
--- a/arch/riscv/boot/dts/microchip/mpfs.dtsi
+++ b/arch/riscv/boot/dts/microchip/mpfs.dtsi
@@ -251,11 +251,9 @@ pdma: dma-controller@3000000 {
 			#dma-cells = <1>;
 		};
 
-		clkcfg: clkcfg@20002000 {
-			compatible = "microchip,mpfs-clkcfg";
-			reg = <0x0 0x20002000 0x0 0x1000>, <0x0 0x3E001000 0x0 0x1000>;
-			clocks = <&refclk>;
-			#clock-cells = <1>;
+		mss_top_sysreg: syscon@20002000 {
+			compatible = "microchip,mpfs-mss-top-sysreg", "syscon", "simple-mfd";
+			reg = <0x0 0x20002000 0x0 0x1000>;
 			#reset-cells = <1>;
 		};
 
@@ -452,7 +450,7 @@ mac0: ethernet@20110000 {
 			local-mac-address = [00 00 00 00 00 00];
 			clocks = <&clkcfg CLK_MAC0>, <&clkcfg CLK_AHB>;
 			clock-names = "pclk", "hclk";
-			resets = <&clkcfg CLK_MAC0>;
+			resets = <&mss_top_sysreg CLK_MAC0>;
 			status = "disabled";
 		};
 
@@ -466,7 +464,7 @@ mac1: ethernet@20112000 {
 			local-mac-address = [00 00 00 00 00 00];
 			clocks = <&clkcfg CLK_MAC1>, <&clkcfg CLK_AHB>;
 			clock-names = "pclk", "hclk";
-			resets = <&clkcfg CLK_MAC1>;
+			resets = <&mss_top_sysreg CLK_MAC1>;
 			status = "disabled";
 		};
 
@@ -550,5 +548,12 @@ syscontroller_qspi: spi@37020100 {
 			clocks = <&scbclk>;
 			status = "disabled";
 		};
+
+		clkcfg: clkcfg@3e001000 {
+			compatible = "microchip,mpfs-clkcfg";
+			reg = <0x0 0x3e001000 0x0 0x1000>;
+			clocks = <&refclk>;
+			#clock-cells = <1>;
+		};
 	};
 };
-- 
2.45.2


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

* [PATCH v3 8/9] clk: divider, gate: create regmap-backed copies of gate and divider clocks
  2025-06-23 12:56 [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code Conor Dooley
                   ` (6 preceding siblings ...)
  2025-06-23 12:56 ` [PATCH v3 7/9] riscv: dts: microchip: convert clock and reset to use syscon Conor Dooley
@ 2025-06-23 12:56 ` Conor Dooley
  2025-07-31 11:23   ` Gabriel FERNANDEZ
  2025-06-23 12:56 ` [PATCH v3 9/9] clk: microchip: mpfs: use regmap clock types Conor Dooley
  8 siblings, 1 reply; 14+ messages in thread
From: Conor Dooley @ 2025-06-23 12:56 UTC (permalink / raw)
  To: sboyd
  Cc: conor, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

From: Conor Dooley <conor.dooley@microchip.com>

Implement regmap-backed copies of gate and divider clocks by replacing
the iomem pointer to the clock registers with a regmap and offset
within.

Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
 drivers/clk/Kconfig              |   8 +
 drivers/clk/Makefile             |   2 +
 drivers/clk/clk-divider-regmap.c | 271 +++++++++++++++++++++++++++++++
 drivers/clk/clk-gate-regmap.c    | 254 +++++++++++++++++++++++++++++
 include/linux/clk-provider.h     | 120 ++++++++++++++
 5 files changed, 655 insertions(+)
 create mode 100644 drivers/clk/clk-divider-regmap.c
 create mode 100644 drivers/clk/clk-gate-regmap.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 19c1ed280fd7f..273154031325e 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -33,6 +33,14 @@ menuconfig COMMON_CLK
 
 if COMMON_CLK
 
+config COMMON_CLK_DIVIDER_REGMAP
+	bool
+	select REGMAP
+
+config COMMON_CLK_GATE_REGMAP
+	bool
+	select REGMAP
+
 config COMMON_CLK_WM831X
 	tristate "Clock driver for WM831x/2x PMICs"
 	depends on MFD_WM831X
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 42867cd37c33d..6dbc1fe1e03e2 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -20,11 +20,13 @@ clk-test-y			:= clk_test.o \
 				   kunit_clk_assigned_rates_zero_consumer.dtbo.o \
 				   kunit_clk_parent_data_test.dtbo.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-divider.o
+obj-$(CONFIG_COMMON_CLK_DIVIDER_REGMAP)	+= clk-divider-regmap.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-factor.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-rate.o
 obj-$(CONFIG_CLK_FIXED_RATE_KUNIT_TEST)	+= clk-fixed-rate-test.o
 clk-fixed-rate-test-y		:= clk-fixed-rate_test.o kunit_clk_fixed_rate_test.dtbo.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-gate.o
+obj-$(CONFIG_COMMON_CLK_GATE_REGMAP)	+= clk-gate-regmap.o
 obj-$(CONFIG_CLK_GATE_KUNIT_TEST) += clk-gate_test.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-multiplier.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-mux.o
diff --git a/drivers/clk/clk-divider-regmap.c b/drivers/clk/clk-divider-regmap.c
new file mode 100644
index 0000000000000..43b8f3dedb9e1
--- /dev/null
+++ b/drivers/clk/clk-divider-regmap.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+
+static inline u32 clk_div_regmap_readl(struct clk_divider_regmap *divider)
+{
+	u32 val;
+
+	regmap_read(divider->regmap, divider->map_offset, &val);
+
+	return val;
+}
+
+static inline void clk_div_regmap_writel(struct clk_divider_regmap *divider, u32 val)
+{
+	regmap_write(divider->regmap, divider->map_offset, val);
+
+}
+
+static unsigned long clk_divider_regmap_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate)
+{
+	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
+	unsigned int val;
+
+	val = clk_div_regmap_readl(divider) >> divider->shift;
+	val &= clk_div_mask(divider->width);
+
+	return divider_recalc_rate(hw, parent_rate, val, divider->table,
+				   divider->flags, divider->width);
+}
+
+static long clk_divider_regmap_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *prate)
+{
+	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
+
+	/* if read only, just return current value */
+	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
+		u32 val;
+
+		val = clk_div_regmap_readl(divider) >> divider->shift;
+		val &= clk_div_mask(divider->width);
+
+		return divider_ro_round_rate(hw, rate, prate, divider->table,
+					     divider->width, divider->flags,
+					     val);
+	}
+
+	return divider_round_rate(hw, rate, prate, divider->table,
+				  divider->width, divider->flags);
+}
+
+static int clk_divider_regmap_determine_rate(struct clk_hw *hw,
+				      struct clk_rate_request *req)
+{
+	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
+
+	/* if read only, just return current value */
+	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
+		u32 val;
+
+		val = clk_div_regmap_readl(divider) >> divider->shift;
+		val &= clk_div_mask(divider->width);
+
+		return divider_ro_determine_rate(hw, req, divider->table,
+						 divider->width,
+						 divider->flags, val);
+	}
+
+	return divider_determine_rate(hw, req, divider->table, divider->width,
+				      divider->flags);
+}
+
+static int clk_divider_regmap_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent_rate)
+{
+	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
+	int value;
+	unsigned long flags = 0;
+	u32 val;
+
+	value = divider_get_val(rate, parent_rate, divider->table,
+				divider->width, divider->flags);
+	if (value < 0)
+		return value;
+
+	if (divider->lock)
+		spin_lock_irqsave(divider->lock, flags);
+	else
+		__acquire(divider->lock);
+
+	if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
+		val = clk_div_mask(divider->width) << (divider->shift + 16);
+	} else {
+		val = clk_div_regmap_readl(divider);
+		val &= ~(clk_div_mask(divider->width) << divider->shift);
+	}
+	val |= (u32)value << divider->shift;
+	clk_div_regmap_writel(divider, val);
+
+	if (divider->lock)
+		spin_unlock_irqrestore(divider->lock, flags);
+	else
+		__release(divider->lock);
+
+	return 0;
+}
+
+const struct clk_ops clk_divider_regmap_ops = {
+	.recalc_rate = clk_divider_regmap_recalc_rate,
+	.round_rate = clk_divider_regmap_round_rate,
+	.determine_rate = clk_divider_regmap_determine_rate,
+	.set_rate = clk_divider_regmap_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_divider_regmap_ops);
+
+const struct clk_ops clk_divider_regmap_ro_ops = {
+	.recalc_rate = clk_divider_regmap_recalc_rate,
+	.round_rate = clk_divider_regmap_round_rate,
+	.determine_rate = clk_divider_regmap_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_divider_regmap_ro_ops);
+
+struct clk_hw *__clk_hw_register_divider_regmap(struct device *dev,
+		struct device_node *np, const char *name,
+		const char *parent_name, const struct clk_hw *parent_hw,
+		const struct clk_parent_data *parent_data, unsigned long flags,
+		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
+		u8 clk_divider_flags, const struct clk_div_table *table,
+		spinlock_t *lock)
+{
+	struct clk_divider_regmap *div;
+	struct clk_hw *hw;
+	struct clk_init_data init = {};
+	int ret;
+
+	if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
+		if (width + shift > 16) {
+			pr_warn("divider value exceeds LOWORD field\n");
+			return ERR_PTR(-EINVAL);
+		}
+	}
+
+	/* allocate the divider */
+	div = kzalloc(sizeof(*div), GFP_KERNEL);
+	if (!div)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
+		init.ops = &clk_divider_regmap_ro_ops;
+	else
+		init.ops = &clk_divider_regmap_ops;
+	init.flags = flags;
+	init.parent_names = parent_name ? &parent_name : NULL;
+	init.parent_hws = parent_hw ? &parent_hw : NULL;
+	init.parent_data = parent_data;
+	if (parent_name || parent_hw || parent_data)
+		init.num_parents = 1;
+	else
+		init.num_parents = 0;
+
+	/* struct clk_divider assignments */
+	div->regmap = regmap;
+	div->map_offset = map_offset;
+	div->shift = shift;
+	div->width = width;
+	div->flags = clk_divider_flags;
+	div->lock = lock;
+	div->hw.init = &init;
+	div->table = table;
+
+	/* register the clock */
+	hw = &div->hw;
+	ret = clk_hw_register(dev, hw);
+	if (ret) {
+		kfree(div);
+		hw = ERR_PTR(ret);
+	}
+
+	return hw;
+}
+EXPORT_SYMBOL_GPL(__clk_hw_register_divider_regmap);
+
+struct clk *clk_register_divider_regmap_table(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
+		u8 clk_divider_flags, const struct clk_div_table *table,
+		spinlock_t *lock)
+{
+	struct clk_hw *hw;
+
+	hw =  __clk_hw_register_divider_regmap(dev, NULL, name, parent_name, NULL,
+					       NULL, flags, regmap, map_offset,
+					       shift, width, clk_divider_flags,
+					       table, lock);
+	if (IS_ERR(hw))
+		return ERR_CAST(hw);
+	return hw->clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_divider_regmap_table);
+
+void clk_unregister_divider_regmap(struct clk *clk)
+{
+	struct clk_divider_regmap *div;
+	struct clk_hw *hw;
+
+	hw = __clk_get_hw(clk);
+	if (!hw)
+		return;
+
+	div = to_clk_divider_regmap(hw);
+
+	clk_unregister(clk);
+	kfree(div);
+}
+EXPORT_SYMBOL_GPL(clk_unregister_divider_regmap);
+
+/**
+ * clk_hw_unregister_divider_regmap - unregister a clk divider
+ * @hw: hardware-specific clock data to unregister
+ */
+void clk_hw_unregister_divider_regmap(struct clk_hw *hw)
+{
+	struct clk_divider_regmap *div;
+
+	div = to_clk_divider_regmap(hw);
+
+	clk_hw_unregister(hw);
+	kfree(div);
+}
+EXPORT_SYMBOL_GPL(clk_hw_unregister_divider_regmap);
+
+static void devm_clk_hw_release_divider_regmap(struct device *dev, void *res)
+{
+	clk_hw_unregister_divider_regmap(*(struct clk_hw **)res);
+}
+
+struct clk_hw *__devm_clk_hw_register_divider_regmap(struct device *dev,
+		struct device_node *np, const char *name,
+		const char *parent_name, const struct clk_hw *parent_hw,
+		const struct clk_parent_data *parent_data, unsigned long flags,
+		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
+		u8 clk_divider_flags, const struct clk_div_table *table,
+		spinlock_t *lock)
+{
+	struct clk_hw **ptr, *hw;
+
+	ptr = devres_alloc(devm_clk_hw_release_divider_regmap, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	hw = __clk_hw_register_divider_regmap(dev, np, name, parent_name, parent_hw,
+					      parent_data, flags, regmap, map_offset,
+					      shift, width, clk_divider_flags, table,
+					      lock);
+
+	if (!IS_ERR(hw)) {
+		*ptr = hw;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return hw;
+}
+EXPORT_SYMBOL_GPL(__devm_clk_hw_register_divider_regmap);
diff --git a/drivers/clk/clk-gate-regmap.c b/drivers/clk/clk-gate-regmap.c
new file mode 100644
index 0000000000000..05d61c1c3c3df
--- /dev/null
+++ b/drivers/clk/clk-gate-regmap.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+/**
+ * DOC: basic gatable clock which can gate and ungate its output
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gating
+ * rate - inherits rate from parent.  No clk_set_rate support
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+static inline u32 clk_gate_regmap_readl(struct clk_gate_regmap *gate)
+{
+	u32 val;
+
+	regmap_read(gate->map, gate->map_offset, &val);
+
+	return val;
+}
+
+static inline void clk_gate_regmap_writel(struct clk_gate_regmap *gate, u32 val)
+{
+	regmap_write(gate->map, gate->map_offset, val);
+
+}
+
+/*
+ * It works on following logic:
+ *
+ * For enabling clock, enable = 1
+ *	set2dis = 1	-> clear bit	-> set = 0
+ *	set2dis = 0	-> set bit	-> set = 1
+ *
+ * For disabling clock, enable = 0
+ *	set2dis = 1	-> set bit	-> set = 1
+ *	set2dis = 0	-> clear bit	-> set = 0
+ *
+ * So, result is always: enable xor set2dis.
+ */
+static void clk_gate_regmap_endisable(struct clk_hw *hw, int enable)
+{
+	struct clk_gate_regmap *gate = to_clk_gate_regmap(hw);
+	int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
+	unsigned long flags;
+	u32 reg;
+
+	set ^= enable;
+
+	if (gate->lock)
+		spin_lock_irqsave(gate->lock, flags);
+	else
+		__acquire(gate->lock);
+
+	if (gate->flags & CLK_GATE_HIWORD_MASK) {
+		reg = BIT(gate->bit_idx + 16);
+		if (set)
+			reg |= BIT(gate->bit_idx);
+	} else {
+		reg = clk_gate_regmap_readl(gate);
+
+		if (set)
+			reg |= BIT(gate->bit_idx);
+		else
+			reg &= ~BIT(gate->bit_idx);
+	}
+
+	clk_gate_regmap_writel(gate, reg);
+
+	if (gate->lock)
+		spin_unlock_irqrestore(gate->lock, flags);
+	else
+		__release(gate->lock);
+}
+
+static int clk_gate_regmap_enable(struct clk_hw *hw)
+{
+	clk_gate_regmap_endisable(hw, 1);
+
+	return 0;
+}
+
+static void clk_gate_regmap_disable(struct clk_hw *hw)
+{
+	clk_gate_regmap_endisable(hw, 0);
+}
+
+int clk_gate_regmap_is_enabled(struct clk_hw *hw)
+{
+	u32 reg;
+	struct clk_gate_regmap *gate = to_clk_gate_regmap(hw);
+
+	reg = clk_gate_regmap_readl(gate);
+
+	/* if a set bit disables this clk, flip it before masking */
+	if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+		reg ^= BIT(gate->bit_idx);
+
+	reg &= BIT(gate->bit_idx);
+
+	return reg ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(clk_gate_regmap_is_enabled);
+
+const struct clk_ops clk_gate_regmap_ops = {
+	.enable = clk_gate_regmap_enable,
+	.disable = clk_gate_regmap_disable,
+	.is_enabled = clk_gate_regmap_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_gate_regmap_ops);
+
+struct clk_hw *__clk_hw_register_gate_regmap(struct device *dev,
+		struct device_node *np, const char *name,
+		const char *parent_name, const struct clk_hw *parent_hw,
+		const struct clk_parent_data *parent_data,
+		unsigned long flags,
+		struct regmap *map, u8 map_offset, u8 bit_idx,
+		u8 clk_gate_flags, spinlock_t *lock)
+{
+	struct clk_gate_regmap *gate;
+	struct clk_hw *hw;
+	struct clk_init_data init = {};
+	int ret = -EINVAL;
+
+	if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
+		if (bit_idx > 15) {
+			pr_err("gate bit exceeds LOWORD field\n");
+			return ERR_PTR(-EINVAL);
+		}
+	}
+
+	/* allocate the gate */
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	if (!gate)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &clk_gate_regmap_ops;
+	init.flags = flags;
+	init.parent_names = parent_name ? &parent_name : NULL;
+	init.parent_hws = parent_hw ? &parent_hw : NULL;
+	init.parent_data = parent_data;
+	if (parent_name || parent_hw || parent_data)
+		init.num_parents = 1;
+	else
+		init.num_parents = 0;
+
+	/* struct clk_gate_regmap assignments */
+	gate->map = map;
+	gate->map_offset = map_offset;
+	gate->bit_idx = bit_idx;
+	gate->flags = clk_gate_flags;
+	gate->lock = lock;
+	gate->hw.init = &init;
+
+	hw = &gate->hw;
+	if (dev || !np)
+		ret = clk_hw_register(dev, hw);
+	else if (np)
+		ret = of_clk_hw_register(np, hw);
+	if (ret) {
+		kfree(gate);
+		hw = ERR_PTR(ret);
+	}
+
+	return hw;
+
+}
+EXPORT_SYMBOL_GPL(__clk_hw_register_gate_regmap);
+
+struct clk *clk_register_gate_regmap(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags, struct regmap *map,
+		u8 map_offset, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock)
+{
+	struct clk_hw *hw;
+
+	hw = __clk_hw_register_gate_regmap(dev, NULL, name, parent_name, NULL,
+					   NULL, flags, map, map_offset, bit_idx,
+					   clk_gate_flags, lock);
+	if (IS_ERR(hw))
+		return ERR_CAST(hw);
+	return hw->clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_gate_regmap);
+
+void clk_unregister_gate_regmap(struct clk *clk)
+{
+	struct clk_gate_regmap *gate;
+	struct clk_hw *hw;
+
+	hw = __clk_get_hw(clk);
+	if (!hw)
+		return;
+
+	gate = to_clk_gate_regmap(hw);
+
+	clk_unregister(clk);
+	kfree(gate);
+}
+EXPORT_SYMBOL_GPL(clk_unregister_gate_regmap);
+
+void clk_hw_unregister_gate_regmap(struct clk_hw *hw)
+{
+	struct clk_gate_regmap *gate;
+
+	gate = to_clk_gate_regmap(hw);
+
+	clk_hw_unregister(hw);
+	kfree(gate);
+}
+EXPORT_SYMBOL_GPL(clk_hw_unregister_gate_regmap);
+
+static void devm_clk_hw_release_gate_regmap(struct device *dev, void *res)
+{
+	clk_hw_unregister_gate_regmap(*(struct clk_hw **)res);
+}
+
+struct clk_hw *__devm_clk_hw_register_gate_regmap(struct device *dev,
+		struct device_node *np, const char *name,
+		const char *parent_name, const struct clk_hw *parent_hw,
+		const struct clk_parent_data *parent_data,
+		unsigned long flags, struct regmap *map,
+		u8 map_offset, u8 bit_idx,
+		u8 clk_gate_flags, spinlock_t *lock)
+{
+	struct clk_hw **ptr, *hw;
+
+	ptr = devres_alloc(devm_clk_hw_release_gate_regmap, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	hw = __clk_hw_register_gate_regmap(dev, np, name, parent_name, parent_hw,
+					   parent_data, flags, map, map_offset,
+					   bit_idx, clk_gate_flags, lock);
+
+	if (!IS_ERR(hw)) {
+		*ptr = hw;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return hw;
+}
+EXPORT_SYMBOL_GPL(__devm_clk_hw_register_gate_regmap);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 2e6e603b74934..6f5cf6670b48d 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -8,6 +8,7 @@
 
 #include <linux/of.h>
 #include <linux/of_clk.h>
+#include <linux/regmap.h>
 
 /*
  * flags used across common struct clk.  these flags should only affect the
@@ -526,6 +527,7 @@ void of_fixed_clk_setup(struct device_node *np);
 struct clk_gate {
 	struct clk_hw hw;
 	void __iomem	*reg;
+	u8		map_offset;
 	u8		bit_idx;
 	u8		flags;
 	spinlock_t	*lock;
@@ -538,6 +540,37 @@ struct clk_gate {
 #define CLK_GATE_BIG_ENDIAN		BIT(2)
 
 extern const struct clk_ops clk_gate_ops;
+
+#ifdef CONFIG_COMMON_CLK_GATE_REGMAP
+/**
+ * struct clk_gate_regmap - gating clock via regmap
+ *
+ * @hw:		handle between common and hardware-specific interfaces
+ * @map:	regmap controlling gate
+ * @map_offset:	register offset within the regmap controlling gate
+ * @bit_idx:	single bit controlling gate
+ * @flags:	hardware-specific flags
+ * @lock:	register lock
+ *
+ * Clock which can gate its output.  Implements .enable & .disable
+ *
+ * Flags:
+ * See clk_gate
+ */
+struct clk_gate_regmap {
+	struct clk_hw hw;
+	struct regmap	*map;
+	u8		map_offset;
+	u8		bit_idx;
+	u8		flags;
+	spinlock_t	*lock;
+};
+
+#define to_clk_gate_regmap(_hw) container_of(_hw, struct clk_gate_regmap, hw)
+
+extern const struct clk_ops clk_gate_regmap_ops;
+#endif
+
 struct clk_hw *__clk_hw_register_gate(struct device *dev,
 		struct device_node *np, const char *name,
 		const char *parent_name, const struct clk_hw *parent_hw,
@@ -663,6 +696,31 @@ void clk_unregister_gate(struct clk *clk);
 void clk_hw_unregister_gate(struct clk_hw *hw);
 int clk_gate_is_enabled(struct clk_hw *hw);
 
+#ifdef CONFIG_COMMON_CLK_GATE_REGMAP
+struct clk_hw *__clk_hw_register_gate_regmap(struct device *dev,
+		struct device_node *np, const char *name,
+		const char *parent_name, const struct clk_hw *parent_hw,
+		const struct clk_parent_data *parent_data,
+		unsigned long flags,
+		struct regmap *map, u8 map_offset, u8 bit_idx,
+		u8 clk_gate_flags, spinlock_t *lock);
+struct clk_hw *__devm_clk_hw_register_gate_regmap(struct device *dev,
+		struct device_node *np, const char *name,
+		const char *parent_name, const struct clk_hw *parent_hw,
+		const struct clk_parent_data *parent_data,
+		unsigned long flags,
+		struct regmap *map, u8 map_offset, u8 bit_idx,
+		u8 clk_gate_flags, spinlock_t *lock);
+struct clk *clk_register_gate_regmap(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		struct regmap *map, u8 map_offset, u8 bit_idx,
+		u8 clk_gate_flags, spinlock_t *lock);
+
+void clk_unregister_gate_regmap(struct clk *clk);
+void clk_hw_unregister_gate_regmap(struct clk_hw *hw);
+int clk_gate_regmap_is_enabled(struct clk_hw *hw);
+#endif
+
 struct clk_div_table {
 	unsigned int	val;
 	unsigned int	div;
@@ -736,6 +794,41 @@ struct clk_divider {
 extern const struct clk_ops clk_divider_ops;
 extern const struct clk_ops clk_divider_ro_ops;
 
+#ifdef CONFIG_COMMON_CLK_DIVIDER_REGMAP
+/**
+ * struct clk_divider_regmap - adjustable divider clock via regmap
+ *
+ * @hw:		handle between common and hardware-specific interfaces
+ * @map:	regmap containing the divider
+ * @map_offset:	register offset within the regmap containing the divider
+ * @shift:	shift to the divider bit field
+ * @width:	width of the divider bit field
+ * @table:	array of value/divider pairs, last entry should have div = 0
+ * @lock:	register lock
+ *
+ * Clock with an adjustable divider affecting its output frequency.  Implements
+ * .recalc_rate, .set_rate and .round_rate
+ *
+ * @flags:
+ * See clk_divider
+ */
+struct clk_divider_regmap {
+	struct clk_hw	hw;
+	struct regmap	*regmap;
+	u8		map_offset;
+	u8		shift;
+	u8		width;
+	u8		flags;
+	const struct clk_div_table	*table;
+	spinlock_t	*lock;
+};
+
+#define to_clk_divider_regmap(_hw) container_of(_hw, struct clk_divider_regmap, hw)
+
+extern const struct clk_ops clk_divider_regmap_ops;
+extern const struct clk_ops clk_divider_regmap_ro_ops;
+#endif
+
 unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
 		unsigned int val, const struct clk_div_table *table,
 		unsigned long flags, unsigned long width);
@@ -972,6 +1065,33 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
 void clk_unregister_divider(struct clk *clk);
 void clk_hw_unregister_divider(struct clk_hw *hw);
 
+#ifdef CONFIG_COMMON_CLK_DIVIDER_REGMAP
+struct clk_hw *__clk_hw_register_divider_regmap(struct device *dev,
+		struct device_node *np, const char *name,
+		const char *parent_name, const struct clk_hw *parent_hw,
+		const struct clk_parent_data *parent_data, unsigned long flags,
+		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
+		u8 clk_divider_flags, const struct clk_div_table *table,
+		spinlock_t *lock);
+
+struct clk_hw *__devm_clk_hw_register_divider_regmap(struct device *dev,
+		struct device_node *np, const char *name,
+		const char *parent_name, const struct clk_hw *parent_hw,
+		const struct clk_parent_data *parent_data, unsigned long flags,
+		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
+		u8 clk_divider_flags, const struct clk_div_table *table,
+		spinlock_t *lock);
+
+struct clk *clk_register_divider_regmap_table(struct device *dev,
+		const char *name, const char *parent_name, unsigned long flags,
+		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
+		u8 clk_divider_flags, const struct clk_div_table *table,
+		spinlock_t *lock);
+
+void clk_unregister_divider_regmap(struct clk *clk);
+void clk_hw_unregister_divider_regmap(struct clk_hw *hw);
+#endif
+
 /**
  * struct clk_mux - multiplexer clock
  *
-- 
2.45.2


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

* [PATCH v3 9/9] clk: microchip: mpfs: use regmap clock types
  2025-06-23 12:56 [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code Conor Dooley
                   ` (7 preceding siblings ...)
  2025-06-23 12:56 ` [PATCH v3 8/9] clk: divider, gate: create regmap-backed copies of gate and divider clocks Conor Dooley
@ 2025-06-23 12:56 ` Conor Dooley
  8 siblings, 0 replies; 14+ messages in thread
From: Conor Dooley @ 2025-06-23 12:56 UTC (permalink / raw)
  To: sboyd
  Cc: conor, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

From: Conor Dooley <conor.dooley@microchip.com>

Convert the PolarFire SoC clock driver to use regmap clock types as a
preparatory work for supporting the new binding for this device that
will only provide the second of the two register regions, and will
require the use of syscon regmap to access the "cfg" and "periph" clocks
currently supported by the driver.

Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
 drivers/clk/microchip/Kconfig    |   4 +
 drivers/clk/microchip/clk-mpfs.c | 151 ++++++++++++++++++++-----------
 2 files changed, 101 insertions(+), 54 deletions(-)

diff --git a/drivers/clk/microchip/Kconfig b/drivers/clk/microchip/Kconfig
index 0724ce65898f3..cab9a909893b2 100644
--- a/drivers/clk/microchip/Kconfig
+++ b/drivers/clk/microchip/Kconfig
@@ -7,6 +7,10 @@ config MCHP_CLK_MPFS
 	bool "Clk driver for PolarFire SoC"
 	depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST
 	default ARCH_MICROCHIP_POLARFIRE
+	depends on MFD_SYSCON
 	select AUXILIARY_BUS
+	select COMMON_CLK_DIVIDER_REGMAP
+	select COMMON_CLK_GATE_REGMAP
+	select REGMAP_MMIO
 	help
 	  Supports Clock Configuration for PolarFire SoC
diff --git a/drivers/clk/microchip/clk-mpfs.c b/drivers/clk/microchip/clk-mpfs.c
index c22632a7439c5..c7fec0fcbe379 100644
--- a/drivers/clk/microchip/clk-mpfs.c
+++ b/drivers/clk/microchip/clk-mpfs.c
@@ -6,8 +6,10 @@
  */
 #include <linux/clk-provider.h>
 #include <linux/io.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/regmap.h>
 #include <dt-bindings/clock/microchip,mpfs-clock.h>
 #include <soc/microchip/mpfs.h>
 
@@ -30,6 +32,14 @@
 #define MSSPLL_POSTDIV_WIDTH	0x07u
 #define MSSPLL_FIXED_DIV	4u
 
+static const struct regmap_config clk_mpfs_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.val_format_endian = REGMAP_ENDIAN_LITTLE,
+	.max_register = REG_SUBBLK_CLOCK_CR,
+};
+
 /*
  * This clock ID is defined here, rather than the binding headers, as it is an
  * internal clock only, and therefore has no consumers in other peripheral
@@ -39,6 +49,7 @@
 
 struct mpfs_clock_data {
 	struct device *dev;
+	struct regmap *regmap;
 	void __iomem *base;
 	void __iomem *msspll_base;
 	struct clk_hw_onecell_data hw_data;
@@ -68,14 +79,12 @@ struct mpfs_msspll_out_hw_clock {
 #define to_mpfs_msspll_out_clk(_hw) container_of(_hw, struct mpfs_msspll_out_hw_clock, hw)
 
 struct mpfs_cfg_hw_clock {
-	struct clk_divider cfg;
-	struct clk_init_data init;
+	struct clk_divider_regmap divider;
 	unsigned int id;
-	u32 reg_offset;
 };
 
 struct mpfs_periph_hw_clock {
-	struct clk_gate periph;
+	struct clk_gate_regmap gate;
 	unsigned int id;
 };
 
@@ -172,15 +181,15 @@ static int mpfs_clk_register_mssplls(struct device *dev, struct mpfs_msspll_hw_c
  * MSS PLL output clocks
  */
 
-#define CLK_PLL_OUT(_id, _name, _parent, _flags, _shift, _width, _offset) {	\
-	.id = _id,								\
-	.output.shift = _shift,							\
-	.output.width = _width,							\
-	.output.table = NULL,							\
-	.reg_offset = _offset,							\
-	.output.flags = _flags,							\
-	.output.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0),	\
-	.output.lock = &mpfs_clk_lock,						\
+#define CLK_PLL_OUT(_id, _name, _parent, _flags, _shift, _width, _offset) {		\
+	.id = _id,									\
+	.output.shift = _shift,								\
+	.output.width = _width,								\
+	.output.table = NULL,								\
+	.reg_offset = _offset,								\
+	.output.flags = _flags,								\
+	.output.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_regmap_ops, 0),	\
+	.output.lock = &mpfs_clk_lock,							\
 }
 
 static struct mpfs_msspll_out_hw_clock mpfs_msspll_out_clks[] = {
@@ -220,15 +229,14 @@ static int mpfs_clk_register_msspll_outs(struct device *dev,
  * "CFG" clocks
  */
 
-#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) {		\
-	.id = _id,									\
-	.cfg.shift = _shift,								\
-	.cfg.width = _width,								\
-	.cfg.table = _table,								\
-	.reg_offset = _offset,								\
-	.cfg.flags = _flags,								\
-	.cfg.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0),		\
-	.cfg.lock = &mpfs_clk_lock,							\
+#define CLK_CFG(_id, _name, _parent, _shift, _width, _table, _flags, _offset) {	\
+	.id = _id,								\
+	.divider.shift = _shift,						\
+	.divider.width = _width,						\
+	.divider.table = _table,						\
+	.divider.map_offset = _offset,						\
+	.divider.flags = _flags,						\
+	.divider.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_regmap_ops, 0),	\
 }
 
 #define CLK_CPU_OFFSET		0u
@@ -245,13 +253,13 @@ static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
 		REG_CLOCK_CONFIG_CR),
 	{
 		.id = CLK_RTCREF,
-		.cfg.shift = 0,
-		.cfg.width = 12,
-		.cfg.table = mpfs_div_rtcref_table,
-		.reg_offset = REG_RTC_CLOCK_CR,
-		.cfg.flags = CLK_DIVIDER_ONE_BASED,
-		.cfg.hw.init =
-			CLK_HW_INIT_PARENTS_DATA("clk_rtcref", mpfs_ext_ref, &clk_divider_ops, 0),
+		.divider.shift = 0,
+		.divider.width = 12,
+		.divider.table = mpfs_div_rtcref_table,
+		.divider.map_offset = REG_RTC_CLOCK_CR,
+		.divider.flags = CLK_DIVIDER_ONE_BASED,
+		.divider.hw.init =
+			CLK_HW_INIT_PARENTS_DATA("clk_rtcref", mpfs_ext_ref, &clk_divider_regmap_ops, 0),
 	}
 };
 
@@ -264,14 +272,14 @@ static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *
 	for (i = 0; i < num_clks; i++) {
 		struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i];
 
-		cfg_hw->cfg.reg = data->base + cfg_hw->reg_offset;
-		ret = devm_clk_hw_register(dev, &cfg_hw->cfg.hw);
+		cfg_hw->divider.regmap = data->regmap;
+		ret = devm_clk_hw_register(dev, &cfg_hw->divider.hw);
 		if (ret)
 			return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
 					     cfg_hw->id);
 
 		id = cfg_hw->id;
-		data->hw_data.hws[id] = &cfg_hw->cfg.hw;
+		data->hw_data.hws[id] = &cfg_hw->divider.hw;
 	}
 
 	return 0;
@@ -281,15 +289,14 @@ static int mpfs_clk_register_cfgs(struct device *dev, struct mpfs_cfg_hw_clock *
  * peripheral clocks - devices connected to axi or ahb buses.
  */
 
-#define CLK_PERIPH(_id, _name, _parent, _shift, _flags) {			\
-	.id = _id,								\
-	.periph.bit_idx = _shift,						\
-	.periph.hw.init = CLK_HW_INIT_HW(_name, _parent, &clk_gate_ops,		\
-				  _flags),					\
-	.periph.lock = &mpfs_clk_lock,						\
+#define CLK_PERIPH(_id, _name, _parent, _shift, _flags) {				\
+	.id = _id,									\
+	.gate.map_offset = REG_SUBBLK_CLOCK_CR,						\
+	.gate.bit_idx = _shift,								\
+	.gate.hw.init = CLK_HW_INIT_HW(_name, _parent, &clk_gate_regmap_ops, _flags),	\
 }
 
-#define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].cfg.hw)
+#define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].divider.hw)
 
 /*
  * Critical clocks:
@@ -346,19 +353,60 @@ static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_c
 	for (i = 0; i < num_clks; i++) {
 		struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i];
 
-		periph_hw->periph.reg = data->base + REG_SUBBLK_CLOCK_CR;
-		ret = devm_clk_hw_register(dev, &periph_hw->periph.hw);
+		periph_hw->gate.map = data->regmap;
+		ret = devm_clk_hw_register(dev, &periph_hw->gate.hw);
 		if (ret)
 			return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
 					     periph_hw->id);
 
 		id = periph_hws[i].id;
-		data->hw_data.hws[id] = &periph_hw->periph.hw;
+		data->hw_data.hws[id] = &periph_hw->gate.hw;
 	}
 
 	return 0;
 }
 
+static inline int mpfs_clk_syscon_probe(struct mpfs_clock_data *clk_data,
+					struct platform_device *pdev)
+{
+	clk_data->regmap = syscon_regmap_lookup_by_compatible("microchip,mpfs-mss-top-sysreg");
+	if (IS_ERR(clk_data->regmap))
+		return PTR_ERR(clk_data->regmap);
+
+	clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(clk_data->msspll_base))
+		return PTR_ERR(clk_data->msspll_base);
+
+	return 0;
+}
+
+static inline int mpfs_clk_old_format_probe(struct mpfs_clock_data *clk_data,
+					    struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	dev_warn(&pdev->dev, "falling back to old devicetree format");
+
+	clk_data->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(clk_data->base))
+		return PTR_ERR(clk_data->base);
+
+	clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(clk_data->msspll_base))
+		return PTR_ERR(clk_data->msspll_base);
+
+	clk_data->regmap = devm_regmap_init_mmio(dev, clk_data->base, &clk_mpfs_regmap_config);
+	if (IS_ERR(clk_data->regmap))
+		return PTR_ERR(clk_data->regmap);
+
+	ret = mpfs_reset_controller_register(dev, clk_data->base + REG_SUBBLK_RESET_CR);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static int mpfs_clk_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -374,13 +422,12 @@ static int mpfs_clk_probe(struct platform_device *pdev)
 	if (!clk_data)
 		return -ENOMEM;
 
-	clk_data->base = devm_platform_ioremap_resource(pdev, 0);
-	if (IS_ERR(clk_data->base))
-		return PTR_ERR(clk_data->base);
-
-	clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 1);
-	if (IS_ERR(clk_data->msspll_base))
-		return PTR_ERR(clk_data->msspll_base);
+	ret = mpfs_clk_syscon_probe(clk_data, pdev);
+	if (ret) {
+		ret = mpfs_clk_old_format_probe(clk_data, pdev);
+		if (ret)
+			return ret;
+	}
 
 	clk_data->hw_data.num = num_clks;
 	clk_data->dev = dev;
@@ -406,11 +453,7 @@ static int mpfs_clk_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
-	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data);
-	if (ret)
-		return ret;
-
-	return mpfs_reset_controller_register(dev, clk_data->base + REG_SUBBLK_RESET_CR);
+	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data);
 }
 
 static const struct of_device_id mpfs_clk_of_match_table[] = {
-- 
2.45.2


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

* Re: [PATCH v3 2/9] dt-bindings: soc: microchip: document the simple-mfd syscon on PolarFire SoC
  2025-06-23 12:56 ` [PATCH v3 2/9] dt-bindings: soc: microchip: document the simple-mfd " Conor Dooley
@ 2025-06-27  7:04   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 14+ messages in thread
From: Krzysztof Kozlowski @ 2025-06-27  7:04 UTC (permalink / raw)
  To: Conor Dooley
  Cc: sboyd, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

On Mon, Jun 23, 2025 at 01:56:16PM +0100, Conor Dooley wrote:
> From: Conor Dooley <conor.dooley@microchip.com>
> 
> "mss-top-sysreg" contains clocks, pinctrl, resets, an interrupt controller
> and more. At this point, only the reset controller child is described as
> that's all that is described by the existing bindings.
> The clock controller already has a dedicated node, and will retain it as
> there are other clock regions, so like the mailbox, a compatible-based
> lookup of the syscon is sufficient to keep the clock driver working as
> before, so no child is needed. There's also an interrupt multiplexing
> service provided by this syscon, for which there is work in progress at
> [1].
> 
> Link: https://lore.kernel.org/linux-gpio/20240723-uncouple-enforcer-7c48e4a4fefe@wendy/ [1]
> Signed-off-by: Conor Dooley <conor.dooley@microchip.com>

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>

Best regards,
Krzysztof


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

* Re: [PATCH v3 4/9] reset: mpfs: add non-auxiliary bus probing
  2025-06-23 12:56 ` [PATCH v3 4/9] reset: mpfs: add non-auxiliary bus probing Conor Dooley
@ 2025-06-27 16:01   ` Philipp Zabel
  0 siblings, 0 replies; 14+ messages in thread
From: Philipp Zabel @ 2025-06-27 16:01 UTC (permalink / raw)
  To: Conor Dooley, sboyd
  Cc: Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, linux-riscv, linux-clk, devicetree, linux-kernel

On Mo, 2025-06-23 at 13:56 +0100, Conor Dooley wrote:
> From: Conor Dooley <conor.dooley@microchip.com>
> 
> While the auxiliary bus was a nice bandaid, and meant that re-writing
> the representation of the clock regions in devicetree was not required,
> it has run its course. The "mss_top_sysreg" region that contains the
> clock and reset regions, also contains pinctrl and an interrupt
> controller, so the time has come rewrite the devicetree and probe the
> reset controller from an mfd devicetree node, rather than implement
> those drivers using the auxiliary bus. Wanting to avoid propagating this
> naive/incorrect description of the hardware to the new pic64gx SoC is a
> major motivating factor here.
> 
> Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
> ---
> v2:
> Implement the request to use regmap_update_bits(). I found that I then
> hated the read/write helpers since they were just bloat, so I ripped
> them out. I replaced the regular spin_lock_irqsave() stuff with a
> guard(spinlock_irqsave), since that's a simpler way of handling the two
> different paths through such a trivial pair of functions.
> ---
>  drivers/reset/reset-mpfs.c | 81 ++++++++++++++++++++++++++++++--------
>  1 file changed, 65 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/reset/reset-mpfs.c b/drivers/reset/reset-mpfs.c
> index 574e59db83a4f..9c3e996f3a099 100644
> --- a/drivers/reset/reset-mpfs.c
> +++ b/drivers/reset/reset-mpfs.c
> @@ -7,12 +7,15 @@
>   *
>   */
>  #include <linux/auxiliary_bus.h>
> +#include <linux/cleanup.h>
>  #include <linux/delay.h>
>  #include <linux/io.h>
> +#include <linux/mfd/syscon.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
> +#include <linux/regmap.h>

Maybe sort these alphabetically.

>  #include <linux/reset-controller.h>
>  #include <dt-bindings/clock/microchip,mpfs-clock.h>
>  #include <soc/microchip/mpfs.h>
> @@ -27,11 +30,14 @@
>  #define MPFS_SLEEP_MIN_US	100
>  #define MPFS_SLEEP_MAX_US	200
>  
> +#define REG_SUBBLK_RESET_CR	0x88u
> +
>  /* block concurrent access to the soft reset register */
>  static DEFINE_SPINLOCK(mpfs_reset_lock);
>  
>  struct mpfs_reset {
>  	void __iomem *base;
> +	struct regmap *regmap;
>  	struct reset_controller_dev rcdev;
>  };
>  
> @@ -46,41 +52,50 @@ static inline struct mpfs_reset *to_mpfs_reset(struct reset_controller_dev *rcde
>  static int mpfs_assert(struct reset_controller_dev *rcdev, unsigned long id)
>  {
>  	struct mpfs_reset *rst = to_mpfs_reset(rcdev);
> -	unsigned long flags;
>  	u32 reg;
>  
> -	spin_lock_irqsave(&mpfs_reset_lock, flags);
> +	guard(spinlock_irqsave)(&mpfs_reset_lock);
> +
> +	if (rst->regmap) {
> +		regmap_update_bits(rst->regmap, REG_SUBBLK_RESET_CR, BIT(id), BIT(id));

mpfs_reset_lock is only needed for the readl()/writel() below.
regmap has its own locking.

> +		return 0;
> +	}
>  
>  	reg = readl(rst->base);
>  	reg |= BIT(id);
>  	writel(reg, rst->base);
>  
> -	spin_unlock_irqrestore(&mpfs_reset_lock, flags);
> -
>  	return 0;
>  }
>  
>  static int mpfs_deassert(struct reset_controller_dev *rcdev, unsigned long id)
>  {
>  	struct mpfs_reset *rst = to_mpfs_reset(rcdev);
> -	unsigned long flags;
>  	u32 reg;
>  
> -	spin_lock_irqsave(&mpfs_reset_lock, flags);
> +	guard(spinlock_irqsave)(&mpfs_reset_lock);
> +
> +	if (rst->regmap) {
> +		regmap_update_bits(rst->regmap, REG_SUBBLK_RESET_CR, BIT(id), 0);

Same as above.


regards
Philipp

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

* Re: [PATCH v3 8/9] clk: divider, gate: create regmap-backed copies of gate and divider clocks
  2025-06-23 12:56 ` [PATCH v3 8/9] clk: divider, gate: create regmap-backed copies of gate and divider clocks Conor Dooley
@ 2025-07-31 11:23   ` Gabriel FERNANDEZ
  2025-08-05 17:13     ` Conor Dooley
  0 siblings, 1 reply; 14+ messages in thread
From: Gabriel FERNANDEZ @ 2025-07-31 11:23 UTC (permalink / raw)
  To: Conor Dooley, sboyd
  Cc: Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel


On 6/23/25 14:56, Conor Dooley wrote:
> From: Conor Dooley <conor.dooley@microchip.com>
>
> Implement regmap-backed copies of gate and divider clocks by replacing
> the iomem pointer to the clock registers with a regmap and offset
> within.
>
> Signed-off-by: Conor Dooley <conor.dooley@microchip.com>

Hi Conor,

Excellent patch, thank you! I really needed this and will be using it.

I would also be interested in having a similar regmap-backed 
implementation for the multiplexer clock.  Do you have any plans to work 
on this as well? If not, I’d be happy to propose a patch for it, with 
your agreement.

Thanks again for your work!

Best regards,

> ---
>   drivers/clk/Kconfig              |   8 +
>   drivers/clk/Makefile             |   2 +
>   drivers/clk/clk-divider-regmap.c | 271 +++++++++++++++++++++++++++++++
>   drivers/clk/clk-gate-regmap.c    | 254 +++++++++++++++++++++++++++++
>   include/linux/clk-provider.h     | 120 ++++++++++++++
>   5 files changed, 655 insertions(+)
>   create mode 100644 drivers/clk/clk-divider-regmap.c
>   create mode 100644 drivers/clk/clk-gate-regmap.c
>
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 19c1ed280fd7f..273154031325e 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -33,6 +33,14 @@ menuconfig COMMON_CLK
>   
>   if COMMON_CLK
>   
> +config COMMON_CLK_DIVIDER_REGMAP
> +	bool
> +	select REGMAP
> +
> +config COMMON_CLK_GATE_REGMAP
> +	bool
> +	select REGMAP
> +

Why not use a single, more generic configuration like COMMON_CLK_REGMAP 
instead of having both COMMON_CLK_DIVIDER_REGMAP and 
COMMON_CLK_GATE_REGMAP ?


>   config COMMON_CLK_WM831X
>   	tristate "Clock driver for WM831x/2x PMICs"
>   	depends on MFD_WM831X
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 42867cd37c33d..6dbc1fe1e03e2 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -20,11 +20,13 @@ clk-test-y			:= clk_test.o \
>   				   kunit_clk_assigned_rates_zero_consumer.dtbo.o \
>   				   kunit_clk_parent_data_test.dtbo.o
>   obj-$(CONFIG_COMMON_CLK)	+= clk-divider.o
> +obj-$(CONFIG_COMMON_CLK_DIVIDER_REGMAP)	+= clk-divider-regmap.o
>   obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-factor.o
>   obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-rate.o
>   obj-$(CONFIG_CLK_FIXED_RATE_KUNIT_TEST)	+= clk-fixed-rate-test.o
>   clk-fixed-rate-test-y		:= clk-fixed-rate_test.o kunit_clk_fixed_rate_test.dtbo.o
>   obj-$(CONFIG_COMMON_CLK)	+= clk-gate.o
> +obj-$(CONFIG_COMMON_CLK_GATE_REGMAP)	+= clk-gate-regmap.o
>   obj-$(CONFIG_CLK_GATE_KUNIT_TEST) += clk-gate_test.o
>   obj-$(CONFIG_COMMON_CLK)	+= clk-multiplier.o
>   obj-$(CONFIG_COMMON_CLK)	+= clk-mux.o
> diff --git a/drivers/clk/clk-divider-regmap.c b/drivers/clk/clk-divider-regmap.c
> new file mode 100644
> index 0000000000000..43b8f3dedb9e1
> --- /dev/null
> +++ b/drivers/clk/clk-divider-regmap.c
> @@ -0,0 +1,271 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/export.h>
> +#include <linux/slab.h>
> +
> +static inline u32 clk_div_regmap_readl(struct clk_divider_regmap *divider)
> +{
> +	u32 val;
> +
> +	regmap_read(divider->regmap, divider->map_offset, &val);
> +
> +	return val;
> +}
> +
> +static inline void clk_div_regmap_writel(struct clk_divider_regmap *divider, u32 val)
> +{
> +	regmap_write(divider->regmap, divider->map_offset, val);
> +
> +}
> +
> +static unsigned long clk_divider_regmap_recalc_rate(struct clk_hw *hw,
> +		unsigned long parent_rate)
> +{
> +	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
> +	unsigned int val;
> +
> +	val = clk_div_regmap_readl(divider) >> divider->shift;
> +	val &= clk_div_mask(divider->width);
> +
> +	return divider_recalc_rate(hw, parent_rate, val, divider->table,
> +				   divider->flags, divider->width);
> +}
> +
> +static long clk_divider_regmap_round_rate(struct clk_hw *hw, unsigned long rate,
> +				unsigned long *prate)
> +{
> +	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
> +
> +	/* if read only, just return current value */
> +	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
> +		u32 val;
> +
> +		val = clk_div_regmap_readl(divider) >> divider->shift;
> +		val &= clk_div_mask(divider->width);
> +
> +		return divider_ro_round_rate(hw, rate, prate, divider->table,
> +					     divider->width, divider->flags,
> +					     val);
> +	}
> +
> +	return divider_round_rate(hw, rate, prate, divider->table,
> +				  divider->width, divider->flags);
> +}
> +
> +static int clk_divider_regmap_determine_rate(struct clk_hw *hw,
> +				      struct clk_rate_request *req)
> +{
> +	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
> +
> +	/* if read only, just return current value */
> +	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
> +		u32 val;
> +
> +		val = clk_div_regmap_readl(divider) >> divider->shift;
> +		val &= clk_div_mask(divider->width);
> +
> +		return divider_ro_determine_rate(hw, req, divider->table,
> +						 divider->width,
> +						 divider->flags, val);
> +	}
> +
> +	return divider_determine_rate(hw, req, divider->table, divider->width,
> +				      divider->flags);
> +}
> +
> +static int clk_divider_regmap_set_rate(struct clk_hw *hw, unsigned long rate,
> +				unsigned long parent_rate)
> +{
> +	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
> +	int value;
> +	unsigned long flags = 0;
> +	u32 val;
> +
> +	value = divider_get_val(rate, parent_rate, divider->table,
> +				divider->width, divider->flags);
> +	if (value < 0)
> +		return value;
> +
> +	if (divider->lock)
> +		spin_lock_irqsave(divider->lock, flags);
> +	else
> +		__acquire(divider->lock);
> +
> +	if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
> +		val = clk_div_mask(divider->width) << (divider->shift + 16);
> +	} else {
> +		val = clk_div_regmap_readl(divider);
> +		val &= ~(clk_div_mask(divider->width) << divider->shift);
> +	}
> +	val |= (u32)value << divider->shift;
> +	clk_div_regmap_writel(divider, val);
> +
> +	if (divider->lock)
> +		spin_unlock_irqrestore(divider->lock, flags);
> +	else
> +		__release(divider->lock);
> +
> +	return 0;
> +}
> +
> +const struct clk_ops clk_divider_regmap_ops = {
> +	.recalc_rate = clk_divider_regmap_recalc_rate,
> +	.round_rate = clk_divider_regmap_round_rate,
> +	.determine_rate = clk_divider_regmap_determine_rate,
> +	.set_rate = clk_divider_regmap_set_rate,
> +};
> +EXPORT_SYMBOL_GPL(clk_divider_regmap_ops);
> +
> +const struct clk_ops clk_divider_regmap_ro_ops = {
> +	.recalc_rate = clk_divider_regmap_recalc_rate,
> +	.round_rate = clk_divider_regmap_round_rate,
> +	.determine_rate = clk_divider_regmap_determine_rate,
> +};
> +EXPORT_SYMBOL_GPL(clk_divider_regmap_ro_ops);
> +
> +struct clk_hw *__clk_hw_register_divider_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock)
> +{
> +	struct clk_divider_regmap *div;
> +	struct clk_hw *hw;
> +	struct clk_init_data init = {};
> +	int ret;
> +
> +	if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
> +		if (width + shift > 16) {
> +			pr_warn("divider value exceeds LOWORD field\n");
> +			return ERR_PTR(-EINVAL);
> +		}
> +	}
> +
> +	/* allocate the divider */
> +	div = kzalloc(sizeof(*div), GFP_KERNEL);
> +	if (!div)
> +		return ERR_PTR(-ENOMEM);
> +
> +	init.name = name;
> +	if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
> +		init.ops = &clk_divider_regmap_ro_ops;
> +	else
> +		init.ops = &clk_divider_regmap_ops;
> +	init.flags = flags;
> +	init.parent_names = parent_name ? &parent_name : NULL;
> +	init.parent_hws = parent_hw ? &parent_hw : NULL;
> +	init.parent_data = parent_data;
> +	if (parent_name || parent_hw || parent_data)
> +		init.num_parents = 1;
> +	else
> +		init.num_parents = 0;
> +
> +	/* struct clk_divider assignments */
> +	div->regmap = regmap;
> +	div->map_offset = map_offset;
> +	div->shift = shift;
> +	div->width = width;
> +	div->flags = clk_divider_flags;
> +	div->lock = lock;
> +	div->hw.init = &init;
> +	div->table = table;
> +
> +	/* register the clock */
> +	hw = &div->hw;
> +	ret = clk_hw_register(dev, hw);
> +	if (ret) {
> +		kfree(div);
> +		hw = ERR_PTR(ret);
> +	}
> +
> +	return hw;
> +}
> +EXPORT_SYMBOL_GPL(__clk_hw_register_divider_regmap);
> +
> +struct clk *clk_register_divider_regmap_table(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock)
> +{
> +	struct clk_hw *hw;
> +
> +	hw =  __clk_hw_register_divider_regmap(dev, NULL, name, parent_name, NULL,
> +					       NULL, flags, regmap, map_offset,
> +					       shift, width, clk_divider_flags,
> +					       table, lock);
> +	if (IS_ERR(hw))
> +		return ERR_CAST(hw);
> +	return hw->clk;
> +}
> +EXPORT_SYMBOL_GPL(clk_register_divider_regmap_table);
> +
> +void clk_unregister_divider_regmap(struct clk *clk)
> +{
> +	struct clk_divider_regmap *div;
> +	struct clk_hw *hw;
> +
> +	hw = __clk_get_hw(clk);
> +	if (!hw)
> +		return;
> +
> +	div = to_clk_divider_regmap(hw);
> +
> +	clk_unregister(clk);
> +	kfree(div);
> +}
> +EXPORT_SYMBOL_GPL(clk_unregister_divider_regmap);
> +
> +/**
> + * clk_hw_unregister_divider_regmap - unregister a clk divider
> + * @hw: hardware-specific clock data to unregister
> + */
> +void clk_hw_unregister_divider_regmap(struct clk_hw *hw)
> +{
> +	struct clk_divider_regmap *div;
> +
> +	div = to_clk_divider_regmap(hw);
> +
> +	clk_hw_unregister(hw);
> +	kfree(div);
> +}
> +EXPORT_SYMBOL_GPL(clk_hw_unregister_divider_regmap);
> +
> +static void devm_clk_hw_release_divider_regmap(struct device *dev, void *res)
> +{
> +	clk_hw_unregister_divider_regmap(*(struct clk_hw **)res);
> +}
> +
> +struct clk_hw *__devm_clk_hw_register_divider_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock)
> +{
> +	struct clk_hw **ptr, *hw;
> +
> +	ptr = devres_alloc(devm_clk_hw_release_divider_regmap, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	hw = __clk_hw_register_divider_regmap(dev, np, name, parent_name, parent_hw,
> +					      parent_data, flags, regmap, map_offset,
> +					      shift, width, clk_divider_flags, table,
> +					      lock);
> +
> +	if (!IS_ERR(hw)) {
> +		*ptr = hw;
> +		devres_add(dev, ptr);
> +	} else {
> +		devres_free(ptr);
> +	}
> +
> +	return hw;
> +}
> +EXPORT_SYMBOL_GPL(__devm_clk_hw_register_divider_regmap);
> diff --git a/drivers/clk/clk-gate-regmap.c b/drivers/clk/clk-gate-regmap.c
> new file mode 100644
> index 0000000000000..05d61c1c3c3df
> --- /dev/null
> +++ b/drivers/clk/clk-gate-regmap.c
> @@ -0,0 +1,254 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +
> +/**
> + * DOC: basic gatable clock which can gate and ungate its output
> + *
> + * Traits of this clock:
> + * prepare - clk_(un)prepare only ensures parent is (un)prepared
> + * enable - clk_enable and clk_disable are functional & control gating
> + * rate - inherits rate from parent.  No clk_set_rate support
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +static inline u32 clk_gate_regmap_readl(struct clk_gate_regmap *gate)
> +{
> +	u32 val;
> +
> +	regmap_read(gate->map, gate->map_offset, &val);
> +
> +	return val;
> +}
> +
> +static inline void clk_gate_regmap_writel(struct clk_gate_regmap *gate, u32 val)
> +{
> +	regmap_write(gate->map, gate->map_offset, val);
> +
> +}
> +
> +/*
> + * It works on following logic:
> + *
> + * For enabling clock, enable = 1
> + *	set2dis = 1	-> clear bit	-> set = 0
> + *	set2dis = 0	-> set bit	-> set = 1
> + *
> + * For disabling clock, enable = 0
> + *	set2dis = 1	-> set bit	-> set = 1
> + *	set2dis = 0	-> clear bit	-> set = 0
> + *
> + * So, result is always: enable xor set2dis.
> + */
> +static void clk_gate_regmap_endisable(struct clk_hw *hw, int enable)
> +{
> +	struct clk_gate_regmap *gate = to_clk_gate_regmap(hw);
> +	int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
> +	unsigned long flags;
> +	u32 reg;
> +
> +	set ^= enable;
> +
> +	if (gate->lock)
> +		spin_lock_irqsave(gate->lock, flags);
> +	else
> +		__acquire(gate->lock);
> +
> +	if (gate->flags & CLK_GATE_HIWORD_MASK) {
> +		reg = BIT(gate->bit_idx + 16);
> +		if (set)
> +			reg |= BIT(gate->bit_idx);
> +	} else {
> +		reg = clk_gate_regmap_readl(gate);
> +
> +		if (set)
> +			reg |= BIT(gate->bit_idx);
> +		else
> +			reg &= ~BIT(gate->bit_idx);
> +	}
> +
> +	clk_gate_regmap_writel(gate, reg);
> +
> +	if (gate->lock)
> +		spin_unlock_irqrestore(gate->lock, flags);
> +	else
> +		__release(gate->lock);
> +}
> +
> +static int clk_gate_regmap_enable(struct clk_hw *hw)
> +{
> +	clk_gate_regmap_endisable(hw, 1);
> +
> +	return 0;
> +}
> +
> +static void clk_gate_regmap_disable(struct clk_hw *hw)
> +{
> +	clk_gate_regmap_endisable(hw, 0);
> +}
> +
> +int clk_gate_regmap_is_enabled(struct clk_hw *hw)
> +{
> +	u32 reg;
> +	struct clk_gate_regmap *gate = to_clk_gate_regmap(hw);
> +
> +	reg = clk_gate_regmap_readl(gate);
> +
> +	/* if a set bit disables this clk, flip it before masking */
> +	if (gate->flags & CLK_GATE_SET_TO_DISABLE)
> +		reg ^= BIT(gate->bit_idx);
> +
> +	reg &= BIT(gate->bit_idx);
> +
> +	return reg ? 1 : 0;
> +}
> +EXPORT_SYMBOL_GPL(clk_gate_regmap_is_enabled);
> +
> +const struct clk_ops clk_gate_regmap_ops = {
> +	.enable = clk_gate_regmap_enable,
> +	.disable = clk_gate_regmap_disable,
> +	.is_enabled = clk_gate_regmap_is_enabled,
> +};
> +EXPORT_SYMBOL_GPL(clk_gate_regmap_ops);
> +
> +struct clk_hw *__clk_hw_register_gate_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data,
> +		unsigned long flags,
> +		struct regmap *map, u8 map_offset, u8 bit_idx,
> +		u8 clk_gate_flags, spinlock_t *lock)
> +{
> +	struct clk_gate_regmap *gate;
> +	struct clk_hw *hw;
> +	struct clk_init_data init = {};
> +	int ret = -EINVAL;
> +
> +	if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
> +		if (bit_idx > 15) {
> +			pr_err("gate bit exceeds LOWORD field\n");
> +			return ERR_PTR(-EINVAL);
> +		}
> +	}
> +
> +	/* allocate the gate */
> +	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> +	if (!gate)
> +		return ERR_PTR(-ENOMEM);
> +
> +	init.name = name;
> +	init.ops = &clk_gate_regmap_ops;
> +	init.flags = flags;
> +	init.parent_names = parent_name ? &parent_name : NULL;
> +	init.parent_hws = parent_hw ? &parent_hw : NULL;
> +	init.parent_data = parent_data;
> +	if (parent_name || parent_hw || parent_data)
> +		init.num_parents = 1;
> +	else
> +		init.num_parents = 0;
> +
> +	/* struct clk_gate_regmap assignments */
> +	gate->map = map;
> +	gate->map_offset = map_offset;
> +	gate->bit_idx = bit_idx;
> +	gate->flags = clk_gate_flags;
> +	gate->lock = lock;
> +	gate->hw.init = &init;
> +
> +	hw = &gate->hw;
> +	if (dev || !np)
> +		ret = clk_hw_register(dev, hw);
> +	else if (np)
> +		ret = of_clk_hw_register(np, hw);
> +	if (ret) {
> +		kfree(gate);
> +		hw = ERR_PTR(ret);
> +	}
> +
> +	return hw;
> +
> +}
> +EXPORT_SYMBOL_GPL(__clk_hw_register_gate_regmap);
> +
> +struct clk *clk_register_gate_regmap(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags, struct regmap *map,
> +		u8 map_offset, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock)
> +{
> +	struct clk_hw *hw;
> +
> +	hw = __clk_hw_register_gate_regmap(dev, NULL, name, parent_name, NULL,
> +					   NULL, flags, map, map_offset, bit_idx,
> +					   clk_gate_flags, lock);
> +	if (IS_ERR(hw))
> +		return ERR_CAST(hw);
> +	return hw->clk;
> +}
> +EXPORT_SYMBOL_GPL(clk_register_gate_regmap);
> +
> +void clk_unregister_gate_regmap(struct clk *clk)
> +{
> +	struct clk_gate_regmap *gate;
> +	struct clk_hw *hw;
> +
> +	hw = __clk_get_hw(clk);
> +	if (!hw)
> +		return;
> +
> +	gate = to_clk_gate_regmap(hw);
> +
> +	clk_unregister(clk);
> +	kfree(gate);
> +}
> +EXPORT_SYMBOL_GPL(clk_unregister_gate_regmap);
> +
> +void clk_hw_unregister_gate_regmap(struct clk_hw *hw)
> +{
> +	struct clk_gate_regmap *gate;
> +
> +	gate = to_clk_gate_regmap(hw);
> +
> +	clk_hw_unregister(hw);
> +	kfree(gate);
> +}
> +EXPORT_SYMBOL_GPL(clk_hw_unregister_gate_regmap);
> +
> +static void devm_clk_hw_release_gate_regmap(struct device *dev, void *res)
> +{
> +	clk_hw_unregister_gate_regmap(*(struct clk_hw **)res);
> +}
> +
> +struct clk_hw *__devm_clk_hw_register_gate_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data,
> +		unsigned long flags, struct regmap *map,
> +		u8 map_offset, u8 bit_idx,
> +		u8 clk_gate_flags, spinlock_t *lock)
> +{
> +	struct clk_hw **ptr, *hw;
> +
> +	ptr = devres_alloc(devm_clk_hw_release_gate_regmap, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	hw = __clk_hw_register_gate_regmap(dev, np, name, parent_name, parent_hw,
> +					   parent_data, flags, map, map_offset,
> +					   bit_idx, clk_gate_flags, lock);
> +
> +	if (!IS_ERR(hw)) {
> +		*ptr = hw;
> +		devres_add(dev, ptr);
> +	} else {
> +		devres_free(ptr);
> +	}
> +
> +	return hw;
> +}
> +EXPORT_SYMBOL_GPL(__devm_clk_hw_register_gate_regmap);
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 2e6e603b74934..6f5cf6670b48d 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -8,6 +8,7 @@
>   
>   #include <linux/of.h>
>   #include <linux/of_clk.h>
> +#include <linux/regmap.h>
>   
>   /*
>    * flags used across common struct clk.  these flags should only affect the
> @@ -526,6 +527,7 @@ void of_fixed_clk_setup(struct device_node *np);
>   struct clk_gate {
>   	struct clk_hw hw;
>   	void __iomem	*reg;
> +	u8		map_offset;

I thought the|map_offset|field would only be needed in the|struct 
clk_gate_regmap|structure, not in|struct clk_gate|.


>   	u8		bit_idx;
>   	u8		flags;
>   	spinlock_t	*lock;
> @@ -538,6 +540,37 @@ struct clk_gate {
>   #define CLK_GATE_BIG_ENDIAN		BIT(2)
>   
>   extern const struct clk_ops clk_gate_ops;
> +
> +#ifdef CONFIG_COMMON_CLK_GATE_REGMAP
> +/**
> + * struct clk_gate_regmap - gating clock via regmap
> + *
> + * @hw:		handle between common and hardware-specific interfaces
> + * @map:	regmap controlling gate
> + * @map_offset:	register offset within the regmap controlling gate
> + * @bit_idx:	single bit controlling gate
> + * @flags:	hardware-specific flags
> + * @lock:	register lock
> + *
> + * Clock which can gate its output.  Implements .enable & .disable
> + *
> + * Flags:
> + * See clk_gate
> + */
> +struct clk_gate_regmap {
> +	struct clk_hw hw;
> +	struct regmap	*map;
> +	u8		map_offset;
I would have preferred if the size of|map_offset|was larger, for example 
using|u16|or|u32|instead of|u8|.

Is there a specific reason for choosing|u8|? Using a larger type might 
offer more flexibility for future use cases.


Thanks again for your work!

Best regards,

Gabriel


> +	u8		bit_idx;
> +	u8		flags;
> +	spinlock_t	*lock;
> +};
> +
> +#define to_clk_gate_regmap(_hw) container_of(_hw, struct clk_gate_regmap, hw)
> +
> +extern const struct clk_ops clk_gate_regmap_ops;
> +#endif
> +
>   struct clk_hw *__clk_hw_register_gate(struct device *dev,
>   		struct device_node *np, const char *name,
>   		const char *parent_name, const struct clk_hw *parent_hw,
> @@ -663,6 +696,31 @@ void clk_unregister_gate(struct clk *clk);
>   void clk_hw_unregister_gate(struct clk_hw *hw);
>   int clk_gate_is_enabled(struct clk_hw *hw);
>   
> +#ifdef CONFIG_COMMON_CLK_GATE_REGMAP
> +struct clk_hw *__clk_hw_register_gate_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data,
> +		unsigned long flags,
> +		struct regmap *map, u8 map_offset, u8 bit_idx,
> +		u8 clk_gate_flags, spinlock_t *lock);
> +struct clk_hw *__devm_clk_hw_register_gate_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data,
> +		unsigned long flags,
> +		struct regmap *map, u8 map_offset, u8 bit_idx,
> +		u8 clk_gate_flags, spinlock_t *lock);
> +struct clk *clk_register_gate_regmap(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		struct regmap *map, u8 map_offset, u8 bit_idx,
> +		u8 clk_gate_flags, spinlock_t *lock);
> +
> +void clk_unregister_gate_regmap(struct clk *clk);
> +void clk_hw_unregister_gate_regmap(struct clk_hw *hw);
> +int clk_gate_regmap_is_enabled(struct clk_hw *hw);
> +#endif
> +
>   struct clk_div_table {
>   	unsigned int	val;
>   	unsigned int	div;
> @@ -736,6 +794,41 @@ struct clk_divider {
>   extern const struct clk_ops clk_divider_ops;
>   extern const struct clk_ops clk_divider_ro_ops;
>   
> +#ifdef CONFIG_COMMON_CLK_DIVIDER_REGMAP
> +/**
> + * struct clk_divider_regmap - adjustable divider clock via regmap
> + *
> + * @hw:		handle between common and hardware-specific interfaces
> + * @map:	regmap containing the divider
> + * @map_offset:	register offset within the regmap containing the divider
> + * @shift:	shift to the divider bit field
> + * @width:	width of the divider bit field
> + * @table:	array of value/divider pairs, last entry should have div = 0
> + * @lock:	register lock
> + *
> + * Clock with an adjustable divider affecting its output frequency.  Implements
> + * .recalc_rate, .set_rate and .round_rate
> + *
> + * @flags:
> + * See clk_divider
> + */
> +struct clk_divider_regmap {
> +	struct clk_hw	hw;
> +	struct regmap	*regmap;
> +	u8		map_offset;
> +	u8		shift;
> +	u8		width;
> +	u8		flags;
> +	const struct clk_div_table	*table;
> +	spinlock_t	*lock;
> +};
> +
> +#define to_clk_divider_regmap(_hw) container_of(_hw, struct clk_divider_regmap, hw)
> +
> +extern const struct clk_ops clk_divider_regmap_ops;
> +extern const struct clk_ops clk_divider_regmap_ro_ops;
> +#endif
> +
>   unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
>   		unsigned int val, const struct clk_div_table *table,
>   		unsigned long flags, unsigned long width);
> @@ -972,6 +1065,33 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
>   void clk_unregister_divider(struct clk *clk);
>   void clk_hw_unregister_divider(struct clk_hw *hw);
>   
> +#ifdef CONFIG_COMMON_CLK_DIVIDER_REGMAP
> +struct clk_hw *__clk_hw_register_divider_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock);
> +
> +struct clk_hw *__devm_clk_hw_register_divider_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock);
> +
> +struct clk *clk_register_divider_regmap_table(struct device *dev,
> +		const char *name, const char *parent_name, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock);
> +
> +void clk_unregister_divider_regmap(struct clk *clk);
> +void clk_hw_unregister_divider_regmap(struct clk_hw *hw);
> +#endif
> +
>   /**
>    * struct clk_mux - multiplexer clock
>    *

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

* Re: [PATCH v3 8/9] clk: divider, gate: create regmap-backed copies of gate and divider clocks
  2025-07-31 11:23   ` Gabriel FERNANDEZ
@ 2025-08-05 17:13     ` Conor Dooley
  0 siblings, 0 replies; 14+ messages in thread
From: Conor Dooley @ 2025-08-05 17:13 UTC (permalink / raw)
  To: Gabriel FERNANDEZ
  Cc: sboyd, Conor Dooley, Daire McNamara, pierre-henry.moussay,
	valentina.fernandezalanis, Michael Turquette, Rob Herring,
	Krzysztof Kozlowski, Jassi Brar, Lee Jones, Paul Walmsley,
	Palmer Dabbelt, Philipp Zabel, linux-riscv, linux-clk, devicetree,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1087 bytes --]

On Thu, Jul 31, 2025 at 01:23:49PM +0200, Gabriel FERNANDEZ wrote:
> 
> On 6/23/25 14:56, Conor Dooley wrote:
> > From: Conor Dooley <conor.dooley@microchip.com>
> > 
> > Implement regmap-backed copies of gate and divider clocks by replacing
> > the iomem pointer to the clock registers with a regmap and offset
> > within.
> > 
> > Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
> 
> Hi Conor,
> 
> Excellent patch, thank you! I really needed this and will be using it.
> 
> I would also be interested in having a similar regmap-backed implementation
> for the multiplexer clock.  Do you have any plans to work on this as well?
> If not, I’d be happy to propose a patch for it, with your agreement.

The only types of clock my driver needed were gate and divider, so those
were all I focused on. I don't really have a plan to implement more,
particular given the lack of feedback here means that I don't even know
if what I have done is what Stephen wants. The rest of your comments
seem reasonable, and I'll try to implement them in a new version.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

end of thread, other threads:[~2025-08-05 17:13 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-23 12:56 [PATCH v3 0/9] Redo PolarFire SoC's mailbox/clock devicestrees and related code Conor Dooley
2025-06-23 12:56 ` [PATCH v3 1/9] dt-bindings: mfd: syscon document the control-scb syscon on PolarFire SoC Conor Dooley
2025-06-23 12:56 ` [PATCH v3 2/9] dt-bindings: soc: microchip: document the simple-mfd " Conor Dooley
2025-06-27  7:04   ` Krzysztof Kozlowski
2025-06-23 12:56 ` [PATCH v3 3/9] soc: microchip: add mfd drivers for two syscon regions " Conor Dooley
2025-06-23 12:56 ` [PATCH v3 4/9] reset: mpfs: add non-auxiliary bus probing Conor Dooley
2025-06-27 16:01   ` Philipp Zabel
2025-06-23 12:56 ` [PATCH v3 5/9] dt-bindings: clk: microchip: mpfs: remove first reg region Conor Dooley
2025-06-23 12:56 ` [PATCH v3 6/9] riscv: dts: microchip: fix mailbox description Conor Dooley
2025-06-23 12:56 ` [PATCH v3 7/9] riscv: dts: microchip: convert clock and reset to use syscon Conor Dooley
2025-06-23 12:56 ` [PATCH v3 8/9] clk: divider, gate: create regmap-backed copies of gate and divider clocks Conor Dooley
2025-07-31 11:23   ` Gabriel FERNANDEZ
2025-08-05 17:13     ` Conor Dooley
2025-06-23 12:56 ` [PATCH v3 9/9] clk: microchip: mpfs: use regmap clock types Conor Dooley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).