* Re: [PATCH 15/25] power: reset: Add basic power reset driver for RDA8810PL
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-15-9ca9184ca977@mainlining.org>
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
* Re: [PATCH 14/25] dt-bindings: power: reset: Add RDA Micro Modem Reset
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-14-9ca9184ca977@mainlining.org>
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
* Re: [PATCH 09/25] dt-bindings: clock: Add RDA Micro RDA8810PL clock/reset controller
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-9-9ca9184ca977@mainlining.org>
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
* Re: [PATCH 11/25] dts: unisoc: rda8810pl: Enable clock/reset driver
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-11-9ca9184ca977@mainlining.org>
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
* Re: [PATCH 07/25] ARM: dts: unisoc: rda8810pl: Enable Real-Time Clock
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-7-9ca9184ca977@mainlining.org>
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
* Re: [PATCH 01/25] ARM: dts: unisoc: rda8810pl: Add label to GPIO nodes
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-1-9ca9184ca977@mainlining.org>
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
* Re: [PATCH 21/25] dt-bindings: mmc: Add RDA SDMMC controller
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-21-9ca9184ca977@mainlining.org>
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
* Re: (subset) [PATCH v13 0/7] spacemit: introduce P1 PMIC support
From: Mark Brown @ 2025-09-16 21:54 UTC (permalink / raw)
To: lee, lgirdwood, alexandre.belloni, robh, krzk+dt, conor+dt,
Alex Elder
Cc: mat.jonczyk, dlan, paul.walmsley, palmer, aou, alex, linux.amoon,
troymitchell988, guodong, linux-rtc, devicetree, linux-riscv,
spacemit, linux-kernel
In-Reply-To: <20250825172057.163883-1-elder@riscstar.com>
On Mon, 25 Aug 2025 12:20:49 -0500, Alex Elder wrote:
> The SpacemiT P1 is an I2C-controlled PMIC that implements 6 buck
> converters and 12 LDOs. It contains a load switch, ADC channels,
> GPIOs, a real-time clock, and a watchdog timer.
>
> This series introduces a multifunction driver for the P1 PMIC as
> well as drivers for its regulators and RTC.
>
> [...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git for-next
Thanks!
[3/7] regulator: spacemit: support SpacemiT P1 regulators
commit: 8b84d712ad849172f6bbcad57534b284d942b0b5
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
^ permalink raw reply
* Re: [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver
From: Dang Huynh @ 2025-09-16 20:33 UTC (permalink / raw)
To: Alexandre Belloni
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
In-Reply-To: <2025091620264900a32db6@mail.local>
Hi,
On Tuesday, September 16, 2025 8:26:49 PM Coordinated Universal Time Alexandre
Belloni wrote:
> Hello,
>
> On 17/09/2025 03:07:17+0700, Dang Huynh wrote:
> > This patch series aims to add support for Clock/Reset, Real-Time Clock and
> > SDMMC on the RDA Micro RDA8810PL platform.
>
> What are the dependencies between the various drivers? Ho< do you expect
> the patches to be merged?
The MMC driver depends on the clock controller (for getting APB2 rates) and
IFC for transferring data.
>
> If the RTC patches are independent, please send them separately.
They're independent. Noted.
>
> > 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,
Best regards,
Dang
^ permalink raw reply
* Re: [PATCH 00/25] RDA8810PL Clock, RTC and MMC driver
From: Alexandre Belloni @ 2025-09-16 20:26 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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-74866def1fe3@mainlining.org>
Hello,
On 17/09/2025 03:07:17+0700, Dang Huynh wrote:
> This patch series aims to add support for Clock/Reset, Real-Time Clock and
> SDMMC on the RDA Micro RDA8810PL platform.
>
What are the dependencies between the various drivers? Ho< do you expect
the patches to be merged?
If the RTC patches are independent, please send them separately.
> 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>
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH 25/25] dts: unisoc: orangepi-i96: Enable SD Card
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 24/25] dts: unisoc: orangepi-2g: Enable SD Card
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 21/25] dt-bindings: mmc: Add RDA SDMMC controller
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 14/25] dt-bindings: power: reset: Add RDA Micro Modem Reset
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 19/25] dmaengine: Add RDA IFC driver
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 17/25] drivers: gpio: rda: Make direction register unreadable
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 18/25] dt-bindings: dma: Add RDA IFC DMA
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 23/25] dts: unisoc: rda8810pl: Add SDMMC controllers
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 16/25] dts: unisoc: rda8810pl: Enable modem reset
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 22/25] mmc: host: Add RDA Micro SD/MMC driver
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 15/25] power: reset: Add basic power reset driver for RDA8810PL
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 20/25] dts: unisoc: rda8810pl: Enable IFC
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 12/25] dts: unisoc: rda8810pl: Add OPP for CPU and define L2 cache
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 13/25] dts: unisoc: orangepi: Disable UART with no users
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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
* [PATCH 10/25] drivers: clk: Add Clock and Reset Driver for RDA Micro RDA8810PL SoC
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
In-Reply-To: <20250917-rda8810pl-drivers-v1-0-9ca9184ca977@mainlining.org>
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, ®, &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, ®);
+ 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
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox