xref: /linux/drivers/clocksource/timer-orion.c (revision 9d8d47ea6ec6048abc75ccc4486aff1a7db1ff4b)
1*9d8d47eaSDaniel Lezcano /*
2*9d8d47eaSDaniel Lezcano  * Marvell Orion SoC timer handling.
3*9d8d47eaSDaniel Lezcano  *
4*9d8d47eaSDaniel Lezcano  * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
5*9d8d47eaSDaniel Lezcano  *
6*9d8d47eaSDaniel Lezcano  * This file is licensed under the terms of the GNU General Public
7*9d8d47eaSDaniel Lezcano  * License version 2.  This program is licensed "as is" without any
8*9d8d47eaSDaniel Lezcano  * warranty of any kind, whether express or implied.
9*9d8d47eaSDaniel Lezcano  *
10*9d8d47eaSDaniel Lezcano  * Timer 0 is used as free-running clocksource, while timer 1 is
11*9d8d47eaSDaniel Lezcano  * used as clock_event_device.
12*9d8d47eaSDaniel Lezcano  */
13*9d8d47eaSDaniel Lezcano 
14*9d8d47eaSDaniel Lezcano #include <linux/kernel.h>
15*9d8d47eaSDaniel Lezcano #include <linux/bitops.h>
16*9d8d47eaSDaniel Lezcano #include <linux/clk.h>
17*9d8d47eaSDaniel Lezcano #include <linux/clockchips.h>
18*9d8d47eaSDaniel Lezcano #include <linux/delay.h>
19*9d8d47eaSDaniel Lezcano #include <linux/interrupt.h>
20*9d8d47eaSDaniel Lezcano #include <linux/of_address.h>
21*9d8d47eaSDaniel Lezcano #include <linux/of_irq.h>
22*9d8d47eaSDaniel Lezcano #include <linux/spinlock.h>
23*9d8d47eaSDaniel Lezcano #include <linux/sched_clock.h>
24*9d8d47eaSDaniel Lezcano 
25*9d8d47eaSDaniel Lezcano #define TIMER_CTRL		0x00
26*9d8d47eaSDaniel Lezcano #define  TIMER0_EN		BIT(0)
27*9d8d47eaSDaniel Lezcano #define  TIMER0_RELOAD_EN	BIT(1)
28*9d8d47eaSDaniel Lezcano #define  TIMER1_EN		BIT(2)
29*9d8d47eaSDaniel Lezcano #define  TIMER1_RELOAD_EN	BIT(3)
30*9d8d47eaSDaniel Lezcano #define TIMER0_RELOAD		0x10
31*9d8d47eaSDaniel Lezcano #define TIMER0_VAL		0x14
32*9d8d47eaSDaniel Lezcano #define TIMER1_RELOAD		0x18
33*9d8d47eaSDaniel Lezcano #define TIMER1_VAL		0x1c
34*9d8d47eaSDaniel Lezcano 
35*9d8d47eaSDaniel Lezcano #define ORION_ONESHOT_MIN	1
36*9d8d47eaSDaniel Lezcano #define ORION_ONESHOT_MAX	0xfffffffe
37*9d8d47eaSDaniel Lezcano 
38*9d8d47eaSDaniel Lezcano static void __iomem *timer_base;
39*9d8d47eaSDaniel Lezcano 
40*9d8d47eaSDaniel Lezcano static unsigned long notrace orion_read_timer(void)
41*9d8d47eaSDaniel Lezcano {
42*9d8d47eaSDaniel Lezcano 	return ~readl(timer_base + TIMER0_VAL);
43*9d8d47eaSDaniel Lezcano }
44*9d8d47eaSDaniel Lezcano 
45*9d8d47eaSDaniel Lezcano static struct delay_timer orion_delay_timer = {
46*9d8d47eaSDaniel Lezcano 	.read_current_timer = orion_read_timer,
47*9d8d47eaSDaniel Lezcano };
48*9d8d47eaSDaniel Lezcano 
49*9d8d47eaSDaniel Lezcano static void orion_delay_timer_init(unsigned long rate)
50*9d8d47eaSDaniel Lezcano {
51*9d8d47eaSDaniel Lezcano 	orion_delay_timer.freq = rate;
52*9d8d47eaSDaniel Lezcano 	register_current_timer_delay(&orion_delay_timer);
53*9d8d47eaSDaniel Lezcano }
54*9d8d47eaSDaniel Lezcano 
55*9d8d47eaSDaniel Lezcano /*
56*9d8d47eaSDaniel Lezcano  * Free-running clocksource handling.
57*9d8d47eaSDaniel Lezcano  */
58*9d8d47eaSDaniel Lezcano static u64 notrace orion_read_sched_clock(void)
59*9d8d47eaSDaniel Lezcano {
60*9d8d47eaSDaniel Lezcano 	return ~readl(timer_base + TIMER0_VAL);
61*9d8d47eaSDaniel Lezcano }
62*9d8d47eaSDaniel Lezcano 
63*9d8d47eaSDaniel Lezcano /*
64*9d8d47eaSDaniel Lezcano  * Clockevent handling.
65*9d8d47eaSDaniel Lezcano  */
66*9d8d47eaSDaniel Lezcano static u32 ticks_per_jiffy;
67*9d8d47eaSDaniel Lezcano 
68*9d8d47eaSDaniel Lezcano static int orion_clkevt_next_event(unsigned long delta,
69*9d8d47eaSDaniel Lezcano 				   struct clock_event_device *dev)
70*9d8d47eaSDaniel Lezcano {
71*9d8d47eaSDaniel Lezcano 	/* setup and enable one-shot timer */
72*9d8d47eaSDaniel Lezcano 	writel(delta, timer_base + TIMER1_VAL);
73*9d8d47eaSDaniel Lezcano 	atomic_io_modify(timer_base + TIMER_CTRL,
74*9d8d47eaSDaniel Lezcano 		TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN);
75*9d8d47eaSDaniel Lezcano 
76*9d8d47eaSDaniel Lezcano 	return 0;
77*9d8d47eaSDaniel Lezcano }
78*9d8d47eaSDaniel Lezcano 
79*9d8d47eaSDaniel Lezcano static int orion_clkevt_shutdown(struct clock_event_device *dev)
80*9d8d47eaSDaniel Lezcano {
81*9d8d47eaSDaniel Lezcano 	/* disable timer */
82*9d8d47eaSDaniel Lezcano 	atomic_io_modify(timer_base + TIMER_CTRL,
83*9d8d47eaSDaniel Lezcano 			 TIMER1_RELOAD_EN | TIMER1_EN, 0);
84*9d8d47eaSDaniel Lezcano 	return 0;
85*9d8d47eaSDaniel Lezcano }
86*9d8d47eaSDaniel Lezcano 
87*9d8d47eaSDaniel Lezcano static int orion_clkevt_set_periodic(struct clock_event_device *dev)
88*9d8d47eaSDaniel Lezcano {
89*9d8d47eaSDaniel Lezcano 	/* setup and enable periodic timer at 1/HZ intervals */
90*9d8d47eaSDaniel Lezcano 	writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD);
91*9d8d47eaSDaniel Lezcano 	writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL);
92*9d8d47eaSDaniel Lezcano 	atomic_io_modify(timer_base + TIMER_CTRL,
93*9d8d47eaSDaniel Lezcano 			 TIMER1_RELOAD_EN | TIMER1_EN,
94*9d8d47eaSDaniel Lezcano 			 TIMER1_RELOAD_EN | TIMER1_EN);
95*9d8d47eaSDaniel Lezcano 	return 0;
96*9d8d47eaSDaniel Lezcano }
97*9d8d47eaSDaniel Lezcano 
98*9d8d47eaSDaniel Lezcano static struct clock_event_device orion_clkevt = {
99*9d8d47eaSDaniel Lezcano 	.name			= "orion_event",
100*9d8d47eaSDaniel Lezcano 	.features		= CLOCK_EVT_FEAT_ONESHOT |
101*9d8d47eaSDaniel Lezcano 				  CLOCK_EVT_FEAT_PERIODIC,
102*9d8d47eaSDaniel Lezcano 	.shift			= 32,
103*9d8d47eaSDaniel Lezcano 	.rating			= 300,
104*9d8d47eaSDaniel Lezcano 	.set_next_event		= orion_clkevt_next_event,
105*9d8d47eaSDaniel Lezcano 	.set_state_shutdown	= orion_clkevt_shutdown,
106*9d8d47eaSDaniel Lezcano 	.set_state_periodic	= orion_clkevt_set_periodic,
107*9d8d47eaSDaniel Lezcano 	.set_state_oneshot	= orion_clkevt_shutdown,
108*9d8d47eaSDaniel Lezcano 	.tick_resume		= orion_clkevt_shutdown,
109*9d8d47eaSDaniel Lezcano };
110*9d8d47eaSDaniel Lezcano 
111*9d8d47eaSDaniel Lezcano static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id)
112*9d8d47eaSDaniel Lezcano {
113*9d8d47eaSDaniel Lezcano 	orion_clkevt.event_handler(&orion_clkevt);
114*9d8d47eaSDaniel Lezcano 	return IRQ_HANDLED;
115*9d8d47eaSDaniel Lezcano }
116*9d8d47eaSDaniel Lezcano 
117*9d8d47eaSDaniel Lezcano static struct irqaction orion_clkevt_irq = {
118*9d8d47eaSDaniel Lezcano 	.name		= "orion_event",
119*9d8d47eaSDaniel Lezcano 	.flags		= IRQF_TIMER,
120*9d8d47eaSDaniel Lezcano 	.handler	= orion_clkevt_irq_handler,
121*9d8d47eaSDaniel Lezcano };
122*9d8d47eaSDaniel Lezcano 
123*9d8d47eaSDaniel Lezcano static int __init orion_timer_init(struct device_node *np)
124*9d8d47eaSDaniel Lezcano {
125*9d8d47eaSDaniel Lezcano 	unsigned long rate;
126*9d8d47eaSDaniel Lezcano 	struct clk *clk;
127*9d8d47eaSDaniel Lezcano 	int irq, ret;
128*9d8d47eaSDaniel Lezcano 
129*9d8d47eaSDaniel Lezcano 	/* timer registers are shared with watchdog timer */
130*9d8d47eaSDaniel Lezcano 	timer_base = of_iomap(np, 0);
131*9d8d47eaSDaniel Lezcano 	if (!timer_base) {
132*9d8d47eaSDaniel Lezcano 		pr_err("%pOFn: unable to map resource\n", np);
133*9d8d47eaSDaniel Lezcano 		return -ENXIO;
134*9d8d47eaSDaniel Lezcano 	}
135*9d8d47eaSDaniel Lezcano 
136*9d8d47eaSDaniel Lezcano 	clk = of_clk_get(np, 0);
137*9d8d47eaSDaniel Lezcano 	if (IS_ERR(clk)) {
138*9d8d47eaSDaniel Lezcano 		pr_err("%pOFn: unable to get clk\n", np);
139*9d8d47eaSDaniel Lezcano 		return PTR_ERR(clk);
140*9d8d47eaSDaniel Lezcano 	}
141*9d8d47eaSDaniel Lezcano 
142*9d8d47eaSDaniel Lezcano 	ret = clk_prepare_enable(clk);
143*9d8d47eaSDaniel Lezcano 	if (ret) {
144*9d8d47eaSDaniel Lezcano 		pr_err("Failed to prepare clock\n");
145*9d8d47eaSDaniel Lezcano 		return ret;
146*9d8d47eaSDaniel Lezcano 	}
147*9d8d47eaSDaniel Lezcano 
148*9d8d47eaSDaniel Lezcano 	/* we are only interested in timer1 irq */
149*9d8d47eaSDaniel Lezcano 	irq = irq_of_parse_and_map(np, 1);
150*9d8d47eaSDaniel Lezcano 	if (irq <= 0) {
151*9d8d47eaSDaniel Lezcano 		pr_err("%pOFn: unable to parse timer1 irq\n", np);
152*9d8d47eaSDaniel Lezcano 		return -EINVAL;
153*9d8d47eaSDaniel Lezcano 	}
154*9d8d47eaSDaniel Lezcano 
155*9d8d47eaSDaniel Lezcano 	rate = clk_get_rate(clk);
156*9d8d47eaSDaniel Lezcano 
157*9d8d47eaSDaniel Lezcano 	/* setup timer0 as free-running clocksource */
158*9d8d47eaSDaniel Lezcano 	writel(~0, timer_base + TIMER0_VAL);
159*9d8d47eaSDaniel Lezcano 	writel(~0, timer_base + TIMER0_RELOAD);
160*9d8d47eaSDaniel Lezcano 	atomic_io_modify(timer_base + TIMER_CTRL,
161*9d8d47eaSDaniel Lezcano 		TIMER0_RELOAD_EN | TIMER0_EN,
162*9d8d47eaSDaniel Lezcano 		TIMER0_RELOAD_EN | TIMER0_EN);
163*9d8d47eaSDaniel Lezcano 
164*9d8d47eaSDaniel Lezcano 	ret = clocksource_mmio_init(timer_base + TIMER0_VAL,
165*9d8d47eaSDaniel Lezcano 				    "orion_clocksource", rate, 300, 32,
166*9d8d47eaSDaniel Lezcano 				    clocksource_mmio_readl_down);
167*9d8d47eaSDaniel Lezcano 	if (ret) {
168*9d8d47eaSDaniel Lezcano 		pr_err("Failed to initialize mmio timer\n");
169*9d8d47eaSDaniel Lezcano 		return ret;
170*9d8d47eaSDaniel Lezcano 	}
171*9d8d47eaSDaniel Lezcano 
172*9d8d47eaSDaniel Lezcano 	sched_clock_register(orion_read_sched_clock, 32, rate);
173*9d8d47eaSDaniel Lezcano 
174*9d8d47eaSDaniel Lezcano 	/* setup timer1 as clockevent timer */
175*9d8d47eaSDaniel Lezcano 	ret = setup_irq(irq, &orion_clkevt_irq);
176*9d8d47eaSDaniel Lezcano 	if (ret) {
177*9d8d47eaSDaniel Lezcano 		pr_err("%pOFn: unable to setup irq\n", np);
178*9d8d47eaSDaniel Lezcano 		return ret;
179*9d8d47eaSDaniel Lezcano 	}
180*9d8d47eaSDaniel Lezcano 
181*9d8d47eaSDaniel Lezcano 	ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ;
182*9d8d47eaSDaniel Lezcano 	orion_clkevt.cpumask = cpumask_of(0);
183*9d8d47eaSDaniel Lezcano 	orion_clkevt.irq = irq;
184*9d8d47eaSDaniel Lezcano 	clockevents_config_and_register(&orion_clkevt, rate,
185*9d8d47eaSDaniel Lezcano 					ORION_ONESHOT_MIN, ORION_ONESHOT_MAX);
186*9d8d47eaSDaniel Lezcano 
187*9d8d47eaSDaniel Lezcano 
188*9d8d47eaSDaniel Lezcano 	orion_delay_timer_init(rate);
189*9d8d47eaSDaniel Lezcano 
190*9d8d47eaSDaniel Lezcano 	return 0;
191*9d8d47eaSDaniel Lezcano }
192*9d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);
193