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 */ 49 static void __iomem *bridge_base; 50 static u32 bridge_timer1_clr_mask; 51 static void __iomem *timer_base; 52 53 54 /* 55 * Number of timer ticks per jiffy. 56 */ 57 static 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 65 static u64 notrace orion_read_sched_clock(void) 66 { 67 return ~readl(timer_base + TIMER0_VAL_OFF); 68 } 69 70 /* 71 * Clockevent handling. 72 */ 73 static int 74 orion_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 110 static 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 133 static 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 157 static 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 169 static 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 180 void __init 181 orion_time_set_base(void __iomem *_timer_base) 182 { 183 timer_base = _timer_base; 184 } 185 186 static unsigned long orion_delay_timer_read(void) 187 { 188 return ~readl(timer_base + TIMER0_VAL_OFF); 189 } 190 191 static struct delay_timer orion_delay_timer = { 192 .read_current_timer = orion_delay_timer_read, 193 }; 194 195 void __init 196 orion_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 } 239
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.