linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 06/25] rtc: Add driver for RDA Micro SoC
  2025-09-16 20:07 Dang Huynh
@ 2025-09-16 20:07 ` Dang Huynh
  0 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh @ 2025-09-16 20:07 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

The RDA Micro SoC has built-in RTC, it supports read/write date
as well as alarm.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 MAINTAINERS           |   6 +
 drivers/rtc/Kconfig   |  11 ++
 drivers/rtc/Makefile  |   1 +
 drivers/rtc/rtc-rda.c | 356 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 374 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fa7f80bd7b2f8bd2099acb9f38070498e7b1cc7e..0549b1d0657f2caaf86a723db139cf9d84d59c4a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21393,6 +21393,12 @@ S:	Supported
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
 F:	tools/testing/selftests/rcutorture
 
+RDA MICRO REAL TIME CLOCK DRIVER
+M:	Dang Huynh <dang.huynh@mainlining.org>
+S:	Maintained
+F:	Documentation/devicetree/bindings/rtc/rda,8810pl-rtc.yaml
+F:	drivers/rtc/rtc-rda.c
+
 RDACM20 Camera Sensor
 M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
 M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 64f6e9756aff4a1f6f6c50f9b4fc2140f66a8578..287fc3bbd474ab78a9bd3b8813e8b9d475c07198 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1471,6 +1471,17 @@ config RTC_DRV_OMAP
 	  This driver can also be built as a module, if so, module
 	  will be called rtc-omap.
 
+config RTC_DRV_RDA
+	tristate "RDA Micro RTC"
+	depends on ARCH_RDA || COMPILE_TEST
+	select REGMAP_MMIO
+	help
+	  If you say yes here you get support for the built-in RTC on
+	  RDA Micro SoC.
+
+	  This driver can also be built as a module, if so, the module
+	  will be called rtc-rda.
+
 config RTC_DRV_S3C
 	tristate "Samsung S3C series SoC RTC"
 	depends on ARCH_EXYNOS || ARCH_S3C64XX || ARCH_S5PV210 || \
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 789bddfea99d8fcd024566891c37ee73e527cf93..02f73062bb158fe4738a3043c58ee40f8a58b3c6 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -141,6 +141,7 @@ obj-$(CONFIG_RTC_DRV_PS3)	+= rtc-ps3.o
 obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o
 obj-$(CONFIG_RTC_DRV_R7301)	+= rtc-r7301.o
 obj-$(CONFIG_RTC_DRV_R9701)	+= rtc-r9701.o
+obj-$(CONFIG_RTC_DRV_RDA)	+= rtc-rda.o
 obj-$(CONFIG_RTC_DRV_RC5T583)	+= rtc-rc5t583.o
 obj-$(CONFIG_RTC_DRV_RC5T619)	+= rtc-rc5t619.o
 obj-$(CONFIG_RTC_DRV_RK808)	+= rtc-rk808.o
diff --git a/drivers/rtc/rtc-rda.c b/drivers/rtc/rtc-rda.c
new file mode 100644
index 0000000000000000000000000000000000000000..bb5aa25fb7d0ad538a0f7f67a80d08fe67af1c5d
--- /dev/null
+++ b/drivers/rtc/rtc-rda.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RTC driver for RDA Micro
+ *
+ * Copyright (C) 2013-2014 RDA Microelectronics Inc.
+ * Copyright (C) 2024 Dang Huynh <dang.huynh@mainlining.org>
+ */
+
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+struct rda_rtc {
+	struct rtc_device *rtc_dev;
+	struct regmap *regmap;
+};
+
+/* RTC Registers */
+#define RDA_RTC_CTRL_REG 0x0
+#define RDA_RTC_CMD_REG 0x4
+#define RDA_RTC_STA_REG 0x8
+#define RDA_RTC_CAL_LOAD_LOW_REG 0xC
+#define RDA_RTC_CAL_LOAD_HIGH_REG 0x10
+#define RDA_RTC_CUR_LOAD_LOW_REG 0x14
+#define RDA_RTC_CUR_LOAD_HIGH_REG 0x18
+#define RDA_RTC_ALARM_LOW_REG 0x1C
+#define RDA_RTC_ALARM_HIGH_REG 0x20
+
+/* RTC Bits */
+#define RDA_RTC_CMD_CAL_LOAD BIT(0)
+#define RDA_RTC_CMD_ALARM_LOAD BIT(4)
+#define RDA_RTC_CMD_ALARM_ENABLE BIT(5)
+#define RDA_RTC_CMD_ALARM_DISABLE BIT(6)
+#define RDA_RTC_CMD_INVALID BIT(31)
+#define RDA_RTC_STA_ALARM_ENABLE BIT(20)
+#define RDA_RTC_STA_NOT_PROG BIT(31)
+
+/* RTC Masks */
+#define RDA_SEC_MASK GENMASK(7, 0)
+#define RDA_MIN_MASK GENMASK(15, 8)
+#define RDA_HRS_MASK GENMASK(23, 16)
+
+#define RDA_MDAY_MASK GENMASK(7, 0)
+#define RDA_MON_MASK GENMASK(11, 8)
+#define RDA_YEAR_MASK GENMASK(22, 16)
+#define RDA_WDAY_MASK GENMASK(26, 24)
+
+static int rda_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+	u32 high, low;
+	int ret;
+
+	ret = rtc_valid_tm(tm);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * The number of years since 1900 in kernel,
+	 * but it is defined since 2000 by HW.
+	 * The number of mons' range is from 0 to 11 in kernel,
+	 * but it is defined from 1 to 12 by HW.
+	 */
+	low = FIELD_PREP(RDA_SEC_MASK, tm->tm_sec) |
+		FIELD_PREP(RDA_MIN_MASK, tm->tm_min) |
+		FIELD_PREP(RDA_HRS_MASK, tm->tm_hour);
+
+	high = FIELD_PREP(RDA_MDAY_MASK, tm->tm_mday) |
+		FIELD_PREP(RDA_MON_MASK, tm->tm_mon + 1) |
+		FIELD_PREP(RDA_YEAR_MASK, tm->tm_year - 100) |
+		FIELD_PREP(RDA_WDAY_MASK, tm->tm_wday);
+
+	ret = regmap_write(rtc->regmap, RDA_RTC_CAL_LOAD_LOW_REG, low);
+	if (ret < 0) {
+		dev_err(dev, "Failed to update RTC low register: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_write(rtc->regmap, RDA_RTC_CAL_LOAD_HIGH_REG, high);
+	if (ret < 0) {
+		dev_err(dev, "Failed to update RTC low register: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG, RDA_RTC_CMD_CAL_LOAD, 1);
+	if (ret < 0) {
+		dev_err(dev, "Failed to update RTC cal load register: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rda_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+	unsigned int high, low;
+	int ret;
+
+	/*
+	 * Check if RTC data is valid.
+	 *
+	 * When this bit is set, it means the data in the RTC is invalid
+	 * or not configured.
+	 */
+	ret = regmap_test_bits(rtc->regmap, RDA_RTC_STA_REG, RDA_RTC_STA_NOT_PROG);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read RTC status: %d\n", ret);
+		return ret;
+	} else if (ret > 0)
+		return -EINVAL;
+
+	ret = regmap_read(rtc->regmap, RDA_RTC_CUR_LOAD_HIGH_REG, &high);
+	if (ret) {
+		dev_err(dev, "Failed to read RTC high reg: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_read(rtc->regmap, RDA_RTC_CUR_LOAD_LOW_REG, &low);
+	if (ret) {
+		dev_err(dev, "Failed to read RTC low reg: %d\n", ret);
+		return ret;
+	}
+
+	tm->tm_sec = FIELD_GET(RDA_SEC_MASK, low);
+	tm->tm_min = FIELD_GET(RDA_MIN_MASK, low);
+	tm->tm_hour = FIELD_GET(RDA_HRS_MASK, low);
+	tm->tm_mday = FIELD_GET(RDA_MDAY_MASK, high);
+	tm->tm_mon = FIELD_GET(RDA_MON_MASK, high);
+	tm->tm_year = FIELD_GET(RDA_YEAR_MASK, high);
+	tm->tm_wday = FIELD_GET(RDA_WDAY_MASK, high);
+
+	/*
+	 * The number of years since 1900 in kernel,
+	 * but it is defined since 2000 by HW.
+	 */
+	tm->tm_year += 100;
+	/*
+	 * The number of mons' range is from 0 to 11 in kernel,
+	 * but it is defined from 1 to 12 by HW.
+	 */
+	tm->tm_mon -= 1;
+
+	return 0;
+}
+
+static int rda_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+	struct rtc_time *tm = &alrm->time;
+	unsigned int high, low;
+	int ret;
+
+	ret = regmap_read(rtc->regmap, RDA_RTC_ALARM_HIGH_REG, &high);
+	if (ret) {
+		dev_err(dev, "Failed to read alarm low reg: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_read(rtc->regmap, RDA_RTC_ALARM_LOW_REG, &low);
+	if (ret) {
+		dev_err(dev, "Failed to read alarm low reg: %d\n", ret);
+		return ret;
+	}
+
+	tm->tm_sec = FIELD_GET(RDA_SEC_MASK, low);
+	tm->tm_min = FIELD_GET(RDA_MIN_MASK, low);
+	tm->tm_hour = FIELD_GET(RDA_HRS_MASK, low);
+	tm->tm_mday = FIELD_GET(RDA_MDAY_MASK, high);
+	tm->tm_mon = FIELD_GET(RDA_MON_MASK, high);
+	tm->tm_year = FIELD_GET(RDA_YEAR_MASK, high);
+	tm->tm_wday = FIELD_GET(RDA_WDAY_MASK, high);
+
+	/*
+	 * The number of years since 1900 in kernel,
+	 * but it is defined since 2000 by HW.
+	 */
+	tm->tm_year += 100;
+	/*
+	 * The number of mons' range is from 0 to 11 in kernel,
+	 * but it is defined from 1 to 12 by HW.
+	 */
+	tm->tm_mon -= 1;
+
+	return 0;
+}
+
+static int rda_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+
+	if (enabled)
+		return regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG,
+				RDA_RTC_CMD_ALARM_ENABLE, 1);
+
+	return regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG,
+			RDA_RTC_CMD_ALARM_DISABLE, 1);
+}
+
+static int rda_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+	struct rtc_time *tm = &alrm->time;
+	u32 high, low;
+	int ret;
+
+	ret = rtc_valid_tm(tm);
+	if (ret < 0)
+		return ret;
+
+	/* TODO: Check if it's necessary to disable IRQ first */
+	rda_rtc_alarm_irq_enable(dev, 0);
+
+	/*
+	 * The number of years since 1900 in kernel,
+	 * but it is defined since 2000 by HW.
+	 * The number of mons' range is from 0 to 11 in kernel,
+	 * but it is defined from 1 to 12 by HW.
+	 */
+	low = FIELD_PREP(RDA_SEC_MASK, tm->tm_sec) |
+		FIELD_PREP(RDA_MIN_MASK, tm->tm_min) |
+		FIELD_PREP(RDA_HRS_MASK, tm->tm_hour);
+
+	high = FIELD_PREP(RDA_MDAY_MASK, tm->tm_mday) |
+		FIELD_PREP(RDA_MON_MASK, tm->tm_mon + 1) |
+		FIELD_PREP(RDA_YEAR_MASK, tm->tm_year - 100) |
+		FIELD_PREP(RDA_WDAY_MASK, tm->tm_wday);
+
+
+	ret = regmap_write(rtc->regmap, RDA_RTC_ALARM_LOW_REG, low);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set low alarm register: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_write(rtc->regmap, RDA_RTC_ALARM_HIGH_REG, high);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set low alarm register: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG, RDA_RTC_CMD_ALARM_LOAD, 1);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set alarm register: %d\n", ret);
+		return ret;
+	}
+
+	dev_dbg(dev, "Alarm set: %4d-%02d-%02d %02d:%02d:%02d\n",
+			2000 + (tm->tm_year - 100), tm->tm_mon + 1, tm->tm_mday,
+			tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+	return 0;
+}
+
+static int rda_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+	int ret;
+
+	ret = regmap_test_bits(rtc->regmap, RDA_RTC_STA_REG, RDA_RTC_STA_ALARM_ENABLE);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read alarm status: %d\n", ret);
+		return ret;
+	}
+
+	seq_printf(seq, "alarm enable\t: %s\n", (ret > 0) ? "yes" : "no");
+
+	return 0;
+}
+
+static const struct rtc_class_ops rda_rtc_ops = {
+	.read_time = rda_rtc_readtime,
+	.set_time = rda_rtc_settime,
+	.read_alarm = rda_rtc_readalarm,
+	.set_alarm = rda_rtc_setalarm,
+	.proc = rda_rtc_proc,
+	.alarm_irq_enable = rda_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rda_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	/* TODO: Check if it's okay to turn on alarm IRQ when it's not set */
+	return rda_rtc_alarm_irq_enable(&pdev->dev, 1);
+}
+
+static int rda_rtc_resume(struct platform_device *pdev)
+{
+	/* If alarms were left, we turn them off. */
+	return rda_rtc_alarm_irq_enable(&pdev->dev, 0);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rda_rtc_pm_ops, rda_rtc_suspend, rda_rtc_resume);
+
+static const struct regmap_config regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static int rda_rtc_probe(struct platform_device *pdev)
+{
+	struct rda_rtc *rda_rtc;
+	void __iomem *base;
+
+	rda_rtc = devm_kzalloc(&pdev->dev, sizeof(*rda_rtc), GFP_KERNEL);
+	if (!rda_rtc)
+		return -ENOMEM;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return dev_err_probe(&pdev->dev, PTR_ERR(base),
+				"failed to remap resource\n");
+
+	rda_rtc->regmap = devm_regmap_init_mmio(&pdev->dev, base, &regmap_config);
+	if (!rda_rtc->regmap)
+		return dev_err_probe(&pdev->dev, PTR_ERR(rda_rtc->regmap),
+				"can't find regmap\n");
+
+	rda_rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+	if (IS_ERR(rda_rtc->rtc_dev))
+		return dev_err_probe(&pdev->dev, PTR_ERR(rda_rtc->rtc_dev),
+				"failed to allocate rtc device\n");
+
+	rda_rtc->rtc_dev->ops = &rda_rtc_ops;
+	rda_rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
+	rda_rtc->rtc_dev->range_max = RTC_TIMESTAMP_END_2127;
+
+	platform_set_drvdata(pdev, rda_rtc);
+
+	return devm_rtc_register_device(rda_rtc->rtc_dev);
+}
+
+static const struct of_device_id rda_rtc_id_table[] = {
+	{ .compatible = "rda,8810pl-rtc", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rda_rtc_id_table);
+
+static struct platform_driver rda_rtc_driver = {
+	.probe = rda_rtc_probe,
+	.driver = {
+		.name = "rtc-rda",
+		.pm = &rda_rtc_pm_ops,
+		.of_match_table = rda_rtc_id_table,
+	},
+};
+module_platform_driver(rda_rtc_driver);
+
+MODULE_AUTHOR("Dang Huynh <dang.huynh@mainlining.org>");
+MODULE_DESCRIPTION("RDA Micro RTC driver");
+MODULE_LICENSE("GPL");

-- 
2.51.0


^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver
@ 2025-09-16 20:24 Dang Huynh via B4 Relay
  2025-09-16 20:24 ` [PATCH 01/25] ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes Dang Huynh via B4 Relay
                   ` (25 more replies)
  0 siblings, 26 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:24 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

This patch series aims to add support for Clock/Reset, Real-Time Clock and
SDMMC on the RDA Micro RDA8810PL platform.

It also adds Intelligent Flow Controller (IOW, a DMA controller) which is
important for working with this MMC IP.

Tested on the Orange Pi 2G-IOT.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
Dang Huynh (25):
      ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes
      drivers: gpio: rda: Make IRQ optional
      dt-bindings: gpio: rda: Make interrupts optional
      rtc: Add timestamp for the end of 2127
      dt-bindings: rtc: Add RDA Micro RDA8810PL RTC
      rtc: Add driver for RDA Micro SoC
      ARM: dts: unisoc: rda8810pl: Enable Real-Time Clock
      ARM: dts: unisoc: rda8810pl: Enable ARM PMU
      dt-bindings: clock: Add RDA Micro RDA8810PL clock/reset controller
      drivers: clk: Add Clock and Reset Driver for RDA Micro RDA8810PL SoC
      dts: unisoc: rda8810pl: Enable clock/reset driver
      dts: unisoc: rda8810pl: Add OPP for CPU and define L2 cache
      dts: unisoc: orangepi: Disable UART with no users
      dt-bindings: power: reset: Add RDA Micro Modem Reset
      power: reset: Add basic power reset driver for RDA8810PL
      dts: unisoc: rda8810pl: Enable modem reset
      drivers: gpio: rda: Make direction register unreadable
      dt-bindings: dma: Add RDA IFC DMA
      dmaengine: Add RDA IFC driver
      dts: unisoc: rda8810pl: Enable IFC
      dt-bindings: mmc: Add RDA SDMMC controller
      mmc: host: Add RDA Micro SD/MMC driver
      dts: unisoc: rda8810pl: Add SDMMC controllers
      dts: unisoc: orangepi-2g: Enable SD Card
      dts: unisoc: orangepi-i96: Enable SD Card

 .../bindings/clock/rda,8810pl-apsyscon.yaml        |  44 ++
 Documentation/devicetree/bindings/dma/rda,ifc.yaml |  42 +
 .../devicetree/bindings/gpio/gpio-rda.yaml         |   3 -
 Documentation/devicetree/bindings/mmc/rda,mmc.yaml |  91 +++
 .../bindings/power/reset/rda,md-reset.yaml         |  36 +
 .../devicetree/bindings/rtc/rda,8810pl-rtc.yaml    |  30 +
 MAINTAINERS                                        |  30 +
 .../boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts  |  24 +-
 .../arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts |  24 +-
 arch/arm/boot/dts/unisoc/rda8810pl.dtsi            | 115 ++-
 drivers/clk/Kconfig                                |   1 +
 drivers/clk/Makefile                               |   1 +
 drivers/clk/rda/Kconfig                            |  14 +
 drivers/clk/rda/Makefile                           |   2 +
 drivers/clk/rda/clk-rda8810.c                      | 770 +++++++++++++++++++
 drivers/dma/Kconfig                                |  10 +
 drivers/dma/Makefile                               |   1 +
 drivers/dma/rda-ifc.c                              | 450 +++++++++++
 drivers/gpio/gpio-rda.c                            |   4 +-
 drivers/mmc/host/Kconfig                           |  12 +
 drivers/mmc/host/Makefile                          |   1 +
 drivers/mmc/host/rda-mmc.c                         | 853 +++++++++++++++++++++
 drivers/power/reset/Kconfig                        |   9 +
 drivers/power/reset/Makefile                       |   1 +
 drivers/power/reset/rda-reboot.c                   |  58 ++
 drivers/rtc/Kconfig                                |  11 +
 drivers/rtc/Makefile                               |   1 +
 drivers/rtc/rtc-rda.c                              | 356 +++++++++
 include/dt-bindings/clock/rda,8810pl-apclk.h       |  79 ++
 include/dt-bindings/dma/rda-ifc.h                  |  28 +
 include/linux/rtc.h                                |   1 +
 31 files changed, 3079 insertions(+), 23 deletions(-)
---
base-commit: 590b221ed4256fd6c34d3dea77aa5bd6e741bbc1
change-id: 20250916-rda8810pl-drivers-9a5271452635

Best regards,
-- 
Dang Huynh <dang.huynh@mainlining.org>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH 01/25] ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
@ 2025-09-16 20:24 ` Dang Huynh via B4 Relay
  2025-09-17  0:39   ` Krzysztof Kozlowski
  2025-09-16 20:24 ` [PATCH 02/25] drivers: gpio: rda: Make IRQ optional Dang Huynh via B4 Relay
                   ` (24 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:24 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

So we can add devices to these GPIO nodes for each board.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/boot/dts/unisoc/rda8810pl.dtsi | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
index f30d6ece49fb33d9c5c3ad9522c83bb8e4f8b488..6553fc102c6751696e75e4de614fc3428d182061 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
+++ b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
@@ -39,7 +39,7 @@ modem@10000000 {
 		#size-cells = <1>;
 		ranges = <0x0 0x10000000 0xfffffff>;
 
-		gpioc@1a08000 {
+		gpioc: gpioc@1a08000 {
 			compatible = "rda,8810pl-gpio";
 			reg = <0x1a08000 0x1000>;
 			gpio-controller;
@@ -76,7 +76,7 @@ timer@10000 {
 			interrupt-names = "hwtimer", "ostimer";
 		};
 
-		gpioa@30000 {
+		gpioa: gpioa@30000 {
 			compatible = "rda,8810pl-gpio";
 			reg = <0x30000 0x1000>;
 			gpio-controller;
@@ -87,7 +87,7 @@ gpioa@30000 {
 			interrupts = <12 IRQ_TYPE_LEVEL_HIGH>;
 		};
 
-		gpiob@31000 {
+		gpiob: gpiob@31000 {
 			compatible = "rda,8810pl-gpio";
 			reg = <0x31000 0x1000>;
 			gpio-controller;
@@ -98,7 +98,7 @@ gpiob@31000 {
 			interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
 		};
 
-		gpiod@32000 {
+		gpiod: gpiod@32000 {
 			compatible = "rda,8810pl-gpio";
 			reg = <0x32000 0x1000>;
 			gpio-controller;

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 02/25] drivers: gpio: rda: Make IRQ optional
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
  2025-09-16 20:24 ` [PATCH 01/25] ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes Dang Huynh via B4 Relay
@ 2025-09-16 20:24 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 03/25] dt-bindings: gpio: rda: Make interrupts optional Dang Huynh via B4 Relay
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:24 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Not all GPIO have IRQ. When we use this driver with
GPIOC (handled by modem) we get:

rda-gpio 11a08000.gpioc: error -ENXIO: IRQ index 0 not found

Let's mark IRQ as optional so this error doesn't show up.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 drivers/gpio/gpio-rda.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpio/gpio-rda.c b/drivers/gpio/gpio-rda.c
index bcd85a2237a532b875df9470d972ac88b95a91cc..b4db8553a2371ae407fdb7e681d0f82c4d9f74b7 100644
--- a/drivers/gpio/gpio-rda.c
+++ b/drivers/gpio/gpio-rda.c
@@ -229,7 +229,7 @@ static int rda_gpio_probe(struct platform_device *pdev)
 	 * RDA8810PL, GPIOC doesn't support interrupt. So we must handle
 	 * those also.
 	 */
