1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2012 ST Microelectronics 4 * Viresh Kumar <vireshk@kernel.org> 5 * 6 * General Purpose Timer Synthesizer clock implementation 7 */ 8 9 #define pr_fmt(fmt) "clk-gpt-synth: " fmt 10 11 #include <linux/clk-provider.h> 12 #include <linux/slab.h> 13 #include <linux/io.h> 14 #include <linux/err.h> 15 #include "clk.h" 16 17 #define GPT_MSCALE_MASK 0xFFF 18 #define GPT_NSCALE_SHIFT 12 19 #define GPT_NSCALE_MASK 0xF 20 21 /* 22 * DOC: General Purpose Timer Synthesizer clock 23 * 24 * Calculates gpt synth clk rate for different values of mscale and nscale 25 * 26 * Fout= Fin/((2 ^ (N+1)) * (M+1)) 27 */ 28 29 #define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw) 30 31 static unsigned long gpt_calc_rate(struct clk_hw *hw, unsigned long prate, 32 int index) 33 { 34 struct clk_gpt *gpt = to_clk_gpt(hw); 35 struct gpt_rate_tbl *rtbl = gpt->rtbl; 36 37 prate /= ((1 << (rtbl[index].nscale + 1)) * (rtbl[index].mscale + 1)); 38 39 return prate; 40 } 41 42 static int clk_gpt_determine_rate(struct clk_hw *hw, 43 struct clk_rate_request *req) 44 { 45 struct clk_gpt *gpt = to_clk_gpt(hw); 46 int unused; 47 48 req->rate = clk_round_rate_index(hw, req->rate, req->best_parent_rate, 49 gpt_calc_rate, gpt->rtbl_cnt, &unused); 50 51 return 0; 52 } 53 54 static unsigned long clk_gpt_recalc_rate(struct clk_hw *hw, 55 unsigned long parent_rate) 56 { 57 struct clk_gpt *gpt = to_clk_gpt(hw); 58 unsigned long flags = 0; 59 unsigned int div = 1, val; 60 61 if (gpt->lock) 62 spin_lock_irqsave(gpt->lock, flags); 63 64 val = readl_relaxed(gpt->reg); 65 66 if (gpt->lock) 67 spin_unlock_irqrestore(gpt->lock, flags); 68 69 div += val & GPT_MSCALE_MASK; 70 div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1); 71 72 if (!div) 73 return 0; 74 75 return parent_rate / div; 76 } 77 78 /* Configures new clock rate of gpt */ 79 static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate, 80 unsigned long prate) 81 { 82 struct clk_gpt *gpt = to_clk_gpt(hw); 83 struct gpt_rate_tbl *rtbl = gpt->rtbl; 84 unsigned long flags = 0, val; 85 int i; 86 87 clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt, 88 &i); 89 90 if (gpt->lock) 91 spin_lock_irqsave(gpt->lock, flags); 92 93 val = readl(gpt->reg) & ~GPT_MSCALE_MASK; 94 val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT); 95 96 val |= rtbl[i].mscale & GPT_MSCALE_MASK; 97 val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT; 98 99 writel_relaxed(val, gpt->reg); 100 101 if (gpt->lock) 102 spin_unlock_irqrestore(gpt->lock, flags); 103 104 return 0; 105 } 106 107 static const struct clk_ops clk_gpt_ops = { 108 .recalc_rate = clk_gpt_recalc_rate, 109 .determine_rate = clk_gpt_determine_rate, 110 .set_rate = clk_gpt_set_rate, 111 }; 112 113 struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned 114 long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 115 rtbl_cnt, spinlock_t *lock) 116 { 117 struct clk_init_data init; 118 struct clk_gpt *gpt; 119 struct clk *clk; 120 121 if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { 122 pr_err("Invalid arguments passed\n"); 123 return ERR_PTR(-EINVAL); 124 } 125 126 gpt = kzalloc(sizeof(*gpt), GFP_KERNEL); 127 if (!gpt) 128 return ERR_PTR(-ENOMEM); 129 130 /* struct clk_gpt assignments */ 131 gpt->reg = reg; 132 gpt->rtbl = rtbl; 133 gpt->rtbl_cnt = rtbl_cnt; 134 gpt->lock = lock; 135 gpt->hw.init = &init; 136 137 init.name = name; 138 init.ops = &clk_gpt_ops; 139 init.flags = flags; 140 init.parent_names = &parent_name; 141 init.num_parents = 1; 142 143 clk = clk_register(NULL, &gpt->hw); 144 if (!IS_ERR_OR_NULL(clk)) 145 return clk; 146 147 pr_err("clk register failed\n"); 148 kfree(gpt); 149 150 return NULL; 151 } 152