diff mbox series

[v3,15/21] reset: add driver for generic reset controllers

Message ID 1579954983-11329-16-git-send-email-amittomer25@gmail.com
State New
Headers show
Series Actions S700 SoC support | expand

Commit Message

Amit Tomer Jan. 25, 2020, 12:22 p.m. UTC
The simplest and most generic form of a reset controller just exposes
multiple MMIO registers, where each bit toggles a separate reset line.
Add a generic driver to describe this kind of reset controller.

This is used on the Action Semi S700, for instance, but also by other
SoCs.

Signed-off-by: Amit Singh Tomar <amittomer25 at gmail.com>
[Andre: make more generic, let it cover multiple registers, slight rework]
Signed-off-by: Andre Przywara <andre.przywara at arm.com>
---
Changes since v2:
        * Newly added patch, not there in v2/v1.
---
 drivers/reset/Kconfig         |   6 +++
 drivers/reset/Makefile        |   1 +
 drivers/reset/reset-generic.c | 111 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 118 insertions(+)
 create mode 100644 drivers/reset/reset-generic.c

Comments

Andre Przywara March 3, 2020, 10:21 a.m. UTC | #1
On Sat, 25 Jan 2020 17:52:57 +0530
Amit Singh Tomar <amittomer25 at gmail.com> wrote:

Hi,

> The simplest and most generic form of a reset controller just exposes
> multiple MMIO registers, where each bit toggles a separate reset line.
> Add a generic driver to describe this kind of reset controller.
> 
> This is used on the Action Semi S700, for instance, but also by other
> SoCs.

