12bac1de2SLennert Buytenhek /*
22bac1de2SLennert Buytenhek * arch/arm/plat-orion/time.c
32bac1de2SLennert Buytenhek *
42bac1de2SLennert Buytenhek * Marvell Orion SoC timer handling.
52bac1de2SLennert Buytenhek *
62bac1de2SLennert Buytenhek * This file is licensed under the terms of the GNU General Public
72bac1de2SLennert Buytenhek * License version 2. This program is licensed "as is" without any
82bac1de2SLennert Buytenhek * warranty of any kind, whether express or implied.
92bac1de2SLennert Buytenhek *
102bac1de2SLennert Buytenhek * Timer 0 is used as free-running clocksource, while timer 1 is
112bac1de2SLennert Buytenhek * used as clock_event_device.
122bac1de2SLennert Buytenhek */
132bac1de2SLennert Buytenhek
142bac1de2SLennert Buytenhek #include <linux/kernel.h>
15a399e3faSNicolas Pitre #include <linux/timer.h>
162bac1de2SLennert Buytenhek #include <linux/clockchips.h>
172bac1de2SLennert Buytenhek #include <linux/interrupt.h>
182bac1de2SLennert Buytenhek #include <linux/irq.h>
1938ff87f7SStephen Boyd #include <linux/sched_clock.h>
2048fce88cSAndrew Lunn #include <plat/time.h>
21f19768ceSRussell King #include <asm/delay.h>
222bac1de2SLennert Buytenhek
232bac1de2SLennert Buytenhek /*
244ee1f6b5SLennert Buytenhek * MBus bridge block registers.
252bac1de2SLennert Buytenhek */
264ee1f6b5SLennert Buytenhek #define BRIDGE_CAUSE_OFF 0x0110
274ee1f6b5SLennert Buytenhek #define BRIDGE_MASK_OFF 0x0114
284ee1f6b5SLennert Buytenhek #define BRIDGE_INT_TIMER0 0x0002
294ee1f6b5SLennert Buytenhek #define BRIDGE_INT_TIMER1 0x0004
302bac1de2SLennert Buytenhek
312bac1de2SLennert Buytenhek
322bac1de2SLennert Buytenhek /*
332bac1de2SLennert Buytenhek * Timer block registers.
342bac1de2SLennert Buytenhek */
354ee1f6b5SLennert Buytenhek #define TIMER_CTRL_OFF 0x0000
362bac1de2SLennert Buytenhek #define TIMER0_EN 0x0001
372bac1de2SLennert Buytenhek #define TIMER0_RELOAD_EN 0x0002
382bac1de2SLennert Buytenhek #define TIMER1_EN 0x0004
392bac1de2SLennert Buytenhek #define TIMER1_RELOAD_EN 0x0008
404ee1f6b5SLennert Buytenhek #define TIMER0_RELOAD_OFF 0x0010
414ee1f6b5SLennert Buytenhek #define TIMER0_VAL_OFF 0x0014
424ee1f6b5SLennert Buytenhek #define TIMER1_RELOAD_OFF 0x0018
434ee1f6b5SLennert Buytenhek #define TIMER1_VAL_OFF 0x001c
444ee1f6b5SLennert Buytenhek
454ee1f6b5SLennert Buytenhek
464ee1f6b5SLennert Buytenhek /*
474ee1f6b5SLennert Buytenhek * SoC-specific data.
484ee1f6b5SLennert Buytenhek */
494ee1f6b5SLennert Buytenhek static void __iomem *bridge_base;
504ee1f6b5SLennert Buytenhek static u32 bridge_timer1_clr_mask;
514ee1f6b5SLennert Buytenhek static void __iomem *timer_base;
524ee1f6b5SLennert Buytenhek
534ee1f6b5SLennert Buytenhek
544ee1f6b5SLennert Buytenhek /*
554ee1f6b5SLennert Buytenhek * Number of timer ticks per jiffy.
564ee1f6b5SLennert Buytenhek */
574ee1f6b5SLennert Buytenhek static u32 ticks_per_jiffy;
582bac1de2SLennert Buytenhek
592bac1de2SLennert Buytenhek
602bac1de2SLennert Buytenhek /*
618a3269fcSStefan Agner * Orion's sched_clock implementation. It has a resolution of
62f06a1624SRussell King * at least 7.5ns (133MHz TCLK).
638a3269fcSStefan Agner */
648a3269fcSStefan Agner
orion_read_sched_clock(void)65b44653baSStephen Boyd static u64 notrace orion_read_sched_clock(void)
668a3269fcSStefan Agner {
672f0778afSMarc Zyngier return ~readl(timer_base + TIMER0_VAL_OFF);
688a3269fcSStefan Agner }
698a3269fcSStefan Agner
708a3269fcSStefan Agner /*
712bac1de2SLennert Buytenhek * Clockevent handling.
722bac1de2SLennert Buytenhek */
732bac1de2SLennert Buytenhek static int
orion_clkevt_next_event(unsigned long delta,struct clock_event_device * dev)742bac1de2SLennert Buytenhek orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev)
752bac1de2SLennert Buytenhek {
762bac1de2SLennert Buytenhek unsigned long flags;
772bac1de2SLennert Buytenhek u32 u;
782bac1de2SLennert Buytenhek
792bac1de2SLennert Buytenhek if (delta == 0)
802bac1de2SLennert Buytenhek return -ETIME;
812bac1de2SLennert Buytenhek
822bac1de2SLennert Buytenhek local_irq_save(flags);
832bac1de2SLennert Buytenhek
842bac1de2SLennert Buytenhek /*
852bac1de2SLennert Buytenhek * Clear and enable clockevent timer interrupt.
862bac1de2SLennert Buytenhek */
874ee1f6b5SLennert Buytenhek writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
882bac1de2SLennert Buytenhek
894ee1f6b5SLennert Buytenhek u = readl(bridge_base + BRIDGE_MASK_OFF);
902bac1de2SLennert Buytenhek u |= BRIDGE_INT_TIMER1;
914ee1f6b5SLennert Buytenhek writel(u, bridge_base + BRIDGE_MASK_OFF);
922bac1de2SLennert Buytenhek
932bac1de2SLennert Buytenhek /*
942bac1de2SLennert Buytenhek * Setup new clockevent timer value.
952bac1de2SLennert Buytenhek */
964ee1f6b5SLennert Buytenhek writel(delta, timer_base + TIMER1_VAL_OFF);
972bac1de2SLennert Buytenhek
982bac1de2SLennert Buytenhek /*
992bac1de2SLennert Buytenhek * Enable the timer.
1002bac1de2SLennert Buytenhek */
1014ee1f6b5SLennert Buytenhek u = readl(timer_base + TIMER_CTRL_OFF);
1022bac1de2SLennert Buytenhek u = (u & ~TIMER1_RELOAD_EN) | TIMER1_EN;
1034ee1f6b5SLennert Buytenhek writel(u, timer_base + TIMER_CTRL_OFF);
1042bac1de2SLennert Buytenhek
1052bac1de2SLennert Buytenhek local_irq_restore(flags);
1062bac1de2SLennert Buytenhek
1072bac1de2SLennert Buytenhek return 0;
1082bac1de2SLennert Buytenhek }
1092bac1de2SLennert Buytenhek
orion_clkevt_shutdown(struct clock_event_device * evt)11010dca88aSViresh Kumar static int orion_clkevt_shutdown(struct clock_event_device *evt)
1112bac1de2SLennert Buytenhek {
1122bac1de2SLennert Buytenhek unsigned long flags;
1132bac1de2SLennert Buytenhek u32 u;
1142bac1de2SLennert Buytenhek
1152bac1de2SLennert Buytenhek local_irq_save(flags);
1162bac1de2SLennert Buytenhek
11710dca88aSViresh Kumar /* Disable timer */
1184ee1f6b5SLennert Buytenhek u = readl(timer_base + TIMER_CTRL_OFF);
1194ee1f6b5SLennert Buytenhek writel(u & ~TIMER1_EN, timer_base + TIMER_CTRL_OFF);
1202bac1de2SLennert Buytenhek
12110dca88aSViresh Kumar /* Disable timer interrupt */
1224ee1f6b5SLennert Buytenhek u = readl(bridge_base + BRIDGE_MASK_OFF);
1234ee1f6b5SLennert Buytenhek writel(u & ~BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
1242bac1de2SLennert Buytenhek
12510dca88aSViresh Kumar /* ACK pending timer interrupt */
1264ee1f6b5SLennert Buytenhek writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
1272bac1de2SLennert Buytenhek
1282bac1de2SLennert Buytenhek local_irq_restore(flags);
12910dca88aSViresh Kumar
13010dca88aSViresh Kumar return 0;
13110dca88aSViresh Kumar }
13210dca88aSViresh Kumar
orion_clkevt_set_periodic(struct clock_event_device * evt)13310dca88aSViresh Kumar static int orion_clkevt_set_periodic(struct clock_event_device *evt)
13410dca88aSViresh Kumar {
13510dca88aSViresh Kumar unsigned long flags;
13610dca88aSViresh Kumar u32 u;
13710dca88aSViresh Kumar
13810dca88aSViresh Kumar local_irq_save(flags);
13910dca88aSViresh Kumar
14010dca88aSViresh Kumar /* Setup timer to fire at 1/HZ intervals */
14110dca88aSViresh Kumar writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD_OFF);
14210dca88aSViresh Kumar writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL_OFF);
14310dca88aSViresh Kumar
14410dca88aSViresh Kumar /* Enable timer interrupt */
14510dca88aSViresh Kumar u = readl(bridge_base + BRIDGE_MASK_OFF);
14610dca88aSViresh Kumar writel(u | BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
14710dca88aSViresh Kumar
14810dca88aSViresh Kumar /* Enable timer */
14910dca88aSViresh Kumar u = readl(timer_base + TIMER_CTRL_OFF);
15010dca88aSViresh Kumar writel(u | TIMER1_EN | TIMER1_RELOAD_EN, timer_base + TIMER_CTRL_OFF);
15110dca88aSViresh Kumar
15210dca88aSViresh Kumar local_irq_restore(flags);
15310dca88aSViresh Kumar
15410dca88aSViresh Kumar return 0;
1552bac1de2SLennert Buytenhek }
1562bac1de2SLennert Buytenhek
1572bac1de2SLennert Buytenhek static struct clock_event_device orion_clkevt = {
1582bac1de2SLennert Buytenhek .name = "orion_tick",
15910dca88aSViresh Kumar .features = CLOCK_EVT_FEAT_ONESHOT |
16010dca88aSViresh Kumar CLOCK_EVT_FEAT_PERIODIC,
1612bac1de2SLennert Buytenhek .rating = 300,
1622bac1de2SLennert Buytenhek .set_next_event = orion_clkevt_next_event,
16310dca88aSViresh Kumar .set_state_shutdown = orion_clkevt_shutdown,
16410dca88aSViresh Kumar .set_state_periodic = orion_clkevt_set_periodic,
16510dca88aSViresh Kumar .set_state_oneshot = orion_clkevt_shutdown,
16610dca88aSViresh Kumar .tick_resume = orion_clkevt_shutdown,
1672bac1de2SLennert Buytenhek };
1682bac1de2SLennert Buytenhek
orion_timer_interrupt(int irq,void * dev_id)1692bac1de2SLennert Buytenhek static irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
1702bac1de2SLennert Buytenhek {
1712bac1de2SLennert Buytenhek /*
1722bac1de2SLennert Buytenhek * ACK timer interrupt and call event handler.
1732bac1de2SLennert Buytenhek */
1744ee1f6b5SLennert Buytenhek writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
1752bac1de2SLennert Buytenhek orion_clkevt.event_handler(&orion_clkevt);
1762bac1de2SLennert Buytenhek
1772bac1de2SLennert Buytenhek return IRQ_HANDLED;
1782bac1de2SLennert Buytenhek }
1792bac1de2SLennert Buytenhek
1804ee1f6b5SLennert Buytenhek void __init
orion_time_set_base(void __iomem * _timer_base)181e96a0309SThomas Petazzoni orion_time_set_base(void __iomem *_timer_base)
1824ee1f6b5SLennert Buytenhek {
183e96a0309SThomas Petazzoni timer_base = _timer_base;
1844ee1f6b5SLennert Buytenhek }
1854ee1f6b5SLennert Buytenhek
orion_delay_timer_read(void)186f19768ceSRussell King static unsigned long orion_delay_timer_read(void)
187f19768ceSRussell King {
188f19768ceSRussell King return ~readl(timer_base + TIMER0_VAL_OFF);
189f19768ceSRussell King }
190f19768ceSRussell King
191f19768ceSRussell King static struct delay_timer orion_delay_timer = {
192f19768ceSRussell King .read_current_timer = orion_delay_timer_read,
193f19768ceSRussell King };
194f19768ceSRussell King
1954ee1f6b5SLennert Buytenhek void __init
orion_time_init(void __iomem * _bridge_base,u32 _bridge_timer1_clr_mask,unsigned int irq,unsigned int tclk)196e96a0309SThomas Petazzoni orion_time_init(void __iomem *_bridge_base, u32 _bridge_timer1_clr_mask,
1974ee1f6b5SLennert Buytenhek unsigned int irq, unsigned int tclk)
1982bac1de2SLennert Buytenhek {
1992bac1de2SLennert Buytenhek u32 u;
2002bac1de2SLennert Buytenhek
2014ee1f6b5SLennert Buytenhek /*
2024ee1f6b5SLennert Buytenhek * Set SoC-specific data.
2034ee1f6b5SLennert Buytenhek */
204e96a0309SThomas Petazzoni bridge_base = _bridge_base;
2054ee1f6b5SLennert Buytenhek bridge_timer1_clr_mask = _bridge_timer1_clr_mask;
2064ee1f6b5SLennert Buytenhek
2072bac1de2SLennert Buytenhek ticks_per_jiffy = (tclk + HZ/2) / HZ;
2082bac1de2SLennert Buytenhek
209f19768ceSRussell King orion_delay_timer.freq = tclk;
210f19768ceSRussell King register_current_timer_delay(&orion_delay_timer);
211f19768ceSRussell King
2128a3269fcSStefan Agner /*
2134ee1f6b5SLennert Buytenhek * Set scale and timer for sched_clock.
2148a3269fcSStefan Agner */
215b44653baSStephen Boyd sched_clock_register(orion_read_sched_clock, 32, tclk);
2162bac1de2SLennert Buytenhek
2172bac1de2SLennert Buytenhek /*
2182bac1de2SLennert Buytenhek * Setup free-running clocksource timer (interrupts
2194ee1f6b5SLennert Buytenhek * disabled).
2202bac1de2SLennert Buytenhek */
2214ee1f6b5SLennert Buytenhek writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
2224ee1f6b5SLennert Buytenhek writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
2234ee1f6b5SLennert Buytenhek u = readl(bridge_base + BRIDGE_MASK_OFF);
2244ee1f6b5SLennert Buytenhek writel(u & ~BRIDGE_INT_TIMER0, bridge_base + BRIDGE_MASK_OFF);
2254ee1f6b5SLennert Buytenhek u = readl(timer_base + TIMER_CTRL_OFF);
2264ee1f6b5SLennert Buytenhek writel(u | TIMER0_EN | TIMER0_RELOAD_EN, timer_base + TIMER_CTRL_OFF);
227bfe45e0bSRussell King clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, "orion_clocksource",
228bfe45e0bSRussell King tclk, 300, 32, clocksource_mmio_readl_down);
2292bac1de2SLennert Buytenhek
2302bac1de2SLennert Buytenhek /*
2314ee1f6b5SLennert Buytenhek * Setup clockevent timer (interrupt-driven).
2322bac1de2SLennert Buytenhek */
233*37b146e3Safzal mohammed if (request_irq(irq, orion_timer_interrupt, IRQF_TIMER, "orion_tick",
234*37b146e3Safzal mohammed NULL))
235*37b146e3Safzal mohammed pr_err("Failed to request irq %u (orion_tick)\n", irq);
236320ab2b0SRusty Russell orion_clkevt.cpumask = cpumask_of(0);
237838a2ae8SShawn Guo clockevents_config_and_register(&orion_clkevt, tclk, 1, 0xfffffffe);
2382bac1de2SLennert Buytenhek }
239