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

ccu_nm.c (5876B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2016 Maxime Ripard
      4 * Maxime Ripard <maxime.ripard@free-electrons.com>
      5 */
      6
      7#include <linux/clk-provider.h>
      8#include <linux/io.h>
      9
     10#include "ccu_frac.h"
     11#include "ccu_gate.h"
     12#include "ccu_nm.h"
     13
     14struct _ccu_nm {
     15	unsigned long	n, min_n, max_n;
     16	unsigned long	m, min_m, max_m;
     17};
     18
     19static unsigned long ccu_nm_calc_rate(unsigned long parent,
     20				      unsigned long n, unsigned long m)
     21{
     22	u64 rate = parent;
     23
     24	rate *= n;
     25	do_div(rate, m);
     26
     27	return rate;
     28}
     29
     30static void ccu_nm_find_best(unsigned long parent, unsigned long rate,
     31			     struct _ccu_nm *nm)
     32{
     33	unsigned long best_rate = 0;
     34	unsigned long best_n = 0, best_m = 0;
     35	unsigned long _n, _m;
     36
     37	for (_n = nm->min_n; _n <= nm->max_n; _n++) {
     38		for (_m = nm->min_m; _m <= nm->max_m; _m++) {
     39			unsigned long tmp_rate = ccu_nm_calc_rate(parent,
     40								  _n, _m);
     41
     42			if (tmp_rate > rate)
     43				continue;
     44
     45			if ((rate - tmp_rate) < (rate - best_rate)) {
     46				best_rate = tmp_rate;
     47				best_n = _n;
     48				best_m = _m;
     49			}
     50		}
     51	}
     52
     53	nm->n = best_n;
     54	nm->m = best_m;
     55}
     56
     57static void ccu_nm_disable(struct clk_hw *hw)
     58{
     59	struct ccu_nm *nm = hw_to_ccu_nm(hw);
     60
     61	return ccu_gate_helper_disable(&nm->common, nm->enable);
     62}
     63
     64static int ccu_nm_enable(struct clk_hw *hw)
     65{
     66	struct ccu_nm *nm = hw_to_ccu_nm(hw);
     67
     68	return ccu_gate_helper_enable(&nm->common, nm->enable);
     69}
     70
     71static int ccu_nm_is_enabled(struct clk_hw *hw)
     72{
     73	struct ccu_nm *nm = hw_to_ccu_nm(hw);
     74
     75	return ccu_gate_helper_is_enabled(&nm->common, nm->enable);
     76}
     77
     78static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw,
     79					unsigned long parent_rate)
     80{
     81	struct ccu_nm *nm = hw_to_ccu_nm(hw);
     82	unsigned long rate;
     83	unsigned long n, m;
     84	u32 reg;
     85
     86	if (ccu_frac_helper_is_enabled(&nm->common, &nm->frac)) {
     87		rate = ccu_frac_helper_read_rate(&nm->common, &nm->frac);
     88
     89		if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
     90			rate /= nm->fixed_post_div;
     91
     92		return rate;
     93	}
     94
     95	reg = readl(nm->common.base + nm->common.reg);
     96
     97	n = reg >> nm->n.shift;
     98	n &= (1 << nm->n.width) - 1;
     99	n += nm->n.offset;
    100	if (!n)
    101		n++;
    102
    103	m = reg >> nm->m.shift;
    104	m &= (1 << nm->m.width) - 1;
    105	m += nm->m.offset;
    106	if (!m)
    107		m++;
    108
    109	if (ccu_sdm_helper_is_enabled(&nm->common, &nm->sdm))
    110		rate = ccu_sdm_helper_read_rate(&nm->common, &nm->sdm, m, n);
    111	else
    112		rate = ccu_nm_calc_rate(parent_rate, n, m);
    113
    114	if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
    115		rate /= nm->fixed_post_div;
    116
    117	return rate;
    118}
    119
    120static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
    121			      unsigned long *parent_rate)
    122{
    123	struct ccu_nm *nm = hw_to_ccu_nm(hw);
    124	struct _ccu_nm _nm;
    125
    126	if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
    127		rate *= nm->fixed_post_div;
    128
    129	if (rate < nm->min_rate) {
    130		rate = nm->min_rate;
    131		if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
    132			rate /= nm->fixed_post_div;
    133		return rate;
    134	}
    135
    136	if (nm->max_rate && rate > nm->max_rate) {
    137		rate = nm->max_rate;
    138		if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
    139			rate /= nm->fixed_post_div;
    140		return rate;
    141	}
    142
    143	if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) {
    144		if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
    145			rate /= nm->fixed_post_div;
    146		return rate;
    147	}
    148
    149	if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) {
    150		if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
    151			rate /= nm->fixed_post_div;
    152		return rate;
    153	}
    154
    155	_nm.min_n = nm->n.min ?: 1;
    156	_nm.max_n = nm->n.max ?: 1 << nm->n.width;
    157	_nm.min_m = 1;
    158	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
    159
    160	ccu_nm_find_best(*parent_rate, rate, &_nm);
    161	rate = ccu_nm_calc_rate(*parent_rate, _nm.n, _nm.m);
    162
    163	if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
    164		rate /= nm->fixed_post_div;
    165
    166	return rate;
    167}
    168
    169static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
    170			   unsigned long parent_rate)
    171{
    172	struct ccu_nm *nm = hw_to_ccu_nm(hw);
    173	struct _ccu_nm _nm;
    174	unsigned long flags;
    175	u32 reg;
    176
    177	/* Adjust target rate according to post-dividers */
    178	if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
    179		rate = rate * nm->fixed_post_div;
    180
    181	if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) {
    182		spin_lock_irqsave(nm->common.lock, flags);
    183
    184		/* most SoCs require M to be 0 if fractional mode is used */
    185		reg = readl(nm->common.base + nm->common.reg);
    186		reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift);
    187		writel(reg, nm->common.base + nm->common.reg);
    188
    189		spin_unlock_irqrestore(nm->common.lock, flags);
    190
    191		ccu_frac_helper_enable(&nm->common, &nm->frac);
    192
    193		return ccu_frac_helper_set_rate(&nm->common, &nm->frac,
    194						rate, nm->lock);
    195	} else {
    196		ccu_frac_helper_disable(&nm->common, &nm->frac);
    197	}
    198
    199	_nm.min_n = nm->n.min ?: 1;
    200	_nm.max_n = nm->n.max ?: 1 << nm->n.width;
    201	_nm.min_m = 1;
    202	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
    203
    204	if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) {
    205		ccu_sdm_helper_enable(&nm->common, &nm->sdm, rate);
    206
    207		/* Sigma delta modulation requires specific N and M factors */
    208		ccu_sdm_helper_get_factors(&nm->common, &nm->sdm, rate,
    209					   &_nm.m, &_nm.n);
    210	} else {
    211		ccu_sdm_helper_disable(&nm->common, &nm->sdm);
    212		ccu_nm_find_best(parent_rate, rate, &_nm);
    213	}
    214
    215	spin_lock_irqsave(nm->common.lock, flags);
    216
    217	reg = readl(nm->common.base + nm->common.reg);
    218	reg &= ~GENMASK(nm->n.width + nm->n.shift - 1, nm->n.shift);
    219	reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift);
    220
    221	reg |= (_nm.n - nm->n.offset) << nm->n.shift;
    222	reg |= (_nm.m - nm->m.offset) << nm->m.shift;
    223	writel(reg, nm->common.base + nm->common.reg);
    224
    225	spin_unlock_irqrestore(nm->common.lock, flags);
    226
    227	ccu_helper_wait_for_lock(&nm->common, nm->lock);
    228
    229	return 0;
    230}
    231
    232const struct clk_ops ccu_nm_ops = {
    233	.disable	= ccu_nm_disable,
    234	.enable		= ccu_nm_enable,
    235	.is_enabled	= ccu_nm_is_enabled,
    236
    237	.recalc_rate	= ccu_nm_recalc_rate,
    238	.round_rate	= ccu_nm_round_rate,
    239	.set_rate	= ccu_nm_set_rate,
    240};
    241EXPORT_SYMBOL_NS_GPL(ccu_nm_ops, SUNXI_CCU);