timer.c revision 201468
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/econa/timer.c 201468 2010-01-04 03:35:45Z rpaulo $");
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/frame.h>
41#include <machine/intr.h>
42
43#include "econa_reg.h"
44#include "econa_var.h"
45
46#define	INITIAL_TIMECOUNTER	(0xffffffff)
47
48static int timers_initialized = 0;
49
50#define	HZ	100
51
52extern unsigned int CPU_clock;
53extern unsigned int AHB_clock;
54extern unsigned int APB_clock;
55
56static unsigned long timer_counter = 0;
57
58struct ec_timer_softc {
59	struct resource	*	timer_res[3];
60	bus_space_tag_t		timer_bst;
61	bus_space_handle_t	timer_bsh;
62	struct mtx		timer_mtx;
63};
64
65static struct resource_spec ec_timer_spec[] = {
66	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
67	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
68	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
69	{ -1, 0 }
70};
71
72static unsigned ec_timer_get_timecount(struct timecounter *);
73
74static struct timecounter ec_timecounter = {
75	.tc_get_timecount = ec_timer_get_timecount,
76	.tc_name = "CPU Timer",
77	/* This is assigned on the fly in the init sequence */
78	.tc_frequency = 0,
79	.tc_counter_mask = ~0u,
80	.tc_quality = 1000,
81};
82
83static struct ec_timer_softc *timer_softc = NULL;
84
85static inline
86void write_4(unsigned int val, unsigned int addr)
87{
88	bus_space_write_4(timer_softc->timer_bst,
89			  timer_softc->timer_bsh, addr, val);
90
91}
92
93static inline
94unsigned int read_4(unsigned int addr)
95{
96
97	return bus_space_read_4(timer_softc->timer_bst,
98	    timer_softc->timer_bsh, addr);
99}
100
101#define	uSECS_PER_TICK	(1000000 / APB_clock)
102#define	TICKS2USECS(x)	((x) * uSECS_PER_TICK)
103
104static unsigned
105read_timer_counter_noint(void)
106{
107
108	arm_mask_irq(0);
109	unsigned int v = read_4(TIMER_TM1_COUNTER_REG);
110	arm_unmask_irq(0);
111	return v;
112}
113
114void
115DELAY(int usec)
116{
117	uint32_t val, val_temp;
118	int nticks;
119
120	if (!timers_initialized) {
121		for (; usec > 0; usec--)
122			for (val = 100; val > 0; val--)
123				;
124		return;
125	}
126
127	val = read_timer_counter_noint();
128	nticks = (((APB_clock / 1000) * usec) / 1000) + 100;
129
130	while (nticks > 0) {
131		val_temp = read_timer_counter_noint();
132		if (val > val_temp)
133			nticks -= (val - val_temp);
134		else
135			nticks -= (val + (timer_counter - val_temp));
136
137		val = val_temp;
138	}
139
140}
141
142/*
143 * Setup timer
144 */
145static inline void
146setup_timer(unsigned int counter_value)
147{
148	unsigned int control_value;
149	unsigned int mask_value;
150
151	control_value = read_4(TIMER_TM_CR_REG);
152
153	mask_value = read_4(TIMER_TM_INTR_MASK_REG);
154	write_4(counter_value, TIMER_TM1_COUNTER_REG);
155	write_4(counter_value, TIMER_TM1_LOAD_REG);
156	write_4(0, TIMER_TM1_MATCH1_REG);
157	write_4(0,TIMER_TM1_MATCH2_REG);
158
159	control_value &= ~(TIMER1_CLOCK_SOURCE);
160	control_value |= TIMER1_UP_DOWN_COUNT;
161
162	write_4(0, TIMER_TM2_COUNTER_REG);
163	write_4(0, TIMER_TM2_LOAD_REG);
164	write_4(~0u, TIMER_TM2_MATCH1_REG);
165	write_4(~0u,TIMER_TM2_MATCH2_REG);
166
167	control_value &= ~(TIMER2_CLOCK_SOURCE);
168	control_value &= ~(TIMER2_UP_DOWN_COUNT);
169
170	mask_value &= ~(63);
171
172	write_4(control_value, TIMER_TM_CR_REG);
173	write_4(mask_value, TIMER_TM_INTR_MASK_REG);
174}
175
176/*
177 * Enable timer
178 */
179static inline void
180timer_enable(void)
181{
182	unsigned int control_value;
183
184	control_value = read_4(TIMER_TM_CR_REG);
185
186	control_value |= TIMER1_OVERFLOW_ENABLE;
187	control_value |= TIMER1_ENABLE;
188	control_value |= TIMER2_OVERFLOW_ENABLE;
189	control_value |= TIMER2_ENABLE;
190
191	write_4(control_value, TIMER_TM_CR_REG);
192}
193
194static inline unsigned int
195read_second_timer_counter(void)
196{
197
198	return read_4(TIMER_TM2_COUNTER_REG);
199}
200
201/*
202 * Get timer interrupt status
203 */
204static inline unsigned int
205read_timer_interrupt_status(void)
206{
207
208	return read_4(TIMER_TM_INTR_STATUS_REG);
209}
210
211/*
212 * Clear timer interrupt status
213 */
214static inline void
215clear_timer_interrupt_status(unsigned int irq)
216{
217	unsigned int interrupt_status;
218
219	interrupt_status =   read_4(TIMER_TM_INTR_STATUS_REG);
220	if (irq == 0) {
221		if (interrupt_status & (TIMER1_MATCH1_INTR))
222			interrupt_status &= ~(TIMER1_MATCH1_INTR);
223		if (interrupt_status & (TIMER1_MATCH2_INTR))
224			interrupt_status &= ~(TIMER1_MATCH2_INTR);
225		if (interrupt_status & (TIMER1_OVERFLOW_INTR))
226			interrupt_status &= ~(TIMER1_OVERFLOW_INTR);
227	}
228	if (irq == 1) {
229		if (interrupt_status & (TIMER2_MATCH1_INTR))
230			interrupt_status &= ~(TIMER2_MATCH1_INTR);
231		if (interrupt_status & (TIMER2_MATCH2_INTR))
232			interrupt_status &= ~(TIMER2_MATCH2_INTR);
233		if (interrupt_status & (TIMER2_OVERFLOW_INTR))
234			interrupt_status &= ~(TIMER2_OVERFLOW_INTR);
235	}
236
237	write_4(interrupt_status, TIMER_TM_INTR_STATUS_REG);
238}
239
240static unsigned
241ec_timer_get_timecount(struct timecounter *a)
242{
243	unsigned int ticks1;
244	arm_mask_irq(1);
245	ticks1 = read_second_timer_counter();
246	arm_unmask_irq(1);
247	return ticks1;
248}
249
250/*
251 * Setup timer
252 */
253static inline void
254do_setup_timer(void)
255{
256
257	timer_counter = APB_clock/HZ;
258	/*
259	 * setup timer-related values
260	 */
261	setup_timer(timer_counter);
262}
263
264void
265cpu_initclocks(void)
266{
267
268	ec_timecounter.tc_frequency = APB_clock;
269	tc_init(&ec_timecounter);
270	timer_enable();
271	timers_initialized = 1;
272}
273
274void
275cpu_startprofclock(void)
276{
277
278}
279
280void
281cpu_stopprofclock(void)
282{
283
284}
285
286static int
287ec_timer_probe(device_t dev)
288{
289
290	device_set_desc(dev, "Econa CPU Timer");
291	return (0);
292}
293
294static int
295ec_reset(void *arg)
296{
297
298	arm_mask_irq(1);
299	clear_timer_interrupt_status(1);
300	arm_unmask_irq(1);
301	return (FILTER_HANDLED);
302}
303
304static int
305ec_hardclock(void *arg)
306{
307	struct	trapframe *frame;
308	unsigned int val;
309	/*clear timer interrupt status*/
310
311	arm_mask_irq(0);
312
313	val = read_4(TIMER_INTERRUPT_STATUS_REG);
314	val &= ~(TIMER1_OVERFLOW_INTERRUPT);
315	write_4(val, TIMER_INTERRUPT_STATUS_REG);
316
317	frame = (struct trapframe *)arg;
318	hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
319
320	arm_unmask_irq(0);
321
322	return (FILTER_HANDLED);
323}
324
325static int
326ec_timer_attach(device_t dev)
327{
328	struct	ec_timer_softc *sc;
329	int	error;
330	void	*ihl;
331
332
333	if (timer_softc != NULL)
334		return (ENXIO);
335
336	sc = (struct ec_timer_softc *)device_get_softc(dev);
337
338	timer_softc = sc;
339
340	error = bus_alloc_resources(dev, ec_timer_spec, sc->timer_res);
341	if (error) {
342		device_printf(dev, "could not allocate resources\n");
343		return (ENXIO);
344	}
345
346	sc->timer_bst = rman_get_bustag(sc->timer_res[0]);
347	sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]);
348
349	do_setup_timer();
350
351	if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK,
352	    ec_hardclock, NULL, NULL, &ihl) != 0) {
353		bus_release_resources(dev, ec_timer_spec, sc->timer_res);
354		device_printf(dev, "could not setup hardclock interrupt\n");
355		return (ENXIO);
356	}
357
358	if (bus_setup_intr(dev, sc->timer_res[2], INTR_TYPE_CLK,
359	    ec_reset, NULL, NULL, &ihl) != 0) {
360		bus_release_resources(dev, ec_timer_spec, sc->timer_res);
361		device_printf(dev, "could not setup timer interrupt\n");
362		return (ENXIO);
363	}
364
365	return (0);
366}
367
368static device_method_t ec_timer_methods[] = {
369	DEVMETHOD(device_probe, ec_timer_probe),
370	DEVMETHOD(device_attach, ec_timer_attach),
371	{ 0, 0 }
372};
373
374static driver_t ec_timer_driver = {
375	"timer",
376	ec_timer_methods,
377	sizeof(struct ec_timer_softc),
378};
379
380static devclass_t ec_timer_devclass;
381
382DRIVER_MODULE(timer, econaarm, ec_timer_driver, ec_timer_devclass, 0, 0);
383