Commit 7d1e3bd9 authored by Jeremy Kerr's avatar Jeremy Kerr Committed by Lee Jones
Browse files

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: default avatarJeremy Kerr <jk@codeconstruct.com.au>
Reviewed-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarLee Jones <lee@kernel.org>
Link: https://lore.kernel.org/r/20230105005010.124948-3-jk@codeconstruct.com.au
parent 0f4d2619
Loading
Loading
Loading
Loading
+21 −6
Original line number Diff line number Diff line
@@ -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 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
}

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