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 long clk_gpt_round_rate(struct clk_hw *hw, unsigned long drate, 43 unsigned long *prate) 44 { 45 struct clk_gpt *gpt = to_clk_gpt(hw); 46 int unused; 47 48 return clk_round_rate_index(hw, drate, *prate, gpt_calc_rate, 49 gpt->rtbl_cnt, &unused); 50 } 51 52 static unsigned long clk_gpt_recalc_rate(struct clk_hw *hw, 53 unsigned long parent_rate) 54 { 55 struct clk_gpt *gpt = to_clk_gpt(hw); 56 unsigned long flags = 0; 57 unsigned int div = 1, val; 58 59 if (gpt->lock) 60 spin_lock_irqsave(gpt->lock, flags); 61 62 val = readl_relaxed(gpt->reg); 63 64 if (gpt->lock) 65 spin_unlock_irqrestore(gpt->lock, flags); 66 67 div += val & GPT_MSCALE_MASK; 68 div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1); 69 70 if (!div) 71 return 0; 72 73 return parent_rate / div; 74 } 75 76 /* Configures new clock rate of gpt */ 77 static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate, 78 unsigned long prate) 79 { 80 struct clk_gpt *gpt = to_clk_gpt(hw); 81 struct gpt_rate_tbl *rtbl = gpt->rtbl; 82 unsigned long flags = 0, val; 83 int i; 84 85 clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt, 86 &i); 87 88 if (gpt->lock) 89 spin_lock_irqsave(gpt->lock, flags); 90 91 val = readl(gpt->reg) & ~GPT_MSCALE_MASK; 92 val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT); 93 94 val |= rtbl[i].mscale & GPT_MSCALE_MASK; 95 val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT; 96 97 writel_relaxed(val, gpt->reg); 98 99 if (gpt->lock) 100 spin_unlock_irqrestore(gpt->lock, flags); 101 102 return 0; 103 } 104 105 static const struct clk_ops clk_gpt_ops = { 106 .recalc_rate = clk_gpt_recalc_rate, 107 .round_rate = clk_gpt_round_rate, 108 .set_rate = clk_gpt_set_rate, 109 }; 110 111 struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned 112 long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 113 rtbl_cnt, spinlock_t *lock) 114 { 115 struct clk_init_data init; 116 struct clk_gpt *gpt; 117 struct clk *clk; 118 119 if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { 120 pr_err("Invalid arguments passed\n"); 121 return ERR_PTR(-EINVAL); 122 } 123 124 gpt = kzalloc(sizeof(*gpt), GFP_KERNEL); 125 if (!gpt) 126 return ERR_PTR(-ENOMEM); 127 128 /* struct clk_gpt assignments */ 129 gpt->reg = reg; 130 gpt->rtbl = rtbl; 131 gpt->rtbl_cnt = rtbl_cnt; 132 gpt->lock = lock; 133 gpt->hw.init = &init; 134 135 init.name = name; 136 init.ops = &clk_gpt_ops; 137 init.flags = flags; 138 init.parent_names = &parent_name; 139 init.num_parents = 1; 140 141 clk = clk_register(NULL, &gpt->hw); 142 if (!IS_ERR_OR_NULL(clk)) 143 return clk; 144 145 pr_err("clk register failed\n"); 146 kfree(gpt); 147 148 return NULL; 149 } 150