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