18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * arch/arm/plat-orion/time.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Marvell Orion SoC timer handling.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
78c2ecf20Sopenharmony_ci * License version 2.  This program is licensed "as is" without any
88c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Timer 0 is used as free-running clocksource, while timer 1 is
118c2ecf20Sopenharmony_ci * used as clock_event_device.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/timer.h>
168c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
178c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
188c2ecf20Sopenharmony_ci#include <linux/irq.h>
198c2ecf20Sopenharmony_ci#include <linux/sched_clock.h>
208c2ecf20Sopenharmony_ci#include <plat/time.h>
218c2ecf20Sopenharmony_ci#include <asm/delay.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * MBus bridge block registers.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci#define BRIDGE_CAUSE_OFF	0x0110
278c2ecf20Sopenharmony_ci#define BRIDGE_MASK_OFF		0x0114
288c2ecf20Sopenharmony_ci#define  BRIDGE_INT_TIMER0	 0x0002
298c2ecf20Sopenharmony_ci#define  BRIDGE_INT_TIMER1	 0x0004
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * Timer block registers.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci#define TIMER_CTRL_OFF		0x0000
368c2ecf20Sopenharmony_ci#define  TIMER0_EN		 0x0001
378c2ecf20Sopenharmony_ci#define  TIMER0_RELOAD_EN	 0x0002
388c2ecf20Sopenharmony_ci#define  TIMER1_EN		 0x0004
398c2ecf20Sopenharmony_ci#define  TIMER1_RELOAD_EN	 0x0008
408c2ecf20Sopenharmony_ci#define TIMER0_RELOAD_OFF	0x0010
418c2ecf20Sopenharmony_ci#define TIMER0_VAL_OFF		0x0014
428c2ecf20Sopenharmony_ci#define TIMER1_RELOAD_OFF	0x0018
438c2ecf20Sopenharmony_ci#define TIMER1_VAL_OFF		0x001c
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/*
478c2ecf20Sopenharmony_ci * SoC-specific data.
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_cistatic void __iomem *bridge_base;
508c2ecf20Sopenharmony_cistatic u32 bridge_timer1_clr_mask;
518c2ecf20Sopenharmony_cistatic void __iomem *timer_base;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/*
558c2ecf20Sopenharmony_ci * Number of timer ticks per jiffy.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistatic u32 ticks_per_jiffy;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*
618c2ecf20Sopenharmony_ci * Orion's sched_clock implementation. It has a resolution of
628c2ecf20Sopenharmony_ci * at least 7.5ns (133MHz TCLK).
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic u64 notrace orion_read_sched_clock(void)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	return ~readl(timer_base + TIMER0_VAL_OFF);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/*
718c2ecf20Sopenharmony_ci * Clockevent handling.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_cistatic int
748c2ecf20Sopenharmony_ciorion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	unsigned long flags;
778c2ecf20Sopenharmony_ci	u32 u;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (delta == 0)
808c2ecf20Sopenharmony_ci		return -ETIME;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	local_irq_save(flags);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/*
858c2ecf20Sopenharmony_ci	 * Clear and enable clockevent timer interrupt.
868c2ecf20Sopenharmony_ci	 */
878c2ecf20Sopenharmony_ci	writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	u = readl(bridge_base + BRIDGE_MASK_OFF);
908c2ecf20Sopenharmony_ci	u |= BRIDGE_INT_TIMER1;
918c2ecf20Sopenharmony_ci	writel(u, bridge_base + BRIDGE_MASK_OFF);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/*
948c2ecf20Sopenharmony_ci	 * Setup new clockevent timer value.
958c2ecf20Sopenharmony_ci	 */
968c2ecf20Sopenharmony_ci	writel(delta, timer_base + TIMER1_VAL_OFF);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/*
998c2ecf20Sopenharmony_ci	 * Enable the timer.
1008c2ecf20Sopenharmony_ci	 */
1018c2ecf20Sopenharmony_ci	u = readl(timer_base + TIMER_CTRL_OFF);
1028c2ecf20Sopenharmony_ci	u = (u & ~TIMER1_RELOAD_EN) | TIMER1_EN;
1038c2ecf20Sopenharmony_ci	writel(u, timer_base + TIMER_CTRL_OFF);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int orion_clkevt_shutdown(struct clock_event_device *evt)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	unsigned long flags;
1138c2ecf20Sopenharmony_ci	u32 u;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	local_irq_save(flags);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* Disable timer */
1188c2ecf20Sopenharmony_ci	u = readl(timer_base + TIMER_CTRL_OFF);
1198c2ecf20Sopenharmony_ci	writel(u & ~TIMER1_EN, timer_base + TIMER_CTRL_OFF);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* Disable timer interrupt */
1228c2ecf20Sopenharmony_ci	u = readl(bridge_base + BRIDGE_MASK_OFF);
1238c2ecf20Sopenharmony_ci	writel(u & ~BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* ACK pending timer interrupt */
1268c2ecf20Sopenharmony_ci	writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int orion_clkevt_set_periodic(struct clock_event_device *evt)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	unsigned long flags;
1368c2ecf20Sopenharmony_ci	u32 u;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	local_irq_save(flags);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* Setup timer to fire at 1/HZ intervals */
1418c2ecf20Sopenharmony_ci	writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD_OFF);
1428c2ecf20Sopenharmony_ci	writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL_OFF);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* Enable timer interrupt */
1458c2ecf20Sopenharmony_ci	u = readl(bridge_base + BRIDGE_MASK_OFF);
1468c2ecf20Sopenharmony_ci	writel(u | BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Enable timer */
1498c2ecf20Sopenharmony_ci	u = readl(timer_base + TIMER_CTRL_OFF);
1508c2ecf20Sopenharmony_ci	writel(u | TIMER1_EN | TIMER1_RELOAD_EN, timer_base + TIMER_CTRL_OFF);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return 0;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic struct clock_event_device orion_clkevt = {
1588c2ecf20Sopenharmony_ci	.name			= "orion_tick",
1598c2ecf20Sopenharmony_ci	.features		= CLOCK_EVT_FEAT_ONESHOT |
1608c2ecf20Sopenharmony_ci				  CLOCK_EVT_FEAT_PERIODIC,
1618c2ecf20Sopenharmony_ci	.rating			= 300,
1628c2ecf20Sopenharmony_ci	.set_next_event		= orion_clkevt_next_event,
1638c2ecf20Sopenharmony_ci	.set_state_shutdown	= orion_clkevt_shutdown,
1648c2ecf20Sopenharmony_ci	.set_state_periodic	= orion_clkevt_set_periodic,
1658c2ecf20Sopenharmony_ci	.set_state_oneshot	= orion_clkevt_shutdown,
1668c2ecf20Sopenharmony_ci	.tick_resume		= orion_clkevt_shutdown,
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	/*
1728c2ecf20Sopenharmony_ci	 * ACK timer interrupt and call event handler.
1738c2ecf20Sopenharmony_ci	 */
1748c2ecf20Sopenharmony_ci	writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
1758c2ecf20Sopenharmony_ci	orion_clkevt.event_handler(&orion_clkevt);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_civoid __init
1818c2ecf20Sopenharmony_ciorion_time_set_base(void __iomem *_timer_base)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	timer_base = _timer_base;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic unsigned long orion_delay_timer_read(void)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	return ~readl(timer_base + TIMER0_VAL_OFF);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic struct delay_timer orion_delay_timer = {
1928c2ecf20Sopenharmony_ci	.read_current_timer = orion_delay_timer_read,
1938c2ecf20Sopenharmony_ci};
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_civoid __init
1968c2ecf20Sopenharmony_ciorion_time_init(void __iomem *_bridge_base, u32 _bridge_timer1_clr_mask,
1978c2ecf20Sopenharmony_ci		unsigned int irq, unsigned int tclk)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	u32 u;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/*
2028c2ecf20Sopenharmony_ci	 * Set SoC-specific data.
2038c2ecf20Sopenharmony_ci	 */
2048c2ecf20Sopenharmony_ci	bridge_base = _bridge_base;
2058c2ecf20Sopenharmony_ci	bridge_timer1_clr_mask = _bridge_timer1_clr_mask;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ticks_per_jiffy = (tclk + HZ/2) / HZ;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	orion_delay_timer.freq = tclk;
2108c2ecf20Sopenharmony_ci	register_current_timer_delay(&orion_delay_timer);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * Set scale and timer for sched_clock.
2148c2ecf20Sopenharmony_ci	 */
2158c2ecf20Sopenharmony_ci	sched_clock_register(orion_read_sched_clock, 32, tclk);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/*
2188c2ecf20Sopenharmony_ci	 * Setup free-running clocksource timer (interrupts
2198c2ecf20Sopenharmony_ci	 * disabled).
2208c2ecf20Sopenharmony_ci	 */
2218c2ecf20Sopenharmony_ci	writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
2228c2ecf20Sopenharmony_ci	writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
2238c2ecf20Sopenharmony_ci	u = readl(bridge_base + BRIDGE_MASK_OFF);
2248c2ecf20Sopenharmony_ci	writel(u & ~BRIDGE_INT_TIMER0, bridge_base + BRIDGE_MASK_OFF);
2258c2ecf20Sopenharmony_ci	u = readl(timer_base + TIMER_CTRL_OFF);
2268c2ecf20Sopenharmony_ci	writel(u | TIMER0_EN | TIMER0_RELOAD_EN, timer_base + TIMER_CTRL_OFF);
2278c2ecf20Sopenharmony_ci	clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, "orion_clocksource",
2288c2ecf20Sopenharmony_ci		tclk, 300, 32, clocksource_mmio_readl_down);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/*
2318c2ecf20Sopenharmony_ci	 * Setup clockevent timer (interrupt-driven).
2328c2ecf20Sopenharmony_ci	 */
2338c2ecf20Sopenharmony_ci	if (request_irq(irq, orion_timer_interrupt, IRQF_TIMER, "orion_tick",
2348c2ecf20Sopenharmony_ci			NULL))
2358c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %u (orion_tick)\n", irq);
2368c2ecf20Sopenharmony_ci	orion_clkevt.cpumask = cpumask_of(0);
2378c2ecf20Sopenharmony_ci	clockevents_config_and_register(&orion_clkevt, tclk, 1, 0xfffffffe);
2388c2ecf20Sopenharmony_ci}
239