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-atmel-st.c (6314B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * linux/arch/arm/mach-at91/at91rm9200_time.c
      4 *
      5 *  Copyright (C) 2003 SAN People
      6 *  Copyright (C) 2003 ATMEL
      7 */
      8
      9#include <linux/kernel.h>
     10#include <linux/interrupt.h>
     11#include <linux/irq.h>
     12#include <linux/clk.h>
     13#include <linux/clockchips.h>
     14#include <linux/export.h>
     15#include <linux/mfd/syscon.h>
     16#include <linux/mfd/syscon/atmel-st.h>
     17#include <linux/of_irq.h>
     18#include <linux/regmap.h>
     19
     20static unsigned long last_crtr;
     21static u32 irqmask;
     22static struct clock_event_device clkevt;
     23static struct regmap *regmap_st;
     24static int timer_latch;
     25
     26/*
     27 * The ST_CRTR is updated asynchronously to the master clock ... but
     28 * the updates as seen by the CPU don't seem to be strictly monotonic.
     29 * Waiting until we read the same value twice avoids glitching.
     30 */
     31static inline unsigned long read_CRTR(void)
     32{
     33	unsigned int x1, x2;
     34
     35	regmap_read(regmap_st, AT91_ST_CRTR, &x1);
     36	do {
     37		regmap_read(regmap_st, AT91_ST_CRTR, &x2);
     38		if (x1 == x2)
     39			break;
     40		x1 = x2;
     41	} while (1);
     42	return x1;
     43}
     44
     45/*
     46 * IRQ handler for the timer.
     47 */
     48static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id)
     49{
     50	u32 sr;
     51
     52	regmap_read(regmap_st, AT91_ST_SR, &sr);
     53	sr &= irqmask;
     54
     55	/*
     56	 * irqs should be disabled here, but as the irq is shared they are only
     57	 * guaranteed to be off if the timer irq is registered first.
     58	 */
     59	WARN_ON_ONCE(!irqs_disabled());
     60
     61	/* simulate "oneshot" timer with alarm */
     62	if (sr & AT91_ST_ALMS) {
     63		clkevt.event_handler(&clkevt);
     64		return IRQ_HANDLED;
     65	}
     66
     67	/* periodic mode should handle delayed ticks */
     68	if (sr & AT91_ST_PITS) {
     69		u32	crtr = read_CRTR();
     70
     71		while (((crtr - last_crtr) & AT91_ST_CRTV) >= timer_latch) {
     72			last_crtr += timer_latch;
     73			clkevt.event_handler(&clkevt);
     74		}
     75		return IRQ_HANDLED;
     76	}
     77
     78	/* this irq is shared ... */
     79	return IRQ_NONE;
     80}
     81
     82static u64 read_clk32k(struct clocksource *cs)
     83{
     84	return read_CRTR();
     85}
     86
     87static struct clocksource clk32k = {
     88	.name		= "32k_counter",
     89	.rating		= 150,
     90	.read		= read_clk32k,
     91	.mask		= CLOCKSOURCE_MASK(20),
     92	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
     93};
     94
     95static void clkdev32k_disable_and_flush_irq(void)
     96{
     97	unsigned int val;
     98
     99	/* Disable and flush pending timer interrupts */
    100	regmap_write(regmap_st, AT91_ST_IDR, AT91_ST_PITS | AT91_ST_ALMS);
    101	regmap_read(regmap_st, AT91_ST_SR, &val);
    102	last_crtr = read_CRTR();
    103}
    104
    105static int clkevt32k_shutdown(struct clock_event_device *evt)
    106{
    107	clkdev32k_disable_and_flush_irq();
    108	irqmask = 0;
    109	regmap_write(regmap_st, AT91_ST_IER, irqmask);
    110	return 0;
    111}
    112
    113static int clkevt32k_set_oneshot(struct clock_event_device *dev)
    114{
    115	clkdev32k_disable_and_flush_irq();
    116
    117	/*
    118	 * ALM for oneshot irqs, set by next_event()
    119	 * before 32 seconds have passed.
    120	 */
    121	irqmask = AT91_ST_ALMS;
    122	regmap_write(regmap_st, AT91_ST_RTAR, last_crtr);
    123	regmap_write(regmap_st, AT91_ST_IER, irqmask);
    124	return 0;
    125}
    126
    127static int clkevt32k_set_periodic(struct clock_event_device *dev)
    128{
    129	clkdev32k_disable_and_flush_irq();
    130
    131	/* PIT for periodic irqs; fixed rate of 1/HZ */
    132	irqmask = AT91_ST_PITS;
    133	regmap_write(regmap_st, AT91_ST_PIMR, timer_latch);
    134	regmap_write(regmap_st, AT91_ST_IER, irqmask);
    135	return 0;
    136}
    137
    138static int
    139clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev)
    140{
    141	u32		alm;
    142	unsigned int	val;
    143
    144	BUG_ON(delta < 2);
    145
    146	/* The alarm IRQ uses absolute time (now+delta), not the relative
    147	 * time (delta) in our calling convention.  Like all clockevents
    148	 * using such "match" hardware, we have a race to defend against.
    149	 *
    150	 * Our defense here is to have set up the clockevent device so the
    151	 * delta is at least two.  That way we never end up writing RTAR
    152	 * with the value then held in CRTR ... which would mean the match
    153	 * wouldn't trigger until 32 seconds later, after CRTR wraps.
    154	 */
    155	alm = read_CRTR();
    156
    157	/* Cancel any pending alarm; flush any pending IRQ */
    158	regmap_write(regmap_st, AT91_ST_RTAR, alm);
    159	regmap_read(regmap_st, AT91_ST_SR, &val);
    160
    161	/* Schedule alarm by writing RTAR. */
    162	alm += delta;
    163	regmap_write(regmap_st, AT91_ST_RTAR, alm);
    164
    165	return 0;
    166}
    167
    168static struct clock_event_device clkevt = {
    169	.name			= "at91_tick",
    170	.features		= CLOCK_EVT_FEAT_PERIODIC |
    171				  CLOCK_EVT_FEAT_ONESHOT,
    172	.rating			= 150,
    173	.set_next_event		= clkevt32k_next_event,
    174	.set_state_shutdown	= clkevt32k_shutdown,
    175	.set_state_periodic	= clkevt32k_set_periodic,
    176	.set_state_oneshot	= clkevt32k_set_oneshot,
    177	.tick_resume		= clkevt32k_shutdown,
    178};
    179
    180/*
    181 * ST (system timer) module supports both clockevents and clocksource.
    182 */
    183static int __init atmel_st_timer_init(struct device_node *node)
    184{
    185	struct clk *sclk;
    186	unsigned int sclk_rate, val;
    187	int irq, ret;
    188
    189	regmap_st = syscon_node_to_regmap(node);
    190	if (IS_ERR(regmap_st)) {
    191		pr_err("Unable to get regmap\n");
    192		return PTR_ERR(regmap_st);
    193	}
    194
    195	/* Disable all timer interrupts, and clear any pending ones */
    196	regmap_write(regmap_st, AT91_ST_IDR,
    197		AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS);
    198	regmap_read(regmap_st, AT91_ST_SR, &val);
    199
    200	/* Get the interrupts property */
    201	irq  = irq_of_parse_and_map(node, 0);
    202	if (!irq) {
    203		pr_err("Unable to get IRQ from DT\n");
    204		return -EINVAL;
    205	}
    206
    207	/* Make IRQs happen for the system timer */
    208	ret = request_irq(irq, at91rm9200_timer_interrupt,
    209			  IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
    210			  "at91_tick", regmap_st);
    211	if (ret) {
    212		pr_err("Unable to setup IRQ\n");
    213		return ret;
    214	}
    215
    216	sclk = of_clk_get(node, 0);
    217	if (IS_ERR(sclk)) {
    218		pr_err("Unable to get slow clock\n");
    219		return PTR_ERR(sclk);
    220	}
    221
    222	ret = clk_prepare_enable(sclk);
    223	if (ret) {
    224		pr_err("Could not enable slow clock\n");
    225		return ret;
    226	}
    227
    228	sclk_rate = clk_get_rate(sclk);
    229	if (!sclk_rate) {
    230		pr_err("Invalid slow clock rate\n");
    231		return -EINVAL;
    232	}
    233	timer_latch = (sclk_rate + HZ / 2) / HZ;
    234
    235	/* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used
    236	 * directly for the clocksource and all clockevents, after adjusting
    237	 * its prescaler from the 1 Hz default.
    238	 */
    239	regmap_write(regmap_st, AT91_ST_RTMR, 1);
    240
    241	/* Setup timer clockevent, with minimum of two ticks (important!!) */
    242	clkevt.cpumask = cpumask_of(0);
    243	clockevents_config_and_register(&clkevt, sclk_rate,
    244					2, AT91_ST_ALMV);
    245
    246	/* register clocksource */
    247	return clocksource_register_hz(&clk32k, sclk_rate);
    248}
    249TIMER_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st",
    250		       atmel_st_timer_init);