18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2019 Christoph Hellwig.
48c2ecf20Sopenharmony_ci * Copyright (c) 2019 Western Digital Corporation or its affiliates.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/types.h>
78c2ecf20Sopenharmony_ci#include <linux/io.h>
88c2ecf20Sopenharmony_ci#include <linux/of.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
118c2ecf20Sopenharmony_ci#include <linux/clkdev.h>
128c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
138c2ecf20Sopenharmony_ci#include <asm/soc.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define K210_SYSCTL_CLK0_FREQ		26000000UL
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* Registers base address */
188c2ecf20Sopenharmony_ci#define K210_SYSCTL_SYSCTL_BASE_ADDR	0x50440000ULL
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* Registers */
218c2ecf20Sopenharmony_ci#define K210_SYSCTL_PLL0		0x08
228c2ecf20Sopenharmony_ci#define K210_SYSCTL_PLL1		0x0c
238c2ecf20Sopenharmony_ci/* clkr: 4bits, clkf1: 6bits, clkod: 4bits, bwadj: 4bits */
248c2ecf20Sopenharmony_ci#define   PLL_RESET		(1 << 20)
258c2ecf20Sopenharmony_ci#define   PLL_PWR		(1 << 21)
268c2ecf20Sopenharmony_ci#define   PLL_INTFB		(1 << 22)
278c2ecf20Sopenharmony_ci#define   PLL_BYPASS		(1 << 23)
288c2ecf20Sopenharmony_ci#define   PLL_TEST		(1 << 24)
298c2ecf20Sopenharmony_ci#define   PLL_OUT_EN		(1 << 25)
308c2ecf20Sopenharmony_ci#define   PLL_TEST_EN		(1 << 26)
318c2ecf20Sopenharmony_ci#define K210_SYSCTL_PLL_LOCK		0x18
328c2ecf20Sopenharmony_ci#define   PLL0_LOCK1		(1 << 0)
338c2ecf20Sopenharmony_ci#define   PLL0_LOCK2		(1 << 1)
348c2ecf20Sopenharmony_ci#define   PLL0_SLIP_CLEAR	(1 << 2)
358c2ecf20Sopenharmony_ci#define   PLL0_TEST_CLK_OUT	(1 << 3)
368c2ecf20Sopenharmony_ci#define   PLL1_LOCK1		(1 << 8)
378c2ecf20Sopenharmony_ci#define   PLL1_LOCK2		(1 << 9)
388c2ecf20Sopenharmony_ci#define   PLL1_SLIP_CLEAR	(1 << 10)
398c2ecf20Sopenharmony_ci#define   PLL1_TEST_CLK_OUT	(1 << 11)
408c2ecf20Sopenharmony_ci#define   PLL2_LOCK1		(1 << 16)
418c2ecf20Sopenharmony_ci#define   PLL2_LOCK2		(1 << 16)
428c2ecf20Sopenharmony_ci#define   PLL2_SLIP_CLEAR	(1 << 18)
438c2ecf20Sopenharmony_ci#define   PLL2_TEST_CLK_OUT	(1 << 19)
448c2ecf20Sopenharmony_ci#define K210_SYSCTL_CLKSEL0	0x20
458c2ecf20Sopenharmony_ci#define   CLKSEL_ACLK		(1 << 0)
468c2ecf20Sopenharmony_ci#define K210_SYSCTL_CLKEN_CENT		0x28
478c2ecf20Sopenharmony_ci#define   CLKEN_CPU		(1 << 0)
488c2ecf20Sopenharmony_ci#define   CLKEN_SRAM0		(1 << 1)
498c2ecf20Sopenharmony_ci#define   CLKEN_SRAM1		(1 << 2)
508c2ecf20Sopenharmony_ci#define   CLKEN_APB0		(1 << 3)
518c2ecf20Sopenharmony_ci#define   CLKEN_APB1		(1 << 4)
528c2ecf20Sopenharmony_ci#define   CLKEN_APB2		(1 << 5)
538c2ecf20Sopenharmony_ci#define K210_SYSCTL_CLKEN_PERI		0x2c
548c2ecf20Sopenharmony_ci#define   CLKEN_ROM		(1 << 0)
558c2ecf20Sopenharmony_ci#define   CLKEN_DMA		(1 << 1)
568c2ecf20Sopenharmony_ci#define   CLKEN_AI		(1 << 2)
578c2ecf20Sopenharmony_ci#define   CLKEN_DVP		(1 << 3)
588c2ecf20Sopenharmony_ci#define   CLKEN_FFT		(1 << 4)
598c2ecf20Sopenharmony_ci#define   CLKEN_GPIO		(1 << 5)
608c2ecf20Sopenharmony_ci#define   CLKEN_SPI0		(1 << 6)
618c2ecf20Sopenharmony_ci#define   CLKEN_SPI1		(1 << 7)
628c2ecf20Sopenharmony_ci#define   CLKEN_SPI2		(1 << 8)
638c2ecf20Sopenharmony_ci#define   CLKEN_SPI3		(1 << 9)
648c2ecf20Sopenharmony_ci#define   CLKEN_I2S0		(1 << 10)
658c2ecf20Sopenharmony_ci#define   CLKEN_I2S1		(1 << 11)
668c2ecf20Sopenharmony_ci#define   CLKEN_I2S2		(1 << 12)
678c2ecf20Sopenharmony_ci#define   CLKEN_I2C0		(1 << 13)
688c2ecf20Sopenharmony_ci#define   CLKEN_I2C1		(1 << 14)
698c2ecf20Sopenharmony_ci#define   CLKEN_I2C2		(1 << 15)
708c2ecf20Sopenharmony_ci#define   CLKEN_UART1		(1 << 16)
718c2ecf20Sopenharmony_ci#define   CLKEN_UART2		(1 << 17)
728c2ecf20Sopenharmony_ci#define   CLKEN_UART3		(1 << 18)
738c2ecf20Sopenharmony_ci#define   CLKEN_AES		(1 << 19)
748c2ecf20Sopenharmony_ci#define   CLKEN_FPIO		(1 << 20)
758c2ecf20Sopenharmony_ci#define   CLKEN_TIMER0		(1 << 21)
768c2ecf20Sopenharmony_ci#define   CLKEN_TIMER1		(1 << 22)
778c2ecf20Sopenharmony_ci#define   CLKEN_TIMER2		(1 << 23)
788c2ecf20Sopenharmony_ci#define   CLKEN_WDT0		(1 << 24)
798c2ecf20Sopenharmony_ci#define   CLKEN_WDT1		(1 << 25)
808c2ecf20Sopenharmony_ci#define   CLKEN_SHA		(1 << 26)
818c2ecf20Sopenharmony_ci#define   CLKEN_OTP		(1 << 27)
828c2ecf20Sopenharmony_ci#define   CLKEN_RTC		(1 << 29)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistruct k210_sysctl {
858c2ecf20Sopenharmony_ci	void __iomem		*regs;
868c2ecf20Sopenharmony_ci	struct clk_hw		hw;
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void k210_set_bits(u32 val, void __iomem *reg)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	writel(readl(reg) | val, reg);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic void k210_clear_bits(u32 val, void __iomem *reg)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	writel(readl(reg) & ~val, reg);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic void k210_pll1_enable(void __iomem *regs)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	u32 val;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	val = readl(regs + K210_SYSCTL_PLL1);
1048c2ecf20Sopenharmony_ci	val &= ~GENMASK(19, 0);				/* clkr1 = 0 */
1058c2ecf20Sopenharmony_ci	val |= FIELD_PREP(GENMASK(9, 4), 0x3B);		/* clkf1 = 59 */
1068c2ecf20Sopenharmony_ci	val |= FIELD_PREP(GENMASK(13, 10), 0x3);	/* clkod1 = 3 */
1078c2ecf20Sopenharmony_ci	val |= FIELD_PREP(GENMASK(19, 14), 0x3B);	/* bwadj1 = 59 */
1088c2ecf20Sopenharmony_ci	writel(val, regs + K210_SYSCTL_PLL1);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	k210_clear_bits(PLL_BYPASS, regs + K210_SYSCTL_PLL1);
1118c2ecf20Sopenharmony_ci	k210_set_bits(PLL_PWR, regs + K210_SYSCTL_PLL1);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * Reset the pll. The magic NOPs come from the Kendryte reference SDK.
1158c2ecf20Sopenharmony_ci	 */
1168c2ecf20Sopenharmony_ci	k210_clear_bits(PLL_RESET, regs + K210_SYSCTL_PLL1);
1178c2ecf20Sopenharmony_ci	k210_set_bits(PLL_RESET, regs + K210_SYSCTL_PLL1);
1188c2ecf20Sopenharmony_ci	nop();
1198c2ecf20Sopenharmony_ci	nop();
1208c2ecf20Sopenharmony_ci	k210_clear_bits(PLL_RESET, regs + K210_SYSCTL_PLL1);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	for (;;) {
1238c2ecf20Sopenharmony_ci		val = readl(regs + K210_SYSCTL_PLL_LOCK);
1248c2ecf20Sopenharmony_ci		if (val & PLL1_LOCK2)
1258c2ecf20Sopenharmony_ci			break;
1268c2ecf20Sopenharmony_ci		writel(val | PLL1_SLIP_CLEAR, regs + K210_SYSCTL_PLL_LOCK);
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	k210_set_bits(PLL_OUT_EN, regs + K210_SYSCTL_PLL1);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic unsigned long k210_sysctl_clk_recalc_rate(struct clk_hw *hw,
1338c2ecf20Sopenharmony_ci		unsigned long parent_rate)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct k210_sysctl *s = container_of(hw, struct k210_sysctl, hw);
1368c2ecf20Sopenharmony_ci	u32 clksel0, pll0;
1378c2ecf20Sopenharmony_ci	u64 pll0_freq, clkr0, clkf0, clkod0;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/*
1408c2ecf20Sopenharmony_ci	 * If the clock selector is not set, use the base frequency.
1418c2ecf20Sopenharmony_ci	 * Otherwise, use PLL0 frequency with a frequency divisor.
1428c2ecf20Sopenharmony_ci	 */
1438c2ecf20Sopenharmony_ci	clksel0 = readl(s->regs + K210_SYSCTL_CLKSEL0);
1448c2ecf20Sopenharmony_ci	if (!(clksel0 & CLKSEL_ACLK))
1458c2ecf20Sopenharmony_ci		return K210_SYSCTL_CLK0_FREQ;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/*
1488c2ecf20Sopenharmony_ci	 * Get PLL0 frequency:
1498c2ecf20Sopenharmony_ci	 * freq = base frequency * clkf0 / (clkr0 * clkod0)
1508c2ecf20Sopenharmony_ci	 */
1518c2ecf20Sopenharmony_ci	pll0 = readl(s->regs + K210_SYSCTL_PLL0);
1528c2ecf20Sopenharmony_ci	clkr0 = 1 + FIELD_GET(GENMASK(3, 0), pll0);
1538c2ecf20Sopenharmony_ci	clkf0 = 1 + FIELD_GET(GENMASK(9, 4), pll0);
1548c2ecf20Sopenharmony_ci	clkod0 = 1 + FIELD_GET(GENMASK(13, 10), pll0);
1558c2ecf20Sopenharmony_ci	pll0_freq = clkf0 * K210_SYSCTL_CLK0_FREQ / (clkr0 * clkod0);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* Get the frequency divisor from the clock selector */
1588c2ecf20Sopenharmony_ci	return pll0_freq / (2ULL << FIELD_GET(0x00000006, clksel0));
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic const struct clk_ops k210_sysctl_clk_ops = {
1628c2ecf20Sopenharmony_ci	.recalc_rate	= k210_sysctl_clk_recalc_rate,
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic const struct clk_init_data k210_clk_init_data = {
1668c2ecf20Sopenharmony_ci	.name		= "k210-sysctl-pll1",
1678c2ecf20Sopenharmony_ci	.ops		= &k210_sysctl_clk_ops,
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int k210_sysctl_probe(struct platform_device *pdev)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct k210_sysctl *s;
1738c2ecf20Sopenharmony_ci	int error;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	pr_info("Kendryte K210 SoC sysctl\n");
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
1788c2ecf20Sopenharmony_ci	if (!s)
1798c2ecf20Sopenharmony_ci		return -ENOMEM;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	s->regs = devm_ioremap_resource(&pdev->dev,
1828c2ecf20Sopenharmony_ci			platform_get_resource(pdev, IORESOURCE_MEM, 0));
1838c2ecf20Sopenharmony_ci	if (IS_ERR(s->regs))
1848c2ecf20Sopenharmony_ci		return PTR_ERR(s->regs);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	s->hw.init = &k210_clk_init_data;
1878c2ecf20Sopenharmony_ci	error = devm_clk_hw_register(&pdev->dev, &s->hw);
1888c2ecf20Sopenharmony_ci	if (error) {
1898c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register clk");
1908c2ecf20Sopenharmony_ci		return error;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	error = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get,
1948c2ecf20Sopenharmony_ci					    &s->hw);
1958c2ecf20Sopenharmony_ci	if (error) {
1968c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "adding clk provider failed\n");
1978c2ecf20Sopenharmony_ci		return error;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return 0;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic const struct of_device_id k210_sysctl_of_match[] = {
2048c2ecf20Sopenharmony_ci	{ .compatible = "kendryte,k210-sysctl", },
2058c2ecf20Sopenharmony_ci	{}
2068c2ecf20Sopenharmony_ci};
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic struct platform_driver k210_sysctl_driver = {
2098c2ecf20Sopenharmony_ci	.driver	= {
2108c2ecf20Sopenharmony_ci		.name		= "k210-sysctl",
2118c2ecf20Sopenharmony_ci		.of_match_table	= k210_sysctl_of_match,
2128c2ecf20Sopenharmony_ci	},
2138c2ecf20Sopenharmony_ci	.probe			= k210_sysctl_probe,
2148c2ecf20Sopenharmony_ci};
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic int __init k210_sysctl_init(void)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	return platform_driver_register(&k210_sysctl_driver);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_cicore_initcall(k210_sysctl_init);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/*
2238c2ecf20Sopenharmony_ci * This needs to be called very early during initialization, given that
2248c2ecf20Sopenharmony_ci * PLL1 needs to be enabled to be able to use all SRAM.
2258c2ecf20Sopenharmony_ci */
2268c2ecf20Sopenharmony_cistatic void __init k210_soc_early_init(const void *fdt)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	void __iomem *regs;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	regs = ioremap(K210_SYSCTL_SYSCTL_BASE_ADDR, 0x1000);
2318c2ecf20Sopenharmony_ci	if (!regs)
2328c2ecf20Sopenharmony_ci		panic("K210 sysctl ioremap");
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* Enable PLL1 to make the KPU SRAM useable */
2358c2ecf20Sopenharmony_ci	k210_pll1_enable(regs);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	k210_set_bits(PLL_OUT_EN, regs + K210_SYSCTL_PLL0);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	k210_set_bits(CLKEN_CPU | CLKEN_SRAM0 | CLKEN_SRAM1,
2408c2ecf20Sopenharmony_ci		      regs + K210_SYSCTL_CLKEN_CENT);
2418c2ecf20Sopenharmony_ci	k210_set_bits(CLKEN_ROM | CLKEN_TIMER0 | CLKEN_RTC,
2428c2ecf20Sopenharmony_ci		      regs + K210_SYSCTL_CLKEN_PERI);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	k210_set_bits(CLKSEL_ACLK, regs + K210_SYSCTL_CLKSEL0);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	iounmap(regs);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ciSOC_EARLY_INIT_DECLARE(generic_k210, "kendryte,k210", k210_soc_early_init);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci#ifdef CONFIG_SOC_KENDRYTE_K210_DTB_BUILTIN
2518c2ecf20Sopenharmony_ci/*
2528c2ecf20Sopenharmony_ci * Generic entry for the default k210.dtb embedded DTB for boards with:
2538c2ecf20Sopenharmony_ci *   - Vendor ID: 0x4B5
2548c2ecf20Sopenharmony_ci *   - Arch ID: 0xE59889E6A5A04149 (= "Canaan AI" in UTF-8 encoded Chinese)
2558c2ecf20Sopenharmony_ci *   - Impl ID:	0x4D41495832303030 (= "MAIX2000")
2568c2ecf20Sopenharmony_ci * These values are reported by the SiPEED MAXDUINO, SiPEED MAIX GO and
2578c2ecf20Sopenharmony_ci * SiPEED Dan dock boards.
2588c2ecf20Sopenharmony_ci */
2598c2ecf20Sopenharmony_ciSOC_BUILTIN_DTB_DECLARE(k210, 0x4B5, 0xE59889E6A5A04149, 0x4D41495832303030);
2608c2ecf20Sopenharmony_ci#endif
261