Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * drivers/clk/clk-axm5516.c
0004  *
0005  * Provides clock implementations for three different types of clock devices on
0006  * the Axxia device: PLL clock, a clock divider and a clock mux.
0007  *
0008  * Copyright (C) 2014 LSI Corporation
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  * struct axxia_clk - Common struct to all Axxia clocks.
0023  * @hw: clk_hw for the common clk framework
0024  * @regmap: Regmap for the clock control registers
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  * struct axxia_pllclk - Axxia PLL generated clock.
0034  * @aclk: Common struct
0035  * @reg: Offset into regmap for PLL control register
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  * axxia_pllclk_recalc - Calculate the PLL generated clock rate given the
0045  * parent clock rate.
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  * struct axxia_divclk - Axxia clock divider
0070  * @aclk: Common struct
0071  * @reg: Offset into regmap for PLL control register
0072  * @shift: Bit position for divider value
0073  * @width: Number of bits in divider value
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  * axxia_divclk_recalc_rate - Calculate clock divider output rage
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  * struct axxia_clkmux - Axxia clock mux
0105  * @aclk: Common struct
0106  * @reg: Offset into regmap for PLL control register
0107  * @shift: Bit position for selection value
0108  * @width: Number of bits in selection value
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  * axxia_clkmux_get_parent - Return the index of selected parent clock
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  * PLLs
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  * Clock dividers
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  * Clock MUXes
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 /* Table of all supported clocks indexed by the clock identifiers from the
0486  * device tree binding
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     /* Update each entry with the allocated regmap and register the clock
0563      * with the common clock framework
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");