Unverified Commit 724ad89f authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'reset-for-v6.9' of git://git.pengutronix.de/pza/linux into soc/late

Reset controller updates for v6.9

Enable support for the Sophgo SG2042 reset controller via reset-simple,
add a GPIO-based reset controller criver for shared GPIO resets, extract
an of_phandle_args_equal() helper function out of cpufreq, and use it in
reset-gpio.

Based on v6.8-rc5 because reset-gpio depends on commits in the
gpio-driver-h-stubs-for-v6.8-rc5 tag.

* tag 'reset-for-v6.9' of git://git.pengutronix.de/pza/linux:
  reset: Instantiate reset GPIO controller for shared reset-gpios
  reset: gpio: Add GPIO-based reset controller
  cpufreq: do not open-code of_phandle_args_equal()
  of: Add of_phandle_args_equal() helper
  reset: simple: add support for Sophgo SG2042
  dt-bindings: reset: sophgo: support SG2042

Link: https://lore.kernel.org/r/20240301111300.4038207-1-p.zabel@pengutronix.de


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 794f8770 c721f189
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/reset/sophgo,sg2042-reset.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Sophgo SG2042 SoC Reset Controller

maintainers:
  - Chen Wang <unicorn_wang@outlook.com>

properties:
  compatible:
    const: sophgo,sg2042-reset

  reg:
    maxItems: 1

  "#reset-cells":
    const: 1

required:
  - compatible
  - reg
  - "#reset-cells"

additionalProperties: false

examples:
  - |
    rstgen: reset-controller@c00 {
        compatible = "sophgo,sg2042-reset";
        reg = <0xc00 0xc>;
        #reset-cells = <1>;
    };
+5 −0
Original line number Diff line number Diff line
@@ -8911,6 +8911,11 @@ F: Documentation/i2c/muxes/i2c-mux-gpio.rst
F:	drivers/i2c/muxes/i2c-mux-gpio.c
F:	include/linux/platform_data/i2c-mux-gpio.h
GENERIC GPIO RESET DRIVER
M:	Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
S:	Maintained
F:	drivers/reset/reset-gpio.c
GENERIC HDLC (WAN) DRIVERS
M:	Krzysztof Halasa <khc@pm.waw.pl>
S:	Maintained
+11 −1
Original line number Diff line number Diff line
@@ -66,6 +66,15 @@ config RESET_BRCMSTB_RESCAL
	  This enables the RESCAL reset controller for SATA, PCIe0, or PCIe1 on
	  BCM7216.

config RESET_GPIO
	tristate "GPIO reset controller"
	help
	  This enables a generic reset controller for resets attached via
	  GPIOs.  Typically for OF platforms this driver expects "reset-gpios"
	  property.

	  If compiled as module, it will be called reset-gpio.

config RESET_HSDK
	bool "Synopsys HSDK Reset Driver"
	depends on HAS_IOMEM
@@ -213,7 +222,7 @@ config RESET_SCMI

config RESET_SIMPLE
	bool "Simple Reset Controller Driver" if COMPILE_TEST || EXPERT
	default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC
	default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_SOPHGO || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC
	depends on HAS_IOMEM
	help
	  This enables a simple reset controller driver for reset lines that
@@ -228,6 +237,7 @@ config RESET_SIMPLE
	   - RCC reset controller in STM32 MCUs
	   - Allwinner SoCs
	   - SiFive FU740 SoCs
	   - Sophgo SoCs

config RESET_SOCFPGA
	bool "SoCFPGA Reset Driver" if COMPILE_TEST && (!ARM || !ARCH_INTEL_SOCFPGA)
+1 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o
obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o
obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
obj-$(CONFIG_RESET_GPIO) += reset-gpio.o
obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
obj-$(CONFIG_RESET_INTEL_GW) += reset-intel-gw.o
+211 −13
Original line number Diff line number Diff line
@@ -5,14 +5,19 @@
 * Copyright 2013 Philipp Zabel, Pengutronix
 */
#include <linux/atomic.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
@@ -23,6 +28,11 @@ static LIST_HEAD(reset_controller_list);
static DEFINE_MUTEX(reset_lookup_mutex);
static LIST_HEAD(reset_lookup_list);

/* Protects reset_gpio_lookup_list */
static DEFINE_MUTEX(reset_gpio_lookup_mutex);
static LIST_HEAD(reset_gpio_lookup_list);
static DEFINE_IDA(reset_gpio_ida);

