596 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * drivers/clk/clk-axm5516.c4 *5 * Provides clock implementations for three different types of clock devices on6 * the Axxia device: PLL clock, a clock divider and a clock mux.7 *8 * Copyright (C) 2014 LSI Corporation9 */10#include <linux/module.h>11#include <linux/kernel.h>12#include <linux/slab.h>13#include <linux/platform_device.h>14#include <linux/of.h>15#include <linux/of_address.h>16#include <linux/clk-provider.h>17#include <linux/regmap.h>18#include <dt-bindings/clock/lsi,axm5516-clks.h>19 20 21/**22 * struct axxia_clk - Common struct to all Axxia clocks.23 * @hw: clk_hw for the common clk framework24 * @regmap: Regmap for the clock control registers25 */26struct axxia_clk {27 struct clk_hw hw;28 struct regmap *regmap;29};30#define to_axxia_clk(_hw) container_of(_hw, struct axxia_clk, hw)31 32/**33 * struct axxia_pllclk - Axxia PLL generated clock.34 * @aclk: Common struct35 * @reg: Offset into regmap for PLL control register36 */37struct axxia_pllclk {38 struct axxia_clk aclk;39 u32 reg;40};41#define to_axxia_pllclk(_aclk) container_of(_aclk, struct axxia_pllclk, aclk)42 43/**44 * axxia_pllclk_recalc - Calculate the PLL generated clock rate given the45 * parent clock rate.46 */47static unsigned long48axxia_pllclk_recalc(struct clk_hw *hw, unsigned long parent_rate)49{50 struct axxia_clk *aclk = to_axxia_clk(hw);51 struct axxia_pllclk *pll = to_axxia_pllclk(aclk);52 unsigned long rate, fbdiv, refdiv, postdiv;53 u32 control;54 55 regmap_read(aclk->regmap, pll->reg, &control);56 postdiv = ((control >> 0) & 0xf) + 1;57 fbdiv = ((control >> 4) & 0xfff) + 3;58 refdiv = ((control >> 16) & 0x1f) + 1;59 rate = (parent_rate / (refdiv * postdiv)) * fbdiv;60 61 return rate;62}63 64static const struct clk_ops axxia_pllclk_ops = {65 .recalc_rate = axxia_pllclk_recalc,66};67 68/**69 * struct axxia_divclk - Axxia clock divider70 * @aclk: Common struct71 * @reg: Offset into regmap for PLL control register72 * @shift: Bit position for divider value73 * @width: Number of bits in divider value74 */75struct axxia_divclk {76 struct axxia_clk aclk;77 u32 reg;78 u32 shift;79 u32 width;80};81#define to_axxia_divclk(_aclk) container_of(_aclk, struct axxia_divclk, aclk)82 83/**84 * axxia_divclk_recalc_rate - Calculate clock divider output rage85 */86static unsigned long87axxia_divclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)88{89 struct axxia_clk *aclk = to_axxia_clk(hw);90 struct axxia_divclk *divclk = to_axxia_divclk(aclk);91 u32 ctrl, div;92 93 regmap_read(aclk->regmap, divclk->reg, &ctrl);94 div = 1 + ((ctrl >> divclk->shift) & ((1 << divclk->width)-1));95 96 return parent_rate / div;97}98 99static const struct clk_ops axxia_divclk_ops = {100 .recalc_rate = axxia_divclk_recalc_rate,101};102 103/**104 * struct axxia_clkmux - Axxia clock mux105 * @aclk: Common struct106 * @reg: Offset into regmap for PLL control register107 * @shift: Bit position for selection value108 * @width: Number of bits in selection value109 */110struct axxia_clkmux {111 struct axxia_clk aclk;112 u32 reg;113 u32 shift;114 u32 width;115};116#define to_axxia_clkmux(_aclk) container_of(_aclk, struct axxia_clkmux, aclk)117 118/**119 * axxia_clkmux_get_parent - Return the index of selected parent clock120 */121static u8 axxia_clkmux_get_parent(struct clk_hw *hw)122{123 struct axxia_clk *aclk = to_axxia_clk(hw);124 struct axxia_clkmux *mux = to_axxia_clkmux(aclk);125 u32 ctrl, parent;126 127 regmap_read(aclk->regmap, mux->reg, &ctrl);128 parent = (ctrl >> mux->shift) & ((1 << mux->width) - 1);129 130 return (u8) parent;131}132 133static const struct clk_ops axxia_clkmux_ops = {134 .get_parent = axxia_clkmux_get_parent,135};136 137 138/*139 * PLLs140 */141 142static struct axxia_pllclk clk_fab_pll = {143 .aclk.hw.init = &(struct clk_init_data){144 .name = "clk_fab_pll",145 .parent_names = (const char *[]){146 "clk_ref0"147 },148 .num_parents = 1,149 .ops = &axxia_pllclk_ops,150 },151 .reg = 0x01800,152};153 154static struct axxia_pllclk clk_cpu_pll = {155 .aclk.hw.init = &(struct clk_init_data){156 .name = "clk_cpu_pll",157 .parent_names = (const char *[]){158 "clk_ref0"159 },160 .num_parents = 1,161 .ops = &axxia_pllclk_ops,162 },163 .reg = 0x02000,164};165 166static struct axxia_pllclk clk_sys_pll = {167 .aclk.hw.init = &(struct clk_init_data){168 .name = "clk_sys_pll",169 .parent_names = (const char *[]){170 "clk_ref0"171 },172 .num_parents = 1,173 .ops = &axxia_pllclk_ops,174 },175 .reg = 0x02800,176};177 178static struct axxia_pllclk clk_sm0_pll = {179 .aclk.hw.init = &(struct clk_init_data){180 .name = "clk_sm0_pll",181 .parent_names = (const char *[]){182 "clk_ref2"183 },184 .num_parents = 1,185 .ops = &axxia_pllclk_ops,186 },187 .reg = 0x03000,188};189 190static struct axxia_pllclk clk_sm1_pll = {191 .aclk.hw.init = &(struct clk_init_data){192 .name = "clk_sm1_pll",193 .parent_names = (const char *[]){194 "clk_ref1"195 },196 .num_parents = 1,197 .ops = &axxia_pllclk_ops,198 },199 .reg = 0x03800,200};201 202/*203 * Clock dividers204 */205 206static struct axxia_divclk clk_cpu0_div = {207 .aclk.hw.init = &(struct clk_init_data){208 .name = "clk_cpu0_div",209 .parent_names = (const char *[]){210 "clk_cpu_pll"211 },212 .num_parents = 1,213 .ops = &axxia_divclk_ops,214 },215 .reg = 0x10008,216 .shift = 0,217 .width = 4,218};219 220static struct axxia_divclk clk_cpu1_div = {221 .aclk.hw.init = &(struct clk_init_data){222 .name = "clk_cpu1_div",223 .parent_names = (const char *[]){224 "clk_cpu_pll"225 },226 .num_parents = 1,227 .ops = &axxia_divclk_ops,228 },229 .reg = 0x10008,230 .shift = 4,231 .width = 4,232};233 234static struct axxia_divclk clk_cpu2_div = {235 .aclk.hw.init = &(struct clk_init_data){236 .name = "clk_cpu2_div",237 .parent_names = (const char *[]){238 "clk_cpu_pll"239 },240 .num_parents = 1,241 .ops = &axxia_divclk_ops,242 },243 .reg = 0x10008,244 .shift = 8,245 .width = 4,246};247 248static struct axxia_divclk clk_cpu3_div = {249 .aclk.hw.init = &(struct clk_init_data){250 .name = "clk_cpu3_div",251 .parent_names = (const char *[]){252 "clk_cpu_pll"253 },254 .num_parents = 1,255 .ops = &axxia_divclk_ops,256 },257 .reg = 0x10008,258 .shift = 12,259 .width = 4,260};261 262static struct axxia_divclk clk_nrcp_div = {263 .aclk.hw.init = &(struct clk_init_data){264 .name = "clk_nrcp_div",265 .parent_names = (const char *[]){266 "clk_sys_pll"267 },268 .num_parents = 1,269 .ops = &axxia_divclk_ops,270 },271 .reg = 0x1000c,272 .shift = 0,273 .width = 4,274};275 276static struct axxia_divclk clk_sys_div = {277 .aclk.hw.init = &(struct clk_init_data){278 .name = "clk_sys_div",279 .parent_names = (const char *[]){280 "clk_sys_pll"281 },282 .num_parents = 1,283 .ops = &axxia_divclk_ops,284 },285 .reg = 0x1000c,286 .shift = 4,287 .width = 4,288};289 290static struct axxia_divclk clk_fab_div = {291 .aclk.hw.init = &(struct clk_init_data){292 .name = "clk_fab_div",293 .parent_names = (const char *[]){294 "clk_fab_pll"295 },296 .num_parents = 1,297 .ops = &axxia_divclk_ops,298 },299 .reg = 0x1000c,300 .shift = 8,301 .width = 4,302};303 304static struct axxia_divclk clk_per_div = {305 .aclk.hw.init = &(struct clk_init_data){306 .name = "clk_per_div",307 .parent_names = (const char *[]){308 "clk_sm1_pll"309 },310 .num_parents = 1,311 .ops = &axxia_divclk_ops,312 },313 .reg = 0x1000c,314 .shift = 12,315 .width = 4,316};317 318static struct axxia_divclk clk_mmc_div = {319 .aclk.hw.init = &(struct clk_init_data){320 .name = "clk_mmc_div",321 .parent_names = (const char *[]){322 "clk_sm1_pll"323 },324 .num_parents = 1,325 .ops = &axxia_divclk_ops,326 },327 .reg = 0x1000c,328 .shift = 16,329 .width = 4,330};331 332/*333 * Clock MUXes334 */335 336static struct axxia_clkmux clk_cpu0_mux = {337 .aclk.hw.init = &(struct clk_init_data){338 .name = "clk_cpu0",339 .parent_names = (const char *[]){340 "clk_ref0",341 "clk_cpu_pll",342 "clk_cpu0_div",343 "clk_cpu0_div"344 },345 .num_parents = 4,346 .ops = &axxia_clkmux_ops,347 },348 .reg = 0x10000,349 .shift = 0,350 .width = 2,351};352 353static struct axxia_clkmux clk_cpu1_mux = {354 .aclk.hw.init = &(struct clk_init_data){355 .name = "clk_cpu1",356 .parent_names = (const char *[]){357 "clk_ref0",358 "clk_cpu_pll",359 "clk_cpu1_div",360 "clk_cpu1_div"361 },362 .num_parents = 4,363 .ops = &axxia_clkmux_ops,364 },365 .reg = 0x10000,366 .shift = 2,367 .width = 2,368};369 370static struct axxia_clkmux clk_cpu2_mux = {371 .aclk.hw.init = &(struct clk_init_data){372 .name = "clk_cpu2",373 .parent_names = (const char *[]){374 "clk_ref0",375 "clk_cpu_pll",376 "clk_cpu2_div",377 "clk_cpu2_div"378 },379 .num_parents = 4,380 .ops = &axxia_clkmux_ops,381 },382 .reg = 0x10000,383 .shift = 4,384 .width = 2,385};386 387static struct axxia_clkmux clk_cpu3_mux = {388 .aclk.hw.init = &(struct clk_init_data){389 .name = "clk_cpu3",390 .parent_names = (const char *[]){391 "clk_ref0",392 "clk_cpu_pll",393 "clk_cpu3_div",394 "clk_cpu3_div"395 },396 .num_parents = 4,397 .ops = &axxia_clkmux_ops,398 },399 .reg = 0x10000,400 .shift = 6,401 .width = 2,402};403 404static struct axxia_clkmux clk_nrcp_mux = {405 .aclk.hw.init = &(struct clk_init_data){406 .name = "clk_nrcp",407 .parent_names = (const char *[]){408 "clk_ref0",409 "clk_sys_pll",410 "clk_nrcp_div",411 "clk_nrcp_div"412 },413 .num_parents = 4,414 .ops = &axxia_clkmux_ops,415 },416 .reg = 0x10004,417 .shift = 0,418 .width = 2,419};420 421static struct axxia_clkmux clk_sys_mux = {422 .aclk.hw.init = &(struct clk_init_data){423 .name = "clk_sys",424 .parent_names = (const char *[]){425 "clk_ref0",426 "clk_sys_pll",427 "clk_sys_div",428 "clk_sys_div"429 },430 .num_parents = 4,431 .ops = &axxia_clkmux_ops,432 },433 .reg = 0x10004,434 .shift = 2,435 .width = 2,436};437 438static struct axxia_clkmux clk_fab_mux = {439 .aclk.hw.init = &(struct clk_init_data){440 .name = "clk_fab",441 .parent_names = (const char *[]){442 "clk_ref0",443 "clk_fab_pll",444 "clk_fab_div",445 "clk_fab_div"446 },447 .num_parents = 4,448 .ops = &axxia_clkmux_ops,449 },450 .reg = 0x10004,451 .shift = 4,452 .width = 2,453};454 455static struct axxia_clkmux clk_per_mux = {456 .aclk.hw.init = &(struct clk_init_data){457 .name = "clk_per",458 .parent_names = (const char *[]){459 "clk_ref1",460 "clk_per_div"461 },462 .num_parents = 2,463 .ops = &axxia_clkmux_ops,464 },465 .reg = 0x10004,466 .shift = 6,467 .width = 1,468};469 470static struct axxia_clkmux clk_mmc_mux = {471 .aclk.hw.init = &(struct clk_init_data){472 .name = "clk_mmc",473 .parent_names = (const char *[]){474 "clk_ref1",475 "clk_mmc_div"476 },477 .num_parents = 2,478 .ops = &axxia_clkmux_ops,479 },480 .reg = 0x10004,481 .shift = 9,482 .width = 1,483};484 485/* Table of all supported clocks indexed by the clock identifiers from the486 * device tree binding487 */488static struct axxia_clk *axmclk_clocks[] = {489 [AXXIA_CLK_FAB_PLL] = &clk_fab_pll.aclk,490 [AXXIA_CLK_CPU_PLL] = &clk_cpu_pll.aclk,491 [AXXIA_CLK_SYS_PLL] = &clk_sys_pll.aclk,492 [AXXIA_CLK_SM0_PLL] = &clk_sm0_pll.aclk,493 [AXXIA_CLK_SM1_PLL] = &clk_sm1_pll.aclk,494 [AXXIA_CLK_FAB_DIV] = &clk_fab_div.aclk,495 [AXXIA_CLK_SYS_DIV] = &clk_sys_div.aclk,496 [AXXIA_CLK_NRCP_DIV] = &clk_nrcp_div.aclk,497 [AXXIA_CLK_CPU0_DIV] = &clk_cpu0_div.aclk,498 [AXXIA_CLK_CPU1_DIV] = &clk_cpu1_div.aclk,499 [AXXIA_CLK_CPU2_DIV] = &clk_cpu2_div.aclk,500 [AXXIA_CLK_CPU3_DIV] = &clk_cpu3_div.aclk,501 [AXXIA_CLK_PER_DIV] = &clk_per_div.aclk,502 [AXXIA_CLK_MMC_DIV] = &clk_mmc_div.aclk,503 [AXXIA_CLK_FAB] = &clk_fab_mux.aclk,504 [AXXIA_CLK_SYS] = &clk_sys_mux.aclk,505 [AXXIA_CLK_NRCP] = &clk_nrcp_mux.aclk,506 [AXXIA_CLK_CPU0] = &clk_cpu0_mux.aclk,507 [AXXIA_CLK_CPU1] = &clk_cpu1_mux.aclk,508 [AXXIA_CLK_CPU2] = &clk_cpu2_mux.aclk,509 [AXXIA_CLK_CPU3] = &clk_cpu3_mux.aclk,510 [AXXIA_CLK_PER] = &clk_per_mux.aclk,511 [AXXIA_CLK_MMC] = &clk_mmc_mux.aclk,512};513 514static struct clk_hw *515of_clk_axmclk_get(struct of_phandle_args *clkspec, void *unused)516{517 unsigned int idx = clkspec->args[0];518 519 if (idx >= ARRAY_SIZE(axmclk_clocks)) {520 pr_err("%s: invalid index %u\n", __func__, idx);521 return ERR_PTR(-EINVAL);522 }523 524 return &axmclk_clocks[idx]->hw;525}526 527static const struct regmap_config axmclk_regmap_config = {528 .reg_bits = 32,529 .reg_stride = 4,530 .val_bits = 32,531 .max_register = 0x1fffc,532 .fast_io = true,533};534 535static const struct of_device_id axmclk_match_table[] = {536 { .compatible = "lsi,axm5516-clks" },537 { }538};539MODULE_DEVICE_TABLE(of, axmclk_match_table);540 541static int axmclk_probe(struct platform_device *pdev)542{543 void __iomem *base;544 int i, ret;545 struct device *dev = &pdev->dev;546 struct regmap *regmap;547 size_t num_clks;548 549 base = devm_platform_ioremap_resource(pdev, 0);550 if (IS_ERR(base))551 return PTR_ERR(base);552 553 regmap = devm_regmap_init_mmio(dev, base, &axmclk_regmap_config);554 if (IS_ERR(regmap))555 return PTR_ERR(regmap);556 557 num_clks = ARRAY_SIZE(axmclk_clocks);558 pr_info("axmclk: supporting %zu clocks\n", num_clks);559 560 /* Update each entry with the allocated regmap and register the clock561 * with the common clock framework562 */563 for (i = 0; i < num_clks; i++) {564 axmclk_clocks[i]->regmap = regmap;565 ret = devm_clk_hw_register(dev, &axmclk_clocks[i]->hw);566 if (ret)567 return ret;568 }569 570 return devm_of_clk_add_hw_provider(dev, of_clk_axmclk_get, NULL);571}572 573static struct platform_driver axmclk_driver = {574 .probe = axmclk_probe,575 .driver = {576 .name = "clk-axm5516",577 .of_match_table = axmclk_match_table,578 },579};580 581static int __init axmclk_init(void)582{583 return platform_driver_register(&axmclk_driver);584}585core_initcall(axmclk_init);586 587static void __exit axmclk_exit(void)588{589 platform_driver_unregister(&axmclk_driver);590}591module_exit(axmclk_exit);592 593MODULE_DESCRIPTION("AXM5516 clock driver");594MODULE_LICENSE("GPL v2");595MODULE_ALIAS("platform:clk-axm5516");596