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

smp_twd.c (7811B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  linux/arch/arm/kernel/smp_twd.c
      4 *
      5 *  Copyright (C) 2002 ARM Ltd.
      6 *  All Rights Reserved
      7 */
      8#include <linux/init.h>
      9#include <linux/kernel.h>
     10#include <linux/clk.h>
     11#include <linux/cpu.h>
     12#include <linux/delay.h>
     13#include <linux/device.h>
     14#include <linux/err.h>
     15#include <linux/smp.h>
     16#include <linux/jiffies.h>
     17#include <linux/clockchips.h>
     18#include <linux/interrupt.h>
     19#include <linux/io.h>
     20#include <linux/of_irq.h>
     21#include <linux/of_address.h>
     22
     23#include <asm/smp_twd.h>
     24
     25/* set up by the platform code */
     26static void __iomem *twd_base;
     27
     28static struct clk *twd_clk;
     29static unsigned long twd_timer_rate;
     30static DEFINE_PER_CPU(bool, percpu_setup_called);
     31
     32static struct clock_event_device __percpu *twd_evt;
     33static unsigned int twd_features =
     34		CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
     35static int twd_ppi;
     36
     37static int twd_shutdown(struct clock_event_device *clk)
     38{
     39	writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
     40	return 0;
     41}
     42
     43static int twd_set_oneshot(struct clock_event_device *clk)
     44{
     45	/* period set, and timer enabled in 'next_event' hook */
     46	writel_relaxed(TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT,
     47		       twd_base + TWD_TIMER_CONTROL);
     48	return 0;
     49}
     50
     51static int twd_set_periodic(struct clock_event_device *clk)
     52{
     53	unsigned long ctrl = TWD_TIMER_CONTROL_ENABLE |
     54			     TWD_TIMER_CONTROL_IT_ENABLE |
     55			     TWD_TIMER_CONTROL_PERIODIC;
     56
     57	writel_relaxed(DIV_ROUND_CLOSEST(twd_timer_rate, HZ),
     58		       twd_base + TWD_TIMER_LOAD);
     59	writel_relaxed(ctrl, twd_base + TWD_TIMER_CONTROL);
     60	return 0;
     61}
     62
     63static int twd_set_next_event(unsigned long evt,
     64			struct clock_event_device *unused)
     65{
     66	unsigned long ctrl = readl_relaxed(twd_base + TWD_TIMER_CONTROL);
     67
     68	ctrl |= TWD_TIMER_CONTROL_ENABLE;
     69
     70	writel_relaxed(evt, twd_base + TWD_TIMER_COUNTER);
     71	writel_relaxed(ctrl, twd_base + TWD_TIMER_CONTROL);
     72
     73	return 0;
     74}
     75
     76/*
     77 * local_timer_ack: checks for a local timer interrupt.
     78 *
     79 * If a local timer interrupt has occurred, acknowledge and return 1.
     80 * Otherwise, return 0.
     81 */
     82static int twd_timer_ack(void)
     83{
     84	if (readl_relaxed(twd_base + TWD_TIMER_INTSTAT)) {
     85		writel_relaxed(1, twd_base + TWD_TIMER_INTSTAT);
     86		return 1;
     87	}
     88
     89	return 0;
     90}
     91
     92static void twd_timer_stop(void)
     93{
     94	struct clock_event_device *clk = raw_cpu_ptr(twd_evt);
     95
     96	twd_shutdown(clk);
     97	disable_percpu_irq(clk->irq);
     98}
     99
    100/*
    101 * Updates clockevent frequency when the cpu frequency changes.
    102 * Called on the cpu that is changing frequency with interrupts disabled.
    103 */
    104static void twd_update_frequency(void *new_rate)
    105{
    106	twd_timer_rate = *((unsigned long *) new_rate);
    107
    108	clockevents_update_freq(raw_cpu_ptr(twd_evt), twd_timer_rate);
    109}
    110
    111static int twd_rate_change(struct notifier_block *nb,
    112	unsigned long flags, void *data)
    113{
    114	struct clk_notifier_data *cnd = data;
    115
    116	/*
    117	 * The twd clock events must be reprogrammed to account for the new
    118	 * frequency.  The timer is local to a cpu, so cross-call to the
    119	 * changing cpu.
    120	 */
    121	if (flags == POST_RATE_CHANGE)
    122		on_each_cpu(twd_update_frequency,
    123				  (void *)&cnd->new_rate, 1);
    124
    125	return NOTIFY_OK;
    126}
    127
    128static struct notifier_block twd_clk_nb = {
    129	.notifier_call = twd_rate_change,
    130};
    131
    132static int twd_clk_init(void)
    133{
    134	if (twd_evt && raw_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
    135		return clk_notifier_register(twd_clk, &twd_clk_nb);
    136
    137	return 0;
    138}
    139core_initcall(twd_clk_init);
    140
    141static void twd_calibrate_rate(void)
    142{
    143	unsigned long count;
    144	u64 waitjiffies;
    145
    146	/*
    147	 * If this is the first time round, we need to work out how fast
    148	 * the timer ticks
    149	 */
    150	if (twd_timer_rate == 0) {
    151		pr_info("Calibrating local timer... ");
    152
    153		/* Wait for a tick to start */
    154		waitjiffies = get_jiffies_64() + 1;
    155
    156		while (get_jiffies_64() < waitjiffies)
    157			udelay(10);
    158
    159		/* OK, now the tick has started, let's get the timer going */
    160		waitjiffies += 5;
    161
    162				 /* enable, no interrupt or reload */
    163		writel_relaxed(0x1, twd_base + TWD_TIMER_CONTROL);
    164
    165				 /* maximum value */
    166		writel_relaxed(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
    167
    168		while (get_jiffies_64() < waitjiffies)
    169			udelay(10);
    170
    171		count = readl_relaxed(twd_base + TWD_TIMER_COUNTER);
    172
    173		twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
    174
    175		pr_cont("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
    176			(twd_timer_rate / 10000) % 100);
    177	}
    178}
    179
    180static irqreturn_t twd_handler(int irq, void *dev_id)
    181{
    182	struct clock_event_device *evt = dev_id;
    183
    184	if (twd_timer_ack()) {
    185		evt->event_handler(evt);
    186		return IRQ_HANDLED;
    187	}
    188
    189	return IRQ_NONE;
    190}
    191
    192static void twd_get_clock(struct device_node *np)
    193{
    194	int err;
    195
    196	if (np)
    197		twd_clk = of_clk_get(np, 0);
    198	else
    199		twd_clk = clk_get_sys("smp_twd", NULL);
    200
    201	if (IS_ERR(twd_clk)) {
    202		pr_err("smp_twd: clock not found %d\n", (int) PTR_ERR(twd_clk));
    203		return;
    204	}
    205
    206	err = clk_prepare_enable(twd_clk);
    207	if (err) {
    208		pr_err("smp_twd: clock failed to prepare+enable: %d\n", err);
    209		clk_put(twd_clk);
    210		return;
    211	}
    212
    213	twd_timer_rate = clk_get_rate(twd_clk);
    214}
    215
    216/*
    217 * Setup the local clock events for a CPU.
    218 */
    219static void twd_timer_setup(void)
    220{
    221	struct clock_event_device *clk = raw_cpu_ptr(twd_evt);
    222	int cpu = smp_processor_id();
    223
    224	/*
    225	 * If the basic setup for this CPU has been done before don't
    226	 * bother with the below.
    227	 */
    228	if (per_cpu(percpu_setup_called, cpu)) {
    229		writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
    230		clockevents_register_device(clk);
    231		enable_percpu_irq(clk->irq, 0);
    232		return;
    233	}
    234	per_cpu(percpu_setup_called, cpu) = true;
    235
    236	twd_calibrate_rate();
    237
    238	/*
    239	 * The following is done once per CPU the first time .setup() is
    240	 * called.
    241	 */
    242	writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
    243
    244	clk->name = "local_timer";
    245	clk->features = twd_features;
    246	clk->rating = 350;
    247	clk->set_state_shutdown = twd_shutdown;
    248	clk->set_state_periodic = twd_set_periodic;
    249	clk->set_state_oneshot = twd_set_oneshot;
    250	clk->tick_resume = twd_shutdown;
    251	clk->set_next_event = twd_set_next_event;
    252	clk->irq = twd_ppi;
    253	clk->cpumask = cpumask_of(cpu);
    254
    255	clockevents_config_and_register(clk, twd_timer_rate,
    256					0xf, 0xffffffff);
    257	enable_percpu_irq(clk->irq, 0);
    258}
    259
    260static int twd_timer_starting_cpu(unsigned int cpu)
    261{
    262	twd_timer_setup();
    263	return 0;
    264}
    265
    266static int twd_timer_dying_cpu(unsigned int cpu)
    267{
    268	twd_timer_stop();
    269	return 0;
    270}
    271
    272static int __init twd_local_timer_common_register(struct device_node *np)
    273{
    274	int err;
    275
    276	twd_evt = alloc_percpu(struct clock_event_device);
    277	if (!twd_evt) {
    278		err = -ENOMEM;
    279		goto out_free;
    280	}
    281
    282	err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt);
    283	if (err) {
    284		pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err);
    285		goto out_free;
    286	}
    287
    288	cpuhp_setup_state_nocalls(CPUHP_AP_ARM_TWD_STARTING,
    289				  "arm/timer/twd:starting",
    290				  twd_timer_starting_cpu, twd_timer_dying_cpu);
    291
    292	twd_get_clock(np);
    293	if (!of_property_read_bool(np, "always-on"))
    294		twd_features |= CLOCK_EVT_FEAT_C3STOP;
    295
    296	/*
    297	 * Immediately configure the timer on the boot CPU, unless we need
    298	 * jiffies to be incrementing to calibrate the rate in which case
    299	 * setup the timer in late_time_init.
    300	 */
    301	if (twd_timer_rate)
    302		twd_timer_setup();
    303	else
    304		late_time_init = twd_timer_setup;
    305
    306	return 0;
    307
    308out_free:
    309	iounmap(twd_base);
    310	twd_base = NULL;
    311	free_percpu(twd_evt);
    312
    313	return err;
    314}
    315
    316static int __init twd_local_timer_of_register(struct device_node *np)
    317{
    318	int err;
    319
    320	twd_ppi = irq_of_parse_and_map(np, 0);
    321	if (!twd_ppi) {
    322		err = -EINVAL;
    323		goto out;
    324	}
    325
    326	twd_base = of_iomap(np, 0);
    327	if (!twd_base) {
    328		err = -ENOMEM;
    329		goto out;
    330	}
    331
    332	err = twd_local_timer_common_register(np);
    333
    334out:
    335	WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
    336	return err;
    337}
    338TIMER_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
    339TIMER_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
    340TIMER_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);