1 /* 2 * Marvell Armada CP110 System Controller 3 * 4 * Copyright (C) 2016 Marvell 5 * 6 * Thomas Petazzoni <thomas.petazzoni@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 13 /* 14 * CP110 has 5 core clocks: 15 * 16 * - APLL (1 Ghz) 17 * - PPv2 core (1/3 APLL) 18 * - EIP (1/2 APLL) 19 * - Core (1/2 EIP) 20 * 21 * - NAND clock, which is either: 22 * - Equal to the core clock 23 * - 2/5 APLL 24 * 25 * CP110 has 32 gatable clocks, for the various peripherals in the 26 * IP. They have fairly complicated parent/child relationships. 27 */ 28 29 #define pr_fmt(fmt) "cp110-system-controller: " fmt 30 31 #include <linux/clk-provider.h> 32 #include <linux/mfd/syscon.h> 33 #include <linux/module.h> 34 #include <linux/of.h> 35 #include <linux/of_address.h> 36 #include <linux/platform_device.h> 37 #include <linux/regmap.h> 38 #include <linux/slab.h> 39 40 #define CP110_PM_CLOCK_GATING_REG 0x220 41 #define CP110_NAND_FLASH_CLK_CTRL_REG 0x700 42 #define NF_CLOCK_SEL_400_MASK BIT(0) 43 44 enum { 45 CP110_CLK_TYPE_CORE, 46 CP110_CLK_TYPE_GATABLE, 47 }; 48 49 #define CP110_MAX_CORE_CLOCKS 5 50 #define CP110_MAX_GATABLE_CLOCKS 32 51 52 #define CP110_CLK_NUM \ 53 (CP110_MAX_CORE_CLOCKS + CP110_MAX_GATABLE_CLOCKS) 54 55 #define CP110_CORE_APLL 0 56 #define CP110_CORE_PPV2 1 57 #define CP110_CORE_EIP 2 58 #define CP110_CORE_CORE 3 59 #define CP110_CORE_NAND 4 60 61 /* A number of gatable clocks need special handling */ 62 #define CP110_GATE_AUDIO 0 63 #define CP110_GATE_COMM_UNIT 1 64 #define CP110_GATE_NAND 2 65 #define CP110_GATE_PPV2 3 66 #define CP110_GATE_SDIO 4 67 #define CP110_GATE_XOR1 7 68 #define CP110_GATE_XOR0 8 69 #define CP110_GATE_PCIE_X1_0 11 70 #define CP110_GATE_PCIE_X1_1 12 71 #define CP110_GATE_PCIE_X4 13 72 #define CP110_GATE_PCIE_XOR 14 73 #define CP110_GATE_SATA 15 74 #define CP110_GATE_SATA_USB 16 75 #define CP110_GATE_MAIN 17 76 #define CP110_GATE_SDMMC 18 77 #define CP110_GATE_SLOW_IO 21 78 #define CP110_GATE_USB3H0 22 79 #define CP110_GATE_USB3H1 23 80 #define CP110_GATE_USB3DEV 24 81 #define CP110_GATE_EIP150 25 82 #define CP110_GATE_EIP197 26 83 84 static struct clk *cp110_clks[CP110_CLK_NUM]; 85 86 static struct clk_onecell_data cp110_clk_data = { 87 .clks = cp110_clks, 88 .clk_num = CP110_CLK_NUM, 89 }; 90 91 struct cp110_gate_clk { 92 struct clk_hw hw; 93 struct regmap *regmap; 94 u8 bit_idx; 95 }; 96 97 #define to_cp110_gate_clk(clk) container_of(clk, struct cp110_gate_clk, hw) 98 99 static int cp110_gate_enable(struct clk_hw *hw) 100 { 101 struct cp110_gate_clk *gate = to_cp110_gate_clk(hw); 102 103 regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG, 104 BIT(gate->bit_idx), BIT(gate->bit_idx)); 105 106 return 0; 107 } 108 109 static void cp110_gate_disable(struct clk_hw *hw) 110 { 111 struct cp110_gate_clk *gate = to_cp110_gate_clk(hw); 112 113 regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG, 114 BIT(gate->bit_idx), 0); 115 } 116 117 static int cp110_gate_is_enabled(struct clk_hw *hw) 118 { 119 struct cp110_gate_clk *gate = to_cp110_gate_clk(hw); 120 u32 val; 121 122 regmap_read(gate->regmap, CP110_PM_CLOCK_GATING_REG, &val); 123 124 return val & BIT(gate->bit_idx); 125 } 126 127 static const struct clk_ops cp110_gate_ops = { 128 .enable = cp110_gate_enable, 129 .disable = cp110_gate_disable, 130 .is_enabled = cp110_gate_is_enabled, 131 }; 132 133 static struct clk *cp110_register_gate(const char *name, 134 const char *parent_name, 135 struct regmap *regmap, u8 bit_idx) 136 { 137 struct cp110_gate_clk *gate; 138 struct clk *clk; 139 struct clk_init_data init; 140 141 gate = kzalloc(sizeof(*gate), GFP_KERNEL); 142 if (!gate) 143 return ERR_PTR(-ENOMEM); 144 145 init.name = name; 146 init.ops = &cp110_gate_ops; 147 init.parent_names = &parent_name; 148 init.num_parents = 1; 149 150 gate->regmap = regmap; 151 gate->bit_idx = bit_idx; 152 gate->hw.init = &init; 153 154 clk = clk_register(NULL, &gate->hw); 155 if (IS_ERR(clk)) 156 kfree(gate); 157 158 return clk; 159 } 160 161 static void cp110_unregister_gate(struct clk *clk) 162 { 163 struct clk_hw *hw; 164 165 hw = __clk_get_hw(clk); 166 if (!hw) 167 return; 168 169 clk_unregister(clk); 170 kfree(to_cp110_gate_clk(hw)); 171 } 172 173 static struct clk *cp110_of_clk_get(struct of_phandle_args *clkspec, void *data) 174 { 175 struct clk_onecell_data *clk_data = data; 176 unsigned int type = clkspec->args[0]; 177 unsigned int idx = clkspec->args[1]; 178 179 if (type == CP110_CLK_TYPE_CORE) { 180 if (idx > CP110_MAX_CORE_CLOCKS) 181 return ERR_PTR(-EINVAL); 182 return clk_data->clks[idx]; 183 } else if (type == CP110_CLK_TYPE_GATABLE) { 184 if (idx > CP110_MAX_GATABLE_CLOCKS) 185 return ERR_PTR(-EINVAL); 186 return clk_data->clks[CP110_MAX_CORE_CLOCKS + idx]; 187 } 188 189 return ERR_PTR(-EINVAL); 190 } 191 192 static int cp110_syscon_clk_probe(struct platform_device *pdev) 193 { 194 struct regmap *regmap; 195 struct device_node *np = pdev->dev.of_node; 196 const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name; 197 struct clk *clk; 198 u32 nand_clk_ctrl; 199 int i, ret; 200 201 regmap = syscon_node_to_regmap(np); 202 if (IS_ERR(regmap)) 203 return PTR_ERR(regmap); 204 205 ret = regmap_read(regmap, CP110_NAND_FLASH_CLK_CTRL_REG, 206 &nand_clk_ctrl); 207 if (ret) 208 return ret; 209 210 /* Register the APLL which is the root of the clk tree */ 211 of_property_read_string_index(np, "core-clock-output-names", 212 CP110_CORE_APLL, &apll_name); 213 clk = clk_register_fixed_rate(NULL, apll_name, NULL, 0, 214 1000 * 1000 * 1000); 215 if (IS_ERR(clk)) { 216 ret = PTR_ERR(clk); 217 goto fail0; 218 } 219 220 cp110_clks[CP110_CORE_APLL] = clk; 221 222 /* PPv2 is APLL/3 */ 223 of_property_read_string_index(np, "core-clock-output-names", 224 CP110_CORE_PPV2, &ppv2_name); 225 clk = clk_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3); 226 if (IS_ERR(clk)) { 227 ret = PTR_ERR(clk); 228 goto fail1; 229 } 230 231 cp110_clks[CP110_CORE_PPV2] = clk; 232 233 /* EIP clock is APLL/2 */ 234 of_property_read_string_index(np, "core-clock-output-names", 235 CP110_CORE_EIP, &eip_name); 236 clk = clk_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2); 237 if (IS_ERR(clk)) { 238 ret = PTR_ERR(clk); 239 goto fail2; 240 } 241 242 cp110_clks[CP110_CORE_EIP] = clk; 243 244 /* Core clock is EIP/2 */ 245 of_property_read_string_index(np, "core-clock-output-names", 246 CP110_CORE_CORE, &core_name); 247 clk = clk_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2); 248 if (IS_ERR(clk)) { 249 ret = PTR_ERR(clk); 250 goto fail3; 251 } 252 253 cp110_clks[CP110_CORE_CORE] = clk; 254 255 /* NAND can be either APLL/2.5 or core clock */ 256 of_property_read_string_index(np, "core-clock-output-names", 257 CP110_CORE_NAND, &nand_name); 258 if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK) 259 clk = clk_register_fixed_factor(NULL, nand_name, 260 apll_name, 0, 2, 5); 261 else 262 clk = clk_register_fixed_factor(NULL, nand_name, 263 core_name, 0, 1, 1); 264 if (IS_ERR(clk)) { 265 ret = PTR_ERR(clk); 266 goto fail4; 267 } 268 269 cp110_clks[CP110_CORE_NAND] = clk; 270 271 for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) { 272 const char *parent, *name; 273 int ret; 274 275 ret = of_property_read_string_index(np, 276 "gate-clock-output-names", 277 i, &name); 278 /* Reached the end of the list? */ 279 if (ret < 0) 280 break; 281 282 if (!strcmp(name, "none")) 283 continue; 284 285 switch (i) { 286 case CP110_GATE_AUDIO: 287 case CP110_GATE_COMM_UNIT: 288 case CP110_GATE_EIP150: 289 case CP110_GATE_EIP197: 290 case CP110_GATE_SLOW_IO: 291 of_property_read_string_index(np, 292 "gate-clock-output-names", 293 CP110_GATE_MAIN, &parent); 294 break; 295 case CP110_GATE_NAND: 296 parent = nand_name; 297 break; 298 case CP110_GATE_PPV2: 299 parent = ppv2_name; 300 break; 301 case CP110_GATE_SDIO: 302 of_property_read_string_index(np, 303 "gate-clock-output-names", 304 CP110_GATE_SDMMC, &parent); 305 break; 306 case CP110_GATE_XOR1: 307 case CP110_GATE_XOR0: 308 case CP110_GATE_PCIE_X1_0: 309 case CP110_GATE_PCIE_X1_1: 310 case CP110_GATE_PCIE_X4: 311 of_property_read_string_index(np, 312 "gate-clock-output-names", 313 CP110_GATE_PCIE_XOR, &parent); 314 break; 315 case CP110_GATE_SATA: 316 case CP110_GATE_USB3H0: 317 case CP110_GATE_USB3H1: 318 case CP110_GATE_USB3DEV: 319 of_property_read_string_index(np, 320 "gate-clock-output-names", 321 CP110_GATE_SATA_USB, &parent); 322 break; 323 default: 324 parent = core_name; 325 break; 326 } 327 328 clk = cp110_register_gate(name, parent, regmap, i); 329 if (IS_ERR(clk)) { 330 ret = PTR_ERR(clk); 331 goto fail_gate; 332 } 333 334 cp110_clks[CP110_MAX_CORE_CLOCKS + i] = clk; 335 } 336 337 ret = of_clk_add_provider(np, cp110_of_clk_get, &cp110_clk_data); 338 if (ret) 339 goto fail_clk_add; 340 341 return 0; 342 343 fail_clk_add: 344 fail_gate: 345 for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) { 346 clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i]; 347 348 if (clk) 349 cp110_unregister_gate(clk); 350 } 351 352 clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]); 353 fail4: 354 clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]); 355 fail3: 356 clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]); 357 fail2: 358 clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]); 359 fail1: 360 clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]); 361 fail0: 362 return ret; 363 } 364 365 static int cp110_syscon_clk_remove(struct platform_device *pdev) 366 { 367 int i; 368 369 of_clk_del_provider(pdev->dev.of_node); 370 371 for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) { 372 struct clk *clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i]; 373 374 if (clk) 375 cp110_unregister_gate(clk); 376 } 377 378 clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]); 379 clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]); 380 clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]); 381 clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]); 382 clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]); 383 384 return 0; 385 } 386 387 static const struct of_device_id cp110_syscon_of_match[] = { 388 { .compatible = "marvell,cp110-system-controller0", }, 389 { } 390 }; 391 MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match); 392 393 static struct platform_driver cp110_syscon_driver = { 394 .probe = cp110_syscon_clk_probe, 395 .remove = cp110_syscon_clk_remove, 396 .driver = { 397 .name = "marvell-cp110-system-controller0", 398 .of_match_table = cp110_syscon_of_match, 399 }, 400 }; 401 402 module_platform_driver(cp110_syscon_driver); 403 404 MODULE_DESCRIPTION("Marvell CP110 System Controller 0 driver"); 405 MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); 406 MODULE_LICENSE("GPL"); 407