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-sun4i-tcon-ch1.c (6366B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright 2015 Maxime Ripard
      4 *
      5 * Maxime Ripard <maxime.ripard@free-electrons.com>
      6 */
      7
      8#include <linux/clk-provider.h>
      9#include <linux/io.h>
     10#include <linux/of.h>
     11#include <linux/of_address.h>
     12#include <linux/slab.h>
     13#include <linux/spinlock.h>
     14
     15#define TCON_CH1_SCLK2_PARENTS		4
     16
     17#define TCON_CH1_SCLK2_GATE_BIT		BIT(31)
     18#define TCON_CH1_SCLK2_MUX_MASK		3
     19#define TCON_CH1_SCLK2_MUX_SHIFT	24
     20#define TCON_CH1_SCLK2_DIV_MASK		0xf
     21#define TCON_CH1_SCLK2_DIV_SHIFT	0
     22
     23#define TCON_CH1_SCLK1_GATE_BIT		BIT(15)
     24#define TCON_CH1_SCLK1_HALF_BIT		BIT(11)
     25
     26struct tcon_ch1_clk {
     27	struct clk_hw	hw;
     28	spinlock_t	lock;
     29	void __iomem	*reg;
     30};
     31
     32#define hw_to_tclk(hw)	container_of(hw, struct tcon_ch1_clk, hw)
     33
     34static void tcon_ch1_disable(struct clk_hw *hw)
     35{
     36	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
     37	unsigned long flags;
     38	u32 reg;
     39
     40	spin_lock_irqsave(&tclk->lock, flags);
     41	reg = readl(tclk->reg);
     42	reg &= ~(TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT);
     43	writel(reg, tclk->reg);
     44	spin_unlock_irqrestore(&tclk->lock, flags);
     45}
     46
     47static int tcon_ch1_enable(struct clk_hw *hw)
     48{
     49	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
     50	unsigned long flags;
     51	u32 reg;
     52
     53	spin_lock_irqsave(&tclk->lock, flags);
     54	reg = readl(tclk->reg);
     55	reg |= TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT;
     56	writel(reg, tclk->reg);
     57	spin_unlock_irqrestore(&tclk->lock, flags);
     58
     59	return 0;
     60}
     61
     62static int tcon_ch1_is_enabled(struct clk_hw *hw)
     63{
     64	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
     65	u32 reg;
     66
     67	reg = readl(tclk->reg);
     68	return reg & (TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT);
     69}
     70
     71static u8 tcon_ch1_get_parent(struct clk_hw *hw)
     72{
     73	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
     74	u32 reg;
     75
     76	reg = readl(tclk->reg) >> TCON_CH1_SCLK2_MUX_SHIFT;
     77	reg &= reg >> TCON_CH1_SCLK2_MUX_MASK;
     78
     79	return reg;
     80}
     81
     82static int tcon_ch1_set_parent(struct clk_hw *hw, u8 index)
     83{
     84	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
     85	unsigned long flags;
     86	u32 reg;
     87
     88	spin_lock_irqsave(&tclk->lock, flags);
     89	reg = readl(tclk->reg);
     90	reg &= ~(TCON_CH1_SCLK2_MUX_MASK << TCON_CH1_SCLK2_MUX_SHIFT);
     91	reg |= index << TCON_CH1_SCLK2_MUX_SHIFT;
     92	writel(reg, tclk->reg);
     93	spin_unlock_irqrestore(&tclk->lock, flags);
     94
     95	return 0;
     96};
     97
     98static unsigned long tcon_ch1_calc_divider(unsigned long rate,
     99					   unsigned long parent_rate,
    100					   u8 *div,
    101					   bool *half)
    102{
    103	unsigned long best_rate = 0;
    104	u8 best_m = 0, m;
    105	bool is_double;
    106
    107	for (m = 1; m < 16; m++) {
    108		u8 d;
    109
    110		for (d = 1; d < 3; d++) {
    111			unsigned long tmp_rate;
    112
    113			tmp_rate = parent_rate / m / d;
    114
    115			if (tmp_rate > rate)
    116				continue;
    117
    118			if (!best_rate ||
    119			    (rate - tmp_rate) < (rate - best_rate)) {
    120				best_rate = tmp_rate;
    121				best_m = m;
    122				is_double = d;
    123			}
    124		}
    125	}
    126
    127	if (div && half) {
    128		*div = best_m;
    129		*half = is_double;
    130	}
    131
    132	return best_rate;
    133}
    134
    135static int tcon_ch1_determine_rate(struct clk_hw *hw,
    136				   struct clk_rate_request *req)
    137{
    138	long best_rate = -EINVAL;
    139	int i;
    140
    141	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
    142		unsigned long parent_rate;
    143		unsigned long tmp_rate;
    144		struct clk_hw *parent;
    145
    146		parent = clk_hw_get_parent_by_index(hw, i);
    147		if (!parent)
    148			continue;
    149
    150		parent_rate = clk_hw_get_rate(parent);
    151
    152		tmp_rate = tcon_ch1_calc_divider(req->rate, parent_rate,
    153						 NULL, NULL);
    154
    155		if (best_rate < 0 ||
    156		    (req->rate - tmp_rate) < (req->rate - best_rate)) {
    157			best_rate = tmp_rate;
    158			req->best_parent_rate = parent_rate;
    159			req->best_parent_hw = parent;
    160		}
    161	}
    162
    163	if (best_rate < 0)
    164		return best_rate;
    165
    166	req->rate = best_rate;
    167	return 0;
    168}
    169
    170static unsigned long tcon_ch1_recalc_rate(struct clk_hw *hw,
    171					  unsigned long parent_rate)
    172{
    173	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
    174	u32 reg;
    175
    176	reg = readl(tclk->reg);
    177
    178	parent_rate /= (reg & TCON_CH1_SCLK2_DIV_MASK) + 1;
    179
    180	if (reg & TCON_CH1_SCLK1_HALF_BIT)
    181		parent_rate /= 2;
    182
    183	return parent_rate;
    184}
    185
    186static int tcon_ch1_set_rate(struct clk_hw *hw, unsigned long rate,
    187			     unsigned long parent_rate)
    188{
    189	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
    190	unsigned long flags;
    191	bool half;
    192	u8 div_m;
    193	u32 reg;
    194
    195	tcon_ch1_calc_divider(rate, parent_rate, &div_m, &half);
    196
    197	spin_lock_irqsave(&tclk->lock, flags);
    198	reg = readl(tclk->reg);
    199	reg &= ~(TCON_CH1_SCLK2_DIV_MASK | TCON_CH1_SCLK1_HALF_BIT);
    200	reg |= (div_m - 1) & TCON_CH1_SCLK2_DIV_MASK;
    201
    202	if (half)
    203		reg |= TCON_CH1_SCLK1_HALF_BIT;
    204
    205	writel(reg, tclk->reg);
    206	spin_unlock_irqrestore(&tclk->lock, flags);
    207
    208	return 0;
    209}
    210
    211static const struct clk_ops tcon_ch1_ops = {
    212	.disable	= tcon_ch1_disable,
    213	.enable		= tcon_ch1_enable,
    214	.is_enabled	= tcon_ch1_is_enabled,
    215
    216	.get_parent	= tcon_ch1_get_parent,
    217	.set_parent	= tcon_ch1_set_parent,
    218
    219	.determine_rate	= tcon_ch1_determine_rate,
    220	.recalc_rate	= tcon_ch1_recalc_rate,
    221	.set_rate	= tcon_ch1_set_rate,
    222};
    223
    224static void __init tcon_ch1_setup(struct device_node *node)
    225{
    226	const char *parents[TCON_CH1_SCLK2_PARENTS];
    227	const char *clk_name = node->name;
    228	struct clk_init_data init;
    229	struct tcon_ch1_clk *tclk;
    230	struct resource res;
    231	struct clk *clk;
    232	void __iomem *reg;
    233	int ret;
    234
    235	of_property_read_string(node, "clock-output-names", &clk_name);
    236
    237	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
    238	if (IS_ERR(reg)) {
    239		pr_err("%s: Could not map the clock registers\n", clk_name);
    240		return;
    241	}
    242
    243	ret = of_clk_parent_fill(node, parents, TCON_CH1_SCLK2_PARENTS);
    244	if (ret != TCON_CH1_SCLK2_PARENTS) {
    245		pr_err("%s Could not retrieve the parents\n", clk_name);
    246		goto err_unmap;
    247	}
    248
    249	tclk = kzalloc(sizeof(*tclk), GFP_KERNEL);
    250	if (!tclk)
    251		goto err_unmap;
    252
    253	init.name = clk_name;
    254	init.ops = &tcon_ch1_ops;
    255	init.parent_names = parents;
    256	init.num_parents = TCON_CH1_SCLK2_PARENTS;
    257	init.flags = CLK_SET_RATE_PARENT;
    258
    259	tclk->reg = reg;
    260	tclk->hw.init = &init;
    261	spin_lock_init(&tclk->lock);
    262
    263	clk = clk_register(NULL, &tclk->hw);
    264	if (IS_ERR(clk)) {
    265		pr_err("%s: Couldn't register the clock\n", clk_name);
    266		goto err_free_data;
    267	}
    268
    269	ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
    270	if (ret) {
    271		pr_err("%s: Couldn't register our clock provider\n", clk_name);
    272		goto err_unregister_clk;
    273	}
    274
    275	return;
    276
    277err_unregister_clk:
    278	clk_unregister(clk);
    279err_free_data:
    280	kfree(tclk);
    281err_unmap:
    282	iounmap(reg);
    283	of_address_to_resource(node, 0, &res);
    284	release_mem_region(res.start, resource_size(&res));
    285}
    286
    287CLK_OF_DECLARE(tcon_ch1, "allwinner,sun4i-a10-tcon-ch1-clk",
    288	       tcon_ch1_setup);