1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Marvell Armada XP SoC clocks |
4 | * |
5 | * Copyright (C) 2012 Marvell |
6 | * |
7 | * Gregory CLEMENT <gregory.clement@free-electrons.com> |
8 | * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> |
9 | * Andrew Lunn <andrew@lunn.ch> |
10 | * |
11 | */ |
12 | |
13 | #include <linux/kernel.h> |
14 | #include <linux/clk-provider.h> |
15 | #include <linux/io.h> |
16 | #include <linux/of.h> |
17 | #include "common.h" |
18 | |
19 | /* |
20 | * Core Clocks |
21 | * |
22 | * Armada XP Sample At Reset is a 64 bit bitfiled split in two |
23 | * register of 32 bits |
24 | */ |
25 | |
26 | #define SARL 0 /* Low part [0:31] */ |
27 | #define SARL_AXP_PCLK_FREQ_OPT 21 |
28 | #define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7 |
29 | #define SARL_AXP_FAB_FREQ_OPT 24 |
30 | #define SARL_AXP_FAB_FREQ_OPT_MASK 0xF |
31 | #define SARH 4 /* High part [32:63] */ |
32 | #define SARH_AXP_PCLK_FREQ_OPT (52-32) |
33 | #define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1 |
34 | #define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3 |
35 | #define SARH_AXP_FAB_FREQ_OPT (51-32) |
36 | #define SARH_AXP_FAB_FREQ_OPT_MASK 0x1 |
37 | #define SARH_AXP_FAB_FREQ_OPT_SHIFT 4 |
38 | |
39 | enum { AXP_CPU_TO_NBCLK, AXP_CPU_TO_HCLK, AXP_CPU_TO_DRAMCLK }; |
40 | |
41 | static const struct coreclk_ratio axp_coreclk_ratios[] __initconst = { |
42 | { .id = AXP_CPU_TO_NBCLK, .name = "nbclk" }, |
43 | { .id = AXP_CPU_TO_HCLK, .name = "hclk" }, |
44 | { .id = AXP_CPU_TO_DRAMCLK, .name = "dramclk" }, |
45 | }; |
46 | |
47 | /* Armada XP TCLK frequency is fixed to 250MHz */ |
48 | static u32 __init axp_get_tclk_freq(void __iomem *sar) |
49 | { |
50 | return 250000000; |
51 | } |
52 | |
53 | static const u32 axp_cpu_freqs[] __initconst = { |
54 | 1000000000, |
55 | 1066000000, |
56 | 1200000000, |
57 | 1333000000, |
58 | 1500000000, |
59 | 1666000000, |
60 | 1800000000, |
61 | 2000000000, |
62 | 667000000, |
63 | 0, |
64 | 800000000, |
65 | 1600000000, |
66 | }; |
67 | |
68 | static u32 __init axp_get_cpu_freq(void __iomem *sar) |
69 | { |
70 | u32 cpu_freq; |
71 | u8 cpu_freq_select = 0; |
72 | |
73 | cpu_freq_select = ((readl(addr: sar + SARL) >> SARL_AXP_PCLK_FREQ_OPT) & |
74 | SARL_AXP_PCLK_FREQ_OPT_MASK); |
75 | /* |
76 | * The upper bit is not contiguous to the other ones and |
77 | * located in the high part of the SAR registers |
78 | */ |
79 | cpu_freq_select |= (((readl(addr: sar + SARH) >> SARH_AXP_PCLK_FREQ_OPT) & |
80 | SARH_AXP_PCLK_FREQ_OPT_MASK) << SARH_AXP_PCLK_FREQ_OPT_SHIFT); |
81 | if (cpu_freq_select >= ARRAY_SIZE(axp_cpu_freqs)) { |
82 | pr_err("CPU freq select unsupported: %d\n" , cpu_freq_select); |
83 | cpu_freq = 0; |
84 | } else |
85 | cpu_freq = axp_cpu_freqs[cpu_freq_select]; |
86 | |
87 | return cpu_freq; |
88 | } |
89 | |
90 | static const int axp_nbclk_ratios[32][2] __initconst = { |
91 | {0, 1}, {1, 2}, {2, 2}, {2, 2}, |
92 | {1, 2}, {1, 2}, {1, 1}, {2, 3}, |
93 | {0, 1}, {1, 2}, {2, 4}, {0, 1}, |
94 | {1, 2}, {0, 1}, {0, 1}, {2, 2}, |
95 | {0, 1}, {0, 1}, {0, 1}, {1, 1}, |
96 | {2, 3}, {0, 1}, {0, 1}, {0, 1}, |
97 | {0, 1}, {0, 1}, {0, 1}, {1, 1}, |
98 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
99 | }; |
100 | |
101 | static const int axp_hclk_ratios[32][2] __initconst = { |
102 | {0, 1}, {1, 2}, {2, 6}, {2, 3}, |
103 | {1, 3}, {1, 4}, {1, 2}, {2, 6}, |
104 | {0, 1}, {1, 6}, {2, 10}, {0, 1}, |
105 | {1, 4}, {0, 1}, {0, 1}, {2, 5}, |
106 | {0, 1}, {0, 1}, {0, 1}, {1, 2}, |
107 | {2, 6}, {0, 1}, {0, 1}, {0, 1}, |
108 | {0, 1}, {0, 1}, {0, 1}, {1, 1}, |
109 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
110 | }; |
111 | |
112 | static const int axp_dramclk_ratios[32][2] __initconst = { |
113 | {0, 1}, {1, 2}, {2, 3}, {2, 3}, |
114 | {1, 3}, {1, 2}, {1, 2}, {2, 6}, |
115 | {0, 1}, {1, 3}, {2, 5}, {0, 1}, |
116 | {1, 4}, {0, 1}, {0, 1}, {2, 5}, |
117 | {0, 1}, {0, 1}, {0, 1}, {1, 1}, |
118 | {2, 3}, {0, 1}, {0, 1}, {0, 1}, |
119 | {0, 1}, {0, 1}, {0, 1}, {1, 1}, |
120 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
121 | }; |
122 | |
123 | static void __init axp_get_clk_ratio( |
124 | void __iomem *sar, int id, int *mult, int *div) |
125 | { |
126 | u32 opt = ((readl(addr: sar + SARL) >> SARL_AXP_FAB_FREQ_OPT) & |
127 | SARL_AXP_FAB_FREQ_OPT_MASK); |
128 | /* |
129 | * The upper bit is not contiguous to the other ones and |
130 | * located in the high part of the SAR registers |
131 | */ |
132 | opt |= (((readl(addr: sar + SARH) >> SARH_AXP_FAB_FREQ_OPT) & |
133 | SARH_AXP_FAB_FREQ_OPT_MASK) << SARH_AXP_FAB_FREQ_OPT_SHIFT); |
134 | |
135 | switch (id) { |
136 | case AXP_CPU_TO_NBCLK: |
137 | *mult = axp_nbclk_ratios[opt][0]; |
138 | *div = axp_nbclk_ratios[opt][1]; |
139 | break; |
140 | case AXP_CPU_TO_HCLK: |
141 | *mult = axp_hclk_ratios[opt][0]; |
142 | *div = axp_hclk_ratios[opt][1]; |
143 | break; |
144 | case AXP_CPU_TO_DRAMCLK: |
145 | *mult = axp_dramclk_ratios[opt][0]; |
146 | *div = axp_dramclk_ratios[opt][1]; |
147 | break; |
148 | } |
149 | } |
150 | |
151 | static const struct coreclk_soc_desc axp_coreclks = { |
152 | .get_tclk_freq = axp_get_tclk_freq, |
153 | .get_cpu_freq = axp_get_cpu_freq, |
154 | .get_clk_ratio = axp_get_clk_ratio, |
155 | .ratios = axp_coreclk_ratios, |
156 | .num_ratios = ARRAY_SIZE(axp_coreclk_ratios), |
157 | }; |
158 | |
159 | /* |
160 | * Clock Gating Control |
161 | */ |
162 | |
163 | static const struct clk_gating_soc_desc axp_gating_desc[] __initconst = { |
164 | { "audio" , NULL, 0, 0 }, |
165 | { "ge3" , NULL, 1, 0 }, |
166 | { "ge2" , NULL, 2, 0 }, |
167 | { "ge1" , NULL, 3, 0 }, |
168 | { "ge0" , NULL, 4, 0 }, |
169 | { "pex00" , NULL, 5, 0 }, |
170 | { "pex01" , NULL, 6, 0 }, |
171 | { "pex02" , NULL, 7, 0 }, |
172 | { "pex03" , NULL, 8, 0 }, |
173 | { "pex10" , NULL, 9, 0 }, |
174 | { "pex11" , NULL, 10, 0 }, |
175 | { "pex12" , NULL, 11, 0 }, |
176 | { "pex13" , NULL, 12, 0 }, |
177 | { "bp" , NULL, 13, 0 }, |
178 | { "sata0lnk" , NULL, 14, 0 }, |
179 | { "sata0" , "sata0lnk" , 15, 0 }, |
180 | { "lcd" , NULL, 16, 0 }, |
181 | { "sdio" , NULL, 17, 0 }, |
182 | { "usb0" , NULL, 18, 0 }, |
183 | { "usb1" , NULL, 19, 0 }, |
184 | { "usb2" , NULL, 20, 0 }, |
185 | { "xor0" , NULL, 22, 0 }, |
186 | { "crypto" , NULL, 23, 0 }, |
187 | { "tdm" , NULL, 25, 0 }, |
188 | { "pex20" , NULL, 26, 0 }, |
189 | { "pex30" , NULL, 27, 0 }, |
190 | { "xor1" , NULL, 28, 0 }, |
191 | { "sata1lnk" , NULL, 29, 0 }, |
192 | { "sata1" , "sata1lnk" , 30, 0 }, |
193 | { } |
194 | }; |
195 | |
196 | static void __init axp_clk_init(struct device_node *np) |
197 | { |
198 | struct device_node *cgnp = |
199 | of_find_compatible_node(NULL, NULL, compat: "marvell,armada-xp-gating-clock" ); |
200 | |
201 | mvebu_coreclk_setup(np, desc: &axp_coreclks); |
202 | |
203 | if (cgnp) { |
204 | mvebu_clk_gating_setup(np: cgnp, desc: axp_gating_desc); |
205 | of_node_put(node: cgnp); |
206 | } |
207 | } |
208 | CLK_OF_DECLARE(axp_clk, "marvell,armada-xp-core-clock" , axp_clk_init); |
209 | |