diff options
author | Jeremy Kerr <jk@codeconstruct.com.au> | 2023-01-05 08:50:10 +0800 |
---|---|---|
committer | Lee Jones <lee@kernel.org> | 2023-02-22 08:25:53 +0000 |
commit | 7d1e3bd94828ad9fc86f55253cd6fec8edd65394 (patch) | |
tree | b19469e3307042d770c948dfb46d52aee5ac70ae /drivers/mfd | |
parent | 0f4d261989a89aa78f309eb0c5cb816028182bca (diff) | |
download | lwn-7d1e3bd94828ad9fc86f55253cd6fec8edd65394.tar.gz lwn-7d1e3bd94828ad9fc86f55253cd6fec8edd65394.zip |
mfd: syscon: Allow reset control for syscon devices
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
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/syscon.c | 27 |
1 files changed, 21 insertions, 6 deletions
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index bdb2ce7ff03b..57b29c325131 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -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); |