cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-aux-synth.c (4914B)


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