[go: nahoru, domu]

1/*
2 * Marvell MVEBU CPU clock handling.
3 *
4 * Copyright (C) 2012 Marvell
5 *
6 * Gregory CLEMENT <gregory.clement@free-electrons.com>
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2.  This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
11 */
12#include <linux/kernel.h>
13#include <linux/clkdev.h>
14#include <linux/clk-provider.h>
15#include <linux/of_address.h>
16#include <linux/io.h>
17#include <linux/of.h>
18#include <linux/delay.h>
19#include <linux/mvebu-pmsu.h>
20#include <asm/smp_plat.h>
21
22#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET               0x0
23#define   SYS_CTRL_CLK_DIVIDER_CTRL_RESET_ALL          0xff
24#define   SYS_CTRL_CLK_DIVIDER_CTRL_RESET_SHIFT        8
25#define SYS_CTRL_CLK_DIVIDER_CTRL2_OFFSET              0x8
26#define   SYS_CTRL_CLK_DIVIDER_CTRL2_NBCLK_RATIO_SHIFT 16
27#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET              0xC
28#define SYS_CTRL_CLK_DIVIDER_MASK                      0x3F
29
30#define PMU_DFS_RATIO_SHIFT 16
31#define PMU_DFS_RATIO_MASK  0x3F
32
33#define MAX_CPU	    4
34struct cpu_clk {
35	struct clk_hw hw;
36	int cpu;
37	const char *clk_name;
38	const char *parent_name;
39	void __iomem *reg_base;
40	void __iomem *pmu_dfs;
41};
42
43static struct clk **clks;
44
45static struct clk_onecell_data clk_data;
46
47#define to_cpu_clk(p) container_of(p, struct cpu_clk, hw)
48
49static unsigned long clk_cpu_recalc_rate(struct clk_hw *hwclk,
50					 unsigned long parent_rate)
51{
52	struct cpu_clk *cpuclk = to_cpu_clk(hwclk);
53	u32 reg, div;
54
55	reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET);
56	div = (reg >> (cpuclk->cpu * 8)) & SYS_CTRL_CLK_DIVIDER_MASK;
57	return parent_rate / div;
58}
59
60static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate,
61			       unsigned long *parent_rate)
62{
63	/* Valid ratio are 1:1, 1:2 and 1:3 */
64	u32 div;
65
66	div = *parent_rate / rate;
67	if (div == 0)
68		div = 1;
69	else if (div > 3)
70		div = 3;
71
72	return *parent_rate / div;
73}
74
75static int clk_cpu_off_set_rate(struct clk_hw *hwclk, unsigned long rate,
76				unsigned long parent_rate)
77
78{
79	struct cpu_clk *cpuclk = to_cpu_clk(hwclk);
80	u32 reg, div;
81	u32 reload_mask;
82
83	div = parent_rate / rate;
84	reg = (readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET)
85		& (~(SYS_CTRL_CLK_DIVIDER_MASK << (cpuclk->cpu * 8))))
86		| (div << (cpuclk->cpu * 8));
87	writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET);
88	/* Set clock divider reload smooth bit mask */
89	reload_mask = 1 << (20 + cpuclk->cpu);
90
91	reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET)
92	    | reload_mask;
93	writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
94
95	/* Now trigger the clock update */
96	reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET)
97	    | 1 << 24;
98	writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
99
100	/* Wait for clocks to settle down then clear reload request */
101	udelay(1000);
102	reg &= ~(reload_mask | 1 << 24);
103	writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
104	udelay(1000);
105
106	return 0;
107}
108
109static int clk_cpu_on_set_rate(struct clk_hw *hwclk, unsigned long rate,
110			       unsigned long parent_rate)
111{
112	u32 reg;
113	unsigned long fabric_div, target_div, cur_rate;
114	struct cpu_clk *cpuclk = to_cpu_clk(hwclk);
115
116	/*
117	 * PMU DFS registers are not mapped, Device Tree does not
118	 * describes them. We cannot change the frequency dynamically.
119	 */
120	if (!cpuclk->pmu_dfs)
121		return -ENODEV;
122
123	cur_rate = __clk_get_rate(hwclk->clk);
124
125	reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL2_OFFSET);
126	fabric_div = (reg >> SYS_CTRL_CLK_DIVIDER_CTRL2_NBCLK_RATIO_SHIFT) &
127		SYS_CTRL_CLK_DIVIDER_MASK;
128
129	/* Frequency is going up */
130	if (rate == 2 * cur_rate)
131		target_div = fabric_div / 2;
132	/* Frequency is going down */
133	else
134		target_div = fabric_div;
135
136	if (target_div == 0)
137		target_div = 1;
138
139	reg = readl(cpuclk->pmu_dfs);
140	reg &= ~(PMU_DFS_RATIO_MASK << PMU_DFS_RATIO_SHIFT);
141	reg |= (target_div << PMU_DFS_RATIO_SHIFT);
142	writel(reg, cpuclk->pmu_dfs);
143
144	reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
145	reg |= (SYS_CTRL_CLK_DIVIDER_CTRL_RESET_ALL <<
146		SYS_CTRL_CLK_DIVIDER_CTRL_RESET_SHIFT);
147	writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
148
149	return mvebu_pmsu_dfs_request(cpuclk->cpu);
150}
151
152static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate,
153			    unsigned long parent_rate)
154{
155	if (__clk_is_enabled(hwclk->clk))
156		return clk_cpu_on_set_rate(hwclk, rate, parent_rate);
157	else
158		return clk_cpu_off_set_rate(hwclk, rate, parent_rate);
159}
160
161static const struct clk_ops cpu_ops = {
162	.recalc_rate = clk_cpu_recalc_rate,
163	.round_rate = clk_cpu_round_rate,
164	.set_rate = clk_cpu_set_rate,
165};
166
167static void __init of_cpu_clk_setup(struct device_node *node)
168{
169	struct cpu_clk *cpuclk;
170	void __iomem *clock_complex_base = of_iomap(node, 0);
171	void __iomem *pmu_dfs_base = of_iomap(node, 1);
172	int ncpus = 0;
173	struct device_node *dn;
174
175	if (clock_complex_base == NULL) {
176		pr_err("%s: clock-complex base register not set\n",
177			__func__);
178		return;
179	}
180
181	if (pmu_dfs_base == NULL)
182		pr_warn("%s: pmu-dfs base register not set, dynamic frequency scaling not available\n",
183			__func__);
184
185	for_each_node_by_type(dn, "cpu")
186		ncpus++;
187
188	cpuclk = kzalloc(ncpus * sizeof(*cpuclk), GFP_KERNEL);
189	if (WARN_ON(!cpuclk))
190		goto cpuclk_out;
191
192	clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL);
193	if (WARN_ON(!clks))
194		goto clks_out;
195
196	for_each_node_by_type(dn, "cpu") {
197		struct clk_init_data init;
198		struct clk *clk;
199		struct clk *parent_clk;
200		char *clk_name = kzalloc(5, GFP_KERNEL);
201		int cpu, err;
202
203		if (WARN_ON(!clk_name))
204			goto bail_out;
205
206		err = of_property_read_u32(dn, "reg", &cpu);
207		if (WARN_ON(err))
208			goto bail_out;
209
210		sprintf(clk_name, "cpu%d", cpu);
211		parent_clk = of_clk_get(node, 0);
212
213		cpuclk[cpu].parent_name = __clk_get_name(parent_clk);
214		cpuclk[cpu].clk_name = clk_name;
215		cpuclk[cpu].cpu = cpu;
216		cpuclk[cpu].reg_base = clock_complex_base;
217		if (pmu_dfs_base)
218			cpuclk[cpu].pmu_dfs = pmu_dfs_base + 4 * cpu;
219		cpuclk[cpu].hw.init = &init;
220
221		init.name = cpuclk[cpu].clk_name;
222		init.ops = &cpu_ops;
223		init.flags = 0;
224		init.parent_names = &cpuclk[cpu].parent_name;
225		init.num_parents = 1;
226
227		clk = clk_register(NULL, &cpuclk[cpu].hw);
228		if (WARN_ON(IS_ERR(clk)))
229			goto bail_out;
230		clks[cpu] = clk;
231	}
232	clk_data.clk_num = MAX_CPU;
233	clk_data.clks = clks;
234	of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data);
235
236	return;
237bail_out:
238	kfree(clks);
239	while(ncpus--)
240		kfree(cpuclk[ncpus].clk_name);
241clks_out:
242	kfree(cpuclk);
243cpuclk_out:
244	iounmap(clock_complex_base);
245}
246
247CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock",
248					 of_cpu_clk_setup);
249