162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 ST Microelectronics 462306a36Sopenharmony_ci * Viresh Kumar <vireshk@kernel.org> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * General Purpose Timer Synthesizer clock implementation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) "clk-gpt-synth: " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/clk-provider.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include "clk.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define GPT_MSCALE_MASK 0xFFF 1862306a36Sopenharmony_ci#define GPT_NSCALE_SHIFT 12 1962306a36Sopenharmony_ci#define GPT_NSCALE_MASK 0xF 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * DOC: General Purpose Timer Synthesizer clock 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Calculates gpt synth clk rate for different values of mscale and nscale 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Fout= Fin/((2 ^ (N+1)) * (M+1)) 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic unsigned long gpt_calc_rate(struct clk_hw *hw, unsigned long prate, 3262306a36Sopenharmony_ci int index) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct clk_gpt *gpt = to_clk_gpt(hw); 3562306a36Sopenharmony_ci struct gpt_rate_tbl *rtbl = gpt->rtbl; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci prate /= ((1 << (rtbl[index].nscale + 1)) * (rtbl[index].mscale + 1)); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci return prate; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic long clk_gpt_round_rate(struct clk_hw *hw, unsigned long drate, 4362306a36Sopenharmony_ci unsigned long *prate) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct clk_gpt *gpt = to_clk_gpt(hw); 4662306a36Sopenharmony_ci int unused; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return clk_round_rate_index(hw, drate, *prate, gpt_calc_rate, 4962306a36Sopenharmony_ci gpt->rtbl_cnt, &unused); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic unsigned long clk_gpt_recalc_rate(struct clk_hw *hw, 5362306a36Sopenharmony_ci unsigned long parent_rate) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct clk_gpt *gpt = to_clk_gpt(hw); 5662306a36Sopenharmony_ci unsigned long flags = 0; 5762306a36Sopenharmony_ci unsigned int div = 1, val; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (gpt->lock) 6062306a36Sopenharmony_ci spin_lock_irqsave(gpt->lock, flags); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci val = readl_relaxed(gpt->reg); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (gpt->lock) 6562306a36Sopenharmony_ci spin_unlock_irqrestore(gpt->lock, flags); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci div += val & GPT_MSCALE_MASK; 6862306a36Sopenharmony_ci div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (!div) 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return parent_rate / div; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Configures new clock rate of gpt */ 7762306a36Sopenharmony_cistatic int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate, 7862306a36Sopenharmony_ci unsigned long prate) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct clk_gpt *gpt = to_clk_gpt(hw); 8162306a36Sopenharmony_ci struct gpt_rate_tbl *rtbl = gpt->rtbl; 8262306a36Sopenharmony_ci unsigned long flags = 0, val; 8362306a36Sopenharmony_ci int i; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt, 8662306a36Sopenharmony_ci &i); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (gpt->lock) 8962306a36Sopenharmony_ci spin_lock_irqsave(gpt->lock, flags); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci val = readl(gpt->reg) & ~GPT_MSCALE_MASK; 9262306a36Sopenharmony_ci val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci val |= rtbl[i].mscale & GPT_MSCALE_MASK; 9562306a36Sopenharmony_ci val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci writel_relaxed(val, gpt->reg); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (gpt->lock) 10062306a36Sopenharmony_ci spin_unlock_irqrestore(gpt->lock, flags); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const struct clk_ops clk_gpt_ops = { 10662306a36Sopenharmony_ci .recalc_rate = clk_gpt_recalc_rate, 10762306a36Sopenharmony_ci .round_rate = clk_gpt_round_rate, 10862306a36Sopenharmony_ci .set_rate = clk_gpt_set_rate, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistruct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned 11262306a36Sopenharmony_ci long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 11362306a36Sopenharmony_ci rtbl_cnt, spinlock_t *lock) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct clk_init_data init; 11662306a36Sopenharmony_ci struct clk_gpt *gpt; 11762306a36Sopenharmony_ci struct clk *clk; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { 12062306a36Sopenharmony_ci pr_err("Invalid arguments passed\n"); 12162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci gpt = kzalloc(sizeof(*gpt), GFP_KERNEL); 12562306a36Sopenharmony_ci if (!gpt) 12662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* struct clk_gpt assignments */ 12962306a36Sopenharmony_ci gpt->reg = reg; 13062306a36Sopenharmony_ci gpt->rtbl = rtbl; 13162306a36Sopenharmony_ci gpt->rtbl_cnt = rtbl_cnt; 13262306a36Sopenharmony_ci gpt->lock = lock; 13362306a36Sopenharmony_ci gpt->hw.init = &init; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci init.name = name; 13662306a36Sopenharmony_ci init.ops = &clk_gpt_ops; 13762306a36Sopenharmony_ci init.flags = flags; 13862306a36Sopenharmony_ci init.parent_names = &parent_name; 13962306a36Sopenharmony_ci init.num_parents = 1; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci clk = clk_register(NULL, &gpt->hw); 14262306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(clk)) 14362306a36Sopenharmony_ci return clk; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci pr_err("clk register failed\n"); 14662306a36Sopenharmony_ci kfree(gpt); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return NULL; 14962306a36Sopenharmony_ci} 150