-	rda_gpio->irq = platform_get_irq(pdev, 0);
+	rda_gpio->irq = platform_get_irq_optional(pdev, 0);
 
 	rda_gpio->base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(rda_gpio->base))

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 03/25] dt-bindings: gpio: rda: Make interrupts optional
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
  2025-09-16 20:24 ` [PATCH 01/25] ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes Dang Huynh via B4 Relay
  2025-09-16 20:24 ` [PATCH 02/25] drivers: gpio: rda: Make IRQ optional Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 04/25] rtc: Add timestamp for the end of 2127 Dang Huynh via B4 Relay
                   ` (22 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

The GPIO controller from the modem does not have an interrupt.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 Documentation/devicetree/bindings/gpio/gpio-rda.yaml | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/gpio-rda.yaml b/Documentation/devicetree/bindings/gpio/gpio-rda.yaml
index 6ece555f074f84b396537917d7149d4061724dcc..dbb73b4d33ed39aa65024376b1af0c4e2fb896db 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-rda.yaml
+++ b/Documentation/devicetree/bindings/gpio/gpio-rda.yaml
@@ -41,9 +41,6 @@ required:
   - gpio-controller
   - "#gpio-cells"
   - ngpios
-  - interrupt-controller
-  - "#interrupt-cells"
-  - interrupts
 
 additionalProperties: false
 

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 04/25] rtc: Add timestamp for the end of 2127
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (2 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 03/25] dt-bindings: gpio: rda: Make interrupts optional Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 05/25] dt-bindings: rtc: Add RDA Micro RDA8810PL RTC Dang Huynh via B4 Relay
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Some RTC (like MT2712 and RDA Micro) can handle until the end of
the year 2127.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 include/linux/rtc.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index 95da051fb155dab4c8ec72ccae7b8e12a117a7f1..ec5828ccc7449388da2ab8bc757030e6795ace30 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -175,6 +175,7 @@ struct rtc_device {
 #define RTC_TIMESTAMP_END_2063		2966371199LL /* 2063-12-31 23:59:59 */
 #define RTC_TIMESTAMP_END_2079		3471292799LL /* 2079-12-31 23:59:59 */
 #define RTC_TIMESTAMP_END_2099		4102444799LL /* 2099-12-31 23:59:59 */
+#define RTC_TIMESTAMP_END_2127		4985971199LL /* 2127-12-31 23:59:59 */
 #define RTC_TIMESTAMP_END_2199		7258118399LL /* 2199-12-31 23:59:59 */
 #define RTC_TIMESTAMP_END_9999		253402300799LL /* 9999-12-31 23:59:59 */
 

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 05/25] dt-bindings: rtc: Add RDA Micro RDA8810PL RTC
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (3 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 04/25] rtc: Add timestamp for the end of 2127 Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 06/25] rtc: Add driver for RDA Micro SoC Dang Huynh via B4 Relay
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Add documentation describing the RTC found in RDA8810PL SoC.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 .../devicetree/bindings/rtc/rda,8810pl-rtc.yaml    | 30 ++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/Documentation/devicetree/bindings/rtc/rda,8810pl-rtc.yaml b/Documentation/devicetree/bindings/rtc/rda,8810pl-rtc.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3ceae294921cc3211cd775d9b3890393196faf82
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/rda,8810pl-rtc.yaml
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rtc/rda,8810pl-rtc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RDA Micro RDA8810PL Real Time Clock
+
+maintainers:
+  - Dang Huynh <dang.huynh@mainlining.org>
+
+properties:
+  compatible:
+    const: rda,8810pl-rtc
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    rtc@1a06000 {
+      compatible = "rda,8810pl-rtc";
+      reg = <0x1a06000 0x1000>;
+    };

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 06/25] rtc: Add driver for RDA Micro SoC
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (4 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 05/25] dt-bindings: rtc: Add RDA Micro RDA8810PL RTC Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-19 13:59   ` kernel test robot
  2025-11-06 22:42   ` Alexandre Belloni
  2025-09-16 20:25 ` [PATCH 07/25] ARM: dts: unisoc: rda8810pl: Enable Real-Time Clock Dang Huynh via B4 Relay
                   ` (19 subsequent siblings)
  25 siblings, 2 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

The RDA Micro SoC has built-in RTC, it supports read/write date
as well as alarm.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 MAINTAINERS           |   6 +
 drivers/rtc/Kconfig   |  11 ++
 drivers/rtc/Makefile  |   1 +
 drivers/rtc/rtc-rda.c | 356 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 374 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fa7f80bd7b2f8bd2099acb9f38070498e7b1cc7e..0549b1d0657f2caaf86a723db139cf9d84d59c4a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21393,6 +21393,12 @@ S:	Supported
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
 F:	tools/testing/selftests/rcutorture
 
+RDA MICRO REAL TIME CLOCK DRIVER
+M:	Dang Huynh <dang.huynh@mainlining.org>
+S:	Maintained
+F:	Documentation/devicetree/bindings/rtc/rda,8810pl-rtc.yaml
+F:	drivers/rtc/rtc-rda.c
+
 RDACM20 Camera Sensor
 M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
 M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 64f6e9756aff4a1f6f6c50f9b4fc2140f66a8578..287fc3bbd474ab78a9bd3b8813e8b9d475c07198 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1471,6 +1471,17 @@ config RTC_DRV_OMAP
 	  This driver can also be built as a module, if so, module
 	  will be called rtc-omap.
 
+config RTC_DRV_RDA
+	tristate "RDA Micro RTC"
+	depends on ARCH_RDA || COMPILE_TEST
+	select REGMAP_MMIO
+	help
+	  If you say yes here you get support for the built-in RTC on
+	  RDA Micro SoC.
+
+	  This driver can also be built as a module, if so, the module
+	  will be called rtc-rda.
+
 config RTC_DRV_S3C
 	tristate "Samsung S3C series SoC RTC"
 	depends on ARCH_EXYNOS || ARCH_S3C64XX || ARCH_S5PV210 || \
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 789bddfea99d8fcd024566891c37ee73e527cf93..02f73062bb158fe4738a3043c58ee40f8a58b3c6 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -141,6 +141,7 @@ obj-$(CONFIG_RTC_DRV_PS3)	+= rtc-ps3.o
 obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o
 obj-$(CONFIG_RTC_DRV_R7301)	+= rtc-r7301.o
 obj-$(CONFIG_RTC_DRV_R9701)	+= rtc-r9701.o
+obj-$(CONFIG_RTC_DRV_RDA)	+= rtc-rda.o
 obj-$(CONFIG_RTC_DRV_RC5T583)	+= rtc-rc5t583.o
 obj-$(CONFIG_RTC_DRV_RC5T619)	+= rtc-rc5t619.o
 obj-$(CONFIG_RTC_DRV_RK808)	+= rtc-rk808.o
diff --git a/drivers/rtc/rtc-rda.c b/drivers/rtc/rtc-rda.c
new file mode 100644
index 0000000000000000000000000000000000000000..bb5aa25fb7d0ad538a0f7f67a80d08fe67af1c5d
--- /dev/null
+++ b/drivers/rtc/rtc-rda.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RTC driver for RDA Micro
+ *
+ * Copyright (C) 2013-2014 RDA Microelectronics Inc.
+ * Copyright (C) 2024 Dang Huynh <dang.huynh@mainlining.org>
+ */
+
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+struct rda_rtc {
+	struct rtc_device *rtc_dev;
+	struct regmap *regmap;
+};
+
+/* RTC Registers */
+#define RDA_RTC_CTRL_REG 0x0
+#define RDA_RTC_CMD_REG 0x4
+#define RDA_RTC_STA_REG 0x8
+#define RDA_RTC_CAL_LOAD_LOW_REG 0xC
+#define RDA_RTC_CAL_LOAD_HIGH_REG 0x10
+#define RDA_RTC_CUR_LOAD_LOW_REG 0x14
+#define RDA_RTC_CUR_LOAD_HIGH_REG 0x18
+#define RDA_RTC_ALARM_LOW_REG 0x1C
+#define RDA_RTC_ALARM_HIGH_REG 0x20
+
+/* RTC Bits */
+#define RDA_RTC_CMD_CAL_LOAD BIT(0)
+#define RDA_RTC_CMD_ALARM_LOAD BIT(4)
+#define RDA_RTC_CMD_ALARM_ENABLE BIT(5)
+#define RDA_RTC_CMD_ALARM_DISABLE BIT(6)
+#define RDA_RTC_CMD_INVALID BIT(31)
+#define RDA_RTC_STA_ALARM_ENABLE BIT(20)
+#define RDA_RTC_STA_NOT_PROG BIT(31)
+
+/* RTC Masks */
+#define RDA_SEC_MASK GENMASK(7, 0)
+#define RDA_MIN_MASK GENMASK(15, 8)
+#define RDA_HRS_MASK GENMASK(23, 16)
+
+#define RDA_MDAY_MASK GENMASK(7, 0)
+#define RDA_MON_MASK GENMASK(11, 8)
+#define RDA_YEAR_MASK GENMASK(22, 16)
+#define RDA_WDAY_MASK GENMASK(26, 24)
+
+static int rda_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+	u32 high, low;
+	int ret;
+
+	ret = rtc_valid_tm(tm);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * The number of years since 1900 in kernel,
+	 * but it is defined since 2000 by HW.
+	 * The number of mons' range is from 0 to 11 in kernel,
+	 * but it is defined from 1 to 12 by HW.
+	 */
+	low = FIELD_PREP(RDA_SEC_MASK, tm->tm_sec) |
+		FIELD_PREP(RDA_MIN_MASK, tm->tm_min) |
+		FIELD_PREP(RDA_HRS_MASK, tm->tm_hour);
+
+	high = FIELD_PREP(RDA_MDAY_MASK, tm->tm_mday) |
+		FIELD_PREP(RDA_MON_MASK, tm->tm_mon + 1) |
+		FIELD_PREP(RDA_YEAR_MASK, tm->tm_year - 100) |
+		FIELD_PREP(RDA_WDAY_MASK, tm->tm_wday);
+
+	ret = regmap_write(rtc->regmap, RDA_RTC_CAL_LOAD_LOW_REG, low);
+	if (ret < 0) {
+		dev_err(dev, "Failed to update RTC low register: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_write(rtc->regmap, RDA_RTC_CAL_LOAD_HIGH_REG, high);
+	if (ret < 0) {
+		dev_err(dev, "Failed to update RTC low register: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG, RDA_RTC_CMD_CAL_LOAD, 1);
+	if (ret < 0) {
+		dev_err(dev, "Failed to update RTC cal load register: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rda_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+	unsigned int high, low;
+	int ret;
+
+	/*
+	 * Check if RTC data is valid.
+	 *
+	 * When this bit is set, it means the data in the RTC is invalid
+	 * or not configured.
+	 */
+	ret = regmap_test_bits(rtc->regmap, RDA_RTC_STA_REG, RDA_RTC_STA_NOT_PROG);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read RTC status: %d\n", ret);
+		return ret;
+	} else if (ret > 0)
+		return -EINVAL;
+
+	ret = regmap_read(rtc->regmap, RDA_RTC_CUR_LOAD_HIGH_REG, &high);
+	if (ret) {
+		dev_err(dev, "Failed to read RTC high reg: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_read(rtc->regmap, RDA_RTC_CUR_LOAD_LOW_REG, &low);
+	if (ret) {
+		dev_err(dev, "Failed to read RTC low reg: %d\n", ret);
+		return ret;
+	}
+
+	tm->tm_sec = FIELD_GET(RDA_SEC_MASK, low);
+	tm->tm_min = FIELD_GET(RDA_MIN_MASK, low);
+	tm->tm_hour = FIELD_GET(RDA_HRS_MASK, low);
+	tm->tm_mday = FIELD_GET(RDA_MDAY_MASK, high);
+	tm->tm_mon = FIELD_GET(RDA_MON_MASK, high);
+	tm->tm_year = FIELD_GET(RDA_YEAR_MASK, high);
+	tm->tm_wday = FIELD_GET(RDA_WDAY_MASK, high);
+
+	/*
+	 * The number of years since 1900 in kernel,
+	 * but it is defined since 2000 by HW.
+	 */
+	tm->tm_year += 100;
+	/*
+	 * The number of mons' range is from 0 to 11 in kernel,
+	 * but it is defined from 1 to 12 by HW.
+	 */
+	tm->tm_mon -= 1;
+
+	return 0;
+}
+
+static int rda_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+	struct rtc_time *tm = &alrm->time;
+	unsigned int high, low;
+	int ret;
+
+	ret = regmap_read(rtc->regmap, RDA_RTC_ALARM_HIGH_REG, &high);
+	if (ret) {
+		dev_err(dev, "Failed to read alarm low reg: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_read(rtc->regmap, RDA_RTC_ALARM_LOW_REG, &low);
+	if (ret) {
+		dev_err(dev, "Failed to read alarm low reg: %d\n", ret);
+		return ret;
+	}
+
+	tm->tm_sec = FIELD_GET(RDA_SEC_MASK, low);
+	tm->tm_min = FIELD_GET(RDA_MIN_MASK, low);
+	tm->tm_hour = FIELD_GET(RDA_HRS_MASK, low);
+	tm->tm_mday = FIELD_GET(RDA_MDAY_MASK, high);
+	tm->tm_mon = FIELD_GET(RDA_MON_MASK, high);
+	tm->tm_year = FIELD_GET(RDA_YEAR_MASK, high);
+	tm->tm_wday = FIELD_GET(RDA_WDAY_MASK, high);
+
+	/*
+	 * The number of years since 1900 in kernel,
+	 * but it is defined since 2000 by HW.
+	 */
+	tm->tm_year += 100;
+	/*
+	 * The number of mons' range is from 0 to 11 in kernel,
+	 * but it is defined from 1 to 12 by HW.
+	 */
+	tm->tm_mon -= 1;
+
+	return 0;
+}
+
+static int rda_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+
+	if (enabled)
+		return regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG,
+				RDA_RTC_CMD_ALARM_ENABLE, 1);
+
+	return regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG,
+			RDA_RTC_CMD_ALARM_DISABLE, 1);
+}
+
+static int rda_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+	struct rtc_time *tm = &alrm->time;
+	u32 high, low;
+	int ret;
+
+	ret = rtc_valid_tm(tm);
+	if (ret < 0)
+		return ret;
+
+	/* TODO: Check if it's necessary to disable IRQ first */
+	rda_rtc_alarm_irq_enable(dev, 0);
+
+	/*
+	 * The number of years since 1900 in kernel,
+	 * but it is defined since 2000 by HW.
+	 * The number of mons' range is from 0 to 11 in kernel,
+	 * but it is defined from 1 to 12 by HW.
+	 */
+	low = FIELD_PREP(RDA_SEC_MASK, tm->tm_sec) |
+		FIELD_PREP(RDA_MIN_MASK, tm->tm_min) |
+		FIELD_PREP(RDA_HRS_MASK, tm->tm_hour);
+
+	high = FIELD_PREP(RDA_MDAY_MASK, tm->tm_mday) |
+		FIELD_PREP(RDA_MON_MASK, tm->tm_mon + 1) |
+		FIELD_PREP(RDA_YEAR_MASK, tm->tm_year - 100) |
+		FIELD_PREP(RDA_WDAY_MASK, tm->tm_wday);
+
+
+	ret = regmap_write(rtc->regmap, RDA_RTC_ALARM_LOW_REG, low);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set low alarm register: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_write(rtc->regmap, RDA_RTC_ALARM_HIGH_REG, high);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set low alarm register: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG, RDA_RTC_CMD_ALARM_LOAD, 1);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set alarm register: %d\n", ret);
+		return ret;
+	}
+
+	dev_dbg(dev, "Alarm set: %4d-%02d-%02d %02d:%02d:%02d\n",
+			2000 + (tm->tm_year - 100), tm->tm_mon + 1, tm->tm_mday,
+			tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+	return 0;
+}
+
+static int rda_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+	struct rda_rtc *rtc = dev_get_drvdata(dev);
+	int ret;
+
+	ret = regmap_test_bits(rtc->regmap, RDA_RTC_STA_REG, RDA_RTC_STA_ALARM_ENABLE);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read alarm status: %d\n", ret);
+		return ret;
+	}
+
+	seq_printf(seq, "alarm enable\t: %s\n", (ret > 0) ? "yes" : "no");
+
+	return 0;
+}
+
+static const struct rtc_class_ops rda_rtc_ops = {
+	.read_time = rda_rtc_readtime,
+	.set_time = rda_rtc_settime,
+	.read_alarm = rda_rtc_readalarm,
+	.set_alarm = rda_rtc_setalarm,
+	.proc = rda_rtc_proc,
+	.alarm_irq_enable = rda_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rda_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	/* TODO: Check if it's okay to turn on alarm IRQ when it's not set */
+	return rda_rtc_alarm_irq_enable(&pdev->dev, 1);
+}
+
+static int rda_rtc_resume(struct platform_device *pdev)
+{
+	/* If alarms were left, we turn them off. */
+	return rda_rtc_alarm_irq_enable(&pdev->dev, 0);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rda_rtc_pm_ops, rda_rtc_suspend, rda_rtc_resume);
+
+static const struct regmap_config regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static int rda_rtc_probe(struct platform_device *pdev)
+{
+	struct rda_rtc *rda_rtc;
+	void __iomem *base;
+
+	rda_rtc = devm_kzalloc(&pdev->dev, sizeof(*rda_rtc), GFP_KERNEL);
+	if (!rda_rtc)
+		return -ENOMEM;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return dev_err_probe(&pdev->dev, PTR_ERR(base),
+				"failed to remap resource\n");
+
+	rda_rtc->regmap = devm_regmap_init_mmio(&pdev->dev, base, &regmap_config);
+	if (!rda_rtc->regmap)
+		return dev_err_probe(&pdev->dev, PTR_ERR(rda_rtc->regmap),
+				"can't find regmap\n");
+
+	rda_rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+	if (IS_ERR(rda_rtc->rtc_dev))
+		return dev_err_probe(&pdev->dev, PTR_ERR(rda_rtc->rtc_dev),
+				"failed to allocate rtc device\n");
+
+	rda_rtc->rtc_dev->ops = &rda_rtc_ops;
+	rda_rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
+	rda_rtc->rtc_dev->range_max = RTC_TIMESTAMP_END_2127;
+
+	platform_set_drvdata(pdev, rda_rtc);
+
+	return devm_rtc_register_device(rda_rtc->rtc_dev);
+}
+
+static const struct of_device_id rda_rtc_id_table[] = {
+	{ .compatible = "rda,8810pl-rtc", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rda_rtc_id_table);
+
+static struct platform_driver rda_rtc_driver = {
+	.probe = rda_rtc_probe,
+	.driver = {
+		.name = "rtc-rda",
+		.pm = &rda_rtc_pm_ops,
+		.of_match_table = rda_rtc_id_table,
+	},
+};
+module_platform_driver(rda_rtc_driver);
+
+MODULE_AUTHOR("Dang Huynh <dang.huynh@mainlining.org>");
+MODULE_DESCRIPTION("RDA Micro RTC driver");
+MODULE_LICENSE("GPL");

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 07/25] ARM: dts: unisoc: rda8810pl: Enable Real-Time Clock
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (5 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 06/25] rtc: Add driver for RDA Micro SoC Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-17  0:40   ` Krzysztof Kozlowski
  2025-09-16 20:25 ` [PATCH 08/25] ARM: dts: unisoc: rda8810pl: Enable ARM PMU Dang Huynh via B4 Relay
                   ` (18 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

The RDA8810PL has built-in RTC.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/boot/dts/unisoc/rda8810pl.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
index 6553fc102c6751696e75e4de614fc3428d182061..609359aa91537168435934077e736b216adf50f4 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
+++ b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
@@ -39,6 +39,11 @@ modem@10000000 {
 		#size-cells = <1>;
 		ranges = <0x0 0x10000000 0xfffffff>;
 
+		rtc@1a06000 {
+			compatible = "rda,8810pl-rtc";
+			reg = <0x1a06000 0x1000>;
+		};
+
 		gpioc: gpioc@1a08000 {
 			compatible = "rda,8810pl-gpio";
 			reg = <0x1a08000 0x1000>;

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 08/25] ARM: dts: unisoc: rda8810pl: Enable ARM PMU
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (6 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 07/25] ARM: dts: unisoc: rda8810pl: Enable Real-Time Clock Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 09/25] dt-bindings: clock: Add RDA Micro RDA8810PL clock/reset controller Dang Huynh via B4 Relay
                   ` (17 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

The SoC uses a standard ARM PMU, enable it.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/boot/dts/unisoc/rda8810pl.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
index 609359aa91537168435934077e736b216adf50f4..45a2fd3e04cea5aac4fb6b40a6b332ce3eee4f2c 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
+++ b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
@@ -6,6 +6,7 @@
  * Copyright (c) 2018 Manivannan Sadhasivam
  */
 
+#include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/interrupt-controller/irq.h>
 
 / {
@@ -25,6 +26,11 @@ cpu@0 {
 		};
 	};
 
+	pmu {
+		compatible = "arm,cortex-a5-pmu";
+		interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
 	sram@100000 {
 		compatible = "mmio-sram";
 		reg = <0x100000 0x10000>;

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 09/25] dt-bindings: clock: Add RDA Micro RDA8810PL clock/reset controller
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (7 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 08/25] ARM: dts: unisoc: rda8810pl: Enable ARM PMU Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-17  0:43   ` Krzysztof Kozlowski
  2025-09-16 20:25 ` [PATCH 10/25] drivers: clk: Add Clock and Reset Driver for RDA Micro RDA8810PL SoC Dang Huynh via B4 Relay
                   ` (16 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Add documentation describing the RDA8810PL Clock and Reset
controller.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 .../bindings/clock/rda,8810pl-apsyscon.yaml        | 44 ++++++++++++
 include/dt-bindings/clock/rda,8810pl-apclk.h       | 79 ++++++++++++++++++++++
 2 files changed, 123 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/rda,8810pl-apsyscon.yaml b/Documentation/devicetree/bindings/clock/rda,8810pl-apsyscon.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..988b609403a96abc4964ab366daa6fec0514595c
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/rda,8810pl-apsyscon.yaml
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/rda,8810pl-apsyscon.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RDA Micro RDA8810PL AP Clock Controller
+
+maintainers:
+  - Dang Huynh <dang.huynh@mainlining.org>
+
+properties:
+  compatible:
+    items:
+      - const: rda,8810pl-apsyscon
+      - const: syscon
+
+  reg:
+    maxItems: 1
+
+  '#clock-cells':
+    const: 1
+
+  '#reset-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - "#clock-cells"
+  - "#reset-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/rda,8810pl-apclk.h>
+
+    ap_syscon: syscon@0 {
+      compatible = "rda,8810pl-apsyscon", "syscon";
+      reg = <0x0 0x1000>;
+      #clock-cells = <1>;
+      #reset-cells = <1>;
+    };
diff --git a/include/dt-bindings/clock/rda,8810pl-apclk.h b/include/dt-bindings/clock/rda,8810pl-apclk.h
new file mode 100644
index 0000000000000000000000000000000000000000..372358e72436a28c0775519f49626c9c5f4c6046
--- /dev/null
+++ b/include/dt-bindings/clock/rda,8810pl-apclk.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+
+#ifndef _DT_BINDINGS_CLK_RDA8810_H_
+#define _DT_BINDINGS_CLK_RDA8810_H_
+
+/* soc clocks */
+#define CLK_CPU 0
+#define CLK_BUS 1
+#define CLK_MEM 2
+
+#define CLK_USB 3
+#define CLK_AXI 4
+#define CLK_GCG 5
+#define CLK_AHB1 6
+#define CLK_APB1 7
+#define CLK_APB2 8
+
+#define CLK_GPU 9
+#define CLK_VPU 10
+#define CLK_VOC 11
+#define CLK_SFLSH 12
+
+#define CLK_UART1 13
+#define CLK_UART2 14
+#define CLK_UART3 15
+
+#define CLK_VOC2 16
+#define CLK_EMMC 17
+
+#define CLK_COUNT (CLK_EMMC + 1)
+
+/* resets */
+#define RST_CPU 0
+
+#define RST_AXI_VOC 1
+#define RST_AXI_DMA 2
+#define RST_AXI_CONNECT 3
+#define RST_AXI_VPU 4
+
+#define RST_GCG_GOUDA 5
+#define RST_GCG_CAMERA 6
+#define RST_GCG_LCDC 7
+
+#define RST_AHB1_USBC 8
+#define RST_AHB1_SPIFLASH 9
+
+#define RST_APB1_TIMER 10
+#define RST_APB1_KEYPAD 11
+#define RST_APB1_GPIO 12
+#define RST_APB1_PWM 13
+#define RST_APB1_AIF 14
+#define RST_APB1_AUIFC 15
+#define RST_APB1_I2C1 16
+#define RST_APB1_I2C2 17
+#define RST_APB1_I2C3 18
+#define RST_APB1_COMREGS 19
+#define RST_APB1_DMC 20
+#define RST_APB1_DDRPHY_P 21
+
+#define RST_APB2_IFC 22
+#define RST_APB2_UART1 23
+#define RST_APB2_UART2 24
+#define RST_APB2_UART3 25
+#define RST_APB2_SPI1 26
+#define RST_APB2_SPI2 27
+#define RST_APB2_SPI3 28
+#define RST_APB2_SDMMC1 29
+#define RST_APB2_SDMMC2 30
+#define RST_APB2_SDMMC3 31
+#define RST_APB2_NAND 32
+
+#define RST_MEM_GPU 33
+#define RST_MEM_VPU 34
+#define RST_MEM_DMC 35
+#define RST_MEM_DDRPHY_P 36
+
+#define RST_COUNT (RST_MEM_DDRPHY_P + 1)
+
+#endif /* _DT_BINDINGS_CLK_RDA8810_H_ */

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 10/25] drivers: clk: Add Clock and Reset Driver for RDA Micro RDA8810PL SoC
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (8 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 09/25] dt-bindings: clock: Add RDA Micro RDA8810PL clock/reset controller Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-20  4:50   ` Stephen Boyd
  2025-09-16 20:25 ` [PATCH 11/25] dts: unisoc: rda8810pl: Enable clock/reset driver Dang Huynh via B4 Relay
                   ` (15 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Add a clock/reset driver for RDA8810PL SoC, which provides clocks for
various subsystems.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 MAINTAINERS                   |   6 +
 drivers/clk/Kconfig           |   1 +
 drivers/clk/Makefile          |   1 +
 drivers/clk/rda/Kconfig       |  14 +
 drivers/clk/rda/Makefile      |   2 +
 drivers/clk/rda/clk-rda8810.c | 770 ++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 794 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 0549b1d0657f2caaf86a723db139cf9d84d59c4a..cbe2ab8af6dcd40dd1456d9df55673dace3c87b2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21393,6 +21393,12 @@ S:	Supported
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
 F:	tools/testing/selftests/rcutorture
 
+RDA MICRO CLOCK AND RESET DRIVER
+M:	Dang Huynh <dang.huynh@mainlining.org>
+S:	Maintained
+F:	Documentation/devicetree/bindings/clock/rda,8810pl-apsyscon.yaml
+F:	drivers/clk/rda/clk-rda8810.c
+
 RDA MICRO REAL TIME CLOCK DRIVER
 M:	Dang Huynh <dang.huynh@mainlining.org>
 S:	Maintained
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index b1425aed659387a676bd933fe50ac4894c7156fe..15f5bc9108b565acb1c3c6e978ad0e5a71f5550d 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -520,6 +520,7 @@ source "drivers/clk/nuvoton/Kconfig"
 source "drivers/clk/pistachio/Kconfig"
 source "drivers/clk/qcom/Kconfig"
 source "drivers/clk/ralink/Kconfig"
+source "drivers/clk/rda/Kconfig"
 source "drivers/clk/renesas/Kconfig"
 source "drivers/clk/rockchip/Kconfig"
 source "drivers/clk/samsung/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 18ed29cfdc1133b6c254190c6092eb263366d5ac..8241bb7f88daaebde766ba92d718b2ca710d6b5f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -141,6 +141,7 @@ obj-$(CONFIG_COMMON_CLK_PISTACHIO)	+= pistachio/
 obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
 obj-$(CONFIG_COMMON_CLK_QCOM)		+= qcom/
 obj-y					+= ralink/
+obj-y					+= rda/
 obj-y					+= renesas/
 obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
 obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
diff --git a/drivers/clk/rda/Kconfig b/drivers/clk/rda/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..b505e3e552cef1e7ea3da4aa46d61d0d0a3d5db0
--- /dev/null
+++ b/drivers/clk/rda/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config CLK_RDA8810
+	bool "RDA Micro RDA8810PL Clock and Reset Controller"
+	depends on ARCH_RDA || COMPILE_TEST
+	select MFD_SYSCON
+	select REGMAP_MMIO
+	select RESET_CONTROLLER
+	help
+	  This driver supports clock and reset for RDA Micro RDA8810 platform.
+	  If you have a board with the RDA8810PL SoC, say Y to use most of the
+	  board peripherals.
+
+	  If unsure, say N.
+
diff --git a/drivers/clk/rda/Makefile b/drivers/clk/rda/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..98848dccabe5d2199d5e9469d6bde154b2b3d86a
--- /dev/null
+++ b/drivers/clk/rda/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CLK_RDA8810) += clk-rda8810.o
diff --git a/drivers/clk/rda/clk-rda8810.c b/drivers/clk/rda/clk-rda8810.c
new file mode 100644
index 0000000000000000000000000000000000000000..8bea60d5376aeb4c67cd15bc1a64dbcf7a0a1f7c
--- /dev/null
+++ b/drivers/clk/rda/clk-rda8810.c
@@ -0,0 +1,770 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RDA8810PL Clock and Reset driver
+ *
+ * Copyright (C) 2013 RDA Microelectronics Inc.
+ * Copyright (c) 2025 Dang Huynh <dang.huynh@mainlining.org>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/reset-controller.h>
+#include <dt-bindings/clock/rda,8810pl-apclk.h>
+
+#define MHZ 1000000U
+
+/*
+ * Some registers are protected, we need to write AP_CTRL_PROTECT_UNLOCK to
+ * AP_REG_DBG then we can make changes to them.
+ */
+#define AP_CTRL_PROTECT_LOCK	0xA50000
+#define AP_CTRL_PROTECT_UNLOCK	0xA50001
+
+/* Register Base */
+#define AP_REG_DBG 0x0
+#define AP_REG_CPU_ENABLE 0x60
+#define AP_REG_AXI_ENABLE 0x6C
+#define AP_REG_AXIDIV2_ENABLE 0x78
+#define AP_REG_GCG_ENABLE 0x84
+#define AP_REG_AHB1_ENABLE 0x90
+#define AP_REG_APB1_ENABLE 0x9C
+#define AP_REG_APB2_ENABLE 0xA8
+#define AP_REG_MEM_ENABLE 0xB4
+#define AP_REG_APO_ENABLE 0xC0
+
+/* AP Clk Config Bits */
+#define AP_PERI_SRC_DIV BIT(12)
+
+/* UART Clock Bits */
+#define AP_UART_DIVIDER GENMASK(9, 0)
+#define AP_UART_SET_PLL BIT(12)
+
+/* AP Clk Enable */
+#define AP_ENABLE_CPU_CORE BIT(0)
+#define AP_ENABLE_CPU_DUMMY BIT(1)
+#define AP_ENABLE_AHB0_CONF BIT(0)
+#define AP_ENABLE_APB0_CONF BIT(1)
+#define AP_ENABLE_AXI_VOC  BIT(2)
+#define AP_ENABLE_AXI_DMA  BIT(3)
+#define AP_ENABLE_AXI_ALWAYS BIT(4)
+#define AP_ENABLE_AXI_CONNECT BIT(5)
+#define AP_ENABLE_APB0_IRQ BIT(6)
+#define AP_ENABLE_AXIDIV2_IMEM BIT(0)
+#define AP_ENABLE_AXIDIV2_ALWAYS BIT(1)
+#define AP_ENABLE_AXIDIV2_CONNECT BIT(2)
+#define AP_ENABLE_AXIDIV2_VPU BIT(3)
+#define AP_ENABLE_GCG_APB_CONF BIT(0)
+#define AP_ENABLE_GCG_GOUDA BIT(1)
+#define AP_ENABLE_GCG_CAMERA BIT(2)
+#define AP_ENABLE_GCG_ALWAYS BIT(3)
+#define AP_ENABLE_GCG_CONNECT BIT(4)
+#define AP_ENABLE_GCG_DPI BIT(7)
+#define AP_ENABLE_AHB1_USBC BIT(0)
+#define AP_ENABLE_AHB1_ALWAYS BIT(1)
+#define AP_ENABLE_AHB1_SPIFLASH BIT(2)
+#define AP_ENABLE_APB1_CONF BIT(0)
+#define AP_ENABLE_APB1_AIF BIT(1)
+#define AP_ENABLE_APB1_AUIFC BIT(2)
+#define AP_ENABLE_APB1_AUIFC_CH0 BIT(3)
+#define AP_ENABLE_APB1_AUIFC_CH1 BIT(4)
+#define AP_ENABLE_APB1_I2C1 BIT(5)
+#define AP_ENABLE_APB1_I2C2 BIT(6)
+#define AP_ENABLE_APB1_I2C3 BIT(7)
+#define AP_ENABLE_APB1D_OSC BIT(8)
+#define AP_ENABLE_APB1D_PWM BIT(9)
+#define AP_ENABLE_APB1_ALWAYS BIT(10)
+#define AP_ENABLE_APB1_DAPLITE BIT(11)
+#define AP_ENABLE_APB1_TIMER BIT(12)
+#define AP_ENABLE_APB1_GPIO BIT(13)
+#define AP_ENABLE_APB2_CONF BIT(0)
+#define AP_ENABLE_APB2_IFC BIT(1)
+#define AP_ENABLE_APB2_IFC_CH0 BIT(2)
+#define AP_ENABLE_APB2_IFC_CH1 BIT(3)
+#define AP_ENABLE_APB2_IFC_CH2 BIT(4)
+#define AP_ENABLE_APB2_IFC_CH3 BIT(5)
+#define AP_ENABLE_APB2_IFC_CH4 BIT(6)
+#define AP_ENABLE_APB2_IFC_CH5 BIT(7)
+#define AP_ENABLE_APB2_IFC_CH6 BIT(8)
+#define AP_ENABLE_APB2_IFC_CH7 BIT(9)
+#define AP_ENABLE_APB2_UART1 BIT(10)
+#define AP_ENABLE_APB2_UART2 BIT(11)
+#define AP_ENABLE_APB2_UART3 BIT(12)
+#define AP_ENABLE_APB2_SPI1 BIT(13)
+#define AP_ENABLE_APB2_SPI2 BIT(14)
+#define AP_ENABLE_APB2_SPI3 BIT(15)
+#define AP_ENABLE_APB2_SDMMC1 BIT(16)
+#define AP_ENABLE_APB2_SDMMC2 BIT(17)
+#define AP_ENABLE_APB2_SDMMC3 BIT(18)
+#define AP_ENABLE_APB2_ALWAYS BIT(19)
+#define AP_ENABLE_APB2_NANDFLASH BIT(20)
+#define AP_ENABLE_MEM_CONF BIT(0)
+#define AP_ENABLE_MEM_DMC  BIT(1)
+#define AP_ENABLE_MEM_GPU  BIT(2)
+#define AP_ENABLE_MEM_VPU  BIT(3)
+#define AP_ENABLE_MEM_DDRPHY_P BIT(4)
+#define AP_ENABLE_MEM_CONNECT BIT(5)
+#define AP_ENABLE_APOC_VPU BIT(0)
+#define AP_ENABLE_APOC_BCK BIT(1)
+#define AP_ENABLE_APOC_UART1 BIT(2)
+#define AP_ENABLE_APOC_UART2 BIT(3)
+#define AP_ENABLE_APOC_UART3 BIT(4)
+#define AP_ENABLE_APOC_VOC_CORE BIT(5)
+#define AP_ENABLE_APOC_VOC BIT(6)
+#define AP_ENABLE_APOC_VOC_ALWAYS BIT(7)
+#define AP_ENABLE_APOC_DDRPHY_N BIT(8)
+#define AP_ENABLE_APOC_DDRPHY2XP BIT(9)
+#define AP_ENABLE_APOC_DDRPHY2XN BIT(10)
+#define AP_ENABLE_APOC_GPU BIT(11)
+#define AP_ENABLE_APOC_USBPHY BIT(12)
+#define AP_ENABLE_APOC_CSI BIT(13)
+#define AP_ENABLE_APOC_DSI BIT(14)
+#define AP_ENABLE_APOC_GPIO BIT(15)
+#define AP_ENABLE_APOC_SPIFLASH BIT(16)
+#define AP_ENABLE_APOC_PIX BIT(17)
+#define AP_ENABLE_APOC_PDGB BIT(18)
+
+/* AP Reset */
+#define AP_RST_CPU_REG 0x1C
+#define AP_RST_AXI_REG 0x24
+#define AP_RST_AXIDIV2_REG 0x2C
+#define AP_RST_GCG_REG 0x34
+#define AP_RST_AHB1_REG 0x3C
+#define AP_RST_APB1_REG 0x44
+#define AP_RST_APB2_REG 0x4C
+#define AP_RST_MEM_REG 0x54
+
+/* Bits */
+#define AP_RST_CPU_CORE BIT(0)
+#define AP_RST_CPU_SYS BIT(1)
+#define AP_RST_AXI_VOC BIT(0)
+#define AP_RST_AXI_DMA BIT(1)
+#define AP_RST_AXI_SYS BIT(2)
+#define AP_RST_AXI_CONNECT BIT(3)
+#define AP_RST_AXI_VPU BIT(5)
+#define AP_RST_AXIDIV2_IMEM BIT(0)
+#define AP_RST_AXIDIV2_SYS BIT(1)
+#define AP_RST_AXIDIV2_VPU BIT(2)
+#define AP_RST_GCG_SYS BIT(0)
+#define AP_RST_GCG_GOUDA BIT(1)
+#define AP_RST_GCG_CAMERA BIT(2)
+#define AP_RST_GCG_LCDC BIT(4)
+#define AP_RST_AHB1_SYS BIT(0)
+#define AP_RST_AHB1_USBC BIT(1)
+#define AP_RST_AHB1_SPIFLASH BIT(2)
+#define AP_RST_APB1_SYS BIT(0)
+#define AP_RST_APB1_TIMER BIT(1)
+#define AP_RST_APB1_KEYPAD BIT(2)
+#define AP_RST_APB1_GPIO BIT(3)
+#define AP_RST_APB1_PWM BIT(4)
+#define AP_RST_APB1_AIF BIT(5)
+#define AP_RST_APB1_AUIFC BIT(6)
+#define AP_RST_APB1_I2C1 BIT(7)
+#define AP_RST_APB1_I2C2 BIT(8)
+#define AP_RST_APB1_I2C3 BIT(9)
+#define AP_RST_APB1_COMREGS BIT(10)
+#define AP_RST_APB1_DMC BIT(11)
+#define AP_RST_APB1_DDRPHY_P BIT(12)
+#define AP_RST_APB2_SYS BIT(0)
+#define AP_RST_APB2_IFC BIT(1)
+#define AP_RST_APB2_UART1 BIT(2)
+#define AP_RST_APB2_UART2 BIT(3)
+#define AP_RST_APB2_UART3 BIT(4)
+#define AP_RST_APB2_SPI1 BIT(5)
+#define AP_RST_APB2_SPI2 BIT(6)
+#define AP_RST_APB2_SPI3 BIT(7)
+#define AP_RST_APB2_SDMMC1 BIT(8)
+#define AP_RST_APB2_SDMMC2 BIT(9)
+#define AP_RST_APB2_SDMMC3 BIT(10)
+#define AP_RST_APB2_NANDFLASH BIT(11)
+#define AP_RST_MEM_SYS BIT(0)
+#define AP_RST_MEM_GPU BIT(1)
+#define AP_RST_MEM_VPU BIT(2)
+#define AP_RST_MEM_DMC BIT(3)
+#define AP_RST_MEM_DDRPHY_P BIT(4)
+
+/* Default PLL frequency */
+#define AP_PLL_CPU_FREQ (988 * MHZ)
+#define AP_PLL_BUS_FREQ (800 * MHZ)
+#define AP_PLL_MEM_FREQ (260 * MHZ)
+#define AP_PLL_USB_FREQ (480 * MHZ)
+
+struct rda8810_reset_list {
+	int reg;
+	int bit;
+};
+
+struct rda_clk_priv {
+	struct device *dev;
+	struct regmap *regmap;
+	struct clk_hw_onecell_data *onecell;
+	struct reset_controller_dev rstctl;
+	const struct rda8810_reset_list *rstlist;
+};
+
+struct rda_clk_hw {
+	int id;
+	int reg;
+	struct clk_hw hw;
+
+	int ena_reg;
+	int ena_bit;
+
+	struct rda_clk_priv *priv;
+};
+
+struct rda_clk_matchdata {
+	const struct rda_clk_hw *clk_list;
+	int max_clocks;
+};
+
+static const struct clk_ops rda8810_clk_ops;
+
+#define RDA_CLK_INIT(_id, _name, _parent, _flags, _reg, _ena_reg, _ena_bit) { \
+	.id = _id, \
+	.reg = _reg, \
+	.ena_reg = _ena_reg, \
+	.ena_bit = _ena_bit, \
+	.hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \
+			_parent, \
+			&rda8810_clk_ops, \
+			_flags) \
+}
+
+#define RDA_CLK_INIT_NO_PARENT(_id, _name, _flags, _reg, _ena_reg, _ena_bit) { \
+	.id = _id, \
+	.reg = _reg, \
+	.ena_reg = _ena_reg, \
+	.ena_bit = _ena_bit, \
+	.hw.init = CLK_HW_INIT_NO_PARENT(_name, &rda8810_clk_ops, _flags) \
+}
+
+#define to_rda_rst(p) container_of(p, struct rda_clk_priv, rstctl)
+
+static inline struct rda_clk_hw *to_rda_hw(struct clk_hw *hw)
+{
+	return container_of(hw, struct rda_clk_hw, hw);
+}
+
+/* clock division value map */
+static const u8 clk_div_map[] = {
+	4*60,	/* 0 */
+	4*60,	/* 1 */
+	4*60,	/* 2 */
+	4*60,	/* 3 */
+	4*60,	/* 4 */
+	4*60,	/* 5 */
+	4*60,	/* 6 */
+	4*60,	/* 7 */
+	4*40,	/* 8 */
+	4*30,	/* 9 */
+	4*24,	/* 10 */
+	4*20,	/* 11 */
+	4*17,	/* 12 */
+	4*15,	/* 13 */
+	4*13,	/* 14 */
+	4*12,	/* 15 */
+	4*11,	/* 16 */
+	4*10,	/* 17 */
+	4*9,	/* 18 */
+	4*8,	/* 19 */
+	4*7,	/* 20 */
+	4*13/2,	/* 21 */
+	4*6,	/* 22 */
+	4*11/2,	/* 23 */
+	4*5,	/* 24 */
+	4*9/2,	/* 25 */
+	4*4,	/* 26 */
+	4*7/2,	/* 27 */
+	4*3,	/* 28 */
+	4*5/2,	/* 29 */
+	4*2,	/* 30 */
+	4*1,	/* 31 */
+};
+
+static u32 apsys_get_divreg(u32 basefreq, u32 reqfreq, u32 *pdiv2)
+{
+	int i;
+	int index;
+	u32 adiv;
+	u32 ndiv;
+
+	adiv = basefreq / (reqfreq >> 2);
+	if (pdiv2) {
+		/* try div2 mode first */
+		ndiv = adiv >> 1;
+	} else {
+		ndiv = adiv;
+	}
+
+	for (i = ARRAY_SIZE(clk_div_map) - 1; i >= 1; i--)
+		if (ndiv < ((clk_div_map[i] + clk_div_map[i-1]) >> 1))
+			break;
+	index = i;
+
+	if (pdiv2) {
+		if (adiv == (clk_div_map[index] << 1)) {
+			/* div2 mode is OK */
+			*pdiv2 = 1;
+		} else {
+			/* try div1 mode */
+			for (i = ARRAY_SIZE(clk_div_map) - 1; i >= 1; i--)
+				if (adiv < ((clk_div_map[i] + clk_div_map[i-1]) >> 1))
+					break;
+			/* compare the results between div1 and div2 */
+			if (abs(adiv - (clk_div_map[index] << 1)) <=
+					abs(adiv - clk_div_map[i])) {
+				*pdiv2 = 1;
+			} else {
+				*pdiv2 = 0;
+				index = i;
+			}
+		}
+	}
+
+	return index;
+}
+
+static u32 apsys_cal_freq_by_divreg(u32 basefreq, u32 reg, u32 div2)
+{
+	u32 newfreq;
+
+	if (reg >= ARRAY_SIZE(clk_div_map))
+		reg = ARRAY_SIZE(clk_div_map) - 1;
+
+	/* Assuming basefreq is smaller than 2^31 (2.147G Hz) */
+	newfreq = (basefreq << (div2 ? 0 : 1)) / (clk_div_map[reg] >> 1);
+	return newfreq;
+}
+
+static void apsys_get_reg_div(struct rda_clk_hw *rda_hw, u32 *reg, u32 *div2)
+{
+	struct rda_clk_priv *priv = rda_hw->priv;
+	int tmp_reg, tmp_div2;
+	int ret;
+
+	ret = regmap_read(priv->regmap, rda_hw->reg, &tmp_reg);
+	if (ret)
+		return;
+
+	tmp_div2 = tmp_reg & AP_PERI_SRC_DIV;
+
+	*reg = tmp_reg;
+	*div2 = tmp_div2;
+}
+
+static int apsys_get_uart_clock(unsigned long parent_rate, u32 *reg)
+{
+	int clksrc = 26000000;
+	u32 div;
+	int rate = 0;
+
+	if (*reg & AP_UART_SET_PLL)
+		clksrc = parent_rate / 8;
+
+	div = FIELD_GET(AP_UART_DIVIDER, *reg);
+
+	/* rate = clksrc / divmode / (div+2) */
+	rate =  clksrc / 4 / (div + 2);
+
+	return rate;
+}
+
+static int apsys_cal_uart_clock(int freq)
+{
+	int new_freq = freq;
+
+	/*
+	 * To calculate maximum clock:
+	 *     freq = 26 MHz / div / (0 + 2)
+	 *
+	 * For lowest clock:
+	 *     freq = 26 MHz / div / (0x3FF + 2)
+	 */
+	if (freq > 3250000)
+		new_freq = 3250000;
+	else if (freq < 6342)
+		new_freq = 6342;
+
+	new_freq = (26000000 + 4 / 2 * new_freq) / (4 * new_freq) - 2;
+
+	return new_freq;
+}
+
+static int rda8810_clk_set_rate(struct clk_hw *clk, unsigned long rate,
+		unsigned long parent_rate)
+{
+	struct rda_clk_hw *rda_hw = to_rda_hw(clk);
+	struct rda_clk_priv *priv = rda_hw->priv;
+	struct device *dev = priv->dev;
+	int val, div2 = 0;
+	int ret;
+
+	switch (rda_hw->id) {
+	case CLK_CPU:
+		val = apsys_get_divreg(AP_PLL_CPU_FREQ, rate, NULL);
+		break;
+	case CLK_AXI:
+	case CLK_AHB1:
+	case CLK_APB1:
+	case CLK_APB2:
+	case CLK_GCG:
+	case CLK_GPU:
+	case CLK_SFLSH:
+	case CLK_VOC:
+	case CLK_VPU:
+		val = apsys_get_divreg(parent_rate, rate, &div2);
+		break;
+	case CLK_UART1:
+	case CLK_UART2:
+	case CLK_UART3:
+		val = apsys_cal_uart_clock(rate);
+		if (val == 0)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (div2)
+		val |= AP_PERI_SRC_DIV;
+
+	dev_dbg(dev, "clk_id: %d - rate: %ld - parent rate: %ld - val: %d - div: %d\n",
+			rda_hw->id, rate, parent_rate, val, div2);
+
+	ret = regmap_write(priv->regmap, rda_hw->reg, val);
+	if (ret < 0)
+		return ret;
+
+	return rate;
+}
+
+static unsigned long rda8810_clk_recalc_rate(struct clk_hw *clk,
+		unsigned long parent_rate)
+{
+	struct rda_clk_hw *rda_hw = to_rda_hw(clk);
+	u32 reg, div2;
+
+	apsys_get_reg_div(rda_hw, &reg, &div2);
+
+	switch (rda_hw->id) {
+	case CLK_CPU:
+		return apsys_cal_freq_by_divreg(AP_PLL_CPU_FREQ, reg, 0);
+	case CLK_BUS:
+		return AP_PLL_BUS_FREQ;
+	case CLK_MEM:
+		return AP_PLL_MEM_FREQ >> (2 + div2);
+	/* Bus peripherals */
+	case CLK_USB:
+		return AP_PLL_USB_FREQ;
+	case CLK_AXI:
+	case CLK_AHB1:
+	case CLK_APB1:
+	case CLK_APB2:
+	case CLK_GCG:
+	case CLK_GPU:
+	case CLK_SFLSH:
+	case CLK_VOC:
+	case CLK_VPU:
+		return apsys_cal_freq_by_divreg(parent_rate, reg, div2);
+	/* For UART clocks, we'll have to do more calculation */
+	case CLK_UART1:
+	case CLK_UART2:
+	case CLK_UART3:
+		return apsys_get_uart_clock(parent_rate, &reg);
+	default:
+		return 0;
+	}
+}
+
+static long rda8810_clk_round_rate(struct clk_hw *clk, unsigned long rate,
+		unsigned long *parent_rate)
+{
+	return rate;
+}
+
+static int rda8810_clk_enable(struct clk_hw *clk)
+{
+	struct rda_clk_hw *rda_hw = to_rda_hw(clk);
+	struct rda_clk_priv *priv = rda_hw->priv;
+
+	if (rda_hw->ena_reg < 0 || rda_hw->ena_bit < 0)
+		return 0;
+
+	return regmap_write(priv->regmap, rda_hw->ena_reg, rda_hw->ena_bit);
+}
+
+static void rda8810_clk_disable(struct clk_hw *clk)
+{
+	struct rda_clk_hw *rda_hw = to_rda_hw(clk);
+	struct rda_clk_priv *priv = rda_hw->priv;
+
+	if (rda_hw->ena_reg < 0 || rda_hw->ena_bit < 0)
+		return;
+
+	regmap_write(priv->regmap, rda_hw->ena_reg + 4, rda_hw->ena_bit);
+}
+
+static const struct clk_ops rda8810_clk_ops = {
+	.enable = rda8810_clk_enable,
+	.disable = rda8810_clk_disable,
+
+	.recalc_rate = rda8810_clk_recalc_rate,
+	.round_rate = rda8810_clk_round_rate,
+	.set_rate = rda8810_clk_set_rate,
+};
+
+/* Root clocks */
+static struct rda_clk_hw rda8810_clk_cpu_desc =
+RDA_CLK_INIT_NO_PARENT(CLK_CPU, "cpu", CLK_IS_CRITICAL, 0xC8,
+		AP_REG_CPU_ENABLE, AP_ENABLE_CPU_CORE);
+static struct rda_clk_hw rda8810_clk_mem_desc =
+RDA_CLK_INIT_NO_PARENT(CLK_MEM, "mem", CLK_IS_CRITICAL, 0xE0, -1, -1);
+
+static struct rda_clk_hw rda8810_clk_bus_desc =
+RDA_CLK_INIT_NO_PARENT(CLK_BUS, "bus", CLK_IS_CRITICAL, -1, -1, -1);
+
+/* Bus clocks */
+static struct rda_clk_hw rda8810_clk_usb_desc = RDA_CLK_INIT(CLK_USB, "usb",
+		(const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
+		0, -1, -1, -1);
+static struct rda_clk_hw rda8810_clk_axi_desc = RDA_CLK_INIT(CLK_AXI, "axi",
+		(const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
+		0, 0xCC, -1, -1);
+static struct rda_clk_hw rda8810_clk_gcg_desc = RDA_CLK_INIT(CLK_GCG, "gcg",
+		(const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
+		0, 0xD0, -1, -1);
+static struct rda_clk_hw rda8810_clk_ahb1_desc = RDA_CLK_INIT(CLK_AHB1, "ahb1",
+		(const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
+		0, 0xD4, -1, -1);
+static struct rda_clk_hw rda8810_clk_apb1_desc = RDA_CLK_INIT(CLK_APB1, "apb1",
+		(const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
+		0, 0xD8, -1, -1);
+static struct rda_clk_hw rda8810_clk_apb2_desc = RDA_CLK_INIT(CLK_APB2, "apb2",
+		(const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
+		0, 0xDC, -1, -1);
+static struct rda_clk_hw rda8810_clk_gpu_desc = RDA_CLK_INIT(CLK_GPU, "gpu",
+		(const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
+		0, 0xE4, AP_REG_APO_ENABLE, AP_ENABLE_APOC_GPU);
+static struct rda_clk_hw rda8810_clk_vpu_desc = RDA_CLK_INIT(CLK_VPU, "vpu",
+		(const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
+		0, 0xE8, AP_REG_APO_ENABLE, AP_ENABLE_APOC_VPU);
+static struct rda_clk_hw rda8810_clk_voc_desc = RDA_CLK_INIT(CLK_VOC, "voc",
+		(const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
+		0, 0xEC, AP_REG_APO_ENABLE,
+		AP_ENABLE_APOC_VOC | AP_ENABLE_APOC_VOC_CORE | AP_ENABLE_APOC_VOC_ALWAYS);
+
+/* APB1 peripherals */
+static struct rda_clk_hw rda8810_clk_spiflash_desc = RDA_CLK_INIT(CLK_SFLSH, "spiflash",
+		(const struct clk_parent_data[]) { { .hw = &rda8810_clk_apb1_desc.hw } },
+		0, 0xF0, AP_REG_APO_ENABLE, AP_ENABLE_APOC_SPIFLASH);
+
+/* APB2 peripherals */
+static struct rda_clk_hw rda8810_clk_uart_desc[] = {
+	RDA_CLK_INIT(CLK_UART1, "uart1",
+			(const struct clk_parent_data[]) { { .hw = &rda8810_clk_apb2_desc.hw } },
+			0, 0xF4,
+			AP_REG_APO_ENABLE, AP_ENABLE_APOC_UART1),
+	RDA_CLK_INIT(CLK_UART2, "uart2",
+			(const struct clk_parent_data[]) { { .hw = &rda8810_clk_apb2_desc.hw } },
+			0, 0xF8,
+			AP_REG_APO_ENABLE, AP_ENABLE_APOC_UART2),
+	RDA_CLK_INIT(CLK_UART3, "uart3",
+			(const struct clk_parent_data[]) { { .hw = &rda8810_clk_apb2_desc.hw } },
+			0, 0xFC,
+			AP_REG_APO_ENABLE, AP_ENABLE_APOC_UART3),
+};
+
+static struct rda_clk_hw *const rda8810_clk_list[] = {
+	&rda8810_clk_cpu_desc,
+	&rda8810_clk_bus_desc,
+	&rda8810_clk_mem_desc,
+
+	&rda8810_clk_usb_desc,
+	&rda8810_clk_axi_desc,
+	&rda8810_clk_gcg_desc,
+	&rda8810_clk_ahb1_desc,
+	&rda8810_clk_apb1_desc,
+	&rda8810_clk_apb2_desc,
+
+	&rda8810_clk_gpu_desc,
+	&rda8810_clk_vpu_desc,
+	&rda8810_clk_voc_desc,
+
+	&rda8810_clk_spiflash_desc,
+
+	&rda8810_clk_uart_desc[0],
+	&rda8810_clk_uart_desc[1],
+	&rda8810_clk_uart_desc[2],
+};
+
+static const struct rda8810_reset_list rda8810_rst_data[] = {
+	/* ID, REG */
+
+	/* CPU */
+	[RST_CPU] = { AP_RST_CPU_REG, AP_RST_CPU_CORE },
+
+	/* AXI */
+	[RST_AXI_VOC] = { AP_RST_AXI_REG, AP_RST_AXI_VOC },
+	[RST_AXI_DMA] = { AP_RST_AXI_REG, AP_RST_AXI_DMA },
+	[RST_AXI_CONNECT] = { AP_RST_AXI_REG, AP_RST_AXI_CONNECT },
+	[RST_AXI_VPU] = { AP_RST_AXI_REG, AP_RST_AXI_VPU },
+
+	/* GCG */
+	[RST_GCG_GOUDA] = { AP_RST_GCG_REG, AP_RST_GCG_GOUDA },
+	[RST_GCG_CAMERA] = { AP_RST_GCG_REG, AP_RST_GCG_CAMERA },
+	[RST_GCG_LCDC] = { AP_RST_GCG_REG, AP_RST_GCG_LCDC },
+
+	/* AHB1 */
+	[RST_AHB1_USBC] = { AP_RST_AHB1_REG, AP_RST_AHB1_USBC },
+	[RST_AHB1_SPIFLASH] = { AP_RST_AHB1_REG, AP_RST_AHB1_SPIFLASH },
+
+	/* APB1 */
+	[RST_APB1_TIMER] = { AP_RST_APB1_REG, AP_RST_APB1_TIMER },
+	[RST_APB1_KEYPAD] = { AP_RST_APB1_REG, AP_RST_APB1_KEYPAD },
+	[RST_APB1_GPIO] = { AP_RST_APB1_REG, AP_RST_APB1_GPIO },
+	[RST_APB1_PWM] = { AP_RST_APB1_REG, AP_RST_APB1_PWM },
+	[RST_APB1_AIF] = { AP_RST_APB1_REG, AP_RST_APB1_AIF },
+	[RST_APB1_AUIFC] = { AP_RST_APB1_REG, AP_RST_APB1_AUIFC },
+	[RST_APB1_I2C1] = { AP_RST_APB1_REG, AP_RST_APB1_I2C1 },
+	[RST_APB1_I2C2] = { AP_RST_APB1_REG, AP_RST_APB1_I2C2 },
+	[RST_APB1_I2C3] = { AP_RST_APB1_REG, AP_RST_APB1_I2C3 },
+	[RST_APB1_COMREGS] = { AP_RST_APB1_REG, AP_RST_APB1_COMREGS },
+	[RST_APB1_DMC] = { AP_RST_APB1_REG, AP_RST_APB1_DMC },
+	[RST_APB1_DDRPHY_P] = { AP_RST_APB1_REG, AP_RST_APB1_DDRPHY_P },
+
+	/* APB2 */
+	[RST_APB2_IFC] = { AP_RST_APB2_REG, AP_RST_APB2_IFC },
+	[RST_APB2_UART1] = { AP_RST_APB2_REG, AP_RST_APB2_UART1 },
+	[RST_APB2_UART2] = { AP_RST_APB2_REG, AP_RST_APB2_UART2 },
+	[RST_APB2_UART3] = { AP_RST_APB2_REG, AP_RST_APB2_UART3 },
+	[RST_APB2_SPI1] = { AP_RST_APB2_REG, AP_RST_APB2_SPI1 },
+	[RST_APB2_SPI2] = { AP_RST_APB2_REG, AP_RST_APB2_SPI2 },
+	[RST_APB2_SPI3] = { AP_RST_APB2_REG, AP_RST_APB2_SPI3 },
+	[RST_APB2_SDMMC1] = { AP_RST_APB2_REG, AP_RST_APB2_SDMMC1 },
+	[RST_APB2_SDMMC2] = { AP_RST_APB2_REG, AP_RST_APB2_SDMMC2 },
+	[RST_APB2_SDMMC3] = { AP_RST_APB2_REG, AP_RST_APB2_SDMMC3 },
+	[RST_APB2_NAND] = { AP_RST_APB2_REG, AP_RST_APB2_NANDFLASH },
+
+	/* MEM */
+	[RST_MEM_GPU] = { AP_RST_MEM_REG, AP_RST_MEM_GPU },
+	[RST_MEM_VPU] = { AP_RST_MEM_REG, AP_RST_MEM_VPU },
+	[RST_MEM_DMC] = { AP_RST_MEM_REG, AP_RST_MEM_DMC },
+	[RST_MEM_DDRPHY_P] = { AP_RST_MEM_REG, AP_RST_MEM_DDRPHY_P },
+};
+
+static int rda8810_reset_assert(struct reset_controller_dev *rstctl, unsigned long id)
+{
+	struct rda_clk_priv *priv = to_rda_rst(rstctl);
+
+	return regmap_write(priv->regmap, rda8810_rst_data[id].reg, rda8810_rst_data[id].bit);
+}
+
+static int rda8810_reset_deassert(struct reset_controller_dev *rstctl, unsigned long id)
+{
+	struct rda_clk_priv *priv = to_rda_rst(rstctl);
+
+	return regmap_write(priv->regmap, rda8810_rst_data[id].reg + 4, rda8810_rst_data[id].bit);
+}
+
+static const struct reset_control_ops rda8810_rst_ops = {
+	.assert = &rda8810_reset_assert,
+	.deassert = &rda8810_reset_deassert,
+};
+
+static int rda8810_clk_register(struct rda_clk_priv *priv)
+{
+	struct device *dev = priv->dev;
+	struct clk_hw_onecell_data *onecell_data;
+	int ret;
+	int i;
+
+	onecell_data = devm_kzalloc(dev,
+			struct_size(onecell_data, hws, ARRAY_SIZE(rda8810_clk_list)),
+			GFP_KERNEL);
+	if (!onecell_data)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(rda8810_clk_list); i++) {
+		rda8810_clk_list[i]->priv = priv;
+
+		ret = devm_clk_hw_register(dev, &rda8810_clk_list[i]->hw);
+		if (ret) {
+			dev_err(dev, "Failed to register clock: %d\n", ret);
+			return ret;
+		}
+		onecell_data->hws[i] = &rda8810_clk_list[i]->hw;
+	}
+	onecell_data->num = i;
+	priv->onecell = onecell_data;
+
+	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, onecell_data);
+}
+
+static int rda8810_rst_register(struct rda_clk_priv *priv)
+{
+	struct device *dev = priv->dev;
+
+	priv->rstctl.dev = priv->dev;
+	priv->rstctl.nr_resets = RST_COUNT;
+	priv->rstctl.of_node = priv->dev->of_node;
+	priv->rstctl.ops = &rda8810_rst_ops;
+	priv->rstctl.owner = THIS_MODULE;
+
+	return devm_reset_controller_register(dev, &priv->rstctl);
+}
+
+static int rda8810_clk_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rda_clk_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return dev_err_probe(dev, -ENOMEM, "Cannot allocate memory\n");
+
+	priv->dev = dev;
+
+	priv->regmap = syscon_node_to_regmap(dev->of_node);
+	if (IS_ERR(priv->regmap))
+		return dev_err_probe(dev, -ENOMEM, "Cannot initialize regmap\n");
+
+	ret = rda8810_clk_register(priv);
+	if (ret)
+		return dev_err_probe(dev, -EINVAL, "Failed to setup clock: %d\n", ret);
+
+	ret = rda8810_rst_register(priv);
+	if (ret)
+		return dev_err_probe(dev, -EINVAL, "Failed to setup reset: %d\n", ret);
+
+	platform_set_drvdata(pdev, priv);
+
+	return 0;
+}
+
+static const struct of_device_id rda8810_clk_of_match_table[] = {
+	{ .compatible = "rda,8810pl-apsyscon", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rda8810_clk_of_match_table);
+
+static struct platform_driver rda8810_clk_driver = {
+	.probe = rda8810_clk_probe,
+	.driver = {
+		.name = "rda8810-clk",
+		.of_match_table = rda8810_clk_of_match_table,
+	},
+};
+module_platform_driver(rda8810_clk_driver);
+
+MODULE_AUTHOR("Dang Huynh <dang.huynh@mainlining.org>");
+MODULE_DESCRIPTION("RDA8810PL clock and reset driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rda8810-clk");

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 11/25] dts: unisoc: rda8810pl: Enable clock/reset driver
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (9 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 10/25] drivers: clk: Add Clock and Reset Driver for RDA Micro RDA8810PL SoC Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-17  0:41   ` Krzysztof Kozlowski
  2025-09-16 20:25 ` [PATCH 12/25] dts: unisoc: rda8810pl: Add OPP for CPU and define L2 cache Dang Huynh via B4 Relay
                   ` (14 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Enable RDA8810PL Clock and Reset driver so we can use it for various
subsystems.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/boot/dts/unisoc/rda8810pl.dtsi | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
index 45a2fd3e04cea5aac4fb6b40a6b332ce3eee4f2c..1f7a6908d68367441e5dc865216cc7a5c39feb35 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
+++ b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
@@ -8,6 +8,7 @@
 
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/clock/rda,8810pl-apclk.h>
 
 / {
 	compatible = "rda,8810pl";
@@ -79,6 +80,13 @@ apb@20900000 {
 		#size-cells = <1>;
 		ranges = <0x0 0x20900000 0x100000>;
 
+		ap_syscon: syscon@0 {
+			compatible = "rda,8810pl-apsyscon", "syscon";
+			reg = <0x0 0x1000>;
+			#clock-cells = <1>;
+			#reset-cells = <1>;
+		};
+
 		timer@10000 {
 			compatible = "rda,8810pl-timer";
 			reg = <0x10000 0x1000>;

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 12/25] dts: unisoc: rda8810pl: Add OPP for CPU and define L2 cache
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (10 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 11/25] dts: unisoc: rda8810pl: Enable clock/reset driver Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 13/25] dts: unisoc: orangepi: Disable UART with no users Dang Huynh via B4 Relay
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Add available frequency table came from downstream kernel, this
ensures that the CPU clock can be dynamically tuned.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/boot/dts/unisoc/rda8810pl.dtsi | 52 +++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
index 1f7a6908d68367441e5dc865216cc7a5c39feb35..299b29e4df6e0a04c5769a568eba73ed1684a9e5 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
+++ b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
@@ -16,6 +16,54 @@ / {
 	#address-cells = <1>;
 	#size-cells = <1>;
 
+	/*
+	 * There are two frequency table for CPU.
+	 *
+	 * "High" table is used when operating in normal mode
+	 * "Low" table is used when operating in power saving mode
+	 */
+	cpu_high_opp_table: opp-table-0 {
+		compatible = "operating-points-v2";
+		opp-shared;
+
+		opp-329333333 {
+			opp-hz = /bits/ 64 <329333333>;
+		};
+
+		opp-395200000 {
+			opp-hz = /bits/ 64 <395200000>;
+		};
+
+		opp-494000000 {
+			opp-hz = /bits/ 64 <494000000>;
+		};
+
+		opp-988000000 {
+			opp-hz = /bits/ 64 <988000000>;
+		};
+	};
+
+	cpu_low_opp_table: opp-table-1 {
+		compatible = "operating-points-v2";
+		opp-shared;
+
+		opp-266666666 {
+			opp-hz = /bits/ 64 <266666666>;
+		};
+
+		opp-320000000 {
+			opp-hz = /bits/ 64 <320000000>;
+		};
+
+		opp-400000000 {
+			opp-hz = /bits/ 64 <400000000>;
+		};
+
+		opp-800000000 {
+			opp-hz = /bits/ 64 <800000000>;
+		};
+	};
+
 	cpus {
 		#address-cells = <1>;
 		#size-cells = <0>;
@@ -24,6 +72,10 @@ cpu@0 {
 			device_type = "cpu";
 			compatible = "arm,cortex-a5";
 			reg = <0x0>;
+			next-level-cache = <&l2>;
+
+			clocks = <&ap_syscon CLK_CPU>;
+			operating-points-v2 = <&cpu_high_opp_table>;
 		};
 	};
 

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 13/25] dts: unisoc: orangepi: Disable UART with no users
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (11 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 12/25] dts: unisoc: rda8810pl: Add OPP for CPU and define L2 cache Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 14/25] dt-bindings: power: reset: Add RDA Micro Modem Reset Dang Huynh via B4 Relay
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

By default, we should keep unused peripherals disabled.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts | 10 ----------
 arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts    | 10 ----------
 2 files changed, 20 deletions(-)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts b/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts
index 98e34248ae80b1fcd673ff01fe045db412d5bcc9..46ccb9ad510c0df142b845d6fc5633b69c2298dd 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts
+++ b/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts
@@ -34,16 +34,6 @@ uart_clk: uart-clk {
 	};
 };
 
-&uart1 {
-	status = "okay";
-	clocks = <&uart_clk>;
-};
-
-&uart2 {
-	status = "okay";
-	clocks = <&uart_clk>;
-};
-
 &uart3 {
 	status = "okay";
 	clocks = <&uart_clk>;
diff --git a/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts b/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts
index 728f76931b995fdfc036b586f899b15a7f07528b..a1d61ef138d12bb3ecb4b24513cc1a7dfbac3107 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts
+++ b/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts
@@ -34,16 +34,6 @@ uart_clk: uart-clk {
 	};
 };
 
-&uart1 {
-	status = "okay";
-	clocks = <&uart_clk>;
-};
-
-&uart2 {
-	status = "okay";
-	clocks = <&uart_clk>;
-};
-
 &uart3 {
 	status = "okay";
 	clocks = <&uart_clk>;

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 14/25] dt-bindings: power: reset: Add RDA Micro Modem Reset
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (12 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 13/25] dts: unisoc: orangepi: Disable UART with no users Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-17  0:44   ` Krzysztof Kozlowski
  2025-09-16 20:25 ` [PATCH 15/25] power: reset: Add basic power reset driver for RDA8810PL Dang Huynh via B4 Relay
                   ` (11 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Add documentation describing the RDA Micro modem reset controller

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 .../bindings/power/reset/rda,md-reset.yaml         | 36 ++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/Documentation/devicetree/bindings/power/reset/rda,md-reset.yaml b/Documentation/devicetree/bindings/power/reset/rda,md-reset.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6d09bc8ee6b257aec9d2c4738d285490044003ea
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/reset/rda,md-reset.yaml
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/reset/rda,md-reset.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RDA Micro Modem Reset
+
+maintainers:
+  - Dang Huynh <dang.huynh@mainlining.org>
+
+description:
+  The modem has a reset register that can be used to fully reset the board.
+
+  To do that, a magic value needs to be written to unprotect the register,
+  then the soft reset register can be used.
+
+properties:
+  compatible:
+    const: rda,md-reset
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    reset-controller@1a00000 {
+      compatible = "rda,md-reset";
+      reg = <0x1a00000 0x4>;
+    };

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 15/25] power: reset: Add basic power reset driver for RDA8810PL
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (13 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 14/25] dt-bindings: power: reset: Add RDA Micro Modem Reset Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-17  0:45   ` Krzysztof Kozlowski
  2025-09-16 20:25 ` [PATCH 16/25] dts: unisoc: rda8810pl: Enable modem reset Dang Huynh via B4 Relay
                   ` (10 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

This basic driver can only reboot, powering off requires the modem
firmware which we don't have yet.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 MAINTAINERS                      |  6 +++++
 drivers/power/reset/Kconfig      |  9 +++++++
 drivers/power/reset/Makefile     |  1 +
 drivers/power/reset/rda-reboot.c | 58 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 74 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index cbe2ab8af6dcd40dd1456d9df55673dace3c87b2..5ec24d8657bffb55c160947a930980e428c6a6b7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21399,6 +21399,12 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/clock/rda,8810pl-apsyscon.yaml
 F:	drivers/clk/rda/clk-rda8810.c
 
+RDA MICRO MODEM RESET DRIVER
+M:	Dang Huynh <dang.huynh@mainlining.org>
+S:	Maintained
+F:	Documentation/devicetree/bindings/power/reset/rda,md-reset.yaml
+F:	drivers/power/reset/rda-reboot.c
+
 RDA MICRO REAL TIME CLOCK DRIVER
 M:	Dang Huynh <dang.huynh@mainlining.org>
 S:	Maintained
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 77ea3129c70806929f3c248667db42f05f5f1d27..de9b1afb94d14a5d23286ddb302af4107d649c12 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -205,6 +205,15 @@ config POWER_RESET_QNAP
 
 	  Say Y if you have a QNAP NAS.
 
+config POWER_RESET_RDA
+	bool "RDA Micro Reset Driver"
+	depends on ARCH_RDA
+	help
+	  This driver supports soft resetting RDA Micro boards by writing
+	  magic values to the modem register.
+
+	  Say Y if you have a board with RDA Micro SoC.
+
 config POWER_RESET_REGULATOR
 	bool "Regulator subsystem power-off driver"
 	depends on OF && REGULATOR
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index b7c2b5940be9971548a5527384d1931abff11c4c..14371230410dad2852489160f4fc23d8fd087d6e 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_POWER_RESET_ODROID_GO_ULTRA_POWEROFF) += odroid-go-ultra-poweroff.o
 obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
 obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
 obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
+obj-$(CONFIG_POWER_RESET_RDA) += rda-reboot.o
 obj-$(CONFIG_POWER_RESET_REGULATOR) += regulator-poweroff.o
 obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
 obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
diff --git a/drivers/power/reset/rda-reboot.c b/drivers/power/reset/rda-reboot.c
new file mode 100644
index 0000000000000000000000000000000000000000..d87b063ba67d847f8e869e50a6c01427b2866889
--- /dev/null
+++ b/drivers/power/reset/rda-reboot.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025, Dang Huynh <dang.huynh@mainlining.org>
+ *
+ * Based on drivers/power/reset/msm-poweroff.c:
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/pm.h>
+#include <linux/mfd/syscon.h>
+
+static void __iomem *rda_md_sysctrl;
+
+static int do_rda_reboot(struct sys_off_data *data)
+{
+	/* unprotect md registers */
+	writel(0x00A50001, rda_md_sysctrl);
+
+	/* reset all */
+	writel(0x80000000, rda_md_sysctrl + 4);
+
+	return NOTIFY_DONE;
+}
+
+static int rda_reboot_probe(struct platform_device *pdev)
+{
+	rda_md_sysctrl = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(rda_md_sysctrl))
+		return PTR_ERR(rda_md_sysctrl);
+
+	devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART,
+				      128, do_rda_reboot, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id of_rda_reboot_match[] = {
+	{ .compatible = "rda,md-reset", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_rda_reboot_match);
+
+static struct platform_driver rda_reboot_driver = {
+	.probe = rda_reboot_probe,
+	.driver = {
+		.name = "rda-reboot",
+		.of_match_table = of_match_ptr(of_rda_reboot_match),
+	},
+};
+builtin_platform_driver(rda_reboot_driver);

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 16/25] dts: unisoc: rda8810pl: Enable modem reset
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (14 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 15/25] power: reset: Add basic power reset driver for RDA8810PL Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-17  0:46   ` Krzysztof Kozlowski
  2025-09-16 20:25 ` [PATCH 17/25] drivers: gpio: rda: Make direction register unreadable Dang Huynh via B4 Relay
                   ` (9 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

This allows us to reboot the board from the OS.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/boot/dts/unisoc/rda8810pl.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
index 299b29e4df6e0a04c5769a568eba73ed1684a9e5..e90ae7845de7b79e55e9cd339a82313b423e0252 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
+++ b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
@@ -98,6 +98,11 @@ modem@10000000 {
 		#size-cells = <1>;
 		ranges = <0x0 0x10000000 0xfffffff>;
 
+		reset-controller@1a00000 {
+			compatible = "rda,md-reset";
+			reg = <0x1a00000 0x4>;
+		};
+
 		rtc@1a06000 {
 			compatible = "rda,8810pl-rtc";
 			reg = <0x1a06000 0x1000>;

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 17/25] drivers: gpio: rda: Make direction register unreadable
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (15 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 16/25] dts: unisoc: rda8810pl: Enable modem reset Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-17  8:00   ` Bartosz Golaszewski
  2025-09-16 20:25 ` [PATCH 18/25] dt-bindings: dma: Add RDA IFC DMA Dang Huynh via B4 Relay
                   ` (8 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

The register doesn't like to be read, this causes the SD Card
Card Detect GPIO to misbehaves in the OS.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 drivers/gpio/gpio-rda.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpio/gpio-rda.c b/drivers/gpio/gpio-rda.c
index b4db8553a2371ae407fdb7e681d0f82c4d9f74b7..56aaa9f33d29469dfb1bf86ed7b63c54b413c89c 100644
--- a/drivers/gpio/gpio-rda.c
+++ b/drivers/gpio/gpio-rda.c
@@ -245,7 +245,7 @@ static int rda_gpio_probe(struct platform_device *pdev)
 		.clr = rda_gpio->base + RDA_GPIO_CLR,
 		.dirout = rda_gpio->base + RDA_GPIO_OEN_SET_OUT,
 		.dirin = rda_gpio->base + RDA_GPIO_OEN_SET_IN,
-		.flags = BGPIOF_READ_OUTPUT_REG_SET,
+		.flags = BGPIOF_READ_OUTPUT_REG_SET | BGPIOF_UNREADABLE_REG_DIR,
 	};
 
 	ret = gpio_generic_chip_init(&rda_gpio->chip, &config);

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 18/25] dt-bindings: dma: Add RDA IFC DMA
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (16 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 17/25] drivers: gpio: rda: Make direction register unreadable Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 19/25] dmaengine: Add RDA IFC driver Dang Huynh via B4 Relay
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

