1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 4 */ 5 6 #include <linux/clk-provider.h> 7 #include <linux/clkdev.h> 8 #include <linux/clk/at91_pmc.h> 9 #include <linux/of.h> 10 #include <linux/mfd/syscon.h> 11 #include <linux/regmap.h> 12 13 #include "pmc.h" 14 15 #define to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw) 16 17 struct clk_plldiv { 18 struct clk_hw hw; 19 struct regmap *regmap; 20 }; 21 22 static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw, 23 unsigned long parent_rate) 24 { 25 struct clk_plldiv *plldiv = to_clk_plldiv(hw); 26 unsigned int mckr; 27 28 regmap_read(plldiv->regmap, AT91_PMC_MCKR, &mckr); 29 30 if (mckr & AT91_PMC_PLLADIV2) 31 return parent_rate / 2; 32 33 return parent_rate; 34 } 35 36 static long clk_plldiv_round_rate(struct clk_hw *hw, unsigned long rate, 37 unsigned long *parent_rate) 38 { 39 unsigned long div; 40 41 if (rate > *parent_rate) 42 return *parent_rate; 43 div = *parent_rate / 2; 44 if (rate < div) 45 return div; 46 47 if (rate - div < *parent_rate - rate) 48 return div; 49 50 return *parent_rate; 51 } 52 53 static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate, 54 unsigned long parent_rate) 55 { 56 struct clk_plldiv *plldiv = to_clk_plldiv(hw); 57 58 if ((parent_rate != rate) && (parent_rate / 2 != rate)) 59 return -EINVAL; 60 61 regmap_update_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2, 62 parent_rate != rate ? AT91_PMC_PLLADIV2 : 0); 63 64 return 0; 65 } 66 67 static const struct clk_ops plldiv_ops = { 68 .recalc_rate = clk_plldiv_recalc_rate, 69 .round_rate = clk_plldiv_round_rate, 70 .set_rate = clk_plldiv_set_rate, 71 }; 72 73 struct clk_hw * __init 74 at91_clk_register_plldiv(struct regmap *regmap, const char *name, 75 const char *parent_name) 76 { 77 struct clk_plldiv *plldiv; 78 struct clk_hw *hw; 79 struct clk_init_data init; 80 int ret; 81 82 plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL); 83 if (!plldiv) 84 return ERR_PTR(-ENOMEM); 85 86 init.name = name; 87 init.ops = &plldiv_ops; 88 init.parent_names = parent_name ? &parent_name : NULL; 89 init.num_parents = parent_name ? 1 : 0; 90 init.flags = CLK_SET_RATE_GATE; 91 92 plldiv->hw.init = &init; 93 plldiv->regmap = regmap; 94 95 hw = &plldiv->hw; 96 ret = clk_hw_register(NULL, &plldiv->hw); 97 if (ret) { 98 kfree(plldiv); 99 hw = ERR_PTR(ret); 100 } 101 102 return hw; 103 } 104