1201468Srpaulo/*-
2201468Srpaulo * Copyright (c) 2009 Yohanes Nugroho <yohanes@gmail.com>.
3201468Srpaulo * All rights reserved.
4201468Srpaulo *
5201468Srpaulo * Redistribution and use in source and binary forms, with or without
6201468Srpaulo * modification, are permitted provided that the following conditions
7201468Srpaulo * are met:
8201468Srpaulo * 1. Redistributions of source code must retain the above copyright
9201468Srpaulo *    notice, this list of conditions and the following disclaimer.
10201468Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
11201468Srpaulo *    notice, this list of conditions and the following disclaimer in the
12201468Srpaulo *    documentation and/or other materials provided with the distribution.
13201468Srpaulo *
14201468Srpaulo * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15201468Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16201468Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17201468Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18201468Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19201468Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20201468Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21201468Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22201468Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23201468Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24201468Srpaulo * SUCH DAMAGE.
25201468Srpaulo */
26201468Srpaulo#include <sys/cdefs.h>
27201468Srpaulo__FBSDID("$FreeBSD$");
28201468Srpaulo
29201468Srpaulo#include <sys/param.h>
30201468Srpaulo#include <sys/systm.h>
31201468Srpaulo#include <sys/bus.h>
32201468Srpaulo#include <sys/kernel.h>
33201468Srpaulo#include <sys/module.h>
34201468Srpaulo#include <sys/malloc.h>
35201468Srpaulo#include <sys/rman.h>
36201468Srpaulo#include <sys/timetc.h>
37201468Srpaulo#include <sys/watchdog.h>
38201468Srpaulo#include <machine/bus.h>
39201468Srpaulo#include <machine/cpu.h>
40201468Srpaulo#include <machine/intr.h>
41201468Srpaulo
42201468Srpaulo#include "econa_reg.h"
43201468Srpaulo#include "econa_var.h"
44201468Srpaulo
45201468Srpaulo#define	INITIAL_TIMECOUNTER	(0xffffffff)
46201468Srpaulo
47201468Srpaulostatic int timers_initialized = 0;
48201468Srpaulo
49201468Srpaulo#define	HZ	100
50201468Srpaulo
51201468Srpauloextern unsigned int CPU_clock;
52201468Srpauloextern unsigned int AHB_clock;
53201468Srpauloextern unsigned int APB_clock;
54201468Srpaulo
55201468Srpaulostatic unsigned long timer_counter = 0;
56201468Srpaulo
57201468Srpaulostruct ec_timer_softc {
58201468Srpaulo	struct resource	*	timer_res[3];
59201468Srpaulo	bus_space_tag_t		timer_bst;
60201468Srpaulo	bus_space_handle_t	timer_bsh;
61201468Srpaulo	struct mtx		timer_mtx;
62201468Srpaulo};
63201468Srpaulo
64201468Srpaulostatic struct resource_spec ec_timer_spec[] = {
65201468Srpaulo	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
66201468Srpaulo	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
67201468Srpaulo	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
68201468Srpaulo	{ -1, 0 }
69201468Srpaulo};
70201468Srpaulo
71201468Srpaulostatic unsigned ec_timer_get_timecount(struct timecounter *);
72201468Srpaulo
73201468Srpaulostatic struct timecounter ec_timecounter = {
74201468Srpaulo	.tc_get_timecount = ec_timer_get_timecount,
75201468Srpaulo	.tc_name = "CPU Timer",
76201468Srpaulo	/* This is assigned on the fly in the init sequence */
77201468Srpaulo	.tc_frequency = 0,
78201468Srpaulo	.tc_counter_mask = ~0u,
79201468Srpaulo	.tc_quality = 1000,
80201468Srpaulo};
81201468Srpaulo
82201468Srpaulostatic struct ec_timer_softc *timer_softc = NULL;
83201468Srpaulo
84201468Srpaulostatic inline
85201468Srpaulovoid write_4(unsigned int val, unsigned int addr)
86201468Srpaulo{
87201468Srpaulo	bus_space_write_4(timer_softc->timer_bst,
88201468Srpaulo			  timer_softc->timer_bsh, addr, val);
89201468Srpaulo
90201468Srpaulo}
91201468Srpaulo
92201468Srpaulostatic inline
93201468Srpaulounsigned int read_4(unsigned int addr)
94201468Srpaulo{
95201468Srpaulo
96201468Srpaulo	return bus_space_read_4(timer_softc->timer_bst,
97201468Srpaulo	    timer_softc->timer_bsh, addr);
98201468Srpaulo}
99201468Srpaulo
100201468Srpaulo#define	uSECS_PER_TICK	(1000000 / APB_clock)
101201468Srpaulo#define	TICKS2USECS(x)	((x) * uSECS_PER_TICK)
102201468Srpaulo
103201468Srpaulostatic unsigned
104201468Srpauloread_timer_counter_noint(void)
105201468Srpaulo{
106201468Srpaulo
107201468Srpaulo	arm_mask_irq(0);
108201468Srpaulo	unsigned int v = read_4(TIMER_TM1_COUNTER_REG);
109201468Srpaulo	arm_unmask_irq(0);
110201468Srpaulo	return v;
111201468Srpaulo}
112201468Srpaulo
113201468Srpaulovoid
114201468SrpauloDELAY(int usec)
115201468Srpaulo{
116201468Srpaulo	uint32_t val, val_temp;
117201468Srpaulo	int nticks;
118201468Srpaulo
119201468Srpaulo	if (!timers_initialized) {
120201468Srpaulo		for (; usec > 0; usec--)
121201468Srpaulo			for (val = 100; val > 0; val--)
122201468Srpaulo				;
123201468Srpaulo		return;
124201468Srpaulo	}
125201468Srpaulo
126201468Srpaulo	val = read_timer_counter_noint();
127201468Srpaulo	nticks = (((APB_clock / 1000) * usec) / 1000) + 100;
128201468Srpaulo
129201468Srpaulo	while (nticks > 0) {
130201468Srpaulo		val_temp = read_timer_counter_noint();
131201468Srpaulo		if (val > val_temp)
132201468Srpaulo			nticks -= (val - val_temp);
133201468Srpaulo		else
134201468Srpaulo			nticks -= (val + (timer_counter - val_temp));
135201468Srpaulo
136201468Srpaulo		val = val_temp;
137201468Srpaulo	}
138201468Srpaulo
139201468Srpaulo}
140201468Srpaulo
141201468Srpaulo/*
142201468Srpaulo * Setup timer
143201468Srpaulo */
144201468Srpaulostatic inline void
145201468Srpaulosetup_timer(unsigned int counter_value)
146201468Srpaulo{
147201468Srpaulo	unsigned int control_value;
148201468Srpaulo	unsigned int mask_value;
149201468Srpaulo
150201468Srpaulo	control_value = read_4(TIMER_TM_CR_REG);
151201468Srpaulo
152201468Srpaulo	mask_value = read_4(TIMER_TM_INTR_MASK_REG);
153201468Srpaulo	write_4(counter_value, TIMER_TM1_COUNTER_REG);
154201468Srpaulo	write_4(counter_value, TIMER_TM1_LOAD_REG);
155201468Srpaulo	write_4(0, TIMER_TM1_MATCH1_REG);
156201468Srpaulo	write_4(0,TIMER_TM1_MATCH2_REG);
157201468Srpaulo
158201468Srpaulo	control_value &= ~(TIMER1_CLOCK_SOURCE);
159201468Srpaulo	control_value |= TIMER1_UP_DOWN_COUNT;
160201468Srpaulo
161201468Srpaulo	write_4(0, TIMER_TM2_COUNTER_REG);
162201468Srpaulo	write_4(0, TIMER_TM2_LOAD_REG);
163201468Srpaulo	write_4(~0u, TIMER_TM2_MATCH1_REG);
164201468Srpaulo	write_4(~0u,TIMER_TM2_MATCH2_REG);
165201468Srpaulo
166201468Srpaulo	control_value &= ~(TIMER2_CLOCK_SOURCE);
167201468Srpaulo	control_value &= ~(TIMER2_UP_DOWN_COUNT);
168201468Srpaulo
169201468Srpaulo	mask_value &= ~(63);
170201468Srpaulo
171201468Srpaulo	write_4(control_value, TIMER_TM_CR_REG);
172201468Srpaulo	write_4(mask_value, TIMER_TM_INTR_MASK_REG);
173201468Srpaulo}
174201468Srpaulo
175201468Srpaulo/*
176201468Srpaulo * Enable timer
177201468Srpaulo */
178201468Srpaulostatic inline void
179201468Srpaulotimer_enable(void)
180201468Srpaulo{
181201468Srpaulo	unsigned int control_value;
182201468Srpaulo
183201468Srpaulo	control_value = read_4(TIMER_TM_CR_REG);
184201468Srpaulo
185201468Srpaulo	control_value |= TIMER1_OVERFLOW_ENABLE;
186201468Srpaulo	control_value |= TIMER1_ENABLE;
187201468Srpaulo	control_value |= TIMER2_OVERFLOW_ENABLE;
188201468Srpaulo	control_value |= TIMER2_ENABLE;
189201468Srpaulo
190201468Srpaulo	write_4(control_value, TIMER_TM_CR_REG);
191201468Srpaulo}
192201468Srpaulo
193201468Srpaulostatic inline unsigned int
194201468Srpauloread_second_timer_counter(void)
195201468Srpaulo{
196201468Srpaulo
197201468Srpaulo	return read_4(TIMER_TM2_COUNTER_REG);
198201468Srpaulo}
199201468Srpaulo
200201468Srpaulo/*
201201468Srpaulo * Get timer interrupt status
202201468Srpaulo */
203201468Srpaulostatic inline unsigned int
204201468Srpauloread_timer_interrupt_status(void)
205201468Srpaulo{
206201468Srpaulo
207201468Srpaulo	return read_4(TIMER_TM_INTR_STATUS_REG);
208201468Srpaulo}
209201468Srpaulo
210201468Srpaulo/*
211201468Srpaulo * Clear timer interrupt status
212201468Srpaulo */
213201468Srpaulostatic inline void
214201468Srpauloclear_timer_interrupt_status(unsigned int irq)
215201468Srpaulo{
216201468Srpaulo	unsigned int interrupt_status;
217201468Srpaulo
218201468Srpaulo	interrupt_status =   read_4(TIMER_TM_INTR_STATUS_REG);
219201468Srpaulo	if (irq == 0) {
220201468Srpaulo		if (interrupt_status & (TIMER1_MATCH1_INTR))
221201468Srpaulo			interrupt_status &= ~(TIMER1_MATCH1_INTR);
222201468Srpaulo		if (interrupt_status & (TIMER1_MATCH2_INTR))
223201468Srpaulo			interrupt_status &= ~(TIMER1_MATCH2_INTR);
224201468Srpaulo		if (interrupt_status & (TIMER1_OVERFLOW_INTR))
225201468Srpaulo			interrupt_status &= ~(TIMER1_OVERFLOW_INTR);
226201468Srpaulo	}
227201468Srpaulo	if (irq == 1) {
228201468Srpaulo		if (interrupt_status & (TIMER2_MATCH1_INTR))
229201468Srpaulo			interrupt_status &= ~(TIMER2_MATCH1_INTR);
230201468Srpaulo		if (interrupt_status & (TIMER2_MATCH2_INTR))
231201468Srpaulo			interrupt_status &= ~(TIMER2_MATCH2_INTR);
232201468Srpaulo		if (interrupt_status & (TIMER2_OVERFLOW_INTR))
233201468Srpaulo			interrupt_status &= ~(TIMER2_OVERFLOW_INTR);
234201468Srpaulo	}
235201468Srpaulo
236201468Srpaulo	write_4(interrupt_status, TIMER_TM_INTR_STATUS_REG);
237201468Srpaulo}
238201468Srpaulo
239201468Srpaulostatic unsigned
240201468Srpauloec_timer_get_timecount(struct timecounter *a)
241201468Srpaulo{
242201468Srpaulo	unsigned int ticks1;
243201468Srpaulo	arm_mask_irq(1);
244201468Srpaulo	ticks1 = read_second_timer_counter();
245201468Srpaulo	arm_unmask_irq(1);
246201468Srpaulo	return ticks1;
247201468Srpaulo}
248201468Srpaulo
249201468Srpaulo/*
250201468Srpaulo * Setup timer
251201468Srpaulo */
252201468Srpaulostatic inline void
253201468Srpaulodo_setup_timer(void)
254201468Srpaulo{
255201468Srpaulo
256201468Srpaulo	timer_counter = APB_clock/HZ;
257201468Srpaulo	/*
258201468Srpaulo	 * setup timer-related values
259201468Srpaulo	 */
260201468Srpaulo	setup_timer(timer_counter);
261201468Srpaulo}
262201468Srpaulo
263201468Srpaulovoid
264201468Srpaulocpu_initclocks(void)
265201468Srpaulo{
266201468Srpaulo
267201468Srpaulo	ec_timecounter.tc_frequency = APB_clock;
268201468Srpaulo	tc_init(&ec_timecounter);
269201468Srpaulo	timer_enable();
270201468Srpaulo	timers_initialized = 1;
271201468Srpaulo}
272201468Srpaulo
273201468Srpaulovoid
274201468Srpaulocpu_startprofclock(void)
275201468Srpaulo{
276201468Srpaulo
277201468Srpaulo}
278201468Srpaulo
279201468Srpaulovoid
280201468Srpaulocpu_stopprofclock(void)
281201468Srpaulo{
282201468Srpaulo
283201468Srpaulo}
284201468Srpaulo
285201468Srpaulostatic int
286201468Srpauloec_timer_probe(device_t dev)
287201468Srpaulo{
288201468Srpaulo
289201468Srpaulo	device_set_desc(dev, "Econa CPU Timer");
290201468Srpaulo	return (0);
291201468Srpaulo}
292201468Srpaulo
293201468Srpaulostatic int
294201468Srpauloec_reset(void *arg)
295201468Srpaulo{
296201468Srpaulo
297201468Srpaulo	arm_mask_irq(1);
298201468Srpaulo	clear_timer_interrupt_status(1);
299201468Srpaulo	arm_unmask_irq(1);
300201468Srpaulo	return (FILTER_HANDLED);
301201468Srpaulo}
302201468Srpaulo
303201468Srpaulostatic int
304201468Srpauloec_hardclock(void *arg)
305201468Srpaulo{
306201468Srpaulo	struct	trapframe *frame;
307201468Srpaulo	unsigned int val;
308201468Srpaulo	/*clear timer interrupt status*/
309201468Srpaulo
310201468Srpaulo	arm_mask_irq(0);
311201468Srpaulo
312201468Srpaulo	val = read_4(TIMER_INTERRUPT_STATUS_REG);
313201468Srpaulo	val &= ~(TIMER1_OVERFLOW_INTERRUPT);
314201468Srpaulo	write_4(val, TIMER_INTERRUPT_STATUS_REG);
315201468Srpaulo
316201468Srpaulo	frame = (struct trapframe *)arg;
317201468Srpaulo	hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
318201468Srpaulo
319201468Srpaulo	arm_unmask_irq(0);
320201468Srpaulo
321201468Srpaulo	return (FILTER_HANDLED);
322201468Srpaulo}
323201468Srpaulo
324201468Srpaulostatic int
325201468Srpauloec_timer_attach(device_t dev)
326201468Srpaulo{
327201468Srpaulo	struct	ec_timer_softc *sc;
328201468Srpaulo	int	error;
329201468Srpaulo	void	*ihl;
330201468Srpaulo
331201468Srpaulo
332201468Srpaulo	if (timer_softc != NULL)
333201468Srpaulo		return (ENXIO);
334201468Srpaulo
335201468Srpaulo	sc = (struct ec_timer_softc *)device_get_softc(dev);
336201468Srpaulo
337201468Srpaulo	timer_softc = sc;
338201468Srpaulo
339201468Srpaulo	error = bus_alloc_resources(dev, ec_timer_spec, sc->timer_res);
340201468Srpaulo	if (error) {
341201468Srpaulo		device_printf(dev, "could not allocate resources\n");
342201468Srpaulo		return (ENXIO);
343201468Srpaulo	}
344201468Srpaulo
345201468Srpaulo	sc->timer_bst = rman_get_bustag(sc->timer_res[0]);
346201468Srpaulo	sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]);
347201468Srpaulo
348201468Srpaulo	do_setup_timer();
349201468Srpaulo
350201468Srpaulo	if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK,
351201468Srpaulo	    ec_hardclock, NULL, NULL, &ihl) != 0) {
352201468Srpaulo		bus_release_resources(dev, ec_timer_spec, sc->timer_res);
353201468Srpaulo		device_printf(dev, "could not setup hardclock interrupt\n");
354201468Srpaulo		return (ENXIO);
355201468Srpaulo	}
356201468Srpaulo
357201468Srpaulo	if (bus_setup_intr(dev, sc->timer_res[2], INTR_TYPE_CLK,
358201468Srpaulo	    ec_reset, NULL, NULL, &ihl) != 0) {
359201468Srpaulo		bus_release_resources(dev, ec_timer_spec, sc->timer_res);
360201468Srpaulo		device_printf(dev, "could not setup timer interrupt\n");
361201468Srpaulo		return (ENXIO);
362201468Srpaulo	}
363201468Srpaulo
364201468Srpaulo	return (0);
365201468Srpaulo}
366201468Srpaulo
367201468Srpaulostatic device_method_t ec_timer_methods[] = {
368201468Srpaulo	DEVMETHOD(device_probe, ec_timer_probe),
369201468Srpaulo	DEVMETHOD(device_attach, ec_timer_attach),
370201468Srpaulo	{ 0, 0 }
371201468Srpaulo};
372201468Srpaulo
373201468Srpaulostatic driver_t ec_timer_driver = {
374201468Srpaulo	"timer",
375201468Srpaulo	ec_timer_methods,
376201468Srpaulo	sizeof(struct ec_timer_softc),
377201468Srpaulo};
378201468Srpaulo
379201468Srpaulostatic devclass_t ec_timer_devclass;
380201468Srpaulo
381201468SrpauloDRIVER_MODULE(timer, econaarm, ec_timer_driver, ec_timer_devclass, 0, 0);
382