The Intelligent Flow Controller (IFC) is a scatter/gather DMA
controller.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 Documentation/devicetree/bindings/dma/rda,ifc.yaml | 42 ++++++++++++++++++++++
 include/dt-bindings/dma/rda-ifc.h                  | 28 +++++++++++++++
 2 files changed, 70 insertions(+)

diff --git a/Documentation/devicetree/bindings/dma/rda,ifc.yaml b/Documentation/devicetree/bindings/dma/rda,ifc.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3bb5932e8ff9404e3980eaef607efceb6d883bda
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/rda,ifc.yaml
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/rda,ifc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RDA Intelligent Flow Controller (IFC)
+
+maintainers:
+  - Dang Huynh <dang.huynh@mainlining.org>
+
+description: |
+  RDA IFC is a DMA controller, it only supports scatter/gather lists.
+
+allOf:
+  - $ref: dma-controller.yaml#
+
+properties:
+  compatible:
+    const: rda,8810pl-ifc
+
+  reg:
+    maxItems: 1
+
+  "#dma-cells":
+    const: 1
+    description:
+      The cell corresponding to DMA request ID
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    ifc: dma-controller@f0000 {
+      compatible = "rda,8810pl-ifc";
+      reg = <0xf0000 0x1000>;
+      #dma-cells = <1>;
+    };
diff --git a/include/dt-bindings/dma/rda-ifc.h b/include/dt-bindings/dma/rda-ifc.h
new file mode 100644
index 0000000000000000000000000000000000000000..e075fe26158e834d1ae87b6dce61e41fb931add3
--- /dev/null
+++ b/include/dt-bindings/dma/rda-ifc.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+
+#ifndef __DT_BINDINGS_DMA_RDA_IFC_H__
+#define __DT_BINDINGS_DMA_RDA_IFC_H__
+
+#define IFC_UART1_TX 0
+#define IFC_UART1_RX 1
+#define IFC_UART2_TX 2
+#define IFC_UART2_RX 3
+#define IFC_SPI1_TX 4
+#define IFC_SPI1_RX 5
+#define IFC_SPI2_TX 6
+#define IFC_SPI2_RX 7
+#define IFC_SPI3_TX 8
+#define IFC_SPI3_RX 9
+#define IFC_SDMMC1_TX 10
+#define IFC_SDMMC1_RX 11
+#define IFC_SDMMC2_TX 12
+#define IFC_SDMMC2_RX 13
+#define IFC_SDMMC3_TX 14
+#define IFC_SDMMC3_RX 15
+#define IFC_NFSC_TX 16
+#define IFC_NFSC_RX 17
+#define IFC_UART3_TX 18
+#define IFC_UART3_RX 19
+#define IFC_NO_REQUEST 20
+
+#endif /* __DT_BINDINGS_DMA_RDA_IFC_H__ */

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 19/25] dmaengine: Add RDA IFC driver
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (17 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 18/25] dt-bindings: dma: Add RDA IFC DMA Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 20/25] dts: unisoc: rda8810pl: Enable IFC Dang Huynh via B4 Relay
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

