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-sun4i.c (6232B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Allwinner A1X SoCs timer handling.
      4 *
      5 * Copyright (C) 2012 Maxime Ripard
      6 *
      7 * Maxime Ripard <maxime.ripard@free-electrons.com>
      8 *
      9 * Based on code from
     10 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
     11 * Benn Huang <benn@allwinnertech.com>
     12 */
     13
     14#include <linux/clk.h>
     15#include <linux/clockchips.h>
     16#include <linux/interrupt.h>
     17#include <linux/irq.h>
     18#include <linux/irqreturn.h>
     19#include <linux/sched_clock.h>
     20#include <linux/of.h>
     21#include <linux/of_address.h>
     22#include <linux/of_irq.h>
     23
     24#include "timer-of.h"
     25
     26#define TIMER_IRQ_EN_REG	0x00
     27#define TIMER_IRQ_EN(val)		BIT(val)
     28#define TIMER_IRQ_ST_REG	0x04
     29#define TIMER_CTL_REG(val)	(0x10 * val + 0x10)
     30#define TIMER_CTL_ENABLE		BIT(0)
     31#define TIMER_CTL_RELOAD		BIT(1)
     32#define TIMER_CTL_CLK_SRC(val)		(((val) & 0x3) << 2)
     33#define TIMER_CTL_CLK_SRC_OSC24M		(1)
     34#define TIMER_CTL_CLK_PRES(val)		(((val) & 0x7) << 4)
     35#define TIMER_CTL_ONESHOT		BIT(7)
     36#define TIMER_INTVAL_REG(val)	(0x10 * (val) + 0x14)
     37#define TIMER_CNTVAL_REG(val)	(0x10 * (val) + 0x18)
     38
     39#define TIMER_SYNC_TICKS	3
     40
     41/*
     42 * When we disable a timer, we need to wait at least for 2 cycles of
     43 * the timer source clock. We will use for that the clocksource timer
     44 * that is already setup and runs at the same frequency than the other
     45 * timers, and we never will be disabled.
     46 */
     47static void sun4i_clkevt_sync(void __iomem *base)
     48{
     49	u32 old = readl(base + TIMER_CNTVAL_REG(1));
     50
     51	while ((old - readl(base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS)
     52		cpu_relax();
     53}
     54
     55static void sun4i_clkevt_time_stop(void __iomem *base, u8 timer)
     56{
     57	u32 val = readl(base + TIMER_CTL_REG(timer));
     58	writel(val & ~TIMER_CTL_ENABLE, base + TIMER_CTL_REG(timer));
     59	sun4i_clkevt_sync(base);
     60}
     61
     62static void sun4i_clkevt_time_setup(void __iomem *base, u8 timer,
     63				    unsigned long delay)
     64{
     65	writel(delay, base + TIMER_INTVAL_REG(timer));
     66}
     67
     68static void sun4i_clkevt_time_start(void __iomem *base, u8 timer,
     69				    bool periodic)
     70{
     71	u32 val = readl(base + TIMER_CTL_REG(timer));
     72
     73	if (periodic)
     74		val &= ~TIMER_CTL_ONESHOT;
     75	else
     76		val |= TIMER_CTL_ONESHOT;
     77
     78	writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
     79	       base + TIMER_CTL_REG(timer));
     80}
     81
     82static int sun4i_clkevt_shutdown(struct clock_event_device *evt)
     83{
     84	struct timer_of *to = to_timer_of(evt);
     85
     86	sun4i_clkevt_time_stop(timer_of_base(to), 0);
     87
     88	return 0;
     89}
     90
     91static int sun4i_clkevt_set_oneshot(struct clock_event_device *evt)
     92{
     93	struct timer_of *to = to_timer_of(evt);
     94
     95	sun4i_clkevt_time_stop(timer_of_base(to), 0);
     96	sun4i_clkevt_time_start(timer_of_base(to), 0, false);
     97
     98	return 0;
     99}
    100
    101static int sun4i_clkevt_set_periodic(struct clock_event_device *evt)
    102{
    103	struct timer_of *to = to_timer_of(evt);
    104
    105	sun4i_clkevt_time_stop(timer_of_base(to), 0);
    106	sun4i_clkevt_time_setup(timer_of_base(to), 0, timer_of_period(to));
    107	sun4i_clkevt_time_start(timer_of_base(to), 0, true);
    108
    109	return 0;
    110}
    111
    112static int sun4i_clkevt_next_event(unsigned long evt,
    113				   struct clock_event_device *clkevt)
    114{
    115	struct timer_of *to = to_timer_of(clkevt);
    116
    117	sun4i_clkevt_time_stop(timer_of_base(to), 0);
    118	sun4i_clkevt_time_setup(timer_of_base(to), 0, evt - TIMER_SYNC_TICKS);
    119	sun4i_clkevt_time_start(timer_of_base(to), 0, false);
    120
    121	return 0;
    122}
    123
    124static void sun4i_timer_clear_interrupt(void __iomem *base)
    125{
    126	writel(TIMER_IRQ_EN(0), base + TIMER_IRQ_ST_REG);
    127}
    128
    129static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
    130{
    131	struct clock_event_device *evt = (struct clock_event_device *)dev_id;
    132	struct timer_of *to = to_timer_of(evt);
    133
    134	sun4i_timer_clear_interrupt(timer_of_base(to));
    135	evt->event_handler(evt);
    136
    137	return IRQ_HANDLED;
    138}
    139
    140static struct timer_of to = {
    141	.flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
    142
    143	.clkevt = {
    144		.name = "sun4i_tick",
    145		.rating = 350,
    146		.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
    147		.set_state_shutdown = sun4i_clkevt_shutdown,
    148		.set_state_periodic = sun4i_clkevt_set_periodic,
    149		.set_state_oneshot = sun4i_clkevt_set_oneshot,
    150		.tick_resume = sun4i_clkevt_shutdown,
    151		.set_next_event = sun4i_clkevt_next_event,
    152		.cpumask = cpu_possible_mask,
    153	},
    154
    155	.of_irq = {
    156		.handler = sun4i_timer_interrupt,
    157		.flags = IRQF_TIMER | IRQF_IRQPOLL,
    158	},
    159};
    160
    161static u64 notrace sun4i_timer_sched_read(void)
    162{
    163	return ~readl(timer_of_base(&to) + TIMER_CNTVAL_REG(1));
    164}
    165
    166static int __init sun4i_timer_init(struct device_node *node)
    167{
    168	int ret;
    169	u32 val;
    170
    171	ret = timer_of_init(node, &to);
    172	if (ret)
    173		return ret;
    174
    175	writel(~0, timer_of_base(&to) + TIMER_INTVAL_REG(1));
    176	writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD |
    177	       TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
    178	       timer_of_base(&to) + TIMER_CTL_REG(1));
    179
    180	/*
    181	 * sched_clock_register does not have priorities, and on sun6i and
    182	 * later there is a better sched_clock registered by arm_arch_timer.c
    183	 */
    184	if (of_machine_is_compatible("allwinner,sun4i-a10") ||
    185	    of_machine_is_compatible("allwinner,sun5i-a13") ||
    186	    of_machine_is_compatible("allwinner,sun5i-a10s") ||
    187	    of_machine_is_compatible("allwinner,suniv-f1c100s"))
    188		sched_clock_register(sun4i_timer_sched_read, 32,
    189				     timer_of_rate(&to));
    190
    191	ret = clocksource_mmio_init(timer_of_base(&to) + TIMER_CNTVAL_REG(1),
    192				    node->name, timer_of_rate(&to), 350, 32,
    193				    clocksource_mmio_readl_down);
    194	if (ret) {
    195		pr_err("Failed to register clocksource\n");
    196		return ret;
    197	}
    198
    199	writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
    200	       timer_of_base(&to) + TIMER_CTL_REG(0));
    201
    202	/* Make sure timer is stopped before playing with interrupts */
    203	sun4i_clkevt_time_stop(timer_of_base(&to), 0);
    204
    205	/* clear timer0 interrupt */
    206	sun4i_timer_clear_interrupt(timer_of_base(&to));
    207
    208	clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
    209					TIMER_SYNC_TICKS, 0xffffffff);
    210
    211	/* Enable timer0 interrupt */
    212	val = readl(timer_of_base(&to) + TIMER_IRQ_EN_REG);
    213	writel(val | TIMER_IRQ_EN(0), timer_of_base(&to) + TIMER_IRQ_EN_REG);
    214
    215	return ret;
    216}
    217TIMER_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
    218		       sun4i_timer_init);
    219TIMER_OF_DECLARE(sun8i_a23, "allwinner,sun8i-a23-timer",
    220		 sun4i_timer_init);
    221TIMER_OF_DECLARE(sun8i_v3s, "allwinner,sun8i-v3s-timer",
    222		 sun4i_timer_init);
    223TIMER_OF_DECLARE(suniv, "allwinner,suniv-f1c100s-timer",
    224		       sun4i_timer_init);