reset: Add Allwinner RESET driver
authorJagan Teki <jagan@amarulasolutions.com>
Fri, 18 Jan 2019 16:48:13 +0000 (22:18 +0530)
committerJagan Teki <jagan@amarulasolutions.com>
Fri, 18 Jan 2019 16:49:08 +0000 (22:19 +0530)
Add common reset driver for all Allwinner SoC's.

Since CLK and RESET share common DT compatible, it is CLK driver
job is to bind the reset driver. So add CLK bind call on respective
SoC driver by passing ccu map descriptor so-that reset deassert,
deassert operations held based on ccu reset table defined from
CLK driver.

Select DM_RESET via CLK_SUNXI, this make hidden section of RESET
since CLK and RESET share common DT compatible and code.

Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
Acked-by: Maxime Ripard <maxime.ripard@bootlin.com>
arch/arm/include/asm/arch-sunxi/ccu.h
drivers/clk/sunxi/Kconfig
drivers/clk/sunxi/clk_a64.c
drivers/reset/Kconfig
drivers/reset/Makefile
drivers/reset/reset-sunxi.c [new file with mode: 0644]

index 24efe0ab0a15fa8ace061111f150cd8a7f473827..5dd97ab227303d6b340196f9c8681f70616e7507 100644 (file)
@@ -8,12 +8,14 @@
 #define _ASM_ARCH_CCU_H
 
 /**
- * enum ccu_flags - ccu clock flags
+ * enum ccu_flags - ccu clock/reset flags
  *
  * @CCU_CLK_F_IS_VALID:                is given clock gate is valid?
+ * @CCU_RST_F_IS_VALID:                is given reset control is valid?
  */
 enum ccu_flags {
        CCU_CLK_F_IS_VALID              = BIT(0),
+       CCU_RST_F_IS_VALID              = BIT(1),
 };
 
 /**
@@ -34,13 +36,33 @@ struct ccu_clk_gate {
        .flags = CCU_CLK_F_IS_VALID,            \
 }
 
+/**
+ * struct ccu_reset - ccu reset
+ * @off:       reset offset
+ * @bit:       reset bit
+ * @flags:     ccu reset control flags
+ */
+struct ccu_reset {
+       u16 off;
+       u32 bit;
+       enum ccu_flags flags;
+};
+
+#define RESET(_off, _bit) {                    \
+       .off = _off,                            \
+       .bit = _bit,                            \
+       .flags = CCU_RST_F_IS_VALID,            \
+}
+
 /**
  * struct ccu_desc - clock control unit descriptor
  *
  * @gates:     clock gates
+ * @resets:    reset unit
  */
 struct ccu_desc {
        const struct ccu_clk_gate *gates;
+       const struct ccu_reset *resets;
 };
 
 /**
@@ -62,4 +84,13 @@ int sunxi_clk_probe(struct udevice *dev);
 
 extern struct clk_ops sunxi_clk_ops;
 
+/**
+ * sunxi_reset_bind() - reset binding
+ *
+ * @dev:       reset device
+ * @count:     reset count
+ * @return 0 success, or error value
+ */
+int sunxi_reset_bind(struct udevice *dev, ulong count);
+
 #endif /* _ASM_ARCH_CCU_H */
index bf5ecb380102dd96e0bebec08bc561d4251c564c..041d711e58c13405e59b30a27b598d83fb7b80bb 100644 (file)
@@ -1,6 +1,7 @@
 config CLK_SUNXI
        bool "Clock support for Allwinner SoCs"
        depends on CLK && ARCH_SUNXI
+       select DM_RESET
        default y
        help
          This enables support for common clock driver API on Allwinner
index 803a2f711d782130395a2415b46cc36874929a7f..eb0a45d97ff145a713732c1fc5bc677f43a06356 100644 (file)
@@ -10,6 +10,7 @@
 #include <errno.h>
 #include <asm/arch/ccu.h>
 #include <dt-bindings/clock/sun50i-a64-ccu.h>
