1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Marvell Armada 375 SoC clocks |
4 | * |
5 | * Copyright (C) 2014 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 | |
23 | /* |
24 | * For the Armada 375 SoCs, the CPU, DDR and L2 clocks frequencies are |
25 | * all modified at the same time, and not separately as for the Armada |
26 | * 370 or the Armada XP SoCs. |
27 | * |
28 | * SAR1[21:17] : CPU frequency DDR frequency L2 frequency |
29 | * 6 = 400 MHz 400 MHz 200 MHz |
30 | * 15 = 600 MHz 600 MHz 300 MHz |
31 | * 21 = 800 MHz 534 MHz 400 MHz |
32 | * 25 = 1000 MHz 500 MHz 500 MHz |
33 | * others reserved. |
34 | * |
35 | * SAR1[22] : TCLK frequency |
36 | * 0 = 166 MHz |
37 | * 1 = 200 MHz |
38 | */ |
39 | |
40 | #define SAR1_A375_TCLK_FREQ_OPT 22 |
41 | #define SAR1_A375_TCLK_FREQ_OPT_MASK 0x1 |
42 | #define SAR1_A375_CPU_DDR_L2_FREQ_OPT 17 |
43 | #define SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK 0x1F |
44 | |
45 | static const u32 armada_375_tclk_frequencies[] __initconst = { |
46 | 166000000, |
47 | 200000000, |
48 | }; |
49 | |
50 | static u32 __init armada_375_get_tclk_freq(void __iomem *sar) |
51 | { |
52 | u8 tclk_freq_select; |
53 | |
54 | tclk_freq_select = ((readl(addr: sar) >> SAR1_A375_TCLK_FREQ_OPT) & |
55 | SAR1_A375_TCLK_FREQ_OPT_MASK); |
56 | return armada_375_tclk_frequencies[tclk_freq_select]; |
57 | } |
58 | |
59 | |
60 | static const u32 armada_375_cpu_frequencies[] __initconst = { |
61 | 0, 0, 0, 0, 0, 0, |
62 | 400000000, |
63 | 0, 0, 0, 0, 0, 0, 0, 0, |
64 | 600000000, |
65 | 0, 0, 0, 0, 0, |
66 | 800000000, |
67 | 0, 0, 0, |
68 | 1000000000, |
69 | }; |
70 | |
71 | static u32 __init armada_375_get_cpu_freq(void __iomem *sar) |
72 | { |
73 | u8 cpu_freq_select; |
74 | |
75 | cpu_freq_select = ((readl(addr: sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) & |
76 | SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK); |
77 | if (cpu_freq_select >= ARRAY_SIZE(armada_375_cpu_frequencies)) { |
78 | pr_err("Selected CPU frequency (%d) unsupported\n" , |
79 | cpu_freq_select); |
80 | return 0; |
81 | } else |
82 | return armada_375_cpu_frequencies[cpu_freq_select]; |
83 | } |
84 | |
85 | enum { A375_CPU_TO_DDR, A375_CPU_TO_L2 }; |
86 | |
87 | static const struct coreclk_ratio armada_375_coreclk_ratios[] __initconst = { |
88 | { .id = A375_CPU_TO_L2, .name = "l2clk" }, |
89 | { .id = A375_CPU_TO_DDR, .name = "ddrclk" }, |
90 | }; |
91 | |
92 | static const int armada_375_cpu_l2_ratios[32][2] __initconst = { |
93 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
94 | {0, 1}, {0, 1}, {1, 2}, {0, 1}, |
95 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
96 | {0, 1}, {0, 1}, {0, 1}, {1, 2}, |
97 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
98 | {0, 1}, {1, 2}, {0, 1}, {0, 1}, |
99 | {0, 1}, {1, 2}, {0, 1}, {0, 1}, |
100 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
101 | }; |
102 | |
103 | static const int armada_375_cpu_ddr_ratios[32][2] __initconst = { |
104 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
105 | {0, 1}, {0, 1}, {1, 1}, {0, 1}, |
106 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
107 | {0, 1}, {0, 1}, {0, 1}, {2, 3}, |
108 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
109 | {0, 1}, {2, 3}, {0, 1}, {0, 1}, |
110 | {0, 1}, {1, 2}, {0, 1}, {0, 1}, |
111 | {0, 1}, {0, 1}, {0, 1}, {0, 1}, |
112 | }; |
113 | |
114 | static void __init armada_375_get_clk_ratio( |
115 | void __iomem *sar, int id, int *mult, int *div) |
116 | { |
117 | u32 opt = ((readl(addr: sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) & |
118 | SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK); |
119 | |
120 | switch (id) { |
121 | case A375_CPU_TO_L2: |
122 | *mult = armada_375_cpu_l2_ratios[opt][0]; |
123 | *div = armada_375_cpu_l2_ratios[opt][1]; |
124 | break; |
125 | case A375_CPU_TO_DDR: |
126 | *mult = armada_375_cpu_ddr_ratios[opt][0]; |
127 | *div = armada_375_cpu_ddr_ratios[opt][1]; |
128 | break; |
129 | } |
130 | } |
131 | |
132 | static const struct coreclk_soc_desc armada_375_coreclks = { |
133 | .get_tclk_freq = armada_375_get_tclk_freq, |
134 | .get_cpu_freq = armada_375_get_cpu_freq, |
135 | .get_clk_ratio = armada_375_get_clk_ratio, |
136 | .ratios = armada_375_coreclk_ratios, |
137 | .num_ratios = ARRAY_SIZE(armada_375_coreclk_ratios), |
138 | }; |
139 | |
140 | static void __init armada_375_coreclk_init(struct device_node *np) |
141 | { |
142 | mvebu_coreclk_setup(np, desc: &armada_375_coreclks); |
143 | } |
144 | CLK_OF_DECLARE(armada_375_core_clk, "marvell,armada-375-core-clock" , |
145 | armada_375_coreclk_init); |
146 | |
147 | /* |
148 | * Clock Gating Control |
149 | */ |
150 | static const struct clk_gating_soc_desc armada_375_gating_desc[] __initconst = { |
151 | { "mu" , NULL, 2 }, |
152 | { "pp" , NULL, 3 }, |
153 | { "ptp" , NULL, 4 }, |
154 | { "pex0" , NULL, 5 }, |
155 | { "pex1" , NULL, 6 }, |
156 | { "audio" , NULL, 8 }, |
157 | { "nd_clk" , "nand" , 11 }, |
158 | { "sata0_link" , "sata0_core" , 14 }, |
159 | { "sata0_core" , NULL, 15 }, |
160 | { "usb3" , NULL, 16 }, |
161 | { "sdio" , NULL, 17 }, |
162 | { "usb" , NULL, 18 }, |
163 | { "gop" , NULL, 19 }, |
164 | { "sata1_link" , "sata1_core" , 20 }, |
165 | { "sata1_core" , NULL, 21 }, |
166 | { "xor0" , NULL, 22 }, |
167 | { "xor1" , NULL, 23 }, |
168 | { "copro" , NULL, 24 }, |
169 | { "tdm" , NULL, 25 }, |
170 | { "crypto0_enc" , NULL, 28 }, |
171 | { "crypto0_core" , NULL, 29 }, |
172 | { "crypto1_enc" , NULL, 30 }, |
173 | { "crypto1_core" , NULL, 31 }, |
174 | { } |
175 | }; |
176 | |
177 | static void __init armada_375_clk_gating_init(struct device_node *np) |
178 | { |
179 | mvebu_clk_gating_setup(np, desc: armada_375_gating_desc); |
180 | } |
181 | CLK_OF_DECLARE(armada_375_clk_gating, "marvell,armada-375-gating-clock" , |
182 | armada_375_clk_gating_init); |
183 | |