1164426Ssam/*	$NetBSD: ixp425_timer.c,v 1.11 2006/04/10 03:36:03 simonb Exp $ */
2164426Ssam
3164426Ssam/*
4164426Ssam * Copyright (c) 2003
5164426Ssam *	Ichiro FUKUHARA <ichiro@ichiro.org>.
6164426Ssam * All rights reserved.
7164426Ssam *
8164426Ssam * Redistribution and use in source and binary forms, with or without
9164426Ssam * modification, are permitted provided that the following conditions
10164426Ssam * are met:
11164426Ssam * 1. Redistributions of source code must retain the above copyright
12164426Ssam *    notice, this list of conditions and the following disclaimer.
13164426Ssam * 2. Redistributions in binary form must reproduce the above copyright
14164426Ssam *    notice, this list of conditions and the following disclaimer in the
15164426Ssam *    documentation and/or other materials provided with the distribution.
16164426Ssam * 3. All advertising materials mentioning features or use of this software
17164426Ssam *    must display the following acknowledgement:
18164426Ssam *	This product includes software developed by Ichiro FUKUHARA.
19164426Ssam * 4. The name of the company nor the name of the author may be used to
20164426Ssam *    endorse or promote products derived from this software without specific
21164426Ssam *    prior written permission.
22164426Ssam *
23164426Ssam * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR
24164426Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25164426Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26164426Ssam * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR
27164426Ssam * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28164426Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29164426Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30164426Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31164426Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32164426Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33164426Ssam * SUCH DAMAGE.
34164426Ssam */
35164426Ssam
36164426Ssam#include <sys/cdefs.h>
37164426Ssam__FBSDID("$FreeBSD$");
38164426Ssam
39164426Ssam#include <sys/param.h>
40164426Ssam#include <sys/systm.h>
41164426Ssam#include <sys/kernel.h>
42164426Ssam#include <sys/module.h>
43164426Ssam#include <sys/time.h>
44164426Ssam#include <sys/bus.h>
45164426Ssam#include <sys/resource.h>
46164426Ssam#include <sys/rman.h>
47164426Ssam#include <sys/timetc.h>
48164426Ssam
49164426Ssam#include <machine/bus.h>
50164426Ssam#include <machine/cpu.h>
51164426Ssam#include <machine/cpufunc.h>
52164426Ssam#include <machine/frame.h>
53164426Ssam#include <machine/resource.h>
54164426Ssam#include <machine/intr.h>
55164426Ssam#include <arm/xscale/ixp425/ixp425reg.h>
56164426Ssam#include <arm/xscale/ixp425/ixp425var.h>
57164426Ssam
58164426Ssamstatic uint32_t counts_per_hz;
59164426Ssam
60164426Ssam/* callback functions for intr_functions */
61166901Spisoint	ixpclk_intr(void *);
62164426Ssam
63164426Ssamstruct ixpclk_softc {
64164426Ssam	device_t		sc_dev;
65164426Ssam	bus_addr_t		sc_baseaddr;
66164426Ssam	bus_space_tag_t		sc_iot;
67164426Ssam	bus_space_handle_t      sc_ioh;
68164426Ssam};
69164426Ssam
70164426Ssamstatic unsigned ixp425_timer_get_timecount(struct timecounter *tc);
71164426Ssam
72164426Ssam#ifndef IXP425_CLOCK_FREQ
73164426Ssam#define	COUNTS_PER_SEC		66666600	/* 66MHz */
74164426Ssam#else
75164426Ssam#define	COUNTS_PER_SEC		IXP425_CLOCK_FREQ
76164426Ssam#endif
77164426Ssam#define	COUNTS_PER_USEC		((COUNTS_PER_SEC / 1000000) + 1)
78164426Ssam
79164426Ssamstatic struct ixpclk_softc *ixpclk_sc = NULL;
80164426Ssam
81164426Ssam#define GET_TS_VALUE(sc)	(*(volatile u_int32_t *) \
82164426Ssam				  (IXP425_TIMER_VBASE + IXP425_OST_TS))
83164426Ssam
84164426Ssamstatic struct timecounter ixp425_timer_timecounter = {
85164426Ssam	ixp425_timer_get_timecount,	/* get_timecount */
86164426Ssam	NULL, 				/* no poll_pps */
87164426Ssam	~0u, 				/* counter_mask */
88164426Ssam	COUNTS_PER_SEC,			/* frequency */
89186352Ssam	"IXP4XX Timer", 		/* name */
90164426Ssam	1000,				/* quality */
91164426Ssam};
92164426Ssam
93164426Ssamstatic int
94164426Ssamixpclk_probe(device_t dev)
95164426Ssam{
96186352Ssam	device_set_desc(dev, "IXP4XX Timer");
97164426Ssam	return (0);
98164426Ssam}
99164426Ssam
100164426Ssamstatic int
101164426Ssamixpclk_attach(device_t dev)
102164426Ssam{
103164426Ssam	struct ixpclk_softc *sc = device_get_softc(dev);
104164426Ssam	struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
105164426Ssam
106164426Ssam	ixpclk_sc = sc;
107164426Ssam
108164426Ssam	sc->sc_dev = dev;
109164426Ssam	sc->sc_iot = sa->sc_iot;
110164426Ssam	sc->sc_baseaddr = IXP425_TIMER_HWBASE;
111164426Ssam
112164426Ssam	if (bus_space_map(sc->sc_iot, sc->sc_baseaddr, 8, 0,
113164426Ssam			  &sc->sc_ioh))
114164426Ssam		panic("%s: Cannot map registers", device_get_name(dev));
115164426Ssam
116164426Ssam	return (0);
117164426Ssam}
118164426Ssam
119164426Ssamstatic device_method_t ixpclk_methods[] = {
120164426Ssam	DEVMETHOD(device_probe, ixpclk_probe),
121164426Ssam	DEVMETHOD(device_attach, ixpclk_attach),
122164426Ssam	{0, 0},
123164426Ssam};
124164426Ssam
125164426Ssamstatic driver_t ixpclk_driver = {
126164426Ssam	"ixpclk",
127164426Ssam	ixpclk_methods,
128164426Ssam	sizeof(struct ixpclk_softc),
129164426Ssam};
130164426Ssamstatic devclass_t ixpclk_devclass;
131164426Ssam
132164426SsamDRIVER_MODULE(ixpclk, ixp, ixpclk_driver, ixpclk_devclass, 0, 0);
133164426Ssamstatic unsigned
134164426Ssamixp425_timer_get_timecount(struct timecounter *tc)
135164426Ssam{
136164426Ssam	uint32_t ret;
137164426Ssam
138164426Ssam	ret = GET_TS_VALUE(sc);
139164426Ssam	return (ret);
140164426Ssam}
141164426Ssam
142164426Ssam/*
143164426Ssam * cpu_initclocks:
144164426Ssam *
145164426Ssam *	Initialize the clock and get them going.
146164426Ssam */
147164426Ssamvoid
148164426Ssamcpu_initclocks(void)
149164426Ssam{
150164426Ssam	struct ixpclk_softc* sc = ixpclk_sc;
151164426Ssam	struct resource *irq;
152164426Ssam	device_t dev = sc->sc_dev;
153164426Ssam	u_int oldirqstate;
154164426Ssam	int rid = 0;
155164426Ssam	void *ihl;
156164426Ssam
157164426Ssam	if (hz < 50 || COUNTS_PER_SEC % hz) {
158164426Ssam		printf("Cannot get %d Hz clock; using 100 Hz\n", hz);
159164426Ssam		hz = 100;
160164426Ssam	}
161164426Ssam	tick = 1000000 / hz;	/* number of microseconds between interrupts */
162164426Ssam
163164426Ssam	/*
164164426Ssam	 * We only have one timer available; stathz and profhz are
165164426Ssam	 * always left as 0 (the upper-layer clock code deals with
166164426Ssam	 * this situation).
167164426Ssam	 */
168164426Ssam	if (stathz != 0)
169164426Ssam		printf("Cannot get %d Hz statclock\n", stathz);
170164426Ssam	stathz = 0;
171164426Ssam
172164426Ssam	if (profhz != 0)
173164426Ssam		printf("Cannot get %d Hz profclock\n", profhz);
174164426Ssam	profhz = 0;
175164426Ssam
176164426Ssam	/* Report the clock frequency. */
177164426Ssam
178164426Ssam	oldirqstate = disable_interrupts(I32_bit);
179164426Ssam
180164426Ssam	irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, IXP425_INT_TMR0,
181164426Ssam	    IXP425_INT_TMR0, 1, RF_ACTIVE);
182164426Ssam	if (!irq)
183164426Ssam		panic("Unable to setup the clock irq handler.\n");
184164426Ssam	else
185166901Spiso		bus_setup_intr(dev, irq, INTR_TYPE_CLK, ixpclk_intr, NULL,
186166901Spiso		    NULL, &ihl);
187164426Ssam
188164426Ssam	/* Set up the new clock parameters. */
189164426Ssam
190164426Ssam	/* clear interrupt */
191164426Ssam	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS,
192164426Ssam			  OST_WARM_RESET | OST_WDOG_INT | OST_TS_INT |
193164426Ssam			  OST_TIM1_INT | OST_TIM0_INT);
194164426Ssam
195164426Ssam	counts_per_hz = COUNTS_PER_SEC / hz;
196164426Ssam
197164426Ssam	/* reload value & Timer enable */
198164426Ssam	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_TIM0_RELOAD,
199164426Ssam			  (counts_per_hz & TIMERRELOAD_MASK) | OST_TIMER_EN);
200164426Ssam
201164426Ssam	tc_init(&ixp425_timer_timecounter);
202164426Ssam	restore_interrupts(oldirqstate);
203164426Ssam	rid = 0;
204164426Ssam}
205164426Ssam
206164426Ssam
207164426Ssam/*
208164426Ssam * DELAY:
209164426Ssam *
210164426Ssam *	Delay for at least N microseconds.
211164426Ssam */
212164426Ssamvoid
213164426SsamDELAY(int n)
214164426Ssam{
215164426Ssam	u_int32_t first, last;
216164426Ssam	int usecs;
217164426Ssam
218164426Ssam	if (n == 0)
219164426Ssam		return;
220164426Ssam
221164426Ssam	/*
222164426Ssam	 * Clamp the timeout at a maximum value (about 32 seconds with
223164426Ssam	 * a 66MHz clock). *Nobody* should be delay()ing for anywhere
224164426Ssam	 * near that length of time and if they are, they should be hung
225164426Ssam	 * out to dry.
226164426Ssam	 */
227164426Ssam	if (n >= (0x80000000U / COUNTS_PER_USEC))
228164426Ssam		usecs = (0x80000000U / COUNTS_PER_USEC) - 1;
229164426Ssam	else
230164426Ssam		usecs = n * COUNTS_PER_USEC;
231164426Ssam
232164426Ssam	/* Note: Timestamp timer counts *up*, unlike the other timers */
233164426Ssam	first = GET_TS_VALUE();
234164426Ssam
235164426Ssam	while (usecs > 0) {
236164426Ssam		last = GET_TS_VALUE();
237164426Ssam		usecs -= (int)(last - first);
238164426Ssam		first = last;
239164426Ssam	}
240164426Ssam}
241164426Ssam
242164426Ssam/*
243164426Ssam * ixpclk_intr:
244164426Ssam *
245164426Ssam *	Handle the hardclock interrupt.
246164426Ssam */
247166901Spisoint
248164426Ssamixpclk_intr(void *arg)
249164426Ssam{
250164426Ssam	struct ixpclk_softc* sc = ixpclk_sc;
251164426Ssam	struct trapframe *frame = arg;
252164426Ssam
253164426Ssam	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS,
254164426Ssam			  OST_TIM0_INT);
255164426Ssam
256164426Ssam	hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
257166901Spiso	return (FILTER_HANDLED);
258164426Ssam}
259164426Ssam
260164426Ssamvoid
261164426Ssamcpu_startprofclock(void)
262164426Ssam{
263164426Ssam}
264164426Ssam
265164426Ssamvoid
266164426Ssamcpu_stopprofclock(void)
267164426Ssam{
268164426Ssam}
269