+#include <dt-bindings/reset/sun50i-a64-ccu.h>
 
 static const struct ccu_clk_gate a64_gates[] = {
        [CLK_BUS_OTG]           = GATE(0x060, BIT(23)),
@@ -26,10 +27,28 @@ static const struct ccu_clk_gate a64_gates[] = {
        [CLK_USB_OHCI1]         = GATE(0x0cc, BIT(17)),
 };
 
+static const struct ccu_reset a64_resets[] = {
+       [RST_USB_PHY0]          = RESET(0x0cc, BIT(0)),
+       [RST_USB_PHY1]          = RESET(0x0cc, BIT(1)),
+       [RST_USB_HSIC]          = RESET(0x0cc, BIT(2)),
+
+       [RST_BUS_OTG]           = RESET(0x2c0, BIT(23)),
+       [RST_BUS_EHCI0]         = RESET(0x2c0, BIT(24)),
+       [RST_BUS_EHCI1]         = RESET(0x2c0, BIT(25)),
+       [RST_BUS_OHCI0]         = RESET(0x2c0, BIT(28)),
+       [RST_BUS_OHCI1]         = RESET(0x2c0, BIT(29)),
+};
+
 static const struct ccu_desc a64_ccu_desc = {
        .gates = a64_gates,
+       .resets = a64_resets,
 };
 
+static int a64_clk_bind(struct udevice *dev)
+{
+       return sunxi_reset_bind(dev, ARRAY_SIZE(a64_resets));
+}
+
 static const struct udevice_id a64_ccu_ids[] = {
        { .compatible = "allwinner,sun50i-a64-ccu",
          .data = (ulong)&a64_ccu_desc },
@@ -43,4 +62,5 @@ U_BOOT_DRIVER(clk_sun50i_a64) = {
        .priv_auto_alloc_size   = sizeof(struct ccu_priv),
        .ops            = &sunxi_clk_ops,
        .probe          = sunxi_clk_probe,
+       .bind           = a64_clk_bind,
 };
index 3a6d61f440cda46cb80d16c67757666552d4be99..a81e767696047de0ff47ee6eb9784f13a0592ed8 100644 (file)
@@ -113,4 +113,12 @@ config RESET_MEDIATEK
        help
          Support for reset controller on MediaTek SoCs.
 
+config RESET_SUNXI
+       bool "RESET support for Allwinner SoCs"
+       depends on DM_RESET && ARCH_SUNXI
+       default y
+       help
+         This enables support for common reset driver for
+         Allwinner SoCs.
+
 endmenu
index 8a4dcab8f6472d39eb5b80c32581f5167464499a..4fad7d412985efa7945ec1511d71c59bb29b58cf 100644 (file)
@@ -18,3 +18,4 @@ obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o
 obj-$(CONFIG_RESET_MESON) += reset-meson.o
 obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
 obj-$(CONFIG_RESET_MEDIATEK) += reset-mediatek.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 (file)
index 0000000..364dc52
--- /dev/null
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2018 Amarula Solutions.
+ * Author: Jagan Teki <jagan@amarulasolutions.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <reset-uclass.h>
+#include <asm/io.h>
+#include <dm/lists.h>
+#include <linux/log2.h>
+#include <asm/arch/ccu.h>
+
+struct sunxi_reset_priv {
+       void *base;
+       ulong count;
+       const struct ccu_desc *desc;
+};
+
+static const struct ccu_reset *priv_to_reset(struct sunxi_reset_priv *priv,
+                                            unsigned long id)
+{
+       return  &priv->desc->resets[id];
+}
+
+static int sunxi_reset_request(struct reset_ctl *reset_ctl)
+{
+       struct sunxi_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+
+       debug("%s: (RST#%ld)\n", __func__, reset_ctl->id);
+
+       if (reset_ctl->id >= priv->count)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int sunxi_reset_free(struct reset_ctl *reset_ctl)
+{
+       debug("%s: (RST#%ld)\n", __func__, reset_ctl->id);
+
+       return 0;
+}
+
+static int sunxi_set_reset(struct reset_ctl *reset_ctl, bool on)
+{
+       struct sunxi_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+       const struct ccu_reset *reset = priv_to_reset(priv, reset_ctl->id);
+       u32 reg;
+
+       if (!(reset->flags & CCU_RST_F_IS_VALID)) {
+               printf("%s: (RST#%ld) unhandled\n", __func__, reset_ctl->id);
+               return 0;
+       }
+
+       debug("%s: (RST#%ld) off#0x%x, BIT(%d)\n", __func__,
+             reset_ctl->id, reset->off, ilog2(reset->bit));
+
+       reg = readl(priv->base + reset->off);
+       if (on)
+               reg |= reset->bit;
+       else
+               reg &= ~reset->bit;
+
+       writel(reg, priv->base + reset->off);
+
+       return 0;
+}
+
+static int sunxi_reset_assert(struct reset_ctl *reset_ctl)
+{
+       return sunxi_set_reset(reset_ctl, false);
+}
+
+static int sunxi_reset_deassert(struct reset_ctl *reset_ctl)
+{
+       return sunxi_set_reset(reset_ctl, true);
+}
+
+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);
+
+       priv->base = dev_read_addr_ptr(dev);
+
+       return 0;
+}
+
+int sunxi_reset_bind(struct udevice *dev, ulong count)
+{
+       struct udevice *rst_dev;
+       struct sunxi_reset_priv *priv;
+       int ret;
+
+       ret = device_bind_driver_to_node(dev, "sunxi_reset", "reset",
+                                        dev_ofnode(dev), &rst_dev);
+       if (ret) {
+               debug("failed to bind sunxi_reset driver (ret=%d)\n", ret);
+               return ret;
+       }
+       priv = malloc(sizeof(struct sunxi_reset_priv));
+       priv->count = count;
+       priv->desc = (const struct ccu_desc *)dev_get_driver_data(dev);
+       rst_dev->priv = priv;
+
+       return 0;
+}
+
+U_BOOT_DRIVER(sunxi_reset) = {
+       .name           = "sunxi_reset",
+       .id             = UCLASS_RESET,
+       .ops            = &sunxi_reset_ops,
+       .probe          = sunxi_reset_probe,
+       .priv_auto_alloc_size = sizeof(struct sunxi_reset_priv),
+};