brintos

brintos / linux-shallow public Read only

0
0
Text · 4.7 KiB · 637938e Raw
195 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2012 ST Microelectronics4 * Viresh Kumar <vireshk@kernel.org>5 *6 * Auxiliary Synthesizer clock implementation7 */8 9#define pr_fmt(fmt) "clk-aux-synth: " fmt10 11#include <linux/clk-provider.h>12#include <linux/slab.h>13#include <linux/io.h>14#include <linux/err.h>15#include "clk.h"16 17/*18 * DOC: Auxiliary Synthesizer clock19 *20 * Aux synth gives rate for different values of eq, x and y21 *22 * Fout from synthesizer can be given from two equations:23 * Fout1 = (Fin * X/Y)/2		EQ124 * Fout2 = Fin * X/Y			EQ225 */26 27#define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw)28 29static const  struct aux_clk_masks default_aux_masks = {30	.eq_sel_mask = AUX_EQ_SEL_MASK,31	.eq_sel_shift = AUX_EQ_SEL_SHIFT,32	.eq1_mask = AUX_EQ1_SEL,33	.eq2_mask = AUX_EQ2_SEL,34	.xscale_sel_mask = AUX_XSCALE_MASK,35	.xscale_sel_shift = AUX_XSCALE_SHIFT,36	.yscale_sel_mask = AUX_YSCALE_MASK,37	.yscale_sel_shift = AUX_YSCALE_SHIFT,38	.enable_bit = AUX_SYNT_ENB,39};40 41static unsigned long aux_calc_rate(struct clk_hw *hw, unsigned long prate,42		int index)43{44	struct clk_aux *aux = to_clk_aux(hw);45	struct aux_rate_tbl *rtbl = aux->rtbl;46	u8 eq = rtbl[index].eq ? 1 : 2;47 48	return (((prate / 10000) * rtbl[index].xscale) /49			(rtbl[index].yscale * eq)) * 10000;50}51 52static long clk_aux_round_rate(struct clk_hw *hw, unsigned long drate,53		unsigned long *prate)54{55	struct clk_aux *aux = to_clk_aux(hw);56	int unused;57 58	return clk_round_rate_index(hw, drate, *prate, aux_calc_rate,59			aux->rtbl_cnt, &unused);60}61 62static unsigned long clk_aux_recalc_rate(struct clk_hw *hw,63		unsigned long parent_rate)64{65	struct clk_aux *aux = to_clk_aux(hw);66	unsigned int num = 1, den = 1, val, eqn;67	unsigned long flags = 0;68 69	if (aux->lock)70		spin_lock_irqsave(aux->lock, flags);71 72	val = readl_relaxed(aux->reg);73 74	if (aux->lock)75		spin_unlock_irqrestore(aux->lock, flags);76 77	eqn = (val >> aux->masks->eq_sel_shift) & aux->masks->eq_sel_mask;78	if (eqn == aux->masks->eq1_mask)79		den = 2;80 81	/* calculate numerator */82	num = (val >> aux->masks->xscale_sel_shift) &83		aux->masks->xscale_sel_mask;84 85	/* calculate denominator */86	den *= (val >> aux->masks->yscale_sel_shift) &87		aux->masks->yscale_sel_mask;88 89	if (!den)90		return 0;91 92	return (((parent_rate / 10000) * num) / den) * 10000;93}94 95/* Configures new clock rate of aux */96static int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate,97				unsigned long prate)98{99	struct clk_aux *aux = to_clk_aux(hw);100	struct aux_rate_tbl *rtbl = aux->rtbl;101	unsigned long val, flags = 0;102	int i;103 104	clk_round_rate_index(hw, drate, prate, aux_calc_rate, aux->rtbl_cnt,105			&i);106 107	if (aux->lock)108		spin_lock_irqsave(aux->lock, flags);109 110	val = readl_relaxed(aux->reg) &111		~(aux->masks->eq_sel_mask << aux->masks->eq_sel_shift);112	val |= (rtbl[i].eq & aux->masks->eq_sel_mask) <<113		aux->masks->eq_sel_shift;114	val &= ~(aux->masks->xscale_sel_mask << aux->masks->xscale_sel_shift);115	val |= (rtbl[i].xscale & aux->masks->xscale_sel_mask) <<116		aux->masks->xscale_sel_shift;117	val &= ~(aux->masks->yscale_sel_mask << aux->masks->yscale_sel_shift);118	val |= (rtbl[i].yscale & aux->masks->yscale_sel_mask) <<119		aux->masks->yscale_sel_shift;120	writel_relaxed(val, aux->reg);121 122	if (aux->lock)123		spin_unlock_irqrestore(aux->lock, flags);124 125	return 0;126}127 128static const struct clk_ops clk_aux_ops = {129	.recalc_rate = clk_aux_recalc_rate,130	.round_rate = clk_aux_round_rate,131	.set_rate = clk_aux_set_rate,132};133 134struct clk *clk_register_aux(const char *aux_name, const char *gate_name,135		const char *parent_name, unsigned long flags, void __iomem *reg,136	        const struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl,137		u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk)138{139	struct clk_aux *aux;140	struct clk_init_data init;141	struct clk *clk;142 143	if (!aux_name || !parent_name || !reg || !rtbl || !rtbl_cnt) {144		pr_err("Invalid arguments passed");145		return ERR_PTR(-EINVAL);146	}147 148	aux = kzalloc(sizeof(*aux), GFP_KERNEL);149	if (!aux)150		return ERR_PTR(-ENOMEM);151 152	/* struct clk_aux assignments */153	if (!masks)154		aux->masks = &default_aux_masks;155	else156		aux->masks = masks;157 158	aux->reg = reg;159	aux->rtbl = rtbl;160	aux->rtbl_cnt = rtbl_cnt;161	aux->lock = lock;162	aux->hw.init = &init;163 164	init.name = aux_name;165	init.ops = &clk_aux_ops;166	init.flags = flags;167	init.parent_names = &parent_name;168	init.num_parents = 1;169 170	clk = clk_register(NULL, &aux->hw);171	if (IS_ERR_OR_NULL(clk))172		goto free_aux;173 174	if (gate_name) {175		struct clk *tgate_clk;176 177		tgate_clk = clk_register_gate(NULL, gate_name, aux_name,178				CLK_SET_RATE_PARENT, reg,179				aux->masks->enable_bit, 0, lock);180		if (IS_ERR_OR_NULL(tgate_clk))181			goto free_aux;182 183		if (gate_clk)184			*gate_clk = tgate_clk;185	}186 187	return clk;188 189free_aux:190	kfree(aux);191	pr_err("clk register failed\n");192 193	return NULL;194}195