Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Marvell Kirkwood SoC clocks |
| 3 | * |
| 4 | * Copyright (C) 2012 Marvell |
| 5 | * |
| 6 | * Gregory CLEMENT <gregory.clement@free-electrons.com> |
| 7 | * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> |
| 8 | * Andrew Lunn <andrew@lunn.ch> |
| 9 | * |
| 10 | * This file is licensed under the terms of the GNU General Public |
| 11 | * License version 2. This program is licensed "as is" without any |
| 12 | * warranty of any kind, whether express or implied. |
| 13 | */ |
| 14 | |
| 15 | #include <linux/kernel.h> |
Mike Turquette | e8e8a9b | 2014-08-27 17:11:38 -0700 | [diff] [blame] | 16 | #include <linux/slab.h> |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 17 | #include <linux/clk-provider.h> |
| 18 | #include <linux/io.h> |
| 19 | #include <linux/of.h> |
Mike Turquette | e8e8a9b | 2014-08-27 17:11:38 -0700 | [diff] [blame] | 20 | #include <linux/of_address.h> |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 21 | #include "common.h" |
| 22 | |
| 23 | /* |
| 24 | * Core Clocks |
| 25 | * |
| 26 | * Kirkwood PLL sample-at-reset configuration |
| 27 | * (6180 has different SAR layout than other Kirkwood SoCs) |
| 28 | * |
| 29 | * SAR0[4:3,22,1] : CPU frequency (6281,6292,6282) |
| 30 | * 4 = 600 MHz |
| 31 | * 6 = 800 MHz |
| 32 | * 7 = 1000 MHz |
| 33 | * 9 = 1200 MHz |
| 34 | * 12 = 1500 MHz |
| 35 | * 13 = 1600 MHz |
| 36 | * 14 = 1800 MHz |
| 37 | * 15 = 2000 MHz |
| 38 | * others reserved. |
| 39 | * |
| 40 | * SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282) |
| 41 | * 1 = (1/2) * CPU |
| 42 | * 3 = (1/3) * CPU |
| 43 | * 5 = (1/4) * CPU |
| 44 | * others reserved. |
| 45 | * |
| 46 | * SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282) |
| 47 | * 2 = (1/2) * CPU |
| 48 | * 4 = (1/3) * CPU |
| 49 | * 6 = (1/4) * CPU |
| 50 | * 7 = (2/9) * CPU |
| 51 | * 8 = (1/5) * CPU |
| 52 | * 9 = (1/6) * CPU |
| 53 | * others reserved. |
| 54 | * |
| 55 | * SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only) |
| 56 | * 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU] |
| 57 | * 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU] |
| 58 | * 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU] |
| 59 | * others reserved. |
| 60 | * |
| 61 | * SAR0[21] : TCLK frequency |
| 62 | * 0 = 200 MHz |
| 63 | * 1 = 166 MHz |
| 64 | * others reserved. |
| 65 | */ |
| 66 | |
| 67 | #define SAR_KIRKWOOD_CPU_FREQ(x) \ |
| 68 | (((x & (1 << 1)) >> 1) | \ |
| 69 | ((x & (1 << 22)) >> 21) | \ |
| 70 | ((x & (3 << 3)) >> 1)) |
| 71 | #define SAR_KIRKWOOD_L2_RATIO(x) \ |
| 72 | (((x & (3 << 9)) >> 9) | \ |
| 73 | (((x & (1 << 19)) >> 17))) |
| 74 | #define SAR_KIRKWOOD_DDR_RATIO 5 |
| 75 | #define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf |
| 76 | #define SAR_MV88F6180_CLK 2 |
| 77 | #define SAR_MV88F6180_CLK_MASK 0x7 |
| 78 | #define SAR_KIRKWOOD_TCLK_FREQ 21 |
| 79 | #define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1 |
| 80 | |
| 81 | enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR }; |
| 82 | |
Sachin Kamat | 847b1c0 | 2013-08-12 14:44:05 +0530 | [diff] [blame] | 83 | static const struct coreclk_ratio kirkwood_coreclk_ratios[] __initconst = { |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 84 | { .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", }, |
| 85 | { .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", } |
| 86 | }; |
| 87 | |
| 88 | static u32 __init kirkwood_get_tclk_freq(void __iomem *sar) |
| 89 | { |
| 90 | u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) & |
| 91 | SAR_KIRKWOOD_TCLK_FREQ_MASK; |
| 92 | return (opt) ? 166666667 : 200000000; |
| 93 | } |
| 94 | |
Sachin Kamat | 847b1c0 | 2013-08-12 14:44:05 +0530 | [diff] [blame] | 95 | static const u32 kirkwood_cpu_freqs[] __initconst = { |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 96 | 0, 0, 0, 0, |
| 97 | 600000000, |
| 98 | 0, |
| 99 | 800000000, |
| 100 | 1000000000, |
| 101 | 0, |
| 102 | 1200000000, |
| 103 | 0, 0, |
| 104 | 1500000000, |
| 105 | 1600000000, |
| 106 | 1800000000, |
| 107 | 2000000000 |
| 108 | }; |
| 109 | |
| 110 | static u32 __init kirkwood_get_cpu_freq(void __iomem *sar) |
| 111 | { |
| 112 | u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar)); |
| 113 | return kirkwood_cpu_freqs[opt]; |
| 114 | } |
| 115 | |
Sachin Kamat | 847b1c0 | 2013-08-12 14:44:05 +0530 | [diff] [blame] | 116 | static const int kirkwood_cpu_l2_ratios[8][2] __initconst = { |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 117 | { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 }, |
| 118 | { 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 } |
| 119 | }; |
| 120 | |
Sachin Kamat | 847b1c0 | 2013-08-12 14:44:05 +0530 | [diff] [blame] | 121 | static const int kirkwood_cpu_ddr_ratios[16][2] __initconst = { |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 122 | { 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, |
| 123 | { 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 }, |
| 124 | { 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 }, |
| 125 | { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 } |
| 126 | }; |
| 127 | |
| 128 | static void __init kirkwood_get_clk_ratio( |
| 129 | void __iomem *sar, int id, int *mult, int *div) |
| 130 | { |
| 131 | switch (id) { |
| 132 | case KIRKWOOD_CPU_TO_L2: |
| 133 | { |
| 134 | u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar)); |
| 135 | *mult = kirkwood_cpu_l2_ratios[opt][0]; |
| 136 | *div = kirkwood_cpu_l2_ratios[opt][1]; |
| 137 | break; |
| 138 | } |
| 139 | case KIRKWOOD_CPU_TO_DDR: |
| 140 | { |
| 141 | u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) & |
| 142 | SAR_KIRKWOOD_DDR_RATIO_MASK; |
| 143 | *mult = kirkwood_cpu_ddr_ratios[opt][0]; |
| 144 | *div = kirkwood_cpu_ddr_ratios[opt][1]; |
| 145 | break; |
| 146 | } |
| 147 | } |
| 148 | } |
| 149 | |
Sachin Kamat | 847b1c0 | 2013-08-12 14:44:05 +0530 | [diff] [blame] | 150 | static const u32 mv88f6180_cpu_freqs[] __initconst = { |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 151 | 0, 0, 0, 0, 0, |
| 152 | 600000000, |
| 153 | 800000000, |
| 154 | 1000000000 |
| 155 | }; |
| 156 | |
| 157 | static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar) |
| 158 | { |
| 159 | u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK; |
| 160 | return mv88f6180_cpu_freqs[opt]; |
| 161 | } |
| 162 | |
Sachin Kamat | 847b1c0 | 2013-08-12 14:44:05 +0530 | [diff] [blame] | 163 | static const int mv88f6180_cpu_ddr_ratios[8][2] __initconst = { |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 164 | { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, |
| 165 | { 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 } |
| 166 | }; |
| 167 | |
| 168 | static void __init mv88f6180_get_clk_ratio( |
| 169 | void __iomem *sar, int id, int *mult, int *div) |
| 170 | { |
| 171 | switch (id) { |
| 172 | case KIRKWOOD_CPU_TO_L2: |
| 173 | { |
| 174 | /* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */ |
| 175 | *mult = 1; |
| 176 | *div = 2; |
| 177 | break; |
| 178 | } |
| 179 | case KIRKWOOD_CPU_TO_DDR: |
| 180 | { |
| 181 | u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & |
| 182 | SAR_MV88F6180_CLK_MASK; |
| 183 | *mult = mv88f6180_cpu_ddr_ratios[opt][0]; |
| 184 | *div = mv88f6180_cpu_ddr_ratios[opt][1]; |
| 185 | break; |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | static const struct coreclk_soc_desc kirkwood_coreclks = { |
| 191 | .get_tclk_freq = kirkwood_get_tclk_freq, |
| 192 | .get_cpu_freq = kirkwood_get_cpu_freq, |
| 193 | .get_clk_ratio = kirkwood_get_clk_ratio, |
| 194 | .ratios = kirkwood_coreclk_ratios, |
| 195 | .num_ratios = ARRAY_SIZE(kirkwood_coreclk_ratios), |
| 196 | }; |
| 197 | |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 198 | static const struct coreclk_soc_desc mv88f6180_coreclks = { |
| 199 | .get_tclk_freq = kirkwood_get_tclk_freq, |
| 200 | .get_cpu_freq = mv88f6180_get_cpu_freq, |
| 201 | .get_clk_ratio = mv88f6180_get_clk_ratio, |
| 202 | .ratios = kirkwood_coreclk_ratios, |
| 203 | .num_ratios = ARRAY_SIZE(kirkwood_coreclk_ratios), |
| 204 | }; |
| 205 | |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 206 | /* |
| 207 | * Clock Gating Control |
| 208 | */ |
| 209 | |
Sachin Kamat | 847b1c0 | 2013-08-12 14:44:05 +0530 | [diff] [blame] | 210 | static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = { |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 211 | { "ge0", NULL, 0, 0 }, |
| 212 | { "pex0", NULL, 2, 0 }, |
| 213 | { "usb0", NULL, 3, 0 }, |
| 214 | { "sdio", NULL, 4, 0 }, |
| 215 | { "tsu", NULL, 5, 0 }, |
| 216 | { "runit", NULL, 7, 0 }, |
| 217 | { "xor0", NULL, 8, 0 }, |
| 218 | { "audio", NULL, 9, 0 }, |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 219 | { "sata0", NULL, 14, 0 }, |
| 220 | { "sata1", NULL, 15, 0 }, |
| 221 | { "xor1", NULL, 16, 0 }, |
| 222 | { "crypto", NULL, 17, 0 }, |
| 223 | { "pex1", NULL, 18, 0 }, |
| 224 | { "ge1", NULL, 19, 0 }, |
| 225 | { "tdm", NULL, 20, 0 }, |
| 226 | { } |
| 227 | }; |
| 228 | |
Mike Turquette | e8e8a9b | 2014-08-27 17:11:38 -0700 | [diff] [blame] | 229 | |
| 230 | /* |
| 231 | * Clock Muxing Control |
| 232 | */ |
| 233 | |
| 234 | struct clk_muxing_soc_desc { |
| 235 | const char *name; |
| 236 | const char **parents; |
| 237 | int num_parents; |
| 238 | int shift; |
| 239 | int width; |
| 240 | unsigned long flags; |
| 241 | }; |
| 242 | |
| 243 | struct clk_muxing_ctrl { |
| 244 | spinlock_t *lock; |
| 245 | struct clk **muxes; |
| 246 | int num_muxes; |
| 247 | }; |
| 248 | |
Mike Turquette | 1052993 | 2014-08-18 17:30:29 +0200 | [diff] [blame] | 249 | static const char *powersave_parents[] = { |
| 250 | "cpuclk", |
| 251 | "ddrclk", |
| 252 | }; |
| 253 | |
| 254 | static const struct clk_muxing_soc_desc kirkwood_mux_desc[] __initconst = { |
| 255 | { "powersave", powersave_parents, ARRAY_SIZE(powersave_parents), |
| 256 | 11, 1, 0 }, |
| 257 | }; |
| 258 | |
Mike Turquette | e8e8a9b | 2014-08-27 17:11:38 -0700 | [diff] [blame] | 259 | #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) |
| 260 | |
| 261 | static struct clk *clk_muxing_get_src( |
| 262 | struct of_phandle_args *clkspec, void *data) |
| 263 | { |
| 264 | struct clk_muxing_ctrl *ctrl = (struct clk_muxing_ctrl *)data; |
| 265 | int n; |
| 266 | |
| 267 | if (clkspec->args_count < 1) |
| 268 | return ERR_PTR(-EINVAL); |
| 269 | |
| 270 | for (n = 0; n < ctrl->num_muxes; n++) { |
| 271 | struct clk_mux *mux = |
| 272 | to_clk_mux(__clk_get_hw(ctrl->muxes[n])); |
| 273 | if (clkspec->args[0] == mux->shift) |
| 274 | return ctrl->muxes[n]; |
| 275 | } |
| 276 | return ERR_PTR(-ENODEV); |
| 277 | } |
| 278 | |
| 279 | static void __init kirkwood_clk_muxing_setup(struct device_node *np, |
| 280 | const struct clk_muxing_soc_desc *desc) |
| 281 | { |
| 282 | struct clk_muxing_ctrl *ctrl; |
| 283 | void __iomem *base; |
| 284 | int n; |
| 285 | |
| 286 | base = of_iomap(np, 0); |
| 287 | if (WARN_ON(!base)) |
| 288 | return; |
| 289 | |
| 290 | ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); |
| 291 | if (WARN_ON(!ctrl)) |
| 292 | goto ctrl_out; |
| 293 | |
| 294 | /* lock must already be initialized */ |
| 295 | ctrl->lock = &ctrl_gating_lock; |
| 296 | |
| 297 | /* Count, allocate, and register clock muxes */ |
| 298 | for (n = 0; desc[n].name;) |
| 299 | n++; |
| 300 | |
| 301 | ctrl->num_muxes = n; |
| 302 | ctrl->muxes = kcalloc(ctrl->num_muxes, sizeof(struct clk *), |
| 303 | GFP_KERNEL); |
| 304 | if (WARN_ON(!ctrl->muxes)) |
| 305 | goto muxes_out; |
| 306 | |
| 307 | for (n = 0; n < ctrl->num_muxes; n++) { |
| 308 | ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name, |
| 309 | desc[n].parents, desc[n].num_parents, |
| 310 | desc[n].flags, base, desc[n].shift, |
| 311 | desc[n].width, desc[n].flags, ctrl->lock); |
| 312 | WARN_ON(IS_ERR(ctrl->muxes[n])); |
| 313 | } |
| 314 | |
| 315 | of_clk_add_provider(np, clk_muxing_get_src, ctrl); |
| 316 | |
| 317 | return; |
| 318 | muxes_out: |
| 319 | kfree(ctrl); |
| 320 | ctrl_out: |
| 321 | iounmap(base); |
| 322 | } |
| 323 | |
Sebastian Hesselbarth | 58d516a | 2014-01-25 19:19:10 +0100 | [diff] [blame] | 324 | static void __init kirkwood_clk_init(struct device_node *np) |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 325 | { |
Sebastian Hesselbarth | 58d516a | 2014-01-25 19:19:10 +0100 | [diff] [blame] | 326 | struct device_node *cgnp = |
| 327 | of_find_compatible_node(NULL, NULL, "marvell,kirkwood-gating-clock"); |
| 328 | |
| 329 | |
| 330 | if (of_device_is_compatible(np, "marvell,mv88f6180-core-clock")) |
| 331 | mvebu_coreclk_setup(np, &mv88f6180_coreclks); |
| 332 | else |
| 333 | mvebu_coreclk_setup(np, &kirkwood_coreclks); |
| 334 | |
Mike Turquette | 1052993 | 2014-08-18 17:30:29 +0200 | [diff] [blame] | 335 | if (cgnp) { |
Sebastian Hesselbarth | 58d516a | 2014-01-25 19:19:10 +0100 | [diff] [blame] | 336 | mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc); |
Mike Turquette | 1052993 | 2014-08-18 17:30:29 +0200 | [diff] [blame] | 337 | kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc); |
| 338 | } |
Sebastian Hesselbarth | e89406c | 2013-05-11 03:08:04 +0200 | [diff] [blame] | 339 | } |
Sebastian Hesselbarth | 58d516a | 2014-01-25 19:19:10 +0100 | [diff] [blame] | 340 | CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock", |
| 341 | kirkwood_clk_init); |
| 342 | CLK_OF_DECLARE(mv88f6180_clk, "marvell,mv88f6180-core-clock", |
| 343 | kirkwood_clk_init); |