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}