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

timer-integrator-ap.c (5351B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Integrator/AP timer driver
      4 * Copyright (C) 2000-2003 Deep Blue Solutions Ltd
      5 * Copyright (c) 2014, Linaro Limited
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/clocksource.h>
     10#include <linux/of_irq.h>
     11#include <linux/of_address.h>
     12#include <linux/of_platform.h>
     13#include <linux/clockchips.h>
     14#include <linux/interrupt.h>
     15#include <linux/sched_clock.h>
     16
     17#include "timer-sp.h"
     18
     19static void __iomem * sched_clk_base;
     20
     21static u64 notrace integrator_read_sched_clock(void)
     22{
     23	return -readl(sched_clk_base + TIMER_VALUE);
     24}
     25
     26static int __init integrator_clocksource_init(unsigned long inrate,
     27					      void __iomem *base)
     28{
     29	u32 ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC;
     30	unsigned long rate = inrate;
     31	int ret;
     32
     33	if (rate >= 1500000) {
     34		rate /= 16;
     35		ctrl |= TIMER_CTRL_DIV16;
     36	}
     37
     38	writel(0xffff, base + TIMER_LOAD);
     39	writel(ctrl, base + TIMER_CTRL);
     40
     41	ret = clocksource_mmio_init(base + TIMER_VALUE, "timer2",
     42				    rate, 200, 16, clocksource_mmio_readl_down);
     43	if (ret)
     44		return ret;
     45
     46	sched_clk_base = base;
     47	sched_clock_register(integrator_read_sched_clock, 16, rate);
     48
     49	return 0;
     50}
     51
     52static unsigned long timer_reload;
     53static void __iomem * clkevt_base;
     54
     55/*
     56 * IRQ handler for the timer
     57 */
     58static irqreturn_t integrator_timer_interrupt(int irq, void *dev_id)
     59{
     60	struct clock_event_device *evt = dev_id;
     61
     62	/* clear the interrupt */
     63	writel(1, clkevt_base + TIMER_INTCLR);
     64
     65	evt->event_handler(evt);
     66
     67	return IRQ_HANDLED;
     68}
     69
     70static int clkevt_shutdown(struct clock_event_device *evt)
     71{
     72	u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
     73
     74	/* Disable timer */
     75	writel(ctrl, clkevt_base + TIMER_CTRL);
     76	return 0;
     77}
     78
     79static int clkevt_set_oneshot(struct clock_event_device *evt)
     80{
     81	u32 ctrl = readl(clkevt_base + TIMER_CTRL) &
     82		   ~(TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC);
     83
     84	/* Leave the timer disabled, .set_next_event will enable it */
     85	writel(ctrl, clkevt_base + TIMER_CTRL);
     86	return 0;
     87}
     88
     89static int clkevt_set_periodic(struct clock_event_device *evt)
     90{
     91	u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
     92
     93	/* Disable timer */
     94	writel(ctrl, clkevt_base + TIMER_CTRL);
     95
     96	/* Enable the timer and start the periodic tick */
     97	writel(timer_reload, clkevt_base + TIMER_LOAD);
     98	ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
     99	writel(ctrl, clkevt_base + TIMER_CTRL);
    100	return 0;
    101}
    102
    103static int clkevt_set_next_event(unsigned long next, struct clock_event_device *evt)
    104{
    105	unsigned long ctrl = readl(clkevt_base + TIMER_CTRL);
    106
    107	writel(ctrl & ~TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
    108	writel(next, clkevt_base + TIMER_LOAD);
    109	writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
    110
    111	return 0;
    112}
    113
    114static struct clock_event_device integrator_clockevent = {
    115	.name			= "timer1",
    116	.features		= CLOCK_EVT_FEAT_PERIODIC |
    117				  CLOCK_EVT_FEAT_ONESHOT,
    118	.set_state_shutdown	= clkevt_shutdown,
    119	.set_state_periodic	= clkevt_set_periodic,
    120	.set_state_oneshot	= clkevt_set_oneshot,
    121	.tick_resume		= clkevt_shutdown,
    122	.set_next_event		= clkevt_set_next_event,
    123	.rating			= 300,
    124};
    125
    126static int integrator_clockevent_init(unsigned long inrate,
    127				      void __iomem *base, int irq)
    128{
    129	unsigned long rate = inrate;
    130	unsigned int ctrl = 0;
    131	int ret;
    132
    133	clkevt_base = base;
    134	/* Calculate and program a divisor */
    135	if (rate > 0x100000 * HZ) {
    136		rate /= 256;
    137		ctrl |= TIMER_CTRL_DIV256;
    138	} else if (rate > 0x10000 * HZ) {
    139		rate /= 16;
    140		ctrl |= TIMER_CTRL_DIV16;
    141	}
    142	timer_reload = rate / HZ;
    143	writel(ctrl, clkevt_base + TIMER_CTRL);
    144
    145	ret = request_irq(irq, integrator_timer_interrupt,
    146			  IRQF_TIMER | IRQF_IRQPOLL, "timer",
    147			  &integrator_clockevent);
    148	if (ret)
    149		return ret;
    150
    151	clockevents_config_and_register(&integrator_clockevent,
    152					rate,
    153					1,
    154					0xffffU);
    155	return 0;
    156}
    157
    158static int __init integrator_ap_timer_init_of(struct device_node *node)
    159{
    160	const char *path;
    161	void __iomem *base;
    162	int err;
    163	int irq;
    164	struct clk *clk;
    165	unsigned long rate;
    166	struct device_node *alias_node;
    167
    168	base = of_io_request_and_map(node, 0, "integrator-timer");
    169	if (IS_ERR(base))
    170		return PTR_ERR(base);
    171
    172	clk = of_clk_get(node, 0);
    173	if (IS_ERR(clk)) {
    174		pr_err("No clock for %pOFn\n", node);
    175		return PTR_ERR(clk);
    176	}
    177	clk_prepare_enable(clk);
    178	rate = clk_get_rate(clk);
    179	writel(0, base + TIMER_CTRL);
    180
    181	err = of_property_read_string(of_aliases,
    182				"arm,timer-primary", &path);
    183	if (err) {
    184		pr_warn("Failed to read property\n");
    185		return err;
    186	}
    187
    188	alias_node = of_find_node_by_path(path);
    189
    190	/*
    191	 * The pointer is used as an identifier not as a pointer, we
    192	 * can drop the refcount on the of__node immediately after
    193	 * getting it.
    194	 */
    195	of_node_put(alias_node);
    196
    197	if (node == alias_node)
    198		/* The primary timer lacks IRQ, use as clocksource */
    199		return integrator_clocksource_init(rate, base);
    200
    201	err = of_property_read_string(of_aliases,
    202				"arm,timer-secondary", &path);
    203	if (err) {
    204		pr_warn("Failed to read property\n");
    205		return err;
    206	}
    207
    208	alias_node = of_find_node_by_path(path);
    209
    210	of_node_put(alias_node);
    211
    212	if (node == alias_node) {
    213		/* The secondary timer will drive the clock event */
    214		irq = irq_of_parse_and_map(node, 0);
    215		return integrator_clockevent_init(rate, base, irq);
    216	}
    217
    218	pr_info("Timer @%p unused\n", base);
    219	clk_disable_unprepare(clk);
    220
    221	return 0;
    222}
    223
    224TIMER_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer",
    225		       integrator_ap_timer_init_of);