18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2012 ST Microelectronics 38c2ecf20Sopenharmony_ci * Viresh Kumar <vireshk@kernel.org> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 68c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 78c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * General Purpose Timer Synthesizer clock implementation 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "clk-gpt-synth: " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include "clk.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define GPT_MSCALE_MASK 0xFFF 218c2ecf20Sopenharmony_ci#define GPT_NSCALE_SHIFT 12 228c2ecf20Sopenharmony_ci#define GPT_NSCALE_MASK 0xF 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * DOC: General Purpose Timer Synthesizer clock 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Calculates gpt synth clk rate for different values of mscale and nscale 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Fout= Fin/((2 ^ (N+1)) * (M+1)) 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic unsigned long gpt_calc_rate(struct clk_hw *hw, unsigned long prate, 358c2ecf20Sopenharmony_ci int index) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct clk_gpt *gpt = to_clk_gpt(hw); 388c2ecf20Sopenharmony_ci struct gpt_rate_tbl *rtbl = gpt->rtbl; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci prate /= ((1 << (rtbl[index].nscale + 1)) * (rtbl[index].mscale + 1)); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return prate; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic long clk_gpt_round_rate(struct clk_hw *hw, unsigned long drate, 468c2ecf20Sopenharmony_ci unsigned long *prate) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct clk_gpt *gpt = to_clk_gpt(hw); 498c2ecf20Sopenharmony_ci int unused; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return clk_round_rate_index(hw, drate, *prate, gpt_calc_rate, 528c2ecf20Sopenharmony_ci gpt->rtbl_cnt, &unused); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic unsigned long clk_gpt_recalc_rate(struct clk_hw *hw, 568c2ecf20Sopenharmony_ci unsigned long parent_rate) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct clk_gpt *gpt = to_clk_gpt(hw); 598c2ecf20Sopenharmony_ci unsigned long flags = 0; 608c2ecf20Sopenharmony_ci unsigned int div = 1, val; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (gpt->lock) 638c2ecf20Sopenharmony_ci spin_lock_irqsave(gpt->lock, flags); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci val = readl_relaxed(gpt->reg); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (gpt->lock) 688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(gpt->lock, flags); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci div += val & GPT_MSCALE_MASK; 718c2ecf20Sopenharmony_ci div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!div) 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return parent_rate / div; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* Configures new clock rate of gpt */ 808c2ecf20Sopenharmony_cistatic int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate, 818c2ecf20Sopenharmony_ci unsigned long prate) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct clk_gpt *gpt = to_clk_gpt(hw); 848c2ecf20Sopenharmony_ci struct gpt_rate_tbl *rtbl = gpt->rtbl; 858c2ecf20Sopenharmony_ci unsigned long flags = 0, val; 868c2ecf20Sopenharmony_ci int i; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt, 898c2ecf20Sopenharmony_ci &i); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (gpt->lock) 928c2ecf20Sopenharmony_ci spin_lock_irqsave(gpt->lock, flags); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci val = readl(gpt->reg) & ~GPT_MSCALE_MASK; 958c2ecf20Sopenharmony_ci val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci val |= rtbl[i].mscale & GPT_MSCALE_MASK; 988c2ecf20Sopenharmony_ci val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci writel_relaxed(val, gpt->reg); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (gpt->lock) 1038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(gpt->lock, flags); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic const struct clk_ops clk_gpt_ops = { 1098c2ecf20Sopenharmony_ci .recalc_rate = clk_gpt_recalc_rate, 1108c2ecf20Sopenharmony_ci .round_rate = clk_gpt_round_rate, 1118c2ecf20Sopenharmony_ci .set_rate = clk_gpt_set_rate, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistruct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned 1158c2ecf20Sopenharmony_ci long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 1168c2ecf20Sopenharmony_ci rtbl_cnt, spinlock_t *lock) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct clk_init_data init; 1198c2ecf20Sopenharmony_ci struct clk_gpt *gpt; 1208c2ecf20Sopenharmony_ci struct clk *clk; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { 1238c2ecf20Sopenharmony_ci pr_err("Invalid arguments passed\n"); 1248c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci gpt = kzalloc(sizeof(*gpt), GFP_KERNEL); 1288c2ecf20Sopenharmony_ci if (!gpt) 1298c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* struct clk_gpt assignments */ 1328c2ecf20Sopenharmony_ci gpt->reg = reg; 1338c2ecf20Sopenharmony_ci gpt->rtbl = rtbl; 1348c2ecf20Sopenharmony_ci gpt->rtbl_cnt = rtbl_cnt; 1358c2ecf20Sopenharmony_ci gpt->lock = lock; 1368c2ecf20Sopenharmony_ci gpt->hw.init = &init; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci init.name = name; 1398c2ecf20Sopenharmony_ci init.ops = &clk_gpt_ops; 1408c2ecf20Sopenharmony_ci init.flags = flags; 1418c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 1428c2ecf20Sopenharmony_ci init.num_parents = 1; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci clk = clk_register(NULL, &gpt->hw); 1458c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(clk)) 1468c2ecf20Sopenharmony_ci return clk; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci pr_err("clk register failed\n"); 1498c2ecf20Sopenharmony_ci kfree(gpt); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return NULL; 1528c2ecf20Sopenharmony_ci} 153