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

berlin2-div.c (6921B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2014 Marvell Technology Group Ltd.
      4 *
      5 * Alexandre Belloni <alexandre.belloni@free-electrons.com>
      6 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
      7 */
      8#include <linux/bitops.h>
      9#include <linux/clk-provider.h>
     10#include <linux/io.h>
     11#include <linux/of.h>
     12#include <linux/of_address.h>
     13#include <linux/slab.h>
     14#include <linux/spinlock.h>
     15
     16#include "berlin2-div.h"
     17
     18/*
     19 * Clock dividers in Berlin2 SoCs comprise a complex cell to select
     20 * input pll and divider. The virtual structure as it is used in Marvell
     21 * BSP code can be seen as:
     22 *
     23 *                      +---+
     24 * pll0 --------------->| 0 |                   +---+
     25 *           +---+      |(B)|--+--------------->| 0 |      +---+
     26 * pll1.0 -->| 0 |  +-->| 1 |  |   +--------+   |(E)|----->| 0 |   +---+
     27 * pll1.1 -->| 1 |  |   +---+  +-->|(C) 1:M |-->| 1 |      |(F)|-->|(G)|->
     28 * ...    -->|(A)|--+          |   +--------+   +---+  +-->| 1 |   +---+
     29 * ...    -->|   |             +-->|(D) 1:3 |----------+   +---+
     30 * pll1.N -->| N |                 +---------
     31 *           +---+
     32 *
     33 * (A) input pll clock mux controlled by               <PllSelect[1:n]>
     34 * (B) input pll bypass mux controlled by              <PllSwitch>
     35 * (C) programmable clock divider controlled by        <Select[1:n]>
     36 * (D) constant div-by-3 clock divider
     37 * (E) programmable clock divider bypass controlled by <Switch>
     38 * (F) constant div-by-3 clock mux controlled by       <D3Switch>
     39 * (G) clock gate controlled by                        <Enable>
     40 *
     41 * For whatever reason, above control signals come in two flavors:
     42 * - single register dividers with all bits in one register
     43 * - shared register dividers with bits spread over multiple registers
     44 *   (including signals for the same cell spread over consecutive registers)
     45 *
     46 * Also, clock gate and pll mux is not available on every div cell, so
     47 * we have to deal with those, too. We reuse common clock composite driver
     48 * for it.
     49 */
     50
     51#define PLL_SELECT_MASK	0x7
     52#define DIV_SELECT_MASK	0x7
     53
     54struct berlin2_div {
     55	struct clk_hw hw;
     56	void __iomem *base;
     57	struct berlin2_div_map map;
     58	spinlock_t *lock;
     59};
     60
     61#define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw)
     62
     63static u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 };
     64
     65static int berlin2_div_is_enabled(struct clk_hw *hw)
     66{
     67	struct berlin2_div *div = to_berlin2_div(hw);
     68	struct berlin2_div_map *map = &div->map;
     69	u32 reg;
     70
     71	if (div->lock)
     72		spin_lock(div->lock);
     73
     74	reg = readl_relaxed(div->base + map->gate_offs);
     75	reg >>= map->gate_shift;
     76
     77	if (div->lock)
     78		spin_unlock(div->lock);
     79
     80	return (reg & 0x1);
     81}
     82
     83static int berlin2_div_enable(struct clk_hw *hw)
     84{
     85	struct berlin2_div *div = to_berlin2_div(hw);
     86	struct berlin2_div_map *map = &div->map;
     87	u32 reg;
     88
     89	if (div->lock)
     90		spin_lock(div->lock);
     91
     92	reg = readl_relaxed(div->base + map->gate_offs);
     93	reg |= BIT(map->gate_shift);
     94	writel_relaxed(reg, div->base + map->gate_offs);
     95
     96	if (div->lock)
     97		spin_unlock(div->lock);
     98
     99	return 0;
    100}
    101
    102static void berlin2_div_disable(struct clk_hw *hw)
    103{
    104	struct berlin2_div *div = to_berlin2_div(hw);
    105	struct berlin2_div_map *map = &div->map;
    106	u32 reg;
    107
    108	if (div->lock)
    109		spin_lock(div->lock);
    110
    111	reg = readl_relaxed(div->base + map->gate_offs);
    112	reg &= ~BIT(map->gate_shift);
    113	writel_relaxed(reg, div->base + map->gate_offs);
    114
    115	if (div->lock)
    116		spin_unlock(div->lock);
    117}
    118
    119static int berlin2_div_set_parent(struct clk_hw *hw, u8 index)
    120{
    121	struct berlin2_div *div = to_berlin2_div(hw);
    122	struct berlin2_div_map *map = &div->map;
    123	u32 reg;
    124
    125	if (div->lock)
    126		spin_lock(div->lock);
    127
    128	/* index == 0 is PLL_SWITCH */
    129	reg = readl_relaxed(div->base + map->pll_switch_offs);
    130	if (index == 0)
    131		reg &= ~BIT(map->pll_switch_shift);
    132	else
    133		reg |= BIT(map->pll_switch_shift);
    134	writel_relaxed(reg, div->base + map->pll_switch_offs);
    135
    136	/* index > 0 is PLL_SELECT */
    137	if (index > 0) {
    138		reg = readl_relaxed(div->base + map->pll_select_offs);
    139		reg &= ~(PLL_SELECT_MASK << map->pll_select_shift);
    140		reg |= (index - 1) << map->pll_select_shift;
    141		writel_relaxed(reg, div->base + map->pll_select_offs);
    142	}
    143
    144	if (div->lock)
    145		spin_unlock(div->lock);
    146
    147	return 0;
    148}
    149
    150static u8 berlin2_div_get_parent(struct clk_hw *hw)
    151{
    152	struct berlin2_div *div = to_berlin2_div(hw);
    153	struct berlin2_div_map *map = &div->map;
    154	u32 reg;
    155	u8 index = 0;
    156
    157	if (div->lock)
    158		spin_lock(div->lock);
    159
    160	/* PLL_SWITCH == 0 is index 0 */
    161	reg = readl_relaxed(div->base + map->pll_switch_offs);
    162	reg &= BIT(map->pll_switch_shift);
    163	if (reg) {
    164		reg = readl_relaxed(div->base + map->pll_select_offs);
    165		reg >>= map->pll_select_shift;
    166		reg &= PLL_SELECT_MASK;
    167		index = 1 + reg;
    168	}
    169
    170	if (div->lock)
    171		spin_unlock(div->lock);
    172
    173	return index;
    174}
    175
    176static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw,
    177					     unsigned long parent_rate)
    178{
    179	struct berlin2_div *div = to_berlin2_div(hw);
    180	struct berlin2_div_map *map = &div->map;
    181	u32 divsw, div3sw, divider = 1;
    182
    183	if (div->lock)
    184		spin_lock(div->lock);
    185
    186	divsw = readl_relaxed(div->base + map->div_switch_offs) &
    187		(1 << map->div_switch_shift);
    188	div3sw = readl_relaxed(div->base + map->div3_switch_offs) &
    189		(1 << map->div3_switch_shift);
    190
    191	/* constant divide-by-3 (dominant) */
    192	if (div3sw != 0) {
    193		divider = 3;
    194	/* divider can be bypassed with DIV_SWITCH == 0 */
    195	} else if (divsw == 0) {
    196		divider = 1;
    197	/* clock divider determined by DIV_SELECT */
    198	} else {
    199		u32 reg;
    200		reg = readl_relaxed(div->base + map->div_select_offs);
    201		reg >>= map->div_select_shift;
    202		reg &= DIV_SELECT_MASK;
    203		divider = clk_div[reg];
    204	}
    205
    206	if (div->lock)
    207		spin_unlock(div->lock);
    208
    209	return parent_rate / divider;
    210}
    211
    212static const struct clk_ops berlin2_div_rate_ops = {
    213	.recalc_rate	= berlin2_div_recalc_rate,
    214};
    215
    216static const struct clk_ops berlin2_div_gate_ops = {
    217	.is_enabled	= berlin2_div_is_enabled,
    218	.enable		= berlin2_div_enable,
    219	.disable	= berlin2_div_disable,
    220};
    221
    222static const struct clk_ops berlin2_div_mux_ops = {
    223	.set_parent	= berlin2_div_set_parent,
    224	.get_parent	= berlin2_div_get_parent,
    225};
    226
    227struct clk_hw * __init
    228berlin2_div_register(const struct berlin2_div_map *map,
    229		     void __iomem *base, const char *name, u8 div_flags,
    230		     const char **parent_names, int num_parents,
    231		     unsigned long flags, spinlock_t *lock)
    232{
    233	const struct clk_ops *mux_ops = &berlin2_div_mux_ops;
    234	const struct clk_ops *rate_ops = &berlin2_div_rate_ops;
    235	const struct clk_ops *gate_ops = &berlin2_div_gate_ops;
    236	struct berlin2_div *div;
    237
    238	div = kzalloc(sizeof(*div), GFP_KERNEL);
    239	if (!div)
    240		return ERR_PTR(-ENOMEM);
    241
    242	/* copy div_map to allow __initconst */
    243	memcpy(&div->map, map, sizeof(*map));
    244	div->base = base;
    245	div->lock = lock;
    246
    247	if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0)
    248		gate_ops = NULL;
    249	if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0)
    250		mux_ops = NULL;
    251
    252	return clk_hw_register_composite(NULL, name, parent_names, num_parents,
    253				      &div->hw, mux_ops, &div->hw, rate_ops,
    254				      &div->hw, gate_ops, flags);
    255}