1179595Sbenno/*-
2179595Sbenno * Copyright (c) 2006 Benno Rice.  All rights reserved.
3179595Sbenno *
4179595Sbenno * Redistribution and use in source and binary forms, with or without
5179595Sbenno * modification, are permitted provided that the following conditions
6179595Sbenno * are met:
7179595Sbenno * 1. Redistributions of source code must retain the above copyright
8179595Sbenno *    notice, this list of conditions and the following disclaimer.
9179595Sbenno * 2. Redistributions in binary form must reproduce the above copyright
10179595Sbenno *    notice, this list of conditions and the following disclaimer in the
11179595Sbenno *    documentation and/or other materials provided with the distribution.
12179595Sbenno *
13179595Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14179595Sbenno * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15179595Sbenno * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16179595Sbenno * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17179595Sbenno * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18179595Sbenno * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19179595Sbenno * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20179595Sbenno * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21179595Sbenno * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22179595Sbenno * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23179595Sbenno */
24179595Sbenno
25179595Sbenno#include <sys/cdefs.h>
26179595Sbenno__FBSDID("$FreeBSD$");
27179595Sbenno
28179595Sbenno#include <sys/param.h>
29179595Sbenno#include <sys/systm.h>
30179595Sbenno#include <sys/bus.h>
31179595Sbenno#include <sys/kernel.h>
32179595Sbenno#include <sys/module.h>
33179595Sbenno#include <sys/malloc.h>
34179595Sbenno#include <sys/rman.h>
35179595Sbenno#include <sys/timetc.h>
36278613Sian#include <machine/armreg.h>
37179595Sbenno#include <machine/bus.h>
38179595Sbenno#include <machine/cpu.h>
39179595Sbenno#include <machine/frame.h>
40179595Sbenno#include <machine/intr.h>
41179595Sbenno
42179595Sbenno#include <arm/xscale/pxa/pxavar.h>
43179595Sbenno#include <arm/xscale/pxa/pxareg.h>
44179595Sbenno
45179595Sbenno#define	PXA_TIMER_FREQUENCY	3686400
46179595Sbenno#define	PXA_TIMER_TICK		(PXA_TIMER_FREQUENCY / hz)
47179595Sbenno
48179595Sbennostruct pxa_timer_softc {
49179595Sbenno	struct resource	*	pt_res[5];
50179595Sbenno	bus_space_tag_t		pt_bst;
51179595Sbenno	bus_space_handle_t	pt_bsh;
52179595Sbenno};
53179595Sbenno
54179595Sbennostatic struct resource_spec pxa_timer_spec[] = {
55179595Sbenno	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
56179595Sbenno	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
57179595Sbenno	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
58179595Sbenno	{ SYS_RES_IRQ,		2,	RF_ACTIVE },
59179595Sbenno	{ SYS_RES_IRQ,		3,	RF_ACTIVE },
60179595Sbenno	{ -1, 0 }
61179595Sbenno};
62179595Sbenno
63179595Sbennostatic struct pxa_timer_softc *timer_softc = NULL;
64179595Sbenno
65179595Sbennostatic int	pxa_timer_probe(device_t);
66179595Sbennostatic int	pxa_timer_attach(device_t);
67179595Sbenno
68179595Sbennostatic driver_filter_t	pxa_hardclock;
69179595Sbenno
70179595Sbennostatic unsigned	pxa_timer_get_timecount(struct timecounter *);
71179595Sbenno
72179595Sbennouint32_t	pxa_timer_get_osmr(int);
73179595Sbennovoid		pxa_timer_set_osmr(int, uint32_t);
74179595Sbennouint32_t	pxa_timer_get_oscr(void);
75179595Sbennovoid		pxa_timer_set_oscr(uint32_t);
76179595Sbennouint32_t	pxa_timer_get_ossr(void);
77179595Sbennovoid		pxa_timer_clear_ossr(uint32_t);
78179595Sbennovoid		pxa_timer_watchdog_enable(void);
79179595Sbennovoid		pxa_timer_watchdog_disable(void);
80179595Sbennovoid		pxa_timer_interrupt_enable(int);
81179595Sbennovoid		pxa_timer_interrupt_disable(int);
82179595Sbenno
83179595Sbennostatic struct timecounter pxa_timer_timecounter = {
84179595Sbenno	.tc_get_timecount = pxa_timer_get_timecount,
85179595Sbenno	.tc_name = "OS Timer",
86179595Sbenno	.tc_frequency = PXA_TIMER_FREQUENCY,
87179595Sbenno	.tc_counter_mask = ~0u,
88179595Sbenno	.tc_quality = 1000,
89179595Sbenno};
90179595Sbenno
91179595Sbennostatic int
92179595Sbennopxa_timer_probe(device_t dev)
93179595Sbenno{
94179595Sbenno
95179595Sbenno	device_set_desc(dev, "OS Timer");
96179595Sbenno	return (0);
97179595Sbenno}
98179595Sbenno
99179595Sbennostatic int
100179595Sbennopxa_timer_attach(device_t dev)
101179595Sbenno{
102179595Sbenno	int	error;
103179595Sbenno	void	*ihl;
104179595Sbenno	struct	pxa_timer_softc *sc;
105179595Sbenno
106179595Sbenno	sc = (struct pxa_timer_softc *)device_get_softc(dev);
107179595Sbenno
108179595Sbenno	if (timer_softc != NULL)
109179595Sbenno		return (ENXIO);
110179595Sbenno
111179595Sbenno	error = bus_alloc_resources(dev, pxa_timer_spec, sc->pt_res);
112179595Sbenno	if (error) {
113179595Sbenno		device_printf(dev, "could not allocate resources\n");
114179595Sbenno		return (ENXIO);
115179595Sbenno	}
116179595Sbenno
117179595Sbenno	sc->pt_bst = rman_get_bustag(sc->pt_res[0]);
118179595Sbenno	sc->pt_bsh = rman_get_bushandle(sc->pt_res[0]);
119179595Sbenno
120179595Sbenno	timer_softc = sc;
121179595Sbenno
122179595Sbenno	pxa_timer_interrupt_disable(-1);
123179595Sbenno	pxa_timer_watchdog_disable();
124179595Sbenno
125179595Sbenno	if (bus_setup_intr(dev, sc->pt_res[1], INTR_TYPE_CLK,
126179595Sbenno	    pxa_hardclock, NULL, NULL, &ihl) != 0) {
127179595Sbenno		bus_release_resources(dev, pxa_timer_spec, sc->pt_res);
128179595Sbenno		device_printf(dev, "could not setup hardclock interrupt\n");
129179595Sbenno		return (ENXIO);
130179595Sbenno	}
131179595Sbenno
132179595Sbenno	return (0);
133179595Sbenno}
134179595Sbenno
135179595Sbennostatic int
136179595Sbennopxa_hardclock(void *arg)
137179595Sbenno{
138179595Sbenno	struct		trapframe *frame;
139179595Sbenno
140179595Sbenno	frame = (struct trapframe *)arg;
141179595Sbenno
142179595Sbenno	/* Clear the interrupt */
143179595Sbenno	pxa_timer_clear_ossr(OST_SR_CH0);
144179595Sbenno
145179595Sbenno	/* Schedule next tick */
146179595Sbenno	pxa_timer_set_osmr(0, pxa_timer_get_oscr() + PXA_TIMER_TICK);
147179595Sbenno
148179595Sbenno	/* Do what we came here for */
149179595Sbenno	hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
150179595Sbenno
151179595Sbenno	return (FILTER_HANDLED);
152179595Sbenno}
153179595Sbenno
154179595Sbennostatic device_method_t pxa_timer_methods[] = {
155179595Sbenno	DEVMETHOD(device_probe, pxa_timer_probe),
156179595Sbenno	DEVMETHOD(device_attach, pxa_timer_attach),
157179595Sbenno
158179595Sbenno	{0, 0}
159179595Sbenno};
160179595Sbenno
161179595Sbennostatic driver_t pxa_timer_driver = {
162179595Sbenno	"timer",
163179595Sbenno	pxa_timer_methods,
164179595Sbenno	sizeof(struct pxa_timer_softc),
165179595Sbenno};
166179595Sbenno
167179595Sbennostatic devclass_t pxa_timer_devclass;
168179595Sbenno
169179595SbennoDRIVER_MODULE(pxatimer, pxa, pxa_timer_driver, pxa_timer_devclass, 0, 0);
170179595Sbenno
171179595Sbennostatic unsigned
172179595Sbennopxa_timer_get_timecount(struct timecounter *tc)
173179595Sbenno{
174179595Sbenno
175179595Sbenno	return (pxa_timer_get_oscr());
176179595Sbenno}
177179595Sbenno
178179595Sbennovoid
179179595Sbennocpu_initclocks(void)
180179595Sbenno{
181179595Sbenno
182179595Sbenno	pxa_timer_set_oscr(0);
183179595Sbenno	pxa_timer_set_osmr(0, PXA_TIMER_TICK);
184179595Sbenno	pxa_timer_interrupt_enable(0);
185179595Sbenno
186179595Sbenno	tc_init(&pxa_timer_timecounter);
187179595Sbenno}
188179595Sbenno
189179595Sbennovoid
190179595Sbennocpu_reset(void)
191179595Sbenno{
192179595Sbenno	uint32_t	val;
193179595Sbenno
194278613Sian	(void)disable_interrupts(PSR_I|PSR_F);
195179595Sbenno
196179595Sbenno	val = pxa_timer_get_oscr();
197179595Sbenno	val += PXA_TIMER_FREQUENCY;
198179595Sbenno	pxa_timer_set_osmr(3, val);
199179595Sbenno	pxa_timer_watchdog_enable();
200179595Sbenno
201179595Sbenno	for(;;);
202179595Sbenno}
203179595Sbenno
204179595Sbennovoid
205179595SbennoDELAY(int usec)
206179595Sbenno{
207179595Sbenno	uint32_t	val;
208179595Sbenno
209179595Sbenno	if (timer_softc == NULL) {
210179595Sbenno		for (; usec > 0; usec--)
211179595Sbenno			for (val = 100; val > 0; val--)
212179595Sbenno				;
213179595Sbenno		return;
214179595Sbenno	}
215179595Sbenno
216179595Sbenno	val = pxa_timer_get_oscr();
217179595Sbenno	val += (PXA_TIMER_FREQUENCY * usec) / 1000000;
218179595Sbenno	while (pxa_timer_get_oscr() <= val);
219179595Sbenno}
220179595Sbenno
221179595Sbennouint32_t
222179595Sbennopxa_timer_get_osmr(int which)
223179595Sbenno{
224179595Sbenno
225179595Sbenno	return (bus_space_read_4(timer_softc->pt_bst,
226179595Sbenno	    timer_softc->pt_bsh, which * 0x4));
227179595Sbenno}
228179595Sbenno
229179595Sbennovoid
230179595Sbennopxa_timer_set_osmr(int which, uint32_t val)
231179595Sbenno{
232179595Sbenno
233179595Sbenno	bus_space_write_4(timer_softc->pt_bst,
234179595Sbenno	    timer_softc->pt_bsh, which * 0x4, val);
235179595Sbenno}
236179595Sbenno
237179595Sbennouint32_t
238179595Sbennopxa_timer_get_oscr()
239179595Sbenno{
240179595Sbenno
241179595Sbenno	return (bus_space_read_4(timer_softc->pt_bst,
242179595Sbenno	    timer_softc->pt_bsh, OST_CR));
243179595Sbenno}
244179595Sbenno
245179595Sbennovoid
246179595Sbennopxa_timer_set_oscr(uint32_t val)
247179595Sbenno{
248179595Sbenno
249179595Sbenno	bus_space_write_4(timer_softc->pt_bst,
250179595Sbenno	    timer_softc->pt_bsh, OST_CR, val);
251179595Sbenno}
252179595Sbenno
253179595Sbennouint32_t
254179595Sbennopxa_timer_get_ossr()
255179595Sbenno{
256179595Sbenno
257179595Sbenno	return (bus_space_read_4(timer_softc->pt_bst,
258179595Sbenno	    timer_softc->pt_bsh, OST_SR));
259179595Sbenno}
260179595Sbenno
261179595Sbennovoid
262179595Sbennopxa_timer_clear_ossr(uint32_t val)
263179595Sbenno{
264179595Sbenno
265179595Sbenno	bus_space_write_4(timer_softc->pt_bst,
266179595Sbenno	    timer_softc->pt_bsh, OST_SR, val);
267179595Sbenno}
268179595Sbenno
269179595Sbennovoid
270179595Sbennopxa_timer_watchdog_enable()
271179595Sbenno{
272179595Sbenno
273179595Sbenno	bus_space_write_4(timer_softc->pt_bst,
274179595Sbenno	    timer_softc->pt_bsh, OST_WR, 0x1);
275179595Sbenno}
276179595Sbenno
277179595Sbennovoid
278179595Sbennopxa_timer_watchdog_disable()
279179595Sbenno{
280179595Sbenno
281179595Sbenno	bus_space_write_4(timer_softc->pt_bst,
282179595Sbenno	    timer_softc->pt_bsh, OST_WR, 0x0);
283179595Sbenno}
284179595Sbenno
285179595Sbennovoid
286179595Sbennopxa_timer_interrupt_enable(int which)
287179595Sbenno{
288179595Sbenno	uint32_t	oier;
289179595Sbenno
290179595Sbenno	if (which == -1) {
291179595Sbenno		bus_space_write_4(timer_softc->pt_bst,
292179595Sbenno		    timer_softc->pt_bsh, OST_IR, 0xf);
293179595Sbenno		return;
294179595Sbenno	}
295179595Sbenno
296179595Sbenno	oier = bus_space_read_4(timer_softc->pt_bst,
297179595Sbenno	    timer_softc->pt_bsh, OST_IR);
298179595Sbenno	oier |= 1 << which;
299179595Sbenno	bus_space_write_4(timer_softc->pt_bst,
300179595Sbenno	    timer_softc->pt_bsh, OST_IR, oier);
301179595Sbenno}
302179595Sbenno
303179595Sbennovoid
304179595Sbennopxa_timer_interrupt_disable(int which)
305179595Sbenno{
306179595Sbenno	uint32_t	oier;
307179595Sbenno
308179595Sbenno	if (which == -1) {
309179595Sbenno		bus_space_write_4(timer_softc->pt_bst,
310179595Sbenno		    timer_softc->pt_bsh, OST_IR, 0);
311179595Sbenno	}
312179595Sbenno
313179595Sbenno	oier = bus_space_read_4(timer_softc->pt_bst,
314179595Sbenno	    timer_softc->pt_bsh, OST_IR);
315179595Sbenno	oier &= ~(1 << which);
316179595Sbenno	bus_space_write_4(timer_softc->pt_bst,
317179595Sbenno	    timer_softc->pt_bsh, OST_IR, oier);
318179595Sbenno}
319