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