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