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 int clk_plldiv_determine_rate(struct clk_hw *hw, 37 struct clk_rate_request *req) 38 { 39 unsigned long div; 40 41 if (req->rate > req->best_parent_rate) { 42 req->rate = req->best_parent_rate; 43 44 return 0; 45 } 46 47 div = req->best_parent_rate / 2; 48 if (req->rate < div) { 49 req->rate = div; 50 51 return 0; 52 } 53 54 if (req->rate - div < req->best_parent_rate - req->rate) { 55 req->rate = div; 56 57 return 0; 58 } 59 60 req->rate = req->best_parent_rate; 61 62 return 0; 63 } 64 65 static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate, 66 unsigned long parent_rate) 67 { 68 struct clk_plldiv *plldiv = to_clk_plldiv(hw); 69 70 if ((parent_rate != rate) && (parent_rate / 2 != rate)) 71 return -EINVAL; 72 73 regmap_update_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2, 74 parent_rate != rate ? AT91_PMC_PLLADIV2 : 0); 75 76 return 0; 77 } 78 79 static const struct clk_ops plldiv_ops = { 80 .recalc_rate = clk_plldiv_recalc_rate, 81 .determine_rate = clk_plldiv_determine_rate, 82 .set_rate = clk_plldiv_set_rate, 83 }; 84 85 struct clk_hw * __init 86 at91_clk_register_plldiv(struct regmap *regmap, const char *name, 87 const char *parent_name) 88 { 89 struct clk_plldiv *plldiv; 90 struct clk_hw *hw; 91 struct clk_init_data init; 92 int ret; 93 94 plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL); 95 if (!plldiv) 96 return ERR_PTR(-ENOMEM); 97 98 init.name = name; 99 init.ops = &plldiv_ops; 100 init.parent_names = parent_name ? &parent_name : NULL; 101 init.num_parents = parent_name ? 1 : 0; 102 init.flags = CLK_SET_RATE_GATE; 103 104 plldiv->hw.init = &init; 105 plldiv->regmap = regmap; 106 107 hw = &plldiv->hw; 108 ret = clk_hw_register(NULL, &plldiv->hw); 109 if (ret) { 110 kfree(plldiv); 111 hw = ERR_PTR(ret); 112 } 113 114 return hw; 115 } 116