mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jisheng Zhang <jszhang@kernel.org>
To: Rob Herring <robh@kernel.org>, Conor Dooley <conor+dt@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Catalin Marinas <catalin.marinas@arm.com>,
	Will Deacon <will@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>,
	Stephen Boyd <sboyd@kernel.org>,
	Brian Masney <bmasney+clk@redhat.com>,
	Jerome Brunet <jbrunet+clk@baylibre.com>,
	Linus Walleij <linusw@kernel.org>,
	Philipp Zabel <p.zabel@pengutronix.de>
Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, linux-clk@vger.kernel.org,
	linux-gpio@vger.kernel.org
Subject: [PATCH v2 10/13] clk: berlin: add Synaptics SL2610 clocks and PLLs
Date: Sun, 15 Nov 2026 18:23:49 +0800	[thread overview]
Message-ID: <20261115102401.3520-11-jszhang@kernel.org> (raw)
In-Reply-To: <20261115102401.3520-1-jszhang@kernel.org>

Support the SL2610 SoC, CPU, AVIO and MCU clocks. Register the PLL
outputs and the divider, gate and fixed-factor clocks used by the CPU
and peripherals.

Match MFD platform devices and register clock providers on their parent
system controller nodes.

Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
---
 MAINTAINERS                     |   1 +
 drivers/clk/berlin/Kconfig      |  27 +++
 drivers/clk/berlin/Makefile     |   2 +
 drivers/clk/berlin/clk-sl2610.c | 360 ++++++++++++++++++++++++++++++++
 drivers/clk/berlin/sl2610-pll.c | 145 +++++++++++++
 drivers/clk/berlin/sl2610-pll.h |  14 ++
 6 files changed, 549 insertions(+)
 create mode 100644 drivers/clk/berlin/clk-sl2610.c
 create mode 100644 drivers/clk/berlin/sl2610-pll.c
 create mode 100644 drivers/clk/berlin/sl2610-pll.h

diff --git a/MAINTAINERS b/MAINTAINERS
index f3067f71d54c..430758242906 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3812,6 +3812,7 @@ F:	Documentation/devicetree/bindings/soc/synaptics/
 F:	arch/arm/boot/dts/synaptics/
 F:	arch/arm/mach-berlin/
 F:	arch/arm64/boot/dts/synaptics/
