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-sp810.c (3469B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *
      4 * Copyright (C) 2013 ARM Limited
      5 */
      6
      7#include <linux/amba/sp810.h>
      8#include <linux/slab.h>
      9#include <linux/clk.h>
     10#include <linux/clk-provider.h>
     11#include <linux/err.h>
     12#include <linux/io.h>
     13#include <linux/of.h>
     14#include <linux/of_address.h>
     15
     16#define to_clk_sp810_timerclken(_hw) \
     17		container_of(_hw, struct clk_sp810_timerclken, hw)
     18
     19struct clk_sp810;
     20
     21struct clk_sp810_timerclken {
     22	struct clk_hw hw;
     23	struct clk *clk;
     24	struct clk_sp810 *sp810;
     25	int channel;
     26};
     27
     28struct clk_sp810 {
     29	struct device_node *node;
     30	void __iomem *base;
     31	spinlock_t lock;
     32	struct clk_sp810_timerclken timerclken[4];
     33};
     34
     35static u8 clk_sp810_timerclken_get_parent(struct clk_hw *hw)
     36{
     37	struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
     38	u32 val = readl(timerclken->sp810->base + SCCTRL);
     39
     40	return !!(val & (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel)));
     41}
     42
     43static int clk_sp810_timerclken_set_parent(struct clk_hw *hw, u8 index)
     44{
     45	struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
     46	struct clk_sp810 *sp810 = timerclken->sp810;
     47	u32 val, shift = SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel);
     48	unsigned long flags = 0;
     49
     50	if (WARN_ON(index > 1))
     51		return -EINVAL;
     52
     53	spin_lock_irqsave(&sp810->lock, flags);
     54
     55	val = readl(sp810->base + SCCTRL);
     56	val &= ~(1 << shift);
     57	val |= index << shift;
     58	writel(val, sp810->base + SCCTRL);
     59
     60	spin_unlock_irqrestore(&sp810->lock, flags);
     61
     62	return 0;
     63}
     64
     65static const struct clk_ops clk_sp810_timerclken_ops = {
     66	.get_parent = clk_sp810_timerclken_get_parent,
     67	.set_parent = clk_sp810_timerclken_set_parent,
     68};
     69
     70static struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec,
     71		void *data)
     72{
     73	struct clk_sp810 *sp810 = data;
     74
     75	if (WARN_ON(clkspec->args_count != 1 ||
     76		    clkspec->args[0] >=	ARRAY_SIZE(sp810->timerclken)))
     77		return NULL;
     78
     79	return sp810->timerclken[clkspec->args[0]].clk;
     80}
     81
     82static void __init clk_sp810_of_setup(struct device_node *node)
     83{
     84	struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL);
     85	const char *parent_names[2];
     86	int num = ARRAY_SIZE(parent_names);
     87	char name[12];
     88	struct clk_init_data init;
     89	static int instance;
     90	int i;
     91	bool deprecated;
     92
     93	if (!sp810)
     94		return;
     95
     96	if (of_clk_parent_fill(node, parent_names, num) != num) {
     97		pr_warn("Failed to obtain parent clocks for SP810!\n");
     98		kfree(sp810);
     99		return;
    100	}
    101
    102	sp810->node = node;
    103	sp810->base = of_iomap(node, 0);
    104	spin_lock_init(&sp810->lock);
    105
    106	init.name = name;
    107	init.ops = &clk_sp810_timerclken_ops;
    108	init.flags = 0;
    109	init.parent_names = parent_names;
    110	init.num_parents = num;
    111
    112	deprecated = !of_find_property(node, "assigned-clock-parents", NULL);
    113
    114	for (i = 0; i < ARRAY_SIZE(sp810->timerclken); i++) {
    115		snprintf(name, sizeof(name), "sp810_%d_%d", instance, i);
    116
    117		sp810->timerclken[i].sp810 = sp810;
    118		sp810->timerclken[i].channel = i;
    119		sp810->timerclken[i].hw.init = &init;
    120
    121		/*
    122		 * If DT isn't setting the parent, force it to be
    123		 * the 1 MHz clock without going through the framework.
    124		 * We do this before clk_register() so that it can determine
    125		 * the parent and setup the tree properly.
    126		 */
    127		if (deprecated)
    128			init.ops->set_parent(&sp810->timerclken[i].hw, 1);
    129
    130		sp810->timerclken[i].clk = clk_register(NULL,
    131				&sp810->timerclken[i].hw);
    132		WARN_ON(IS_ERR(sp810->timerclken[i].clk));
    133	}
    134
    135	of_clk_add_provider(node, clk_sp810_timerclken_of_get, sp810);
    136	instance++;
    137}
    138CLK_OF_DECLARE(sp810, "arm,sp810", clk_sp810_of_setup);