IFC stands for Intelligent Flow Control, a scatter/gather DMA
controller.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 MAINTAINERS           |   6 +
 drivers/dma/Kconfig   |  10 ++
 drivers/dma/Makefile  |   1 +
 drivers/dma/rda-ifc.c | 450 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 467 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 5ec24d8657bffb55c160947a930980e428c6a6b7..91be43782f4ba8aacb629002d357a66704f10b2b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21399,6 +21399,12 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/clock/rda,8810pl-apsyscon.yaml
 F:	drivers/clk/rda/clk-rda8810.c
 
+RDA MICRO INTELLIGENT FLOW CONTROLLER DRIVER
+M:	Dang Huynh <dang.huynh@mainlining.org>
+S:	Maintained
+F:	Documentation/devicetree/bindings/dma/rda,ifc.yaml
+F:	drivers/dma/rda-ifc.c
+
 RDA MICRO MODEM RESET DRIVER
 M:	Dang Huynh <dang.huynh@mainlining.org>
 S:	Maintained
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index b8a74b1798ba1d44b26553990428c065de6fc535..4a032acba932b0e8cc17c8e0f15b9ecafbab210b 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -571,6 +571,16 @@ config PLX_DMA
 	  These are exposed via extra functions on the switch's
 	  upstream port. Each function exposes one DMA channel.
 
+config RDA_IFC
+	bool "RDA IFC support"
+	depends on ARCH_RDA
+	select DMA_ENGINE
+	help
+	  Support RDA Intelligent Flow Controller for RDA Micro SoC.
+	  The Intelligent Flow Controller is a scatter/gather DMA controller.
+
+	  If unsure, say N.
+
 config SOPHGO_CV1800B_DMAMUX
 	tristate "Sophgo CV1800/SG2000 series SoC DMA multiplexer support"
 	depends on MFD_SYSCON
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index a54d7688392b1a0e956fa5d23633507f52f017d9..40f6c61dcce739f3ffd064fbdc23388cfca83184 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_PL330_DMA) += pl330.o
 obj-$(CONFIG_PLX_DMA) += plx_dma.o
 obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
 obj-$(CONFIG_PXA_DMA) += pxa_dma.o
+obj-$(CONFIG_RDA_IFC) += rda-ifc.o
 obj-$(CONFIG_RENESAS_DMA) += sh/
 obj-$(CONFIG_SF_PDMA) += sf-pdma/
 obj-$(CONFIG_SOPHGO_CV1800B_DMAMUX) += cv1800b-dmamux.o
diff --git a/drivers/dma/rda-ifc.c b/drivers/dma/rda-ifc.c
new file mode 100644
index 0000000000000000000000000000000000000000..ff7f59876a5895fbdc1adf584e11519bcfcfdb11
--- /dev/null
+++ b/drivers/dma/rda-ifc.c
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RDA Micro Intelligent Flow Controller
+ *
+ * Copyright (C) 2013 RDA Microelectronics Inc.
+ * Copyright (C) 2025 Dang Huynh <dang.huynh@mainlining.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include "dmaengine.h"
+
+#include <dt-bindings/dma/rda-ifc.h>
+
+/* Registers */
+#define IFC_REG_GET_CH 0x0
+#define IFC_REG_DMA_STATUS 0x4
+#define IFC_REG_DEBUG_STATUS 0x8
+
+/* Channel registers */
+#define IFC_REG_SG_CONTROL 0x0
+#define IFC_REG_SG_STATUS 0x4
+#define IFC_REG_SG_START_ADDR 0x8
+#define IFC_REG_SG_TC 0xC
+
+#define IFC_REG_CH_RFSPI_CONTROL 0x80
+#define IFC_REG_CH_RFSPI_STATUS 0x84
+#define IFC_REG_CH_RFSPI_START_ADDR 0x88
+#define IFC_REG_CH_RFSPI_END_ADDR 0x8C
+#define IFC_REG_CH_RFSPI_TC 0x90
+
+/* Bits */
+/* DMA_STATUS */
+#define IFC_DMA_CH_ENABLE GENMASK(8, 0)
+#define IFC_DMA_CH_BUSY GENMASK(23, 16)
+/* DEBUG_STATUS */
+#define IFC_DBG_STATUS BIT(0)
+/* CONTROL */
+#define IFC_CTL_ENABLE BIT(0)
+#define IFC_CTL_DISABLE BIT(1)
+#define IFC_CTL_CH_RD_HW_EXCH BIT(2)
+#define IFC_CTL_CH_WR_HW_EXCH BIT(3)
+#define IFC_CTL_AUTODISABLE BIT(4)
+#define IFC_CTL_SIZE GENMASK(7, 5) /* byte: 0 - halfword: 1 - word: 2 */
+#define IFC_CTL_REQ_SRC GENMASK(15, 8)
+#define IFC_CTL_FLUSH BIT(16)
+#define IFC_CTL_SG_NUM GENMASK(24, 17)
+/* STATUS */
+#define IFC_STATUS_ENABLE BIT(0)
+#define IFC_STATUS_FIFO_EMPTY BIT(4)
+
+/*
+ * An available IFC channel can be obtained by reading IFC_REG_GET_CH register,
+ * if no channel are available the register reads 0xF.
+ *
+ * Memory map for each channel (starts at 0x10):
+ *
+ * [IFC_REG_SG_CONTROL]
+ * [IFC_REG_SG_STATUS]
+ * [IFC_REG_SG_START_ADDR] (1st sg_table)
+ * [IFC_REG_SG_TC]
+ * ...
+ *
+ * Depends on the hardware, it might support more than one sg table. If it
+ * does, the next sg table is right next to previous table.
+ *
+ * The next channel is right after the memory map above.
+ *
+ * The DMA channel MUST be disabled after the transaction is done or some IP
+ * might misbehaves.
+ */
+
+struct rda_ifc_chan {
+	struct rda_ifc *rda_ifc;
+	void __iomem *chan_base;
+	spinlock_t lock;
+
+	struct dma_chan chan;
+	unsigned int request_id;
+
+	enum dma_transfer_direction direction;
+	struct dma_slave_config sconfig;
+	struct dma_async_tx_descriptor tx;
+};
+
+struct rda_ifc {
+	struct device *dev;
+	void __iomem *base;
+
+	struct dma_device ddev;
+
+	int sg_max;
+	int max_chan;
+	struct rda_ifc_chan channels[] __counted_by(max_chan);
+};
+
+struct rda_ifc_platinfo {
+	int sg_max;
+	int std_channb;
+};
+
+static struct rda_ifc_chan *to_ifc_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct rda_ifc_chan, chan);
+}
+
+static int rda_ifc_device_config(struct dma_chan *chan, struct dma_slave_config *config)
+{
+	struct rda_ifc_chan *ifc_chan = to_ifc_chan(chan);
+
+	ifc_chan->direction = (ifc_chan->request_id & 1) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
+	memcpy(&ifc_chan->sconfig, config, sizeof(*config));
+
+	return 0;
+}
+
+static void rda_ifc_issue_pending(struct dma_chan *chan)
+{
+	struct rda_ifc_chan *ifc_chan = to_ifc_chan(chan);
+	unsigned long flags;
+	u32 control;
+
+	spin_lock_irqsave(&ifc_chan->lock, flags);
+
+	control = readl(ifc_chan->chan_base);
+	control |= IFC_CTL_ENABLE;
+	writel(control, ifc_chan->chan_base);
+
+	spin_unlock_irqrestore(&ifc_chan->lock, flags);
+}
+
+static dma_cookie_t rda_ifc_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	return dma_cookie_assign(tx);
+}
+
+static struct dma_async_tx_descriptor *rda_ifc_prep_slave_sg(struct dma_chan *chan,
+		struct scatterlist *sgl, unsigned int sg_len,
+		enum dma_transfer_direction direction, unsigned long dma_flags,
+		void *context)
+{
+	struct rda_ifc_chan *ifc_chan = to_ifc_chan(chan);
+	struct rda_ifc *ifc = ifc_chan->rda_ifc;
+	struct device *dev = dmaengine_get_dma_device(chan);
+	struct scatterlist *sg;
+	unsigned long flags;
+	u32 control = 0;
+	int width;
+	int i;
+
+	if (sg_len > ifc->sg_max) {
+		dev_err(dev, "sg_len %d overflowed (max sg %d)\n",
+				sg_len, ifc->sg_max);
+		return NULL;
+	}
+
+	if (direction != ifc_chan->direction) {
+		dev_err(dev, "Inconsistent transfer direction\n");
+		return NULL;
+	}
+
+	spin_lock_irqsave(&ifc_chan->lock, flags);
+
+	if (ifc_chan->direction == DMA_DEV_TO_MEM)
+		width = ifc_chan->sconfig.src_addr_width;
+	else
+		width = ifc_chan->sconfig.dst_addr_width;
+
+	switch (width) {
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		control |= FIELD_PREP(IFC_CTL_SIZE, 0);
+		break;
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		control |= FIELD_PREP(IFC_CTL_SIZE, 1);
+		break;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		control |= FIELD_PREP(IFC_CTL_SIZE, 2);
+		break;
+	default:
+		return NULL;
+	}
+
+	for_each_sg(sgl, sg, sg_len, i) {
+		if (!IS_ALIGNED(sg_dma_address(sg), width)) {
+			dev_err(dev, "Unaligned DMA address\n");
+			spin_unlock_irqrestore(&ifc_chan->lock, flags);
+			return NULL;
+		}
+
+		writel(sg_dma_address(sg), ifc_chan->chan_base + IFC_REG_SG_START_ADDR + (8 * i));
+		writel(sg_dma_len(sg), ifc_chan->chan_base + IFC_REG_SG_TC + (8 * i));
+	}
+
+	control |= FIELD_PREP(IFC_CTL_REQ_SRC, ifc_chan->request_id) |
+		IFC_CTL_CH_RD_HW_EXCH |
+		FIELD_PREP(IFC_CTL_SG_NUM, sg_len-1);
+	writel(control, ifc_chan->chan_base);
+
+	spin_unlock_irqrestore(&ifc_chan->lock, flags);
+
+	dma_async_tx_descriptor_init(&ifc_chan->tx, chan);
+	ifc_chan->tx.tx_submit = rda_ifc_tx_submit;
+
+	return &ifc_chan->tx;
+}
+
+static enum dma_status rda_ifc_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
+		struct dma_tx_state *tx_state)
+{
+	struct rda_ifc_chan *ifc_chan = to_ifc_chan(chan);
+	enum dma_status dmaret;
+	unsigned long flags;
+	u32 status;
+	int residue = 0;
+	int tmp_residue = 0;
+	int i;
+
+	dmaret = dma_cookie_status(chan, cookie, tx_state);
+	if (!tx_state || (dmaret == DMA_COMPLETE))
+		return dmaret;
+
+	spin_lock_irqsave(&ifc_chan->lock, flags);
+
+	status = readl(ifc_chan->chan_base + 4);
+
+	if (status & IFC_STATUS_FIFO_EMPTY)
+		dmaret = DMA_COMPLETE;
+	else
+		dmaret = DMA_IN_PROGRESS;
+
+	if (dmaret == DMA_IN_PROGRESS) {
+		/* gather residue from all sg */
+		for (i = 0; i < ifc_chan->rda_ifc->sg_max; i++) {
+			tmp_residue = readl(ifc_chan->chan_base + 12 + (8 * i));
+			residue += tmp_residue;
+		}
+
+		dma_set_residue(tx_state, residue);
+	}
+
+	spin_unlock_irqrestore(&ifc_chan->lock, flags);
+
+	return dmaret;
+}
+
+static int rda_ifc_terminate_all(struct dma_chan *chan)
+{
+	struct rda_ifc_chan *ifc_chan = to_ifc_chan(chan);
+	struct device *dev = dmaengine_get_dma_device(chan);
+	unsigned long flags;
+	u32 status, control;
+	int ret;
+	int i;
+
+	spin_lock_irqsave(&ifc_chan->lock, flags);
+
+	status = readl(ifc_chan->chan_base + 4);
+
+	/* Flush operation only supports read requests */
+	if (ifc_chan->direction == DMA_DEV_TO_MEM) {
+		if (status & IFC_STATUS_FIFO_EMPTY)
+			goto clear_chan;
+
+		control = readl(ifc_chan->chan_base);
+		control |= IFC_CTL_FLUSH;
+		writel(control, ifc_chan->chan_base);
+
+		ret = readl_poll_timeout(ifc_chan->chan_base + 4, status,
+				(status & IFC_STATUS_FIFO_EMPTY), 100, 1000*1000);
+		if (ret < 0)
+			dev_err(dev, "Timed out flushing FIFO\n");
+	}
+
+clear_chan:
+	control = readl(ifc_chan->chan_base);
+	control |= IFC_CTL_DISABLE;
+	writel(control, ifc_chan->chan_base);
+
+	for (i = 0; i < ifc_chan->rda_ifc->sg_max; i++)
+		writel(0, ifc_chan->chan_base + 12 + (8 * i));
+
+	spin_unlock_irqrestore(&ifc_chan->lock, flags);
+	return 0;
+}
+
+static int rda_ifc_chan_init(struct rda_ifc *ifc, struct dma_device *ddev,
+		int id)
+{
+	struct rda_ifc_chan *chan = &ifc->channels[id];
+
+	spin_lock_init(&chan->lock);
+	chan->rda_ifc = ifc;
+	chan->chan.chan_id = id;
+	chan->chan.device = &ifc->ddev;
+	chan->chan_base = ifc->base + 0x10 + (id * (8 + (8 * ifc->sg_max)));
+
+	list_add_tail(&chan->chan.device_node, &ddev->channels);
+	return 0;
+}
+
+static int rda_ifc_ddev_init(struct rda_ifc *ifc, struct dma_device *ddev)
+{
+	int ret;
+	int i;
+
+	dma_cap_zero(ddev->cap_mask);
+	dma_cap_set(DMA_SLAVE, ddev->cap_mask);
+	dma_cap_set(DMA_PRIVATE, ddev->cap_mask);
+
+	/* IFC maximum segment size is 32 MB */
+	dma_set_max_seg_size(ddev->dev, 0x1FFFFFF);
+	dma_set_mask_and_coherent(ddev->dev, DMA_BIT_MASK(32));
+
+	/* IFC supports 8-bit and 32-bit transfers */
+	ddev->copy_align = DMAENGINE_ALIGN_4_BYTES;
+	ddev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+		BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+		BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+	ddev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+		BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+		BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+	ddev->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
+
+	ddev->device_config = rda_ifc_device_config;
+	ddev->device_issue_pending = rda_ifc_issue_pending;
+	ddev->device_prep_slave_sg = rda_ifc_prep_slave_sg;
+	ddev->device_terminate_all = rda_ifc_terminate_all;
+	ddev->device_tx_status = rda_ifc_tx_status;
+	ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+	INIT_LIST_HEAD(&ddev->channels);
+	for (i = 0; i < ifc->max_chan; i++) {
+		ret = rda_ifc_chan_init(ifc, ddev, i);
+		if (ret)
+			return ret;
+	}
+	ddev->chancnt = i;
+
+	return 0;
+}
+
+static struct dma_chan *rda_ifc_xlate(struct of_phandle_args *dma_spec,
+		struct of_dma *of_dma)
+{
+	struct rda_ifc *ifc = of_dma->of_dma_data;
+	struct rda_ifc_chan *ifc_chan;
+	struct dma_chan *chan;
+	unsigned int request;
+
+	if (dma_spec->args_count != 1)
+		return NULL;
+
+	request = dma_spec->args[0];
+	if (request >= IFC_NO_REQUEST)
+		return NULL;
+
+	chan = dma_get_any_slave_channel(&ifc->ddev);
+	if (!chan)
+		return NULL;
+
+	ifc_chan = to_ifc_chan(chan);
+	ifc_chan->request_id = request;
+
+	return chan;
+}
+
+static int rda_ifc_probe(struct platform_device *pdev)
+{
+	const struct rda_ifc_platinfo *platinfo;
+	struct rda_ifc *ifc;
+	struct dma_device *ddev;
+	int ret;
+
+	platinfo = of_device_get_match_data(&pdev->dev);
+	if (!platinfo)
+		return -EINVAL;
+
+	ifc = devm_kzalloc(&pdev->dev,
+			struct_size(ifc, channels, platinfo->std_channb),
+			GFP_KERNEL);
+	if (!ifc)
+		return dev_err_probe(&pdev->dev, -ENOMEM, "Failed to allocate memory\n");
+
+	ifc->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(ifc->base))
+		return dev_err_probe(&pdev->dev, PTR_ERR(ifc->base), "Cannot get base address\n");
+
+	ifc->max_chan = platinfo->std_channb;
+	ifc->sg_max = platinfo->sg_max;
+
+	ddev = &ifc->ddev;
+	ddev->dev = &pdev->dev;
+	ret = rda_ifc_ddev_init(ifc, ddev);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, ifc);
+
+	ret = dma_async_device_register(ddev);
+	if (ret)
+		return ret;
+
+	ret = of_dma_controller_register(pdev->dev.of_node, rda_ifc_xlate, ifc);
+	if (ret < 0)
+		return dev_err_probe(&pdev->dev, ret, "Cannot register DMA controller\n");
+
+	return 0;
+}
+
+static void rda_ifc_remove(struct platform_device *pdev)
+{
+	struct rda_ifc *ifc = platform_get_drvdata(pdev);
+
+	dma_async_device_unregister(&ifc->ddev);
+	of_dma_controller_free(pdev->dev.of_node);
+}
+
+static const struct rda_ifc_platinfo rda8810pl_data = {
+	.sg_max = 1,
+	.std_channb = 7
+};
+
+static const struct of_device_id rda_ifc_of_match[] = {
+	{ .compatible = "rda,8810pl-ifc", .data = &rda8810pl_data },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rda_ifc_of_match);
+
+static struct platform_driver rda_ifc_driver = {
+	.probe = rda_ifc_probe,
+	.remove = rda_ifc_remove,
+	.driver = {
+		.name = "rda-ifc",
+		.of_match_table = rda_ifc_of_match,
+	},
+};
+module_platform_driver(rda_ifc_driver);
+
+MODULE_AUTHOR("Dang Huynh <dang.huynh@mainlining.org>");
+MODULE_DESCRIPTION("RDA IFC driver");
+MODULE_LICENSE("GPL");

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 20/25] dts: unisoc: rda8810pl: Enable IFC
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (18 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 19/25] dmaengine: Add RDA IFC driver Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 21/25] dt-bindings: mmc: Add RDA SDMMC controller Dang Huynh via B4 Relay
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Enable IFC so we can use it with I2C, SPI, SDMMC.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/boot/dts/unisoc/rda8810pl.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
index e90ae7845de7b79e55e9cd339a82313b423e0252..4b3ae19e9da41ee9ffa76dd4fff01824c07ce045 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
+++ b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
@@ -212,6 +212,12 @@ uart3: serial@90000 {
 			interrupts = <11 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 		};
+
+		ifc: dma-controller@f0000 {
+			compatible = "rda,8810pl-ifc";
+			reg = <0xf0000 0x1000>;
+			#dma-cells = <1>;
+		};
 	};
 
 	l2: cache-controller@21100000 {

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 21/25] dt-bindings: mmc: Add RDA SDMMC controller
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (19 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 20/25] dts: unisoc: rda8810pl: Enable IFC Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-17  0:00   ` Krzysztof Kozlowski
  2025-09-16 20:25 ` [PATCH 22/25] mmc: host: Add RDA Micro SD/MMC driver Dang Huynh via B4 Relay
                   ` (4 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Add documentation describing the SD/MMC controller in RDA Micro
RDA8810PL SoC.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 Documentation/devicetree/bindings/mmc/rda,mmc.yaml | 91 ++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/rda,mmc.yaml b/Documentation/devicetree/bindings/mmc/rda,mmc.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..dfdd9c6d3044061c342519e35e39c7751874bb03
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/rda,mmc.yaml
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/rda,mmc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RDA Micro SD/MMC Controller
+
+allOf:
+  - $ref: mmc-controller.yaml
+
+maintainers:
+  - Dang Huynh <dang.huynh@mainlining.org>
+
+properties:
+  compatible:
+    const: rda,8810pl-mmc
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    const: apb
+
+  resets:
+    maxItems: 1
+
+  dmas:
+    minItems: 2
+    maxItems: 2
+
+  dma-names:
+    items:
+      - const: tx
+      - const: rx
+
+  rda,mclk-adj:
+    $ref: /schemas/types.yaml#/definitions/uint8
+    description:
+      Some board need MCLK to be adjusted for the card to work.
+      If not present, MCLK will be handled by an external PCLK.
+    minimum: 0
+    maximum: 255
+
+  rda,mclk-inv:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description:
+      Some board need MCLK to be inverted for the card to work.
+      If not present, MCLK is not inverted.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+  - dmas
+  - dma-names
+  - vmmc-supply
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/clock/rda,8810pl-apclk.h>
+    #include <dt-bindings/dma/rda-ifc.h>
+    mmc1: mmc@20950000 {
+      compatible = "rda,8810pl-mmc";
+      reg = <0x20950000 0x1000>;
+      interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&ap_syscon CLK_APB2>;
+      clock-names = "apb";
+      resets = <&ap_syscon RST_APB2_SDMMC1>;
+      dmas = <&ifc IFC_SDMMC1_TX>, <&ifc IFC_SDMMC1_RX>;
+      dma-names = "tx", "rx";
+      vmmc-supply = <&vdd_sdmmc>;
+      rda,mclk-adj = /bits/ 8 <1>;
+      rda,mclk-inv;
+      status = "disabled";
+    };
+
+...

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 22/25] mmc: host: Add RDA Micro SD/MMC driver
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (20 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 21/25] dt-bindings: mmc: Add RDA SDMMC controller Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-17  0:48   ` Krzysztof Kozlowski
  2025-09-16 20:25 ` [PATCH 23/25] dts: unisoc: rda8810pl: Add SDMMC controllers Dang Huynh via B4 Relay
                   ` (3 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

RDA Micro RDA8810PL includes an SD/MMC controller. This controller
supports SD/SDIO/MMC interface.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 MAINTAINERS                |   6 +
 drivers/mmc/host/Kconfig   |  12 +
 drivers/mmc/host/Makefile  |   1 +
 drivers/mmc/host/rda-mmc.c | 853 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 872 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 91be43782f4ba8aacb629002d357a66704f10b2b..33e04ce35dcc4cbadd715ec9199f2453237b8002 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21417,6 +21417,12 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/rtc/rda,8810pl-rtc.yaml
 F:	drivers/rtc/rtc-rda.c
 
+RDA MICRO SECURE DIGITAL AND MULTIMEDIA CARD DRIVER
+M:	Dang Huynh <dang.huynh@mainlining.org>
+S:	Maintained
+F:	Documentation/devicetree/bindings/mmc/rda,mmc.yaml
+F:	drivers/mmc/host/rda-mmc.c
+
 RDACM20 Camera Sensor
 M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
 M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 4afa0130779d97ca9d1c0ed2102b0babdedcaeeb..352a6eb4e30793b7311c7877c238a7fe31121123 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -1040,6 +1040,18 @@ config MMC_MTK
 	  This is needed if support for any SD/SDIO/MMC devices is required.
 	  If unsure, say N.
 
+config MMC_RDA
+	tristate "RDA Micro SD/MMC Card Interface support"
+	depends on ARCH_RDA
+	depends on COMMON_CLK
+	depends on HAS_DMA
+	help
+	  This selects the RDA Micro Secure digital and Multimedia card interface. The
+	  controller supports SD/SDIO/MMC interface.
+	  If you have a board with RDA SoC and it uses this interface, say Y or M here.
+
+	  If unsure, say N.
+
 config MMC_SDHCI_MICROCHIP_PIC32
 	tristate "Microchip PIC32MZDA SDHCI support"
 	depends on MMC_SDHCI && PIC32MZDA && MMC_SDHCI_PLTFM
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 5057fea8afb696e210e465a6a2aafc68adad7854..d819e18a478e35cb7de6d67b1cf827e1b3d09815 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_MMC_ALCOR)	+= alcor.o
 obj-$(CONFIG_MMC_MTK)		+= mtk-sd.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
 obj-$(CONFIG_MMC_OMAP_HS)	+= omap_hsmmc.o
+obj-$(CONFIG_MMC_RDA)		+= rda-mmc.o
 obj-$(CONFIG_MMC_ATMELMCI)	+= atmel-mci.o
 obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
 obj-$(CONFIG_MMC_MVSDIO)	+= mvsdio.o
