xref: /openbmc/linux/arch/arm/plat-orion/time.c (revision 95e9fd10)
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 <asm/sched_clock.h>
20 
21 /*
22  * MBus bridge block registers.
23  */
24 #define BRIDGE_CAUSE_OFF	0x0110
25 #define BRIDGE_MASK_OFF		0x0114
26 #define  BRIDGE_INT_TIMER0	 0x0002
27 #define  BRIDGE_INT_TIMER1	 0x0004
28 
29 
30 /*
31  * Timer block registers.
32  */
33 #define TIMER_CTRL_OFF		0x0000
34 #define  TIMER0_EN		 0x0001
35 #define  TIMER0_RELOAD_EN	 0x0002
36 #define  TIMER1_EN		 0x0004
37 #define  TIMER1_RELOAD_EN	 0x0008
38 #define TIMER0_RELOAD_OFF	0x0010
39 #define TIMER0_VAL_OFF		0x0014
40 #define TIMER1_RELOAD_OFF	0x0018
41 #define TIMER1_VAL_OFF		0x001c
42 
43 
44 /*
45  * SoC-specific data.
46  */
47 static void __iomem *bridge_base;
48 static u32 bridge_timer1_clr_mask;
49 static void __iomem *timer_base;
50 
51 
52 /*
53  * Number of timer ticks per jiffy.
54  */
55 static u32 ticks_per_jiffy;
56 
57 
58 /*
59  * Orion's sched_clock implementation. It has a resolution of
60  * at least 7.5ns (133MHz TCLK).
61  */
62 
63 static u32 notrace orion_read_sched_clock(void)
64 {
65 	return ~readl(timer_base + TIMER0_VAL_OFF);
66 }
67 
68 /*
69  * Clockevent handling.
70  */
71 static int
72 orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev)
73 {
74 	unsigned long flags;
75 	u32 u;
76 
77 	if (delta == 0)
78 		return -ETIME;
79 
80 	local_irq_save(flags);
81 
82 	/*
83 	 * Clear and enable clockevent timer interrupt.
84 	 */
85 	writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
86 
87 	u = readl(bridge_base + BRIDGE_MASK_OFF);
88 	u |= BRIDGE_INT_TIMER1;
89 	writel(u, bridge_base + BRIDGE_MASK_OFF);
90 
91 	/*
92 	 * Setup new clockevent timer value.
93 	 */
94 	writel(delta, timer_base + TIMER1_VAL_OFF);
95 
96 	/*
97 	 * Enable the timer.
98 	 */
99 	u = readl(timer_base + TIMER_CTRL_OFF);
100 	u = (u & ~TIMER1_RELOAD_EN) | TIMER1_EN;
101 	writel(u, timer_base + TIMER_CTRL_OFF);
102 
103 	local_irq_restore(flags);
104 
105 	return 0;
106 }
107 
108 static void
109 orion_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
110 {
111 	unsigned long flags;
112 	u32 u;
113 
114 	local_irq_save(flags);
115 	if (mode == CLOCK_EVT_MODE_PERIODIC) {
116 		/*
117 		 * Setup timer to fire at 1/HZ intervals.
118 		 */
119 		writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD_OFF);
120 		writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL_OFF);
121 
122 		/*
123 		 * Enable timer interrupt.
124 		 */
125 		u = readl(bridge_base + BRIDGE_MASK_OFF);
126 		writel(u | BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
127 
128 		/*
129 		 * Enable timer.
130 		 */
131 		u = readl(timer_base + TIMER_CTRL_OFF);
132 		writel(u | TIMER1_EN | TIMER1_RELOAD_EN,
133 		       timer_base + TIMER_CTRL_OFF);
134 	} else {
135 		/*
136 		 * Disable timer.
137 		 */
138 		u = readl(timer_base + TIMER_CTRL_OFF);
139 		writel(u & ~TIMER1_EN, timer_base + TIMER_CTRL_OFF);
140 
141 		/*
142 		 * Disable timer interrupt.
143 		 */
144 		u = readl(bridge_base + BRIDGE_MASK_OFF);
145 		writel(u & ~BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
146 
147 		/*
148 		 * ACK pending timer interrupt.
149 		 */
150 		writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
151 
152 	}
153 	local_irq_restore(flags);
154 }
155 
156 static struct clock_event_device orion_clkevt = {
157 	.name		= "orion_tick",
158 	.features	= CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
159 	.shift		= 32,
160 	.rating		= 300,
161 	.set_next_event	= orion_clkevt_next_event,
162 	.set_mode	= orion_clkevt_mode,
163 };
164 
165 static irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
166 {
167 	/*
168 	 * ACK timer interrupt and call event handler.
169 	 */
170 	writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
171 	orion_clkevt.event_handler(&orion_clkevt);
172 
173 	return IRQ_HANDLED;
174 }
175 
176 static struct irqaction orion_timer_irq = {
177 	.name		= "orion_tick",
178 	.flags		= IRQF_DISABLED | IRQF_TIMER,
179 	.handler	= orion_timer_interrupt
180 };
181 
182 void __init
183 orion_time_set_base(u32 _timer_base)
184 {
185 	timer_base = (void __iomem *)_timer_base;
186 }
187 
188 void __init
189 orion_time_init(u32 _bridge_base, u32 _bridge_timer1_clr_mask,
190 		unsigned int irq, unsigned int tclk)
191 {
192 	u32 u;
193 
194 	/*
195 	 * Set SoC-specific data.
196 	 */
197 	bridge_base = (void __iomem *)_bridge_base;
198 	bridge_timer1_clr_mask = _bridge_timer1_clr_mask;
199 
200 	ticks_per_jiffy = (tclk + HZ/2) / HZ;
201 
202 	/*
203 	 * Set scale and timer for sched_clock.
204 	 */
205 	setup_sched_clock(orion_read_sched_clock, 32, tclk);
206 
207 	/*
208 	 * Setup free-running clocksource timer (interrupts
209 	 * disabled).
210 	 */
211 	writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
212 	writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
213 	u = readl(bridge_base + BRIDGE_MASK_OFF);
214 	writel(u & ~BRIDGE_INT_TIMER0, bridge_base + BRIDGE_MASK_OFF);
215 	u = readl(timer_base + TIMER_CTRL_OFF);
216 	writel(u | TIMER0_EN | TIMER0_RELOAD_EN, timer_base + TIMER_CTRL_OFF);
217 	clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, "orion_clocksource",
218 		tclk, 300, 32, clocksource_mmio_readl_down);
219 
220 	/*
221 	 * Setup clockevent timer (interrupt-driven).
222 	 */
223 	setup_irq(irq, &orion_timer_irq);
224 	orion_clkevt.mult = div_sc(tclk, NSEC_PER_SEC, orion_clkevt.shift);
225 	orion_clkevt.max_delta_ns = clockevent_delta2ns(0xfffffffe, &orion_clkevt);
226 	orion_clkevt.min_delta_ns = clockevent_delta2ns(1, &orion_clkevt);
227 	orion_clkevt.cpumask = cpumask_of(0);
228 	clockevents_register_device(&orion_clkevt);
229 }
230