As tempting as this may be, but the S900 uses the more common style of integrating the reset controller in the CMU device:
	mmc0: mmc at e0330000 {
	...
		clocks = <&cmu CLK_SD0>;
		resets = <&cmu RESET_SD0>;
	...

So you should probably follow suit here for the S700 as well, especially as the Linux S700 CMU driver already defines resets, they just don't seem to be used yet.

So please drop this patch and the next one, and integrate the reset functionality in clk_owl.c, similar to what we do for sunxi.

Cheers,
Andre.

P.S. I agree with Mani that the Ethernet patches should be a separate series.

> 
> Signed-off-by: Amit Singh Tomar <amittomer25 at gmail.com>
> [Andre: make more generic, let it cover multiple registers, slight rework]
> Signed-off-by: Andre Przywara <andre.przywara at arm.com>
> ---
> Changes since v2:
>         * Newly added patch, not there in v2/v1.
> ---
>  drivers/reset/Kconfig         |   6 +++
>  drivers/reset/Makefile        |   1 +
>  drivers/reset/reset-generic.c | 111 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 118 insertions(+)
>  create mode 100644 drivers/reset/reset-generic.c
> 
> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
> index 75ccd65..1cdc159 100644
> --- a/drivers/reset/Kconfig
> +++ b/drivers/reset/Kconfig
> @@ -12,6 +12,12 @@ config DM_RESET
>  	  although driving such reset isgnals using GPIOs may be more
>  	  appropriate in this case.
>  
> +config GENERIC_RESET
> +        bool "Generic Reset controller driver"
> +        depends on DM_RESET
> +        help
> +          Support Generic reset controller.
> +
>  config SANDBOX_RESET
>  	bool "Enable the sandbox reset test driver"
>  	depends on DM_MAILBOX && SANDBOX
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index 0a044d5..5e027a1 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -4,6 +4,7 @@
>  #
>  
>  obj-$(CONFIG_DM_RESET) += reset-uclass.o
> +obj-$(CONFIG_GENERIC_RESET) += reset-generic.o
>  obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset.o
>  obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset-test.o
>  obj-$(CONFIG_STI_RESET) += sti-reset.o
> diff --git a/drivers/reset/reset-generic.c b/drivers/reset/reset-generic.c
> new file mode 100644
> index 0000000..9c45087
> --- /dev/null
> +++ b/drivers/reset/reset-generic.c
> @@ -0,0 +1,111 @@
> +/*
> + * Copyright (C) 2017 Amit Singh Tomar <amittomer25 at gmail.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <reset-uclass.h>
> +#include <linux/bitops.h>
> +#include <linux/io.h>
> +#include <linux/sizes.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +struct generic_reset_priv {
> +	void __iomem *membase;
> +	int max_reset;
> +};
> +
> +#define BITS_PER_BYTE 8
> +static int generic_reset_toggle(struct reset_ctl *rst, bool assert)
> +{
> +	struct generic_reset_priv *priv = dev_get_priv(rst->dev);
> +	int reg_width = sizeof(u32);
> +	int bank, offset;
> +	u32 reg;
> +
> +	if (rst->id >= priv->max_reset)
> +		return -EINVAL;
> +
> +	bank = rst->id / (reg_width * BITS_PER_BYTE);
> +	offset = rst->id % (reg_width * BITS_PER_BYTE);
> +
> +	reg = readl(priv->membase + (bank * reg_width));
> +	if (assert)
> +		writel(reg & ~BIT(offset), priv->membase + (bank * reg_width));
> +	else
> +		writel(reg | BIT(offset), priv->membase + (bank * reg_width));
> +
> +	return 0;
> +}
> +
> +static int generic_reset_assert(struct reset_ctl *rst)
> +{
> +	return generic_reset_toggle(rst, true);
> +}
> +
> +static int generic_reset_deassert(struct reset_ctl *rst)
> +{
> +	return generic_reset_toggle(rst, false);
> +}
> +
> +static int generic_reset_free(struct reset_ctl *rst)
> +{
> +	return 0;
> +}
> +
> +static int generic_reset_request(struct reset_ctl *rst)
> +{
> +	struct generic_reset_priv *priv = dev_get_priv(rst->dev);
> +
> +	if (rst->id >= priv->max_reset)
> +		return -EINVAL;
> +
> +	return generic_reset_assert(rst);
> +}
> +
> +struct reset_ops generic_reset_reset_ops = {
> +	.free = generic_reset_free,
> +	.request = generic_reset_request,
> +	.rst_assert = generic_reset_assert,
> +	.rst_deassert = generic_reset_deassert,
> +};
> +
> +static const struct udevice_id generic_reset_ids[] = {
> +	{ .compatible = "generic-reset" },
> +	{ .compatible = "actions,s700-reset" },
> +	{ }
> +};
> +
> +static int generic_reset_probe(struct udevice *dev)
> +{
> +	struct generic_reset_priv *priv = dev_get_priv(dev);
> +	fdt_addr_t addr;
> +	fdt_size_t size;
> +
> +	addr = devfdt_get_addr_size_index(dev, 0, &size);
> +	if (addr == FDT_ADDR_T_NONE)
> +		return -EINVAL;
> +
> +	priv->max_reset = dev_read_u32_default(dev, "num-resets", -1);
> +	if (priv->max_reset == -1)
> +		priv->max_reset = size * BITS_PER_BYTE;
> +
> +	priv->membase = devm_ioremap(dev, addr, size);
> +	if (!priv->membase)
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +U_BOOT_DRIVER(generic_reset) = {
> +	.name = "generic_reset",
> +	.id = UCLASS_RESET,
> +	.of_match = generic_reset_ids,
> +	.ops = &generic_reset_reset_ops,
> +	.probe = generic_reset_probe,
> +	.priv_auto_alloc_size = sizeof(struct generic_reset_priv),
> +};
diff mbox series

Patch

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 75ccd65..1cdc159 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -12,6 +12,12 @@  config DM_RESET
 	  although driving such reset isgnals using GPIOs may be more
 	  appropriate in this case.
 
+config GENERIC_RESET
+        bool "Generic Reset controller driver"
+        depends on DM_RESET
+        help
+          Support Generic reset controller.
+
 config SANDBOX_RESET
 	bool "Enable the sandbox reset test driver"
 	depends on DM_MAILBOX && SANDBOX
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 0a044d5..5e027a1 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -4,6 +4,7 @@ 
 #
 
 obj-$(CONFIG_DM_RESET) += reset-uclass.o
+obj-$(CONFIG_GENERIC_RESET) += reset-generic.o
 obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset.o
 obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset-test.o
 obj-$(CONFIG_STI_RESET) += sti-reset.o
diff --git a/drivers/reset/reset-generic.c b/drivers/reset/reset-generic.c
new file mode 100644
index 0000000..9c45087
--- /dev/null
+++ b/drivers/reset/reset-generic.c
@@ -0,0 +1,111 @@ 
+/*
+ * Copyright (C) 2017 Amit Singh Tomar <amittomer25 at gmail.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <reset-uclass.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/sizes.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct generic_reset_priv {
+	void __iomem *membase;
+	int max_reset;
+};
+
+#define BITS_PER_BYTE 8
+static int generic_reset_toggle(struct reset_ctl *rst, bool assert)
+{
+	struct generic_reset_priv *priv = dev_get_priv(rst->dev);
+	int reg_width = sizeof(u32);
+	int bank, offset;
+	u32 reg;
+
+	if (rst->id >= priv->max_reset)
+		return -EINVAL;
+
+	bank = rst->id / (reg_width * BITS_PER_BYTE);
+	offset = rst->id % (reg_width * BITS_PER_BYTE);
+
+	reg = readl(priv->membase + (bank * reg_width));
+	if (assert)
+		writel(reg & ~BIT(offset), priv->membase + (bank * reg_width));
+	else
+		writel(reg | BIT(offset), priv->membase + (bank * reg_width));
+
+	return 0;
+}
+
+static int generic_reset_assert(struct reset_ctl *rst)
+{
+	return generic_reset_toggle(rst, true);
+}
+
+static int generic_reset_deassert(struct reset_ctl *rst)
+{
+	return generic_reset_toggle(rst, false);
+}
+
+static int generic_reset_free(struct reset_ctl *rst)
+{
+	return 0;
+}
+
+static int generic_reset_request(struct reset_ctl *rst)
+{
+	struct generic_reset_priv *priv = dev_get_priv(rst->dev);
+
+	if (rst->id >= priv->max_reset)
+		return -EINVAL;
+
+	return generic_reset_assert(rst);
+}
+
+struct reset_ops generic_reset_reset_ops = {
+	.free = generic_reset_free,
+	.request = generic_reset_request,
+	.rst_assert = generic_reset_assert,
+	.rst_deassert = generic_reset_deassert,
+};
+
+static const struct udevice_id generic_reset_ids[] = {
+	{ .compatible = "generic-reset" },
+	{ .compatible = "actions,s700-reset" },
+	{ }
+};
+
+static int generic_reset_probe(struct udevice *dev)
+{
+	struct generic_reset_priv *priv = dev_get_priv(dev);
+	fdt_addr_t addr;
+	fdt_size_t size;
+
+	addr = devfdt_get_addr_size_index(dev, 0, &size);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->max_reset = dev_read_u32_default(dev, "num-resets", -1);
+	if (priv->max_reset == -1)
+		priv->max_reset = size * BITS_PER_BYTE;
+
+	priv->membase = devm_ioremap(dev, addr, size);
+	if (!priv->membase)
+		return -EFAULT;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(generic_reset) = {
+	.name = "generic_reset",
+	.id = UCLASS_RESET,
+	.of_match = generic_reset_ids,
+	.ops = &generic_reset_reset_ops,
+	.probe = generic_reset_probe,
+	.priv_auto_alloc_size = sizeof(struct generic_reset_priv),
+};