diff --git a/drivers/mmc/host/rda-mmc.c b/drivers/mmc/host/rda-mmc.c
new file mode 100644
index 0000000000000000000000000000000000000000..1767d387bc482694fa9935bc59ceba7e2a8a7535
--- /dev/null
+++ b/drivers/mmc/host/rda-mmc.c
@@ -0,0 +1,853 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SD/MMC driver for RDA Micro platform
+ *
+ * Copyright (C) 2013 RDA Microelectronics Inc.
+ * Copyright (c) 2025 Dang Huynh <dang.huynh@mainlining.org>
+ */
+
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/iopoll.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/debugfs.h>
+#include <linux/scatterlist.h>
+#include <linux/spinlock.h>
+
+/* Registers Definitions */
+#define SDMMC_REG_CTRL 0x0
+#define SDMMC_REG_FIFO_TXRX 0x8
+#define SDMMC_REG_CONFIG 0x800
+#define SDMMC_REG_STATUS 0x804
+#define SDMMC_REG_CMD_INDEX 0x808
+#define SDMMC_REG_CMD_ARG 0x80C
+#define SDMMC_REG_RESP_INDEX 0x810
+#define SDMMC_REG_RESP_ARG3 0x814
+#define SDMMC_REG_RESP_ARG2 0x818
+#define SDMMC_REG_RESP_ARG1 0x81C
+#define SDMMC_REG_RESP_ARG0 0x820
+#define SDMMC_REG_DATA_WIDTH 0x824
+#define SDMMC_REG_BLOCK_SIZE 0x828
+#define SDMMC_REG_BLOCK_COUNT 0x82C
+#define SDMMC_REG_INT_STATUS 0x830
+#define SDMMC_REG_INT_MASK 0x834
+#define SDMMC_REG_INT_CLEAR 0x838
+#define SDMMC_REG_TRANS_SPEED 0x83C
+#define SDMMC_REG_MCLK_ADJUST 0x840
+
+/* Bits def */
+/* CTRL */
+#define SDMMC_CTRL_ENDIAN GENMASK(2, 0)
+#define SDMMC_CTRL_SOFTRST_L BIT(3)
+
+/* CONFIG */
+#define SDMMC_CFG_SENDCMD BIT(0)
+#define SDMMC_CFG_SUSPEND BIT(1)
+#define SDMMC_CFG_RSP_EN BIT(4)
+#define SDMMC_CFG_RSP_SEL GENMASK(6, 5)
+#define SDMMC_CFG_RD_WT_EN BIT(8)
+#define SDMMC_CFG_RD_WT_SEL BIT(9)
+#define SDMMC_CFG_S_M_SEL BIT(10)
+#define SDMMC_CFG_AUTO_FLAG_EN BIT(16)
+#define SDMMC_CFG_SAMPLE_EDGE_SEL_FALL_EN BIT(17)
+
+/* STATUS */
+#define SDMMC_STATUS_NOTOVER BIT(0)
+#define SDMMC_STATUS_BUSY BIT(1)
+#define SDMMC_STATUS_DLBUSY BIT(2)
+#define SDMMC_STATUS_SUSPEND BIT(3)
+#define SDMMC_STATUS_RSP_ERR BIT(8)
+#define SDMMC_STATUS_NO_RSP_ERR BIT(9)
+#define SDMMC_STATUS_CRC_STATUS GENMASK(14, 12)
+#define SDMMC_STATUS_DATA_ERROR GENMASK(23, 16)
+#define SDMMC_STATUS_DAT3_VAL BIT(24)
+
+/* INTERRUPTS */
+/* Mask and Clear */
+#define SDMMC_INT_NO_RSP BIT(0)
+#define SDMMC_INT_RSP_ERR BIT(1)
+#define SDMMC_INT_RD_ERR BIT(2)
+#define SDMMC_INT_WR_ERR BIT(3)
+#define SDMMC_INT_DAT_OVER BIT(4)
+#define SDMMC_INT_TXDMA_DONE BIT(5)
+#define SDMMC_INT_RXDMA_DONE BIT(6)
+#define SDMMC_INT_SDIO BIT(7)
+
+#define SDMMC_MCLK_INVERT BIT(4)
+#define SDMMC_MCLK_DISABLE BIT(5)
+
+struct rda_mmc_host {
+	struct device *dev;
+
+	struct mmc_host *mmc;
+	struct mmc_request *mrq;
+
+	unsigned int clock;
+	unsigned int bus_width;
+	unsigned int power_mode;
+	struct regulator *vmmc;
+
+	void __iomem *base;
+	int irq;
+
+	struct clk *clk;
+	struct reset_control *reset;
+
+	dma_cookie_t dma_cookie;
+	struct dma_chan *dma_tx;
+	struct dma_chan *dma_rx;
+
+	bool sdio_irq;
+	bool sdio_irq_trigger;
+
+	spinlock_t lock;
+	struct completion c;
+
+	/* device tree properties */
+	bool mclk_inv;
+	u8 mclk_adj;
+};
+
+static int rda_mmc_hw_init(struct rda_mmc_host *priv)
+{
+	void __iomem *base = priv->base;
+
+	disable_irq(priv->irq);
+
+	writel(FIELD_PREP(SDMMC_CTRL_ENDIAN, 1) | SDMMC_CTRL_SOFTRST_L,
+			base + SDMMC_REG_CTRL);
+	writel(SDMMC_INT_RD_ERR | SDMMC_INT_WR_ERR | SDMMC_INT_DAT_OVER,
+			base + SDMMC_REG_INT_MASK);
+	writel(0xFFFFFFFF, base + SDMMC_REG_INT_CLEAR);
+
+	enable_irq(priv->irq);
+
+	return 0;
+}
+
+static void rda_mmc_reset(struct rda_mmc_host *priv)
+{
+	reset_control_assert(priv->reset);
+	mdelay(1);
+	reset_control_deassert(priv->reset);
+	mdelay(1);
+}
+
+static void rda_mmc_recv_resp(struct mmc_host *host, struct mmc_command *cmd)
+{
+	struct rda_mmc_host *priv = mmc_priv(host);
+	struct device *dev = mmc_dev(host);
+	void __iomem *base = priv->base;
+	u32 status;
+	int ret;
+
+	/* If the controller is busy, wait until it finishes */
+	ret = readl_poll_timeout(base + SDMMC_REG_STATUS, status,
+			!(status & SDMMC_STATUS_NOTOVER), 50, 1000 * 1000);
+	if (ret) {
+		dev_err(dev, "Timed out waiting for the controller\n");
+		cmd->error = ret;
+		return;
+	}
+
+	if (status & SDMMC_STATUS_NO_RSP_ERR)
+		return;
+
+	if (status & SDMMC_STATUS_RSP_ERR) {
+		cmd->error = -EILSEQ;
+		return;
+	}
+
+	if (mmc_resp_type(cmd) & MMC_RSP_R2) {
+		cmd->resp[0] = readl_relaxed(base + SDMMC_REG_RESP_ARG3);
+		cmd->resp[1] = readl_relaxed(base + SDMMC_REG_RESP_ARG2);
+		cmd->resp[2] = readl_relaxed(base + SDMMC_REG_RESP_ARG1);
+		cmd->resp[3] = readl_relaxed(base + SDMMC_REG_RESP_ARG0) << 1;
+	} else {
+		cmd->resp[0] = readl_relaxed(base + SDMMC_REG_RESP_ARG3);
+	}
+
+	dev_dbg(dev, "response: resp[0] = 0x%x, resp[1] = 0x%x, resp[2] = 0x%x, resp[3] = 0x%x\n",
+			cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
+}
+
+static inline struct dma_chan *rda_mmc_get_dma_chan(struct rda_mmc_host *priv,
+		struct mmc_data *data)
+{
+	if (data->flags & MMC_DATA_WRITE)
+		return priv->dma_tx;
+	else
+		return priv->dma_rx;
+}
+
+static int rda_mmc_send_data(struct mmc_host *host, struct mmc_data *data)
+{
+	struct rda_mmc_host *priv = mmc_priv(host);
+	struct device *dev = mmc_dev(host);
+	struct dma_slave_config slave_config;
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *chan;
+	int ret;
+
+	if (!data) {
+		dev_err(dev, "No MMC request or data\n");
+		goto fail;
+	}
+
+	if (data->flags & MMC_DATA_WRITE) {
+		slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		slave_config.direction = DMA_MEM_TO_DEV;
+	} else {
+		slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		slave_config.direction = DMA_DEV_TO_MEM;
+	}
+
+	data->sg_count = dma_map_sg(dev, data->sg, data->sg_len, mmc_get_dma_dir(data));
+	if (data->sg_count == 0) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	chan = rda_mmc_get_dma_chan(priv, data);
+
+	ret = dmaengine_slave_config(chan, &slave_config);
+	if (ret) {
+		dev_err(dev, "Failed to configure DMAC\n");
+		goto fail_dma;
+	}
+
+	desc = dmaengine_prep_slave_sg(chan, data->sg, data->sg_count,
+			slave_config.direction, DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(dev, "Failed to allocate DMA descriptor\n");
+		goto fail_dma;
+	}
+
+	priv->dma_cookie = dmaengine_submit(desc);
+	if (!priv->dma_cookie) {
+		dev_err(dev, "Failed to submit DMA request\n");
+		goto fail_dma;
+	}
+
+	dma_async_issue_pending(chan);
+
+	return 0;
+
+fail_dma:
+	dma_unmap_sg(dev, data->sg, data->sg_len, mmc_get_dma_dir(data));
+fail:
+	return -EINVAL;
+}
+
+static int rda_mmc_prepare_data(struct mmc_host *host, struct mmc_command *cmd,
+		struct mmc_data *data, u32 *cfg)
+{
+	struct rda_mmc_host *priv = mmc_priv(host);
+	struct device *dev = mmc_dev(host);
+	void __iomem *base = priv->base;
+	int hw_blksz = 2; /* 1 word */
+	int i = 0;
+
+	/* If we're still here, we'll assume there's data ops */
+	*cfg |= SDMMC_CFG_RD_WT_EN;
+
+	/* Tell the controller we have a write operation */
+	if (data->flags & MMC_DATA_WRITE)
+		*cfg |= SDMMC_CFG_RD_WT_SEL;
+
+	/* Multiple data read/write */
+	if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
+		*cfg |= SDMMC_CFG_S_M_SEL;
+
+		/*
+		 * Tell the controller to automatically issue CMD12 when the last block
+		 * transfer is completed on non-SDIO cards.
+		 */
+		if (!mmc_card_sdio(host->card))
+			*cfg |= SDMMC_CFG_AUTO_FLAG_EN;
+	}
+
+	/* Blocksize on this IP is calculated by how many words are requested */
+	if (data->blksz > 4) {
+		for (i = 4; i < data->blksz; i <<= 1)
+			hw_blksz++;
+	}
+
+	if (unlikely(hw_blksz > 11)) {
+		dev_err(dev, "Requested %d but hardware can only support 11!\n", hw_blksz);
+		return -EINVAL;
+	}
+
+	writel_relaxed(data->blocks, base + SDMMC_REG_BLOCK_COUNT);
+	writel_relaxed(hw_blksz, base + SDMMC_REG_BLOCK_SIZE);
+
+	return 0;
+}
+
+static int rda_mmc_send_cmd(struct mmc_host *host, struct mmc_command *cmd,
+		struct mmc_data *data)
+{
+	struct rda_mmc_host *priv = mmc_priv(host);
+	void __iomem *base = priv->base;
+	u32 cfg = SDMMC_CFG_SENDCMD;
+	int ret;
+
+	switch (mmc_resp_type(cmd)) {
+	case MMC_RSP_R2:
+		cfg |= SDMMC_CFG_RSP_EN | FIELD_PREP(SDMMC_CFG_RSP_SEL, 2);
+		break;
+	case MMC_RSP_R3:
+		cfg |= SDMMC_CFG_RSP_EN | FIELD_PREP(SDMMC_CFG_RSP_SEL, 1);
+		break;
+	default:
+		cfg |= SDMMC_CFG_RSP_EN;
+		break;
+	}
+
+	/* No data */
+	if (!data)
+		goto send_to_soc;
+
+	/* Data operations */
+	ret = rda_mmc_prepare_data(host, cmd, data, &cfg);
+	if (ret < 0)
+		return -EINVAL;
+
+	ret = rda_mmc_send_data(host, data);
+	if (ret < 0)
+		return -EINVAL;
+
+send_to_soc:
+	writel(cmd->opcode, base + SDMMC_REG_CMD_INDEX);
+	writel(cmd->arg, base + SDMMC_REG_CMD_ARG);
+	writel(cfg, base + SDMMC_REG_CONFIG);
+
+	dev_dbg(priv->dev, "mmc_resp_type = %d, cmd->opcode = 0x%x, cmd->arg = 0x%x - cfg: 0x%x\n",
+			mmc_resp_type(cmd), cmd->opcode, cmd->arg, cfg);
+
+	rda_mmc_recv_resp(host, cmd);
+
+	return 0;
+}
+
+/*
+ * Once data transfer failed (or aborted), the controller needs to be
+ * cleaned up.
+ */
+static void rda_mmc_data_abort(struct mmc_host *host, struct mmc_request *mrq)
+{
+	struct rda_mmc_host *priv = mmc_priv(host);
+	struct device *dev = mmc_dev(host);
+	void __iomem *base = priv->base;
+	struct mmc_command stop;
+	int ret;
+
+	writel_relaxed(0, base + SDMMC_REG_BLOCK_COUNT);
+	writel_relaxed(0, base + SDMMC_REG_BLOCK_SIZE);
+
+	if (!host->card)
+		return;
+
+	/*
+	 * Issue a stop command first, because if the controller timed out,
+	 * it'll not return an IRQ or any indicator.
+	 */
+	if (!mmc_card_sdio(host->card)) {
+		if (!mrq->stop) {
+			stop.opcode = MMC_STOP_TRANSMISSION;
+			stop.arg = 0;
+			stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+			ret = rda_mmc_send_cmd(host, &stop, NULL);
+		} else {
+			ret = rda_mmc_send_cmd(host, mrq->stop, NULL);
+		}
+
+		if (ret < 0)
+			dev_err(dev, "Failed to send stop command\n");
+	}
+}
+
+static void rda_mmc_request(struct mmc_host *host, struct mmc_request *req)
+{
+	struct rda_mmc_host *priv = mmc_priv(host);
+	struct device *dev = mmc_dev(host);
+	struct mmc_data *data = NULL;
+	struct dma_chan *chan;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	WARN_ON(priv->mrq);
+	priv->mrq = req;
+
+	if (req->data) {
+		dev_dbg(dev, "Block size = %d - Blocks = %d - Offset: %d - Length: %d\n",
+				req->data->blksz, req->data->blocks,
+				req->data->sg->offset, req->data->sg->length);
+		data = req->data;
+	}
+
+	if (rda_mmc_send_cmd(host, req->cmd, data) < 0) {
+		req->cmd->error = -EINVAL;
+		if (data)
+			req->data->error = -EINVAL;
+
+		goto done_irqunlock;
+	}
+
+	/* Interrupt will pick up on this */
+	if (!data)
+		goto done_irqunlock;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/*
+	 * On a data operation, we rely on our interrupt to tell us
+	 * when the transmission is finished (or failed).
+	 *
+	 * However with this IP, if the operation timed out, it will
+	 * not trigger an IRQ and we'll not return.
+	 */
+	if (data) {
+		if (wait_for_completion_timeout(&priv->c,
+					msecs_to_jiffies(5000)) == 0) {
+			spin_lock_irqsave(&priv->lock, flags);
+			priv->mrq = NULL;
+
+			dma_unmap_sg(dev, data->sg, data->sg_len,
+					mmc_get_dma_dir(data));
+
+			chan = rda_mmc_get_dma_chan(priv, data);
+
+			dmaengine_terminate_sync(chan);
+			rda_mmc_data_abort(host, req);
+
+			req->cmd->error = -ETIMEDOUT;
+			req->data->error = -ETIMEDOUT;
+			goto done_irqunlock;
+		}
+	}
+
+	return;
+
+done_irqunlock:
+	priv->mrq = NULL;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	mmc_request_done(host, req);
+}
+
+static void rda_mmc_set_ios(struct mmc_host *host, struct mmc_ios *ios)
+{
+	struct rda_mmc_host *priv = mmc_priv(host);
+	struct device *dev = mmc_dev(host);
+	void __iomem *base = priv->base;
+	unsigned long mclk_rate;
+	unsigned int clk_div;
+	unsigned long flags;
+	u32 reg_mclk = 0;
+	int ret;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if (priv->bus_width != ios->bus_width) {
+		priv->bus_width = ios->bus_width;
+		writel(BIT(priv->bus_width), base + SDMMC_REG_DATA_WIDTH);
+	}
+
+	if (priv->power_mode != ios->power_mode) {
+		priv->power_mode = ios->power_mode;
+		if (priv->power_mode == MMC_POWER_UP) {
+			ret = regulator_enable(priv->vmmc);
+			if (ret)
+				dev_err(dev, "Failed to turn on vmmc\n");
+		} else if (priv->power_mode == MMC_POWER_OFF) {
+			ret = regulator_disable(priv->vmmc);
+			if (ret)
+				dev_err(dev, "Failed to turn off vmmc\n");
+		}
+	}
+
+	if (priv->clock != ios->clock) {
+		priv->clock = ios->clock;
+		if (ios->clock) {
+			/* trans speed  */
+			mclk_rate = clk_get_rate(priv->clk);
+			if (mclk_rate == 0) {
+				dev_err(dev, "Invalid APB clock rate\n");
+				goto bailout;
+			}
+
+			clk_div = mclk_rate / (2 * ios->clock);
+			if (mclk_rate % (2 * ios->clock))
+				clk_div++;
+
+			if (clk_div >= 1)
+				clk_div -= 1;
+
+			if (clk_div > 255)
+				clk_div = 255;
+
+			/* mclk adjust */
+			if (priv->mclk_inv)
+				reg_mclk = SDMMC_MCLK_INVERT;
+
+			reg_mclk |= priv->mclk_adj;
+
+			writel_relaxed(clk_div, base + SDMMC_REG_TRANS_SPEED);
+			writel_relaxed(reg_mclk, base + SDMMC_REG_MCLK_ADJUST);
+
+			dev_dbg(dev, "set clk = %d - mclk = %ld - divider = %d\n",
+					ios->clock, mclk_rate, clk_div);
+		} else {
+			writel_relaxed(SDMMC_MCLK_DISABLE, base + SDMMC_REG_MCLK_ADJUST);
+		}
+	}
+
+bailout:
+	dev_dbg(dev, "buswidth=%d, clock=%d, power=%d\n",
+			ios->bus_width, ios->clock, ios->power_mode);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void rda_mmc_crc_status(struct mmc_host *host)
+{
+	struct rda_mmc_host *priv = mmc_priv(host);
+	struct device *dev = mmc_dev(host);
+	void __iomem *base = priv->base;
+	const char *crc_error;
+	u32 status;
+
+	status = readl_relaxed(base + SDMMC_REG_STATUS);
+
+	switch (FIELD_GET(SDMMC_STATUS_CRC_STATUS, status)) {
+	case 0b101:
+		crc_error = "Transmission Error";
+		break;
+	case 0b010:
+		crc_error = "Transmission Right";
+		break;
+	case 0b111:
+		crc_error = "Flash Programming Error";
+		break;
+	default:
+		crc_error = "Unknown";
+		break;
+	}
+
+	dev_err(dev, "CRC Error: %s - DATA_ERROR: 0x%lx\n", crc_error,
+			FIELD_GET(SDMMC_STATUS_DATA_ERROR, status));
+}
+
+static irqreturn_t rda_mmc_irq(int irq, void *dev_id)
+{
+	struct rda_mmc_host *priv = dev_id;
+	struct mmc_host *host = mmc_from_priv(priv);
+	struct device *dev = mmc_dev(host);
+	void __iomem *base = priv->base;
+	struct mmc_request *mrq;
+	u32 status;
+	irqreturn_t irqret = IRQ_NONE;
+
+	status = readl(base + SDMMC_REG_INT_STATUS);
+	writel((status & 0xFF), base + SDMMC_REG_INT_CLEAR);
+
+	dev_dbg(dev, "IRQ requested - status: 0x%x\n", status);
+
+	if (!priv->mrq || !priv->mrq->data)
+		goto irq_done;
+
+	mrq = priv->mrq;
+
+	if (mrq->data && ((status & SDMMC_INT_RD_ERR) || (status & SDMMC_INT_WR_ERR)))
+		mrq->data->error = -EILSEQ;
+
+	if (priv->sdio_irq && (status & SDMMC_INT_SDIO))
+		priv->sdio_irq_trigger = true;
+
+	irqret = IRQ_WAKE_THREAD;
+
+	/* We got an error, no need to do the additional checks */
+	if (mrq->data->error)
+		goto irq_done;
+
+	/*
+	 * If we don't have any error but DAT_OVER isn't triggered, then we'll assume
+	 * that we got an unexpected IRQ (during a data transfer)
+	 */
+	if (!mrq->data->error && !(status & SDMMC_INT_DAT_OVER))
+		irqret = IRQ_HANDLED;
+
+irq_done:
+	if (irqret == IRQ_NONE)
+		dev_info(dev,
+			"Unexpected IRQ - was a data transfer requested? IRQ: 0x%x\n", status);
+
+	return irqret;
+}
+
+static irqreturn_t rda_mmc_irq_fn(int irq, void *dev_id)
+{
+	struct rda_mmc_host *priv = dev_id;
+	struct mmc_host *host = mmc_from_priv(priv);
+	struct device *dev = mmc_dev(host);
+	struct mmc_request *mrq;
+	struct dma_chan *chan;
+	struct dma_tx_state state;
+	enum dma_status dma_status;
+	unsigned long flags;
+
+	if (WARN_ON(!priv->mrq))
+		return IRQ_NONE;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	mrq = priv->mrq;
+
+	if (mrq->data) {
+		chan = rda_mmc_get_dma_chan(priv, mrq->data);
+
+		if (mrq->data->error) {
+			mrq->data->bytes_xfered = 0;
+			rda_mmc_crc_status(host);
+			dmaengine_terminate_sync(chan);
+			rda_mmc_data_abort(host, mrq);
+		} else {
+			mrq->data->bytes_xfered =
+				mrq->data->blocks * mrq->data->blksz;
+
+			/*
+			 * With this IP, just because a TXDMA/RXDMA interrupt is triggered,
+			 * doesn't mean the MMC is fully processed.
+			 */
+			dma_status = dmaengine_tx_status(chan, priv->dma_cookie, &state);
+			dev_dbg(mmc_dev(host), "DMA Status: %d\n", dma_status);
+			if (dma_status != DMA_COMPLETE) {
+				dev_err(dev, "Transmit IRQ triggered but DMA is not finished\n");
+				mrq->data->error = -ETIMEDOUT;
+				mrq->data->bytes_xfered = 0;
+				dmaengine_terminate_sync(chan);
+				rda_mmc_data_abort(host, mrq);
+			}
+		}
+
+		/*
+		 * Since we told the controller to automatically send a stop command,
+		 * we don't have to send a stop command here.
+		 */
+		dma_unmap_sg(dev, mrq->data->sg, mrq->data->sg_len,
+				mmc_get_dma_dir(mrq->data));
+		dmaengine_terminate_sync(chan);
+	}
+
+	priv->mrq = NULL;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	complete(&priv->c);
+	mmc_request_done(host, mrq);
+
+	if (priv->sdio_irq && priv->sdio_irq_trigger)
+		mmc_signal_sdio_irq(host);
+
+	return IRQ_HANDLED;
+}
+
+static int rda_mmc_card_busy(struct mmc_host *host)
+{
+	struct rda_mmc_host *priv = mmc_priv(host);
+	void __iomem *base = priv->base;
+	u32 status = readl(base + SDMMC_REG_STATUS);
+
+	return (status & SDMMC_STATUS_DLBUSY);
+}
+
+static void rda_mmc_sdio_enable_irq(struct mmc_host *host, int enable)
+{
+	struct rda_mmc_host *priv = mmc_priv(host);
+	void __iomem *base = priv->base;
+	u32 intmask = readl(base + SDMMC_REG_INT_MASK);
+
+	if (enable) {
+		intmask |= SDMMC_INT_SDIO;
+		priv->sdio_irq = true;
+	} else {
+		intmask &= ~SDMMC_INT_SDIO;
+		priv->sdio_irq = false;
+	}
+
+	priv->sdio_irq_trigger = false;
+
+	writel(intmask, base + SDMMC_REG_INT_MASK);
+}
+
+static const struct mmc_host_ops rda_mmc_ops = {
+	.request = rda_mmc_request,
+	.set_ios = rda_mmc_set_ios,
+	.get_cd = mmc_gpio_get_cd,
+	.get_ro = mmc_gpio_get_ro,
+	.card_busy = rda_mmc_card_busy,
+	.enable_sdio_irq = rda_mmc_sdio_enable_irq,
+};
+
+static void rda_mmc_of_parse(struct device_node *np, struct rda_mmc_host *priv)
+{
+	bool mclk_inv = false;
+	u8 mclk_adj = 1;
+
+	if (of_property_present(np, "rda,mclk-inv"))
+		mclk_inv = true;
+
+	of_property_read_u8(np, "rda,mclk-adj", &mclk_adj);
+
+	priv->mclk_inv = mclk_inv;
+	priv->mclk_adj = mclk_adj;
+}
+
+static int rda_mmc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rda_mmc_host *priv;
+	struct mmc_host *mmc;
+	struct clk *clk;
+	struct reset_control *reset;
+	struct dma_chan *tx, *rx;
+	struct regulator *vmmc;
+	void __iomem *base;
+	int irq;
+	int ret;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return dev_err_probe(dev, PTR_ERR(base), "Cannot get iomap\n");
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return dev_err_probe(dev, irq, "Cannot get IRQ: %d\n", irq);
+
+	clk = devm_clk_get_enabled(dev, "apb");
+	if (IS_ERR(clk))
+		return dev_err_probe(dev, PTR_ERR(clk), "Cannot get clock device\n");
+
+	vmmc = devm_regulator_get(dev, "vmmc");
+	if (IS_ERR(vmmc))
+		return dev_err_probe(dev, PTR_ERR(vmmc), "Failed to obtain regulator\n");
+
+	reset = devm_reset_control_get_by_index(dev, 0);
+	if (IS_ERR(reset))
+		return dev_err_probe(dev, PTR_ERR(reset), "Failed to obtain reset\n");
+
+	tx = dma_request_chan(dev, "tx");
+	if (IS_ERR(tx))
+		return dev_err_probe(dev, PTR_ERR(tx), "Failed to request tx channel\n");
+
+	rx = dma_request_chan(dev, "rx");
+	if (IS_ERR(rx))
+		return dev_err_probe(dev, PTR_ERR(rx), "Failed to request rx channel\n");
+
+	mmc = devm_mmc_alloc_host(dev, sizeof(*priv));
+	if (IS_ERR(mmc)) {
+		dev_err(dev, "Cannot allocate memory for MMC\n");
+		ret = PTR_ERR(mmc);
+		goto fail_release_dma;
+	}
+
+	priv = mmc_priv(mmc);
+	priv->dev = dev;
+	priv->base = base;
+	priv->irq = irq;
+	priv->clk = clk;
+	priv->reset = reset;
+	priv->dma_tx = tx;
+	priv->dma_rx = rx;
+	priv->vmmc = vmmc;
+	spin_lock_init(&priv->lock);
+	init_completion(&priv->c);
+
+	mmc->ops = &rda_mmc_ops;
+
+	mmc->max_segs = 1;
+	mmc->max_blk_size = 4096;
+	mmc->max_blk_count = 0xFFFF;
+	mmc->max_req_size = 0xFFFF;
+	mmc->max_seg_size = 0xFFFF;
+
+	mmc->f_min = 1000000;
+	mmc->caps = MMC_CAP_4_BIT_DATA;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	rda_mmc_of_parse(dev->of_node, priv);
+
+	ret = mmc_of_parse(mmc);
+	if (ret) {
+		dev_err(dev, "Failed to parse device tree: %d\n", ret);
+		goto fail_release_dma;
+	}
+
+	rda_mmc_reset(priv);
+	rda_mmc_hw_init(priv);
+
+	priv->bus_width = -1;
+
+	ret = devm_request_threaded_irq(dev, irq, rda_mmc_irq, rda_mmc_irq_fn,
+			IRQF_ONESHOT, mmc_hostname(mmc), priv);
+	if (ret) {
+		dev_err(dev, "Failed to request IRQ: %d\n", ret);
+		goto fail_release_dma;
+	}
+
+	ret = mmc_add_host(mmc);
+	if (ret) {
+		dev_err(dev, "Failed to add MMC host: %d\n", ret);
+		goto fail_release_dma;
+	}
+
+	platform_set_drvdata(pdev, mmc);
+	return 0;
+
+fail_release_dma:
+	dma_release_channel(rx);
+	dma_release_channel(tx);
+	return ret;
+}
+
+static void rda_mmc_remove(struct platform_device *pdev)
+{
+	struct rda_mmc_host *host = platform_get_drvdata(pdev);
+
+	mmc_remove_host(host->mmc);
+	dma_release_channel(host->dma_rx);
+	dma_release_channel(host->dma_tx);
+}
+
+static const struct of_device_id rda_mmc_dt_ids[] = {
+	{ .compatible = "rda,8810pl-mmc", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rda_mmc_dt_ids);
+
+static struct platform_driver rda_mmc_driver = {
+	.probe		= rda_mmc_probe,
+	.remove		= rda_mmc_remove,
+	.driver		= {
+		.name	= "rda-mmc",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+		.of_match_table = rda_mmc_dt_ids,
+	},
+};
+module_platform_driver(rda_mmc_driver);
+
+MODULE_AUTHOR("Dang Huynh <dang.huynh@mainlining.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MMC/SD driver for RDA platform");
+MODULE_ALIAS("platform:rda-mmc");

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 23/25] dts: unisoc: rda8810pl: Add SDMMC controllers
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (21 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 22/25] mmc: host: Add RDA Micro SD/MMC driver Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 24/25] dts: unisoc: orangepi-2g: Enable SD Card Dang Huynh via B4 Relay
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Add SDMMC1 and 2 controllers for the RDA8810PL platform.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/boot/dts/unisoc/rda8810pl.dtsi | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
index 4b3ae19e9da41ee9ffa76dd4fff01824c07ce045..e68f8330ce2c4750b6944612bee03b42694137e4 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
+++ b/arch/arm/boot/dts/unisoc/rda8810pl.dtsi
@@ -9,6 +9,7 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/interrupt-controller/irq.h>
 #include <dt-bindings/clock/rda,8810pl-apclk.h>
