0001
0002
0003
0004
0005
0006 #include <linux/clk-provider.h>
0007 #include <linux/err.h>
0008 #include <linux/io.h>
0009 #include <linux/mfd/syscon.h>
0010 #include <linux/of.h>
0011 #include <linux/of_address.h>
0012 #include <linux/regmap.h>
0013 #include <linux/slab.h>
0014
0015 #define PMU_XTAL_FREQ_RATIO 0x66c
0016 #define XTAL_ALP_PER_4ILP 0x00001fff
0017 #define XTAL_CTL_EN 0x80000000
0018 #define PMU_SLOW_CLK_PERIOD 0x6dc
0019
0020 struct bcm53573_ilp {
0021 struct clk_hw hw;
0022 struct regmap *regmap;
0023 };
0024
0025 static int bcm53573_ilp_enable(struct clk_hw *hw)
0026 {
0027 struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
0028
0029 regmap_write(ilp->regmap, PMU_SLOW_CLK_PERIOD, 0x10199);
0030 regmap_write(ilp->regmap, 0x674, 0x10000);
0031
0032 return 0;
0033 }
0034
0035 static void bcm53573_ilp_disable(struct clk_hw *hw)
0036 {
0037 struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
0038
0039 regmap_write(ilp->regmap, PMU_SLOW_CLK_PERIOD, 0);
0040 regmap_write(ilp->regmap, 0x674, 0);
0041 }
0042
0043 static unsigned long bcm53573_ilp_recalc_rate(struct clk_hw *hw,
0044 unsigned long parent_rate)
0045 {
0046 struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
0047 struct regmap *regmap = ilp->regmap;
0048 u32 last_val, cur_val;
0049 int sum = 0, num = 0, loop_num = 0;
0050 int avg;
0051
0052
0053 regmap_write(regmap, PMU_XTAL_FREQ_RATIO, XTAL_CTL_EN);
0054
0055
0056 regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &last_val);
0057 last_val &= XTAL_ALP_PER_4ILP;
0058
0059
0060
0061
0062
0063
0064 while (num < 20) {
0065 regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &cur_val);
0066 cur_val &= XTAL_ALP_PER_4ILP;
0067
0068 if (cur_val != last_val) {
0069
0070 sum += cur_val;
0071 num++;
0072 loop_num = 0;
0073 last_val = cur_val;
0074 } else if (++loop_num > 5000) {
0075
0076 sum += cur_val;
0077 num++;
0078 break;
0079 }
0080
0081 cpu_relax();
0082 }
0083
0084
0085 regmap_write(regmap, PMU_XTAL_FREQ_RATIO, 0x0);
0086
0087 avg = sum / num;
0088
0089 return parent_rate * 4 / avg;
0090 }
0091
0092 static const struct clk_ops bcm53573_ilp_clk_ops = {
0093 .enable = bcm53573_ilp_enable,
0094 .disable = bcm53573_ilp_disable,
0095 .recalc_rate = bcm53573_ilp_recalc_rate,
0096 };
0097
0098 static void bcm53573_ilp_init(struct device_node *np)
0099 {
0100 struct bcm53573_ilp *ilp;
0101 struct clk_init_data init = { };
0102 const char *parent_name;
0103 int err;
0104
0105 ilp = kzalloc(sizeof(*ilp), GFP_KERNEL);
0106 if (!ilp)
0107 return;
0108
0109 parent_name = of_clk_get_parent_name(np, 0);
0110 if (!parent_name) {
0111 err = -ENOENT;
0112 goto err_free_ilp;
0113 }
0114
0115 ilp->regmap = syscon_node_to_regmap(of_get_parent(np));
0116 if (IS_ERR(ilp->regmap)) {
0117 err = PTR_ERR(ilp->regmap);
0118 goto err_free_ilp;
0119 }
0120
0121 init.name = np->name;
0122 init.ops = &bcm53573_ilp_clk_ops;
0123 init.parent_names = &parent_name;
0124 init.num_parents = 1;
0125
0126 ilp->hw.init = &init;
0127 err = clk_hw_register(NULL, &ilp->hw);
0128 if (err)
0129 goto err_free_ilp;
0130
0131 err = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &ilp->hw);
0132 if (err)
0133 goto err_clk_hw_unregister;
0134
0135 return;
0136
0137 err_clk_hw_unregister:
0138 clk_hw_unregister(&ilp->hw);
0139 err_free_ilp:
0140 kfree(ilp);
0141 pr_err("Failed to init ILP clock: %d\n", err);
0142 }
0143
0144
0145 CLK_OF_DECLARE(bcm53573_ilp_clk, "brcm,bcm53573-ilp", bcm53573_ilp_init);