Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2012 ST Microelectronics
0004  * Viresh Kumar <vireshk@kernel.org>
0005  *
0006  * General Purpose Timer Synthesizer clock implementation
0007  */
0008 
0009 #define pr_fmt(fmt) "clk-gpt-synth: " fmt
0010 
0011 #include <linux/clk-provider.h>
0012 #include <linux/slab.h>
0013 #include <linux/io.h>
0014 #include <linux/err.h>
0015 #include "clk.h"
0016 
0017 #define GPT_MSCALE_MASK     0xFFF
0018 #define GPT_NSCALE_SHIFT    12
0019 #define GPT_NSCALE_MASK     0xF
0020 
0021 /*
0022  * DOC: General Purpose Timer Synthesizer clock
0023  *
0024  * Calculates gpt synth clk rate for different values of mscale and nscale
0025  *
0026  * Fout= Fin/((2 ^ (N+1)) * (M+1))
0027  */
0028 
0029 #define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw)
0030 
0031 static unsigned long gpt_calc_rate(struct clk_hw *hw, unsigned long prate,
0032         int index)
0033 {
0034     struct clk_gpt *gpt = to_clk_gpt(hw);
0035     struct gpt_rate_tbl *rtbl = gpt->rtbl;
0036 
0037     prate /= ((1 << (rtbl[index].nscale + 1)) * (rtbl[index].mscale + 1));
0038 
0039     return prate;
0040 }
0041 
0042 static long clk_gpt_round_rate(struct clk_hw *hw, unsigned long drate,
0043         unsigned long *prate)
0044 {
0045     struct clk_gpt *gpt = to_clk_gpt(hw);
0046     int unused;
0047 
0048     return clk_round_rate_index(hw, drate, *prate, gpt_calc_rate,
0049             gpt->rtbl_cnt, &unused);
0050 }
0051 
0052 static unsigned long clk_gpt_recalc_rate(struct clk_hw *hw,
0053         unsigned long parent_rate)
0054 {
0055     struct clk_gpt *gpt = to_clk_gpt(hw);
0056     unsigned long flags = 0;
0057     unsigned int div = 1, val;
0058 
0059     if (gpt->lock)
0060         spin_lock_irqsave(gpt->lock, flags);
0061 
0062     val = readl_relaxed(gpt->reg);
0063 
0064     if (gpt->lock)
0065         spin_unlock_irqrestore(gpt->lock, flags);
0066 
0067     div += val & GPT_MSCALE_MASK;
0068     div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1);
0069 
0070     if (!div)
0071         return 0;
0072 
0073     return parent_rate / div;
0074 }
0075 
0076 /* Configures new clock rate of gpt */
0077 static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate,
0078                 unsigned long prate)
0079 {
0080     struct clk_gpt *gpt = to_clk_gpt(hw);
0081     struct gpt_rate_tbl *rtbl = gpt->rtbl;
0082     unsigned long flags = 0, val;
0083     int i;
0084 
0085     clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt,
0086             &i);
0087 
0088     if (gpt->lock)
0089         spin_lock_irqsave(gpt->lock, flags);
0090 
0091     val = readl(gpt->reg) & ~GPT_MSCALE_MASK;
0092     val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT);
0093 
0094     val |= rtbl[i].mscale & GPT_MSCALE_MASK;
0095     val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT;
0096 
0097     writel_relaxed(val, gpt->reg);
0098 
0099     if (gpt->lock)
0100         spin_unlock_irqrestore(gpt->lock, flags);
0101 
0102     return 0;
0103 }
0104 
0105 static const struct clk_ops clk_gpt_ops = {
0106     .recalc_rate = clk_gpt_recalc_rate,
0107     .round_rate = clk_gpt_round_rate,
0108     .set_rate = clk_gpt_set_rate,
0109 };
0110 
0111 struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned
0112         long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8
0113         rtbl_cnt, spinlock_t *lock)
0114 {
0115     struct clk_init_data init;
0116     struct clk_gpt *gpt;
0117     struct clk *clk;
0118 
0119     if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) {
0120         pr_err("Invalid arguments passed\n");
0121         return ERR_PTR(-EINVAL);
0122     }
0123 
0124     gpt = kzalloc(sizeof(*gpt), GFP_KERNEL);
0125     if (!gpt)
0126         return ERR_PTR(-ENOMEM);
0127 
0128     /* struct clk_gpt assignments */
0129     gpt->reg = reg;
0130     gpt->rtbl = rtbl;
0131     gpt->rtbl_cnt = rtbl_cnt;
0132     gpt->lock = lock;
0133     gpt->hw.init = &init;
0134 
0135     init.name = name;
0136     init.ops = &clk_gpt_ops;
0137     init.flags = flags;
0138     init.parent_names = &parent_name;
0139     init.num_parents = 1;
0140 
0141     clk = clk_register(NULL, &gpt->hw);
0142     if (!IS_ERR_OR_NULL(clk))
0143         return clk;
0144 
0145     pr_err("clk register failed\n");
0146     kfree(gpt);
0147 
0148     return NULL;
0149 }