* [PATCH v3 1/8] reset: imx8mp-audiomix: Fix bad mask values
2025-10-29 13:52 [PATCH v3 0/8] Add support for i.MX8ULP's SIM LPAV Laurentiu Mihalcea
@ 2025-10-29 13:52 ` Laurentiu Mihalcea
2025-10-29 13:52 ` [PATCH v3 2/8] dt-bindings: clock: document 8ULP's SIM LPAV Laurentiu Mihalcea
` (6 subsequent siblings)
7 siblings, 0 replies; 21+ messages in thread
From: Laurentiu Mihalcea @ 2025-10-29 13:52 UTC (permalink / raw)
To: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang
Cc: linux-clk, imx, devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
As per the i.MX8MP TRM, section 14.2 "AUDIO_BLK_CTRL", table 14.2.3.1.1
"memory map", the definition of the EARC control register shows that the
EARC controller software reset is controlled via bit 0, while the EARC PHY
software reset is controlled via bit 1.
This means that the current definitions of IMX8MP_AUDIOMIX_EARC_RESET_MASK
and IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK are wrong since their values would
imply that the EARC controller software reset is controlled via bit 1 and
the EARC PHY software reset is controlled via bit 2. Fix them.
Fixes: a83bc87cd30a ("reset: imx8mp-audiomix: Prepare the code for more reset bits")
Cc: stable@vger.kernel.org
Reviewed-by: Shengjiu Wang <shengjiu.wang@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
---
drivers/reset/reset-imx8mp-audiomix.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
index 6b357adfe646..eceb37ff5dc5 100644
--- a/drivers/reset/reset-imx8mp-audiomix.c
+++ b/drivers/reset/reset-imx8mp-audiomix.c
@@ -14,8 +14,8 @@
#include <linux/reset-controller.h>
#define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
-#define IMX8MP_AUDIOMIX_EARC_RESET_MASK BIT(1)
-#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK BIT(2)
+#define IMX8MP_AUDIOMIX_EARC_RESET_MASK BIT(0)
+#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK BIT(1)
#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET 0x108
#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK BIT(5)
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v3 2/8] dt-bindings: clock: document 8ULP's SIM LPAV
2025-10-29 13:52 [PATCH v3 0/8] Add support for i.MX8ULP's SIM LPAV Laurentiu Mihalcea
2025-10-29 13:52 ` [PATCH v3 1/8] reset: imx8mp-audiomix: Fix bad mask values Laurentiu Mihalcea
@ 2025-10-29 13:52 ` Laurentiu Mihalcea
2025-10-29 13:52 ` [PATCH v3 3/8] clk: imx: add driver for imx8ulp's sim lpav Laurentiu Mihalcea
` (5 subsequent siblings)
7 siblings, 0 replies; 21+ messages in thread
From: Laurentiu Mihalcea @ 2025-10-29 13:52 UTC (permalink / raw)
To: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang
Cc: linux-clk, imx, devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
Add documentation for i.MX8ULP's SIM LPAV module.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
---
.../bindings/clock/fsl,imx8ulp-sim-lpav.yaml | 72 +++++++++++++++++++
include/dt-bindings/clock/imx8ulp-clock.h | 5 ++
.../dt-bindings/reset/fsl,imx8ulp-sim-lpav.h | 16 +++++
3 files changed, 93 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/fsl,imx8ulp-sim-lpav.yaml
create mode 100644 include/dt-bindings/reset/fsl,imx8ulp-sim-lpav.h
diff --git a/Documentation/devicetree/bindings/clock/fsl,imx8ulp-sim-lpav.yaml b/Documentation/devicetree/bindings/clock/fsl,imx8ulp-sim-lpav.yaml
new file mode 100644
index 000000000000..662e07528d76
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/fsl,imx8ulp-sim-lpav.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/fsl,imx8ulp-sim-lpav.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX8ULP LPAV System Integration Module (SIM)
+
+maintainers:
+ - Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
+
+description:
+ The i.MX8ULP LPAV subsystem contains a block control module known as
+ SIM LPAV, which offers functionalities such as clock gating or reset
+ line assertion/de-assertion.
+
+properties:
+ compatible:
+ const: fsl,imx8ulp-sim-lpav
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 3
+
+ clock-names:
+ items:
+ - const: bus
+ - const: core
+ - const: plat
+
+ '#clock-cells':
+ const: 1
+
+ '#reset-cells':
+ const: 1
+
+ mux-controller:
+ $ref: /schemas/mux/reg-mux.yaml#
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - '#clock-cells'
+ - '#reset-cells'
+ - mux-controller
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/imx8ulp-clock.h>
+
+ clock-controller@2da50000 {
+ compatible = "fsl,imx8ulp-sim-lpav";
+ reg = <0x2da50000 0x10000>;
+ clocks = <&cgc2 IMX8ULP_CLK_LPAV_BUS_DIV>,
+ <&cgc2 IMX8ULP_CLK_HIFI_DIVCORE>,
+ <&cgc2 IMX8ULP_CLK_HIFI_DIVPLAT>;
+ clock-names = "bus", "core", "plat";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+
+ mux-controller {
+ compatible = "reg-mux";
+ #mux-control-cells = <1>;
+ mux-reg-masks = <0x8 0x00000200>;
+ };
+ };
diff --git a/include/dt-bindings/clock/imx8ulp-clock.h b/include/dt-bindings/clock/imx8ulp-clock.h
index 827404fadf5c..c62d84d093a9 100644
--- a/include/dt-bindings/clock/imx8ulp-clock.h
+++ b/include/dt-bindings/clock/imx8ulp-clock.h
@@ -255,4 +255,9 @@
#define IMX8ULP_CLK_PCC5_END 56
+/* LPAV SIM */
+#define IMX8ULP_CLK_SIM_LPAV_HIFI_CORE 0
+#define IMX8ULP_CLK_SIM_LPAV_HIFI_PBCLK 1
+#define IMX8ULP_CLK_SIM_LPAV_HIFI_PLAT 2
+
#endif
diff --git a/include/dt-bindings/reset/fsl,imx8ulp-sim-lpav.h b/include/dt-bindings/reset/fsl,imx8ulp-sim-lpav.h
new file mode 100644
index 000000000000..adf95bb26d21
--- /dev/null
+++ b/include/dt-bindings/reset/fsl,imx8ulp-sim-lpav.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright 2025 NXP
+ */
+
+#ifndef DT_BINDING_RESET_IMX8ULP_SIM_LPAV_H
+#define DT_BINDING_RESET_IMX8ULP_SIM_LPAV_H
+
+#define IMX8ULP_SIM_LPAV_HIFI4_DSP_DBG_RST 0
+#define IMX8ULP_SIM_LPAV_HIFI4_DSP_RST 1
+#define IMX8ULP_SIM_LPAV_HIFI4_DSP_STALL 2
+#define IMX8ULP_SIM_LPAV_DSI_RST_BYTE_N 3
+#define IMX8ULP_SIM_LPAV_DSI_RST_ESC_N 4
+#define IMX8ULP_SIM_LPAV_DSI_RST_DPI_N 5
+
+#endif /* DT_BINDING_RESET_IMX8ULP_SIM_LPAV_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v3 3/8] clk: imx: add driver for imx8ulp's sim lpav
2025-10-29 13:52 [PATCH v3 0/8] Add support for i.MX8ULP's SIM LPAV Laurentiu Mihalcea
2025-10-29 13:52 ` [PATCH v3 1/8] reset: imx8mp-audiomix: Fix bad mask values Laurentiu Mihalcea
2025-10-29 13:52 ` [PATCH v3 2/8] dt-bindings: clock: document 8ULP's SIM LPAV Laurentiu Mihalcea
@ 2025-10-29 13:52 ` Laurentiu Mihalcea
2025-10-29 16:17 ` Frank Li
` (2 more replies)
2025-10-29 13:52 ` [PATCH v3 4/8] reset: imx8mp-audiomix: Drop unneeded macros Laurentiu Mihalcea
` (4 subsequent siblings)
7 siblings, 3 replies; 21+ messages in thread
From: Laurentiu Mihalcea @ 2025-10-29 13:52 UTC (permalink / raw)
To: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang
Cc: linux-clk, imx, devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
The i.MX8ULP System Integration Module (SIM) LPAV module is a block
control module found inside the LPAV subsystem, which offers some clock
gating options and reset line assertion/de-assertion capabilities.
Therefore, the clock gate management is supported by registering the
module's driver as a clock provider, while the reset capabilities are
managed via the auxiliary device API to allow the DT node to act as a
reset and clock provider.
Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
---
drivers/clk/imx/Makefile | 1 +
drivers/clk/imx/clk-imx8ulp-sim-lpav.c | 160 +++++++++++++++++++++++++
2 files changed, 161 insertions(+)
create mode 100644 drivers/clk/imx/clk-imx8ulp-sim-lpav.c
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 03f2b2a1ab63..208b46873a18 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -41,6 +41,7 @@ clk-imx-lpcg-scu-$(CONFIG_CLK_IMX8QXP) += clk-lpcg-scu.o clk-imx8qxp-lpcg.o
clk-imx-acm-$(CONFIG_CLK_IMX8QXP) = clk-imx8-acm.o
obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp.o
+obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp-sim-lpav.o
obj-$(CONFIG_CLK_IMX1) += clk-imx1.o
obj-$(CONFIG_CLK_IMX25) += clk-imx25.o
diff --git a/drivers/clk/imx/clk-imx8ulp-sim-lpav.c b/drivers/clk/imx/clk-imx8ulp-sim-lpav.c
new file mode 100644
index 000000000000..1614d9209734
--- /dev/null
+++ b/drivers/clk/imx/clk-imx8ulp-sim-lpav.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <dt-bindings/clock/imx8ulp-clock.h>
+
+#include <linux/auxiliary_bus.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define SYSCTRL0 0x8
+
+#define IMX8ULP_HIFI_CLK_GATE(gname, cname, pname, bidx) \
+ { \
+ .name = gname "_cg", \
+ .id = IMX8ULP_CLK_SIM_LPAV_HIFI_##cname, \
+ .parent = { .fw_name = pname }, \
+ .bit = bidx, \
+ }
+
+struct clk_imx8ulp_sim_lpav_data {
+ void __iomem *base;
+ struct regmap *regmap;
+ spinlock_t lock; /* shared by MUX, clock gate and reset */
+ unsigned long flags; /* for spinlock usage */
+ struct clk_hw_onecell_data clk_data; /* keep last */
+};
+
+struct clk_imx8ulp_sim_lpav_gate {
+ const char *name;
+ int id;
+ const struct clk_parent_data parent;
+ u8 bit;
+};
+
+static struct clk_imx8ulp_sim_lpav_gate gates[] = {
+ IMX8ULP_HIFI_CLK_GATE("hifi_core", CORE, "core", 17),
+ IMX8ULP_HIFI_CLK_GATE("hifi_pbclk", PBCLK, "bus", 18),
+ IMX8ULP_HIFI_CLK_GATE("hifi_plat", PLAT, "plat", 19)
+};
+
+static void clk_imx8ulp_sim_lpav_lock(void *arg) __acquires(&data->lock)
+{
+ struct clk_imx8ulp_sim_lpav_data *data = dev_get_drvdata(arg);
+
+ spin_lock_irqsave(&data->lock, data->flags);
+}
+
+static void clk_imx8ulp_sim_lpav_unlock(void *arg) __releases(&data->lock)
+{
+ struct clk_imx8ulp_sim_lpav_data *data = dev_get_drvdata(arg);
+
+ spin_unlock_irqrestore(&data->lock, data->flags);
+}
+
+static const struct regmap_config clk_imx8ulp_sim_lpav_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .lock = clk_imx8ulp_sim_lpav_lock,
+ .unlock = clk_imx8ulp_sim_lpav_unlock,
+};
+
+static int clk_imx8ulp_sim_lpav_probe(struct platform_device *pdev)
+{
+ struct clk_imx8ulp_sim_lpav_data *data;
+ struct regmap_config regmap_config;
+ struct auxiliary_device *adev;
+ struct clk_hw *hw;
+ int i, ret;
+
+ data = devm_kzalloc(&pdev->dev,
+ struct_size(data, clk_data.hws, ARRAY_SIZE(gates)),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, data);
+
+ memcpy(®map_config, &clk_imx8ulp_sim_lpav_regmap_cfg, sizeof(regmap_config));
+ regmap_config.lock_arg = &pdev->dev;
+
+ /*
+ * this lock is used directly by the clock gate and indirectly
+ * by the reset and mux controller via the regmap API
+ */
+ spin_lock_init(&data->lock);
+
+ data->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->base))
+ return dev_err_probe(&pdev->dev, PTR_ERR(data->base),
+ "failed to ioremap base\n");
+ /*
+ * although the clock gate doesn't use the regmap API to modify the
+ * registers, we still need the regmap because of the reset auxiliary
+ * driver and the MUX drivers, which use the parent device's regmap
+ */
+ data->regmap = devm_regmap_init_mmio(&pdev->dev, data->base, ®map_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(data->regmap),
+ "failed to initialize regmap\n");
+
+ data->clk_data.num = ARRAY_SIZE(gates);
+
+ for (i = 0; i < ARRAY_SIZE(gates); i++) {
+ hw = devm_clk_hw_register_gate_parent_data(&pdev->dev,
+ gates[i].name,
+ &gates[i].parent,
+ CLK_SET_RATE_PARENT,
+ data->base + SYSCTRL0,
+ gates[i].bit,
+ 0x0, &data->lock);
+ if (IS_ERR(hw))
+ return dev_err_probe(&pdev->dev, PTR_ERR(hw),
+ "failed to register %s gate\n",
+ gates[i].name);
+
+ data->clk_data.hws[i] = hw;
+ }
+
+ adev = devm_auxiliary_device_create(&pdev->dev, "reset", NULL);
+ if (!adev)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "failed to register aux reset\n");
+
+ ret = devm_of_clk_add_hw_provider(&pdev->dev,
+ of_clk_hw_onecell_get,
+ &data->clk_data);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to register clk hw provider\n");
+
+ /* used to probe MUX child device */
+ return devm_of_platform_populate(&pdev->dev);
+}
+
+static const struct of_device_id clk_imx8ulp_sim_lpav_of_match[] = {
+ { .compatible = "fsl,imx8ulp-sim-lpav" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clk_imx8ulp_sim_lpav_of_match);
+
+static struct platform_driver clk_imx8ulp_sim_lpav_driver = {
+ .probe = clk_imx8ulp_sim_lpav_probe,
+ .driver = {
+ .name = "clk-imx8ulp-sim-lpav",
+ .of_match_table = clk_imx8ulp_sim_lpav_of_match,
+ },
+};
+module_platform_driver(clk_imx8ulp_sim_lpav_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("i.MX8ULP LPAV System Integration Module (SIM) clock driver");
+MODULE_AUTHOR("Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>");
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v3 3/8] clk: imx: add driver for imx8ulp's sim lpav
2025-10-29 13:52 ` [PATCH v3 3/8] clk: imx: add driver for imx8ulp's sim lpav Laurentiu Mihalcea
@ 2025-10-29 16:17 ` Frank Li
2025-11-03 10:54 ` kernel test robot
2025-11-04 3:57 ` Peng Fan
2 siblings, 0 replies; 21+ messages in thread
From: Frank Li @ 2025-10-29 16:17 UTC (permalink / raw)
To: Laurentiu Mihalcea
Cc: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang, linux-clk, imx,
devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On Wed, Oct 29, 2025 at 06:52:24AM -0700, Laurentiu Mihalcea wrote:
> From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>
> The i.MX8ULP System Integration Module (SIM) LPAV module is a block
> control module found inside the LPAV subsystem, which offers some clock
> gating options and reset line assertion/de-assertion capabilities.
>
> Therefore, the clock gate management is supported by registering the
> module's driver as a clock provider, while the reset capabilities are
> managed via the auxiliary device API to allow the DT node to act as a
> reset and clock provider.
>
> Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
> ---
> drivers/clk/imx/Makefile | 1 +
> drivers/clk/imx/clk-imx8ulp-sim-lpav.c | 160 +++++++++++++++++++++++++
> 2 files changed, 161 insertions(+)
> create mode 100644 drivers/clk/imx/clk-imx8ulp-sim-lpav.c
>
> diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
> index 03f2b2a1ab63..208b46873a18 100644
> --- a/drivers/clk/imx/Makefile
> +++ b/drivers/clk/imx/Makefile
> @@ -41,6 +41,7 @@ clk-imx-lpcg-scu-$(CONFIG_CLK_IMX8QXP) += clk-lpcg-scu.o clk-imx8qxp-lpcg.o
> clk-imx-acm-$(CONFIG_CLK_IMX8QXP) = clk-imx8-acm.o
>
> obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp.o
> +obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp-sim-lpav.o
>
> obj-$(CONFIG_CLK_IMX1) += clk-imx1.o
> obj-$(CONFIG_CLK_IMX25) += clk-imx25.o
> diff --git a/drivers/clk/imx/clk-imx8ulp-sim-lpav.c b/drivers/clk/imx/clk-imx8ulp-sim-lpav.c
> new file mode 100644
> index 000000000000..1614d9209734
> --- /dev/null
> +++ b/drivers/clk/imx/clk-imx8ulp-sim-lpav.c
> @@ -0,0 +1,160 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 NXP
> + */
> +
> +#include <dt-bindings/clock/imx8ulp-clock.h>
> +
> +#include <linux/auxiliary_bus.h>
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#define SYSCTRL0 0x8
> +
> +#define IMX8ULP_HIFI_CLK_GATE(gname, cname, pname, bidx) \
> + { \
> + .name = gname "_cg", \
> + .id = IMX8ULP_CLK_SIM_LPAV_HIFI_##cname, \
> + .parent = { .fw_name = pname }, \
> + .bit = bidx, \
> + }
> +
> +struct clk_imx8ulp_sim_lpav_data {
> + void __iomem *base;
> + struct regmap *regmap;
> + spinlock_t lock; /* shared by MUX, clock gate and reset */
> + unsigned long flags; /* for spinlock usage */
> + struct clk_hw_onecell_data clk_data; /* keep last */
> +};
> +
> +struct clk_imx8ulp_sim_lpav_gate {
> + const char *name;
> + int id;
> + const struct clk_parent_data parent;
> + u8 bit;
> +};
> +
> +static struct clk_imx8ulp_sim_lpav_gate gates[] = {
> + IMX8ULP_HIFI_CLK_GATE("hifi_core", CORE, "core", 17),
> + IMX8ULP_HIFI_CLK_GATE("hifi_pbclk", PBCLK, "bus", 18),
> + IMX8ULP_HIFI_CLK_GATE("hifi_plat", PLAT, "plat", 19)
> +};
> +
> +static void clk_imx8ulp_sim_lpav_lock(void *arg) __acquires(&data->lock)
> +{
> + struct clk_imx8ulp_sim_lpav_data *data = dev_get_drvdata(arg);
> +
> + spin_lock_irqsave(&data->lock, data->flags);
> +}
> +
> +static void clk_imx8ulp_sim_lpav_unlock(void *arg) __releases(&data->lock)
> +{
> + struct clk_imx8ulp_sim_lpav_data *data = dev_get_drvdata(arg);
> +
> + spin_unlock_irqrestore(&data->lock, data->flags);
> +}
> +
> +static const struct regmap_config clk_imx8ulp_sim_lpav_regmap_cfg = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .lock = clk_imx8ulp_sim_lpav_lock,
> + .unlock = clk_imx8ulp_sim_lpav_unlock,
> +};
> +
> +static int clk_imx8ulp_sim_lpav_probe(struct platform_device *pdev)
> +{
> + struct clk_imx8ulp_sim_lpav_data *data;
> + struct regmap_config regmap_config;
> + struct auxiliary_device *adev;
> + struct clk_hw *hw;
> + int i, ret;
> +
> + data = devm_kzalloc(&pdev->dev,
> + struct_size(data, clk_data.hws, ARRAY_SIZE(gates)),
> + GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + dev_set_drvdata(&pdev->dev, data);
> +
> + memcpy(®map_config, &clk_imx8ulp_sim_lpav_regmap_cfg, sizeof(regmap_config));
> + regmap_config.lock_arg = &pdev->dev;
You copy clk_imx8ulp_sim_lpav_regmap_cfg to regmap_config and only once.
look like not neccessary to use clk_imx8ulp_sim_lpav_regmap_cfg at
all.
struct regmap_config regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.lock = clk_imx8ulp_sim_lpav_lock,
.unlock = clk_imx8ulp_sim_lpav_unlock,
.lock_arg = &pdev->dev;
};
it will be more straightforward.
Frank
> +
> + /*
> + * this lock is used directly by the clock gate and indirectly
> + * by the reset and mux controller via the regmap API
> + */
> + spin_lock_init(&data->lock);
> +
> + data->base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(data->base))
> + return dev_err_probe(&pdev->dev, PTR_ERR(data->base),
> + "failed to ioremap base\n");
> + /*
> + * although the clock gate doesn't use the regmap API to modify the
> + * registers, we still need the regmap because of the reset auxiliary
> + * driver and the MUX drivers, which use the parent device's regmap
> + */
> + data->regmap = devm_regmap_init_mmio(&pdev->dev, data->base, ®map_config);
> + if (IS_ERR(data->regmap))
> + return dev_err_probe(&pdev->dev, PTR_ERR(data->regmap),
> + "failed to initialize regmap\n");
> +
> + data->clk_data.num = ARRAY_SIZE(gates);
> +
> + for (i = 0; i < ARRAY_SIZE(gates); i++) {
> + hw = devm_clk_hw_register_gate_parent_data(&pdev->dev,
> + gates[i].name,
> + &gates[i].parent,
> + CLK_SET_RATE_PARENT,
> + data->base + SYSCTRL0,
> + gates[i].bit,
> + 0x0, &data->lock);
> + if (IS_ERR(hw))
> + return dev_err_probe(&pdev->dev, PTR_ERR(hw),
> + "failed to register %s gate\n",
> + gates[i].name);
> +
> + data->clk_data.hws[i] = hw;
> + }
> +
> + adev = devm_auxiliary_device_create(&pdev->dev, "reset", NULL);
> + if (!adev)
> + return dev_err_probe(&pdev->dev, -ENODEV,
> + "failed to register aux reset\n");
> +
> + ret = devm_of_clk_add_hw_provider(&pdev->dev,
> + of_clk_hw_onecell_get,
> + &data->clk_data);
> + if (ret)
> + return dev_err_probe(&pdev->dev, ret,
> + "failed to register clk hw provider\n");
> +
> + /* used to probe MUX child device */
> + return devm_of_platform_populate(&pdev->dev);
> +}
> +
> +static const struct of_device_id clk_imx8ulp_sim_lpav_of_match[] = {
> + { .compatible = "fsl,imx8ulp-sim-lpav" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, clk_imx8ulp_sim_lpav_of_match);
> +
> +static struct platform_driver clk_imx8ulp_sim_lpav_driver = {
> + .probe = clk_imx8ulp_sim_lpav_probe,
> + .driver = {
> + .name = "clk-imx8ulp-sim-lpav",
> + .of_match_table = clk_imx8ulp_sim_lpav_of_match,
> + },
> +};
> +module_platform_driver(clk_imx8ulp_sim_lpav_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("i.MX8ULP LPAV System Integration Module (SIM) clock driver");
> +MODULE_AUTHOR("Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>");
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v3 3/8] clk: imx: add driver for imx8ulp's sim lpav
2025-10-29 13:52 ` [PATCH v3 3/8] clk: imx: add driver for imx8ulp's sim lpav Laurentiu Mihalcea
2025-10-29 16:17 ` Frank Li
@ 2025-11-03 10:54 ` kernel test robot
2025-11-04 3:57 ` Peng Fan
2 siblings, 0 replies; 21+ messages in thread
From: kernel test robot @ 2025-11-03 10:54 UTC (permalink / raw)
To: Laurentiu Mihalcea, Abel Vesa, Peng Fan, Michael Turquette,
Stephen Boyd, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Shawn Guo, Fabio Estevam, Philipp Zabel, Daniel Baluta,
Shengjiu Wang
Cc: llvm, oe-kbuild-all, linux-clk, imx, devicetree, linux-arm-kernel,
linux-kernel, Pengutronix Kernel Team
Hi Laurentiu,
kernel test robot noticed the following build errors:
[auto build test ERROR on abelvesa/clk/imx]
[also build test ERROR on abelvesa/for-next linus/master pza/reset/next v6.18-rc4 next-20251103]
[cannot apply to pza/imx-drm/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Laurentiu-Mihalcea/reset-imx8mp-audiomix-Fix-bad-mask-values/20251029-225054
base: https://git.kernel.org/pub/scm/linux/kernel/git/abelvesa/linux.git clk/imx
patch link: https://lore.kernel.org/r/20251029135229.890-4-laurentiumihalcea111%40gmail.com
patch subject: [PATCH v3 3/8] clk: imx: add driver for imx8ulp's sim lpav
config: arm-randconfig-001-20251103 (https://download.01.org/0day-ci/archive/20251103/202511031829.Ju1yYI9x-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project d2625a438020ad35330cda29c3def102c1687b1b)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251103/202511031829.Ju1yYI9x-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511031829.Ju1yYI9x-lkp@intel.com/
All errors (new ones prefixed by >>, old ones prefixed by <<):
>> ERROR: modpost: "__devm_auxiliary_device_create" [drivers/clk/imx/clk-imx8ulp-sim-lpav.ko] undefined!
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v3 3/8] clk: imx: add driver for imx8ulp's sim lpav
2025-10-29 13:52 ` [PATCH v3 3/8] clk: imx: add driver for imx8ulp's sim lpav Laurentiu Mihalcea
2025-10-29 16:17 ` Frank Li
2025-11-03 10:54 ` kernel test robot
@ 2025-11-04 3:57 ` Peng Fan
2 siblings, 0 replies; 21+ messages in thread
From: Peng Fan @ 2025-11-04 3:57 UTC (permalink / raw)
To: Laurentiu Mihalcea
Cc: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang, linux-clk, imx,
devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On Wed, Oct 29, 2025 at 06:52:24AM -0700, Laurentiu Mihalcea wrote:
>From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>
>The i.MX8ULP System Integration Module (SIM) LPAV module is a block
>control module found inside the LPAV subsystem, which offers some clock
>gating options and reset line assertion/de-assertion capabilities.
>
>Therefore, the clock gate management is supported by registering the
>module's driver as a clock provider, while the reset capabilities are
>managed via the auxiliary device API to allow the DT node to act as a
>reset and clock provider.
>
>Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
Reviewed-by: Peng Fan <peng.fan@nxp.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v3 4/8] reset: imx8mp-audiomix: Drop unneeded macros
2025-10-29 13:52 [PATCH v3 0/8] Add support for i.MX8ULP's SIM LPAV Laurentiu Mihalcea
` (2 preceding siblings ...)
2025-10-29 13:52 ` [PATCH v3 3/8] clk: imx: add driver for imx8ulp's sim lpav Laurentiu Mihalcea
@ 2025-10-29 13:52 ` Laurentiu Mihalcea
2025-10-29 16:19 ` Frank Li
2025-10-29 13:52 ` [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API Laurentiu Mihalcea
` (3 subsequent siblings)
7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Mihalcea @ 2025-10-29 13:52 UTC (permalink / raw)
To: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang
Cc: linux-clk, imx, devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
The macros defining the mask values for the EARC, EARC PHY resets,
and the DSP RUN_STALL signal can be dropped as they are not and will
not be used anywhere else except to set the value of the "mask" field
from "struct imx8mp_reset_map". In this particular case, based on the
name of the "mask" field, you can already deduce what these values are
for, which is why defining macros for them doesn't offer any new
information, nor does it help with the code readability.
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
---
drivers/reset/reset-imx8mp-audiomix.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
index eceb37ff5dc5..e9643365a62c 100644
--- a/drivers/reset/reset-imx8mp-audiomix.c
+++ b/drivers/reset/reset-imx8mp-audiomix.c
@@ -14,11 +14,7 @@
#include <linux/reset-controller.h>
#define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
-#define IMX8MP_AUDIOMIX_EARC_RESET_MASK BIT(0)
-#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK BIT(1)
-
#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET 0x108
-#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK BIT(5)
struct imx8mp_reset_map {
unsigned int offset;
@@ -29,17 +25,17 @@ struct imx8mp_reset_map {
static const struct imx8mp_reset_map reset_map[] = {
[IMX8MP_AUDIOMIX_EARC_RESET] = {
.offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
- .mask = IMX8MP_AUDIOMIX_EARC_RESET_MASK,
+ .mask = BIT(0),
.active_low = true,
},
[IMX8MP_AUDIOMIX_EARC_PHY_RESET] = {
.offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
- .mask = IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK,
+ .mask = BIT(1),
.active_low = true,
},
[IMX8MP_AUDIOMIX_DSP_RUNSTALL] = {
.offset = IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET,
- .mask = IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK,
+ .mask = BIT(5),
.active_low = false,
},
};
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v3 4/8] reset: imx8mp-audiomix: Drop unneeded macros
2025-10-29 13:52 ` [PATCH v3 4/8] reset: imx8mp-audiomix: Drop unneeded macros Laurentiu Mihalcea
@ 2025-10-29 16:19 ` Frank Li
0 siblings, 0 replies; 21+ messages in thread
From: Frank Li @ 2025-10-29 16:19 UTC (permalink / raw)
To: Laurentiu Mihalcea
Cc: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang, linux-clk, imx,
devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On Wed, Oct 29, 2025 at 06:52:25AM -0700, Laurentiu Mihalcea wrote:
> From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>
> The macros defining the mask values for the EARC, EARC PHY resets,
> and the DSP RUN_STALL signal can be dropped as they are not and will
> not be used anywhere else except to set the value of the "mask" field
> from "struct imx8mp_reset_map". In this particular case, based on the
> name of the "mask" field, you can already deduce what these values are
> for, which is why defining macros for them doesn't offer any new
> information, nor does it help with the code readability.
>
> Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
> Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> ---
> drivers/reset/reset-imx8mp-audiomix.c | 10 +++-------
> 1 file changed, 3 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
> index eceb37ff5dc5..e9643365a62c 100644
> --- a/drivers/reset/reset-imx8mp-audiomix.c
> +++ b/drivers/reset/reset-imx8mp-audiomix.c
> @@ -14,11 +14,7 @@
> #include <linux/reset-controller.h>
>
> #define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
> -#define IMX8MP_AUDIOMIX_EARC_RESET_MASK BIT(0)
> -#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK BIT(1)
> -
> #define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET 0x108
> -#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK BIT(5)
>
> struct imx8mp_reset_map {
> unsigned int offset;
> @@ -29,17 +25,17 @@ struct imx8mp_reset_map {
> static const struct imx8mp_reset_map reset_map[] = {
> [IMX8MP_AUDIOMIX_EARC_RESET] = {
> .offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
> - .mask = IMX8MP_AUDIOMIX_EARC_RESET_MASK,
> + .mask = BIT(0),
> .active_low = true,
> },
> [IMX8MP_AUDIOMIX_EARC_PHY_RESET] = {
> .offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
> - .mask = IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK,
> + .mask = BIT(1),
> .active_low = true,
> },
> [IMX8MP_AUDIOMIX_DSP_RUNSTALL] = {
> .offset = IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET,
> - .mask = IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK,
> + .mask = BIT(5),
> .active_low = false,
> },
> };
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API
2025-10-29 13:52 [PATCH v3 0/8] Add support for i.MX8ULP's SIM LPAV Laurentiu Mihalcea
` (3 preceding siblings ...)
2025-10-29 13:52 ` [PATCH v3 4/8] reset: imx8mp-audiomix: Drop unneeded macros Laurentiu Mihalcea
@ 2025-10-29 13:52 ` Laurentiu Mihalcea
2025-10-29 16:37 ` Frank Li
2025-10-30 13:06 ` Philipp Zabel
2025-10-29 13:52 ` [PATCH v3 6/8] reset: imx8mp-audiomix: Extend the driver usage Laurentiu Mihalcea
` (2 subsequent siblings)
7 siblings, 2 replies; 21+ messages in thread
From: Laurentiu Mihalcea @ 2025-10-29 13:52 UTC (permalink / raw)
To: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang
Cc: linux-clk, imx, devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
As far as the Linux kernel is concerned, block devices such as i.MX8MP's
AUDIOMIX block control or i.MX8ULP's SIM LPAV can simultaneously act as
clock controllers, reset controllers or mux controllers. Since these IPs
offer different functionalities through different subsystem APIs, it's
important to make sure that the register R-M-W cycles are performed under
the same lock across all subsystem APIs. This will ensure that registers
will not end up with the wrong values because of race conditions (e.g.
clock consumer tries to update block control register A, while, at the
same time, reset consumer tries to update the same block control register).
However, the aforementioned race conditions will only impact block control
IPs which use the same register for multiple functionalities. For example,
i.MX8MP's AUDIOMIX block control IP provides clock gating functionalities
and reset control functionalities through different registers. This is why
the current approach (i.e. clock control and reset control work using
different locks) has worked well so far.
Since we want to extend this driver to be usable for i.MX8ULP's SIM LPAV
block control IP, we need to make sure that clock control, reset control,
and mux control APIs use the same lock since all of these functionalities
are performed using the SYSCTRL0 register.
To do so, we need to switch to the regmap API and, if possible, use the
parent device's regmap, which, in the case of i.MX8ULP, will be the clock
controller. This way, we can make sure that the clock gates and the reset
controller will use the same lock to perform the register R-M-W cycles.
This change will also work fine for cases where we don't really need to
share the lock across multiple APIs (e.g. i.MX8MP's AUDIOMIX block
control) since regmap will take care of the locking we were previously
explicitly performing in the driver.
The transition to the regmap API also involves some cleanup. Specifically,
we can make use of devres to unmap the device's memory and get rid of the
memory mapping-related error paths and the remove() function altogether.
Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
---
drivers/reset/reset-imx8mp-audiomix.c | 91 +++++++++++++++++----------
1 file changed, 57 insertions(+), 34 deletions(-)
diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
index e9643365a62c..3f6d11270918 100644
--- a/drivers/reset/reset-imx8mp-audiomix.c
+++ b/drivers/reset/reset-imx8mp-audiomix.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/regmap.h>
#include <linux/reset-controller.h>
#define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
@@ -42,8 +43,8 @@ static const struct imx8mp_reset_map reset_map[] = {
struct imx8mp_audiomix_reset {
struct reset_controller_dev rcdev;
- spinlock_t lock; /* protect register read-modify-write cycle */
void __iomem *base;
+ struct regmap *regmap;
};
static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)
@@ -55,26 +56,15 @@ static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);
- void __iomem *reg_addr = priv->base;
- unsigned int mask, offset, active_low;
- unsigned long reg, flags;
+ unsigned int mask, offset, active_low, shift, val;
mask = reset_map[id].mask;
offset = reset_map[id].offset;
active_low = reset_map[id].active_low;
+ shift = ffs(mask) - 1;
+ val = (active_low ^ assert) << shift;
- spin_lock_irqsave(&priv->lock, flags);
-
- reg = readl(reg_addr + offset);
- if (active_low ^ assert)
- reg |= mask;
- else
- reg &= ~mask;
- writel(reg, reg_addr + offset);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return 0;
+ return regmap_update_bits(priv->regmap, offset, mask, val);
}
static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,
@@ -94,6 +84,50 @@ static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
.deassert = imx8mp_audiomix_reset_deassert,
};
+static const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+/* assumption: registered only if not using parent regmap */
+static void imx8mp_audiomix_reset_iounmap(void *data)
+{
+ struct imx8mp_audiomix_reset *priv = dev_get_drvdata(data);
+
+ iounmap(priv->base);
+}
+
+/* assumption: dev_set_drvdata() is called before this */
+static int imx8mp_audiomix_reset_get_regmap(struct device *dev)
+{
+ struct imx8mp_audiomix_reset *priv;
+ int ret;
+
+ priv = dev_get_drvdata(dev);
+
+ /* try to use the parent's regmap */
+ priv->regmap = dev_get_regmap(dev->parent, NULL);
+ if (priv->regmap)
+ return 0;
+
+ /* ... if that's not possible then initialize the regmap right now */
+ priv->base = of_iomap(dev->parent->of_node, 0);
+ if (!priv->base)
+ return dev_err_probe(dev, -ENOMEM, "failed to iomap address space\n");
+
+ ret = devm_add_action_or_reset(dev, imx8mp_audiomix_reset_iounmap, dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register action\n");
+
+ priv->regmap = devm_regmap_init_mmio(dev, priv->base, ®map_config);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap),
+ "failed to initialize regmap\n");
+
+ return 0;
+}
+
static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
@@ -105,36 +139,26 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
if (!priv)
return -ENOMEM;
- spin_lock_init(&priv->lock);
-
priv->rcdev.owner = THIS_MODULE;
priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
priv->rcdev.of_node = dev->parent->of_node;
priv->rcdev.dev = dev;
priv->rcdev.of_reset_n_cells = 1;
- priv->base = of_iomap(dev->parent->of_node, 0);
- if (!priv->base)
- return -ENOMEM;
+ /* keep before call to imx8mp_audiomix_reset_init_regmap() */
dev_set_drvdata(dev, priv);
+ ret = imx8mp_audiomix_reset_get_regmap(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get regmap\n");
+
ret = devm_reset_controller_register(dev, &priv->rcdev);
if (ret)
- goto out_unmap;
+ return dev_err_probe(dev, ret,
+ "failed to register reset controller\n");
return 0;
-
-out_unmap:
- iounmap(priv->base);
- return ret;
-}
-
-static void imx8mp_audiomix_reset_remove(struct auxiliary_device *adev)
-{
- struct imx8mp_audiomix_reset *priv = dev_get_drvdata(&adev->dev);
-
- iounmap(priv->base);
}
static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
@@ -147,7 +171,6 @@ MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
static struct auxiliary_driver imx8mp_audiomix_reset_driver = {
.probe = imx8mp_audiomix_reset_probe,
- .remove = imx8mp_audiomix_reset_remove,
.id_table = imx8mp_audiomix_reset_ids,
};
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API
2025-10-29 13:52 ` [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API Laurentiu Mihalcea
@ 2025-10-29 16:37 ` Frank Li
2025-10-29 20:51 ` Laurentiu Mihalcea
2025-10-30 13:06 ` Philipp Zabel
1 sibling, 1 reply; 21+ messages in thread
From: Frank Li @ 2025-10-29 16:37 UTC (permalink / raw)
To: Laurentiu Mihalcea
Cc: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang, linux-clk, imx,
devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On Wed, Oct 29, 2025 at 06:52:26AM -0700, Laurentiu Mihalcea wrote:
> From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>
> As far as the Linux kernel is concerned, block devices such as i.MX8MP's
> AUDIOMIX block control or i.MX8ULP's SIM LPAV can simultaneously act as
> clock controllers, reset controllers or mux controllers. Since these IPs
> offer different functionalities through different subsystem APIs, it's
> important to make sure that the register R-M-W cycles are performed under
> the same lock across all subsystem APIs. This will ensure that registers
> will not end up with the wrong values because of race conditions (e.g.
> clock consumer tries to update block control register A, while, at the
> same time, reset consumer tries to update the same block control register).
>
> However, the aforementioned race conditions will only impact block control
> IPs which use the same register for multiple functionalities. For example,
> i.MX8MP's AUDIOMIX block control IP provides clock gating functionalities
> and reset control functionalities through different registers. This is why
> the current approach (i.e. clock control and reset control work using
> different locks) has worked well so far.
>
> Since we want to extend this driver to be usable for i.MX8ULP's SIM LPAV
> block control IP, we need to make sure that clock control, reset control,
> and mux control APIs use the same lock since all of these functionalities
> are performed using the SYSCTRL0 register.
>
> To do so, we need to switch to the regmap API and, if possible, use the
> parent device's regmap, which, in the case of i.MX8ULP, will be the clock
> controller. This way, we can make sure that the clock gates and the reset
> controller will use the same lock to perform the register R-M-W cycles.
>
> This change will also work fine for cases where we don't really need to
> share the lock across multiple APIs (e.g. i.MX8MP's AUDIOMIX block
> control) since regmap will take care of the locking we were previously
> explicitly performing in the driver.
>
> The transition to the regmap API also involves some cleanup. Specifically,
> we can make use of devres to unmap the device's memory and get rid of the
> memory mapping-related error paths and the remove() function altogether.
Can you simpifly commit message? The key points are 1: lock, 2: nice API.
like regmap_update_bits().
Switch to using the regmap API to simplify register access. The regmap
infrastructure provides synchronized register access and richer helpers
such as regmap_update_bits().
>
> Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
> ---
> drivers/reset/reset-imx8mp-audiomix.c | 91 +++++++++++++++++----------
> 1 file changed, 57 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
> index e9643365a62c..3f6d11270918 100644
> --- a/drivers/reset/reset-imx8mp-audiomix.c
> +++ b/drivers/reset/reset-imx8mp-audiomix.c
> @@ -11,6 +11,7 @@
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/of_address.h>
> +#include <linux/regmap.h>
> #include <linux/reset-controller.h>
>
> #define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
> @@ -42,8 +43,8 @@ static const struct imx8mp_reset_map reset_map[] = {
>
> struct imx8mp_audiomix_reset {
> struct reset_controller_dev rcdev;
> - spinlock_t lock; /* protect register read-modify-write cycle */
> void __iomem *base;
> + struct regmap *regmap;
> };
>
> static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)
> @@ -55,26 +56,15 @@ static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev,
> unsigned long id, bool assert)
> {
> struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);
> - void __iomem *reg_addr = priv->base;
> - unsigned int mask, offset, active_low;
> - unsigned long reg, flags;
> + unsigned int mask, offset, active_low, shift, val;
>
> mask = reset_map[id].mask;
> offset = reset_map[id].offset;
> active_low = reset_map[id].active_low;
> + shift = ffs(mask) - 1;
> + val = (active_low ^ assert) << shift;
>
> - spin_lock_irqsave(&priv->lock, flags);
> -
> - reg = readl(reg_addr + offset);
> - if (active_low ^ assert)
> - reg |= mask;
> - else
> - reg &= ~mask;
> - writel(reg, reg_addr + offset);
> -
> - spin_unlock_irqrestore(&priv->lock, flags);
> -
> - return 0;
> + return regmap_update_bits(priv->regmap, offset, mask, val);
> }
>
> static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,
> @@ -94,6 +84,50 @@ static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
> .deassert = imx8mp_audiomix_reset_deassert,
> };
>
> +static const struct regmap_config regmap_config = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> +};
> +
> +/* assumption: registered only if not using parent regmap */
> +static void imx8mp_audiomix_reset_iounmap(void *data)
> +{
> + struct imx8mp_audiomix_reset *priv = dev_get_drvdata(data);
> +
> + iounmap(priv->base);
> +}
> +
> +/* assumption: dev_set_drvdata() is called before this */
> +static int imx8mp_audiomix_reset_get_regmap(struct device *dev)
> +{
> + struct imx8mp_audiomix_reset *priv;
> + int ret;
> +
> + priv = dev_get_drvdata(dev);
> +
> + /* try to use the parent's regmap */
> + priv->regmap = dev_get_regmap(dev->parent, NULL);
> + if (priv->regmap)
> + return 0;
> +
> + /* ... if that's not possible then initialize the regmap right now */
> + priv->base = of_iomap(dev->parent->of_node, 0);
Not sure why need map parent devices's ioresource here. You'd better use
regmap_attach_dev() at parent devices, so dev_get_regmap() will get it.
Frank
> + if (!priv->base)
> + return dev_err_probe(dev, -ENOMEM, "failed to iomap address space\n");
> +
> + ret = devm_add_action_or_reset(dev, imx8mp_audiomix_reset_iounmap, dev);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to register action\n");
> +
> + priv->regmap = devm_regmap_init_mmio(dev, priv->base, ®map_config);
> + if (IS_ERR(priv->regmap))
> + return dev_err_probe(dev, PTR_ERR(priv->regmap),
> + "failed to initialize regmap\n");
> +
> + return 0;
> +}
> +
> static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
> const struct auxiliary_device_id *id)
> {
> @@ -105,36 +139,26 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
> if (!priv)
> return -ENOMEM;
>
> - spin_lock_init(&priv->lock);
> -
> priv->rcdev.owner = THIS_MODULE;
> priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
> priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
> priv->rcdev.of_node = dev->parent->of_node;
> priv->rcdev.dev = dev;
> priv->rcdev.of_reset_n_cells = 1;
> - priv->base = of_iomap(dev->parent->of_node, 0);
> - if (!priv->base)
> - return -ENOMEM;
>
> + /* keep before call to imx8mp_audiomix_reset_init_regmap() */
> dev_set_drvdata(dev, priv);
>
> + ret = imx8mp_audiomix_reset_get_regmap(dev);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to get regmap\n");
> +
> ret = devm_reset_controller_register(dev, &priv->rcdev);
> if (ret)
> - goto out_unmap;
> + return dev_err_probe(dev, ret,
> + "failed to register reset controller\n");
>
> return 0;
> -
> -out_unmap:
> - iounmap(priv->base);
> - return ret;
> -}
> -
> -static void imx8mp_audiomix_reset_remove(struct auxiliary_device *adev)
> -{
> - struct imx8mp_audiomix_reset *priv = dev_get_drvdata(&adev->dev);
> -
> - iounmap(priv->base);
> }
>
> static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
> @@ -147,7 +171,6 @@ MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
>
> static struct auxiliary_driver imx8mp_audiomix_reset_driver = {
> .probe = imx8mp_audiomix_reset_probe,
> - .remove = imx8mp_audiomix_reset_remove,
> .id_table = imx8mp_audiomix_reset_ids,
> };
>
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API
2025-10-29 16:37 ` Frank Li
@ 2025-10-29 20:51 ` Laurentiu Mihalcea
2025-10-31 18:34 ` Frank Li
0 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Mihalcea @ 2025-10-29 20:51 UTC (permalink / raw)
To: Frank Li
Cc: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang, linux-clk, imx,
devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On 10/29/2025 6:37 PM, Frank Li wrote:
> On Wed, Oct 29, 2025 at 06:52:26AM -0700, Laurentiu Mihalcea wrote:
>> From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>>
>> As far as the Linux kernel is concerned, block devices such as i.MX8MP's
>> AUDIOMIX block control or i.MX8ULP's SIM LPAV can simultaneously act as
>> clock controllers, reset controllers or mux controllers. Since these IPs
>> offer different functionalities through different subsystem APIs, it's
>> important to make sure that the register R-M-W cycles are performed under
>> the same lock across all subsystem APIs. This will ensure that registers
>> will not end up with the wrong values because of race conditions (e.g.
>> clock consumer tries to update block control register A, while, at the
>> same time, reset consumer tries to update the same block control register).
>>
>> However, the aforementioned race conditions will only impact block control
>> IPs which use the same register for multiple functionalities. For example,
>> i.MX8MP's AUDIOMIX block control IP provides clock gating functionalities
>> and reset control functionalities through different registers. This is why
>> the current approach (i.e. clock control and reset control work using
>> different locks) has worked well so far.
>>
>> Since we want to extend this driver to be usable for i.MX8ULP's SIM LPAV
>> block control IP, we need to make sure that clock control, reset control,
>> and mux control APIs use the same lock since all of these functionalities
>> are performed using the SYSCTRL0 register.
>>
>> To do so, we need to switch to the regmap API and, if possible, use the
>> parent device's regmap, which, in the case of i.MX8ULP, will be the clock
>> controller. This way, we can make sure that the clock gates and the reset
>> controller will use the same lock to perform the register R-M-W cycles.
>>
>> This change will also work fine for cases where we don't really need to
>> share the lock across multiple APIs (e.g. i.MX8MP's AUDIOMIX block
>> control) since regmap will take care of the locking we were previously
>> explicitly performing in the driver.
>>
>> The transition to the regmap API also involves some cleanup. Specifically,
>> we can make use of devres to unmap the device's memory and get rid of the
>> memory mapping-related error paths and the remove() function altogether.
> Can you simpifly commit message? The key points are 1: lock, 2: nice API.
> like regmap_update_bits().
>
> Switch to using the regmap API to simplify register access. The regmap
> infrastructure provides synchronized register access and richer helpers
> such as regmap_update_bits().
this change isn't about simplifying the register access, nor is it about synchronizing
the register access. Previous version was already doing that.
the key takeaway here is we want to have the SAME lock used across different APIs.
regmap is a way to do that.
>
>> Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>> ---
>> drivers/reset/reset-imx8mp-audiomix.c | 91 +++++++++++++++++----------
>> 1 file changed, 57 insertions(+), 34 deletions(-)
>>
>> diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
>> index e9643365a62c..3f6d11270918 100644
>> --- a/drivers/reset/reset-imx8mp-audiomix.c
>> +++ b/drivers/reset/reset-imx8mp-audiomix.c
>> @@ -11,6 +11,7 @@
>> #include <linux/module.h>
>> #include <linux/of.h>
>> #include <linux/of_address.h>
>> +#include <linux/regmap.h>
>> #include <linux/reset-controller.h>
>>
>> #define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
>> @@ -42,8 +43,8 @@ static const struct imx8mp_reset_map reset_map[] = {
>>
>> struct imx8mp_audiomix_reset {
>> struct reset_controller_dev rcdev;
>> - spinlock_t lock; /* protect register read-modify-write cycle */
>> void __iomem *base;
>> + struct regmap *regmap;
>> };
>>
>> static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)
>> @@ -55,26 +56,15 @@ static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev,
>> unsigned long id, bool assert)
>> {
>> struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);
>> - void __iomem *reg_addr = priv->base;
>> - unsigned int mask, offset, active_low;
>> - unsigned long reg, flags;
>> + unsigned int mask, offset, active_low, shift, val;
>>
>> mask = reset_map[id].mask;
>> offset = reset_map[id].offset;
>> active_low = reset_map[id].active_low;
>> + shift = ffs(mask) - 1;
>> + val = (active_low ^ assert) << shift;
>>
>> - spin_lock_irqsave(&priv->lock, flags);
>> -
>> - reg = readl(reg_addr + offset);
>> - if (active_low ^ assert)
>> - reg |= mask;
>> - else
>> - reg &= ~mask;
>> - writel(reg, reg_addr + offset);
>> -
>> - spin_unlock_irqrestore(&priv->lock, flags);
>> -
>> - return 0;
>> + return regmap_update_bits(priv->regmap, offset, mask, val);
>> }
>>
>> static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,
>> @@ -94,6 +84,50 @@ static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
>> .deassert = imx8mp_audiomix_reset_deassert,
>> };
>>
>> +static const struct regmap_config regmap_config = {
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = 4,
>> +};
>> +
>> +/* assumption: registered only if not using parent regmap */
>> +static void imx8mp_audiomix_reset_iounmap(void *data)
>> +{
>> + struct imx8mp_audiomix_reset *priv = dev_get_drvdata(data);
>> +
>> + iounmap(priv->base);
>> +}
>> +
>> +/* assumption: dev_set_drvdata() is called before this */
>> +static int imx8mp_audiomix_reset_get_regmap(struct device *dev)
>> +{
>> + struct imx8mp_audiomix_reset *priv;
>> + int ret;
>> +
>> + priv = dev_get_drvdata(dev);
>> +
>> + /* try to use the parent's regmap */
>> + priv->regmap = dev_get_regmap(dev->parent, NULL);
>> + if (priv->regmap)
>> + return 0;
>> +
>> + /* ... if that's not possible then initialize the regmap right now */
>> + priv->base = of_iomap(dev->parent->of_node, 0);
> Not sure why need map parent devices's ioresource here. You'd better use
> regmap_attach_dev() at parent devices, so dev_get_regmap() will get it.
why would we want to force the parent device to use regmap if it doesn't need to?
>
> Frank
>
>> + if (!priv->base)
>> + return dev_err_probe(dev, -ENOMEM, "failed to iomap address space\n");
>> +
>> + ret = devm_add_action_or_reset(dev, imx8mp_audiomix_reset_iounmap, dev);
>> + if (ret)
>> + return dev_err_probe(dev, ret, "failed to register action\n");
>> +
>> + priv->regmap = devm_regmap_init_mmio(dev, priv->base, ®map_config);
>> + if (IS_ERR(priv->regmap))
>> + return dev_err_probe(dev, PTR_ERR(priv->regmap),
>> + "failed to initialize regmap\n");
>> +
>> + return 0;
>> +}
>> +
>> static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
>> const struct auxiliary_device_id *id)
>> {
>> @@ -105,36 +139,26 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
>> if (!priv)
>> return -ENOMEM;
>>
>> - spin_lock_init(&priv->lock);
>> -
>> priv->rcdev.owner = THIS_MODULE;
>> priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
>> priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
>> priv->rcdev.of_node = dev->parent->of_node;
>> priv->rcdev.dev = dev;
>> priv->rcdev.of_reset_n_cells = 1;
>> - priv->base = of_iomap(dev->parent->of_node, 0);
>> - if (!priv->base)
>> - return -ENOMEM;
>>
>> + /* keep before call to imx8mp_audiomix_reset_init_regmap() */
>> dev_set_drvdata(dev, priv);
>>
>> + ret = imx8mp_audiomix_reset_get_regmap(dev);
>> + if (ret)
>> + return dev_err_probe(dev, ret, "failed to get regmap\n");
>> +
>> ret = devm_reset_controller_register(dev, &priv->rcdev);
>> if (ret)
>> - goto out_unmap;
>> + return dev_err_probe(dev, ret,
>> + "failed to register reset controller\n");
>>
>> return 0;
>> -
>> -out_unmap:
>> - iounmap(priv->base);
>> - return ret;
>> -}
>> -
>> -static void imx8mp_audiomix_reset_remove(struct auxiliary_device *adev)
>> -{
>> - struct imx8mp_audiomix_reset *priv = dev_get_drvdata(&adev->dev);
>> -
>> - iounmap(priv->base);
>> }
>>
>> static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
>> @@ -147,7 +171,6 @@ MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
>>
>> static struct auxiliary_driver imx8mp_audiomix_reset_driver = {
>> .probe = imx8mp_audiomix_reset_probe,
>> - .remove = imx8mp_audiomix_reset_remove,
>> .id_table = imx8mp_audiomix_reset_ids,
>> };
>>
>> --
>> 2.43.0
>>
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API
2025-10-29 20:51 ` Laurentiu Mihalcea
@ 2025-10-31 18:34 ` Frank Li
0 siblings, 0 replies; 21+ messages in thread
From: Frank Li @ 2025-10-31 18:34 UTC (permalink / raw)
To: Laurentiu Mihalcea
Cc: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang, linux-clk, imx,
devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On Wed, Oct 29, 2025 at 10:51:17PM +0200, Laurentiu Mihalcea wrote:
>
> On 10/29/2025 6:37 PM, Frank Li wrote:
> > On Wed, Oct 29, 2025 at 06:52:26AM -0700, Laurentiu Mihalcea wrote:
> >> From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
> >>
> >> As far as the Linux kernel is concerned, block devices such as i.MX8MP's
> >> AUDIOMIX block control or i.MX8ULP's SIM LPAV can simultaneously act as
> >> clock controllers, reset controllers or mux controllers. Since these IPs
> >> offer different functionalities through different subsystem APIs, it's
> >> important to make sure that the register R-M-W cycles are performed under
> >> the same lock across all subsystem APIs. This will ensure that registers
> >> will not end up with the wrong values because of race conditions (e.g.
> >> clock consumer tries to update block control register A, while, at the
> >> same time, reset consumer tries to update the same block control register).
> >>
> >> However, the aforementioned race conditions will only impact block control
> >> IPs which use the same register for multiple functionalities. For example,
> >> i.MX8MP's AUDIOMIX block control IP provides clock gating functionalities
> >> and reset control functionalities through different registers. This is why
> >> the current approach (i.e. clock control and reset control work using
> >> different locks) has worked well so far.
> >>
> >> Since we want to extend this driver to be usable for i.MX8ULP's SIM LPAV
> >> block control IP, we need to make sure that clock control, reset control,
> >> and mux control APIs use the same lock since all of these functionalities
> >> are performed using the SYSCTRL0 register.
> >>
> >> To do so, we need to switch to the regmap API and, if possible, use the
> >> parent device's regmap, which, in the case of i.MX8ULP, will be the clock
> >> controller. This way, we can make sure that the clock gates and the reset
> >> controller will use the same lock to perform the register R-M-W cycles.
> >>
> >> This change will also work fine for cases where we don't really need to
> >> share the lock across multiple APIs (e.g. i.MX8MP's AUDIOMIX block
> >> control) since regmap will take care of the locking we were previously
> >> explicitly performing in the driver.
> >>
> >> The transition to the regmap API also involves some cleanup. Specifically,
> >> we can make use of devres to unmap the device's memory and get rid of the
> >> memory mapping-related error paths and the remove() function altogether.
> > Can you simpifly commit message? The key points are 1: lock, 2: nice API.
> > like regmap_update_bits().
> >
> > Switch to using the regmap API to simplify register access. The regmap
> > infrastructure provides synchronized register access and richer helpers
> > such as regmap_update_bits().
>
>
> this change isn't about simplifying the register access, nor is it about synchronizing
>
> the register access. Previous version was already doing that.
>
>
> the key takeaway here is we want to have the SAME lock used across different APIs.
>
> regmap is a way to do that.
This sentense already be enough descript current situation, you write 6 big
paragaph.
Frank
>
>
> >
> >> Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
> >> ---
> >> drivers/reset/reset-imx8mp-audiomix.c | 91 +++++++++++++++++----------
> >> 1 file changed, 57 insertions(+), 34 deletions(-)
> >>
> >> diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
> >> index e9643365a62c..3f6d11270918 100644
> >> --- a/drivers/reset/reset-imx8mp-audiomix.c
> >> +++ b/drivers/reset/reset-imx8mp-audiomix.c
> >> @@ -11,6 +11,7 @@
> >> #include <linux/module.h>
> >> #include <linux/of.h>
> >> #include <linux/of_address.h>
> >> +#include <linux/regmap.h>
> >> #include <linux/reset-controller.h>
> >>
> >> #define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
> >> @@ -42,8 +43,8 @@ static const struct imx8mp_reset_map reset_map[] = {
> >>
> >> struct imx8mp_audiomix_reset {
> >> struct reset_controller_dev rcdev;
> >> - spinlock_t lock; /* protect register read-modify-write cycle */
> >> void __iomem *base;
> >> + struct regmap *regmap;
> >> };
> >>
> >> static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)
> >> @@ -55,26 +56,15 @@ static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev,
> >> unsigned long id, bool assert)
> >> {
> >> struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);
> >> - void __iomem *reg_addr = priv->base;
> >> - unsigned int mask, offset, active_low;
> >> - unsigned long reg, flags;
> >> + unsigned int mask, offset, active_low, shift, val;
> >>
> >> mask = reset_map[id].mask;
> >> offset = reset_map[id].offset;
> >> active_low = reset_map[id].active_low;
> >> + shift = ffs(mask) - 1;
> >> + val = (active_low ^ assert) << shift;
> >>
> >> - spin_lock_irqsave(&priv->lock, flags);
> >> -
> >> - reg = readl(reg_addr + offset);
> >> - if (active_low ^ assert)
> >> - reg |= mask;
> >> - else
> >> - reg &= ~mask;
> >> - writel(reg, reg_addr + offset);
> >> -
> >> - spin_unlock_irqrestore(&priv->lock, flags);
> >> -
> >> - return 0;
> >> + return regmap_update_bits(priv->regmap, offset, mask, val);
> >> }
> >>
> >> static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,
> >> @@ -94,6 +84,50 @@ static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
> >> .deassert = imx8mp_audiomix_reset_deassert,
> >> };
> >>
> >> +static const struct regmap_config regmap_config = {
> >> + .reg_bits = 32,
> >> + .val_bits = 32,
> >> + .reg_stride = 4,
> >> +};
> >> +
> >> +/* assumption: registered only if not using parent regmap */
> >> +static void imx8mp_audiomix_reset_iounmap(void *data)
> >> +{
> >> + struct imx8mp_audiomix_reset *priv = dev_get_drvdata(data);
> >> +
> >> + iounmap(priv->base);
> >> +}
> >> +
> >> +/* assumption: dev_set_drvdata() is called before this */
> >> +static int imx8mp_audiomix_reset_get_regmap(struct device *dev)
> >> +{
> >> + struct imx8mp_audiomix_reset *priv;
> >> + int ret;
> >> +
> >> + priv = dev_get_drvdata(dev);
> >> +
> >> + /* try to use the parent's regmap */
> >> + priv->regmap = dev_get_regmap(dev->parent, NULL);
> >> + if (priv->regmap)
> >> + return 0;
> >> +
> >> + /* ... if that's not possible then initialize the regmap right now */
> >> + priv->base = of_iomap(dev->parent->of_node, 0);
> > Not sure why need map parent devices's ioresource here. You'd better use
> > regmap_attach_dev() at parent devices, so dev_get_regmap() will get it.
>
>
> why would we want to force the parent device to use regmap if it doesn't need to?
>
>
> >
> > Frank
> >
> >> + if (!priv->base)
> >> + return dev_err_probe(dev, -ENOMEM, "failed to iomap address space\n");
> >> +
> >> + ret = devm_add_action_or_reset(dev, imx8mp_audiomix_reset_iounmap, dev);
> >> + if (ret)
> >> + return dev_err_probe(dev, ret, "failed to register action\n");
> >> +
> >> + priv->regmap = devm_regmap_init_mmio(dev, priv->base, ®map_config);
> >> + if (IS_ERR(priv->regmap))
> >> + return dev_err_probe(dev, PTR_ERR(priv->regmap),
> >> + "failed to initialize regmap\n");
> >> +
> >> + return 0;
> >> +}
> >> +
> >> static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
> >> const struct auxiliary_device_id *id)
> >> {
> >> @@ -105,36 +139,26 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
> >> if (!priv)
> >> return -ENOMEM;
> >>
> >> - spin_lock_init(&priv->lock);
> >> -
> >> priv->rcdev.owner = THIS_MODULE;
> >> priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
> >> priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
> >> priv->rcdev.of_node = dev->parent->of_node;
> >> priv->rcdev.dev = dev;
> >> priv->rcdev.of_reset_n_cells = 1;
> >> - priv->base = of_iomap(dev->parent->of_node, 0);
> >> - if (!priv->base)
> >> - return -ENOMEM;
> >>
> >> + /* keep before call to imx8mp_audiomix_reset_init_regmap() */
> >> dev_set_drvdata(dev, priv);
> >>
> >> + ret = imx8mp_audiomix_reset_get_regmap(dev);
> >> + if (ret)
> >> + return dev_err_probe(dev, ret, "failed to get regmap\n");
> >> +
> >> ret = devm_reset_controller_register(dev, &priv->rcdev);
> >> if (ret)
> >> - goto out_unmap;
> >> + return dev_err_probe(dev, ret,
> >> + "failed to register reset controller\n");
> >>
> >> return 0;
> >> -
> >> -out_unmap:
> >> - iounmap(priv->base);
> >> - return ret;
> >> -}
> >> -
> >> -static void imx8mp_audiomix_reset_remove(struct auxiliary_device *adev)
> >> -{
> >> - struct imx8mp_audiomix_reset *priv = dev_get_drvdata(&adev->dev);
> >> -
> >> - iounmap(priv->base);
> >> }
> >>
> >> static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
> >> @@ -147,7 +171,6 @@ MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
> >>
> >> static struct auxiliary_driver imx8mp_audiomix_reset_driver = {
> >> .probe = imx8mp_audiomix_reset_probe,
> >> - .remove = imx8mp_audiomix_reset_remove,
> >> .id_table = imx8mp_audiomix_reset_ids,
> >> };
> >>
> >> --
> >> 2.43.0
> >>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API
2025-10-29 13:52 ` [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API Laurentiu Mihalcea
2025-10-29 16:37 ` Frank Li
@ 2025-10-30 13:06 ` Philipp Zabel
2025-10-31 11:53 ` Laurentiu Mihalcea
1 sibling, 1 reply; 21+ messages in thread
From: Philipp Zabel @ 2025-10-30 13:06 UTC (permalink / raw)
To: Laurentiu Mihalcea, Abel Vesa, Peng Fan, Michael Turquette,
Stephen Boyd, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Shawn Guo, Fabio Estevam, Daniel Baluta, Shengjiu Wang
Cc: linux-clk, imx, devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On Mi, 2025-10-29 at 06:52 -0700, Laurentiu Mihalcea wrote:
> From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>
> As far as the Linux kernel is concerned, block devices such as i.MX8MP's
> AUDIOMIX block control or i.MX8ULP's SIM LPAV can simultaneously act as
> clock controllers, reset controllers or mux controllers. Since these IPs
> offer different functionalities through different subsystem APIs, it's
> important to make sure that the register R-M-W cycles are performed under
> the same lock across all subsystem APIs. This will ensure that registers
> will not end up with the wrong values because of race conditions (e.g.
> clock consumer tries to update block control register A, while, at the
> same time, reset consumer tries to update the same block control register).
>
> However, the aforementioned race conditions will only impact block control
> IPs which use the same register for multiple functionalities. For example,
> i.MX8MP's AUDIOMIX block control IP provides clock gating functionalities
> and reset control functionalities through different registers. This is why
> the current approach (i.e. clock control and reset control work using
> different locks) has worked well so far.
>
> Since we want to extend this driver to be usable for i.MX8ULP's SIM LPAV
> block control IP, we need to make sure that clock control, reset control,
> and mux control APIs use the same lock since all of these functionalities
> are performed using the SYSCTRL0 register.
>
> To do so, we need to switch to the regmap API and, if possible, use the
> parent device's regmap, which, in the case of i.MX8ULP, will be the clock
> controller. This way, we can make sure that the clock gates and the reset
> controller will use the same lock to perform the register R-M-W cycles.
>
> This change will also work fine for cases where we don't really need to
> share the lock across multiple APIs (e.g. i.MX8MP's AUDIOMIX block
> control) since regmap will take care of the locking we were previously
> explicitly performing in the driver.
>
> The transition to the regmap API also involves some cleanup. Specifically,
> we can make use of devres to unmap the device's memory and get rid of the
> memory mapping-related error paths and the remove() function altogether.
>
> Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
> ---
> drivers/reset/reset-imx8mp-audiomix.c | 91 +++++++++++++++++----------
> 1 file changed, 57 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
> index e9643365a62c..3f6d11270918 100644
> --- a/drivers/reset/reset-imx8mp-audiomix.c
> +++ b/drivers/reset/reset-imx8mp-audiomix.c
> @@ -11,6 +11,7 @@
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/of_address.h>
> +#include <linux/regmap.h>
> #include <linux/reset-controller.h>
>
> #define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
> @@ -42,8 +43,8 @@ static const struct imx8mp_reset_map reset_map[] = {
>
> struct imx8mp_audiomix_reset {
> struct reset_controller_dev rcdev;
> - spinlock_t lock; /* protect register read-modify-write cycle */
> void __iomem *base;
Drop base as well, better let devres handle this.
[...]
> +/* assumption: registered only if not using parent regmap */
> +static void imx8mp_audiomix_reset_iounmap(void *data)
Pass base instead of dev.
> +{
> + struct imx8mp_audiomix_reset *priv = dev_get_drvdata(data);
> +
> + iounmap(priv->base);
void __iomem *base = data;
iounmap(base);
> +}
> +
> +/* assumption: dev_set_drvdata() is called before this */
Why not just pass priv instead of dev?
> +static int imx8mp_audiomix_reset_get_regmap(struct device *dev)
> +{
> + struct imx8mp_audiomix_reset *priv;
> + int ret;
> +
> + priv = dev_get_drvdata(dev);
> +
> + /* try to use the parent's regmap */
> + priv->regmap = dev_get_regmap(dev->parent, NULL);
> + if (priv->regmap)
> + return 0;
> +
> + /* ... if that's not possible then initialize the regmap right now */
> + priv->base = of_iomap(dev->parent->of_node, 0);
Make base a local variable ...
> + if (!priv->base)
> + return dev_err_probe(dev, -ENOMEM, "failed to iomap address space\n");
> +
> + ret = devm_add_action_or_reset(dev, imx8mp_audiomix_reset_iounmap, dev);
... and pass it as data instead of dev.
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to register action\n");
> +
> + priv->regmap = devm_regmap_init_mmio(dev, priv->base, ®map_config);
> + if (IS_ERR(priv->regmap))
> + return dev_err_probe(dev, PTR_ERR(priv->regmap),
> + "failed to initialize regmap\n");
> +
> + return 0;
> +}
> +
> static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
> const struct auxiliary_device_id *id)
> {
> @@ -105,36 +139,26 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
> if (!priv)
> return -ENOMEM;
>
> - spin_lock_init(&priv->lock);
> -
> priv->rcdev.owner = THIS_MODULE;
> priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
> priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
> priv->rcdev.of_node = dev->parent->of_node;
> priv->rcdev.dev = dev;
> priv->rcdev.of_reset_n_cells = 1;
> - priv->base = of_iomap(dev->parent->of_node, 0);
> - if (!priv->base)
> - return -ENOMEM;
>
> + /* keep before call to imx8mp_audiomix_reset_init_regmap() */
Not needed if priv is passed to it directly.
regards
Philipp
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API
2025-10-30 13:06 ` Philipp Zabel
@ 2025-10-31 11:53 ` Laurentiu Mihalcea
0 siblings, 0 replies; 21+ messages in thread
From: Laurentiu Mihalcea @ 2025-10-31 11:53 UTC (permalink / raw)
To: Philipp Zabel, Abel Vesa, Peng Fan, Michael Turquette,
Stephen Boyd, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Shawn Guo, Fabio Estevam, Daniel Baluta, Shengjiu Wang
Cc: linux-clk, imx, devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On 10/30/2025 6:06 AM, Philipp Zabel wrote:
> On Mi, 2025-10-29 at 06:52 -0700, Laurentiu Mihalcea wrote:
>> From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>>
>> As far as the Linux kernel is concerned, block devices such as i.MX8MP's
>> AUDIOMIX block control or i.MX8ULP's SIM LPAV can simultaneously act as
>> clock controllers, reset controllers or mux controllers. Since these IPs
>> offer different functionalities through different subsystem APIs, it's
>> important to make sure that the register R-M-W cycles are performed under
>> the same lock across all subsystem APIs. This will ensure that registers
>> will not end up with the wrong values because of race conditions (e.g.
>> clock consumer tries to update block control register A, while, at the
>> same time, reset consumer tries to update the same block control register).
>>
>> However, the aforementioned race conditions will only impact block control
>> IPs which use the same register for multiple functionalities. For example,
>> i.MX8MP's AUDIOMIX block control IP provides clock gating functionalities
>> and reset control functionalities through different registers. This is why
>> the current approach (i.e. clock control and reset control work using
>> different locks) has worked well so far.
>>
>> Since we want to extend this driver to be usable for i.MX8ULP's SIM LPAV
>> block control IP, we need to make sure that clock control, reset control,
>> and mux control APIs use the same lock since all of these functionalities
>> are performed using the SYSCTRL0 register.
>>
>> To do so, we need to switch to the regmap API and, if possible, use the
>> parent device's regmap, which, in the case of i.MX8ULP, will be the clock
>> controller. This way, we can make sure that the clock gates and the reset
>> controller will use the same lock to perform the register R-M-W cycles.
>>
>> This change will also work fine for cases where we don't really need to
>> share the lock across multiple APIs (e.g. i.MX8MP's AUDIOMIX block
>> control) since regmap will take care of the locking we were previously
>> explicitly performing in the driver.
>>
>> The transition to the regmap API also involves some cleanup. Specifically,
>> we can make use of devres to unmap the device's memory and get rid of the
>> memory mapping-related error paths and the remove() function altogether.
>>
>> Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>> ---
>> drivers/reset/reset-imx8mp-audiomix.c | 91 +++++++++++++++++----------
>> 1 file changed, 57 insertions(+), 34 deletions(-)
>>
>> diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
>> index e9643365a62c..3f6d11270918 100644
>> --- a/drivers/reset/reset-imx8mp-audiomix.c
>> +++ b/drivers/reset/reset-imx8mp-audiomix.c
>> @@ -11,6 +11,7 @@
>> #include <linux/module.h>
>> #include <linux/of.h>
>> #include <linux/of_address.h>
>> +#include <linux/regmap.h>
>> #include <linux/reset-controller.h>
>>
>> #define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
>> @@ -42,8 +43,8 @@ static const struct imx8mp_reset_map reset_map[] = {
>>
>> struct imx8mp_audiomix_reset {
>> struct reset_controller_dev rcdev;
>> - spinlock_t lock; /* protect register read-modify-write cycle */
>> void __iomem *base;
> Drop base as well, better let devres handle this.
>
> [...]
>
>> +/* assumption: registered only if not using parent regmap */
>> +static void imx8mp_audiomix_reset_iounmap(void *data)
> Pass base instead of dev.
ACK. Will need some extra casts/annotations though as sparse will definitely
not be happy with this.
>
>> +{
>> + struct imx8mp_audiomix_reset *priv = dev_get_drvdata(data);
>> +
>> + iounmap(priv->base);
> void __iomem *base = data;
>
> iounmap(base);
>
>> +}
>> +
>> +/* assumption: dev_set_drvdata() is called before this */
> Why not just pass priv instead of dev?
>
>> +static int imx8mp_audiomix_reset_get_regmap(struct device *dev)
>> +{
>> + struct imx8mp_audiomix_reset *priv;
>> + int ret;
>> +
>> + priv = dev_get_drvdata(dev);
>> +
>> + /* try to use the parent's regmap */
>> + priv->regmap = dev_get_regmap(dev->parent, NULL);
>> + if (priv->regmap)
>> + return 0;
>> +
>> + /* ... if that's not possible then initialize the regmap right now */
>> + priv->base = of_iomap(dev->parent->of_node, 0);
> Make base a local variable ...
>
>> + if (!priv->base)
>> + return dev_err_probe(dev, -ENOMEM, "failed to iomap address space\n");
>> +
>> + ret = devm_add_action_or_reset(dev, imx8mp_audiomix_reset_iounmap, dev);
> ... and pass it as data instead of dev.
>
>> + if (ret)
>> + return dev_err_probe(dev, ret, "failed to register action\n");
>> +
>> + priv->regmap = devm_regmap_init_mmio(dev, priv->base, ®map_config);
>> + if (IS_ERR(priv->regmap))
>> + return dev_err_probe(dev, PTR_ERR(priv->regmap),
>> + "failed to initialize regmap\n");
>> +
>> + return 0;
>> +}
>> +
>> static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
>> const struct auxiliary_device_id *id)
>> {
>> @@ -105,36 +139,26 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
>> if (!priv)
>> return -ENOMEM;
>>
>> - spin_lock_init(&priv->lock);
>> -
>> priv->rcdev.owner = THIS_MODULE;
>> priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
>> priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
>> priv->rcdev.of_node = dev->parent->of_node;
>> priv->rcdev.dev = dev;
>> priv->rcdev.of_reset_n_cells = 1;
>> - priv->base = of_iomap(dev->parent->of_node, 0);
>> - if (!priv->base)
>> - return -ENOMEM;
>>
>> + /* keep before call to imx8mp_audiomix_reset_init_regmap() */
> Not needed if priv is passed to it directly.
right, somehow I forgot that rcdev holds a pointer to our "struct device" even though it's right there....
will make the change, thanks!
>
> regards
> Philipp
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v3 6/8] reset: imx8mp-audiomix: Extend the driver usage
2025-10-29 13:52 [PATCH v3 0/8] Add support for i.MX8ULP's SIM LPAV Laurentiu Mihalcea
` (4 preceding siblings ...)
2025-10-29 13:52 ` [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API Laurentiu Mihalcea
@ 2025-10-29 13:52 ` Laurentiu Mihalcea
2025-10-29 16:42 ` Frank Li
2025-10-29 13:52 ` [PATCH v3 7/8] reset: imx8mp-audiomix: Support i.MX8ULP SIM LPAV Laurentiu Mihalcea
2025-10-29 13:52 ` [PATCH v3 8/8] arm64: dts: imx8ulp: add sim lpav node Laurentiu Mihalcea
7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Mihalcea @ 2025-10-29 13:52 UTC (permalink / raw)
To: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang
Cc: linux-clk, imx, devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
Replace the previous approach, in which a single reset map is used, by a
per-driver approach, in which each auxiliary device driver holds a
reference to a certain reset map.
This change is needed to allow the driver to be reused for other NXP block
control IPs such as i.MX8ULP's SIM LPAV.
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
---
drivers/reset/reset-imx8mp-audiomix.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
index 3f6d11270918..5ee5a32c6950 100644
--- a/drivers/reset/reset-imx8mp-audiomix.c
+++ b/drivers/reset/reset-imx8mp-audiomix.c
@@ -23,7 +23,12 @@ struct imx8mp_reset_map {
bool active_low;
};
-static const struct imx8mp_reset_map reset_map[] = {
+struct imx8mp_reset_info {
+ const struct imx8mp_reset_map *map;
+ int num_lines;
+};
+
+static const struct imx8mp_reset_map imx8mp_reset_map[] = {
[IMX8MP_AUDIOMIX_EARC_RESET] = {
.offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
.mask = BIT(0),
@@ -41,10 +46,16 @@ static const struct imx8mp_reset_map reset_map[] = {
},
};
+static const struct imx8mp_reset_info imx8mp_reset_info = {
+ .map = imx8mp_reset_map,
+ .num_lines = ARRAY_SIZE(imx8mp_reset_map),
+};
+
struct imx8mp_audiomix_reset {
struct reset_controller_dev rcdev;
void __iomem *base;
struct regmap *regmap;
+ const struct imx8mp_reset_info *rinfo;
};
static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)
@@ -56,6 +67,7 @@ static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);
+ const struct imx8mp_reset_map *reset_map = priv->rinfo->map;
unsigned int mask, offset, active_low, shift, val;
mask = reset_map[id].mask;
@@ -140,7 +152,8 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
return -ENOMEM;
priv->rcdev.owner = THIS_MODULE;
- priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
+ priv->rinfo = (void *)id->driver_data;
+ priv->rcdev.nr_resets = priv->rinfo->num_lines;
priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
priv->rcdev.of_node = dev->parent->of_node;
priv->rcdev.dev = dev;
@@ -164,6 +177,7 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
{
.name = "clk_imx8mp_audiomix.reset",
+ .driver_data = (kernel_ulong_t)&imx8mp_reset_info,
},
{ }
};
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v3 6/8] reset: imx8mp-audiomix: Extend the driver usage
2025-10-29 13:52 ` [PATCH v3 6/8] reset: imx8mp-audiomix: Extend the driver usage Laurentiu Mihalcea
@ 2025-10-29 16:42 ` Frank Li
0 siblings, 0 replies; 21+ messages in thread
From: Frank Li @ 2025-10-29 16:42 UTC (permalink / raw)
To: Laurentiu Mihalcea
Cc: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang, linux-clk, imx,
devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On Wed, Oct 29, 2025 at 06:52:27AM -0700, Laurentiu Mihalcea wrote:
> From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>
> Replace the previous approach, in which a single reset map is used, by a
> per-driver approach, in which each auxiliary device driver holds a
> reference to a certain reset map.
Needn't "Replace the previous approach".
Replace single reset map with per-device reset map to allow the driver to
be reused for other NXP block.
>
> This change is needed to allow the driver to be reused for other NXP block
> control IPs such as i.MX8ULP's SIM LPAV.
avoid use words "this change[patch]"
Frank
>
> Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
> Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
> ---
> drivers/reset/reset-imx8mp-audiomix.c | 18 ++++++++++++++++--
> 1 file changed, 16 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
> index 3f6d11270918..5ee5a32c6950 100644
> --- a/drivers/reset/reset-imx8mp-audiomix.c
> +++ b/drivers/reset/reset-imx8mp-audiomix.c
> @@ -23,7 +23,12 @@ struct imx8mp_reset_map {
> bool active_low;
> };
>
> -static const struct imx8mp_reset_map reset_map[] = {
> +struct imx8mp_reset_info {
> + const struct imx8mp_reset_map *map;
> + int num_lines;
> +};
> +
> +static const struct imx8mp_reset_map imx8mp_reset_map[] = {
> [IMX8MP_AUDIOMIX_EARC_RESET] = {
> .offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
> .mask = BIT(0),
> @@ -41,10 +46,16 @@ static const struct imx8mp_reset_map reset_map[] = {
> },
> };
>
> +static const struct imx8mp_reset_info imx8mp_reset_info = {
> + .map = imx8mp_reset_map,
> + .num_lines = ARRAY_SIZE(imx8mp_reset_map),
> +};
> +
> struct imx8mp_audiomix_reset {
> struct reset_controller_dev rcdev;
> void __iomem *base;
> struct regmap *regmap;
> + const struct imx8mp_reset_info *rinfo;
> };
>
> static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)
> @@ -56,6 +67,7 @@ static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev,
> unsigned long id, bool assert)
> {
> struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);
> + const struct imx8mp_reset_map *reset_map = priv->rinfo->map;
> unsigned int mask, offset, active_low, shift, val;
>
> mask = reset_map[id].mask;
> @@ -140,7 +152,8 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
> return -ENOMEM;
>
> priv->rcdev.owner = THIS_MODULE;
> - priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
> + priv->rinfo = (void *)id->driver_data;
> + priv->rcdev.nr_resets = priv->rinfo->num_lines;
> priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
> priv->rcdev.of_node = dev->parent->of_node;
> priv->rcdev.dev = dev;
> @@ -164,6 +177,7 @@ static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
> static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
> {
> .name = "clk_imx8mp_audiomix.reset",
> + .driver_data = (kernel_ulong_t)&imx8mp_reset_info,
> },
> { }
> };
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v3 7/8] reset: imx8mp-audiomix: Support i.MX8ULP SIM LPAV
2025-10-29 13:52 [PATCH v3 0/8] Add support for i.MX8ULP's SIM LPAV Laurentiu Mihalcea
` (5 preceding siblings ...)
2025-10-29 13:52 ` [PATCH v3 6/8] reset: imx8mp-audiomix: Extend the driver usage Laurentiu Mihalcea
@ 2025-10-29 13:52 ` Laurentiu Mihalcea
2025-10-29 16:43 ` Frank Li
2025-10-29 13:52 ` [PATCH v3 8/8] arm64: dts: imx8ulp: add sim lpav node Laurentiu Mihalcea
7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Mihalcea @ 2025-10-29 13:52 UTC (permalink / raw)
To: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang
Cc: linux-clk, imx, devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
Support i.MX8ULP's SIM LPAV by adding its reset map definition.
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
---
drivers/reset/reset-imx8mp-audiomix.c | 45 +++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
index 5ee5a32c6950..4e420c6b8fdc 100644
--- a/drivers/reset/reset-imx8mp-audiomix.c
+++ b/drivers/reset/reset-imx8mp-audiomix.c
@@ -3,6 +3,7 @@
* Copyright 2024 NXP
*/
+#include <dt-bindings/reset/fsl,imx8ulp-sim-lpav.h>
#include <dt-bindings/reset/imx8mp-reset-audiomix.h>
#include <linux/auxiliary_bus.h>
@@ -17,6 +18,8 @@
#define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET 0x108
+#define IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET 0x8
+
struct imx8mp_reset_map {
unsigned int offset;
unsigned int mask;
@@ -51,6 +54,44 @@ static const struct imx8mp_reset_info imx8mp_reset_info = {
.num_lines = ARRAY_SIZE(imx8mp_reset_map),
};
+static const struct imx8mp_reset_map imx8ulp_reset_map[] = {
+ [IMX8ULP_SIM_LPAV_HIFI4_DSP_DBG_RST] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(25),
+ .active_low = false,
+ },
+ [IMX8ULP_SIM_LPAV_HIFI4_DSP_RST] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(16),
+ .active_low = false,
+ },
+ [IMX8ULP_SIM_LPAV_HIFI4_DSP_STALL] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(13),
+ .active_low = false,
+ },
+ [IMX8ULP_SIM_LPAV_DSI_RST_BYTE_N] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(5),
+ .active_low = true,
+ },
+ [IMX8ULP_SIM_LPAV_DSI_RST_ESC_N] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(4),
+ .active_low = true,
+ },
+ [IMX8ULP_SIM_LPAV_DSI_RST_DPI_N] = {
+ .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
+ .mask = BIT(3),
+ .active_low = true,
+ },
+};
+
+static const struct imx8mp_reset_info imx8ulp_reset_info = {
+ .map = imx8ulp_reset_map,
+ .num_lines = ARRAY_SIZE(imx8ulp_reset_map),
+};
+
struct imx8mp_audiomix_reset {
struct reset_controller_dev rcdev;
void __iomem *base;
@@ -179,6 +220,10 @@ static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
.name = "clk_imx8mp_audiomix.reset",
.driver_data = (kernel_ulong_t)&imx8mp_reset_info,
},
+ {
+ .name = "clk_imx8ulp_sim_lpav.reset",
+ .driver_data = (kernel_ulong_t)&imx8ulp_reset_info,
+ },
{ }
};
MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v3 7/8] reset: imx8mp-audiomix: Support i.MX8ULP SIM LPAV
2025-10-29 13:52 ` [PATCH v3 7/8] reset: imx8mp-audiomix: Support i.MX8ULP SIM LPAV Laurentiu Mihalcea
@ 2025-10-29 16:43 ` Frank Li
0 siblings, 0 replies; 21+ messages in thread
From: Frank Li @ 2025-10-29 16:43 UTC (permalink / raw)
To: Laurentiu Mihalcea
Cc: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang, linux-clk, imx,
devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On Wed, Oct 29, 2025 at 06:52:28AM -0700, Laurentiu Mihalcea wrote:
> From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>
> Support i.MX8ULP's SIM LPAV by adding its reset map definition.
>
> Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
> Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> ---
> drivers/reset/reset-imx8mp-audiomix.c | 45 +++++++++++++++++++++++++++
> 1 file changed, 45 insertions(+)
>
> diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
> index 5ee5a32c6950..4e420c6b8fdc 100644
> --- a/drivers/reset/reset-imx8mp-audiomix.c
> +++ b/drivers/reset/reset-imx8mp-audiomix.c
> @@ -3,6 +3,7 @@
> * Copyright 2024 NXP
> */
>
> +#include <dt-bindings/reset/fsl,imx8ulp-sim-lpav.h>
> #include <dt-bindings/reset/imx8mp-reset-audiomix.h>
>
> #include <linux/auxiliary_bus.h>
> @@ -17,6 +18,8 @@
> #define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
> #define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET 0x108
>
> +#define IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET 0x8
> +
> struct imx8mp_reset_map {
> unsigned int offset;
> unsigned int mask;
> @@ -51,6 +54,44 @@ static const struct imx8mp_reset_info imx8mp_reset_info = {
> .num_lines = ARRAY_SIZE(imx8mp_reset_map),
> };
>
> +static const struct imx8mp_reset_map imx8ulp_reset_map[] = {
> + [IMX8ULP_SIM_LPAV_HIFI4_DSP_DBG_RST] = {
> + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
> + .mask = BIT(25),
> + .active_low = false,
> + },
> + [IMX8ULP_SIM_LPAV_HIFI4_DSP_RST] = {
> + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
> + .mask = BIT(16),
> + .active_low = false,
> + },
> + [IMX8ULP_SIM_LPAV_HIFI4_DSP_STALL] = {
> + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
> + .mask = BIT(13),
> + .active_low = false,
> + },
> + [IMX8ULP_SIM_LPAV_DSI_RST_BYTE_N] = {
> + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
> + .mask = BIT(5),
> + .active_low = true,
> + },
> + [IMX8ULP_SIM_LPAV_DSI_RST_ESC_N] = {
> + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
> + .mask = BIT(4),
> + .active_low = true,
> + },
> + [IMX8ULP_SIM_LPAV_DSI_RST_DPI_N] = {
> + .offset = IMX8ULP_SIM_LPAV_SYSCTRL0_OFFSET,
> + .mask = BIT(3),
> + .active_low = true,
> + },
> +};
> +
> +static const struct imx8mp_reset_info imx8ulp_reset_info = {
> + .map = imx8ulp_reset_map,
> + .num_lines = ARRAY_SIZE(imx8ulp_reset_map),
> +};
> +
> struct imx8mp_audiomix_reset {
> struct reset_controller_dev rcdev;
> void __iomem *base;
> @@ -179,6 +220,10 @@ static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
> .name = "clk_imx8mp_audiomix.reset",
> .driver_data = (kernel_ulong_t)&imx8mp_reset_info,
> },
> + {
> + .name = "clk_imx8ulp_sim_lpav.reset",
> + .driver_data = (kernel_ulong_t)&imx8ulp_reset_info,
> + },
> { }
> };
> MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v3 8/8] arm64: dts: imx8ulp: add sim lpav node
2025-10-29 13:52 [PATCH v3 0/8] Add support for i.MX8ULP's SIM LPAV Laurentiu Mihalcea
` (6 preceding siblings ...)
2025-10-29 13:52 ` [PATCH v3 7/8] reset: imx8mp-audiomix: Support i.MX8ULP SIM LPAV Laurentiu Mihalcea
@ 2025-10-29 13:52 ` Laurentiu Mihalcea
2025-10-29 16:44 ` Frank Li
7 siblings, 1 reply; 21+ messages in thread
From: Laurentiu Mihalcea @ 2025-10-29 13:52 UTC (permalink / raw)
To: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang
Cc: linux-clk, imx, devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
Add DT node for the SIM LPAV module.
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8ulp.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8ulp.dtsi b/arch/arm64/boot/dts/freescale/imx8ulp.dtsi
index 13b01f3aa2a4..9b5d98766512 100644
--- a/arch/arm64/boot/dts/freescale/imx8ulp.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8ulp.dtsi
@@ -776,6 +776,23 @@ edma2: dma-controller@2d800000 {
"ch28", "ch29", "ch30", "ch31";
};
+ sim_lpav: clock-controller@2da50000 {
+ compatible = "fsl,imx8ulp-sim-lpav";
+ reg = <0x2da50000 0x10000>;
+ clocks = <&cgc2 IMX8ULP_CLK_LPAV_BUS_DIV>,
+ <&cgc2 IMX8ULP_CLK_HIFI_DIVCORE>,
+ <&cgc2 IMX8ULP_CLK_HIFI_DIVPLAT>;
+ clock-names = "bus", "core", "plat";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+
+ sim_lpav_mux: mux-controller {
+ compatible = "reg-mux";
+ #mux-control-cells = <1>;
+ mux-reg-masks = <0x8 0x00000200>;
+ };
+ };
+
cgc2: clock-controller@2da60000 {
compatible = "fsl,imx8ulp-cgc2";
reg = <0x2da60000 0x10000>;
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v3 8/8] arm64: dts: imx8ulp: add sim lpav node
2025-10-29 13:52 ` [PATCH v3 8/8] arm64: dts: imx8ulp: add sim lpav node Laurentiu Mihalcea
@ 2025-10-29 16:44 ` Frank Li
0 siblings, 0 replies; 21+ messages in thread
From: Frank Li @ 2025-10-29 16:44 UTC (permalink / raw)
To: Laurentiu Mihalcea
Cc: Abel Vesa, Peng Fan, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Shawn Guo, Fabio Estevam,
Philipp Zabel, Daniel Baluta, Shengjiu Wang, linux-clk, imx,
devicetree, linux-arm-kernel, linux-kernel,
Pengutronix Kernel Team
On Wed, Oct 29, 2025 at 06:52:29AM -0700, Laurentiu Mihalcea wrote:
> From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
>
> Add DT node for the SIM LPAV module.
>
> Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
> Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> ---
> arch/arm64/boot/dts/freescale/imx8ulp.dtsi | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/freescale/imx8ulp.dtsi b/arch/arm64/boot/dts/freescale/imx8ulp.dtsi
> index 13b01f3aa2a4..9b5d98766512 100644
> --- a/arch/arm64/boot/dts/freescale/imx8ulp.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8ulp.dtsi
> @@ -776,6 +776,23 @@ edma2: dma-controller@2d800000 {
> "ch28", "ch29", "ch30", "ch31";
> };
>
> + sim_lpav: clock-controller@2da50000 {
> + compatible = "fsl,imx8ulp-sim-lpav";
> + reg = <0x2da50000 0x10000>;
> + clocks = <&cgc2 IMX8ULP_CLK_LPAV_BUS_DIV>,
> + <&cgc2 IMX8ULP_CLK_HIFI_DIVCORE>,
> + <&cgc2 IMX8ULP_CLK_HIFI_DIVPLAT>;
> + clock-names = "bus", "core", "plat";
> + #clock-cells = <1>;
> + #reset-cells = <1>;
> +
> + sim_lpav_mux: mux-controller {
> + compatible = "reg-mux";
> + #mux-control-cells = <1>;
> + mux-reg-masks = <0x8 0x00000200>;
> + };
> + };
> +
> cgc2: clock-controller@2da60000 {
> compatible = "fsl,imx8ulp-cgc2";
> reg = <0x2da60000 0x10000>;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 21+ messages in thread