+F:	drivers/clk/berlin/*sl2610*
 F:	drivers/clk/berlin/sl-*
 F:	drivers/pinctrl/berlin/pinctrl-sl2610.c
 F:	drivers/reset/reset-sl2610.c
diff --git a/drivers/clk/berlin/Kconfig b/drivers/clk/berlin/Kconfig
index 8f71d4c7a478..be7e47e0ff52 100644
--- a/drivers/clk/berlin/Kconfig
+++ b/drivers/clk/berlin/Kconfig
@@ -5,3 +5,30 @@ config SL_COMMON_CLK
 
 config SL_SYSMGR_CLK
 	bool
+
+config SL2610_PLL
+	tristate "Synaptics SL2610 PLL Driver"
+	depends on ARCH_BERLIN || COMPILE_TEST
+	default n
+	help
+	  Support the integer and fractional PLL outputs used by the SoC,
+	  CPU and audio clock controllers on Synaptics SL2610 SoCs.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called sl2610-pll.
+
+config SL2610_CLK
+	tristate "Synaptics SL2610 Clock Driver"
+	depends on ARCH_BERLIN || COMPILE_TEST
+	depends on OF && HAS_IOMEM
+	select SL2610_PLL
+	select SL_COMMON_CLK
+	select SL_SYSMGR_CLK
+	select SOC_SL2610_SYSCON
+	help
+	  This driver provides support for clock controller on Synaptics SL2610
+	  SoC. The clock controller generates and supplies clocks to various
+	  peripherals within the SoC.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called clk-sl2610.
diff --git a/drivers/clk/berlin/Makefile b/drivers/clk/berlin/Makefile
index 5dbd0fd264ad..ec87dbed7dd4 100644
--- a/drivers/clk/berlin/Makefile
+++ b/drivers/clk/berlin/Makefile
@@ -5,3 +5,5 @@ obj-$(CONFIG_MACH_BERLIN_BG2CD)	+= bg2.o
 obj-$(CONFIG_MACH_BERLIN_BG2Q)	+= bg2q.o
 obj-$(CONFIG_SL_COMMON_CLK)	+= sl-clk.o
 obj-$(CONFIG_SL_SYSMGR_CLK)	+= sl-sysmgr-clk.o
+obj-$(CONFIG_SL2610_PLL)	+= sl2610-pll.o
+obj-$(CONFIG_SL2610_CLK)	+= clk-sl2610.o
diff --git a/drivers/clk/berlin/clk-sl2610.c b/drivers/clk/berlin/clk-sl2610.c
new file mode 100644
index 000000000000..b51d3523028d
--- /dev/null
+++ b/drivers/clk/berlin/clk-sl2610.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Synaptics Incorporated
+ *
+ * Author: Jisheng Zhang <jszhang@kernel.org>
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/module.h>
+
+#include <dt-bindings/clock/syna,sl2610.h>
+
+#include "sl2610-pll.h"
+#include "sl-clk.h"
+#include "sl-sysmgr-clk.h"
+
+#define REG_CLKENABLE		0x0420
+
+#define CLK_GATE_CTRL0		0x0600
+
+#define SYSPLL0_OFFSET		0x0200
+#define SYSPLL1_OFFSET		0x0220
+#define CPUPLL_OFFSET		0x2000
+#define APLL0_OFFSET		0x0008
+#define APLL1_OFFSET		0x0030
+
+static DEFINE_SPINLOCK(gate_lock);
+static DEFINE_SPINLOCK(lock);
+
+enum {
+	SYSPLL0_OUT, SYSPLL0_OUTF,
+	SYSPLL1_OUT, SYSPLL1_OUTF,
+	CPUPLL_OUT, CPUPLL_OUTF,
+	APLL0_OUT, APLL0_OUTF,
+	APLL1_OUT, APLL1_OUTF,
+};
+
+static const char * const clk_names[] = {
+	[SYSPLL0_OUT]		= "syspll0_out",
+	[SYSPLL0_OUTF]		= "syspll0_outf",
+	[SYSPLL1_OUT]		= "syspll1_out",
+	[SYSPLL1_OUTF]		= "syspll1_outf",
+	[CPUPLL_OUT]		= "cpupll_out",
+	[CPUPLL_OUTF]		= "cpupll_outf",
+	[APLL0_OUT]		= "apll0_out",
+	[APLL0_OUTF]		= "apll0_outf",
+	[APLL1_OUT]		= "apll1_out",
+	[APLL1_OUTF]		= "apll1_outf",
+};
+
+static const struct sl_gateclk_desc sl2610_gates[] = {
+	{ "usb0coreclk",	"perifsysclk",	CLK_USB0CORE,		0 },
+	{ "sdiosysclk",		"perifsysclk",	CLK_SDIOSYS,		1 },
+	{ "emmcsysclk",		"perifsysclk",	CLK_EMMCSYS,		2 },
+	{ "gpuaxiclk",		"hpcclk",	CLK_GPUAXI,		3 },
+	{ "gethrgmiisysclk",	"perifsysclk",	CLK_GETHRGMIISYS,	4 },
+	{ "sdio1sysclk",	"perifsysclk",	CLK_SDIO1SYS,		5 },
+	{ "usb1coreclk",	"perifsysclk",	CLK_USB1CORE,		6 },
+	{ "gethrgmii1sysclk",	"perifsysclk",	CLK_GETHRGMII1SYS,	7 },
+	{ "usb0phyrefclk",	"perifsysclk",	CLK_USB0PHYREF,		8 },
+	{ "usb1phyrefclk",	"perifsysclk",	CLK_USB1PHYREF,		9 },
+	{ "apbuart0clk",	"apbcoreclk",	CLK_APBUART0,		10 },
+	{ "apbuart1clk",	"apbcoreclk",	CLK_APBUART1,		11 },
+	{ "apbuart2clk",	"apbcoreclk",	CLK_APBUART2,		12 },
+	{ "apbuart3lck",	"apbcoreclk",	CLK_APBUART3,		13 },
+	{ "apbi2c0clk",		"apbcoreclk",	CLK_APBI2C0,		14 },
+	{ "apbi2c1lck",		"apbcoreclk",	CLK_APBI2C1,		15 },
+	{ "apbspi0clk",		"apbcoreclk",	CLK_APBSPI0,		16 },
+	{ "apbspi1clk",		"apbcoreclk",	CLK_APBSPI1,		17 },
+	{ "apbspi2clk",		"apbcoreclk",	CLK_APBSPI2,		18 },
+	{ "apbspi3clk",		"apbcoreclk",	CLK_APBSPI3,		19 },
+	{ "apbgpioclk",		"apbcoreclk",	CLK_APBGPIO,		20 },
+	{ "apbtimersclk",	"apbcoreclk",	CLK_APBTIMERS,		21 },
+	{ "apbsyscntclk",	"apbcoreclk",	CLK_APBSYSCNT,		22 },
+	{ "apbwdtclk",		"apbcoreclk",	CLK_APBWDT,		23 },
+	{ "apbdmaclk",		"apbcoreclk",	CLK_APBDMA,		24 },
+};
+
+static const struct sl_clk_desc sl2610_descs[] = {
+	{ "cpufastrefclk",	CLK_CPUFASTREF,		0x440, CLK_IS_CRITICAL },
+	{ "memfastrefclk",	CLK_MEMFASTREF,		0x444, CLK_IS_CRITICAL },
+	{ "cfgclk",		CLK_CFG,		0x448, CLK_IS_CRITICAL },
+	{ "sysclk",		CLK_SYS,		0x44c, CLK_IS_CRITICAL },
+	{ "perifsysclk",	CLK_PERIFSYS,		0x450, CLK_IS_CRITICAL },
+	{ "apbcoreclk",		CLK_APBCORE,		0x454, CLK_IS_CRITICAL },
+	{ "apbserclk",		CLK_APBSER,		0x458, CLK_IS_CRITICAL },
+	{ "atbclk",		CLK_ATB,		0x45c },
+	{ "hpcclk",		CLK_HPC,		0x460, CLK_IS_CRITICAL },
+	{ "emmcclk",		CLK_EMMC,		0x464 },
+	{ "sd0clk",		CLK_SD0,		0x468 },
+	{ "sd1clk",		CLK_SD1,		0x46c },
+	{ "gethrgmiiclk",	CLK_GETHRGMII,		0x470 },
+	{ "gethrgmii1clk",	CLK_GETHRGMII1,		0x474 },
+	{ "ge0_ptp_refclk",	CLK_GE0_PTP_REF,	0x478 },
+	{ "ge1_ptp_refclk",	CLK_GE1_PTP_REF,	0x47c },
+	{ "usb2testclk",	CLK_USB2TEST,		0x480 },
+	{ "usb2test480mg0clk",	CLK_USB2TEST480MG0,	0x484 },
+	{ "usb2test480mg1clk",	CLK_USB2TEST480MG1,	0x488 },
+	{ "usb2test480mg2clk",	CLK_USB2TEST480MG2,	0x48c },
+	{ "usb2test100mg0clk",	CLK_USB2TEST100MG0,	0x490 },
+	{ "usb2test100mg1clk",	CLK_USB2TEST100MG1,	0x494 },
+	{ "usb2test100mg2clk",	CLK_USB2TEST100MG2,	0x498 },
+	{ "usb2test100mg3clk",	CLK_USB2TEST100MG3,	0x49c },
+	{ "periftest125mg0clk",	CLK_PERIFTEST125MG0,	0x4a0 },
+	{ "periftest200mg0clk",	CLK_PERIFTEST200MG0,	0x4a4 },
+	{ "periftest200mg1clk",	CLK_PERIFTEST200MG1,	0x4a8 },
+	{ "gpuclk",		CLK_GPU,		0x4ac },
+	{ "npuclk",		CLK_NPU,		0x4b0 },
+	{ "aviosysclk",		CLK_AVIOSYS,		0x4b4, CLK_IS_CRITICAL },
+	{ "aiosysclk",		CLK_AIOSYS,		0x4b8, CLK_IS_CRITICAL },
+	{ "avio_lcdc2scanclk",	CLK_AVIO_LCDC2SCAN,	0x4bc },
+	{ "avio_ipiclk",	CLK_AVIO_IPI,		0x4c0 },
+	{ "avio_pclk",		CLK_AVIO_P,		0x4c4 },
+	{ "avio_dphyrxtxescclk", CLK_AVIO_DPHYRXTXESC,	0x4c8 },
+	{ "aviofpllclk",	CLK_AVIOFPLL,		0x4cc },
+	{ "avio_rx_scanbyteclk", CLK_AVIO_RX_SCANBYTE,	0x4d0 },
+	{ "avio_rx_scantestclk", CLK_AVIO_RX_SCANTEST,	0x4d4 },
+};
+
+static const char * const default_parent_names[] = {
+	"syspll0_out",
+	"apll0_out",
+	"syspll0_outf",
+	"apll0_outf",
+	"syspll1_out",
+	"syspll1_outf",
+};
+
+static const char * const ge_ptp_refclk_parent_names[] = {
+	"osc",
+	"apll0_outf",
+	"syspll1_out",
+	"syspll0_out",
+	"clk_mcu_axi",
+};
+
+static int sl2610_soc_clk_setup(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *parent_np = dev->parent->of_node;
+	struct clk_hw_onecell_data *clk_data;
+	const char * const *parent_names;
+	int num_parents;
+	void __iomem *base;
+	struct clk_hw **hws;
+	int n, ret;
+	u32 id;
+
+	base = devm_of_iomap(dev, parent_np, 0, NULL);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, SL2610_CLK_MAX), GFP_KERNEL);
+	if (!clk_data)
+		return -ENOMEM;
+	clk_data->num = SL2610_CLK_MAX;
+	hws = clk_data->hws;
+
+	/* sysplls */
+	ret = sl2610_pll_register(dev, base + SYSPLL0_OFFSET,
+				  clk_names[SYSPLL0_OUT], clk_names[SYSPLL0_OUTF]);
+	if (ret)
+		return ret;
+	ret = sl2610_pll_register(dev, base + SYSPLL1_OFFSET,
+				  clk_names[SYSPLL1_OUT], clk_names[SYSPLL1_OUTF]);
+	if (ret)
+		return ret;
+
+	/* normal clocks */
+	for (n = 0; n < ARRAY_SIZE(sl2610_descs); n++) {
+		const struct sl_clk_desc *cd = &sl2610_descs[n];
+
+		id = cd->id;
+		if (id == CLK_GE0_PTP_REF || id == CLK_GE1_PTP_REF) {
+			parent_names = ge_ptp_refclk_parent_names;
+			num_parents = ARRAY_SIZE(ge_ptp_refclk_parent_names);
+		} else {
+			parent_names = default_parent_names;
+			num_parents = ARRAY_SIZE(default_parent_names);
+		}
+		hws[id] = sl_clk_register(dev, base + cd->offset, cd->name,
+					  parent_names, num_parents, cd->flags, &lock);
+		if (IS_ERR(hws[id]))
+			return PTR_ERR(hws[id]);
+	}
+
+	/* gate clocks */
+	for (n = 0; n < ARRAY_SIZE(sl2610_gates); n++) {
+		const struct sl_gateclk_desc *gd = &sl2610_gates[n];
+
+		id = gd->id;
+		hws[id] = devm_clk_hw_register_gate(dev, gd->name, gd->parent_name,
+						    gd->flags, base + REG_CLKENABLE,
+						    gd->bit_idx, 0, &gate_lock);
+		if (IS_ERR(hws[id]))
+			return PTR_ERR(hws[id]);
+	}
+
+	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+}
+
+static int sl2610_cpu_clk_setup(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *parent_np = dev->parent->of_node;
+	struct clk_hw_onecell_data *clk_data;
+	struct clk_hw **hws;
+	void __iomem *base;
+	int ret;
+
+	base = devm_of_iomap(dev, parent_np, 0, NULL);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, SL2610_CPU_CLK_MAX), GFP_KERNEL);
+	if (!clk_data)
+		return -ENOMEM;
+	clk_data->num = SL2610_CPU_CLK_MAX;
+	hws = clk_data->hws;
+
+	/* cpupll */
+	ret = sl2610_pll_register(dev, base + CPUPLL_OFFSET,
+				  clk_names[CPUPLL_OUT], clk_names[CPUPLL_OUTF]);
+	if (ret)
+		return ret;
+
+	/* TODO: support cpupll bypass */
+	hws[CLK_CPU0] =
+		devm_clk_hw_register_fixed_factor(dev, "cpu0", clk_names[CPUPLL_OUTF], 0, 1, 1);
+	if (IS_ERR(hws[CLK_CPU0]))
+		return PTR_ERR(hws[CLK_CPU0]);
+	hws[CLK_CPU1] =
+		devm_clk_hw_register_fixed_factor(dev, "cpu1", clk_names[CPUPLL_OUTF], 0, 1, 1);
+	if (IS_ERR(hws[CLK_CPU1]))
+		return PTR_ERR(hws[CLK_CPU1]);
+	hws[CLK_DSU] =
+		devm_clk_hw_register_fixed_factor(dev, "dsu", clk_names[CPUPLL_OUT], 0, 1, 1);
+	if (IS_ERR(hws[CLK_DSU]))
+		return PTR_ERR(hws[CLK_DSU]);
+
+	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+}
+
+static int sl2610_aio_clk_setup(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *parent_np = dev->parent->of_node;
+	void __iomem *base;
+	int ret;
+
+	base = devm_of_iomap(dev, parent_np, 0, NULL);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	/* aplls */
+	ret = sl2610_pll_register(dev, base + APLL0_OFFSET,
+				  clk_names[APLL0_OUT], clk_names[APLL0_OUTF]);
+	if (ret)
+		return ret;
+	ret = sl2610_pll_register(dev, base + APLL1_OFFSET,
+				  clk_names[APLL1_OUT], clk_names[APLL1_OUTF]);
+	if (ret)
+		return ret;
+
+	/* TODO aio clks */
+
+	return 0;
+}
+
+static const struct sl_sysmgr_fixedclk_desc sl2610_sysmgr_fixeds[] = {
+	{ "clk_mcu_ref",	CLK_MCU_REF,	25000000 },
+	{ "clk_mcu_pll",	CLK_MCU_PLL,	400000000 },
+	{ "clk_mcu_io_pll",	CLK_MCU_IO_PLL,	300000000 },
+	{ "clk_mcu_apb",	CLK_MCU_APB,	100000000 },
+	{ "clk_mcu_axi",	CLK_MCU_AXI,	200000000 },
+};
+
+static const struct sl_sysmgr_gateclk_desc sl2610_sysmgr_gates[] = {
+	{ "clk_cfg_uart0",	"clk_mcu_apb",	CLK_CFG_UART0,	0 },
+	{ "clk_ser_uart0",	"clk_mcu_ref",	CLK_SER_UART0,	1 },
+	{ "clk_cfg_uart1",	"clk_mcu_apb",	CLK_CFG_UART1,	2 },
+	{ "clk_cfg_uart2",	"clk_mcu_apb",	CLK_CFG_UART2,	3 },
+	{ "clk_cfg_uart3",	"clk_mcu_apb",	CLK_CFG_UART3,	4 },
+	{ "clk_cfg_gpio",	"clk_mcu_apb",	CLK_CFG_GPIO,	5 },
+	{ "clk_cfg_i2cm0",	"clk_mcu_apb",	CLK_CFG_I2CM0,	6 },
+	{ "clk_cfg_i2cm1",	"clk_mcu_apb",	CLK_CFG_I2CM1,	7 },
+	{ "clk_cfg_spim",	"clk_mcu_apb",	CLK_CFG_SPIM,	8 },
+	{ "clk_cfg_spis",	"clk_mcu_apb",	CLK_CFG_SPIS,	9 },
+	{ "clk_cfg_i3c",	"clk_mcu_apb",	CLK_CFG_I3C,	10 },
+	{ "clk_cfg_xspi",	"clk_mcu_apb",	CLK_CFG_XSPI,	11 },
+	{ "clk_axi_xspi",	"clk_mcu_axi",	CLK_AXI_XSPI,	12 },
+	{ "clk_cfg_pvt",	"clk_mcu_apb",	CLK_CFG_PVT,	13 },
+	{ "clk_cfg_adc",	"clk_mcu_apb",	CLK_CFG_ADC,	14 },
+	{ "clk_cfg_pwm",	"clk_mcu_apb",	CLK_CFG_PWM,	15 },
+	{ "clk_bus_can0",	"clk_mcu_axi",	CLK_BUS_CAN0,	16 },
+	{ "clk_bus_can1",	"clk_mcu_axi",	CLK_BUS_CAN1,	17 },
+	{ "clk_cfg_pdm",	"clk_mcu_apb",	CLK_CFG_PDM,	18 },
+};
+
+static const struct sl_sysmgr_clk_desc sl2610_sysmgr_clks[] = {
+	{ "clk_ser_uart1",	"clk_mcu_pll",	CLK_SER_UART1,	0x604,	4 },
+	{ "clk_ser_uart2",	"clk_mcu_pll",	CLK_SER_UART2,	0x608,	4 },
+	{ "clk_ser_uart3",	"clk_mcu_pll",	CLK_SER_UART3,	0x60c,	4 },
+	{ "clk_deb_gpio",	"clk_mcu_pll",	CLK_DEB_GPIO,	0x610,	4 },
+	{ "clk_ser_i2cm0",	"clk_mcu_pll",	CLK_SER_I2CM0,	0x614,	4 },
+	{ "clk_ser_i2cm1",	"clk_mcu_pll",	CLK_SER_I2CM1,	0x618,	4 },
+	{ "clk_ser_spim",	"clk_mcu_pll",	CLK_SER_SPIM,	0x61c,	4 },
+	{ "clk_ser_spis",	"clk_mcu_pll",	CLK_SER_SPIS,	0x620,	4 },
+	{ "clk_ser_i3c",	"clk_mcu_pll",	CLK_SER_I3C,	0x624,	4 },
+	{ "clk_ser_xspi",	"clk_mcu_io_pll", CLK_SER_XSPI,	0x628,	4 },
+	{ "clk_hs_pvt",		"clk_mcu_pll",	CLK_HS_PVT,	0x62c,	4 },
+	{ "clk_core_adc",	"clk_mcu_pll",	CLK_CORE_ADC,	0x630,	8 },
+	{ "clk_ser_pwm",	"clk_mcu_pll",	CLK_SER_PWM,	0x634,	4 },
+	{ "clk_ser_can0",	"clk_mcu_pll",	CLK_SER_CAN0,	0x638,	4 },
+	{ "clk_ser_can1",	"clk_mcu_pll",	CLK_SER_CAN1,	0x63c,	4 },
+	{ "clk_ser_pdm",	"clk_mcu_pll",	CLK_SER_PDM,	0x640,	8 },
+	{ "clk_ref_calib",	"clk_mcu_pll",	CLK_REF_CALIB,	0x644,	8 },
+};
+
+static int sl2610_sysmgr_clk_setup(struct platform_device *pdev)
+{
+	return sl_sysmgr_clk_setup(pdev, sl2610_sysmgr_fixeds, ARRAY_SIZE(sl2610_sysmgr_fixeds),
+				   sl2610_sysmgr_gates, ARRAY_SIZE(sl2610_sysmgr_gates),
+				   CLK_GATE_CTRL0,
+				   sl2610_sysmgr_clks, ARRAY_SIZE(sl2610_sysmgr_clks));
+}
+
+static const struct platform_device_id sl2610_clks_ids[] = {
+	{ "sl2610-soc-clk", (kernel_ulong_t)sl2610_soc_clk_setup },
+	{ "sl2610-cpu-clk", (kernel_ulong_t)sl2610_cpu_clk_setup },
+	{ "sl2610-avio-clk", (kernel_ulong_t)sl2610_aio_clk_setup },
+	{ "sl2610-mcu-clk", (kernel_ulong_t)sl2610_sysmgr_clk_setup },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, sl2610_clks_ids);
+
+static int sl2610_clks_probe(struct platform_device *pdev)
+{
+	int (*clk_setup)(struct platform_device *pdev);
+
+	clk_setup = (void *)platform_get_device_id(pdev)->driver_data;
+
+	return clk_setup(pdev);
+}
+
+static struct platform_driver sl2610_clks_driver = {
+	.probe = sl2610_clks_probe,
+	.id_table = sl2610_clks_ids,
+	.driver = {
+		.name = "sl2610-clk",
+	},
+};
+module_platform_driver(sl2610_clks_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
+MODULE_DESCRIPTION("Synaptics SL2610 clock driver");
diff --git a/drivers/clk/berlin/sl2610-pll.c b/drivers/clk/berlin/sl2610-pll.c
new file mode 100644
index 000000000000..62c5d2d19c36
--- /dev/null
+++ b/drivers/clk/berlin/sl2610-pll.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2021 Synaptics Incorporated
+ *
+ * Author: Jisheng Zhang <jszhang@kernel.org>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_clk.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "sl2610-pll.h"
+
+#define FRAC_BITS		24
+
+#define CTRLC			0x08
+#define DIVR_MASK		GENMASK(8, 0)
+#define CTRLD			0x0c
+#define DIVF_MASK		GENMASK(8, 0)
+#define CTRLE			0x10
+#define DIVFF_MASK		GENMASK(23, 0)
+#define CTRLF			0x14
+#define DIVQ_MASK		GENMASK(4, 0)
+#define CTRLG			0x18
+#define DIVQF_MASK		GENMASK(2, 0)
+
+#define SL2610_APLL
+struct sl2610_pll {
+	void __iomem *ctrl;
+	struct clk_hw pllout;
+	struct clk_hw plloutf;
+};
+
+#define to_pllout(_hw) container_of(_hw, struct sl2610_pll, pllout)
+#define to_plloutf(_hw) container_of(_hw, struct sl2610_pll, plloutf)
+
+static unsigned long sl2610_pll_recalc_rate(struct sl2610_pll *pll,
+					    unsigned long parent_rate, bool fractional_output)
+{
+	u32 dm, dn, frac, dp;
+	u64 rate;
+
+	dm = (readl_relaxed(pll->ctrl + CTRLC) & DIVR_MASK) + 1;
+	dn = (readl_relaxed(pll->ctrl + CTRLD) & DIVF_MASK) + 1;
+	frac = readl_relaxed(pll->ctrl + CTRLE) & DIVFF_MASK;
+
+	if (fractional_output)
+		dp = (readl_relaxed(pll->ctrl + CTRLG) & DIVQF_MASK) + 1;
+	else
+		dp = ((readl_relaxed(pll->ctrl + CTRLF) & DIVQ_MASK) + 1) * 2;
+
+	rate = (u64)parent_rate * dn;
+	rate += ((u64)parent_rate * frac + BIT_ULL(FRAC_BITS - 1)) >> FRAC_BITS;
+	rate = DIV_ROUND_CLOSEST_ULL(rate * 4, dm * dp);
+
+	return rate;
+}
+
+static unsigned long sl2610_pllout_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	return sl2610_pll_recalc_rate(to_pllout(hw), parent_rate, false);
+}
+
+static unsigned long sl2610_plloutf_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	return sl2610_pll_recalc_rate(to_plloutf(hw), parent_rate, true);
+}
+
+static const struct clk_ops sl2610_pllout_ops = {
+	.recalc_rate = sl2610_pllout_recalc_rate,
+};
+
+static const struct clk_ops sl2610_plloutf_ops = {
+	.recalc_rate = sl2610_plloutf_recalc_rate,
+};
+
+static void sl2610_pll_put_clk(void *data)
+{
+	clk_put(data);
+}
+
+int sl2610_pll_register(struct device *dev, void __iomem *ctrl,
+			const char *out, const char *outf)
+{
+	struct device_node *np = dev->parent->of_node;
+	struct clk_parent_data parent = {};
+	struct clk_init_data init = {};
+	struct sl2610_pll *pll;
+	struct clk *input;
+	int ret;
+
+	if (!dev || !ctrl || !out || !outf)
+		return -EINVAL;
+
+	input = of_clk_get(np, 0);
+	if (IS_ERR(input))
+		return dev_err_probe(dev, PTR_ERR(input),
+				     "failed to get PLL reference clock\n");
+	parent.hw = __clk_get_hw(input);
+	ret = devm_add_action_or_reset(dev, sl2610_pll_put_clk, input);
+	if (ret)
+		return ret;
+
+	pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return -ENOMEM;
+	pll->ctrl = ctrl;
+
+	init.name = out;
+	init.ops = &sl2610_pllout_ops;
+	init.parent_data = &parent;
+	init.num_parents = 1;
+	init.flags = CLK_GET_RATE_NOCACHE;
+	pll->pllout.init = &init;
+
+	ret = devm_clk_hw_register(dev, &pll->pllout);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register pllout\n");
+
+	init.name = outf;
+	init.ops = &sl2610_plloutf_ops;
+	pll->plloutf.init = &init;
+
+	ret = devm_clk_hw_register(dev, &pll->plloutf);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register plloutf\n");
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sl2610_pll_register);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Synaptics SL2610 PLL clock driver");
diff --git a/drivers/clk/berlin/sl2610-pll.h b/drivers/clk/berlin/sl2610-pll.h
new file mode 100644
index 000000000000..b61a93b28610
--- /dev/null
+++ b/drivers/clk/berlin/sl2610-pll.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __SL2610_PLL_H
+#define __SL2610_PLL_H
+
+#include <linux/io.h>
+
+struct clk_hw_onecell_data;
+struct device;
+struct device_node;
+
+int sl2610_pll_register(struct device *dev, void __iomem *ctrl,
+			const char *out, const char *outf);
+
+#endif /* __SL2610_PLL_H */
-- 
2.55.0


  parent reply	other threads:[~2026-11-15 10:45 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-15 10:23 [PATCH v2 00/13] arm64: add Synaptics SL2610 SoCs and RDK boards Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 01/13] arm64: kconfig: let ARCH_BERLIN cover Synaptics arm64 SoCs Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 02/13] pinctrl: berlin: use u16 instead of u8 for the offset Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 03/13] pinctrl: berlin: enable module build support Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 04/13] pinctrl: berlin: add optional pinconf support Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 05/13] dt-bindings: soc: synaptics: add SL2610 system controllers Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 06/13] soc: synaptics: add SL2610 system controller support Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 07/13] reset: add Synaptics SL2610 reset " Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 08/13] pinctrl: berlin: add Synaptics SL2610 pin " Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 09/13] clk: berlin: add Synaptics clock controller helpers Jisheng Zhang
2026-11-15 10:23 ` Jisheng Zhang [this message]
2026-11-15 10:23 ` [PATCH v2 11/13] dt-bindings: arm: berlin: add Synaptics SL2610 SoCs and RDK boards Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 12/13] arm64: dts: synaptics: add SL2610 family SoCs Jisheng Zhang
2026-11-15 10:23 ` [PATCH v2 13/13] arm64: dts: synaptics: add SL2610 family RDK boards Jisheng Zhang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261115102401.3520-11-jszhang@kernel.org \
    --to=jszhang@kernel.org \
    --cc=bmasney+clk@redhat.com \
    --cc=catalin.marinas@arm.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=jbrunet+clk@baylibre.com \
    --cc=krzk+dt@kernel.org \
    --cc=linusw@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=p.zabel@pengutronix.de \
    --cc=robh@kernel.org \
    --cc=sboyd@kernel.org \
    --cc=sebastian.hesselbarth@gmail.com \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®