]>
Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
bda00303 RJ |
2 | /* |
3 | * Marvell PXA family clocks | |
4 | * | |
5 | * Copyright (C) 2014 Robert Jarzmik | |
6 | * | |
7 | * Common clock code for PXA clocks ("CKEN" type clocks + DT) | |
bda00303 RJ |
8 | */ |
9 | #include <linux/clk.h> | |
10 | #include <linux/clk-provider.h> | |
11 | #include <linux/clkdev.h> | |
62e59c4e | 12 | #include <linux/io.h> |
bda00303 RJ |
13 | #include <linux/of.h> |
14 | ||
15 | #include <dt-bindings/clock/pxa-clock.h> | |
16 | #include "clk-pxa.h" | |
17 | ||
9fe69429 RJ |
18 | #define KHz 1000 |
19 | #define MHz (1000 * 1000) | |
20 | ||
21 | #define MDREFR_K0DB4 (1 << 29) /* SDCLK0 Divide by 4 Control/Status */ | |
22 | #define MDREFR_K2FREE (1 << 25) /* SDRAM Free-Running Control */ | |
23 | #define MDREFR_K1FREE (1 << 24) /* SDRAM Free-Running Control */ | |
24 | #define MDREFR_K0FREE (1 << 23) /* SDRAM Free-Running Control */ | |
25 | #define MDREFR_SLFRSH (1 << 22) /* SDRAM Self-Refresh Control/Status */ | |
26 | #define MDREFR_APD (1 << 20) /* SDRAM/SSRAM Auto-Power-Down Enable */ | |
27 | #define MDREFR_K2DB2 (1 << 19) /* SDCLK2 Divide by 2 Control/Status */ | |
28 | #define MDREFR_K2RUN (1 << 18) /* SDCLK2 Run Control/Status */ | |
29 | #define MDREFR_K1DB2 (1 << 17) /* SDCLK1 Divide by 2 Control/Status */ | |
30 | #define MDREFR_K1RUN (1 << 16) /* SDCLK1 Run Control/Status */ | |
31 | #define MDREFR_E1PIN (1 << 15) /* SDCKE1 Level Control/Status */ | |
32 | #define MDREFR_K0DB2 (1 << 14) /* SDCLK0 Divide by 2 Control/Status */ | |
33 | #define MDREFR_K0RUN (1 << 13) /* SDCLK0 Run Control/Status */ | |
34 | #define MDREFR_E0PIN (1 << 12) /* SDCKE0 Level Control/Status */ | |
35 | #define MDREFR_DB2_MASK (MDREFR_K2DB2 | MDREFR_K1DB2) | |
36 | #define MDREFR_DRI_MASK 0xFFF | |
37 | ||
84558ff7 | 38 | static DEFINE_SPINLOCK(pxa_clk_lock); |
bda00303 RJ |
39 | |
40 | static struct clk *pxa_clocks[CLK_MAX]; | |
41 | static struct clk_onecell_data onecell_data = { | |
42 | .clks = pxa_clocks, | |
43 | .clk_num = CLK_MAX, | |
44 | }; | |
45 | ||
14dd5b01 RJ |
46 | struct pxa_clk { |
47 | struct clk_hw hw; | |
48 | struct clk_fixed_factor lp; | |
49 | struct clk_fixed_factor hp; | |
50 | struct clk_gate gate; | |
51 | bool (*is_in_low_power)(void); | |
52 | }; | |
53 | ||
54 | #define to_pxa_clk(_hw) container_of(_hw, struct pxa_clk, hw) | |
bda00303 RJ |
55 | |
56 | static unsigned long cken_recalc_rate(struct clk_hw *hw, | |
57 | unsigned long parent_rate) | |
58 | { | |
14dd5b01 | 59 | struct pxa_clk *pclk = to_pxa_clk(hw); |
bda00303 RJ |
60 | struct clk_fixed_factor *fix; |
61 | ||
62 | if (!pclk->is_in_low_power || pclk->is_in_low_power()) | |
63 | fix = &pclk->lp; | |
64 | else | |
65 | fix = &pclk->hp; | |
4e907ef6 | 66 | __clk_hw_set_clk(&fix->hw, hw); |
bda00303 RJ |
67 | return clk_fixed_factor_ops.recalc_rate(&fix->hw, parent_rate); |
68 | } | |
69 | ||
5fc6eb7d | 70 | static const struct clk_ops cken_rate_ops = { |
bda00303 RJ |
71 | .recalc_rate = cken_recalc_rate, |
72 | }; | |
73 | ||
74 | static u8 cken_get_parent(struct clk_hw *hw) | |
75 | { | |
14dd5b01 | 76 | struct pxa_clk *pclk = to_pxa_clk(hw); |
bda00303 RJ |
77 | |
78 | if (!pclk->is_in_low_power) | |
79 | return 0; | |
80 | return pclk->is_in_low_power() ? 0 : 1; | |
81 | } | |
82 | ||
5fc6eb7d | 83 | static const struct clk_ops cken_mux_ops = { |
bda00303 RJ |
84 | .get_parent = cken_get_parent, |
85 | .set_parent = dummy_clk_set_parent, | |
86 | }; | |
87 | ||
88 | void __init clkdev_pxa_register(int ckid, const char *con_id, | |
89 | const char *dev_id, struct clk *clk) | |
90 | { | |
91 | if (!IS_ERR(clk) && (ckid != CLK_NONE)) | |
92 | pxa_clocks[ckid] = clk; | |
93 | if (!IS_ERR(clk)) | |
94 | clk_register_clkdev(clk, con_id, dev_id); | |
95 | } | |
96 | ||
14dd5b01 | 97 | int __init clk_pxa_cken_init(const struct desc_clk_cken *clks, int nb_clks) |
bda00303 RJ |
98 | { |
99 | int i; | |
14dd5b01 | 100 | struct pxa_clk *pxa_clk; |
bda00303 RJ |
101 | struct clk *clk; |
102 | ||
103 | for (i = 0; i < nb_clks; i++) { | |
14dd5b01 RJ |
104 | pxa_clk = kzalloc(sizeof(*pxa_clk), GFP_KERNEL); |
105 | pxa_clk->is_in_low_power = clks[i].is_in_low_power; | |
106 | pxa_clk->lp = clks[i].lp; | |
107 | pxa_clk->hp = clks[i].hp; | |
108 | pxa_clk->gate = clks[i].gate; | |
84558ff7 | 109 | pxa_clk->gate.lock = &pxa_clk_lock; |
14dd5b01 RJ |
110 | clk = clk_register_composite(NULL, clks[i].name, |
111 | clks[i].parent_names, 2, | |
112 | &pxa_clk->hw, &cken_mux_ops, | |
113 | &pxa_clk->hw, &cken_rate_ops, | |
114 | &pxa_clk->gate.hw, &clk_gate_ops, | |
115 | clks[i].flags); | |
116 | clkdev_pxa_register(clks[i].ckid, clks[i].con_id, | |
117 | clks[i].dev_id, clk); | |
bda00303 RJ |
118 | } |
119 | return 0; | |
120 | } | |
121 | ||
6f8a444a | 122 | void __init clk_pxa_dt_common_init(struct device_node *np) |
bda00303 RJ |
123 | { |
124 | of_clk_add_provider(np, of_clk_src_onecell_get, &onecell_data); | |
125 | } | |
9fe69429 RJ |
126 | |
127 | void pxa2xx_core_turbo_switch(bool on) | |
128 | { | |
129 | unsigned long flags; | |
130 | unsigned int unused, clkcfg; | |
131 | ||
132 | local_irq_save(flags); | |
133 | ||
134 | asm("mrc p14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); | |
135 | clkcfg &= ~CLKCFG_TURBO & ~CLKCFG_HALFTURBO; | |
136 | if (on) | |
137 | clkcfg |= CLKCFG_TURBO; | |
138 | clkcfg |= CLKCFG_FCS; | |
139 | ||
140 | asm volatile( | |
141 | " b 2f\n" | |
142 | " .align 5\n" | |
143 | "1: mcr p14, 0, %1, c6, c0, 0\n" | |
144 | " b 3f\n" | |
145 | "2: b 1b\n" | |
146 | "3: nop\n" | |
c82a2cb8 | 147 | : "=&r" (unused) : "r" (clkcfg)); |
9fe69429 RJ |
148 | |
149 | local_irq_restore(flags); | |
150 | } | |
151 | ||
152 | void pxa2xx_cpll_change(struct pxa2xx_freq *freq, | |
84558ff7 SB |
153 | u32 (*mdrefr_dri)(unsigned int), void __iomem *mdrefr, |
154 | void __iomem *cccr) | |
9fe69429 RJ |
155 | { |
156 | unsigned int clkcfg = freq->clkcfg; | |
157 | unsigned int unused, preset_mdrefr, postset_mdrefr; | |
158 | unsigned long flags; | |
159 | ||
160 | local_irq_save(flags); | |
161 | ||
162 | /* Calculate the next MDREFR. If we're slowing down the SDRAM clock | |
163 | * we need to preset the smaller DRI before the change. If we're | |
164 | * speeding up we need to set the larger DRI value after the change. | |
165 | */ | |
166 | preset_mdrefr = postset_mdrefr = readl(mdrefr); | |
167 | if ((preset_mdrefr & MDREFR_DRI_MASK) > mdrefr_dri(freq->membus_khz)) { | |
168 | preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK); | |
169 | preset_mdrefr |= mdrefr_dri(freq->membus_khz); | |
170 | } | |
171 | postset_mdrefr = | |
172 | (postset_mdrefr & ~MDREFR_DRI_MASK) | | |
173 | mdrefr_dri(freq->membus_khz); | |
174 | ||
175 | /* If we're dividing the memory clock by two for the SDRAM clock, this | |
176 | * must be set prior to the change. Clearing the divide must be done | |
177 | * after the change. | |
178 | */ | |
179 | if (freq->div2) { | |
180 | preset_mdrefr |= MDREFR_DB2_MASK; | |
181 | postset_mdrefr |= MDREFR_DB2_MASK; | |
182 | } else { | |
183 | postset_mdrefr &= ~MDREFR_DB2_MASK; | |
184 | } | |
185 | ||
186 | /* Set new the CCCR and prepare CLKCFG */ | |
187 | writel(freq->cccr, cccr); | |
188 | ||
189 | asm volatile( | |
190 | " ldr r4, [%1]\n" | |
191 | " b 2f\n" | |
192 | " .align 5\n" | |
193 | "1: str %3, [%1] /* preset the MDREFR */\n" | |
194 | " mcr p14, 0, %2, c6, c0, 0 /* set CLKCFG[FCS] */\n" | |
195 | " str %4, [%1] /* postset the MDREFR */\n" | |
196 | " b 3f\n" | |
197 | "2: b 1b\n" | |
198 | "3: nop\n" | |
199 | : "=&r" (unused) | |
200 | : "r" (mdrefr), "r" (clkcfg), "r" (preset_mdrefr), | |
201 | "r" (postset_mdrefr) | |
202 | : "r4", "r5"); | |
203 | ||
204 | local_irq_restore(flags); | |
205 | } | |
206 | ||
207 | int pxa2xx_determine_rate(struct clk_rate_request *req, | |
208 | struct pxa2xx_freq *freqs, int nb_freqs) | |
209 | { | |
2517b32b | 210 | int i, closest_below = -1, closest_above = -1; |
9fe69429 RJ |
211 | unsigned long rate; |
212 | ||
213 | for (i = 0; i < nb_freqs; i++) { | |
214 | rate = freqs[i].cpll; | |
215 | if (rate == req->rate) | |
216 | break; | |
217 | if (rate < req->min_rate) | |
218 | continue; | |
219 | if (rate > req->max_rate) | |
220 | continue; | |
221 | if (rate <= req->rate) | |
222 | closest_below = i; | |
223 | if ((rate >= req->rate) && (closest_above == -1)) | |
224 | closest_above = i; | |
225 | } | |
226 | ||
227 | req->best_parent_hw = NULL; | |
228 | ||
2517b32b AB |
229 | if (i < nb_freqs) { |
230 | rate = req->rate; | |
231 | } else if (closest_below >= 0) { | |
9fe69429 | 232 | rate = freqs[closest_below].cpll; |
2517b32b | 233 | } else if (closest_above >= 0) { |
9fe69429 | 234 | rate = freqs[closest_above].cpll; |
2517b32b AB |
235 | } else { |
236 | pr_debug("%s(rate=%lu) no match\n", __func__, req->rate); | |
237 | return -EINVAL; | |
238 | } | |
9fe69429 | 239 | |
2517b32b AB |
240 | pr_debug("%s(rate=%lu) rate=%lu\n", __func__, req->rate, rate); |
241 | req->rate = rate; | |
9fe69429 | 242 | |
2517b32b | 243 | return 0; |
9fe69429 | 244 | } |