Add a driver for simple reset controllers, where each reset line is
controlled by a single bit in a memory-mapped register range.

The implementation is derived from the Linux reset-simple driver. The
controller logic itself is generic; the driver currently matches the
Metanoia MT2824 instance and further controllers can be supported by
adding their compatible to the match table.

Signed-off-by: Benoît Monin <[email protected]>
---
 MAINTAINERS                  |   5 ++
 drivers/reset/Kconfig        |   8 +++
 drivers/reset/Makefile       |   1 +
 drivers/reset/reset-simple.c | 115 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 129 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index eb4209bdea6..aed0dcc515a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1570,6 +1570,11 @@ S:       Maintained
 T:     git 
https://git.u-boot-project.org/u-boot/custodians/u-boot-nand-flash.git
 F:     drivers/mtd/nand/raw/
 
+RESET SIMPLE DRIVER
+M:     Benoît Monin <[email protected]>
+S:     Maintained
+F:     drivers/reset/reset-simple.c
+
 RISC-V
 M:     Tim Ouyang <[email protected]>
 M:     Leo Liang <[email protected]>
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index db764ed5ace..be178125be6 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -149,6 +149,14 @@ config RESET_MESON
        help
          Support for reset controller on Amlogic Meson SoC.
 
+config RESET_SIMPLE
+       bool "Generic driver for simple reset controller"
+       depends on DM_RESET
+       help
+         This enables the reset driver for simple controllers where
+         each reset line is controlled by a single bit in a memory
+         mapped region.
+
 config RESET_SOCFPGA
        bool "Reset controller driver for SoCFPGA"
        depends on DM_RESET && ARCH_SOCFPGA
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 384f4336331..553260a71e8 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_RESET_AST2600) += reset-ast2600.o
 obj-$(CONFIG_RESET_AST2700) += reset-ast2700.o
 obj-$(CONFIG_$(PHASE_)RESET_ROCKCHIP) += reset-rockchip.o rst-rk3506.o 
rst-rk3528.o rst-rk3576.o rst-rk3588.o
 obj-$(CONFIG_RESET_MESON) += reset-meson.o
+obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
 obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
 obj-$(CONFIG_RESET_MEDIATEK) += reset-mediatek.o
 obj-$(CONFIG_RESET_MTMIPS) += reset-mtmips.o
diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c
new file mode 100644
index 00000000000..5df16a341d4
--- /dev/null
+++ b/drivers/reset/reset-simple.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Simple Reset Controller Driver
+ *
+ * Based on Linux reset-simple driver
+ */
+
+#include <dm.h>
+#include <reset-uclass.h>
+#include <linux/io.h>
+
+struct reset_simple_priv {
+       void __iomem *membase;
+       int nr_resets;
+};
+
+static int reset_simple_update(struct reset_ctl *rst, bool assert)
+{
+       struct reset_simple_priv *priv = dev_get_priv(rst->dev);
+       int reg_width = sizeof(u32);
+       int bank, offset;
+       u32 reg;
+
+       if (rst->id >= priv->nr_resets)
+               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 reset_simple_request(struct reset_ctl *rst)
+{
+       struct reset_simple_priv *priv = dev_get_priv(rst->dev);
+
+       if (rst->id >= priv->nr_resets)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int reset_simple_assert(struct reset_ctl *rst)
+{
+       return reset_simple_update(rst, true);
+}
+
+static int reset_simple_deassert(struct reset_ctl *rst)
+{
+       return reset_simple_update(rst, false);
+}
+
+static int reset_simple_status(struct reset_ctl *rst)
+{
+       struct reset_simple_priv *priv = dev_get_priv(rst->dev);
+       int reg_width = sizeof(u32);
+       int bank, offset;
+       u32 reg;
+
+       if (rst->id >= priv->nr_resets)
+               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));
+
+       return !!(reg & BIT(offset));
+}
+
+static const struct reset_ops reset_simple_ops = {
+       .request = reset_simple_request,
+       .rst_assert = reset_simple_assert,
+       .rst_deassert = reset_simple_deassert,
+       .rst_status = reset_simple_status,
+};
+
+static const struct udevice_id reset_simple_ids[] = {
+       { .compatible = "metanoia,mt2824-reset" },
+       { }
+};
+
+static int reset_simple_probe(struct udevice *dev)
+{
+       struct reset_simple_priv *priv = dev_get_priv(dev);
+       fdt_addr_t addr;
+       fdt_size_t size;
+
+       addr = dev_read_addr_size_index(dev, 0, &size);
+       if (addr == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       priv->nr_resets = size * BITS_PER_BYTE;
+
+       priv->membase = devm_ioremap(dev, addr, size);
+       if (!priv->membase)
+               return -EFAULT;
+
+       return 0;
+}
+
+U_BOOT_DRIVER(reset_simple) = {
+       .name = "simple-reset",
+       .id = UCLASS_RESET,
+       .of_match = reset_simple_ids,
+       .ops = &reset_simple_ops,
+       .probe = reset_simple_probe,
+       .priv_auto = sizeof(struct reset_simple_priv),
+};

-- 
2.55.0

Reply via email to