xref: /openbmc/linux/arch/arm/plat-orion/time.c (revision 37b146e3)
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 	 */
23337b146e3Safzal mohammed 	if (request_irq(irq, orion_timer_interrupt, IRQF_TIMER, "orion_tick",
23437b146e3Safzal mohammed 			NULL))
23537b146e3Safzal 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