0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/module.h>
0011 #include <linux/kernel.h>
0012 #include <linux/slab.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/of.h>
0015 #include <linux/of_address.h>
0016 #include <linux/clk-provider.h>
0017 #include <linux/regmap.h>
0018 #include <dt-bindings/clock/lsi,axm5516-clks.h>
0019
0020
0021
0022
0023
0024
0025
0026 struct axxia_clk {
0027 struct clk_hw hw;
0028 struct regmap *regmap;
0029 };
0030 #define to_axxia_clk(_hw) container_of(_hw, struct axxia_clk, hw)
0031
0032
0033
0034
0035
0036
0037 struct axxia_pllclk {
0038 struct axxia_clk aclk;
0039 u32 reg;
0040 };
0041 #define to_axxia_pllclk(_aclk) container_of(_aclk, struct axxia_pllclk, aclk)
0042
0043
0044
0045
0046
0047 static unsigned long
0048 axxia_pllclk_recalc(struct clk_hw *hw, unsigned long parent_rate)
0049 {
0050 struct axxia_clk *aclk = to_axxia_clk(hw);
0051 struct axxia_pllclk *pll = to_axxia_pllclk(aclk);
0052 unsigned long rate, fbdiv, refdiv, postdiv;
0053 u32 control;
0054
0055 regmap_read(aclk->regmap, pll->reg, &control);
0056 postdiv = ((control >> 0) & 0xf) + 1;
0057 fbdiv = ((control >> 4) & 0xfff) + 3;
0058 refdiv = ((control >> 16) & 0x1f) + 1;
0059 rate = (parent_rate / (refdiv * postdiv)) * fbdiv;
0060
0061 return rate;
0062 }
0063
0064 static const struct clk_ops axxia_pllclk_ops = {
0065 .recalc_rate = axxia_pllclk_recalc,
0066 };
0067
0068
0069
0070
0071
0072
0073
0074
0075 struct axxia_divclk {
0076 struct axxia_clk aclk;
0077 u32 reg;
0078 u32 shift;
0079 u32 width;
0080 };
0081 #define to_axxia_divclk(_aclk) container_of(_aclk, struct axxia_divclk, aclk)
0082
0083
0084
0085
0086 static unsigned long
0087 axxia_divclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
0088 {
0089 struct axxia_clk *aclk = to_axxia_clk(hw);
0090 struct axxia_divclk *divclk = to_axxia_divclk(aclk);
0091 u32 ctrl, div;
0092
0093 regmap_read(aclk->regmap, divclk->reg, &ctrl);
0094 div = 1 + ((ctrl >> divclk->shift) & ((1 << divclk->width)-1));
0095
0096 return parent_rate / div;
0097 }
0098
0099 static const struct clk_ops axxia_divclk_ops = {
0100 .recalc_rate = axxia_divclk_recalc_rate,
0101 };
0102
0103
0104
0105
0106
0107
0108
0109
0110 struct axxia_clkmux {
0111 struct axxia_clk aclk;
0112 u32 reg;
0113 u32 shift;
0114 u32 width;
0115 };
0116 #define to_axxia_clkmux(_aclk) container_of(_aclk, struct axxia_clkmux, aclk)
0117
0118
0119
0120
0121 static u8 axxia_clkmux_get_parent(struct clk_hw *hw)
0122 {
0123 struct axxia_clk *aclk = to_axxia_clk(hw);
0124 struct axxia_clkmux *mux = to_axxia_clkmux(aclk);
0125 u32 ctrl, parent;
0126
0127 regmap_read(aclk->regmap, mux->reg, &ctrl);
0128 parent = (ctrl >> mux->shift) & ((1 << mux->width) - 1);
0129
0130 return (u8) parent;
0131 }
0132
0133 static const struct clk_ops axxia_clkmux_ops = {
0134 .get_parent = axxia_clkmux_get_parent,
0135 };
0136
0137
0138
0139
0140
0141
0142 static struct axxia_pllclk clk_fab_pll = {
0143 .aclk.hw.init = &(struct clk_init_data){
0144 .name = "clk_fab_pll",
0145 .parent_names = (const char *[]){
0146 "clk_ref0"
0147 },
0148 .num_parents = 1,
0149 .ops = &axxia_pllclk_ops,
0150 },
0151 .reg = 0x01800,
0152 };
0153
0154 static struct axxia_pllclk clk_cpu_pll = {
0155 .aclk.hw.init = &(struct clk_init_data){
0156 .name = "clk_cpu_pll",
0157 .parent_names = (const char *[]){
0158 "clk_ref0"
0159 },
0160 .num_parents = 1,
0161 .ops = &axxia_pllclk_ops,
0162 },
0163 .reg = 0x02000,
0164 };
0165
0166 static struct axxia_pllclk clk_sys_pll = {
0167 .aclk.hw.init = &(struct clk_init_data){
0168 .name = "clk_sys_pll",
0169 .parent_names = (const char *[]){
0170 "clk_ref0"
0171 },
0172 .num_parents = 1,
0173 .ops = &axxia_pllclk_ops,
0174 },
0175 .reg = 0x02800,
0176 };
0177
0178 static struct axxia_pllclk clk_sm0_pll = {
0179 .aclk.hw.init = &(struct clk_init_data){
0180 .name = "clk_sm0_pll",
0181 .parent_names = (const char *[]){
0182 "clk_ref2"
0183 },
0184 .num_parents = 1,
0185 .ops = &axxia_pllclk_ops,
0186 },
0187 .reg = 0x03000,
0188 };
0189
0190 static struct axxia_pllclk clk_sm1_pll = {
0191 .aclk.hw.init = &(struct clk_init_data){
0192 .name = "clk_sm1_pll",
0193 .parent_names = (const char *[]){
0194 "clk_ref1"
0195 },
0196 .num_parents = 1,
0197 .ops = &axxia_pllclk_ops,
0198 },
0199 .reg = 0x03800,
0200 };
0201
0202
0203
0204
0205
0206 static struct axxia_divclk clk_cpu0_div = {
0207 .aclk.hw.init = &(struct clk_init_data){
0208 .name = "clk_cpu0_div",
0209 .parent_names = (const char *[]){
0210 "clk_cpu_pll"
0211 },
0212 .num_parents = 1,
0213 .ops = &axxia_divclk_ops,
0214 },
0215 .reg = 0x10008,
0216 .shift = 0,
0217 .width = 4,
0218 };
0219
0220 static struct axxia_divclk clk_cpu1_div = {
0221 .aclk.hw.init = &(struct clk_init_data){
0222 .name = "clk_cpu1_div",
0223 .parent_names = (const char *[]){
0224 "clk_cpu_pll"
0225 },
0226 .num_parents = 1,
0227 .ops = &axxia_divclk_ops,
0228 },
0229 .reg = 0x10008,
0230 .shift = 4,
0231 .width = 4,
0232 };
0233
0234 static struct axxia_divclk clk_cpu2_div = {
0235 .aclk.hw.init = &(struct clk_init_data){
0236 .name = "clk_cpu2_div",
0237 .parent_names = (const char *[]){
0238 "clk_cpu_pll"
0239 },
0240 .num_parents = 1,
0241 .ops = &axxia_divclk_ops,
0242 },
0243 .reg = 0x10008,
0244 .shift = 8,
0245 .width = 4,
0246 };
0247
0248 static struct axxia_divclk clk_cpu3_div = {
0249 .aclk.hw.init = &(struct clk_init_data){
0250 .name = "clk_cpu3_div",
0251 .parent_names = (const char *[]){
0252 "clk_cpu_pll"
0253 },
0254 .num_parents = 1,
0255 .ops = &axxia_divclk_ops,
0256 },
0257 .reg = 0x10008,
0258 .shift = 12,
0259 .width = 4,
0260 };
0261
0262 static struct axxia_divclk clk_nrcp_div = {
0263 .aclk.hw.init = &(struct clk_init_data){
0264 .name = "clk_nrcp_div",
0265 .parent_names = (const char *[]){
0266 "clk_sys_pll"
0267 },
0268 .num_parents = 1,
0269 .ops = &axxia_divclk_ops,
0270 },
0271 .reg = 0x1000c,
0272 .shift = 0,
0273 .width = 4,
0274 };
0275
0276 static struct axxia_divclk clk_sys_div = {
0277 .aclk.hw.init = &(struct clk_init_data){
0278 .name = "clk_sys_div",
0279 .parent_names = (const char *[]){
0280 "clk_sys_pll"
0281 },
0282 .num_parents = 1,
0283 .ops = &axxia_divclk_ops,
0284 },
0285 .reg = 0x1000c,
0286 .shift = 4,
0287 .width = 4,
0288 };
0289
0290 static struct axxia_divclk clk_fab_div = {
0291 .aclk.hw.init = &(struct clk_init_data){
0292 .name = "clk_fab_div",
0293 .parent_names = (const char *[]){
0294 "clk_fab_pll"
0295 },
0296 .num_parents = 1,
0297 .ops = &axxia_divclk_ops,
0298 },
0299 .reg = 0x1000c,
0300 .shift = 8,
0301 .width = 4,
0302 };
0303
0304 static struct axxia_divclk clk_per_div = {
0305 .aclk.hw.init = &(struct clk_init_data){
0306 .name = "clk_per_div",
0307 .parent_names = (const char *[]){
0308 "clk_sm1_pll"
0309 },
0310 .num_parents = 1,
0311 .ops = &axxia_divclk_ops,
0312 },
0313 .reg = 0x1000c,
0314 .shift = 12,
0315 .width = 4,
0316 };
0317
0318 static struct axxia_divclk clk_mmc_div = {
0319 .aclk.hw.init = &(struct clk_init_data){
0320 .name = "clk_mmc_div",
0321 .parent_names = (const char *[]){
0322 "clk_sm1_pll"
0323 },
0324 .num_parents = 1,
0325 .ops = &axxia_divclk_ops,
0326 },
0327 .reg = 0x1000c,
0328 .shift = 16,
0329 .width = 4,
0330 };
0331
0332
0333
0334
0335
0336 static struct axxia_clkmux clk_cpu0_mux = {
0337 .aclk.hw.init = &(struct clk_init_data){
0338 .name = "clk_cpu0",
0339 .parent_names = (const char *[]){
0340 "clk_ref0",
0341 "clk_cpu_pll",
0342 "clk_cpu0_div",
0343 "clk_cpu0_div"
0344 },
0345 .num_parents = 4,
0346 .ops = &axxia_clkmux_ops,
0347 },
0348 .reg = 0x10000,
0349 .shift = 0,
0350 .width = 2,
0351 };
0352
0353 static struct axxia_clkmux clk_cpu1_mux = {
0354 .aclk.hw.init = &(struct clk_init_data){
0355 .name = "clk_cpu1",
0356 .parent_names = (const char *[]){
0357 "clk_ref0",
0358 "clk_cpu_pll",
0359 "clk_cpu1_div",
0360 "clk_cpu1_div"
0361 },
0362 .num_parents = 4,
0363 .ops = &axxia_clkmux_ops,
0364 },
0365 .reg = 0x10000,
0366 .shift = 2,
0367 .width = 2,
0368 };
0369
0370 static struct axxia_clkmux clk_cpu2_mux = {
0371 .aclk.hw.init = &(struct clk_init_data){
0372 .name = "clk_cpu2",
0373 .parent_names = (const char *[]){
0374 "clk_ref0",
0375 "clk_cpu_pll",
0376 "clk_cpu2_div",
0377 "clk_cpu2_div"
0378 },
0379 .num_parents = 4,
0380 .ops = &axxia_clkmux_ops,
0381 },
0382 .reg = 0x10000,
0383 .shift = 4,
0384 .width = 2,
0385 };
0386
0387 static struct axxia_clkmux clk_cpu3_mux = {
0388 .aclk.hw.init = &(struct clk_init_data){
0389 .name = "clk_cpu3",
0390 .parent_names = (const char *[]){
0391 "clk_ref0",
0392 "clk_cpu_pll",
0393 "clk_cpu3_div",
0394 "clk_cpu3_div"
0395 },
0396 .num_parents = 4,
0397 .ops = &axxia_clkmux_ops,
0398 },
0399 .reg = 0x10000,
0400 .shift = 6,
0401 .width = 2,
0402 };
0403
0404 static struct axxia_clkmux clk_nrcp_mux = {
0405 .aclk.hw.init = &(struct clk_init_data){
0406 .name = "clk_nrcp",
0407 .parent_names = (const char *[]){
0408 "clk_ref0",
0409 "clk_sys_pll",
0410 "clk_nrcp_div",
0411 "clk_nrcp_div"
0412 },
0413 .num_parents = 4,
0414 .ops = &axxia_clkmux_ops,
0415 },
0416 .reg = 0x10004,
0417 .shift = 0,
0418 .width = 2,
0419 };
0420
0421 static struct axxia_clkmux clk_sys_mux = {
0422 .aclk.hw.init = &(struct clk_init_data){
0423 .name = "clk_sys",
0424 .parent_names = (const char *[]){
0425 "clk_ref0",
0426 "clk_sys_pll",
0427 "clk_sys_div",
0428 "clk_sys_div"
0429 },
0430 .num_parents = 4,
0431 .ops = &axxia_clkmux_ops,
0432 },
0433 .reg = 0x10004,
0434 .shift = 2,
0435 .width = 2,
0436 };
0437
0438 static struct axxia_clkmux clk_fab_mux = {
0439 .aclk.hw.init = &(struct clk_init_data){
0440 .name = "clk_fab",
0441 .parent_names = (const char *[]){
0442 "clk_ref0",
0443 "clk_fab_pll",
0444 "clk_fab_div",
0445 "clk_fab_div"
0446 },
0447 .num_parents = 4,
0448 .ops = &axxia_clkmux_ops,
0449 },
0450 .reg = 0x10004,
0451 .shift = 4,
0452 .width = 2,
0453 };
0454
0455 static struct axxia_clkmux clk_per_mux = {
0456 .aclk.hw.init = &(struct clk_init_data){
0457 .name = "clk_per",
0458 .parent_names = (const char *[]){
0459 "clk_ref1",
0460 "clk_per_div"
0461 },
0462 .num_parents = 2,
0463 .ops = &axxia_clkmux_ops,
0464 },
0465 .reg = 0x10004,
0466 .shift = 6,
0467 .width = 1,
0468 };
0469
0470 static struct axxia_clkmux clk_mmc_mux = {
0471 .aclk.hw.init = &(struct clk_init_data){
0472 .name = "clk_mmc",
0473 .parent_names = (const char *[]){
0474 "clk_ref1",
0475 "clk_mmc_div"
0476 },
0477 .num_parents = 2,
0478 .ops = &axxia_clkmux_ops,
0479 },
0480 .reg = 0x10004,
0481 .shift = 9,
0482 .width = 1,
0483 };
0484
0485
0486
0487
0488 static struct axxia_clk *axmclk_clocks[] = {
0489 [AXXIA_CLK_FAB_PLL] = &clk_fab_pll.aclk,
0490 [AXXIA_CLK_CPU_PLL] = &clk_cpu_pll.aclk,
0491 [AXXIA_CLK_SYS_PLL] = &clk_sys_pll.aclk,
0492 [AXXIA_CLK_SM0_PLL] = &clk_sm0_pll.aclk,
0493 [AXXIA_CLK_SM1_PLL] = &clk_sm1_pll.aclk,
0494 [AXXIA_CLK_FAB_DIV] = &clk_fab_div.aclk,
0495 [AXXIA_CLK_SYS_DIV] = &clk_sys_div.aclk,
0496 [AXXIA_CLK_NRCP_DIV] = &clk_nrcp_div.aclk,
0497 [AXXIA_CLK_CPU0_DIV] = &clk_cpu0_div.aclk,
0498 [AXXIA_CLK_CPU1_DIV] = &clk_cpu1_div.aclk,
0499 [AXXIA_CLK_CPU2_DIV] = &clk_cpu2_div.aclk,
0500 [AXXIA_CLK_CPU3_DIV] = &clk_cpu3_div.aclk,
0501 [AXXIA_CLK_PER_DIV] = &clk_per_div.aclk,
0502 [AXXIA_CLK_MMC_DIV] = &clk_mmc_div.aclk,
0503 [AXXIA_CLK_FAB] = &clk_fab_mux.aclk,
0504 [AXXIA_CLK_SYS] = &clk_sys_mux.aclk,
0505 [AXXIA_CLK_NRCP] = &clk_nrcp_mux.aclk,
0506 [AXXIA_CLK_CPU0] = &clk_cpu0_mux.aclk,
0507 [AXXIA_CLK_CPU1] = &clk_cpu1_mux.aclk,
0508 [AXXIA_CLK_CPU2] = &clk_cpu2_mux.aclk,
0509 [AXXIA_CLK_CPU3] = &clk_cpu3_mux.aclk,
0510 [AXXIA_CLK_PER] = &clk_per_mux.aclk,
0511 [AXXIA_CLK_MMC] = &clk_mmc_mux.aclk,
0512 };
0513
0514 static struct clk_hw *
0515 of_clk_axmclk_get(struct of_phandle_args *clkspec, void *unused)
0516 {
0517 unsigned int idx = clkspec->args[0];
0518
0519 if (idx >= ARRAY_SIZE(axmclk_clocks)) {
0520 pr_err("%s: invalid index %u\n", __func__, idx);
0521 return ERR_PTR(-EINVAL);
0522 }
0523
0524 return &axmclk_clocks[idx]->hw;
0525 }
0526
0527 static const struct regmap_config axmclk_regmap_config = {
0528 .reg_bits = 32,
0529 .reg_stride = 4,
0530 .val_bits = 32,
0531 .max_register = 0x1fffc,
0532 .fast_io = true,
0533 };
0534
0535 static const struct of_device_id axmclk_match_table[] = {
0536 { .compatible = "lsi,axm5516-clks" },
0537 { }
0538 };
0539 MODULE_DEVICE_TABLE(of, axmclk_match_table);
0540
0541 static int axmclk_probe(struct platform_device *pdev)
0542 {
0543 void __iomem *base;
0544 struct resource *res;
0545 int i, ret;
0546 struct device *dev = &pdev->dev;
0547 struct regmap *regmap;
0548 size_t num_clks;
0549
0550 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0551 base = devm_ioremap_resource(dev, res);
0552 if (IS_ERR(base))
0553 return PTR_ERR(base);
0554
0555 regmap = devm_regmap_init_mmio(dev, base, &axmclk_regmap_config);
0556 if (IS_ERR(regmap))
0557 return PTR_ERR(regmap);
0558
0559 num_clks = ARRAY_SIZE(axmclk_clocks);
0560 pr_info("axmclk: supporting %zu clocks\n", num_clks);
0561
0562
0563
0564
0565 for (i = 0; i < num_clks; i++) {
0566 axmclk_clocks[i]->regmap = regmap;
0567 ret = devm_clk_hw_register(dev, &axmclk_clocks[i]->hw);
0568 if (ret)
0569 return ret;
0570 }
0571
0572 return of_clk_add_hw_provider(dev->of_node, of_clk_axmclk_get, NULL);
0573 }
0574
0575 static int axmclk_remove(struct platform_device *pdev)
0576 {
0577 of_clk_del_provider(pdev->dev.of_node);
0578 return 0;
0579 }
0580
0581 static struct platform_driver axmclk_driver = {
0582 .probe = axmclk_probe,
0583 .remove = axmclk_remove,
0584 .driver = {
0585 .name = "clk-axm5516",
0586 .of_match_table = axmclk_match_table,
0587 },
0588 };
0589
0590 static int __init axmclk_init(void)
0591 {
0592 return platform_driver_register(&axmclk_driver);
0593 }
0594 core_initcall(axmclk_init);
0595
0596 static void __exit axmclk_exit(void)
0597 {
0598 platform_driver_unregister(&axmclk_driver);
0599 }
0600 module_exit(axmclk_exit);
0601
0602 MODULE_DESCRIPTION("AXM5516 clock driver");
0603 MODULE_LICENSE("GPL v2");
0604 MODULE_ALIAS("platform:clk-axm5516");