/**
 * struct reset_control - a reset control
 * @rcdev: a pointer to the reset controller device
@@ -63,6 +73,16 @@ struct reset_control_array {
	struct reset_control *rstc[] __counted_by(num_rstcs);
};

/**
 * struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices
 * @of_args: phandle to the reset controller with all the args like GPIO number
 * @list: list entry for the reset_gpio_lookup_list
 */
struct reset_gpio_lookup {
	struct of_phandle_args of_args;
	struct list_head list;
};

static const char *rcdev_name(struct reset_controller_dev *rcdev)
{
	if (rcdev->dev)
@@ -71,6 +91,9 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev)
	if (rcdev->of_node)
		return rcdev->of_node->full_name;

	if (rcdev->of_args)
		return rcdev->of_args->np->full_name;

	return NULL;
}

@@ -99,6 +122,9 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
 */
int reset_controller_register(struct reset_controller_dev *rcdev)
{
	if (rcdev->of_node && rcdev->of_args)
		return -EINVAL;

	if (!rcdev->of_xlate) {
		rcdev->of_reset_n_cells = 1;
		rcdev->of_xlate = of_reset_simple_xlate;
@@ -813,12 +839,171 @@ static void __reset_control_put_internal(struct reset_control *rstc)
	kref_put(&rstc->refcnt, __reset_control_release);
}

static int __reset_add_reset_gpio_lookup(int id, struct device_node *np,
					 unsigned int gpio,
					 unsigned int of_flags)
{
	const struct fwnode_handle *fwnode = of_fwnode_handle(np);
	unsigned int lookup_flags;
	const char *label_tmp;

	/*
	 * Later we map GPIO flags between OF and Linux, however not all
	 * constants from include/dt-bindings/gpio/gpio.h and
	 * include/linux/gpio/machine.h match each other.
	 */
	if (of_flags > GPIO_ACTIVE_LOW) {
		pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n",
		       of_flags, gpio);
		return -EINVAL;
	}

	struct gpio_device *gdev __free(gpio_device_put) = gpio_device_find_by_fwnode(fwnode);
	if (!gdev)
		return -EPROBE_DEFER;

	label_tmp = gpio_device_get_label(gdev);
	if (!label_tmp)
		return -EINVAL;

	char *label __free(kfree) = kstrdup(label_tmp, GFP_KERNEL);
	if (!label)
		return -ENOMEM;

	/* Size: one lookup entry plus sentinel */
	struct gpiod_lookup_table *lookup __free(kfree) = kzalloc(struct_size(lookup, table, 2),
								  GFP_KERNEL);
	if (!lookup)
		return -ENOMEM;

	lookup->dev_id = kasprintf(GFP_KERNEL, "reset-gpio.%d", id);
	if (!lookup->dev_id)
		return -ENOMEM;

	lookup_flags = GPIO_PERSISTENT;
	lookup_flags |= of_flags & GPIO_ACTIVE_LOW;
	lookup->table[0] = GPIO_LOOKUP(no_free_ptr(label), gpio, "reset",
				       lookup_flags);

	/* Not freed on success, because it is persisent subsystem data. */
	gpiod_add_lookup_table(no_free_ptr(lookup));

	return 0;
}

/*
 * @args:	phandle to the GPIO provider with all the args like GPIO number
 */
