150 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2012 ST Microelectronics4 * Viresh Kumar <vireshk@kernel.org>5 *6 * General Purpose Timer Synthesizer clock implementation7 */8 9#define pr_fmt(fmt) "clk-gpt-synth: " fmt10 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 0xFFF18#define GPT_NSCALE_SHIFT 1219#define GPT_NSCALE_MASK 0xF20 21/*22 * DOC: General Purpose Timer Synthesizer clock23 *24 * Calculates gpt synth clk rate for different values of mscale and nscale25 *26 * Fout= Fin/((2 ^ (N+1)) * (M+1))27 */28 29#define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw)30 31static 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 42static 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 52static 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 */77static 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 105static 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 111struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned112 long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8113 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