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 * Auxiliary Synthesizer clock implementation 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "clk-aux-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/* 218c2ecf20Sopenharmony_ci * DOC: Auxiliary Synthesizer clock 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Aux synth gives rate for different values of eq, x and y 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Fout from synthesizer can be given from two equations: 268c2ecf20Sopenharmony_ci * Fout1 = (Fin * X/Y)/2 EQ1 278c2ecf20Sopenharmony_ci * Fout2 = Fin * X/Y EQ2 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const struct aux_clk_masks default_aux_masks = { 338c2ecf20Sopenharmony_ci .eq_sel_mask = AUX_EQ_SEL_MASK, 348c2ecf20Sopenharmony_ci .eq_sel_shift = AUX_EQ_SEL_SHIFT, 358c2ecf20Sopenharmony_ci .eq1_mask = AUX_EQ1_SEL, 368c2ecf20Sopenharmony_ci .eq2_mask = AUX_EQ2_SEL, 378c2ecf20Sopenharmony_ci .xscale_sel_mask = AUX_XSCALE_MASK, 388c2ecf20Sopenharmony_ci .xscale_sel_shift = AUX_XSCALE_SHIFT, 398c2ecf20Sopenharmony_ci .yscale_sel_mask = AUX_YSCALE_MASK, 408c2ecf20Sopenharmony_ci .yscale_sel_shift = AUX_YSCALE_SHIFT, 418c2ecf20Sopenharmony_ci .enable_bit = AUX_SYNT_ENB, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic unsigned long aux_calc_rate(struct clk_hw *hw, unsigned long prate, 458c2ecf20Sopenharmony_ci int index) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct clk_aux *aux = to_clk_aux(hw); 488c2ecf20Sopenharmony_ci struct aux_rate_tbl *rtbl = aux->rtbl; 498c2ecf20Sopenharmony_ci u8 eq = rtbl[index].eq ? 1 : 2; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return (((prate / 10000) * rtbl[index].xscale) / 528c2ecf20Sopenharmony_ci (rtbl[index].yscale * eq)) * 10000; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic long clk_aux_round_rate(struct clk_hw *hw, unsigned long drate, 568c2ecf20Sopenharmony_ci unsigned long *prate) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct clk_aux *aux = to_clk_aux(hw); 598c2ecf20Sopenharmony_ci int unused; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return clk_round_rate_index(hw, drate, *prate, aux_calc_rate, 628c2ecf20Sopenharmony_ci aux->rtbl_cnt, &unused); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic unsigned long clk_aux_recalc_rate(struct clk_hw *hw, 668c2ecf20Sopenharmony_ci unsigned long parent_rate) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct clk_aux *aux = to_clk_aux(hw); 698c2ecf20Sopenharmony_ci unsigned int num = 1, den = 1, val, eqn; 708c2ecf20Sopenharmony_ci unsigned long flags = 0; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (aux->lock) 738c2ecf20Sopenharmony_ci spin_lock_irqsave(aux->lock, flags); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci val = readl_relaxed(aux->reg); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (aux->lock) 788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(aux->lock, flags); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci eqn = (val >> aux->masks->eq_sel_shift) & aux->masks->eq_sel_mask; 818c2ecf20Sopenharmony_ci if (eqn == aux->masks->eq1_mask) 828c2ecf20Sopenharmony_ci den = 2; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* calculate numerator */ 858c2ecf20Sopenharmony_ci num = (val >> aux->masks->xscale_sel_shift) & 868c2ecf20Sopenharmony_ci aux->masks->xscale_sel_mask; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* calculate denominator */ 898c2ecf20Sopenharmony_ci den *= (val >> aux->masks->yscale_sel_shift) & 908c2ecf20Sopenharmony_ci aux->masks->yscale_sel_mask; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (!den) 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return (((parent_rate / 10000) * num) / den) * 10000; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Configures new clock rate of aux */ 998c2ecf20Sopenharmony_cistatic int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate, 1008c2ecf20Sopenharmony_ci unsigned long prate) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct clk_aux *aux = to_clk_aux(hw); 1038c2ecf20Sopenharmony_ci struct aux_rate_tbl *rtbl = aux->rtbl; 1048c2ecf20Sopenharmony_ci unsigned long val, flags = 0; 1058c2ecf20Sopenharmony_ci int i; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci clk_round_rate_index(hw, drate, prate, aux_calc_rate, aux->rtbl_cnt, 1088c2ecf20Sopenharmony_ci &i); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (aux->lock) 1118c2ecf20Sopenharmony_ci spin_lock_irqsave(aux->lock, flags); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci val = readl_relaxed(aux->reg) & 1148c2ecf20Sopenharmony_ci ~(aux->masks->eq_sel_mask << aux->masks->eq_sel_shift); 1158c2ecf20Sopenharmony_ci val |= (rtbl[i].eq & aux->masks->eq_sel_mask) << 1168c2ecf20Sopenharmony_ci aux->masks->eq_sel_shift; 1178c2ecf20Sopenharmony_ci val &= ~(aux->masks->xscale_sel_mask << aux->masks->xscale_sel_shift); 1188c2ecf20Sopenharmony_ci val |= (rtbl[i].xscale & aux->masks->xscale_sel_mask) << 1198c2ecf20Sopenharmony_ci aux->masks->xscale_sel_shift; 1208c2ecf20Sopenharmony_ci val &= ~(aux->masks->yscale_sel_mask << aux->masks->yscale_sel_shift); 1218c2ecf20Sopenharmony_ci val |= (rtbl[i].yscale & aux->masks->yscale_sel_mask) << 1228c2ecf20Sopenharmony_ci aux->masks->yscale_sel_shift; 1238c2ecf20Sopenharmony_ci writel_relaxed(val, aux->reg); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (aux->lock) 1268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(aux->lock, flags); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic const struct clk_ops clk_aux_ops = { 1328c2ecf20Sopenharmony_ci .recalc_rate = clk_aux_recalc_rate, 1338c2ecf20Sopenharmony_ci .round_rate = clk_aux_round_rate, 1348c2ecf20Sopenharmony_ci .set_rate = clk_aux_set_rate, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistruct clk *clk_register_aux(const char *aux_name, const char *gate_name, 1388c2ecf20Sopenharmony_ci const char *parent_name, unsigned long flags, void __iomem *reg, 1398c2ecf20Sopenharmony_ci const struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, 1408c2ecf20Sopenharmony_ci u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct clk_aux *aux; 1438c2ecf20Sopenharmony_ci struct clk_init_data init; 1448c2ecf20Sopenharmony_ci struct clk *clk; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (!aux_name || !parent_name || !reg || !rtbl || !rtbl_cnt) { 1478c2ecf20Sopenharmony_ci pr_err("Invalid arguments passed"); 1488c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci aux = kzalloc(sizeof(*aux), GFP_KERNEL); 1528c2ecf20Sopenharmony_ci if (!aux) 1538c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* struct clk_aux assignments */ 1568c2ecf20Sopenharmony_ci if (!masks) 1578c2ecf20Sopenharmony_ci aux->masks = &default_aux_masks; 1588c2ecf20Sopenharmony_ci else 1598c2ecf20Sopenharmony_ci aux->masks = masks; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci aux->reg = reg; 1628c2ecf20Sopenharmony_ci aux->rtbl = rtbl; 1638c2ecf20Sopenharmony_ci aux->rtbl_cnt = rtbl_cnt; 1648c2ecf20Sopenharmony_ci aux->lock = lock; 1658c2ecf20Sopenharmony_ci aux->hw.init = &init; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci init.name = aux_name; 1688c2ecf20Sopenharmony_ci init.ops = &clk_aux_ops; 1698c2ecf20Sopenharmony_ci init.flags = flags; 1708c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 1718c2ecf20Sopenharmony_ci init.num_parents = 1; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci clk = clk_register(NULL, &aux->hw); 1748c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(clk)) 1758c2ecf20Sopenharmony_ci goto free_aux; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (gate_name) { 1788c2ecf20Sopenharmony_ci struct clk *tgate_clk; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci tgate_clk = clk_register_gate(NULL, gate_name, aux_name, 1818c2ecf20Sopenharmony_ci CLK_SET_RATE_PARENT, reg, 1828c2ecf20Sopenharmony_ci aux->masks->enable_bit, 0, lock); 1838c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(tgate_clk)) 1848c2ecf20Sopenharmony_ci goto free_aux; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (gate_clk) 1878c2ecf20Sopenharmony_ci *gate_clk = tgate_clk; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return clk; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cifree_aux: 1938c2ecf20Sopenharmony_ci kfree(aux); 1948c2ecf20Sopenharmony_ci pr_err("clk register failed\n"); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return NULL; 1978c2ecf20Sopenharmony_ci} 198