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-half-divider.c (5742B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
      4 */
      5
      6#include <linux/clk-provider.h>
      7#include <linux/io.h>
      8#include <linux/slab.h>
      9#include "clk.h"
     10
     11#define div_mask(width)	((1 << (width)) - 1)
     12
     13static bool _is_best_half_div(unsigned long rate, unsigned long now,
     14			      unsigned long best, unsigned long flags)
     15{
     16	if (flags & CLK_DIVIDER_ROUND_CLOSEST)
     17		return abs(rate - now) < abs(rate - best);
     18
     19	return now <= rate && now > best;
     20}
     21
     22static unsigned long clk_half_divider_recalc_rate(struct clk_hw *hw,
     23						  unsigned long parent_rate)
     24{
     25	struct clk_divider *divider = to_clk_divider(hw);
     26	unsigned int val;
     27
     28	val = readl(divider->reg) >> divider->shift;
     29	val &= div_mask(divider->width);
     30	val = val * 2 + 3;
     31
     32	return DIV_ROUND_UP_ULL(((u64)parent_rate * 2), val);
     33}
     34
     35static int clk_half_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
     36				    unsigned long *best_parent_rate, u8 width,
     37				    unsigned long flags)
     38{
     39	unsigned int i, bestdiv = 0;
     40	unsigned long parent_rate, best = 0, now, maxdiv;
     41	unsigned long parent_rate_saved = *best_parent_rate;
     42
     43	if (!rate)
     44		rate = 1;
     45
     46	maxdiv = div_mask(width);
     47
     48	if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
     49		parent_rate = *best_parent_rate;
     50		bestdiv = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), rate);
     51		if (bestdiv < 3)
     52			bestdiv = 0;
     53		else
     54			bestdiv = (bestdiv - 3) / 2;
     55		bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
     56		return bestdiv;
     57	}
     58
     59	/*
     60	 * The maximum divider we can use without overflowing
     61	 * unsigned long in rate * i below
     62	 */
     63	maxdiv = min(ULONG_MAX / rate, maxdiv);
     64
     65	for (i = 0; i <= maxdiv; i++) {
     66		if (((u64)rate * (i * 2 + 3)) == ((u64)parent_rate_saved * 2)) {
     67			/*
     68			 * It's the most ideal case if the requested rate can be
     69			 * divided from parent clock without needing to change
     70			 * parent rate, so return the divider immediately.
     71			 */
     72			*best_parent_rate = parent_rate_saved;
     73			return i;
     74		}
     75		parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
     76						((u64)rate * (i * 2 + 3)) / 2);
     77		now = DIV_ROUND_UP_ULL(((u64)parent_rate * 2),
     78				       (i * 2 + 3));
     79
     80		if (_is_best_half_div(rate, now, best, flags)) {
     81			bestdiv = i;
     82			best = now;
     83			*best_parent_rate = parent_rate;
     84		}
     85	}
     86
     87	if (!bestdiv) {
     88		bestdiv = div_mask(width);
     89		*best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), 1);
     90	}
     91
     92	return bestdiv;
     93}
     94
     95static long clk_half_divider_round_rate(struct clk_hw *hw, unsigned long rate,
     96					unsigned long *prate)
     97{
     98	struct clk_divider *divider = to_clk_divider(hw);
     99	int div;
    100
    101	div = clk_half_divider_bestdiv(hw, rate, prate,
    102				       divider->width,
    103				       divider->flags);
    104
    105	return DIV_ROUND_UP_ULL(((u64)*prate * 2), div * 2 + 3);
    106}
    107
    108static int clk_half_divider_set_rate(struct clk_hw *hw, unsigned long rate,
    109				     unsigned long parent_rate)
    110{
    111	struct clk_divider *divider = to_clk_divider(hw);
    112	unsigned int value;
    113	unsigned long flags = 0;
    114	u32 val;
    115
    116	value = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), rate);
    117	value = (value - 3) / 2;
    118	value =  min_t(unsigned int, value, div_mask(divider->width));
    119
    120	if (divider->lock)
    121		spin_lock_irqsave(divider->lock, flags);
    122	else
    123		__acquire(divider->lock);
    124
    125	if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
    126		val = div_mask(divider->width) << (divider->shift + 16);
    127	} else {
    128		val = readl(divider->reg);
    129		val &= ~(div_mask(divider->width) << divider->shift);
    130	}
    131	val |= value << divider->shift;
    132	writel(val, divider->reg);
    133
    134	if (divider->lock)
    135		spin_unlock_irqrestore(divider->lock, flags);
    136	else
    137		__release(divider->lock);
    138
    139	return 0;
    140}
    141
    142static const struct clk_ops clk_half_divider_ops = {
    143	.recalc_rate = clk_half_divider_recalc_rate,
    144	.round_rate = clk_half_divider_round_rate,
    145	.set_rate = clk_half_divider_set_rate,
    146};
    147
    148/*
    149 * Register a clock branch.
    150 * Most clock branches have a form like
    151 *
    152 * src1 --|--\
    153 *        |M |--[GATE]-[DIV]-
    154 * src2 --|--/
    155 *
    156 * sometimes without one of those components.
    157 */
    158struct clk *rockchip_clk_register_halfdiv(const char *name,
    159					  const char *const *parent_names,
    160					  u8 num_parents, void __iomem *base,
    161					  int muxdiv_offset, u8 mux_shift,
    162					  u8 mux_width, u8 mux_flags,
    163					  u8 div_shift, u8 div_width,
    164					  u8 div_flags, int gate_offset,
    165					  u8 gate_shift, u8 gate_flags,
    166					  unsigned long flags,
    167					  spinlock_t *lock)
    168{
    169	struct clk_hw *hw = ERR_PTR(-ENOMEM);
    170	struct clk_mux *mux = NULL;
    171	struct clk_gate *gate = NULL;
    172	struct clk_divider *div = NULL;
    173	const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
    174			     *gate_ops = NULL;
    175
    176	if (num_parents > 1) {
    177		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
    178		if (!mux)
    179			return ERR_PTR(-ENOMEM);
    180
    181		mux->reg = base + muxdiv_offset;
    182		mux->shift = mux_shift;
    183		mux->mask = BIT(mux_width) - 1;
    184		mux->flags = mux_flags;
    185		mux->lock = lock;
    186		mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
    187							: &clk_mux_ops;
    188	}
    189
    190	if (gate_offset >= 0) {
    191		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
    192		if (!gate)
    193			goto err_gate;
    194
    195		gate->flags = gate_flags;
    196		gate->reg = base + gate_offset;
    197		gate->bit_idx = gate_shift;
    198		gate->lock = lock;
    199		gate_ops = &clk_gate_ops;
    200	}
    201
    202	if (div_width > 0) {
    203		div = kzalloc(sizeof(*div), GFP_KERNEL);
    204		if (!div)
    205			goto err_div;
    206
    207		div->flags = div_flags;
    208		div->reg = base + muxdiv_offset;
    209		div->shift = div_shift;
    210		div->width = div_width;
    211		div->lock = lock;
    212		div_ops = &clk_half_divider_ops;
    213	}
    214
    215	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
    216				       mux ? &mux->hw : NULL, mux_ops,
    217				       div ? &div->hw : NULL, div_ops,
    218				       gate ? &gate->hw : NULL, gate_ops,
    219				       flags);
    220	if (IS_ERR(hw))
    221		goto err_div;
    222
    223	return hw->clk;
    224err_div:
    225	kfree(gate);
    226err_gate:
    227	kfree(mux);
    228	return ERR_CAST(hw);
    229}