timer.c revision 264219
1/*-
2 * Copyright (c) 2009 Yohanes Nugroho <yohanes@gmail.com>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: head/sys/arm/cavium/cns11xx/timer.c 257200 2013-10-27 01:34:10Z ian $");
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/bus.h>
32#include <sys/kernel.h>
33#include <sys/module.h>
34#include <sys/malloc.h>
35#include <sys/rman.h>
36#include <sys/timetc.h>
37#include <sys/watchdog.h>
38#include <machine/bus.h>
39#include <machine/cpu.h>
40#include <machine/intr.h>
41
42#include "econa_reg.h"
43#include "econa_var.h"
44
45#define	INITIAL_TIMECOUNTER	(0xffffffff)
46
47static int timers_initialized = 0;
48
49#define	HZ	100
50
51extern unsigned int CPU_clock;
52extern unsigned int AHB_clock;
53extern unsigned int APB_clock;
54
55static unsigned long timer_counter = 0;
56
57struct ec_timer_softc {
58	struct resource	*	timer_res[3];
59	bus_space_tag_t		timer_bst;
60	bus_space_handle_t	timer_bsh;
61	struct mtx		timer_mtx;
62};
63
64static struct resource_spec ec_timer_spec[] = {
65	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
66	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
67	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
68	{ -1, 0 }
69};
70
71static unsigned ec_timer_get_timecount(struct timecounter *);
72
73static struct timecounter ec_timecounter = {
74	.tc_get_timecount = ec_timer_get_timecount,
75	.tc_name = "CPU Timer",
76	/* This is assigned on the fly in the init sequence */
77	.tc_frequency = 0,
78	.tc_counter_mask = ~0u,
79	.tc_quality = 1000,
80};
81
82static struct ec_timer_softc *timer_softc = NULL;
83
84static inline
85void write_4(unsigned int val, unsigned int addr)
86{
87	bus_space_write_4(timer_softc->timer_bst,
88			  timer_softc->timer_bsh, addr, val);
89
90}
91
92static inline
93unsigned int read_4(unsigned int addr)
94{
95
96	return bus_space_read_4(timer_softc->timer_bst,
97	    timer_softc->timer_bsh, addr);
98}
99
100#define	uSECS_PER_TICK	(1000000 / APB_clock)
101#define	TICKS2USECS(x)	((x) * uSECS_PER_TICK)
102
103static unsigned
104read_timer_counter_noint(void)
105{
106
107	arm_mask_irq(0);
108	unsigned int v = read_4(TIMER_TM1_COUNTER_REG);
109	arm_unmask_irq(0);
110	return v;
111}
112
113void
114DELAY(int usec)
115{
116	uint32_t val, val_temp;
117	int nticks;
118
119	if (!timers_initialized) {
120		for (; usec > 0; usec--)
121			for (val = 100; val > 0; val--)
122				;
123		return;
124	}
125
126	val = read_timer_counter_noint();
127	nticks = (((APB_clock / 1000) * usec) / 1000) + 100;
128
129	while (nticks > 0) {
130		val_temp = read_timer_counter_noint();
131		if (val > val_temp)
132			nticks -= (val - val_temp);
133		else
134			nticks -= (val + (timer_counter - val_temp));
135
136		val = val_temp;
137	}
138
139}
140
141/*
142 * Setup timer
143 */
144static inline void
145setup_timer(unsigned int counter_value)
146{
147	unsigned int control_value;
148	unsigned int mask_value;
149
150	control_value = read_4(TIMER_TM_CR_REG);
151
152	mask_value = read_4(TIMER_TM_INTR_MASK_REG);
153	write_4(counter_value, TIMER_TM1_COUNTER_REG);
154	write_4(counter_value, TIMER_TM1_LOAD_REG);
155	write_4(0, TIMER_TM1_MATCH1_REG);
156	write_4(0,TIMER_TM1_MATCH2_REG);
157
158	control_value &= ~(TIMER1_CLOCK_SOURCE);
159	control_value |= TIMER1_UP_DOWN_COUNT;
160
161	write_4(0, TIMER_TM2_COUNTER_REG);
162	write_4(0, TIMER_TM2_LOAD_REG);
163	write_4(~0u, TIMER_TM2_MATCH1_REG);
164	write_4(~0u,TIMER_TM2_MATCH2_REG);
165
166	control_value &= ~(TIMER2_CLOCK_SOURCE);
167	control_value &= ~(TIMER2_UP_DOWN_COUNT);
168
169	mask_value &= ~(63);
170
171	write_4(control_value, TIMER_TM_CR_REG);
172	write_4(mask_value, TIMER_TM_INTR_MASK_REG);
173}
174
175/*
176 * Enable timer
177 */
178static inline void
179timer_enable(void)
180{
181	unsigned int control_value;
182
183	control_value = read_4(TIMER_TM_CR_REG);
184
185	control_value |= TIMER1_OVERFLOW_ENABLE;
186	control_value |= TIMER1_ENABLE;
187	control_value |= TIMER2_OVERFLOW_ENABLE;
188	control_value |= TIMER2_ENABLE;
189
190	write_4(control_value, TIMER_TM_CR_REG);
191}
192
193static inline unsigned int
194read_second_timer_counter(void)
195{
196
197	return read_4(TIMER_TM2_COUNTER_REG);
198}
199
200/*
201 * Get timer interrupt status
202 */
203static inline unsigned int
204read_timer_interrupt_status(void)
205{
206
207	return read_4(TIMER_TM_INTR_STATUS_REG);
208}
209
210/*
211 * Clear timer interrupt status
212 */
213static inline void
214clear_timer_interrupt_status(unsigned int irq)
215{
216	unsigned int interrupt_status;
217
218	interrupt_status =   read_4(TIMER_TM_INTR_STATUS_REG);
219	if (irq == 0) {
220		if (interrupt_status & (TIMER1_MATCH1_INTR))
221			interrupt_status &= ~(TIMER1_MATCH1_INTR);
222		if (interrupt_status & (TIMER1_MATCH2_INTR))
223			interrupt_status &= ~(TIMER1_MATCH2_INTR);
224		if (interrupt_status & (TIMER1_OVERFLOW_INTR))
225			interrupt_status &= ~(TIMER1_OVERFLOW_INTR);
226	}
227	if (irq == 1) {
228		if (interrupt_status & (TIMER2_MATCH1_INTR))
229			interrupt_status &= ~(TIMER2_MATCH1_INTR);
230		if (interrupt_status & (TIMER2_MATCH2_INTR))
231			interrupt_status &= ~(TIMER2_MATCH2_INTR);
232		if (interrupt_status & (TIMER2_OVERFLOW_INTR))
233			interrupt_status &= ~(TIMER2_OVERFLOW_INTR);
234	}
235
236	write_4(interrupt_status, TIMER_TM_INTR_STATUS_REG);
237}
238
239static unsigned
240ec_timer_get_timecount(struct timecounter *a)
241{
242	unsigned int ticks1;
243	arm_mask_irq(1);
244	ticks1 = read_second_timer_counter();
245	arm_unmask_irq(1);
246	return ticks1;
247}
248
249/*
250 * Setup timer
251 */
252static inline void
253do_setup_timer(void)
254{
255
256	timer_counter = APB_clock/HZ;
257	/*
258	 * setup timer-related values
259	 */
260	setup_timer(timer_counter);
261}
262
263void
264cpu_initclocks(void)
265{
266
267	ec_timecounter.tc_frequency = APB_clock;
268	tc_init(&ec_timecounter);
269	timer_enable();
270	timers_initialized = 1;
271}
272
273void
274cpu_startprofclock(void)
275{
276
277}
278
279void
280cpu_stopprofclock(void)
281{
282
283}
284
285static int
286ec_timer_probe(device_t dev)
287{
288
289	device_set_desc(dev, "Econa CPU Timer");
290	return (0);
291}
292
293static int
294ec_reset(void *arg)
295{
296
297	arm_mask_irq(1);
298	clear_timer_interrupt_status(1);
299	arm_unmask_irq(1);
300	return (FILTER_HANDLED);
301}
302
303static int
304ec_hardclock(void *arg)
305{
306	struct	trapframe *frame;
307	unsigned int val;
308	/*clear timer interrupt status*/
309
310	arm_mask_irq(0);
311
312	val = read_4(TIMER_INTERRUPT_STATUS_REG);
313	val &= ~(TIMER1_OVERFLOW_INTERRUPT);
314	write_4(val, TIMER_INTERRUPT_STATUS_REG);
315
316	frame = (struct trapframe *)arg;
317	hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
318
319	arm_unmask_irq(0);
320
321	return (FILTER_HANDLED);
322}
323
324static int
325ec_timer_attach(device_t dev)
326{
327	struct	ec_timer_softc *sc;
328	int	error;
329	void	*ihl;
330
331
332	if (timer_softc != NULL)
333		return (ENXIO);
334
335	sc = (struct ec_timer_softc *)device_get_softc(dev);
336
337	timer_softc = sc;
338
339	error = bus_alloc_resources(dev, ec_timer_spec, sc->timer_res);
340	if (error) {
341		device_printf(dev, "could not allocate resources\n");
342		return (ENXIO);
343	}
344
345	sc->timer_bst = rman_get_bustag(sc->timer_res[0]);
346	sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]);
347
348	do_setup_timer();
349
350	if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK,
351	    ec_hardclock, NULL, NULL, &ihl) != 0) {
352		bus_release_resources(dev, ec_timer_spec, sc->timer_res);
353		device_printf(dev, "could not setup hardclock interrupt\n");
354		return (ENXIO);
355	}
356
357	if (bus_setup_intr(dev, sc->timer_res[2], INTR_TYPE_CLK,
358	    ec_reset, NULL, NULL, &ihl) != 0) {
359		bus_release_resources(dev, ec_timer_spec, sc->timer_res);
360		device_printf(dev, "could not setup timer interrupt\n");
361		return (ENXIO);
362	}
363
364	return (0);
365}
366
367static device_method_t ec_timer_methods[] = {
368	DEVMETHOD(device_probe, ec_timer_probe),
369	DEVMETHOD(device_attach, ec_timer_attach),
370	{ 0, 0 }
371};
372
373static driver_t ec_timer_driver = {
374	"timer",
375	ec_timer_methods,
376	sizeof(struct ec_timer_softc),
377};
378
379static devclass_t ec_timer_devclass;
380
381DRIVER_MODULE(timer, econaarm, ec_timer_driver, ec_timer_devclass, 0, 0);
382