Skip to content

Commit ed54725

Browse files
automerging branch "kernel.org-palmer-linux/wip-prci_clock" into "riscv-all"
# Conflicts: # drivers/clk/sifive/Kconfig # drivers/clk/sifive/Makefile
2 parents e641349 + 2a6e43d commit ed54725

File tree

4 files changed

+360
-0
lines changed

4 files changed

+360
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
SiFive U54 SoC clocks
2+
3+
This binding uses the common clock binding:
4+
Documentation/devicetree/bindings/clock/clock-bindings.txt
5+
6+
The U54 PRCI controller generates clocks for the U54 SoC. There is
7+
a core PLL that sets the processor frequency and PLLs for ethernet
8+
and DDR. It takes an input clock from the board, typically an oscillator
9+
or crystal.
10+
11+
Required properties:
12+
- compatible: Should be "sifive,aloeprci0"
13+
- #clock-cells: Should be <1>
14+
- reg: Specifies base physical address and size of the registers
15+
- clocks: phandles to the parent clock used as input
16+
17+
Example:
18+
19+
refclk: refclk {
20+
#clock-cells = <0>;
21+
compatible = "fixed-clock";
22+
clock-frequency = <33333333>;
23+
clock-output-names = "xtal";
24+
};
25+
26+
u54: prci@10000000 {
27+
compatible = "sifive,aloeprci0";
28+
reg = <0x0 0x10000000 0x0 0x1000>;
29+
clocks = <&refclk>;
30+
#clock-cells = <1>;
31+
};
32+
33+
tlclk: tlclk {
34+
compatible = "fixed-factor-clock";
35+
clocks = <&u54 0>; /* Core frequency */
36+
#clock-cells = <0>;
37+
clock-div = <2>;
38+
clock-mult = <1>;
39+
};
40+
41+
ethernet@10090000 {
42+
...
43+
clocks = <&prci 1>; /* TX clock */
44+
};

drivers/clk/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ source "drivers/clk/mvebu/Kconfig"
287287
source "drivers/clk/qcom/Kconfig"
288288
source "drivers/clk/renesas/Kconfig"
289289
source "drivers/clk/samsung/Kconfig"
290+
source "drivers/clk/sifive/Kconfig"
290291
source "drivers/clk/sprd/Kconfig"
291292
source "drivers/clk/sunxi-ng/Kconfig"
292293
source "drivers/clk/tegra/Kconfig"

drivers/clk/sifive/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
config CLK_U54_PRCI
22
bool "PRCI driver for U54 SoCs"
3+
depends on RISCV
34
---help---
45
Supports Power Reset Clock interface found in U540 SoCs
56

