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

time.c (5598B)


      1/*
      2 * arch/arm/plat-orion/time.c
      3 *
      4 * Marvell Orion SoC timer handling.
      5 *
      6 * This file is licensed under the terms of the GNU General Public
      7 * License version 2.  This program is licensed "as is" without any
      8 * warranty of any kind, whether express or implied.
      9 *
     10 * Timer 0 is used as free-running clocksource, while timer 1 is
     11 * used as clock_event_device.
     12 */
     13
     14#include <linux/kernel.h>
     15#include <linux/timer.h>
     16#include <linux/clockchips.h>
     17#include <linux/interrupt.h>
     18#include <linux/irq.h>
     19#include <linux/sched_clock.h>
     20#include <plat/time.h>
     21#include <asm/delay.h>
     22
     23/*
     24 * MBus bridge block registers.
     25 */
     26#define BRIDGE_CAUSE_OFF	0x0110
     27#define BRIDGE_MASK_OFF		0x0114
     28#define  BRIDGE_INT_TIMER0	 0x0002
     29#define  BRIDGE_INT_TIMER1	 0x0004
     30
     31
     32/*
     33 * Timer block registers.
     34 */
     35#define TIMER_CTRL_OFF		0x0000
     36#define  TIMER0_EN		 0x0001
     37#define  TIMER0_RELOAD_EN	 0x0002
     38#define  TIMER1_EN		 0x0004
     39#define  TIMER1_RELOAD_EN	 0x0008
     40#define TIMER0_RELOAD_OFF	0x0010
     41#define TIMER0_VAL_OFF		0x0014
     42#define TIMER1_RELOAD_OFF	0x0018
     43#define TIMER1_VAL_OFF		0x001c
     44
     45
     46/*
     47 * SoC-specific data.
     48 */
     49static void __iomem *bridge_base;
     50static u32 bridge_timer1_clr_mask;
     51static void __iomem *timer_base;
     52
     53
     54/*
     55 * Number of timer ticks per jiffy.
     56 */
     57static u32 ticks_per_jiffy;
     58
     59
     60/*
     61 * Orion's sched_clock implementation. It has a resolution of
     62 * at least 7.5ns (133MHz TCLK).
     63 */
     64
     65static u64 notrace orion_read_sched_clock(void)
     66{
     67	return ~readl(timer_base + TIMER0_VAL_OFF);
     68}
     69
     70/*
     71 * Clockevent handling.
     72 */
     73static int
     74orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev)
     75{
     76	unsigned long flags;
     77	u32 u;
     78
     79	if (delta == 0)
     80		return -ETIME;
     81
     82	local_irq_save(flags);
     83
     84	/*
     85	 * Clear and enable clockevent timer interrupt.
     86	 */
     87	writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
     88
     89	u = readl(bridge_base + BRIDGE_MASK_OFF);
     90	u |= BRIDGE_INT_TIMER1;
     91	writel(u, bridge_base + BRIDGE_MASK_OFF);
     92
     93	/*
     94	 * Setup new clockevent timer value.
     95	 */
     96	writel(delta, timer_base + TIMER1_VAL_OFF);
     97
     98	/*
     99	 * Enable the timer.
    100	 */
    101	u = readl(timer_base + TIMER_CTRL_OFF);
    102	u = (u & ~TIMER1_RELOAD_EN) | TIMER1_EN;
    103	writel(u, timer_base + TIMER_CTRL_OFF);
    104
    105	local_irq_restore(flags);
    106
    107	return 0;
    108}
    109
    110static int orion_clkevt_shutdown(struct clock_event_device *evt)
    111{
    112	unsigned long flags;
    113	u32 u;
    114
    115	local_irq_save(flags);
    116
    117	/* Disable timer */
    118	u = readl(timer_base + TIMER_CTRL_OFF);
    119	writel(u & ~TIMER1_EN, timer_base + TIMER_CTRL_OFF);
    120
    121	/* Disable timer interrupt */
    122	u = readl(bridge_base + BRIDGE_MASK_OFF);
    123	writel(u & ~BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
    124
    125	/* ACK pending timer interrupt */
    126	writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
    127
    128	local_irq_restore(flags);
    129
    130	return 0;
    131}
    132
    133static int orion_clkevt_set_periodic(struct clock_event_device *evt)
    134{
    135	unsigned long flags;
    136	u32 u;
    137
    138	local_irq_save(flags);
    139
    140	/* Setup timer to fire at 1/HZ intervals */
    141	writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD_OFF);
    142	writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL_OFF);
    143
    144	/* Enable timer interrupt */
    145	u = readl(bridge_base + BRIDGE_MASK_OFF);
    146	writel(u | BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
    147
    148	/* Enable timer */
    149	u = readl(timer_base + TIMER_CTRL_OFF);
    150	writel(u | TIMER1_EN | TIMER1_RELOAD_EN, timer_base + TIMER_CTRL_OFF);
    151
    152	local_irq_restore(flags);
    153
    154	return 0;
    155}
    156
    157static struct clock_event_device orion_clkevt = {
    158	.name			= "orion_tick",
    159	.features		= CLOCK_EVT_FEAT_ONESHOT |
    160				  CLOCK_EVT_FEAT_PERIODIC,
    161	.rating			= 300,
    162	.set_next_event		= orion_clkevt_next_event,
    163	.set_state_shutdown	= orion_clkevt_shutdown,
    164	.set_state_periodic	= orion_clkevt_set_periodic,
    165	.set_state_oneshot	= orion_clkevt_shutdown,
    166	.tick_resume		= orion_clkevt_shutdown,
    167};
    168
    169static irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
    170{
    171	/*
    172	 * ACK timer interrupt and call event handler.
    173	 */
    174	writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
    175	orion_clkevt.event_handler(&orion_clkevt);
    176
    177	return IRQ_HANDLED;
    178}
    179
    180void __init
    181orion_time_set_base(void __iomem *_timer_base)
    182{
    183	timer_base = _timer_base;
    184}
    185
    186static unsigned long orion_delay_timer_read(void)
    187{
    188	return ~readl(timer_base + TIMER0_VAL_OFF);
    189}
    190
    191static struct delay_timer orion_delay_timer = {
    192	.read_current_timer = orion_delay_timer_read,
    193};
    194
    195void __init
    196orion_time_init(void __iomem *_bridge_base, u32 _bridge_timer1_clr_mask,
    197		unsigned int irq, unsigned int tclk)
    198{
    199	u32 u;
    200
    201	/*
    202	 * Set SoC-specific data.
    203	 */
    204	bridge_base = _bridge_base;
    205	bridge_timer1_clr_mask = _bridge_timer1_clr_mask;
    206
    207	ticks_per_jiffy = (tclk + HZ/2) / HZ;
    208
    209	orion_delay_timer.freq = tclk;
    210	register_current_timer_delay(&orion_delay_timer);
    211
    212	/*
    213	 * Set scale and timer for sched_clock.
    214	 */
    215	sched_clock_register(orion_read_sched_clock, 32, tclk);
    216
    217	/*
    218	 * Setup free-running clocksource timer (interrupts
    219	 * disabled).
    220	 */
    221	writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
    222	writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
    223	u = readl(bridge_base + BRIDGE_MASK_OFF);
    224	writel(u & ~BRIDGE_INT_TIMER0, bridge_base + BRIDGE_MASK_OFF);
    225	u = readl(timer_base + TIMER_CTRL_OFF);
    226	writel(u | TIMER0_EN | TIMER0_RELOAD_EN, timer_base + TIMER_CTRL_OFF);
    227	clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, "orion_clocksource",
    228		tclk, 300, 32, clocksource_mmio_readl_down);
    229
    230	/*
    231	 * Setup clockevent timer (interrupt-driven).
    232	 */
    233	if (request_irq(irq, orion_timer_interrupt, IRQF_TIMER, "orion_tick",
    234			NULL))
    235		pr_err("Failed to request irq %u (orion_tick)\n", irq);
    236	orion_clkevt.cpumask = cpumask_of(0);
    237	clockevents_config_and_register(&orion_clkevt, tclk, 1, 0xfffffffe);
    238}