mfd: syscon: Allow reset control for syscon devices
authorJeremy Kerr <jk@codeconstruct.com.au>
Thu, 5 Jan 2023 00:50:10 +0000 (08:50 +0800)
committerLee Jones <lee@kernel.org>
Wed, 22 Feb 2023 08:25:53 +0000 (08:25 +0000)
Simple syscon devices may require deassertion of a reset signal in order
to access their register set. Rather than requiring a custom driver to
implement this, we can use the generic "resets" specifiers to link a
reset line to the syscon.

This change adds an optional reset line to the syscon device
description, and deasserts the reset if detected.

Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Lee Jones <lee@kernel.org>
Link: https://lore.kernel.org/r/20230105005010.124948-3-jk@codeconstruct.com.au
drivers/mfd/syscon.c

index bdb2ce7..57b29c3 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/platform_data/syscon.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/reset.h>
 #include <linux/mfd/syscon.h>
 #include <linux/slab.h>
 
@@ -31,6 +32,7 @@ static LIST_HEAD(syscon_list);
 struct syscon {
        struct device_node *np;
        struct regmap *regmap;
+       struct reset_control *reset;
        struct list_head list;
 };
 
@@ -40,7 +42,7 @@ static const struct regmap_config syscon_regmap_config = {
        .reg_stride = 4,
 };
 
-static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
+static struct syscon *of_syscon_register(struct device_node *np, bool check_res)
 {
        struct clk *clk;
        struct syscon *syscon;
@@ -50,6 +52,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
        int ret;
        struct regmap_config syscon_config = syscon_regmap_config;
        struct resource res;
+       struct reset_control *reset;
 
        syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
        if (!syscon)
@@ -114,7 +117,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
                goto err_regmap;
        }
 
-       if (check_clk) {
+       if (check_res) {
                clk = of_clk_get(np, 0);
                if (IS_ERR(clk)) {
                        ret = PTR_ERR(clk);
@@ -124,8 +127,18 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
                } else {
                        ret = regmap_mmio_attach_clk(regmap, clk);
                        if (ret)
-                               goto err_attach;
+                               goto err_attach_clk;
                }
+
+               reset = of_reset_control_get_optional_exclusive(np, NULL);
+               if (IS_ERR(reset)) {
+                       ret = PTR_ERR(reset);
+                       goto err_attach_clk;
+               }
+
+               ret = reset_control_deassert(reset);
+               if (ret)
+                       goto err_reset;
        }
 
        syscon->regmap = regmap;
@@ -137,7 +150,9 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
 
        return syscon;
 
-err_attach:
+err_reset:
+       reset_control_put(reset);
+err_attach_clk:
        if (!IS_ERR(clk))
                clk_put(clk);
 err_clk:
@@ -150,7 +165,7 @@ err_map:
 }
 
 static struct regmap *device_node_get_regmap(struct device_node *np,
-                                            bool check_clk)
+                                            bool check_res)
 {
        struct syscon *entry, *syscon = NULL;
 
@@ -165,7 +180,7 @@ static struct regmap *device_node_get_regmap(struct device_node *np,
        spin_unlock(&syscon_list_slock);
 
        if (!syscon)
-               syscon = of_syscon_register(np, check_clk);
+               syscon = of_syscon_register(np, check_res);
 
        if (IS_ERR(syscon))
                return ERR_CAST(syscon);