+#include <dt-bindings/dma/rda-ifc.h>
 
 / {
 	compatible = "rda,8810pl";
@@ -199,6 +200,30 @@ uart1: serial@0 {
 			status = "disabled";
 		};
 
+		mmc1: mmc@50000 {
+			compatible = "rda,8810pl-mmc";
+			reg = <0x50000 0x1000>;
+			interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ap_syscon CLK_APB2>;
+			clock-names = "apb";
+			resets = <&ap_syscon RST_APB2_SDMMC1>;
+			dmas = <&ifc IFC_SDMMC1_TX>, <&ifc IFC_SDMMC1_RX>;
+			dma-names = "tx", "rx";
+			status = "disabled";
+		};
+
+		mmc2: mmc@60000 {
+			compatible = "rda,8810pl-mmc";
+			reg = <0x60000 0x1000>;
+			interrupts = <4 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ap_syscon CLK_APB2>;
+			clock-names = "apb";
+			resets = <&ap_syscon RST_APB2_SDMMC2>;
+			dmas = <&ifc IFC_SDMMC2_TX>, <&ifc IFC_SDMMC2_RX>;
+			dma-names = "tx", "rx";
+			status = "disabled";
+		};
+
 		uart2: serial@10000 {
 			compatible = "rda,8810pl-uart";
 			reg = <0x10000 0x1000>;

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 24/25] dts: unisoc: orangepi-2g: Enable SD Card
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (22 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 23/25] dts: unisoc: rda8810pl: Add SDMMC controllers Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-16 20:25 ` [PATCH 25/25] dts: unisoc: orangepi-i96: " Dang Huynh via B4 Relay
  2025-09-17 10:03 ` [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Manivannan Sadhasivam
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Since we have a SDMMC controller, we can use the SD card slot on the
Orange Pi 2G-IOT.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 .../boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts    | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts b/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts
index 46ccb9ad510c0df142b845d6fc5633b69c2298dd..f6b1a1485645f5714cdf14447cbee50e28e3c076 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts
+++ b/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts
@@ -6,6 +6,7 @@
 
 /dts-v1/;
 
+#include <dt-bindings/gpio/gpio.h>
 #include "rda8810pl.dtsi"
 
 / {
@@ -27,6 +28,13 @@ memory@80000000 {
 		reg = <0x80000000 0x10000000>;
 	};
 
+	vdd_sdmmc: regulator-fixed {
+		compatible = "regulator-fixed";
+		regulator-name = "vdd_sdmmc";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+	};
+
 	uart_clk: uart-clk {
 		compatible = "fixed-clock";
 		clock-frequency = <921600>;
@@ -34,6 +42,18 @@ uart_clk: uart-clk {
 	};
 };
 
+&mmc1 {
+	status = "okay";
+	no-sdio;
+	no-mmc;
+	bus-width = <4>;
+	max-frequency = <30000000>;
+	cd-gpios = <&gpiob 4 GPIO_ACTIVE_LOW>;
+	vmmc-supply = <&vdd_sdmmc>;
+	rda,mclk-adj = /bits/ 8 <1>;
+	rda,mclk-inv;
+};
+
 &uart3 {
 	status = "okay";
 	clocks = <&uart_clk>;

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH 25/25] dts: unisoc: orangepi-i96: Enable SD Card
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (23 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 24/25] dts: unisoc: orangepi-2g: Enable SD Card Dang Huynh via B4 Relay
@ 2025-09-16 20:25 ` Dang Huynh via B4 Relay
  2025-09-17 10:03 ` [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Manivannan Sadhasivam
  25 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh via B4 Relay @ 2025-09-16 20:25 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

From: Dang Huynh <dang.huynh@mainlining.org>

Since we have a SDMMC controller, we can use the SD card slot on the
Orange Pi i96.

Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
---
 arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts b/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts
index a1d61ef138d12bb3ecb4b24513cc1a7dfbac3107..fbb7b5be62051627e80d940cb5e5ccff9047c13c 100644
--- a/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts
+++ b/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts
@@ -6,6 +6,7 @@
 
 /dts-v1/;
 
+#include <dt-bindings/gpio/gpio.h>
 #include "rda8810pl.dtsi"
 
 / {
@@ -27,6 +28,13 @@ memory@80000000 {
 		reg = <0x80000000 0x10000000>;
 	};
 
+	vdd_sdmmc: regulator-fixed {
+		compatible = "regulator-fixed";
+		regulator-name = "vdd_sdmmc";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+	};
+
 	uart_clk: uart-clk {
 		compatible = "fixed-clock";
 		clock-frequency = <921600>;
@@ -34,6 +42,18 @@ uart_clk: uart-clk {
 	};
 };
 
+&mmc1 {
+	status = "okay";
+	no-sdio;
+	no-mmc;
+	bus-width = <4>;
+	max-frequency = <30000000>;
+	cd-gpios = <&gpiob 4 GPIO_ACTIVE_LOW>;
+	vmmc-supply = <&vdd_sdmmc>;
+	rda,mclk-adj = /bits/ 8 <1>;
+	rda,mclk-inv;
+};
+
 &uart3 {
 	status = "okay";
 	clocks = <&uart_clk>;

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH 21/25] dt-bindings: mmc: Add RDA SDMMC controller
  2025-09-16 20:25 ` [PATCH 21/25] dt-bindings: mmc: Add RDA SDMMC controller Dang Huynh via B4 Relay
@ 2025-09-17  0:00   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 42+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-17  0:00 UTC (permalink / raw)
  To: dang.huynh, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc

On 17/09/2025 22:25, Dang Huynh via B4 Relay wrote:
> From: Dang Huynh <dang.huynh@mainlining.org>
> 
> Add documentation describing the SD/MMC controller in RDA Micro
> RDA8810PL SoC.
> 
> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> ---
>  Documentation/devicetree/bindings/mmc/rda,mmc.yaml | 91 ++++++++++++++++++++++
>  1 file changed, 91 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/mmc/rda,mmc.yaml b/Documentation/devicetree/bindings/mmc/rda,mmc.yaml
> new file mode 100644
> index 0000000000000000000000000000000000000000..dfdd9c6d3044061c342519e35e39c7751874bb03
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/rda,mmc.yaml

filename based on compatible.

> @@ -0,0 +1,91 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mmc/rda,mmc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RDA Micro SD/MMC Controller
> +
> +allOf:
> +  - $ref: mmc-controller.yaml
> +
> +maintainers:
> +  - Dang Huynh <dang.huynh@mainlining.org>
> +
> +properties:
> +  compatible:
> +    const: rda,8810pl-mmc
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1
> +
> +  clock-names:
> +    const: apb
> +
> +  resets:
> +    maxItems: 1
> +
> +  dmas:
> +    minItems: 2

Drop

> +    maxItems: 2
> +
> +  dma-names:
> +    items:
> +      - const: tx
> +      - const: rx
> +
> +  rda,mclk-adj:
> +    $ref: /schemas/types.yaml#/definitions/uint8
> +    description:
> +      Some board need MCLK to be adjusted for the card to work.
> +      If not present, MCLK will be handled by an external PCLK.

Adjusted for what? What is the value exactly? Where is the MCLK located?

> +    minimum: 0
> +    maximum: 255
> +
> +  rda,mclk-inv:
> +    $ref: /schemas/types.yaml#/definitions/flag
> +    description:
> +      Some board need MCLK to be inverted for the card to work.
> +      If not present, MCLK is not inverted.
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - clock-names
> +  - resets
> +  - dmas
> +  - dma-names
> +  - vmmc-supply
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    #include <dt-bindings/clock/rda,8810pl-apclk.h>
> +    #include <dt-bindings/dma/rda-ifc.h>
> +    mmc1: mmc@20950000 {

Drop unused label.

> +      compatible = "rda,8810pl-mmc";
> +      reg = <0x20950000 0x1000>;
> +      interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
> +      clocks = <&ap_syscon CLK_APB2>;
> +      clock-names = "apb";
> +      resets = <&ap_syscon RST_APB2_SDMMC1>;
> +      dmas = <&ifc IFC_SDMMC1_TX>, <&ifc IFC_SDMMC1_RX>;
> +      dma-names = "tx", "rx";
> +      vmmc-supply = <&vdd_sdmmc>;
> +      rda,mclk-adj = /bits/ 8 <1>;
> +      rda,mclk-inv;
> +      status = "disabled";
Cannot be disabled.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 01/25] ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes
  2025-09-16 20:24 ` [PATCH 01/25] ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes Dang Huynh via B4 Relay
@ 2025-09-17  0:39   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 42+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-17  0:39 UTC (permalink / raw)
  To: dang.huynh, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc

On 17/09/2025 22:24, Dang Huynh via B4 Relay wrote:
> From: Dang Huynh <dang.huynh@mainlining.org>
> 
> So we can add devices to these GPIO nodes for each board.

No, it's pointless. Squash it with the user.

Also, way you organized patchset is just making things complicated. DTS
cannot be split all over it, like put randomly at the beginning, middle
and at the end.

Read DT submitting patches and maintainer soc rules.

> 


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 07/25] ARM: dts: unisoc: rda8810pl: Enable Real-Time Clock
  2025-09-16 20:25 ` [PATCH 07/25] ARM: dts: unisoc: rda8810pl: Enable Real-Time Clock Dang Huynh via B4 Relay
@ 2025-09-17  0:40   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 42+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-17  0:40 UTC (permalink / raw)
  To: dang.huynh, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc

On 17/09/2025 22:25, Dang Huynh via B4 Relay wrote:
> From: Dang Huynh <dang.huynh@mainlining.org>
> 
> The RDA8810PL has built-in RTC.
> 
> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> ---

DTS  cannot be in the middle of patchset. Please stop creating this fake
dependencies.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 11/25] dts: unisoc: rda8810pl: Enable clock/reset driver
  2025-09-16 20:25 ` [PATCH 11/25] dts: unisoc: rda8810pl: Enable clock/reset driver Dang Huynh via B4 Relay
@ 2025-09-17  0:41   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 42+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-17  0:41 UTC (permalink / raw)
  To: dang.huynh, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc

On 17/09/2025 22:25, Dang Huynh via B4 Relay wrote:
> From: Dang Huynh <dang.huynh@mainlining.org>
> 
> Enable RDA8810PL Clock and Reset driver so we can use it for various
> subsystems.
> 
> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>

Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching. For bindings, the preferred subjects are
explained here:
https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters

You use random prefixes... This is "ARM: dts:" and the drivers ARE NOT
"drivers:".


> ---
>  arch/arm/boot/dts/unisoc/rda8810pl.dtsi | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 09/25] dt-bindings: clock: Add RDA Micro RDA8810PL clock/reset controller
  2025-09-16 20:25 ` [PATCH 09/25] dt-bindings: clock: Add RDA Micro RDA8810PL clock/reset controller Dang Huynh via B4 Relay
@ 2025-09-17  0:43   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 42+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-17  0:43 UTC (permalink / raw)
  To: dang.huynh, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc

On 17/09/2025 22:25, Dang Huynh via B4 Relay wrote:
> +
> +required:
> +  - compatible
> +  - reg
> +  - "#clock-cells"

Use consistent quotes, either ' or "

> +  - "#reset-cells"
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/clock/rda,8810pl-apclk.h>
> +
> +    ap_syscon: syscon@0 {
> +      compatible = "rda,8810pl-apsyscon", "syscon";
> +      reg = <0x0 0x1000>;
> +      #clock-cells = <1>;
> +      #reset-cells = <1>;
> +    };
> diff --git a/include/dt-bindings/clock/rda,8810pl-apclk.h b/include/dt-bindings/clock/rda,8810pl-apclk.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..372358e72436a28c0775519f49626c9c5f4c6046
> --- /dev/null
> +++ b/include/dt-bindings/clock/rda,8810pl-apclk.h
> @@ -0,0 +1,79 @@
> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
> +
> +#ifndef _DT_BINDINGS_CLK_RDA8810_H_
> +#define _DT_BINDINGS_CLK_RDA8810_H_
> +
> +/* soc clocks */
> +#define CLK_CPU 0
> +#define CLK_BUS 1
> +#define CLK_MEM 2
> +
> +#define CLK_USB 3
> +#define CLK_AXI 4
> +#define CLK_GCG 5
> +#define CLK_AHB1 6
> +#define CLK_APB1 7
> +#define CLK_APB2 8
> +
> +#define CLK_GPU 9
> +#define CLK_VPU 10
> +#define CLK_VOC 11
> +#define CLK_SFLSH 12
> +
> +#define CLK_UART1 13
> +#define CLK_UART2 14
> +#define CLK_UART3 15
> +
> +#define CLK_VOC2 16
> +#define CLK_EMMC 17
> +
> +#define CLK_COUNT (CLK_EMMC + 1)

Drop, not a binding.

> +
> +/* resets */
> +#define RST_CPU 0


Missing indentation before the values, everywhere.

> +
> +#define RST_AXI_VOC 1
> +#define RST_AXI_DMA 2
> +#define RST_AXI_CONNECT 3
> +#define RST_AXI_VPU 4
> +
> +#define RST_GCG_GOUDA 5
> +#define RST_GCG_CAMERA 6
> +#define RST_GCG_LCDC 7
> +
> +#define RST_AHB1_USBC 8
> +#define RST_AHB1_SPIFLASH 9
> +
> +#define RST_APB1_TIMER 10
> +#define RST_APB1_KEYPAD 11
> +#define RST_APB1_GPIO 12
> +#define RST_APB1_PWM 13
> +#define RST_APB1_AIF 14
> +#define RST_APB1_AUIFC 15
> +#define RST_APB1_I2C1 16
> +#define RST_APB1_I2C2 17
> +#define RST_APB1_I2C3 18
> +#define RST_APB1_COMREGS 19
> +#define RST_APB1_DMC 20
> +#define RST_APB1_DDRPHY_P 21
> +
> +#define RST_APB2_IFC 22
> +#define RST_APB2_UART1 23
> +#define RST_APB2_UART2 24
> +#define RST_APB2_UART3 25
> +#define RST_APB2_SPI1 26
> +#define RST_APB2_SPI2 27
> +#define RST_APB2_SPI3 28
> +#define RST_APB2_SDMMC1 29
> +#define RST_APB2_SDMMC2 30
> +#define RST_APB2_SDMMC3 31
> +#define RST_APB2_NAND 32
> +
> +#define RST_MEM_GPU 33
> +#define RST_MEM_VPU 34
> +#define RST_MEM_DMC 35
> +#define RST_MEM_DDRPHY_P 36
> +
> +#define RST_COUNT (RST_MEM_DDRPHY_P + 1)

Drop, not a binding.

> +
> +#endif /* _DT_BINDINGS_CLK_RDA8810_H_ */
> 


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 14/25] dt-bindings: power: reset: Add RDA Micro Modem Reset
  2025-09-16 20:25 ` [PATCH 14/25] dt-bindings: power: reset: Add RDA Micro Modem Reset Dang Huynh via B4 Relay
@ 2025-09-17  0:44   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 42+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-17  0:44 UTC (permalink / raw)
  To: dang.huynh, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc

On 17/09/2025 22:25, Dang Huynh via B4 Relay wrote:
> From: Dang Huynh <dang.huynh@mainlining.org>
> 
> Add documentation describing the RDA Micro modem reset controller
> 
> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> ---
>  .../bindings/power/reset/rda,md-reset.yaml         | 36 ++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/power/reset/rda,md-reset.yaml b/Documentation/devicetree/bindings/power/reset/rda,md-reset.yaml
> new file mode 100644
> index 0000000000000000000000000000000000000000..6d09bc8ee6b257aec9d2c4738d285490044003ea
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/reset/rda,md-reset.yaml
> @@ -0,0 +1,36 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/power/reset/rda,md-reset.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: RDA Micro Modem Reset

What Modem? RDA created only one modem? Still wouldn't it have some
model number?

> +
> +maintainers:
> +  - Dang Huynh <dang.huynh@mainlining.org>
> +
> +description:
> +  The modem has a reset register that can be used to fully reset the board.
> +
> +  To do that, a magic value needs to be written to unprotect the register,
> +  then the soft reset register can be used.
> +
> +properties:
> +  compatible:
> +    const: rda,md-reset

Missing SoC compatible. Every SoC block needs one.


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 15/25] power: reset: Add basic power reset driver for RDA8810PL
  2025-09-16 20:25 ` [PATCH 15/25] power: reset: Add basic power reset driver for RDA8810PL Dang Huynh via B4 Relay
@ 2025-09-17  0:45   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 42+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-17  0:45 UTC (permalink / raw)
  To: dang.huynh, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc

On 17/09/2025 22:25, Dang Huynh via B4 Relay wrote:
> From: Dang Huynh <dang.huynh@mainlining.org>
> 
> This basic driver can only reboot, powering off requires the modem
> firmware which we don't have yet.
> 
> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> ---
>  MAINTAINERS                      |  6 +++++
>  drivers/power/reset/Kconfig      |  9 +++++++
>  drivers/power/reset/Makefile     |  1 +
>  drivers/power/reset/rda-reboot.c | 58 ++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 74 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index cbe2ab8af6dcd40dd1456d9df55673dace3c87b2..5ec24d8657bffb55c160947a930980e428c6a6b7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -21399,6 +21399,12 @@ S:	Maintained
>  F:	Documentation/devicetree/bindings/clock/rda,8810pl-apsyscon.yaml
>  F:	drivers/clk/rda/clk-rda8810.c
>  
> +RDA MICRO MODEM RESET DRIVER
> +M:	Dang Huynh <dang.huynh@mainlining.org>
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/power/reset/rda,md-reset.yaml
> +F:	drivers/power/reset/rda-reboot.c
> +
>  RDA MICRO REAL TIME CLOCK DRIVER
>  M:	Dang Huynh <dang.huynh@mainlining.org>
>  S:	Maintained
> diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
> index 77ea3129c70806929f3c248667db42f05f5f1d27..de9b1afb94d14a5d23286ddb302af4107d649c12 100644
> --- a/drivers/power/reset/Kconfig
> +++ b/drivers/power/reset/Kconfig
> @@ -205,6 +205,15 @@ config POWER_RESET_QNAP
>  
>  	  Say Y if you have a QNAP NAS.
>  
> +config POWER_RESET_RDA
> +	bool "RDA Micro Reset Driver"
> +	depends on ARCH_RDA

|| COMPILE_TEST
everywhere, for all your drivers.

> +	help
> +	  This driver supports soft resetting RDA Micro boards by writing
> +	  magic values to the modem register.
> +
> +	  Say Y if you have a board with RDA Micro SoC.
> +
>  config POWER_RESET_REGULATOR
>  	bool "Regulator subsystem power-off driver"
>  	depends on OF && REGULATOR
> diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
> index b7c2b5940be9971548a5527384d1931abff11c4c..14371230410dad2852489160f4fc23d8fd087d6e 100644
> --- a/drivers/power/reset/Makefile
> +++ b/drivers/power/reset/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_POWER_RESET_ODROID_GO_ULTRA_POWEROFF) += odroid-go-ultra-poweroff.o
>  obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
>  obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
>  obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
> +obj-$(CONFIG_POWER_RESET_RDA) += rda-reboot.o
>  obj-$(CONFIG_POWER_RESET_REGULATOR) += regulator-poweroff.o
>  obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
>  obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
> diff --git a/drivers/power/reset/rda-reboot.c b/drivers/power/reset/rda-reboot.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..d87b063ba67d847f8e869e50a6c01427b2866889
> --- /dev/null
> +++ b/drivers/power/reset/rda-reboot.c
> @@ -0,0 +1,58 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2025, Dang Huynh <dang.huynh@mainlining.org>
> + *
> + * Based on drivers/power/reset/msm-poweroff.c:
> + * Copyright (c) 2013, The Linux Foundation. All rights reserved.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/reboot.h>
> +#include <linux/pm.h>
> +#include <linux/mfd/syscon.h>
> +
> +static void __iomem *rda_md_sysctrl;

No, no singletons.

> +
> +static int do_rda_reboot(struct sys_off_data *data)
> +{
> +	/* unprotect md registers */
> +	writel(0x00A50001, rda_md_sysctrl);
> +
> +	/* reset all */
> +	writel(0x80000000, rda_md_sysctrl + 4);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static int rda_reboot_probe(struct platform_device *pdev)
> +{
> +	rda_md_sysctrl = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(rda_md_sysctrl))
> +		return PTR_ERR(rda_md_sysctrl);
> +
> +	devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART,
> +				      128, do_rda_reboot, NULL);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id of_rda_reboot_match[] = {
> +	{ .compatible = "rda,md-reset", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, of_rda_reboot_match);
> +
> +static struct platform_driver rda_reboot_driver = {
> +	.probe = rda_reboot_probe,
> +	.driver = {
> +		.name = "rda-reboot",
> +		.of_match_table = of_match_ptr(of_rda_reboot_match),


Drop of_match_ptr, you have warnings here.

> +	},
> +};
> +builtin_platform_driver(rda_reboot_driver);
> 


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 16/25] dts: unisoc: rda8810pl: Enable modem reset
  2025-09-16 20:25 ` [PATCH 16/25] dts: unisoc: rda8810pl: Enable modem reset Dang Huynh via B4 Relay
@ 2025-09-17  0:46   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 42+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-17  0:46 UTC (permalink / raw)
  To: dang.huynh, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc

On 17/09/2025 22:25, Dang Huynh via B4 Relay wrote:
> From: Dang Huynh <dang.huynh@mainlining.org>
> 
> This allows us to reboot the board from the OS.
> 
> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> ---
>  arch/arm/boot/dts/unisoc/rda8810pl.dtsi | 5 +++++
>  1 file changed, 5 insertions(+)

Please don't do patch per one node. Add logical chunks, but not that
detailed. There are no dependencies here, so way you organize patches
and split them is just confusing.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 22/25] mmc: host: Add RDA Micro SD/MMC driver
  2025-09-16 20:25 ` [PATCH 22/25] mmc: host: Add RDA Micro SD/MMC driver Dang Huynh via B4 Relay
@ 2025-09-17  0:48   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 42+ messages in thread
From: Krzysztof Kozlowski @ 2025-09-17  0:48 UTC (permalink / raw)
  To: dang.huynh, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc

On 17/09/2025 22:25, Dang Huynh via B4 Relay wrote:
> From: Dang Huynh <dang.huynh@mainlining.org>
> 
> RDA Micro RDA8810PL includes an SD/MMC controller. This controller
> supports SD/SDIO/MMC interface.
> 
> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> ---
>  MAINTAINERS                |   6 +
>  drivers/mmc/host/Kconfig   |  12 +
>  drivers/mmc/host/Makefile  |   1 +
>  drivers/mmc/host/rda-mmc.c | 853 +++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 872 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 91be43782f4ba8aacb629002d357a66704f10b2b..33e04ce35dcc4cbadd715ec9199f2453237b8002 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -21417,6 +21417,12 @@ S:	Maintained
>  F:	Documentation/devicetree/bindings/rtc/rda,8810pl-rtc.yaml
>  F:	drivers/rtc/rtc-rda.c
>  
> +RDA MICRO SECURE DIGITAL AND MULTIMEDIA CARD DRIVER
> +M:	Dang Huynh <dang.huynh@mainlining.org>
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/mmc/rda,mmc.yaml
> +F:	drivers/mmc/host/rda-mmc.c
> +
>  RDACM20 Camera Sensor
>  M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
>  M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 4afa0130779d97ca9d1c0ed2102b0babdedcaeeb..352a6eb4e30793b7311c7877c238a7fe31121123 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -1040,6 +1040,18 @@ config MMC_MTK
>  	  This is needed if support for any SD/SDIO/MMC devices is required.
>  	  If unsure, say N.
>  
> +config MMC_RDA
> +	tristate "RDA Micro SD/MMC Card Interface support"
> +	depends on ARCH_RDA

Missing compile test

> +	depends on COMMON_CLK
> +	depends on HAS_DMA
> +	help
> +	  This selects the RDA Micro Secure digital and Multimedia card interface. The
> +	  controller supports SD/SDIO/MMC interface.
> +	  If you have a board with RDA SoC and it uses this interface, say Y or M here.
> +
> +	  If unsure, say N.


...

> +};
> +MODULE_DEVICE_TABLE(of, rda_mmc_dt_ids);
> +
> +static struct platform_driver rda_mmc_driver = {
> +	.probe		= rda_mmc_probe,
> +	.remove		= rda_mmc_remove,
> +	.driver		= {
> +		.name	= "rda-mmc",
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +		.of_match_table = rda_mmc_dt_ids,
> +	},
> +};
> +module_platform_driver(rda_mmc_driver);
> +
> +MODULE_AUTHOR("Dang Huynh <dang.huynh@mainlining.org>");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("MMC/SD driver for RDA platform");
> +MODULE_ALIAS("platform:rda-mmc");

You should not need MODULE_ALIAS() in normal cases. If you need it,
usually it means your device ID table is wrong (e.g. misses either
entries or MODULE_DEVICE_TABLE()). MODULE_ALIAS() is not a substitute
for incomplete ID table.


> 


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 17/25] drivers: gpio: rda: Make direction register unreadable
  2025-09-16 20:25 ` [PATCH 17/25] drivers: gpio: rda: Make direction register unreadable Dang Huynh via B4 Relay
@ 2025-09-17  8:00   ` Bartosz Golaszewski
  0 siblings, 0 replies; 42+ messages in thread
From: Bartosz Golaszewski @ 2025-09-17  8:00 UTC (permalink / raw)
  To: dang.huynh
  Cc: Dang Huynh via B4 Relay, linux-arm-kernel, linux-unisoc,
	devicetree, linux-kernel, linux-gpio, linux-rtc, linux-clk,
	linux-pm, dmaengine, linux-hardening, linux-mmc,
	Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Alexandre Belloni, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Sebastian Reichel, Vinod Koul, Kees Cook, Gustavo A. R. Silva,
	Ulf Hansson

On Tue, 16 Sep 2025 22:25:14 +0200, Dang Huynh via B4 Relay
<devnull+dang.huynh.mainlining.org@kernel.org> said:
> From: Dang Huynh <dang.huynh@mainlining.org>
>
> The register doesn't like to be read, this causes the SD Card
> Card Detect GPIO to misbehaves in the OS.
>

Hi!

Sorry but this message is unintelligible, please say precisely what is going
on and why you need this and why it won't break existing users.

Also: the title should be "gpio: rda: ...".

Bartosz

> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> ---
>  drivers/gpio/gpio-rda.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpio/gpio-rda.c b/drivers/gpio/gpio-rda.c
> index b4db8553a2371ae407fdb7e681d0f82c4d9f74b7..56aaa9f33d29469dfb1bf86ed7b63c54b413c89c 100644
> --- a/drivers/gpio/gpio-rda.c
> +++ b/drivers/gpio/gpio-rda.c
> @@ -245,7 +245,7 @@ static int rda_gpio_probe(struct platform_device *pdev)
>  		.clr = rda_gpio->base + RDA_GPIO_CLR,
>  		.dirout = rda_gpio->base + RDA_GPIO_OEN_SET_OUT,
>  		.dirin = rda_gpio->base + RDA_GPIO_OEN_SET_IN,
> -		.flags = BGPIOF_READ_OUTPUT_REG_SET,
> +		.flags = BGPIOF_READ_OUTPUT_REG_SET | BGPIOF_UNREADABLE_REG_DIR,
>  	};
>
>  	ret = gpio_generic_chip_init(&rda_gpio->chip, &config);
>
> --
> 2.51.0
>
>
>

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver
  2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
                   ` (24 preceding siblings ...)
  2025-09-16 20:25 ` [PATCH 25/25] dts: unisoc: orangepi-i96: " Dang Huynh via B4 Relay
@ 2025-09-17 10:03 ` Manivannan Sadhasivam
  2025-09-18  5:02   ` Dang Huynh
  25 siblings, 1 reply; 42+ messages in thread
From: Manivannan Sadhasivam @ 2025-09-17 10:03 UTC (permalink / raw)
  To: dang.huynh
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson, linux-arm-kernel,
	linux-unisoc, devicetree, linux-kernel, linux-gpio, linux-rtc,
	linux-clk, linux-pm, dmaengine, linux-hardening, linux-mmc

On Wed, Sep 17, 2025 at 03:24:57AM GMT, Dang Huynh via B4 Relay wrote:
> This patch series aims to add support for Clock/Reset, Real-Time Clock and
> SDMMC on the RDA Micro RDA8810PL platform.
> 
> It also adds Intelligent Flow Controller (IOW, a DMA controller) which is
> important for working with this MMC IP.
> 
> Tested on the Orange Pi 2G-IOT.
> 

Thanks for work! Is it possible to split this patchset logically to ease
reviewing and also merging? It currently touches different subsystems and has 25
patches.

You could easily split this into different series adding Clock/Reset, RTC, IFC,
SDMMC and other misc patches in one series.

- Mani

> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
> ---
> Dang Huynh (25):
>       ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes
>       drivers: gpio: rda: Make IRQ optional
>       dt-bindings: gpio: rda: Make interrupts optional
>       rtc: Add timestamp for the end of 2127
>       dt-bindings: rtc: Add RDA Micro RDA8810PL RTC
>       rtc: Add driver for RDA Micro SoC
>       ARM: dts: unisoc: rda8810pl: Enable Real-Time Clock
>       ARM: dts: unisoc: rda8810pl: Enable ARM PMU
>       dt-bindings: clock: Add RDA Micro RDA8810PL clock/reset controller
>       drivers: clk: Add Clock and Reset Driver for RDA Micro RDA8810PL SoC
>       dts: unisoc: rda8810pl: Enable clock/reset driver
>       dts: unisoc: rda8810pl: Add OPP for CPU and define L2 cache
>       dts: unisoc: orangepi: Disable UART with no users
>       dt-bindings: power: reset: Add RDA Micro Modem Reset
>       power: reset: Add basic power reset driver for RDA8810PL
>       dts: unisoc: rda8810pl: Enable modem reset
>       drivers: gpio: rda: Make direction register unreadable
>       dt-bindings: dma: Add RDA IFC DMA
>       dmaengine: Add RDA IFC driver
>       dts: unisoc: rda8810pl: Enable IFC
>       dt-bindings: mmc: Add RDA SDMMC controller
>       mmc: host: Add RDA Micro SD/MMC driver
>       dts: unisoc: rda8810pl: Add SDMMC controllers
>       dts: unisoc: orangepi-2g: Enable SD Card
>       dts: unisoc: orangepi-i96: Enable SD Card
> 
>  .../bindings/clock/rda,8810pl-apsyscon.yaml        |  44 ++
>  Documentation/devicetree/bindings/dma/rda,ifc.yaml |  42 +
>  .../devicetree/bindings/gpio/gpio-rda.yaml         |   3 -
>  Documentation/devicetree/bindings/mmc/rda,mmc.yaml |  91 +++
>  .../bindings/power/reset/rda,md-reset.yaml         |  36 +
>  .../devicetree/bindings/rtc/rda,8810pl-rtc.yaml    |  30 +
>  MAINTAINERS                                        |  30 +
>  .../boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts  |  24 +-
>  .../arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts |  24 +-
>  arch/arm/boot/dts/unisoc/rda8810pl.dtsi            | 115 ++-
>  drivers/clk/Kconfig                                |   1 +
>  drivers/clk/Makefile                               |   1 +
>  drivers/clk/rda/Kconfig                            |  14 +
>  drivers/clk/rda/Makefile                           |   2 +
>  drivers/clk/rda/clk-rda8810.c                      | 770 +++++++++++++++++++
>  drivers/dma/Kconfig                                |  10 +
>  drivers/dma/Makefile                               |   1 +
>  drivers/dma/rda-ifc.c                              | 450 +++++++++++
>  drivers/gpio/gpio-rda.c                            |   4 +-
>  drivers/mmc/host/Kconfig                           |  12 +
>  drivers/mmc/host/Makefile                          |   1 +
>  drivers/mmc/host/rda-mmc.c                         | 853 +++++++++++++++++++++
>  drivers/power/reset/Kconfig                        |   9 +
>  drivers/power/reset/Makefile                       |   1 +
>  drivers/power/reset/rda-reboot.c                   |  58 ++
>  drivers/rtc/Kconfig                                |  11 +
>  drivers/rtc/Makefile                               |   1 +
>  drivers/rtc/rtc-rda.c                              | 356 +++++++++
>  include/dt-bindings/clock/rda,8810pl-apclk.h       |  79 ++
>  include/dt-bindings/dma/rda-ifc.h                  |  28 +
>  include/linux/rtc.h                                |   1 +
>  31 files changed, 3079 insertions(+), 23 deletions(-)
> ---
> base-commit: 590b221ed4256fd6c34d3dea77aa5bd6e741bbc1
> change-id: 20250916-rda8810pl-drivers-9a5271452635
> 
> Best regards,
> -- 
> Dang Huynh <dang.huynh@mainlining.org>
> 
> 

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver
  2025-09-17 10:03 ` [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Manivannan Sadhasivam
@ 2025-09-18  5:02   ` Dang Huynh
  0 siblings, 0 replies; 42+ messages in thread
From: Dang Huynh @ 2025-09-18  5:02 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson, linux-arm-kernel,
	linux-unisoc, devicetree, linux-kernel, linux-gpio, linux-rtc,
	linux-clk, linux-pm, dmaengine, linux-hardening, linux-mmc

On 2025-09-17 17:03, Manivannan Sadhasivam wrote:
> On Wed, Sep 17, 2025 at 03:24:57AM GMT, Dang Huynh via B4 Relay wrote:
>> This patch series aims to add support for Clock/Reset, Real-Time Clock 
>> and
>> SDMMC on the RDA Micro RDA8810PL platform.
>> 
>> It also adds Intelligent Flow Controller (IOW, a DMA controller) which 
>> is
>> important for working with this MMC IP.
>> 
>> Tested on the Orange Pi 2G-IOT.
>> 
> 
> Thanks for work! Is it possible to split this patchset logically to 
> ease
> reviewing and also merging? It currently touches different subsystems 
> and has 25
> patches.
> 
> You could easily split this into different series adding Clock/Reset, 
> RTC, IFC,
> SDMMC and other misc patches in one series.
Will do. Is it possible for you to test it on your i96 board?

> 
> - Mani
> 
>> Signed-off-by: Dang Huynh <dang.huynh@mainlining.org>
>> ---
>> Dang Huynh (25):
>>       ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes
>>       drivers: gpio: rda: Make IRQ optional
>>       dt-bindings: gpio: rda: Make interrupts optional
>>       rtc: Add timestamp for the end of 2127
>>       dt-bindings: rtc: Add RDA Micro RDA8810PL RTC
>>       rtc: Add driver for RDA Micro SoC
>>       ARM: dts: unisoc: rda8810pl: Enable Real-Time Clock
>>       ARM: dts: unisoc: rda8810pl: Enable ARM PMU
>>       dt-bindings: clock: Add RDA Micro RDA8810PL clock/reset 
>> controller
>>       drivers: clk: Add Clock and Reset Driver for RDA Micro RDA8810PL 
>> SoC
>>       dts: unisoc: rda8810pl: Enable clock/reset driver
>>       dts: unisoc: rda8810pl: Add OPP for CPU and define L2 cache
>>       dts: unisoc: orangepi: Disable UART with no users
>>       dt-bindings: power: reset: Add RDA Micro Modem Reset
>>       power: reset: Add basic power reset driver for RDA8810PL
>>       dts: unisoc: rda8810pl: Enable modem reset
>>       drivers: gpio: rda: Make direction register unreadable
>>       dt-bindings: dma: Add RDA IFC DMA
>>       dmaengine: Add RDA IFC driver
>>       dts: unisoc: rda8810pl: Enable IFC
>>       dt-bindings: mmc: Add RDA SDMMC controller
>>       mmc: host: Add RDA Micro SD/MMC driver
>>       dts: unisoc: rda8810pl: Add SDMMC controllers
>>       dts: unisoc: orangepi-2g: Enable SD Card
>>       dts: unisoc: orangepi-i96: Enable SD Card
>> 
>>  .../bindings/clock/rda,8810pl-apsyscon.yaml        |  44 ++
>>  Documentation/devicetree/bindings/dma/rda,ifc.yaml |  42 +
>>  .../devicetree/bindings/gpio/gpio-rda.yaml         |   3 -
>>  Documentation/devicetree/bindings/mmc/rda,mmc.yaml |  91 +++
>>  .../bindings/power/reset/rda,md-reset.yaml         |  36 +
>>  .../devicetree/bindings/rtc/rda,8810pl-rtc.yaml    |  30 +
>>  MAINTAINERS                                        |  30 +
>>  .../boot/dts/unisoc/rda8810pl-orangepi-2g-iot.dts  |  24 +-
>>  .../arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dts |  24 +-
>>  arch/arm/boot/dts/unisoc/rda8810pl.dtsi            | 115 ++-
>>  drivers/clk/Kconfig                                |   1 +
>>  drivers/clk/Makefile                               |   1 +
>>  drivers/clk/rda/Kconfig                            |  14 +
>>  drivers/clk/rda/Makefile                           |   2 +
>>  drivers/clk/rda/clk-rda8810.c                      | 770 
>> +++++++++++++++++++
>>  drivers/dma/Kconfig                                |  10 +
>>  drivers/dma/Makefile                               |   1 +
>>  drivers/dma/rda-ifc.c                              | 450 +++++++++++
>>  drivers/gpio/gpio-rda.c                            |   4 +-
>>  drivers/mmc/host/Kconfig                           |  12 +
>>  drivers/mmc/host/Makefile                          |   1 +
>>  drivers/mmc/host/rda-mmc.c                         | 853 
>> +++++++++++++++++++++
>>  drivers/power/reset/Kconfig                        |   9 +
>>  drivers/power/reset/Makefile                       |   1 +
>>  drivers/power/reset/rda-reboot.c                   |  58 ++
>>  drivers/rtc/Kconfig                                |  11 +
>>  drivers/rtc/Makefile                               |   1 +
>>  drivers/rtc/rtc-rda.c                              | 356 +++++++++
>>  include/dt-bindings/clock/rda,8810pl-apclk.h       |  79 ++
>>  include/dt-bindings/dma/rda-ifc.h                  |  28 +
>>  include/linux/rtc.h                                |   1 +
>>  31 files changed, 3079 insertions(+), 23 deletions(-)
>> ---
>> base-commit: 590b221ed4256fd6c34d3dea77aa5bd6e741bbc1
>> change-id: 20250916-rda8810pl-drivers-9a5271452635
>> 
>> Best regards,
>> --
>> Dang Huynh <dang.huynh@mainlining.org>
>> 
>> 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 06/25] rtc: Add driver for RDA Micro SoC
  2025-09-16 20:25 ` [PATCH 06/25] rtc: Add driver for RDA Micro SoC Dang Huynh via B4 Relay
@ 2025-09-19 13:59   ` kernel test robot
  2025-11-06 22:42   ` Alexandre Belloni
  1 sibling, 0 replies; 42+ messages in thread
From: kernel test robot @ 2025-09-19 13:59 UTC (permalink / raw)
  To: Dang Huynh via B4 Relay, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Alexandre Belloni, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Sebastian Reichel, Vinod Koul,
	Kees Cook, Gustavo A. R. Silva, Ulf Hansson
  Cc: oe-kbuild-all, linux-arm-kernel, linux-unisoc, devicetree,
	linux-kernel, linux-gpio, linux-rtc, linux-clk, linux-pm,
	dmaengine, linux-hardening, linux-mmc, Dang Huynh

Hi Dang,

kernel test robot noticed the following build errors:

[auto build test ERROR on 590b221ed4256fd6c34d3dea77aa5bd6e741bbc1]

url:    https://github.com/intel-lab-lkp/linux/commits/Dang-Huynh-via-B4-Relay/ARM-dts-unisoc-rda8810pl-Add-label-to-GPIO-nodes/20250917-043025
base:   590b221ed4256fd6c34d3dea77aa5bd6e741bbc1
patch link:    https://lore.kernel.org/r/20250917-rda8810pl-drivers-v1-6-9ca9184ca977%40mainlining.org
patch subject: [PATCH 06/25] rtc: Add driver for RDA Micro SoC
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20250919/202509192152.OXdK6bpd-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 15.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250919/202509192152.OXdK6bpd-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202509192152.OXdK6bpd-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/rtc/rtc-rda.c: In function 'rda_rtc_settime':
>> drivers/rtc/rtc-rda.c:67:15: error: implicit declaration of function 'FIELD_PREP' [-Wimplicit-function-declaration]
      67 |         low = FIELD_PREP(RDA_SEC_MASK, tm->tm_sec) |
         |               ^~~~~~~~~~
   drivers/rtc/rtc-rda.c: In function 'rda_rtc_readtime':
>> drivers/rtc/rtc-rda.c:128:22: error: implicit declaration of function 'FIELD_GET' [-Wimplicit-function-declaration]
     128 |         tm->tm_sec = FIELD_GET(RDA_SEC_MASK, low);
         |                      ^~~~~~~~~


vim +/FIELD_PREP +67 drivers/rtc/rtc-rda.c

    50	
    51	static int rda_rtc_settime(struct device *dev, struct rtc_time *tm)
    52	{
    53		struct rda_rtc *rtc = dev_get_drvdata(dev);
    54		u32 high, low;
    55		int ret;
    56	
    57		ret = rtc_valid_tm(tm);
    58		if (ret < 0)
    59			return ret;
    60	
    61		/*
    62		 * The number of years since 1900 in kernel,
    63		 * but it is defined since 2000 by HW.
    64		 * The number of mons' range is from 0 to 11 in kernel,
    65		 * but it is defined from 1 to 12 by HW.
    66		 */
  > 67		low = FIELD_PREP(RDA_SEC_MASK, tm->tm_sec) |
    68			FIELD_PREP(RDA_MIN_MASK, tm->tm_min) |
    69			FIELD_PREP(RDA_HRS_MASK, tm->tm_hour);
    70	
    71		high = FIELD_PREP(RDA_MDAY_MASK, tm->tm_mday) |
    72			FIELD_PREP(RDA_MON_MASK, tm->tm_mon + 1) |
    73			FIELD_PREP(RDA_YEAR_MASK, tm->tm_year - 100) |
    74			FIELD_PREP(RDA_WDAY_MASK, tm->tm_wday);
    75	
    76		ret = regmap_write(rtc->regmap, RDA_RTC_CAL_LOAD_LOW_REG, low);
    77		if (ret < 0) {
    78			dev_err(dev, "Failed to update RTC low register: %d\n", ret);
    79			return ret;
    80		}
    81	
    82		ret = regmap_write(rtc->regmap, RDA_RTC_CAL_LOAD_HIGH_REG, high);
    83		if (ret < 0) {
    84			dev_err(dev, "Failed to update RTC low register: %d\n", ret);
    85			return ret;
    86		}
    87	
    88		ret = regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG, RDA_RTC_CMD_CAL_LOAD, 1);
    89		if (ret < 0) {
    90			dev_err(dev, "Failed to update RTC cal load register: %d\n", ret);
    91			return ret;
    92		}
    93	
    94		return 0;
    95	}
    96	
    97	static int rda_rtc_readtime(struct device *dev, struct rtc_time *tm)
    98	{
    99		struct rda_rtc *rtc = dev_get_drvdata(dev);
   100		unsigned int high, low;
   101		int ret;
   102	
   103		/*
   104		 * Check if RTC data is valid.
   105		 *
   106		 * When this bit is set, it means the data in the RTC is invalid
   107		 * or not configured.
   108		 */
   109		ret = regmap_test_bits(rtc->regmap, RDA_RTC_STA_REG, RDA_RTC_STA_NOT_PROG);
   110		if (ret < 0) {
   111			dev_err(dev, "Failed to read RTC status: %d\n", ret);
   112			return ret;
   113		} else if (ret > 0)
   114			return -EINVAL;
   115	
   116		ret = regmap_read(rtc->regmap, RDA_RTC_CUR_LOAD_HIGH_REG, &high);
   117		if (ret) {
   118			dev_err(dev, "Failed to read RTC high reg: %d\n", ret);
   119			return ret;
   120		}
   121	
   122		ret = regmap_read(rtc->regmap, RDA_RTC_CUR_LOAD_LOW_REG, &low);
   123		if (ret) {
   124			dev_err(dev, "Failed to read RTC low reg: %d\n", ret);
   125			return ret;
   126		}
   127	
 > 128		tm->tm_sec = FIELD_GET(RDA_SEC_MASK, low);
   129		tm->tm_min = FIELD_GET(RDA_MIN_MASK, low);
   130		tm->tm_hour = FIELD_GET(RDA_HRS_MASK, low);
   131		tm->tm_mday = FIELD_GET(RDA_MDAY_MASK, high);
   132		tm->tm_mon = FIELD_GET(RDA_MON_MASK, high);
   133		tm->tm_year = FIELD_GET(RDA_YEAR_MASK, high);
   134		tm->tm_wday = FIELD_GET(RDA_WDAY_MASK, high);
   135	
   136		/*
   137		 * The number of years since 1900 in kernel,
   138		 * but it is defined since 2000 by HW.
   139		 */
   140		tm->tm_year += 100;
   141		/*
   142		 * The number of mons' range is from 0 to 11 in kernel,
   143		 * but it is defined from 1 to 12 by HW.
   144		 */
   145		tm->tm_mon -= 1;
   146	
   147		return 0;
   148	}
   149	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 10/25] drivers: clk: Add Clock and Reset Driver for RDA Micro RDA8810PL SoC
  2025-09-16 20:25 ` [PATCH 10/25] drivers: clk: Add Clock and Reset Driver for RDA Micro RDA8810PL SoC Dang Huynh via B4 Relay
@ 2025-09-20  4:50   ` Stephen Boyd
  0 siblings, 0 replies; 42+ messages in thread
