From: <peter.wang@mediatek.com>
To: <linux-kernel@vger.kernel.org>
Cc: <p.zabel@pengutronix.de>, <robh@kernel.org>,
<krzysztof.kozlowski@linaro.org>, <conor+dt@kernel.org>,
<matthias.bgg@gmail.com>,
<angelogioacchino.delregno@collabora.com>,
<wsd_upstream@mediatek.com>, <linux-mediatek@lists.infradead.org>,
<linux-devicetree@vger.kernel.org>, <peter.wang@mediatek.com>,
<chun-hung.wu@mediatek.com>, <alice.chao@mediatek.com>,
<naomi.chu@mediatek.com>, <ed.tsai@mediatek.com>
Subject: [PATCH v1 2/2] reset: mediatek: add syscon-based reset controller driver
Date: Fri, 26 Jun 2026 15:46:09 +0800 [thread overview]
Message-ID: <20260626074820.2537772-3-peter.wang@mediatek.com> (raw)
In-Reply-To: <20260626074820.2537772-1-peter.wang@mediatek.com>
From: Peter Wang <peter.wang@mediatek.com>
Add a new reset controller driver for MediaTek SoCs that expose reset
lines through memory-mapped registers managed by a syscon node.
The driver reads reset line configurations from the device tree property
"mediatek,reset-bits", which encodes each reset as a 5-tuple of:
<assert_offset, assert_bit, deassert_offset, deassert_bit, delay_us>
This allows support for hardware designs that use separate set/clear
registers for assertion and deassertion. The regmap handle is obtained
from the parent syscon node, and the driver registers itself with the
kernel reset controller framework, providing assert, deassert, and reset
operations.
The per-line pulse width between assert and deassert is configured
via the 'delay_us' field in the 'mediatek,reset-bits' device tree
property. If set to zero, no delay is inserted.
Signed-off-by: Peter Wang <peter.wang@mediatek.com>
---
drivers/reset/Kconfig | 10 ++
drivers/reset/Makefile | 1 +
drivers/reset/reset-mediatek-syscon.c | 230 ++++++++++++++++++++++++++
3 files changed, 241 insertions(+)
create mode 100644 drivers/reset/reset-mediatek-syscon.c
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index d009eb0849a3..64586fa8bcd9 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -187,6 +187,16 @@ config RESET_MCHP_SPARX5
help
This driver supports switch core reset for the Microchip Sparx5 SoC.
+config RESET_MEDIATEK_SYSCON
+ tristate "MediaTek Syscon Reset Driver"
+ depends on (ARCH_MEDIATEK || COMPILE_TEST) && HAS_IOMEM && OF
+ select MFD_SYSCON
+ help
+ This enables reset driver support for MediaTek devices
+ that use memory-mapped reset registers as part of a syscon
+ device node. Say Y if you want to control reset signals
+ provided by this controller. Otherwise, say N.
+
config RESET_NPCM
bool "NPCM BMC Reset Driver" if COMPILE_TEST
default ARCH_NPCM
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 3e52569bd276..c0a7fdbe9768 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_RESET_K230) += reset-k230.o
obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
obj-$(CONFIG_RESET_MCHP_SPARX5) += reset-microchip-sparx5.o
+obj-$(CONFIG_RESET_MEDIATEK_SYSCON) += reset-mediatek-syscon.o
obj-$(CONFIG_RESET_NPCM) += reset-npcm.o
obj-$(CONFIG_RESET_NUVOTON_MA35D1) += reset-ma35d1.o
obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
diff --git a/drivers/reset/reset-mediatek-syscon.c b/drivers/reset/reset-mediatek-syscon.c
new file mode 100644
index 000000000000..da8aa646cee7
--- /dev/null
+++ b/drivers/reset/reset-mediatek-syscon.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 MediaTek Inc.
+ * Authors:
+ * Peter Wang <peter.wang@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+/* Must match the maximum value in mediatek,syscon-reset.yaml */
+#define MEDIATEK_SYSCON_RESET_MAX_DELAY_US 1000000 /* 1 second */
+
+/**
+ * struct mediatek_syscon_reset_control - The control for a single reset line.
+ *
+ * @assert_offset: Register offset from syscon base to assert the reset.
+ * @assert_bit: Bit index within the assert register to set for assertion.
+ * @deassert_offset: Register offset from syscon base to deassert the reset.
+ * @deassert_bit: Bit index within the deassert register to set for deassertion.
+ * @delay_us: Delay in microseconds between assertion and deassertion.
+ * Zero means no delay is inserted.
+ */
+struct mediatek_syscon_reset_control {
+ unsigned int assert_offset;
+ unsigned int assert_bit;
+ unsigned int deassert_offset;
+ unsigned int deassert_bit;
+ unsigned int delay_us;
+};
+
+/**
+ * struct mediatek_syscon_reset_data - Private data for a MediaTek syscon
+ * reset controller.
+ *
+ * @rcdev: The embedded reset controller device, registered with the core.
+ * @regmap: The regmap handle for accessing hardware reset registers.
+ * @mrstc: Pointer to an array of reset line configurations
+ * (struct mediatek_syscon_reset_control).
+ */
+struct mediatek_syscon_reset_data {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+ struct mediatek_syscon_reset_control *mrstc;
+};
+
+/**
+ * mediatek_syscon_reset_assert() - Assert a specific reset line.
+ * @rcdev: The reset controller device.
+ * @id: The index of the reset line to assert.
+ *
+ * This function implements the .assert operation for the reset controller. It
+ * sets the specific assertion bit for the given reset ID by writing to the
+ * hardware register defined in the driver's configuration data.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int mediatek_syscon_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct mediatek_syscon_reset_data *data =
+ container_of(rcdev, struct mediatek_syscon_reset_data, rcdev);
+ struct mediatek_syscon_reset_control *rstc = &data->mrstc[id];
+ unsigned int val = BIT(rstc->assert_bit);
+
+ return regmap_write_bits(data->regmap, rstc->assert_offset, val, val);
+}
+
+/**
+ * mediatek_syscon_reset_deassert() - Deassert a specific reset line.
+ * @rcdev: The reset controller device.
+ * @id: The index of the reset line to deassert.
+ *
+ * Writes a '1' to the bit in the dedicated hardware deassert (clear) register
+ * for the given reset line. This assumes the hardware uses separate
+ * set-to-assert and set-to-deassert registers; @deassert_offset must point
+ * to the clear register, not the same register as @assert_offset.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int mediatek_syscon_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct mediatek_syscon_reset_data *data =
+ container_of(rcdev, struct mediatek_syscon_reset_data, rcdev);
+ struct mediatek_syscon_reset_control *rstc = &data->mrstc[id];
+ unsigned int val = BIT(rstc->deassert_bit);
+
+ return regmap_write_bits(data->regmap, rstc->deassert_offset, val, val);
+}
+
+/**
+ * mediatek_syscon_reset_reset() - Assert then deassert a reset line.
+ * @rcdev: The reset controller device.
+ * @id: The index of the reset line to reset.
+ *
+ * Performs a full reset cycle by asserting the reset line, waiting for the
+ * per-line hardware-required pulse width defined in the 'mediatek,reset-bits'
+ * device tree property, then deasserting it. If the pulse width is zero,
+ * no delay is inserted.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int mediatek_syscon_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct mediatek_syscon_reset_data *data =
+ container_of(rcdev, struct mediatek_syscon_reset_data, rcdev);
+ struct mediatek_syscon_reset_control *rstc = &data->mrstc[id];
+ int ret;
+
+ ret = mediatek_syscon_reset_assert(rcdev, id);
+ if (ret)
+ return ret;
+
+ if (rstc->delay_us)
+ fsleep(rstc->delay_us);
+
+ return mediatek_syscon_reset_deassert(rcdev, id);
+}
+
+static const struct reset_control_ops mediatek_syscon_reset_ops = {
+ .assert = mediatek_syscon_reset_assert,
+ .deassert = mediatek_syscon_reset_deassert,
+ .reset = mediatek_syscon_reset_reset,
+};
+
+static int mediatek_syscon_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mediatek_syscon_reset_control *mrstc;
+ struct mediatek_syscon_reset_data *data;
+ struct device_node *np = dev->of_node;
+ int nr_resets, i, count, idx, ret;
+ struct regmap *regmap;
+ u32 *arr;
+
+ if (!dev->parent) {
+ dev_err(dev, "missing parent device node\n");
+ return -EINVAL;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ regmap = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ count = of_property_count_u32_elems(np, "mediatek,reset-bits");
+ if (count <= 0) {
+ dev_err(dev, "failed to read mediatek,reset-bits property: %d\n", count);
+ return -EINVAL;
+ }
+ if (count % 5 != 0) {
+ dev_err(dev, "mediatek,reset-bits count (%d) must be a multiple of 5\n", count);
+ return -EINVAL;
+ }
+
+ nr_resets = count / 5;
+
+ arr = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
+ if (!arr)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "mediatek,reset-bits", arr, count);
+ if (ret) {
+ dev_err(dev, "failed to read mediatek,reset-bits: %d\n", ret);
+ return ret;
+ }
+
+ mrstc = devm_kcalloc(dev, nr_resets, sizeof(*mrstc), GFP_KERNEL);
+ if (!mrstc)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_resets; i++) {
+ idx = i * 5;
+ mrstc[i].assert_offset = arr[idx];
+ mrstc[i].assert_bit = arr[idx + 1];
+ mrstc[i].deassert_offset = arr[idx + 2];
+ mrstc[i].deassert_bit = arr[idx + 3];
+ mrstc[i].delay_us = arr[idx + 4];
+
+ if (mrstc[i].assert_bit >= 32 || mrstc[i].deassert_bit >= 32) {
+ dev_err(dev, "reset[%d]: bit index out of range\n", i);
+ return -EINVAL;
+ }
+
+ if (mrstc[i].delay_us > MEDIATEK_SYSCON_RESET_MAX_DELAY_US) {
+ dev_err(dev, "reset[%d]: delay %u us exceeds maximum %u us\n",
+ i, mrstc[i].delay_us,
+ MEDIATEK_SYSCON_RESET_MAX_DELAY_US);
+ return -EINVAL;
+ }
+ }
+
+ data->regmap = regmap;
+ data->mrstc = mrstc;
+ data->rcdev.ops = &mediatek_syscon_reset_ops;
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.of_node = np;
+ data->rcdev.nr_resets = nr_resets;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static const struct of_device_id mediatek_syscon_reset_of_match[] = {
+ { .compatible = "mediatek,syscon-reset" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mediatek_syscon_reset_of_match);
+
+static struct platform_driver mediatek_syscon_reset_driver = {
+ .probe = mediatek_syscon_reset_probe,
+ .driver = {
+ .name = "mediatek-syscon-reset",
+ .of_match_table = mediatek_syscon_reset_of_match,
+ },
+};
+module_platform_driver(mediatek_syscon_reset_driver);
+
+MODULE_AUTHOR("Peter Wang <peter.wang@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek SYSCON Regmap Reset Driver");
+MODULE_LICENSE("GPL v2");
--
2.45.2
prev parent reply other threads:[~2026-06-26 7:48 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-26 7:46 [PATCH v1 0/2] reset: mediatek: add syscon-based reset controller peter.wang
2026-06-26 7:46 ` [PATCH v1 1/2] Documentation: dt: reset: add mediatek,syscon-reset binding peter.wang
2026-06-26 8:33 ` Philipp Zabel
2026-06-26 15:53 ` Conor Dooley
2026-07-01 6:36 ` Peter Wang (王信友)
2026-07-01 6:35 ` Peter Wang (王信友)
2026-07-01 16:57 ` Conor Dooley
2026-07-02 8:52 ` Peter Wang (王信友)
2026-07-02 18:44 ` Conor Dooley
2026-07-02 8:55 ` AngeloGioacchino Del Regno
2026-07-02 9:39 ` Peter Wang (王信友)
2026-07-03 9:21 ` Philipp Zabel
2026-06-26 7:46 ` peter.wang [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260626074820.2537772-3-peter.wang@mediatek.com \
--to=peter.wang@mediatek.com \
--cc=alice.chao@mediatek.com \
--cc=angelogioacchino.delregno@collabora.com \
--cc=chun-hung.wu@mediatek.com \
--cc=conor+dt@kernel.org \
--cc=ed.tsai@mediatek.com \
--cc=krzysztof.kozlowski@linaro.org \
--cc=linux-devicetree@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=matthias.bgg@gmail.com \
--cc=naomi.chu@mediatek.com \
--cc=p.zabel@pengutronix.de \
--cc=robh@kernel.org \
--cc=wsd_upstream@mediatek.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox