xref: /linux/drivers/soc/canaan/k210-sysctl.c (revision 802fee26d8afd073c630a74dbe1a996970f3fd90)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2019 Christoph Hellwig.
4  * Copyright (c) 2019 Western Digital Corporation or its affiliates.
5  */
6 #include <linux/types.h>
7 #include <linux/io.h>
8 #include <linux/of.h>
9 #include <linux/platform_device.h>
10 #include <linux/clk-provider.h>
11 #include <linux/clkdev.h>
12 #include <linux/bitfield.h>
13 #include <asm/soc.h>
14 
15 #include <soc/canaan/k210-sysctl.h>
16 
17 #define K210_SYSCTL_CLK0_FREQ		26000000UL
18 
19 /* Registers base address */
20 #define K210_SYSCTL_SYSCTL_BASE_ADDR	0x50440000ULL
21 
22 /* Register bits */
23 /* K210_SYSCTL_PLL1: clkr: 4bits, clkf1: 6bits, clkod: 4bits, bwadj: 4bits */
24 #define PLL_RESET		(1 << 20)
25 #define PLL_PWR			(1 << 21)
26 #define PLL_BYPASS		(1 << 23)
27 #define PLL_OUT_EN		(1 << 25)
28 /* K210_SYSCTL_PLL_LOCK */
29 #define PLL1_LOCK1		(1 << 8)
30 #define PLL1_LOCK2		(1 << 9)
31 #define PLL1_SLIP_CLEAR		(1 << 10)
32 /* K210_SYSCTL_SEL0 */
33 #define CLKSEL_ACLK		(1 << 0)
34 /* K210_SYSCTL_CLKEN_CENT */
35 #define CLKEN_CPU		(1 << 0)
36 #define CLKEN_SRAM0		(1 << 1)
37 #define CLKEN_SRAM1		(1 << 2)
38 /* K210_SYSCTL_EN_PERI */
39 #define CLKEN_ROM		(1 << 0)
40 #define CLKEN_TIMER0		(1 << 21)
41 #define CLKEN_RTC		(1 << 29)
42 
43 struct k210_sysctl {
44 	void __iomem		*regs;
45 	struct clk_hw		hw;
46 };
47 
48 static void k210_set_bits(u32 val, void __iomem *reg)
49 {
50 	writel(readl(reg) | val, reg);
51 }
52 
53 static void k210_clear_bits(u32 val, void __iomem *reg)
54 {
55 	writel(readl(reg) & ~val, reg);
56 }
57 
58 static void k210_pll1_enable(void __iomem *regs)
59 {
60 	u32 val;
61 
62 	val = readl(regs + K210_SYSCTL_PLL1);
63 	val &= ~GENMASK(19, 0);				/* clkr1 = 0 */
64 	val |= FIELD_PREP(GENMASK(9, 4), 0x3B);		/* clkf1 = 59 */
65 	val |= FIELD_PREP(GENMASK(13, 10), 0x3);	/* clkod1 = 3 */
66 	val |= FIELD_PREP(GENMASK(19, 14), 0x3B);	/* bwadj1 = 59 */
67 	writel(val, regs + K210_SYSCTL_PLL1);
68 
69 	k210_clear_bits(PLL_BYPASS, regs + K210_SYSCTL_PLL1);
70 	k210_set_bits(PLL_PWR, regs + K210_SYSCTL_PLL1);
71 
72 	/*
73 	 * Reset the pll. The magic NOPs come from the Kendryte reference SDK.
74 	 */
75 	k210_clear_bits(PLL_RESET, regs + K210_SYSCTL_PLL1);
76 	k210_set_bits(PLL_RESET, regs + K210_SYSCTL_PLL1);
77 	nop();
78 	nop();
79 	k210_clear_bits(PLL_RESET, regs + K210_SYSCTL_PLL1);
80 
81 	for (;;) {
82 		val = readl(regs + K210_SYSCTL_PLL_LOCK);
83 		if (val & PLL1_LOCK2)
84 			break;
85 		writel(val | PLL1_SLIP_CLEAR, regs + K210_SYSCTL_PLL_LOCK);
86 	}
87 
88 	k210_set_bits(PLL_OUT_EN, regs + K210_SYSCTL_PLL1);
89 }
90 
91 static unsigned long k210_sysctl_clk_recalc_rate(struct clk_hw *hw,
92 		unsigned long parent_rate)
93 {
94 	struct k210_sysctl *s = container_of(hw, struct k210_sysctl, hw);
95 	u32 clksel0, pll0;
96 	u64 pll0_freq, clkr0, clkf0, clkod0;
97 
98 	/*
99 	 * If the clock selector is not set, use the base frequency.
100 	 * Otherwise, use PLL0 frequency with a frequency divisor.
101 	 */
102 	clksel0 = readl(s->regs + K210_SYSCTL_SEL0);
103 	if (!(clksel0 & CLKSEL_ACLK))
104 		return K210_SYSCTL_CLK0_FREQ;
105 
106 	/*
107 	 * Get PLL0 frequency:
108 	 * freq = base frequency * clkf0 / (clkr0 * clkod0)
109 	 */
110 	pll0 = readl(s->regs + K210_SYSCTL_PLL0);
111 	clkr0 = 1 + FIELD_GET(GENMASK(3, 0), pll0);
112 	clkf0 = 1 + FIELD_GET(GENMASK(9, 4), pll0);
113 	clkod0 = 1 + FIELD_GET(GENMASK(13, 10), pll0);
114 	pll0_freq = clkf0 * K210_SYSCTL_CLK0_FREQ / (clkr0 * clkod0);
115 
116 	/* Get the frequency divisor from the clock selector */
117 	return pll0_freq / (2ULL << FIELD_GET(0x00000006, clksel0));
118 }
119 
120 static const struct clk_ops k210_sysctl_clk_ops = {
121 	.recalc_rate	= k210_sysctl_clk_recalc_rate,
122 };
123 
124 static const struct clk_init_data k210_clk_init_data = {
125 	.name		= "k210-sysctl-pll1",
126 	.ops		= &k210_sysctl_clk_ops,
127 };
128 
129 static int k210_sysctl_probe(struct platform_device *pdev)
130 {
131 	struct k210_sysctl *s;
132 	int error;
133 
134 	pr_info("Kendryte K210 SoC sysctl\n");
135 
136 	s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
137 	if (!s)
138 		return -ENOMEM;
139 
140 	s->regs = devm_ioremap_resource(&pdev->dev,
141 			platform_get_resource(pdev, IORESOURCE_MEM, 0));
142 	if (IS_ERR(s->regs))
143 		return PTR_ERR(s->regs);
144 
145 	s->hw.init = &k210_clk_init_data;
146 	error = devm_clk_hw_register(&pdev->dev, &s->hw);
147 	if (error) {
148 		dev_err(&pdev->dev, "failed to register clk");
149 		return error;
150 	}
151 
152 	error = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get,
153 					    &s->hw);
154 	if (error) {
155 		dev_err(&pdev->dev, "adding clk provider failed\n");
156 		return error;
157 	}
158 
159 	return 0;
160 }
161 
162 static const struct of_device_id k210_sysctl_of_match[] = {
163 	{ .compatible = "kendryte,k210-sysctl", },
164 	{}
165 };
166 
167 static struct platform_driver k210_sysctl_driver = {
168 	.driver	= {
169 		.name		= "k210-sysctl",
170 		.of_match_table	= k210_sysctl_of_match,
171 	},
172 	.probe			= k210_sysctl_probe,
173 };
174 
175 static int __init k210_sysctl_init(void)
176 {
177 	return platform_driver_register(&k210_sysctl_driver);
178 }
179 core_initcall(k210_sysctl_init);
180 
181 /*
182  * This needs to be called very early during initialization, given that
183  * PLL1 needs to be enabled to be able to use all SRAM.
184  */
185 static void __init k210_soc_early_init(const void *fdt)
186 {
187 	void __iomem *regs;
188 
189 	regs = ioremap(K210_SYSCTL_SYSCTL_BASE_ADDR, 0x1000);
190 	if (!regs)
191 		panic("K210 sysctl ioremap");
192 
193 	/* Enable PLL1 to make the KPU SRAM useable */
194 	k210_pll1_enable(regs);
195 
196 	k210_set_bits(PLL_OUT_EN, regs + K210_SYSCTL_PLL0);
197 
198 	k210_set_bits(CLKEN_CPU | CLKEN_SRAM0 | CLKEN_SRAM1,
199 		      regs + K210_SYSCTL_EN_CENT);
200 	k210_set_bits(CLKEN_ROM | CLKEN_TIMER0 | CLKEN_RTC,
201 		      regs + K210_SYSCTL_EN_PERI);
202 
203 	k210_set_bits(CLKSEL_ACLK, regs + K210_SYSCTL_SEL0);
204 
205 	iounmap(regs);
206 }
207 SOC_EARLY_INIT_DECLARE(generic_k210, "kendryte,k210", k210_soc_early_init);
208