From: Stephen Boyd @ 2025-09-20  4:50 UTC (permalink / raw)
  To: Alexandre Belloni, Bartosz Golaszewski, Conor Dooley,
	Dang Huynh via B4 Relay, Gustavo A. R. Silva, Kees Cook,
	Krzysztof Kozlowski, Linus Walleij, Manivannan Sadhasivam,
	Michael Turquette, Philipp Zabel, Rob Herring, Sebastian Reichel,
	Ulf Hansson, Vinod Koul, dang.huynh
  Cc: linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc, Dang Huynh

Quoting Dang Huynh via B4 Relay (2025-09-16 13:25:07)
> diff --git a/drivers/clk/rda/Kconfig b/drivers/clk/rda/Kconfig
> new file mode 100644
> index 0000000000000000000000000000000000000000..b505e3e552cef1e7ea3da4aa46d61d0d0a3d5db0
> --- /dev/null
> +++ b/drivers/clk/rda/Kconfig
> @@ -0,0 +1,14 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config CLK_RDA8810
> +       bool "RDA Micro RDA8810PL Clock and Reset Controller"

Can it be tristate?

> +       depends on ARCH_RDA || COMPILE_TEST
> +       select MFD_SYSCON
> +       select REGMAP_MMIO
> +       select RESET_CONTROLLER
> +       help
> +         This driver supports clock and reset for RDA Micro RDA8810 platform.
> +         If you have a board with the RDA8810PL SoC, say Y to use most of the
> +         board peripherals.
> +
> +         If unsure, say N.
> +
> new file mode 100644
> index 0000000000000000000000000000000000000000..8bea60d5376aeb4c67cd15bc1a64dbcf7a0a1f7c
> --- /dev/null
> +++ b/drivers/clk/rda/clk-rda8810.c
> @@ -0,0 +1,770 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * RDA8810PL Clock and Reset driver
> + *
> + * Copyright (C) 2013 RDA Microelectronics Inc.
> + * Copyright (c) 2025 Dang Huynh <dang.huynh@mainlining.org>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>

Is this used?

> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>

Is this used?

> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/reset-controller.h>
> +#include <dt-bindings/clock/rda,8810pl-apclk.h>
> +
> +#define MHZ 1000000U
> +
> +/*
> + * Some registers are protected, we need to write AP_CTRL_PROTECT_UNLOCK to
> + * AP_REG_DBG then we can make changes to them.
> + */
> +#define AP_CTRL_PROTECT_LOCK   0xA50000
> +#define AP_CTRL_PROTECT_UNLOCK 0xA50001

Lowercase hex please. I don't see these used though so what's going on?

> +
> +/* Register Base */
> +#define AP_REG_DBG 0x0
> +#define AP_REG_CPU_ENABLE 0x60
> +#define AP_REG_AXI_ENABLE 0x6C
> +#define AP_REG_AXIDIV2_ENABLE 0x78
> +#define AP_REG_GCG_ENABLE 0x84
> +#define AP_REG_AHB1_ENABLE 0x90
> +#define AP_REG_APB1_ENABLE 0x9C
> +#define AP_REG_APB2_ENABLE 0xA8
> +#define AP_REG_MEM_ENABLE 0xB4
> +#define AP_REG_APO_ENABLE 0xC0
> +
> +/* AP Clk Config Bits */
> +#define AP_PERI_SRC_DIV BIT(12)
> +
> +/* UART Clock Bits */
> +#define AP_UART_DIVIDER GENMASK(9, 0)
> +#define AP_UART_SET_PLL BIT(12)
> +
> +/* AP Clk Enable */
> +#define AP_ENABLE_CPU_CORE BIT(0)
> +#define AP_ENABLE_CPU_DUMMY BIT(1)
> +#define AP_ENABLE_AHB0_CONF BIT(0)
> +#define AP_ENABLE_APB0_CONF BIT(1)
> +#define AP_ENABLE_AXI_VOC  BIT(2)
> +#define AP_ENABLE_AXI_DMA  BIT(3)
> +#define AP_ENABLE_AXI_ALWAYS BIT(4)
> +#define AP_ENABLE_AXI_CONNECT BIT(5)
> +#define AP_ENABLE_APB0_IRQ BIT(6)
> +#define AP_ENABLE_AXIDIV2_IMEM BIT(0)
> +#define AP_ENABLE_AXIDIV2_ALWAYS BIT(1)
> +#define AP_ENABLE_AXIDIV2_CONNECT BIT(2)
> +#define AP_ENABLE_AXIDIV2_VPU BIT(3)
> +#define AP_ENABLE_GCG_APB_CONF BIT(0)
> +#define AP_ENABLE_GCG_GOUDA BIT(1)
> +#define AP_ENABLE_GCG_CAMERA BIT(2)
> +#define AP_ENABLE_GCG_ALWAYS BIT(3)
> +#define AP_ENABLE_GCG_CONNECT BIT(4)
> +#define AP_ENABLE_GCG_DPI BIT(7)
> +#define AP_ENABLE_AHB1_USBC BIT(0)
> +#define AP_ENABLE_AHB1_ALWAYS BIT(1)
> +#define AP_ENABLE_AHB1_SPIFLASH BIT(2)
> +#define AP_ENABLE_APB1_CONF BIT(0)
> +#define AP_ENABLE_APB1_AIF BIT(1)
> +#define AP_ENABLE_APB1_AUIFC BIT(2)
> +#define AP_ENABLE_APB1_AUIFC_CH0 BIT(3)
> +#define AP_ENABLE_APB1_AUIFC_CH1 BIT(4)
> +#define AP_ENABLE_APB1_I2C1 BIT(5)
> +#define AP_ENABLE_APB1_I2C2 BIT(6)
> +#define AP_ENABLE_APB1_I2C3 BIT(7)
> +#define AP_ENABLE_APB1D_OSC BIT(8)
> +#define AP_ENABLE_APB1D_PWM BIT(9)
> +#define AP_ENABLE_APB1_ALWAYS BIT(10)
> +#define AP_ENABLE_APB1_DAPLITE BIT(11)
> +#define AP_ENABLE_APB1_TIMER BIT(12)
> +#define AP_ENABLE_APB1_GPIO BIT(13)
> +#define AP_ENABLE_APB2_CONF BIT(0)
> +#define AP_ENABLE_APB2_IFC BIT(1)
> +#define AP_ENABLE_APB2_IFC_CH0 BIT(2)
> +#define AP_ENABLE_APB2_IFC_CH1 BIT(3)
> +#define AP_ENABLE_APB2_IFC_CH2 BIT(4)
> +#define AP_ENABLE_APB2_IFC_CH3 BIT(5)
> +#define AP_ENABLE_APB2_IFC_CH4 BIT(6)
> +#define AP_ENABLE_APB2_IFC_CH5 BIT(7)
> +#define AP_ENABLE_APB2_IFC_CH6 BIT(8)
> +#define AP_ENABLE_APB2_IFC_CH7 BIT(9)
> +#define AP_ENABLE_APB2_UART1 BIT(10)
> +#define AP_ENABLE_APB2_UART2 BIT(11)
> +#define AP_ENABLE_APB2_UART3 BIT(12)
> +#define AP_ENABLE_APB2_SPI1 BIT(13)
> +#define AP_ENABLE_APB2_SPI2 BIT(14)
> +#define AP_ENABLE_APB2_SPI3 BIT(15)
> +#define AP_ENABLE_APB2_SDMMC1 BIT(16)
> +#define AP_ENABLE_APB2_SDMMC2 BIT(17)
> +#define AP_ENABLE_APB2_SDMMC3 BIT(18)
> +#define AP_ENABLE_APB2_ALWAYS BIT(19)
> +#define AP_ENABLE_APB2_NANDFLASH BIT(20)
> +#define AP_ENABLE_MEM_CONF BIT(0)
> +#define AP_ENABLE_MEM_DMC  BIT(1)
> +#define AP_ENABLE_MEM_GPU  BIT(2)
> +#define AP_ENABLE_MEM_VPU  BIT(3)
> +#define AP_ENABLE_MEM_DDRPHY_P BIT(4)
> +#define AP_ENABLE_MEM_CONNECT BIT(5)
> +#define AP_ENABLE_APOC_VPU BIT(0)
> +#define AP_ENABLE_APOC_BCK BIT(1)
> +#define AP_ENABLE_APOC_UART1 BIT(2)
> +#define AP_ENABLE_APOC_UART2 BIT(3)
> +#define AP_ENABLE_APOC_UART3 BIT(4)
> +#define AP_ENABLE_APOC_VOC_CORE BIT(5)
> +#define AP_ENABLE_APOC_VOC BIT(6)
> +#define AP_ENABLE_APOC_VOC_ALWAYS BIT(7)
> +#define AP_ENABLE_APOC_DDRPHY_N BIT(8)
> +#define AP_ENABLE_APOC_DDRPHY2XP BIT(9)
> +#define AP_ENABLE_APOC_DDRPHY2XN BIT(10)
> +#define AP_ENABLE_APOC_GPU BIT(11)
> +#define AP_ENABLE_APOC_USBPHY BIT(12)
> +#define AP_ENABLE_APOC_CSI BIT(13)
> +#define AP_ENABLE_APOC_DSI BIT(14)
> +#define AP_ENABLE_APOC_GPIO BIT(15)
> +#define AP_ENABLE_APOC_SPIFLASH BIT(16)
> +#define AP_ENABLE_APOC_PIX BIT(17)
> +#define AP_ENABLE_APOC_PDGB BIT(18)
> +
> +/* AP Reset */
> +#define AP_RST_CPU_REG 0x1C
> +#define AP_RST_AXI_REG 0x24
> +#define AP_RST_AXIDIV2_REG 0x2C
> +#define AP_RST_GCG_REG 0x34
> +#define AP_RST_AHB1_REG 0x3C
> +#define AP_RST_APB1_REG 0x44
> +#define AP_RST_APB2_REG 0x4C
> +#define AP_RST_MEM_REG 0x54
> +
> +/* Bits */
> +#define AP_RST_CPU_CORE BIT(0)
> +#define AP_RST_CPU_SYS BIT(1)

Please add tabs because this is really hard to read when the define is
one space away from the definition.

> +#define AP_RST_AXI_VOC BIT(0)
> +#define AP_RST_AXI_DMA BIT(1)
> +#define AP_RST_AXI_SYS BIT(2)
> +#define AP_RST_AXI_CONNECT BIT(3)
> +#define AP_RST_AXI_VPU BIT(5)
> +#define AP_RST_AXIDIV2_IMEM BIT(0)
> +#define AP_RST_AXIDIV2_SYS BIT(1)
> +#define AP_RST_AXIDIV2_VPU BIT(2)
> +#define AP_RST_GCG_SYS BIT(0)
> +#define AP_RST_GCG_GOUDA BIT(1)
> +#define AP_RST_GCG_CAMERA BIT(2)
> +#define AP_RST_GCG_LCDC BIT(4)
> +#define AP_RST_AHB1_SYS BIT(0)
> +#define AP_RST_AHB1_USBC BIT(1)
> +#define AP_RST_AHB1_SPIFLASH BIT(2)
> +#define AP_RST_APB1_SYS BIT(0)
> +#define AP_RST_APB1_TIMER BIT(1)
> +#define AP_RST_APB1_KEYPAD BIT(2)
> +#define AP_RST_APB1_GPIO BIT(3)
> +#define AP_RST_APB1_PWM BIT(4)
> +#define AP_RST_APB1_AIF BIT(5)
> +#define AP_RST_APB1_AUIFC BIT(6)
> +#define AP_RST_APB1_I2C1 BIT(7)
> +#define AP_RST_APB1_I2C2 BIT(8)
> +#define AP_RST_APB1_I2C3 BIT(9)
> +#define AP_RST_APB1_COMREGS BIT(10)
> +#define AP_RST_APB1_DMC BIT(11)
> +#define AP_RST_APB1_DDRPHY_P BIT(12)
> +#define AP_RST_APB2_SYS BIT(0)
> +#define AP_RST_APB2_IFC BIT(1)
> +#define AP_RST_APB2_UART1 BIT(2)
> +#define AP_RST_APB2_UART2 BIT(3)
> +#define AP_RST_APB2_UART3 BIT(4)
> +#define AP_RST_APB2_SPI1 BIT(5)
> +#define AP_RST_APB2_SPI2 BIT(6)
> +#define AP_RST_APB2_SPI3 BIT(7)
> +#define AP_RST_APB2_SDMMC1 BIT(8)
> +#define AP_RST_APB2_SDMMC2 BIT(9)
> +#define AP_RST_APB2_SDMMC3 BIT(10)
> +#define AP_RST_APB2_NANDFLASH BIT(11)
> +#define AP_RST_MEM_SYS BIT(0)
> +#define AP_RST_MEM_GPU BIT(1)
> +#define AP_RST_MEM_VPU BIT(2)
> +#define AP_RST_MEM_DMC BIT(3)
> +#define AP_RST_MEM_DDRPHY_P BIT(4)
> +
> +/* Default PLL frequency */
> +#define AP_PLL_CPU_FREQ (988 * MHZ)
> +#define AP_PLL_BUS_FREQ (800 * MHZ)
> +#define AP_PLL_MEM_FREQ (260 * MHZ)
> +#define AP_PLL_USB_FREQ (480 * MHZ)

Are these inputs to the clk controller? Can we get these values from
fixed rate clk nodes in the DT instead of hard coding the rates in this
driver, especially if this is controllable outside of this device?

> +
> +struct rda8810_reset_list {
> +       int reg;
> +       int bit;

Why signed?

> +};
> +
> +struct rda_clk_priv {
> +       struct device *dev;
> +       struct regmap *regmap;
> +       struct clk_hw_onecell_data *onecell;
> +       struct reset_controller_dev rstctl;
> +       const struct rda8810_reset_list *rstlist;
> +};
> +
> +struct rda_clk_hw {
> +       int id;
> +       int reg;

Why signed?

> +       struct clk_hw hw;
> +
> +       int ena_reg;
> +       int ena_bit;

Why signed? Are they offsets? Use u32 or u8?

> +
> +       struct rda_clk_priv *priv;
> +};
> +
> +struct rda_clk_matchdata {
> +       const struct rda_clk_hw *clk_list;
> +       int max_clocks;
> +};
> +
> +static const struct clk_ops rda8810_clk_ops;

Presumably this can be defined instead of forward declared and then the
clk data can follow the functions?

