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 * Auxiliary Synthesizer clock implementation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) "clk-aux-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/* 1862306a36Sopenharmony_ci * DOC: Auxiliary Synthesizer clock 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Aux synth gives rate for different values of eq, x and y 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Fout from synthesizer can be given from two equations: 2362306a36Sopenharmony_ci * Fout1 = (Fin * X/Y)/2 EQ1 2462306a36Sopenharmony_ci * Fout2 = Fin * X/Y EQ2 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic const struct aux_clk_masks default_aux_masks = { 3062306a36Sopenharmony_ci .eq_sel_mask = AUX_EQ_SEL_MASK, 3162306a36Sopenharmony_ci .eq_sel_shift = AUX_EQ_SEL_SHIFT, 3262306a36Sopenharmony_ci .eq1_mask = AUX_EQ1_SEL, 3362306a36Sopenharmony_ci .eq2_mask = AUX_EQ2_SEL, 3462306a36Sopenharmony_ci .xscale_sel_mask = AUX_XSCALE_MASK, 3562306a36Sopenharmony_ci .xscale_sel_shift = AUX_XSCALE_SHIFT, 3662306a36Sopenharmony_ci .yscale_sel_mask = AUX_YSCALE_MASK, 3762306a36Sopenharmony_ci .yscale_sel_shift = AUX_YSCALE_SHIFT, 3862306a36Sopenharmony_ci .enable_bit = AUX_SYNT_ENB, 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic unsigned long aux_calc_rate(struct clk_hw *hw, unsigned long prate, 4262306a36Sopenharmony_ci int index) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct clk_aux *aux = to_clk_aux(hw); 4562306a36Sopenharmony_ci struct aux_rate_tbl *rtbl = aux->rtbl; 4662306a36Sopenharmony_ci u8 eq = rtbl[index].eq ? 1 : 2; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return (((prate / 10000) * rtbl[index].xscale) / 4962306a36Sopenharmony_ci (rtbl[index].yscale * eq)) * 10000; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic long clk_aux_round_rate(struct clk_hw *hw, unsigned long drate, 5362306a36Sopenharmony_ci unsigned long *prate) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct clk_aux *aux = to_clk_aux(hw); 5662306a36Sopenharmony_ci int unused; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return clk_round_rate_index(hw, drate, *prate, aux_calc_rate, 5962306a36Sopenharmony_ci aux->rtbl_cnt, &unused); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic unsigned long clk_aux_recalc_rate(struct clk_hw *hw, 6362306a36Sopenharmony_ci unsigned long parent_rate) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct clk_aux *aux = to_clk_aux(hw); 6662306a36Sopenharmony_ci unsigned int num = 1, den = 1, val, eqn; 6762306a36Sopenharmony_ci unsigned long flags = 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (aux->lock) 7062306a36Sopenharmony_ci spin_lock_irqsave(aux->lock, flags); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci val = readl_relaxed(aux->reg); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (aux->lock) 7562306a36Sopenharmony_ci spin_unlock_irqrestore(aux->lock, flags); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci eqn = (val >> aux->masks->eq_sel_shift) & aux->masks->eq_sel_mask; 7862306a36Sopenharmony_ci if (eqn == aux->masks->eq1_mask) 7962306a36Sopenharmony_ci den = 2; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* calculate numerator */ 8262306a36Sopenharmony_ci num = (val >> aux->masks->xscale_sel_shift) & 8362306a36Sopenharmony_ci aux->masks->xscale_sel_mask; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* calculate denominator */ 8662306a36Sopenharmony_ci den *= (val >> aux->masks->yscale_sel_shift) & 8762306a36Sopenharmony_ci aux->masks->yscale_sel_mask; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!den) 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return (((parent_rate / 10000) * num) / den) * 10000; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* Configures new clock rate of aux */ 9662306a36Sopenharmony_cistatic int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate, 9762306a36Sopenharmony_ci unsigned long prate) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct clk_aux *aux = to_clk_aux(hw); 10062306a36Sopenharmony_ci struct aux_rate_tbl *rtbl = aux->rtbl; 10162306a36Sopenharmony_ci unsigned long val, flags = 0; 10262306a36Sopenharmony_ci int i; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci clk_round_rate_index(hw, drate, prate, aux_calc_rate, aux->rtbl_cnt, 10562306a36Sopenharmony_ci &i); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (aux->lock) 10862306a36Sopenharmony_ci spin_lock_irqsave(aux->lock, flags); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci val = readl_relaxed(aux->reg) & 11162306a36Sopenharmony_ci ~(aux->masks->eq_sel_mask << aux->masks->eq_sel_shift); 11262306a36Sopenharmony_ci val |= (rtbl[i].eq & aux->masks->eq_sel_mask) << 11362306a36Sopenharmony_ci aux->masks->eq_sel_shift; 11462306a36Sopenharmony_ci val &= ~(aux->masks->xscale_sel_mask << aux->masks->xscale_sel_shift); 11562306a36Sopenharmony_ci val |= (rtbl[i].xscale & aux->masks->xscale_sel_mask) << 11662306a36Sopenharmony_ci aux->masks->xscale_sel_shift; 11762306a36Sopenharmony_ci val &= ~(aux->masks->yscale_sel_mask << aux->masks->yscale_sel_shift); 11862306a36Sopenharmony_ci val |= (rtbl[i].yscale & aux->masks->yscale_sel_mask) << 11962306a36Sopenharmony_ci aux->masks->yscale_sel_shift; 12062306a36Sopenharmony_ci writel_relaxed(val, aux->reg); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (aux->lock) 12362306a36Sopenharmony_ci spin_unlock_irqrestore(aux->lock, flags); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic const struct clk_ops clk_aux_ops = { 12962306a36Sopenharmony_ci .recalc_rate = clk_aux_recalc_rate, 13062306a36Sopenharmony_ci .round_rate = clk_aux_round_rate, 13162306a36Sopenharmony_ci .set_rate = clk_aux_set_rate, 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistruct clk *clk_register_aux(const char *aux_name, const char *gate_name, 13562306a36Sopenharmony_ci const char *parent_name, unsigned long flags, void __iomem *reg, 13662306a36Sopenharmony_ci const struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, 13762306a36Sopenharmony_ci u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct clk_aux *aux; 14062306a36Sopenharmony_ci struct clk_init_data init; 14162306a36Sopenharmony_ci struct clk *clk; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!aux_name || !parent_name || !reg || !rtbl || !rtbl_cnt) { 14462306a36Sopenharmony_ci pr_err("Invalid arguments passed"); 14562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci aux = kzalloc(sizeof(*aux), GFP_KERNEL); 14962306a36Sopenharmony_ci if (!aux) 15062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* struct clk_aux assignments */ 15362306a36Sopenharmony_ci if (!masks) 15462306a36Sopenharmony_ci aux->masks = &default_aux_masks; 15562306a36Sopenharmony_ci else 15662306a36Sopenharmony_ci aux->masks = masks; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci aux->reg = reg; 15962306a36Sopenharmony_ci aux->rtbl = rtbl; 16062306a36Sopenharmony_ci aux->rtbl_cnt = rtbl_cnt; 16162306a36Sopenharmony_ci aux->lock = lock; 16262306a36Sopenharmony_ci aux->hw.init = &init; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci init.name = aux_name; 16562306a36Sopenharmony_ci init.ops = &clk_aux_ops; 16662306a36Sopenharmony_ci init.flags = flags; 16762306a36Sopenharmony_ci init.parent_names = &parent_name; 16862306a36Sopenharmony_ci init.num_parents = 1; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci clk = clk_register(NULL, &aux->hw); 17162306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clk)) 17262306a36Sopenharmony_ci goto free_aux; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (gate_name) { 17562306a36Sopenharmony_ci struct clk *tgate_clk; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci tgate_clk = clk_register_gate(NULL, gate_name, aux_name, 17862306a36Sopenharmony_ci CLK_SET_RATE_PARENT, reg, 17962306a36Sopenharmony_ci aux->masks->enable_bit, 0, lock); 18062306a36Sopenharmony_ci if (IS_ERR_OR_NULL(tgate_clk)) 18162306a36Sopenharmony_ci goto free_aux; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (gate_clk) 18462306a36Sopenharmony_ci *gate_clk = tgate_clk; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return clk; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cifree_aux: 19062306a36Sopenharmony_ci kfree(aux); 19162306a36Sopenharmony_ci pr_err("clk register failed\n"); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return NULL; 19462306a36Sopenharmony_ci} 195