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
This commit is contained in:
Jeremy Kerr 2023-01-05 08:50:10 +08:00 committed by Lee Jones
parent 0f4d261989
commit 7d1e3bd948

View 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);