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

dove-divider.c (5869B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Marvell Dove PMU Core PLL divider driver
      4 *
      5 * Cleaned up by substantially rewriting, and converted to DT by
      6 * Russell King.  Origin is not known.
      7 */
      8#include <linux/clk-provider.h>
      9#include <linux/delay.h>
     10#include <linux/io.h>
     11#include <linux/kernel.h>
     12#include <linux/of.h>
     13#include <linux/of_address.h>
     14
     15#include "dove-divider.h"
     16
     17struct dove_clk {
     18	const char *name;
     19	struct clk_hw hw;
     20	void __iomem *base;
     21	spinlock_t *lock;
     22	u8 div_bit_start;
     23	u8 div_bit_end;
     24	u8 div_bit_load;
     25	u8 div_bit_size;
     26	u32 *divider_table;
     27};
     28
     29enum {
     30	DIV_CTRL0 = 0,
     31	DIV_CTRL1 = 4,
     32	DIV_CTRL1_N_RESET_MASK = BIT(10),
     33};
     34
     35#define to_dove_clk(hw) container_of(hw, struct dove_clk, hw)
     36
     37static void dove_load_divider(void __iomem *base, u32 val, u32 mask, u32 load)
     38{
     39	u32 v;
     40
     41	v = readl_relaxed(base + DIV_CTRL1) | DIV_CTRL1_N_RESET_MASK;
     42	writel_relaxed(v, base + DIV_CTRL1);
     43
     44	v = (readl_relaxed(base + DIV_CTRL0) & ~(mask | load)) | val;
     45	writel_relaxed(v, base + DIV_CTRL0);
     46	writel_relaxed(v | load, base + DIV_CTRL0);
     47	ndelay(250);
     48	writel_relaxed(v, base + DIV_CTRL0);
     49}
     50
     51static unsigned int dove_get_divider(struct dove_clk *dc)
     52{
     53	unsigned int divider;
     54	u32 val;
     55
     56	val = readl_relaxed(dc->base + DIV_CTRL0);
     57	val >>= dc->div_bit_start;
     58
     59	divider = val & ~(~0 << dc->div_bit_size);
     60
     61	if (dc->divider_table)
     62		divider = dc->divider_table[divider];
     63
     64	return divider;
     65}
     66
     67static int dove_calc_divider(const struct dove_clk *dc, unsigned long rate,
     68			     unsigned long parent_rate, bool set)
     69{
     70	unsigned int divider, max;
     71
     72	divider = DIV_ROUND_CLOSEST(parent_rate, rate);
     73
     74	if (dc->divider_table) {
     75		unsigned int i;
     76
     77		for (i = 0; dc->divider_table[i]; i++)
     78			if (divider == dc->divider_table[i]) {
     79				divider = i;
     80				break;
     81			}
     82
     83		if (!dc->divider_table[i])
     84			return -EINVAL;
     85	} else {
     86		max = 1 << dc->div_bit_size;
     87
     88		if (set && (divider == 0 || divider >= max))
     89			return -EINVAL;
     90		if (divider >= max)
     91			divider = max - 1;
     92		else if (divider == 0)
     93			divider = 1;
     94	}
     95
     96	return divider;
     97}
     98
     99static unsigned long dove_recalc_rate(struct clk_hw *hw, unsigned long parent)
    100{
    101	struct dove_clk *dc = to_dove_clk(hw);
    102	unsigned int divider = dove_get_divider(dc);
    103	unsigned long rate = DIV_ROUND_CLOSEST(parent, divider);
    104
    105	pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",
    106		 __func__, dc->name, divider, parent, rate);
    107
    108	return rate;
    109}
    110
    111static long dove_round_rate(struct clk_hw *hw, unsigned long rate,
    112			    unsigned long *parent)
    113{
    114	struct dove_clk *dc = to_dove_clk(hw);
    115	unsigned long parent_rate = *parent;
    116	int divider;
    117
    118	divider = dove_calc_divider(dc, rate, parent_rate, false);
    119	if (divider < 0)
    120		return divider;
    121
    122	rate = DIV_ROUND_CLOSEST(parent_rate, divider);
    123
    124	pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",
    125		 __func__, dc->name, divider, parent_rate, rate);
    126
    127	return rate;
    128}
    129
    130static int dove_set_clock(struct clk_hw *hw, unsigned long rate,
    131			  unsigned long parent_rate)
    132{
    133	struct dove_clk *dc = to_dove_clk(hw);
    134	u32 mask, load, div;
    135	int divider;
    136
    137	divider = dove_calc_divider(dc, rate, parent_rate, true);
    138	if (divider < 0)
    139		return divider;
    140
    141	pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",
    142		 __func__, dc->name, divider, parent_rate, rate);
    143
    144	div = (u32)divider << dc->div_bit_start;
    145	mask = ~(~0 << dc->div_bit_size) << dc->div_bit_start;
    146	load = BIT(dc->div_bit_load);
    147
    148	spin_lock(dc->lock);
    149	dove_load_divider(dc->base, div, mask, load);
    150	spin_unlock(dc->lock);
    151
    152	return 0;
    153}
    154
    155static const struct clk_ops dove_divider_ops = {
    156	.set_rate	= dove_set_clock,
    157	.round_rate	= dove_round_rate,
    158	.recalc_rate	= dove_recalc_rate,
    159};
    160
    161static struct clk *clk_register_dove_divider(struct device *dev,
    162	struct dove_clk *dc, const char **parent_names, size_t num_parents,
    163	void __iomem *base)
    164{
    165	char name[32];
    166	struct clk_init_data init = {
    167		.name = name,
    168		.ops = &dove_divider_ops,
    169		.parent_names = parent_names,
    170		.num_parents = num_parents,
    171	};
    172
    173	strlcpy(name, dc->name, sizeof(name));
    174
    175	dc->hw.init = &init;
    176	dc->base = base;
    177	dc->div_bit_size = dc->div_bit_end - dc->div_bit_start + 1;
    178
    179	return clk_register(dev, &dc->hw);
    180}
    181
    182static DEFINE_SPINLOCK(dove_divider_lock);
    183
    184static u32 axi_divider[] = {-1, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 0};
    185
    186static struct dove_clk dove_hw_clocks[4] = {
    187	{
    188		.name = "axi",
    189		.lock = &dove_divider_lock,
    190		.div_bit_start = 1,
    191		.div_bit_end = 6,
    192		.div_bit_load = 7,
    193		.divider_table = axi_divider,
    194	}, {
    195		.name = "gpu",
    196		.lock = &dove_divider_lock,
    197		.div_bit_start = 8,
    198		.div_bit_end = 13,
    199		.div_bit_load = 14,
    200	}, {
    201		.name = "vmeta",
    202		.lock = &dove_divider_lock,
    203		.div_bit_start = 15,
    204		.div_bit_end = 20,
    205		.div_bit_load = 21,
    206	}, {
    207		.name = "lcd",
    208		.lock = &dove_divider_lock,
    209		.div_bit_start = 22,
    210		.div_bit_end = 27,
    211		.div_bit_load = 28,
    212	},
    213};
    214
    215static const char *core_pll[] = {
    216	"core-pll",
    217};
    218
    219static int dove_divider_init(struct device *dev, void __iomem *base,
    220	struct clk **clks)
    221{
    222	struct clk *clk;
    223	int i;
    224
    225	/*
    226	 * Create the core PLL clock.  We treat this as a fixed rate
    227	 * clock as we don't know any better, and documentation is sparse.
    228	 */
    229	clk = clk_register_fixed_rate(dev, core_pll[0], NULL, 0, 2000000000UL);
    230	if (IS_ERR(clk))
    231		return PTR_ERR(clk);
    232
    233	for (i = 0; i < ARRAY_SIZE(dove_hw_clocks); i++)
    234		clks[i] = clk_register_dove_divider(dev, &dove_hw_clocks[i],
    235						    core_pll,
    236						    ARRAY_SIZE(core_pll), base);
    237
    238	return 0;
    239}
    240
    241static struct clk *dove_divider_clocks[4];
    242
    243static struct clk_onecell_data dove_divider_data = {
    244	.clks = dove_divider_clocks,
    245	.clk_num = ARRAY_SIZE(dove_divider_clocks),
    246};
    247
    248void __init dove_divider_clk_init(struct device_node *np)
    249{
    250	void __iomem *base;
    251
    252	base = of_iomap(np, 0);
    253	if (WARN_ON(!base))
    254		return;
    255
    256	if (WARN_ON(dove_divider_init(NULL, base, dove_divider_clocks))) {
    257		iounmap(base);
    258		return;
    259	}
    260
    261	of_clk_add_provider(np, of_clk_src_onecell_get, &dove_divider_data);
    262}