Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : /* 3 : : * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> 4 : : */ 5 : : #include <linux/module.h> 6 : : #include <linux/clk-provider.h> 7 : : #include <linux/slab.h> 8 : : #include <linux/err.h> 9 : : #include <linux/of.h> 10 : : #include <linux/platform_device.h> 11 : : 12 : : /* 13 : : * DOC: basic fixed multiplier and divider clock that cannot gate 14 : : * 15 : : * Traits of this clock: 16 : : * prepare - clk_prepare only ensures that parents are prepared 17 : : * enable - clk_enable only ensures that parents are enabled 18 : : * rate - rate is fixed. clk->rate = parent->rate / div * mult 19 : : * parent - fixed parent. No clk_set_parent support 20 : : */ 21 : : 22 : 3 : static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, 23 : : unsigned long parent_rate) 24 : : { 25 : : struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); 26 : : unsigned long long int rate; 27 : : 28 : 3 : rate = (unsigned long long int)parent_rate * fix->mult; 29 : 3 : do_div(rate, fix->div); 30 : 3 : return (unsigned long)rate; 31 : : } 32 : : 33 : 0 : static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate, 34 : : unsigned long *prate) 35 : : { 36 : : struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); 37 : : 38 : 0 : if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { 39 : : unsigned long best_parent; 40 : : 41 : 0 : best_parent = (rate / fix->mult) * fix->div; 42 : 0 : *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent); 43 : : } 44 : : 45 : 0 : return (*prate / fix->div) * fix->mult; 46 : : } 47 : : 48 : 0 : static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate, 49 : : unsigned long parent_rate) 50 : : { 51 : : /* 52 : : * We must report success but we can do so unconditionally because 53 : : * clk_factor_round_rate returns values that ensure this call is a 54 : : * nop. 55 : : */ 56 : : 57 : 0 : return 0; 58 : : } 59 : : 60 : : const struct clk_ops clk_fixed_factor_ops = { 61 : : .round_rate = clk_factor_round_rate, 62 : : .set_rate = clk_factor_set_rate, 63 : : .recalc_rate = clk_factor_recalc_rate, 64 : : }; 65 : : EXPORT_SYMBOL_GPL(clk_fixed_factor_ops); 66 : : 67 : : static struct clk_hw * 68 : 3 : __clk_hw_register_fixed_factor(struct device *dev, struct device_node *np, 69 : : const char *name, const char *parent_name, int index, 70 : : unsigned long flags, unsigned int mult, unsigned int div) 71 : : { 72 : : struct clk_fixed_factor *fix; 73 : 3 : struct clk_init_data init = { }; 74 : 3 : struct clk_parent_data pdata = { .index = index }; 75 : : struct clk_hw *hw; 76 : : int ret; 77 : : 78 : : fix = kmalloc(sizeof(*fix), GFP_KERNEL); 79 : 3 : if (!fix) 80 : : return ERR_PTR(-ENOMEM); 81 : : 82 : : /* struct clk_fixed_factor assignments */ 83 : 3 : fix->mult = mult; 84 : 3 : fix->div = div; 85 : 3 : fix->hw.init = &init; 86 : : 87 : 3 : init.name = name; 88 : 3 : init.ops = &clk_fixed_factor_ops; 89 : 3 : init.flags = flags; 90 : 3 : if (parent_name) 91 : 3 : init.parent_names = &parent_name; 92 : : else 93 : 0 : init.parent_data = &pdata; 94 : 3 : init.num_parents = 1; 95 : : 96 : 3 : hw = &fix->hw; 97 : 3 : if (dev) 98 : 3 : ret = clk_hw_register(dev, hw); 99 : : else 100 : 0 : ret = of_clk_hw_register(np, hw); 101 : 3 : if (ret) { 102 : 0 : kfree(fix); 103 : : hw = ERR_PTR(ret); 104 : : } 105 : : 106 : 3 : return hw; 107 : : } 108 : : 109 : 3 : struct clk_hw *clk_hw_register_fixed_factor(struct device *dev, 110 : : const char *name, const char *parent_name, unsigned long flags, 111 : : unsigned int mult, unsigned int div) 112 : : { 113 : 3 : return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, -1, 114 : : flags, mult, div); 115 : : } 116 : : EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor); 117 : : 118 : 0 : struct clk *clk_register_fixed_factor(struct device *dev, const char *name, 119 : : const char *parent_name, unsigned long flags, 120 : : unsigned int mult, unsigned int div) 121 : : { 122 : : struct clk_hw *hw; 123 : : 124 : : hw = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult, 125 : : div); 126 : 0 : if (IS_ERR(hw)) 127 : : return ERR_CAST(hw); 128 : 0 : return hw->clk; 129 : : } 130 : : EXPORT_SYMBOL_GPL(clk_register_fixed_factor); 131 : : 132 : 0 : void clk_unregister_fixed_factor(struct clk *clk) 133 : : { 134 : : struct clk_hw *hw; 135 : : 136 : 0 : hw = __clk_get_hw(clk); 137 : 0 : if (!hw) 138 : 0 : return; 139 : : 140 : 0 : clk_unregister(clk); 141 : 0 : kfree(to_clk_fixed_factor(hw)); 142 : : } 143 : : EXPORT_SYMBOL_GPL(clk_unregister_fixed_factor); 144 : : 145 : 0 : void clk_hw_unregister_fixed_factor(struct clk_hw *hw) 146 : : { 147 : : struct clk_fixed_factor *fix; 148 : : 149 : : fix = to_clk_fixed_factor(hw); 150 : : 151 : 0 : clk_hw_unregister(hw); 152 : 0 : kfree(fix); 153 : 0 : } 154 : : EXPORT_SYMBOL_GPL(clk_hw_unregister_fixed_factor); 155 : : 156 : : #ifdef CONFIG_OF 157 : : static const struct of_device_id set_rate_parent_matches[] = { 158 : : { .compatible = "allwinner,sun4i-a10-pll3-2x-clk" }, 159 : : { /* Sentinel */ }, 160 : : }; 161 : : 162 : 0 : static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node) 163 : : { 164 : : struct clk_hw *hw; 165 : 0 : const char *clk_name = node->name; 166 : : unsigned long flags = 0; 167 : : u32 div, mult; 168 : : int ret; 169 : : 170 : 0 : if (of_property_read_u32(node, "clock-div", &div)) { 171 : 0 : pr_err("%s Fixed factor clock <%pOFn> must have a clock-div property\n", 172 : : __func__, node); 173 : 0 : return ERR_PTR(-EIO); 174 : : } 175 : : 176 : 0 : if (of_property_read_u32(node, "clock-mult", &mult)) { 177 : 0 : pr_err("%s Fixed factor clock <%pOFn> must have a clock-mult property\n", 178 : : __func__, node); 179 : 0 : return ERR_PTR(-EIO); 180 : : } 181 : : 182 : 0 : of_property_read_string(node, "clock-output-names", &clk_name); 183 : : 184 : 0 : if (of_match_node(set_rate_parent_matches, node)) 185 : : flags |= CLK_SET_RATE_PARENT; 186 : : 187 : 0 : hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, 0, 188 : : flags, mult, div); 189 : 0 : if (IS_ERR(hw)) { 190 : : /* 191 : : * Clear OF_POPULATED flag so that clock registration can be 192 : : * attempted again from probe function. 193 : : */ 194 : : of_node_clear_flag(node, OF_POPULATED); 195 : 0 : return ERR_CAST(hw); 196 : : } 197 : : 198 : 0 : ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); 199 : 0 : if (ret) { 200 : : clk_hw_unregister_fixed_factor(hw); 201 : 0 : return ERR_PTR(ret); 202 : : } 203 : : 204 : : return hw; 205 : : } 206 : : 207 : : /** 208 : : * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock 209 : : */ 210 : 0 : void __init of_fixed_factor_clk_setup(struct device_node *node) 211 : : { 212 : 0 : _of_fixed_factor_clk_setup(node); 213 : 0 : } 214 : : CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock", 215 : : of_fixed_factor_clk_setup); 216 : : 217 : 0 : static int of_fixed_factor_clk_remove(struct platform_device *pdev) 218 : : { 219 : : struct clk_hw *clk = platform_get_drvdata(pdev); 220 : : 221 : 0 : of_clk_del_provider(pdev->dev.of_node); 222 : : clk_hw_unregister_fixed_factor(clk); 223 : : 224 : 0 : return 0; 225 : : } 226 : : 227 : 0 : static int of_fixed_factor_clk_probe(struct platform_device *pdev) 228 : : { 229 : : struct clk_hw *clk; 230 : : 231 : : /* 232 : : * This function is not executed when of_fixed_factor_clk_setup 233 : : * succeeded. 234 : : */ 235 : 0 : clk = _of_fixed_factor_clk_setup(pdev->dev.of_node); 236 : 0 : if (IS_ERR(clk)) 237 : 0 : return PTR_ERR(clk); 238 : : 239 : : platform_set_drvdata(pdev, clk); 240 : : 241 : 0 : return 0; 242 : : } 243 : : 244 : : static const struct of_device_id of_fixed_factor_clk_ids[] = { 245 : : { .compatible = "fixed-factor-clock" }, 246 : : { } 247 : : }; 248 : : MODULE_DEVICE_TABLE(of, of_fixed_factor_clk_ids); 249 : : 250 : : static struct platform_driver of_fixed_factor_clk_driver = { 251 : : .driver = { 252 : : .name = "of_fixed_factor_clk", 253 : : .of_match_table = of_fixed_factor_clk_ids, 254 : : }, 255 : : .probe = of_fixed_factor_clk_probe, 256 : : .remove = of_fixed_factor_clk_remove, 257 : : }; 258 : 3 : builtin_platform_driver(of_fixed_factor_clk_driver); 259 : : #endif