lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250917-rda8810pl-drivers-v1-10-9ca9184ca977@mainlining.org>
Date: Wed, 17 Sep 2025 03:25:07 +0700
From: Dang Huynh via B4 Relay <devnull+dang.huynh.mainlining.org@...nel.org>
To: Manivannan Sadhasivam <mani@...nel.org>, Rob Herring <robh@...nel.org>, 
 Krzysztof Kozlowski <krzk+dt@...nel.org>, 
 Conor Dooley <conor+dt@...nel.org>, 
 Linus Walleij <linus.walleij@...aro.org>, 
 Bartosz Golaszewski <brgl@...ev.pl>, 
 Alexandre Belloni <alexandre.belloni@...tlin.com>, 
 Michael Turquette <mturquette@...libre.com>, 
 Stephen Boyd <sboyd@...nel.org>, Philipp Zabel <p.zabel@...gutronix.de>, 
 Sebastian Reichel <sre@...nel.org>, Vinod Koul <vkoul@...nel.org>, 
 Kees Cook <kees@...nel.org>, "Gustavo A. R. Silva" <gustavoars@...nel.org>, 
 Ulf Hansson <ulf.hansson@...aro.org>
Cc: linux-arm-kernel@...ts.infradead.org, linux-unisoc@...ts.infradead.org, 
 devicetree@...r.kernel.org, linux-kernel@...r.kernel.org, 
 linux-gpio@...r.kernel.org, linux-rtc@...r.kernel.org, 
 linux-clk@...r.kernel.org, linux-pm@...r.kernel.org, 
 dmaengine@...r.kernel.org, linux-hardening@...r.kernel.org, 
 linux-mmc@...r.kernel.org, Dang Huynh <dang.huynh@...nlining.org>
Subject: [PATCH 10/25] drivers: clk: Add Clock and Reset Driver for RDA
 Micro RDA8810PL SoC

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

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

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

-- 
2.51.0



Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