k210-sysctl.c (802fee26d8afd073c630a74dbe1a996970f3fd90) k210-sysctl.c (c6ca7616f7d5c2ce166280107ba74db1d528fcb7)
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 */
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>
6#include <linux/io.h>
8#include <linux/of.h>
9#include <linux/platform_device.h>
7#include <linux/platform_device.h>
10#include <linux/clk-provider.h>
11#include <linux/clkdev.h>
12#include <linux/bitfield.h>
8#include <linux/of_platform.h>
9#include <linux/clk.h>
13#include <asm/soc.h>
14
15#include <soc/canaan/k210-sysctl.h>
16
10#include <asm/soc.h>
11
12#include <soc/canaan/k210-sysctl.h>
13
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
43struct k210_sysctl {
44 void __iomem *regs;
45 struct clk_hw hw;
46};
47
48static void k210_set_bits(u32 val, void __iomem *reg)
49{
50 writel(readl(reg) | val, reg);
51}
52
53static void k210_clear_bits(u32 val, void __iomem *reg)
54{
55 writel(readl(reg) & ~val, reg);
56}
57
58static 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
91static 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
120static const struct clk_ops k210_sysctl_clk_ops = {
121 .recalc_rate = k210_sysctl_clk_recalc_rate,
122};
123
124static const struct clk_init_data k210_clk_init_data = {
125 .name = "k210-sysctl-pll1",
126 .ops = &k210_sysctl_clk_ops,
127};
128
129static int k210_sysctl_probe(struct platform_device *pdev)
130{
14static int k210_sysctl_probe(struct platform_device *pdev)
15{
131 struct k210_sysctl *s;
132 int error;
16 struct device *dev = &pdev->dev;
17 struct clk *pclk;
18 int ret;
133
19
134 pr_info("Kendryte K210 SoC sysctl\n");
20 dev_info(dev, "K210 system controller\n");
135
21
136 s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
137 if (!s)
138 return -ENOMEM;
22 /* Get power bus clock */
23 pclk = devm_clk_get(dev, NULL);
24 if (IS_ERR(pclk))
25 return dev_err_probe(dev, PTR_ERR(pclk),
26 "Get bus clock failed\n");
139
27
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;
28 ret = clk_prepare_enable(pclk);
29 if (ret) {
30 dev_err(dev, "Enable bus clock failed\n");
31 return ret;
150 }
151
32 }
33
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 }
34 /* Populate children */
35 ret = devm_of_platform_populate(dev);
36 if (ret)
37 dev_err(dev, "Populate platform failed %d\n", ret);
158
38
159 return 0;
39 return ret;
160}
161
162static const struct of_device_id k210_sysctl_of_match[] = {
40}
41
42static const struct of_device_id k210_sysctl_of_match[] = {
163 { .compatible = "kendryte,k210-sysctl", },
164 {}
43 { .compatible = "canaan,k210-sysctl", },
44 { /* sentinel */ },
165};
166
167static 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};
45};
46
47static struct platform_driver k210_sysctl_driver = {
48 .driver = {
49 .name = "k210-sysctl",
50 .of_match_table = k210_sysctl_of_match,
51 },
52 .probe = k210_sysctl_probe,
53};
54builtin_platform_driver(k210_sysctl_driver);
174
55
175static int __init k210_sysctl_init(void)
176{
177 return platform_driver_register(&k210_sysctl_driver);
178}
179core_initcall(k210_sysctl_init);
56/*
57 * System controller registers base address and size.
58 */
59#define K210_SYSCTL_BASE_ADDR 0x50440000ULL
60#define K210_SYSCTL_BASE_SIZE 0x1000
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 */
185static void __init k210_soc_early_init(const void *fdt)
186{
61
62/*
63 * This needs to be called very early during initialization, given that
64 * PLL1 needs to be enabled to be able to use all SRAM.
65 */
66static void __init k210_soc_early_init(const void *fdt)
67{
187 void __iomem *regs;
68 void __iomem *sysctl_base;
188
69
189 regs = ioremap(K210_SYSCTL_SYSCTL_BASE_ADDR, 0x1000);
190 if (!regs)
191 panic("K210 sysctl ioremap");
70 sysctl_base = ioremap(K210_SYSCTL_BASE_ADDR, K210_SYSCTL_BASE_SIZE);
71 if (!sysctl_base)
72 panic("k210-sysctl: ioremap failed");
192
73
193 /* Enable PLL1 to make the KPU SRAM useable */
194 k210_pll1_enable(regs);
74 k210_clk_early_init(sysctl_base);
195
75
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);
76 iounmap(sysctl_base);
206}
77}
207SOC_EARLY_INIT_DECLARE(generic_k210, "kendryte,k210", k210_soc_early_init);
78SOC_EARLY_INIT_DECLARE(k210_soc, "canaan,kendryte-k210", k210_soc_early_init);