Re: [PATCH v3 5/8] reset: imx8mp-audiomix: Switch to using regmap API
From: Philipp Zabel <p.zabel@pengutronix.de>
Date: 2025-10-30 13:06:49
Also in:
imx, linux-clk, linux-devicetree, lkml
On Mi, 2025-10-29 at 06:52 -0700, Laurentiu Mihalcea wrote:
quoted hunk ↗ jump to hunk
From: Laurentiu Mihalcea <redacted> 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 <redacted> --- 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.
quoted hunk ↗ jump to hunk
+ 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