> +
> +#define RDA_CLK_INIT(_id, _name, _parent, _flags, _reg, _ena_reg, _ena_bit) { \
> +       .id = _id, \
> +       .reg = _reg, \
> +       .ena_reg = _ena_reg, \
> +       .ena_bit = _ena_bit, \
> +       .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \
> +                       _parent, \
> +                       &rda8810_clk_ops, \
> +                       _flags) \
> +}
> +
> +#define RDA_CLK_INIT_NO_PARENT(_id, _name, _flags, _reg, _ena_reg, _ena_bit) { \
> +       .id = _id, \
> +       .reg = _reg, \
> +       .ena_reg = _ena_reg, \
> +       .ena_bit = _ena_bit, \
> +       .hw.init = CLK_HW_INIT_NO_PARENT(_name, &rda8810_clk_ops, _flags) \
> +}
> +
> +#define to_rda_rst(p) container_of(p, struct rda_clk_priv, rstctl)
> +
> +static inline struct rda_clk_hw *to_rda_hw(struct clk_hw *hw)
> +{
> +       return container_of(hw, struct rda_clk_hw, hw);
> +}
> +
> +/* clock division value map */
> +static const u8 clk_div_map[] = {
> +       4*60,   /* 0 */

Maybe
	[0 .. 7] = 4 * 60,
	[8] = 4 * 40,

etc. so the comment isn't needed and it is shorter.

> +       4*60,   /* 1 */
> +       4*60,   /* 2 */
> +       4*60,   /* 3 */
> +       4*60,   /* 4 */
> +       4*60,   /* 5 */
> +       4*60,   /* 6 */
> +       4*60,   /* 7 */
> +       4*40,   /* 8 */
> +       4*30,   /* 9 */
> +       4*24,   /* 10 */
> +       4*20,   /* 11 */
> +       4*17,   /* 12 */
> +       4*15,   /* 13 */
> +       4*13,   /* 14 */
> +       4*12,   /* 15 */
> +       4*11,   /* 16 */
> +       4*10,   /* 17 */
> +       4*9,    /* 18 */
> +       4*8,    /* 19 */
> +       4*7,    /* 20 */
> +       4*13/2, /* 21 */
> +       4*6,    /* 22 */
> +       4*11/2, /* 23 */
> +       4*5,    /* 24 */
> +       4*9/2,  /* 25 */
> +       4*4,    /* 26 */
> +       4*7/2,  /* 27 */
> +       4*3,    /* 28 */
> +       4*5/2,  /* 29 */
> +       4*2,    /* 30 */
> +       4*1,    /* 31 */
> +};
> +
> +static u32 apsys_get_divreg(u32 basefreq, u32 reqfreq, u32 *pdiv2)
> +{
> +       int i;
> +       int index;
> +       u32 adiv;
> +       u32 ndiv;
> +
> +       adiv = basefreq / (reqfreq >> 2);
> +       if (pdiv2) {
> +               /* try div2 mode first */
> +               ndiv = adiv >> 1;
> +       } else {
> +               ndiv = adiv;
> +       }
> +
> +       for (i = ARRAY_SIZE(clk_div_map) - 1; i >= 1; i--)
> +               if (ndiv < ((clk_div_map[i] + clk_div_map[i-1]) >> 1))
> +                       break;
> +       index = i;
> +
> +       if (pdiv2) {
> +               if (adiv == (clk_div_map[index] << 1)) {
> +                       /* div2 mode is OK */
> +                       *pdiv2 = 1;
> +               } else {
> +                       /* try div1 mode */
> +                       for (i = ARRAY_SIZE(clk_div_map) - 1; i >= 1; i--)
> +                               if (adiv < ((clk_div_map[i] + clk_div_map[i-1]) >> 1))
> +                                       break;
> +                       /* compare the results between div1 and div2 */
> +                       if (abs(adiv - (clk_div_map[index] << 1)) <=
> +                                       abs(adiv - clk_div_map[i])) {
> +                               *pdiv2 = 1;
> +                       } else {
> +                               *pdiv2 = 0;
> +                               index = i;
> +                       }
> +               }
> +       }
> +
> +       return index;
> +}
> +
> +static u32 apsys_cal_freq_by_divreg(u32 basefreq, u32 reg, u32 div2)
> +{
> +       u32 newfreq;
> +
> +       if (reg >= ARRAY_SIZE(clk_div_map))
> +               reg = ARRAY_SIZE(clk_div_map) - 1;
> +
> +       /* Assuming basefreq is smaller than 2^31 (2.147G Hz) */
> +       newfreq = (basefreq << (div2 ? 0 : 1)) / (clk_div_map[reg] >> 1);
> +       return newfreq;
> +}
> +
> +static void apsys_get_reg_div(struct rda_clk_hw *rda_hw, u32 *reg, u32 *div2)
> +{
> +       struct rda_clk_priv *priv = rda_hw->priv;
> +       int tmp_reg, tmp_div2;
> +       int ret;
> +
> +       ret = regmap_read(priv->regmap, rda_hw->reg, &tmp_reg);
> +       if (ret)
> +               return;
> +
> +       tmp_div2 = tmp_reg & AP_PERI_SRC_DIV;
> +
> +       *reg = tmp_reg;
> +       *div2 = tmp_div2;
> +}
> +
> +static int apsys_get_uart_clock(unsigned long parent_rate, u32 *reg)
> +{
> +       int clksrc = 26000000;
> +       u32 div;
> +       int rate = 0;
> +
> +       if (*reg & AP_UART_SET_PLL)
> +               clksrc = parent_rate / 8;
> +
> +       div = FIELD_GET(AP_UART_DIVIDER, *reg);
> +
> +       /* rate = clksrc / divmode / (div+2) */
> +       rate =  clksrc / 4 / (div + 2);

Maybe

	const unsigned int divmode = 4;

	rate = clksrc / divmode / (div + 2);

and then the comment is unnecessary.

> +
> +       return rate;
> +}
> +
> +static int apsys_cal_uart_clock(int freq)
> +{
> +       int new_freq = freq;
> +
> +       /*
> +        * To calculate maximum clock:
> +        *     freq = 26 MHz / div / (0 + 2)
> +        *
> +        * For lowest clock:
> +        *     freq = 26 MHz / div / (0x3FF + 2)
> +        */
> +       if (freq > 3250000)
> +               new_freq = 3250000;
> +       else if (freq < 6342)
> +               new_freq = 6342;

Use clamp()

> +
> +       new_freq = (26000000 + 4 / 2 * new_freq) / (4 * new_freq) - 2;
> +
> +       return new_freq;
> +}
> +
> +static int rda8810_clk_set_rate(struct clk_hw *clk, unsigned long rate,
> +               unsigned long parent_rate)
> +{
> +       struct rda_clk_hw *rda_hw = to_rda_hw(clk);
> +       struct rda_clk_priv *priv = rda_hw->priv;
> +       struct device *dev = priv->dev;
> +       int val, div2 = 0;
> +       int ret;
> +
> +       switch (rda_hw->id) {
> +       case CLK_CPU:
> +               val = apsys_get_divreg(AP_PLL_CPU_FREQ, rate, NULL);
> +               break;
> +       case CLK_AXI:
> +       case CLK_AHB1:
> +       case CLK_APB1:
> +       case CLK_APB2:
> +       case CLK_GCG:
> +       case CLK_GPU:
> +       case CLK_SFLSH:
> +       case CLK_VOC:
> +       case CLK_VPU:
> +               val = apsys_get_divreg(parent_rate, rate, &div2);
> +               break;
> +       case CLK_UART1:
> +       case CLK_UART2:
> +       case CLK_UART3:
> +               val = apsys_cal_uart_clock(rate);
> +               if (val == 0)
> +                       return -EINVAL;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       if (div2)
> +               val |= AP_PERI_SRC_DIV;
> +
> +       dev_dbg(dev, "clk_id: %d - rate: %ld - parent rate: %ld - val: %d - div: %d\n",
> +                       rda_hw->id, rate, parent_rate, val, div2);
> +
> +       ret = regmap_write(priv->regmap, rda_hw->reg, val);
> +       if (ret < 0)
> +               return ret;
> +
> +       return rate;
> +}
> +
> +static unsigned long rda8810_clk_recalc_rate(struct clk_hw *clk,
> +               unsigned long parent_rate)
> +{
> +       struct rda_clk_hw *rda_hw = to_rda_hw(clk);
> +       u32 reg, div2;
> +
> +       apsys_get_reg_div(rda_hw, &reg, &div2);
> +
> +       switch (rda_hw->id) {
> +       case CLK_CPU:
> +               return apsys_cal_freq_by_divreg(AP_PLL_CPU_FREQ, reg, 0);
> +       case CLK_BUS:
> +               return AP_PLL_BUS_FREQ;
> +       case CLK_MEM:
> +               return AP_PLL_MEM_FREQ >> (2 + div2);
> +       /* Bus peripherals */
> +       case CLK_USB:
> +               return AP_PLL_USB_FREQ;
> +       case CLK_AXI:
> +       case CLK_AHB1:
> +       case CLK_APB1:
> +       case CLK_APB2:
> +       case CLK_GCG:
> +       case CLK_GPU:
> +       case CLK_SFLSH:
> +       case CLK_VOC:
> +       case CLK_VPU:
> +               return apsys_cal_freq_by_divreg(parent_rate, reg, div2);
> +       /* For UART clocks, we'll have to do more calculation */
> +       case CLK_UART1:
> +       case CLK_UART2:
> +       case CLK_UART3:
> +               return apsys_get_uart_clock(parent_rate, &reg);
> +       default:
> +               return 0;
> +       }
> +}
> +
> +static long rda8810_clk_round_rate(struct clk_hw *clk, unsigned long rate,
> +               unsigned long *parent_rate)
> +{
> +       return rate;
> +}
> +
> +static int rda8810_clk_enable(struct clk_hw *clk)
> +{
> +       struct rda_clk_hw *rda_hw = to_rda_hw(clk);
> +       struct rda_clk_priv *priv = rda_hw->priv;
> +
> +       if (rda_hw->ena_reg < 0 || rda_hw->ena_bit < 0)
> +               return 0;
> +
> +       return regmap_write(priv->regmap, rda_hw->ena_reg, rda_hw->ena_bit);
> +}
> +
> +static void rda8810_clk_disable(struct clk_hw *clk)
> +{
> +       struct rda_clk_hw *rda_hw = to_rda_hw(clk);
> +       struct rda_clk_priv *priv = rda_hw->priv;
> +
> +       if (rda_hw->ena_reg < 0 || rda_hw->ena_bit < 0)
> +               return;
> +
> +       regmap_write(priv->regmap, rda_hw->ena_reg + 4, rda_hw->ena_bit);
> +}
> +
> +static const struct clk_ops rda8810_clk_ops = {
> +       .enable = rda8810_clk_enable,
> +       .disable = rda8810_clk_disable,
> +
> +       .recalc_rate = rda8810_clk_recalc_rate,
> +       .round_rate = rda8810_clk_round_rate,
> +       .set_rate = rda8810_clk_set_rate,
> +};
> +
> +/* Root clocks */
> +static struct rda_clk_hw rda8810_clk_cpu_desc =
> +RDA_CLK_INIT_NO_PARENT(CLK_CPU, "cpu", CLK_IS_CRITICAL, 0xC8,
> +               AP_REG_CPU_ENABLE, AP_ENABLE_CPU_CORE);
> +static struct rda_clk_hw rda8810_clk_mem_desc =
> +RDA_CLK_INIT_NO_PARENT(CLK_MEM, "mem", CLK_IS_CRITICAL, 0xE0, -1, -1);
> +
> +static struct rda_clk_hw rda8810_clk_bus_desc =
> +RDA_CLK_INIT_NO_PARENT(CLK_BUS, "bus", CLK_IS_CRITICAL, -1, -1, -1);

If they're critical can we just enabled them once in probe and not make
structures in memory that are basically useless?

> +
> +/* Bus clocks */
> +static struct rda_clk_hw rda8810_clk_usb_desc = RDA_CLK_INIT(CLK_USB, "usb",
> +               (const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
> +               0, -1, -1, -1);
> +static struct rda_clk_hw rda8810_clk_axi_desc = RDA_CLK_INIT(CLK_AXI, "axi",
> +               (const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
> +               0, 0xCC, -1, -1);
> +static struct rda_clk_hw rda8810_clk_gcg_desc = RDA_CLK_INIT(CLK_GCG, "gcg",
> +               (const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
> +               0, 0xD0, -1, -1);
> +static struct rda_clk_hw rda8810_clk_ahb1_desc = RDA_CLK_INIT(CLK_AHB1, "ahb1",
> +               (const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
> +               0, 0xD4, -1, -1);
> +static struct rda_clk_hw rda8810_clk_apb1_desc = RDA_CLK_INIT(CLK_APB1, "apb1",
> +               (const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
> +               0, 0xD8, -1, -1);
> +static struct rda_clk_hw rda8810_clk_apb2_desc = RDA_CLK_INIT(CLK_APB2, "apb2",
> +               (const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
> +               0, 0xDC, -1, -1);
> +static struct rda_clk_hw rda8810_clk_gpu_desc = RDA_CLK_INIT(CLK_GPU, "gpu",
> +               (const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
> +               0, 0xE4, AP_REG_APO_ENABLE, AP_ENABLE_APOC_GPU);
> +static struct rda_clk_hw rda8810_clk_vpu_desc = RDA_CLK_INIT(CLK_VPU, "vpu",
> +               (const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
> +               0, 0xE8, AP_REG_APO_ENABLE, AP_ENABLE_APOC_VPU);
> +static struct rda_clk_hw rda8810_clk_voc_desc = RDA_CLK_INIT(CLK_VOC, "voc",
> +               (const struct clk_parent_data[]) { { .hw = &rda8810_clk_bus_desc.hw } },
> +               0, 0xEC, AP_REG_APO_ENABLE,
> +               AP_ENABLE_APOC_VOC | AP_ENABLE_APOC_VOC_CORE | AP_ENABLE_APOC_VOC_ALWAYS);
> +
> +/* APB1 peripherals */
> +static struct rda_clk_hw rda8810_clk_spiflash_desc = RDA_CLK_INIT(CLK_SFLSH, "spiflash",
> +               (const struct clk_parent_data[]) { { .hw = &rda8810_clk_apb1_desc.hw } },
> +               0, 0xF0, AP_REG_APO_ENABLE, AP_ENABLE_APOC_SPIFLASH);
> +
> +/* APB2 peripherals */
> +static struct rda_clk_hw rda8810_clk_uart_desc[] = {
> +       RDA_CLK_INIT(CLK_UART1, "uart1",
> +                       (const struct clk_parent_data[]) { { .hw = &rda8810_clk_apb2_desc.hw } },

If the only parent is a clk_hw pointer please use clk_hws in struct
clk_init_data instead of parent_data.

> +                       0, 0xF4,
> +                       AP_REG_APO_ENABLE, AP_ENABLE_APOC_UART1),
> +       RDA_CLK_INIT(CLK_UART2, "uart2",
> +                       (const struct clk_parent_data[]) { { .hw = &rda8810_clk_apb2_desc.hw } },
> +                       0, 0xF8,
> +                       AP_REG_APO_ENABLE, AP_ENABLE_APOC_UART2),
> +       RDA_CLK_INIT(CLK_UART3, "uart3",
> +                       (const struct clk_parent_data[]) { { .hw = &rda8810_clk_apb2_desc.hw } },
> +                       0, 0xFC,
> +                       AP_REG_APO_ENABLE, AP_ENABLE_APOC_UART3),
> +};
> +
> +static struct rda_clk_hw *const rda8810_clk_list[] = {
> +       &rda8810_clk_cpu_desc,
> +       &rda8810_clk_bus_desc,
> +       &rda8810_clk_mem_desc,
> +
> +       &rda8810_clk_usb_desc,
> +       &rda8810_clk_axi_desc,
> +       &rda8810_clk_gcg_desc,
> +       &rda8810_clk_ahb1_desc,
> +       &rda8810_clk_apb1_desc,
> +       &rda8810_clk_apb2_desc,
> +
> +       &rda8810_clk_gpu_desc,
> +       &rda8810_clk_vpu_desc,
> +       &rda8810_clk_voc_desc,
> +
> +       &rda8810_clk_spiflash_desc,
> +
> +       &rda8810_clk_uart_desc[0],
> +       &rda8810_clk_uart_desc[1],
> +       &rda8810_clk_uart_desc[2],
> +};
> +
> +static const struct rda8810_reset_list rda8810_rst_data[] = {
> +       /* ID, REG */
> +
> +       /* CPU */
> +       [RST_CPU] = { AP_RST_CPU_REG, AP_RST_CPU_CORE },
> +
> +       /* AXI */
> +       [RST_AXI_VOC] = { AP_RST_AXI_REG, AP_RST_AXI_VOC },
> +       [RST_AXI_DMA] = { AP_RST_AXI_REG, AP_RST_AXI_DMA },
> +       [RST_AXI_CONNECT] = { AP_RST_AXI_REG, AP_RST_AXI_CONNECT },
> +       [RST_AXI_VPU] = { AP_RST_AXI_REG, AP_RST_AXI_VPU },
> +
> +       /* GCG */
> +       [RST_GCG_GOUDA] = { AP_RST_GCG_REG, AP_RST_GCG_GOUDA },
> +       [RST_GCG_CAMERA] = { AP_RST_GCG_REG, AP_RST_GCG_CAMERA },
> +       [RST_GCG_LCDC] = { AP_RST_GCG_REG, AP_RST_GCG_LCDC },
> +
> +       /* AHB1 */
> +       [RST_AHB1_USBC] = { AP_RST_AHB1_REG, AP_RST_AHB1_USBC },
> +       [RST_AHB1_SPIFLASH] = { AP_RST_AHB1_REG, AP_RST_AHB1_SPIFLASH },
> +
> +       /* APB1 */
> +       [RST_APB1_TIMER] = { AP_RST_APB1_REG, AP_RST_APB1_TIMER },
> +       [RST_APB1_KEYPAD] = { AP_RST_APB1_REG, AP_RST_APB1_KEYPAD },
> +       [RST_APB1_GPIO] = { AP_RST_APB1_REG, AP_RST_APB1_GPIO },
> +       [RST_APB1_PWM] = { AP_RST_APB1_REG, AP_RST_APB1_PWM },
> +       [RST_APB1_AIF] = { AP_RST_APB1_REG, AP_RST_APB1_AIF },
> +       [RST_APB1_AUIFC] = { AP_RST_APB1_REG, AP_RST_APB1_AUIFC },
> +       [RST_APB1_I2C1] = { AP_RST_APB1_REG, AP_RST_APB1_I2C1 },
> +       [RST_APB1_I2C2] = { AP_RST_APB1_REG, AP_RST_APB1_I2C2 },
> +       [RST_APB1_I2C3] = { AP_RST_APB1_REG, AP_RST_APB1_I2C3 },
> +       [RST_APB1_COMREGS] = { AP_RST_APB1_REG, AP_RST_APB1_COMREGS },
> +       [RST_APB1_DMC] = { AP_RST_APB1_REG, AP_RST_APB1_DMC },
> +       [RST_APB1_DDRPHY_P] = { AP_RST_APB1_REG, AP_RST_APB1_DDRPHY_P },
> +
> +       /* APB2 */
> +       [RST_APB2_IFC] = { AP_RST_APB2_REG, AP_RST_APB2_IFC },
> +       [RST_APB2_UART1] = { AP_RST_APB2_REG, AP_RST_APB2_UART1 },
> +       [RST_APB2_UART2] = { AP_RST_APB2_REG, AP_RST_APB2_UART2 },
> +       [RST_APB2_UART3] = { AP_RST_APB2_REG, AP_RST_APB2_UART3 },
> +       [RST_APB2_SPI1] = { AP_RST_APB2_REG, AP_RST_APB2_SPI1 },
> +       [RST_APB2_SPI2] = { AP_RST_APB2_REG, AP_RST_APB2_SPI2 },
> +       [RST_APB2_SPI3] = { AP_RST_APB2_REG, AP_RST_APB2_SPI3 },
> +       [RST_APB2_SDMMC1] = { AP_RST_APB2_REG, AP_RST_APB2_SDMMC1 },
> +       [RST_APB2_SDMMC2] = { AP_RST_APB2_REG, AP_RST_APB2_SDMMC2 },
> +       [RST_APB2_SDMMC3] = { AP_RST_APB2_REG, AP_RST_APB2_SDMMC3 },
> +       [RST_APB2_NAND] = { AP_RST_APB2_REG, AP_RST_APB2_NANDFLASH },
> +
> +       /* MEM */
> +       [RST_MEM_GPU] = { AP_RST_MEM_REG, AP_RST_MEM_GPU },
> +       [RST_MEM_VPU] = { AP_RST_MEM_REG, AP_RST_MEM_VPU },
> +       [RST_MEM_DMC] = { AP_RST_MEM_REG, AP_RST_MEM_DMC },
> +       [RST_MEM_DDRPHY_P] = { AP_RST_MEM_REG, AP_RST_MEM_DDRPHY_P },
> +};
> +
> +static int rda8810_reset_assert(struct reset_controller_dev *rstctl, unsigned long id)
> +{
> +       struct rda_clk_priv *priv = to_rda_rst(rstctl);
> +
> +       return regmap_write(priv->regmap, rda8810_rst_data[id].reg, rda8810_rst_data[id].bit);
> +}
> +
> +static int rda8810_reset_deassert(struct reset_controller_dev *rstctl, unsigned long id)
> +{
> +       struct rda_clk_priv *priv = to_rda_rst(rstctl);
> +
> +       return regmap_write(priv->regmap, rda8810_rst_data[id].reg + 4, rda8810_rst_data[id].bit);
> +}
> +
> +static const struct reset_control_ops rda8810_rst_ops = {
> +       .assert = &rda8810_reset_assert,
> +       .deassert = &rda8810_reset_deassert,
> +};
> +
> +static int rda8810_clk_register(struct rda_clk_priv *priv)
> +{
> +       struct device *dev = priv->dev;
> +       struct clk_hw_onecell_data *onecell_data;
> +       int ret;
> +       int i;
> +
> +       onecell_data = devm_kzalloc(dev,
> +                       struct_size(onecell_data, hws, ARRAY_SIZE(rda8810_clk_list)),
> +                       GFP_KERNEL);
> +       if (!onecell_data)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < ARRAY_SIZE(rda8810_clk_list); i++) {
> +               rda8810_clk_list[i]->priv = priv;
> +
> +               ret = devm_clk_hw_register(dev, &rda8810_clk_list[i]->hw);
> +               if (ret) {
> +                       dev_err(dev, "Failed to register clock: %d\n", ret);
> +                       return ret;
> +               }
> +               onecell_data->hws[i] = &rda8810_clk_list[i]->hw;
> +       }
> +       onecell_data->num = i;
> +       priv->onecell = onecell_data;
> +
> +       return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, onecell_data);
> +}
> +
> +static int rda8810_rst_register(struct rda_clk_priv *priv)
> +{
> +       struct device *dev = priv->dev;
> +
> +       priv->rstctl.dev = priv->dev;
> +       priv->rstctl.nr_resets = RST_COUNT;
> +       priv->rstctl.of_node = priv->dev->of_node;
> +       priv->rstctl.ops = &rda8810_rst_ops;
> +       priv->rstctl.owner = THIS_MODULE;
> +
> +       return devm_reset_controller_register(dev, &priv->rstctl);

Please use auxiliary bus and put the reset driver in drivers/reset/

> +}
> +
> +static int rda8810_clk_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct rda_clk_priv *priv;
> +       int ret;
> +
> +       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return dev_err_probe(dev, -ENOMEM, "Cannot allocate memory\n");

No error messages for allocation failures please. The allocator already
has a more informative message.

> +
> +       priv->dev = dev;
> +
> +       priv->regmap = syscon_node_to_regmap(dev->of_node);

Why is it a syscon?

> +       if (IS_ERR(priv->regmap))
> +               return dev_err_probe(dev, -ENOMEM, "Cannot initialize regmap\n");
> +
> +       ret = rda8810_clk_register(priv);
> +       if (ret)
> +               return dev_err_probe(dev, -EINVAL, "Failed to setup clock: %d\n", ret);
> +
> +       ret = rda8810_rst_register(priv);
> +       if (ret)
> +               return dev_err_probe(dev, -EINVAL, "Failed to setup reset: %d\n", ret);
> +
> +       platform_set_drvdata(pdev, priv);
> +
> +       return 0;

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH 06/25] rtc: Add driver for RDA Micro SoC
  2025-09-16 20:25 ` [PATCH 06/25] rtc: Add driver for RDA Micro SoC Dang Huynh via B4 Relay
  2025-09-19 13:59   ` kernel test robot
@ 2025-11-06 22:42   ` Alexandre Belloni
  1 sibling, 0 replies; 42+ messages in thread
From: Alexandre Belloni @ 2025-11-06 22:42 UTC (permalink / raw)
  To: dang.huynh
  Cc: Manivannan Sadhasivam, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski,
	Michael Turquette, Stephen Boyd, Philipp Zabel, Sebastian Reichel,
	Vinod Koul, Kees Cook, Gustavo A. R. Silva, Ulf Hansson,
	linux-arm-kernel, linux-unisoc, devicetree, linux-kernel,
	linux-gpio, linux-rtc, linux-clk, linux-pm, dmaengine,
	linux-hardening, linux-mmc

Hello,

There are checkpatch --strict issues, please fix them.


On 17/09/2025 03:25:03+0700, Dang Huynh via B4 Relay wrote:
>  MAINTAINERS           |   6 +
>  drivers/rtc/Kconfig   |  11 ++
>  drivers/rtc/Makefile  |   1 +
>  drivers/rtc/rtc-rda.c | 356 ++++++++++++++++++++++++++++++++++++++++++++++++++

Unless you can guarantee this driver will support all the future RDA
SoC RTCs, the filename needs to be SoC specific.

> +config RTC_DRV_RDA
> +	tristate "RDA Micro RTC"
> +	depends on ARCH_RDA || COMPILE_TEST
> +	select REGMAP_MMIO
> +	help
> +	  If you say yes here you get support for the built-in RTC on
> +	  RDA Micro SoC.

You probably also need to list which ones are supported.

> +static int rda_rtc_settime(struct device *dev, struct rtc_time *tm)
> +{
> +	struct rda_rtc *rtc = dev_get_drvdata(dev);
> +	u32 high, low;
> +	int ret;
> +
> +	ret = rtc_valid_tm(tm);
> +	if (ret < 0)
> +		return ret;

The RTC core will never pass an invalid rtc_tm, this check is useless.

> +
> +	/*
> +	 * The number of years since 1900 in kernel,
> +	 * but it is defined since 2000 by HW.
> +	 * The number of mons' range is from 0 to 11 in kernel,
> +	 * but it is defined from 1 to 12 by HW.

This comment is not super useful as this is super common in the RTC
drivers,. If you want to keep it, please fix it.

> +	 */
> +	low = FIELD_PREP(RDA_SEC_MASK, tm->tm_sec) |
> +		FIELD_PREP(RDA_MIN_MASK, tm->tm_min) |
> +		FIELD_PREP(RDA_HRS_MASK, tm->tm_hour);
> +
> +	high = FIELD_PREP(RDA_MDAY_MASK, tm->tm_mday) |
> +		FIELD_PREP(RDA_MON_MASK, tm->tm_mon + 1) |
> +		FIELD_PREP(RDA_YEAR_MASK, tm->tm_year - 100) |
> +		FIELD_PREP(RDA_WDAY_MASK, tm->tm_wday);
> +
> +	ret = regmap_write(rtc->regmap, RDA_RTC_CAL_LOAD_LOW_REG, low);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to update RTC low register: %d\n", ret);

This needs to be a dev_dbg or removed.

> +		return ret;
> +	}
> +
> +	ret = regmap_write(rtc->regmap, RDA_RTC_CAL_LOAD_HIGH_REG, high);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to update RTC low register: %d\n", ret);

Ditto

> +		return ret;
> +	}
> +
> +	ret = regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG, RDA_RTC_CMD_CAL_LOAD, 1);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to update RTC cal load register: %d\n", ret);

Ditto

> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rda_rtc_readtime(struct device *dev, struct rtc_time *tm)
> +{
> +	struct rda_rtc *rtc = dev_get_drvdata(dev);
> +	unsigned int high, low;
> +	int ret;
> +
> +	/*
> +	 * Check if RTC data is valid.
> +	 *
> +	 * When this bit is set, it means the data in the RTC is invalid
> +	 * or not configured.
> +	 */
> +	ret = regmap_test_bits(rtc->regmap, RDA_RTC_STA_REG, RDA_RTC_STA_NOT_PROG);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to read RTC status: %d\n", ret);

dev_dbg

> +		return ret;
> +	} else if (ret > 0)
> +		return -EINVAL;
> +
> +	ret = regmap_read(rtc->regmap, RDA_RTC_CUR_LOAD_HIGH_REG, &high);
> +	if (ret) {
> +		dev_err(dev, "Failed to read RTC high reg: %d\n", ret);

Ditto

> +		return ret;
> +	}
> +
> +	ret = regmap_read(rtc->regmap, RDA_RTC_CUR_LOAD_LOW_REG, &low);
> +	if (ret) {
> +		dev_err(dev, "Failed to read RTC low reg: %d\n", ret);

Ditto

> +		return ret;
> +	}
> +
> +	tm->tm_sec = FIELD_GET(RDA_SEC_MASK, low);
> +	tm->tm_min = FIELD_GET(RDA_MIN_MASK, low);
> +	tm->tm_hour = FIELD_GET(RDA_HRS_MASK, low);
> +	tm->tm_mday = FIELD_GET(RDA_MDAY_MASK, high);
> +	tm->tm_mon = FIELD_GET(RDA_MON_MASK, high);
> +	tm->tm_year = FIELD_GET(RDA_YEAR_MASK, high);
> +	tm->tm_wday = FIELD_GET(RDA_WDAY_MASK, high);
> +
> +	/*
> +	 * The number of years since 1900 in kernel,
> +	 * but it is defined since 2000 by HW.
> +	 */
> +	tm->tm_year += 100;
> +	/*
> +	 * The number of mons' range is from 0 to 11 in kernel,
> +	 * but it is defined from 1 to 12 by HW.
> +	 */

You can probably drop both comments.

> +	tm->tm_mon -= 1;
> +
> +	return 0;
> +}
> +
> +static int rda_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +	struct rda_rtc *rtc = dev_get_drvdata(dev);
> +	struct rtc_time *tm = &alrm->time;
> +	unsigned int high, low;
> +	int ret;
> +
> +	ret = regmap_read(rtc->regmap, RDA_RTC_ALARM_HIGH_REG, &high);
> +	if (ret) {
> +		dev_err(dev, "Failed to read alarm low reg: %d\n", ret);

Just to be clear, the driver is super verbose with all those dev_err.
Strings are bloating the kernel and those string will probably never be
seen by any user and event if they are seen, the user doesn't have any
other action to do other than retrying. Please remove them of move them
to dev_dbg

> +		return ret;
> +	}
> +
> +	ret = regmap_read(rtc->regmap, RDA_RTC_ALARM_LOW_REG, &low);
> +	if (ret) {
> +		dev_err(dev, "Failed to read alarm low reg: %d\n", ret);
> +		return ret;
> +	}
> +
> +	tm->tm_sec = FIELD_GET(RDA_SEC_MASK, low);
> +	tm->tm_min = FIELD_GET(RDA_MIN_MASK, low);
> +	tm->tm_hour = FIELD_GET(RDA_HRS_MASK, low);
> +	tm->tm_mday = FIELD_GET(RDA_MDAY_MASK, high);
> +	tm->tm_mon = FIELD_GET(RDA_MON_MASK, high);
> +	tm->tm_year = FIELD_GET(RDA_YEAR_MASK, high);
> +	tm->tm_wday = FIELD_GET(RDA_WDAY_MASK, high);
> +
> +	/*
> +	 * The number of years since 1900 in kernel,
> +	 * but it is defined since 2000 by HW.
> +	 */
> +	tm->tm_year += 100;
> +	/*
> +	 * The number of mons' range is from 0 to 11 in kernel,
> +	 * but it is defined from 1 to 12 by HW.
> +	 */
> +	tm->tm_mon -= 1;
> +
> +	return 0;
> +}
> +
> +static int rda_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
> +{
> +	struct rda_rtc *rtc = dev_get_drvdata(dev);
> +
> +	if (enabled)
> +		return regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG,
> +				RDA_RTC_CMD_ALARM_ENABLE, 1);
> +
> +	return regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG,
> +			RDA_RTC_CMD_ALARM_DISABLE, 1);

Wow, this is super weird, so you have one bit to enable and one to
disable the alarm. Is RDA_RTC_CMD_REG write only?

> +}
> +
> +static int rda_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +	struct rda_rtc *rtc = dev_get_drvdata(dev);
> +	struct rtc_time *tm = &alrm->time;
> +	u32 high, low;
> +	int ret;
> +
> +	ret = rtc_valid_tm(tm);
> +	if (ret < 0)
> +		return ret;
> +

tm will never be invalid

> +	/* TODO: Check if it's necessary to disable IRQ first */

I'd say probably not ;)

> +	rda_rtc_alarm_irq_enable(dev, 0);
> +
> +	/*
> +	 * The number of years since 1900 in kernel,
> +	 * but it is defined since 2000 by HW.
> +	 * The number of mons' range is from 0 to 11 in kernel,
> +	 * but it is defined from 1 to 12 by HW.
> +	 */

This is still the same comment...

> +	low = FIELD_PREP(RDA_SEC_MASK, tm->tm_sec) |
> +		FIELD_PREP(RDA_MIN_MASK, tm->tm_min) |
> +		FIELD_PREP(RDA_HRS_MASK, tm->tm_hour);
> +
> +	high = FIELD_PREP(RDA_MDAY_MASK, tm->tm_mday) |
> +		FIELD_PREP(RDA_MON_MASK, tm->tm_mon + 1) |
> +		FIELD_PREP(RDA_YEAR_MASK, tm->tm_year - 100) |
> +		FIELD_PREP(RDA_WDAY_MASK, tm->tm_wday);
> +
> +
> +	ret = regmap_write(rtc->regmap, RDA_RTC_ALARM_LOW_REG, low);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set low alarm register: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = regmap_write(rtc->regmap, RDA_RTC_ALARM_HIGH_REG, high);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set low alarm register: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = regmap_update_bits(rtc->regmap, RDA_RTC_CMD_REG, RDA_RTC_CMD_ALARM_LOAD, 1);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set alarm register: %d\n", ret);
> +		return ret;
> +	}
> +
> +	dev_dbg(dev, "Alarm set: %4d-%02d-%02d %02d:%02d:%02d\n",
> +			2000 + (tm->tm_year - 100), tm->tm_mon + 1, tm->tm_mday,
> +			tm->tm_hour, tm->tm_min, tm->tm_sec);

You probably want to use %ptR or drop this as we have a tracepoint just
after.

> +
> +	return 0;
> +}
> +
> +static int rda_rtc_proc(struct device *dev, struct seq_file *seq)
> +{
> +	struct rda_rtc *rtc = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = regmap_test_bits(rtc->regmap, RDA_RTC_STA_REG, RDA_RTC_STA_ALARM_ENABLE);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to read alarm status: %d\n", ret);
> +		return ret;
> +	}
> +
> +	seq_printf(seq, "alarm enable\t: %s\n", (ret > 0) ? "yes" : "no");
> +
> +	return 0;
> +}

Drop this function, this interface is obsolete

> +
> +static const struct rtc_class_ops rda_rtc_ops = {
> +	.read_time = rda_rtc_readtime,
> +	.set_time = rda_rtc_settime,
> +	.read_alarm = rda_rtc_readalarm,
> +	.set_alarm = rda_rtc_setalarm,
> +	.proc = rda_rtc_proc,
> +	.alarm_irq_enable = rda_rtc_alarm_irq_enable,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int rda_rtc_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +	/* TODO: Check if it's okay to turn on alarm IRQ when it's not set */
> +	return rda_rtc_alarm_irq_enable(&pdev->dev, 1);
> +}
> +
> +static int rda_rtc_resume(struct platform_device *pdev)
> +{
> +	/* If alarms were left, we turn them off. */
> +	return rda_rtc_alarm_irq_enable(&pdev->dev, 0);
> +}

Let userspace enabling/disabling alarm, the kernel must not decide to
enable or disable them which fixes your TODO

> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(rda_rtc_pm_ops, rda_rtc_suspend, rda_rtc_resume);
> +
> +static const struct regmap_config regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static int rda_rtc_probe(struct platform_device *pdev)
> +{
> +	struct rda_rtc *rda_rtc;
> +	void __iomem *base;
> +
> +	rda_rtc = devm_kzalloc(&pdev->dev, sizeof(*rda_rtc), GFP_KERNEL);
> +	if (!rda_rtc)
> +		return -ENOMEM;
> +
> +	base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(base))
> +		return dev_err_probe(&pdev->dev, PTR_ERR(base),
> +				"failed to remap resource\n");
> +
> +	rda_rtc->regmap = devm_regmap_init_mmio(&pdev->dev, base, &regmap_config);
> +	if (!rda_rtc->regmap)
> +		return dev_err_probe(&pdev->dev, PTR_ERR(rda_rtc->regmap),
> +				"can't find regmap\n");
> +
> +	rda_rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
> +	if (IS_ERR(rda_rtc->rtc_dev))
> +		return dev_err_probe(&pdev->dev, PTR_ERR(rda_rtc->rtc_dev),
> +				"failed to allocate rtc device\n");
> +
> +	rda_rtc->rtc_dev->ops = &rda_rtc_ops;
> +	rda_rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
> +	rda_rtc->rtc_dev->range_max = RTC_TIMESTAMP_END_2127;
> +
> +	platform_set_drvdata(pdev, rda_rtc);
> +
> +	return devm_rtc_register_device(rda_rtc->rtc_dev);
> +}
> +
> +static const struct of_device_id rda_rtc_id_table[] = {
> +	{ .compatible = "rda,8810pl-rtc", },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, rda_rtc_id_table);
> +
> +static struct platform_driver rda_rtc_driver = {
> +	.probe = rda_rtc_probe,
> +	.driver = {
> +		.name = "rtc-rda",
> +		.pm = &rda_rtc_pm_ops,
> +		.of_match_table = rda_rtc_id_table,
> +	},
> +};
> +module_platform_driver(rda_rtc_driver);
> +
> +MODULE_AUTHOR("Dang Huynh <dang.huynh@mainlining.org>");
> +MODULE_DESCRIPTION("RDA Micro RTC driver");
> +MODULE_LICENSE("GPL");
> 
> -- 
> 2.51.0
> 
> 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2025-11-06 22:43 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-16 20:24 [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Dang Huynh via B4 Relay
2025-09-16 20:24 ` [PATCH 01/25] ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes Dang Huynh via B4 Relay
2025-09-17  0:39   ` Krzysztof Kozlowski
2025-09-16 20:24 ` [PATCH 02/25] drivers: gpio: rda: Make IRQ optional Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 03/25] dt-bindings: gpio: rda: Make interrupts optional Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 04/25] rtc: Add timestamp for the end of 2127 Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 05/25] dt-bindings: rtc: Add RDA Micro RDA8810PL RTC Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 06/25] rtc: Add driver for RDA Micro SoC Dang Huynh via B4 Relay
2025-09-19 13:59   ` kernel test robot
2025-11-06 22:42   ` Alexandre Belloni
2025-09-16 20:25 ` [PATCH 07/25] ARM: dts: unisoc: rda8810pl: Enable Real-Time Clock Dang Huynh via B4 Relay
2025-09-17  0:40   ` Krzysztof Kozlowski
2025-09-16 20:25 ` [PATCH 08/25] ARM: dts: unisoc: rda8810pl: Enable ARM PMU Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 09/25] dt-bindings: clock: Add RDA Micro RDA8810PL clock/reset controller Dang Huynh via B4 Relay
2025-09-17  0:43   ` Krzysztof Kozlowski
2025-09-16 20:25 ` [PATCH 10/25] drivers: clk: Add Clock and Reset Driver for RDA Micro RDA8810PL SoC Dang Huynh via B4 Relay
2025-09-20  4:50   ` Stephen Boyd
2025-09-16 20:25 ` [PATCH 11/25] dts: unisoc: rda8810pl: Enable clock/reset driver Dang Huynh via B4 Relay
2025-09-17  0:41   ` Krzysztof Kozlowski
2025-09-16 20:25 ` [PATCH 12/25] dts: unisoc: rda8810pl: Add OPP for CPU and define L2 cache Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 13/25] dts: unisoc: orangepi: Disable UART with no users Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 14/25] dt-bindings: power: reset: Add RDA Micro Modem Reset Dang Huynh via B4 Relay
2025-09-17  0:44   ` Krzysztof Kozlowski
2025-09-16 20:25 ` [PATCH 15/25] power: reset: Add basic power reset driver for RDA8810PL Dang Huynh via B4 Relay
2025-09-17  0:45   ` Krzysztof Kozlowski
2025-09-16 20:25 ` [PATCH 16/25] dts: unisoc: rda8810pl: Enable modem reset Dang Huynh via B4 Relay
2025-09-17  0:46   ` Krzysztof Kozlowski
2025-09-16 20:25 ` [PATCH 17/25] drivers: gpio: rda: Make direction register unreadable Dang Huynh via B4 Relay
2025-09-17  8:00   ` Bartosz Golaszewski
2025-09-16 20:25 ` [PATCH 18/25] dt-bindings: dma: Add RDA IFC DMA Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 19/25] dmaengine: Add RDA IFC driver Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 20/25] dts: unisoc: rda8810pl: Enable IFC Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 21/25] dt-bindings: mmc: Add RDA SDMMC controller Dang Huynh via B4 Relay
2025-09-17  0:00   ` Krzysztof Kozlowski
2025-09-16 20:25 ` [PATCH 22/25] mmc: host: Add RDA Micro SD/MMC driver Dang Huynh via B4 Relay
2025-09-17  0:48   ` Krzysztof Kozlowski
2025-09-16 20:25 ` [PATCH 23/25] dts: unisoc: rda8810pl: Add SDMMC controllers Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 24/25] dts: unisoc: orangepi-2g: Enable SD Card Dang Huynh via B4 Relay
2025-09-16 20:25 ` [PATCH 25/25] dts: unisoc: orangepi-i96: " Dang Huynh via B4 Relay
2025-09-17 10:03 ` [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver Manivannan Sadhasivam
2025-09-18  5:02   ` Dang Huynh
  -- strict thread matches above, loose matches on Subject: below --
2025-09-16 20:07 Dang Huynh
2025-09-16 20:07 ` [PATCH 06/25] rtc: Add driver for RDA Micro SoC Dang Huynh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).