static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
{
	struct reset_gpio_lookup *rgpio_dev;
	struct platform_device *pdev;
	int id, ret;

	/*
	 * Currently only #gpio-cells=2 is supported with the meaning of:
	 * args[0]: GPIO number
	 * args[1]: GPIO flags
	 * TODO: Handle other cases.
	 */
	if (args->args_count != 2)
		return -ENOENT;

	/*
	 * Registering reset-gpio device might cause immediate
	 * bind, resulting in its probe() registering new reset controller thus
	 * taking reset_list_mutex lock via reset_controller_register().
	 */
	lockdep_assert_not_held(&reset_list_mutex);

	mutex_lock(&reset_gpio_lookup_mutex);

	list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) {
		if (args->np == rgpio_dev->of_args.np) {
			if (of_phandle_args_equal(args, &rgpio_dev->of_args))
				goto out; /* Already on the list, done */
		}
	}

	id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
	if (id < 0) {
		ret = id;
		goto err_unlock;
	}

	/* Not freed on success, because it is persisent subsystem data. */
	rgpio_dev = kzalloc(sizeof(*rgpio_dev), GFP_KERNEL);
	if (!rgpio_dev) {
		ret = -ENOMEM;
		goto err_ida_free;
	}

	ret = __reset_add_reset_gpio_lookup(id, args->np, args->args[0],
					    args->args[1]);
	if (ret < 0)
		goto err_kfree;

	rgpio_dev->of_args = *args;
	/*
	 * We keep the device_node reference, but of_args.np is put at the end
	 * of __of_reset_control_get(), so get it one more time.
	 * Hold reference as long as rgpio_dev memory is valid.
	 */
	of_node_get(rgpio_dev->of_args.np);
	pdev = platform_device_register_data(NULL, "reset-gpio", id,
					     &rgpio_dev->of_args,
					     sizeof(rgpio_dev->of_args));
	ret = PTR_ERR_OR_ZERO(pdev);
	if (ret)
		goto err_put;

	list_add(&rgpio_dev->list, &reset_gpio_lookup_list);

out:
	mutex_unlock(&reset_gpio_lookup_mutex);

	return 0;

err_put:
	of_node_put(rgpio_dev->of_args.np);
err_kfree:
	kfree(rgpio_dev);
err_ida_free:
	ida_free(&reset_gpio_ida, id);
err_unlock:
	mutex_unlock(&reset_gpio_lookup_mutex);

	return ret;
}

static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_args *args,
						       bool gpio_fallback)
{
	struct reset_controller_dev *rcdev;

	lockdep_assert_held(&reset_list_mutex);

	list_for_each_entry(rcdev, &reset_controller_list, list) {
		if (gpio_fallback) {
			if (rcdev->of_args && of_phandle_args_equal(args,
								    rcdev->of_args))
				return rcdev;
		} else {
			if (args->np == rcdev->of_node)
				return rcdev;
		}
	}

	return NULL;
}

struct reset_control *
__of_reset_control_get(struct device_node *node, const char *id, int index,
		       bool shared, bool optional, bool acquired)
{
	bool gpio_fallback = false;
	struct reset_control *rstc;
	struct reset_controller_dev *r, *rcdev;
	struct reset_controller_dev *rcdev;
	struct of_phandle_args args;
	int rstc_id;
	int ret;
@@ -839,39 +1024,52 @@ __of_reset_control_get(struct device_node *node, const char *id, int index,
					 index, &args);
	if (ret == -EINVAL)
		return ERR_PTR(ret);
	if (ret) {
		if (!IS_ENABLED(CONFIG_RESET_GPIO))
			return optional ? NULL : ERR_PTR(ret);

		/*
		 * There can be only one reset-gpio for regular devices, so
		 * don't bother with the "reset-gpios" phandle index.
		 */
		ret = of_parse_phandle_with_args(node, "reset-gpios", "#gpio-cells",
						 0, &args);
		if (ret)
			return optional ? NULL : ERR_PTR(ret);

	mutex_lock(&reset_list_mutex);
	rcdev = NULL;
	list_for_each_entry(r, &reset_controller_list, list) {
		if (args.np == r->of_node) {
			rcdev = r;
			break;
		gpio_fallback = true;

		ret = __reset_add_reset_gpio_device(&args);
		if (ret) {
			rstc = ERR_PTR(ret);
			goto out_put;
		}
	}

	mutex_lock(&reset_list_mutex);
	rcdev = __reset_find_rcdev(&args, gpio_fallback);
	if (!rcdev) {
		rstc = ERR_PTR(-EPROBE_DEFER);
		goto out;
		goto out_unlock;
	}

	if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
		rstc = ERR_PTR(-EINVAL);
		goto out;
		goto out_unlock;
	}

	rstc_id = rcdev->of_xlate(rcdev, &args);
	if (rstc_id < 0) {
		rstc = ERR_PTR(rstc_id);
		goto out;
		goto out_unlock;
	}

	/* reset_list_mutex also protects the rcdev's reset_control list */
	rstc = __reset_control_get_internal(rcdev, rstc_id, shared, acquired);

out:
out_unlock:
	mutex_unlock(&reset_list_mutex);
out_put:
	of_node_put(args.np);

	return rstc;
Loading