drivers/clk/sifive/u54-prci.c

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
/*
2+
* This program is free software; you can redistribute it and/or modify
3+
* it under the terms of the GNU General Public License version 2 as
4+
* published by the Free Software Foundation.
5+
*
6+
* This program is distributed in the hope that it will be useful,
7+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
8+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9+
* GNU General Public License for more details.
10+
*
11+
* Copyright (C) 2018 SiFive, Inc.
12+
*/
13+
14+
#include <linux/clkdev.h>
15+
#include <linux/clk-provider.h>
16+
#include <linux/clk.h>
17+
#include <linux/err.h>
18+
#include <linux/of.h>
19+
#include <linux/platform_device.h>
20+
#include <linux/slab.h>
21+
#include <linux/log2.h>
22+
23+
#define CORE_CLOCK 0
24+
#define GEMTX_CLOCK 1
25+
#define PRCI_CLOCKS 2
26+
27+
#define MIN_REF 7000000ULL
28+
#define MAX_REF 200000000ULL
29+
#define MAX_PARENT 600000000ULL
30+
#define MAX_VCO 4800000000ULL
31+
#define MAX_DIV 64ULL
32+
#define MAX_R 64ULL
33+
34+
#define PLL_LOCK 0x80000000ULL
35+
#define NAME_LEN 40ULL
36+
37+
struct sifive_u54_prci_driver;
38+
39+
struct sifive_u54_prci_pll {
40+
struct clk_hw hw;
41+
struct sifive_u54_prci_driver *driver;
42+
char name[NAME_LEN];
43+
u32 freq;
44+
u32 glcm;
45+
};
46+
47+
struct sifive_u54_prci_driver {
48+
struct clk_onecell_data table;
49+
struct clk *clks[PRCI_CLOCKS];
50+
struct sifive_u54_prci_pll plls[PRCI_CLOCKS];
51+
void __iomem *reg;
52+
};
53+
54+
#define to_sifive_u54_prci_pll(hw) container_of(hw, struct sifive_u54_prci_pll, hw)
55+
56+
struct sifive_u54_pll_cfg {
57+
unsigned long r, f, q, a;
58+
};
59+
60+
static struct sifive_u54_pll_cfg sifive_u54_pll_cfg(u32 reg)
61+
{
62+
struct sifive_u54_pll_cfg cfg;
63+
cfg.r = (reg >> 0) & 0x3f;
64+
cfg.f = (reg >> 6) & 0x1ff;
65+
cfg.q = (reg >> 15) & 0x7;
66+
cfg.a = (reg >> 18) & 0x7;
67+
return cfg;
68+
}
69+
70+
static u32 sifive_u54_pll_reg(struct sifive_u54_pll_cfg cfg)
71+
{
72+
u32 reg = 0;
73+
reg |= (cfg.r & 0x3f) << 0;
74+
reg |= (cfg.f & 0x1ff) << 6;
75+
reg |= (cfg.q & 0x7) << 15;
76+
reg |= (cfg.a & 0x7) << 18;
77+
reg |= 1<<25; // internal feedback
78+
return reg;
79+
}
80+
81+
static unsigned long sifive_u54_pll_rate(struct sifive_u54_pll_cfg cfg, unsigned long parent)
82+
{
83+
return (parent*2*(cfg.f+1) / (cfg.r+1)) >> cfg.q;
84+
}
85+
86+
static struct sifive_u54_pll_cfg sifive_u54_pll_configure(unsigned long target, unsigned long parent)
87+
{
88+
struct sifive_u54_pll_cfg cfg;
89+
unsigned long scale, ratio, best_delta, filter;
90+
u32 max_r, best_r, best_f, r;
91+
92+
/* Confirm input frequency is within bounds */
93+
if (WARN_ON(parent > MAX_PARENT)) { parent = MAX_PARENT; }
94+
if (WARN_ON(parent < MIN_REF)) { parent = MIN_REF; }
95+
96+
/* Calculate the Q shift and target VCO */
97+
scale = MAX_VCO / target;
98+
if (scale <= 1) {
99+
cfg.q = 1;
100+
target = MAX_VCO;
101+
} else if (scale > MAX_DIV) {
102+
cfg.q = ilog2(MAX_DIV);
103+
target = MAX_VCO/2;
104+
} else {
105+
cfg.q = ilog2(scale);
106+
target = target << cfg.q;
107+
}
108+
109+
/* Precalcualte the target ratio */
110+
ratio = (target << 20) / parent;
111+
112+
/* Placeholder values */
113+
best_r = 0;
114+
best_f = 0;
115+
best_delta = MAX_VCO;
116+
117+
/* Consider all values for R which land within [MIN_REF, MAX_REF]; prefer smaller R */
118+
max_r = min(MAX_R, parent / MIN_REF);
119+
for (r = DIV_ROUND_UP(parent, MAX_REF); r <= max_r; ++r) {
120+
/* What is the best F we can pick in this case? */
121+
u32 f = (ratio*r + (1<<20)) >> 21;
122+
unsigned long ref = parent / r;
123+
unsigned long vco = ref * f * 2;
124+
unsigned long delta;
125+
126+
/* Ensure rounding didn't take us out of range */
127+
if (vco > target) --f;
128+
if (vco < MAX_VCO/2) ++f;
129+
vco = ref * f * 2;
130+
131+
delta = abs(target - vco);
132+
if (delta < best_delta) {
133+
best_delta = delta;
134+
best_r = r;
135+
best_f = f;
136+
}
137+
}
138+
139+
cfg.r = best_r - 1;
140+
cfg.f = best_f - 1;
141+
142+
/* Pick the best PLL jitter filter */
143+
filter = parent / best_r;
144+
BUG_ON(filter < 7000000);
145+
if (filter < 11000000) {
146+
cfg.a = 1;
147+
} else if (filter < 18000000) {
148+
cfg.a = 2;
149+
} else if (filter < 30000000) {
150+
cfg.a = 3;
151+
} else if (filter < 50000000) {
152+
cfg.a = 4;
153+
} else if (filter < 80000000) {
154+
cfg.a = 5;
155+
} else if (filter < 130000000) {
156+
cfg.a = 6;
157+
} else {
158+
BUG_ON (filter > 200000000);
159+
cfg.a = 7;
160+
}
161+
162+
return cfg;
163+
}
164+
165+
static unsigned long sifive_u54_prci_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
166+
{
167+
struct sifive_u54_prci_pll *pll = to_sifive_u54_prci_pll(hw);
168+
struct sifive_u54_prci_driver *driver = pll->driver;
169+
170+
u32 reg = ioread32(driver->reg + pll->freq);
171+
struct sifive_u54_pll_cfg cfg = sifive_u54_pll_cfg(reg);
172+
173+
return sifive_u54_pll_rate(cfg, parent_rate);
174+
}
175+
176+
static long sifive_u54_prci_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate)
177+
{
178+
struct sifive_u54_pll_cfg cfg = sifive_u54_pll_configure(rate, *parent_rate);
179+
return sifive_u54_pll_rate(cfg, *parent_rate);
180+
}
181+
182+
static int sifive_u54_prci_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
183+
{
184+
struct sifive_u54_prci_pll *pll = to_sifive_u54_prci_pll(hw);
185+
struct sifive_u54_prci_driver *driver = pll->driver;
186+
187+
struct sifive_u54_pll_cfg cfg = sifive_u54_pll_configure(rate, parent_rate);
188+
u32 reg = sifive_u54_pll_reg(cfg);
189+
190+
/* Switch to reg clock and reconfigure PLL */
191+
iowrite32(1, driver->reg + pll->glcm);
192+
iowrite32(reg, driver->reg + pll->freq);
193+
194+
/* Wait for lock and switch back to PLL */
195+
while (!(ioread32(driver->reg + pll->freq) & PLL_LOCK));
196+
iowrite32(0, driver->reg + pll->glcm);
197+
198+
return 0;
199+
}
200+
201+
static const struct clk_ops sifive_u54_prci_ops_rw = {
202+
.recalc_rate = sifive_u54_prci_recalc_rate,
203+
.round_rate = sifive_u54_prci_round_rate,
204+
.set_rate = sifive_u54_prci_set_rate,
205+
};
206+
207+
static const struct clk_ops sifive_u54_prci_ops_ro = {
208+
.recalc_rate = sifive_u54_prci_recalc_rate,
209+
};
210+
211+
static ssize_t sifive_u54_pll_show(struct device *dev, struct device_attribute *attr, char *buf)
212+
{
213+
struct sifive_u54_prci_driver *driver = dev_get_drvdata(dev);
214+
return sprintf(buf, "%ld", clk_get_rate(driver->clks[0]));
215+
}
216+
217+
static ssize_t sifive_u54_pll_rate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
218+
{
219+
struct sifive_u54_prci_driver *driver = dev_get_drvdata(dev);
220+
unsigned long rate;
221+
char *endp;
222+
223+
rate = simple_strtoul(buf, &endp, 0);
224+
if (*endp != 0 && *endp != '\n')
225+
return -EINVAL;
226+
227+
clk_set_rate(driver->clks[0], rate);
228+
return count;
229+
}
230+
231+
static DEVICE_ATTR(rate, 0644, sifive_u54_pll_show, sifive_u54_pll_rate_store);
232+
233+
static int sifive_u54_prci_probe(struct platform_device *pdev)
234+
{
235+
struct device *dev = &pdev->dev;
236+
struct clk_init_data init;
237+
struct sifive_u54_prci_driver *driver;
238+
struct resource *res;
239+
const char *parent;
240+
int i;
241+
242+
parent = of_clk_get_parent_name(dev->of_node, 0);
243+
if (!parent) {
244+
dev_err(dev, "No OF parent clocks found\n");
245+
return -EINVAL;
246+
}
247+
248+
driver = devm_kzalloc(dev, sizeof(*driver), GFP_KERNEL);
249+
if (!driver) {
250+
dev_err(dev, "Out of memory\n");
251+
return -ENOMEM;
252+
}
253+
254+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
255+
driver->reg = devm_ioremap_resource(dev, res);
256+
if (IS_ERR(driver->reg))
257+
return PTR_ERR(driver->reg);
258+
259+
/* Link the data structure */
260+
driver->table.clk_num = PRCI_CLOCKS;
261+
driver->table.clks = &driver->clks[0];
262+
dev_set_drvdata(dev, driver);
263+
264+
/* Describe the clocks */
265+
snprintf(driver->plls[CORE_CLOCK].name, NAME_LEN, "%s.core", dev->of_node->name);
266+
driver->plls[CORE_CLOCK].freq = 0x4;
267+
driver->plls[CORE_CLOCK].glcm = 0x24;
268+
snprintf(driver->plls[GEMTX_CLOCK].name, NAME_LEN, "%s.gemtx", dev->of_node->name);
269+
driver->plls[GEMTX_CLOCK].freq = 0x1c;
270+
driver->plls[GEMTX_CLOCK].glcm = 0; /* None; cannot be set_rate */
271+
272+
/* Export the clocks */
273+
for (i = 0; i < PRCI_CLOCKS; ++i) {
274+
init.name = &driver->plls[i].name[0];
275+
init.ops = driver->plls[i].glcm ? &sifive_u54_prci_ops_rw : &sifive_u54_prci_ops_ro;
276+
init.num_parents = 1;
277+
init.parent_names = &parent;
278+
init.flags = 0;
279+
280+
driver->plls[i].driver = driver;
281+
driver->plls[i].hw.init = &init;
282+
283+
driver->clks[i] = devm_clk_register(dev, &driver->plls[i].hw);
284+
if (IS_ERR(driver->clks[i])) {
285+
dev_err(dev, "Failed to register clock %d, %ld\n", i, PTR_ERR(driver->clks[i]));
286+
return PTR_ERR(driver->clks[i]);
287+
}
288+
}
289+
290+
of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &driver->table);
291+
device_create_file(dev, &dev_attr_rate);
292+
dev_info(dev, "Registered U54 core clocks\n");
293+
294+
return 0;
295+
}
296+
297+
static const struct of_device_id sifive_u54_prci_of_match[] = {
298+
{ .compatible = "sifive,aloeprci0", },
299+
{}
300+
};
301+
302+
static struct platform_driver sifive_u54_prci_driver = {
303+
.driver = {
304+
.name = "sifive-u54-prci",
305+
.of_match_table = sifive_u54_prci_of_match,
306+
},
307+
.probe = sifive_u54_prci_probe,
308+
};
309+
310+
static int __init sifive_u54_prci_init(void)
311+
{
312+
return platform_driver_register(&sifive_u54_prci_driver);
313+
}
314+
core_initcall(sifive_u54_prci_init);

0 commit comments

Comments
 (0)