diff mbox

[U-Boot,v1,05/11] sunxi: add module reset (UCLASS_RESET) support for sunxi

Message ID 3c7b1cdadabb3eed64fa84a1c5475e0c5b443ef8.1487349600.git.philipp.tomsich@theobroma-systems.com
State Changes Requested
Delegated to: Jagannadha Sutradharudu Teki
Headers show

Commit Message

Philipp Tomsich Feb. 17, 2017, 5:52 p.m. UTC
In order to have the device model describe the module reset bits
on sunxi (well, at least for anything newer than sun6i), we need
a (rather simple) driver for 'allwinner,sun6i-a31-clock-reset'
nodes.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 drivers/reset/Kconfig       |   9 ++++
 drivers/reset/Makefile      |   1 +
 drivers/reset/reset-sunxi.c | 107 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+)
 create mode 100644 drivers/reset/reset-sunxi.c

Comments

Maxime Ripard Feb. 21, 2017, 8:16 p.m. UTC | #1
On Fri, Feb 17, 2017 at 06:52:42PM +0100, Philipp Tomsich wrote:
> In order to have the device model describe the module reset bits
> on sunxi (well, at least for anything newer than sun6i), we need
> a (rather simple) driver for 'allwinner,sun6i-a31-clock-reset'
> nodes.

This one (and the next one) isn't the binding that we've settled for
in the kernel. This DT was only one of the intermediate versions that
got sent.

Maxime
diff mbox

Patch

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index c42b0bc..8db25fc 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -43,4 +43,13 @@  config RESET_UNIPHIER
 	  Say Y if you want to control reset signals provided by System Control
 	  block, Media I/O block, Peripheral Block.
 
+config RESET_SUNXI
+        bool "Reset controller driver for Allwiner SoCs"
+	depends on DM_RESET && ARCH_SUNXI
+	default y
+	help
+	  Support for reset controllers on Allwinner SoCs.
+	  Say Y if you want to control reset signals provided by CCU (e.g. sun50i)
+	  or PRCM (e.g. sun6i, sun9i) blocks.
+
 endmenu
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 5c4305c..0086da9 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -1,10 +1,11 @@ 
 # Copyright (c) 2016, NVIDIA CORPORATION.
 #
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_DM_RESET) += reset-uclass.o
 obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset.o
 obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset-test.o
 obj-$(CONFIG_TEGRA_CAR_RESET) += tegra-car-reset.o
 obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o
 obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
+obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c
new file mode 100644
index 0000000..b667ca1
--- /dev/null
+++ b/drivers/reset/reset-sunxi.c
@@ -0,0 +1,107 @@ 
+/*
+ * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <reset-uclass.h>
+#include <dm/device.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/sizes.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sunxi_reset_priv {
+	void __iomem *base;
+	size_t  size;
+};
+
+static int sunxi_reset_request(struct reset_ctl *reset_ctl)
+{
+	debug("%s (%s): id %ld\n",
+	      reset_ctl->dev->name, __func__, reset_ctl->id);
+	return 0;
+}
+
+static int sunxi_reset_free(struct reset_ctl *reset_ctl)
+{
+	debug("%s (%s): id %ld\n",
+	      reset_ctl->dev->name, __func__, reset_ctl->id);
+	return 0;
+}
+
+static int sunxi_reset_update(struct reset_ctl *reset_ctl, bool assert)
+{
+	struct sunxi_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+	unsigned long id = reset_ctl->id;
+	unsigned long offset = id / 32; /* TODO: symbolic name */
+	unsigned int bit = id % 32;
+
+	debug("%s (%s): id %ld base %p offset %lx bit %d assert %d size %ld\n",
+	      reset_ctl->dev->name, __func__, id, priv->base, offset,
+	      bit, assert, priv->size);
+
+	if (offset >= priv->size)
+		return -EINVAL;
+
+	if (assert)
+		clrbits_le32(priv->base + offset, BIT(bit));
+	else
+		setbits_le32(priv->base + offset, BIT(bit));
+
+	return 0;
+}
+
+static int sunxi_reset_assert(struct reset_ctl *reset_ctl)
+{
+	return sunxi_reset_update(reset_ctl, true);
+}
+
+static int sunxi_reset_deassert(struct reset_ctl *reset_ctl)
+{
+	return sunxi_reset_update(reset_ctl, false);
+}
+
+static const struct reset_ops sunxi_reset_ops = {
+	.request = sunxi_reset_request,
+	.free = sunxi_reset_free,
+	.rst_assert = sunxi_reset_assert,
+	.rst_deassert = sunxi_reset_deassert,
+};
+
+static int sunxi_reset_probe(struct udevice *dev)
+{
+	struct sunxi_reset_priv *priv = dev_get_priv(dev);
+	fdt_addr_t addr;
+	fdt_size_t size;
+
+	addr = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev->of_offset,
+						  "reg", 0, &size, false);
+	if (addr == FDT_ADDR_T_NONE) {
+		debug("%s: failed to find base address ('reg')\n", dev->name);
+		return -ENODEV;
+	}
+	priv->base = (void *)addr;
+	priv->size = size;
+
+	if (!priv->base)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static const struct udevice_id sunxi_reset_match[] = {
+	{ .compatible = "allwinner,sun6i-a31-clock-reset" },
+	{ }
+};
+
+U_BOOT_DRIVER(sunxi_reset) = {
+	.name = "sunxi-reset",
+	.id = UCLASS_RESET,
+	.of_match = sunxi_reset_match,
+	.ops = &sunxi_reset_ops,
+	.priv_auto_alloc_size = sizeof(struct sunxi_reset_priv),
+	.probe = sunxi_reset_probe,
+};