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: releng/10.2/sys/arm/xscale/ixp425/ixp425_timer.c 278613 2015-02-12 03:50:33Z ian $");
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
49278613Sian#include <machine/armreg.h>
50164426Ssam#include <machine/bus.h>
51164426Ssam#include <machine/cpu.h>
52164426Ssam#include <machine/cpufunc.h>
53164426Ssam#include <machine/frame.h>
54164426Ssam#include <machine/resource.h>
55164426Ssam#include <machine/intr.h>
56164426Ssam#include <arm/xscale/ixp425/ixp425reg.h>
57164426Ssam#include <arm/xscale/ixp425/ixp425var.h>
58164426Ssam
59164426Ssamstatic uint32_t counts_per_hz;
60164426Ssam
61164426Ssam/* callback functions for intr_functions */
62166901Spisoint	ixpclk_intr(void *);
63164426Ssam
64164426Ssamstruct ixpclk_softc {
65164426Ssam	device_t		sc_dev;
66164426Ssam	bus_addr_t		sc_baseaddr;
67164426Ssam	bus_space_tag_t		sc_iot;
68164426Ssam	bus_space_handle_t      sc_ioh;
69164426Ssam};
70164426Ssam
71164426Ssamstatic unsigned ixp425_timer_get_timecount(struct timecounter *tc);
72164426Ssam
73164426Ssam#ifndef IXP425_CLOCK_FREQ
74164426Ssam#define	COUNTS_PER_SEC		66666600	/* 66MHz */
75164426Ssam#else
76164426Ssam#define	COUNTS_PER_SEC		IXP425_CLOCK_FREQ
77164426Ssam#endif
78164426Ssam#define	COUNTS_PER_USEC		((COUNTS_PER_SEC / 1000000) + 1)
79164426Ssam
80164426Ssamstatic struct ixpclk_softc *ixpclk_sc = NULL;
81164426Ssam
82164426Ssam#define GET_TS_VALUE(sc)	(*(volatile u_int32_t *) \
83164426Ssam				  (IXP425_TIMER_VBASE + IXP425_OST_TS))
84164426Ssam
85164426Ssamstatic struct timecounter ixp425_timer_timecounter = {
86164426Ssam	ixp425_timer_get_timecount,	/* get_timecount */
87164426Ssam	NULL, 				/* no poll_pps */
88164426Ssam	~0u, 				/* counter_mask */
89164426Ssam	COUNTS_PER_SEC,			/* frequency */
90186352Ssam	"IXP4XX Timer", 		/* name */
91164426Ssam	1000,				/* quality */
92164426Ssam};
93164426Ssam
94164426Ssamstatic int
95164426Ssamixpclk_probe(device_t dev)
96164426Ssam{
97186352Ssam	device_set_desc(dev, "IXP4XX Timer");
98164426Ssam	return (0);
99164426Ssam}
100164426Ssam
101164426Ssamstatic int
102164426Ssamixpclk_attach(device_t dev)
103164426Ssam{
104164426Ssam	struct ixpclk_softc *sc = device_get_softc(dev);
105164426Ssam	struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
106164426Ssam
107164426Ssam	ixpclk_sc = sc;
108164426Ssam
109164426Ssam	sc->sc_dev = dev;
110164426Ssam	sc->sc_iot = sa->sc_iot;
111164426Ssam	sc->sc_baseaddr = IXP425_TIMER_HWBASE;
112164426Ssam
113164426Ssam	if (bus_space_map(sc->sc_iot, sc->sc_baseaddr, 8, 0,
114164426Ssam			  &sc->sc_ioh))
115164426Ssam		panic("%s: Cannot map registers", device_get_name(dev));
116164426Ssam
117164426Ssam	return (0);
118164426Ssam}
119164426Ssam
120164426Ssamstatic device_method_t ixpclk_methods[] = {
121164426Ssam	DEVMETHOD(device_probe, ixpclk_probe),
122164426Ssam	DEVMETHOD(device_attach, ixpclk_attach),
123164426Ssam	{0, 0},
124164426Ssam};
125164426Ssam
126164426Ssamstatic driver_t ixpclk_driver = {
127164426Ssam	"ixpclk",
128164426Ssam	ixpclk_methods,
129164426Ssam	sizeof(struct ixpclk_softc),
130164426Ssam};
131164426Ssamstatic devclass_t ixpclk_devclass;
132164426Ssam
133164426SsamDRIVER_MODULE(ixpclk, ixp, ixpclk_driver, ixpclk_devclass, 0, 0);
134164426Ssamstatic unsigned
135164426Ssamixp425_timer_get_timecount(struct timecounter *tc)
136164426Ssam{
137164426Ssam	uint32_t ret;
138164426Ssam
139164426Ssam	ret = GET_TS_VALUE(sc);
140164426Ssam	return (ret);
141164426Ssam}
142164426Ssam
143164426Ssam/*
144164426Ssam * cpu_initclocks:
145164426Ssam *
146164426Ssam *	Initialize the clock and get them going.
147164426Ssam */
148164426Ssamvoid
149164426Ssamcpu_initclocks(void)
150164426Ssam{
151164426Ssam	struct ixpclk_softc* sc = ixpclk_sc;
152164426Ssam	struct resource *irq;
153164426Ssam	device_t dev = sc->sc_dev;
154164426Ssam	u_int oldirqstate;
155164426Ssam	int rid = 0;
156164426Ssam	void *ihl;
157164426Ssam
158164426Ssam	if (hz < 50 || COUNTS_PER_SEC % hz) {
159164426Ssam		printf("Cannot get %d Hz clock; using 100 Hz\n", hz);
160164426Ssam		hz = 100;
161164426Ssam	}
162164426Ssam	tick = 1000000 / hz;	/* number of microseconds between interrupts */
163164426Ssam
164164426Ssam	/*
165164426Ssam	 * We only have one timer available; stathz and profhz are
166164426Ssam	 * always left as 0 (the upper-layer clock code deals with
167164426Ssam	 * this situation).
168164426Ssam	 */
169164426Ssam	if (stathz != 0)
170164426Ssam		printf("Cannot get %d Hz statclock\n", stathz);
171164426Ssam	stathz = 0;
172164426Ssam
173164426Ssam	if (profhz != 0)
174164426Ssam		printf("Cannot get %d Hz profclock\n", profhz);
175164426Ssam	profhz = 0;
176164426Ssam
177164426Ssam	/* Report the clock frequency. */
178164426Ssam
179278613Sian	oldirqstate = disable_interrupts(PSR_I);
180164426Ssam
181164426Ssam	irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, IXP425_INT_TMR0,
182164426Ssam	    IXP425_INT_TMR0, 1, RF_ACTIVE);
183164426Ssam	if (!irq)
184164426Ssam		panic("Unable to setup the clock irq handler.\n");
185164426Ssam	else
186166901Spiso		bus_setup_intr(dev, irq, INTR_TYPE_CLK, ixpclk_intr, NULL,
187166901Spiso		    NULL, &ihl);
188164426Ssam
189164426Ssam	/* Set up the new clock parameters. */
190164426Ssam
191164426Ssam	/* clear interrupt */
192164426Ssam	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS,
193164426Ssam			  OST_WARM_RESET | OST_WDOG_INT | OST_TS_INT |
194164426Ssam			  OST_TIM1_INT | OST_TIM0_INT);
195164426Ssam
196164426Ssam	counts_per_hz = COUNTS_PER_SEC / hz;
197164426Ssam
198164426Ssam	/* reload value & Timer enable */
199164426Ssam	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_TIM0_RELOAD,
200164426Ssam			  (counts_per_hz & TIMERRELOAD_MASK) | OST_TIMER_EN);
201164426Ssam
202164426Ssam	tc_init(&ixp425_timer_timecounter);
203164426Ssam	restore_interrupts(oldirqstate);
204164426Ssam	rid = 0;
205164426Ssam}
206164426Ssam
207164426Ssam
208164426Ssam/*
209164426Ssam * DELAY:
210164426Ssam *
211164426Ssam *	Delay for at least N microseconds.
212164426Ssam */
213164426Ssamvoid
214164426SsamDELAY(int n)
215164426Ssam{
216164426Ssam	u_int32_t first, last;
217164426Ssam	int usecs;
218164426Ssam
219164426Ssam	if (n == 0)
220164426Ssam		return;
221164426Ssam
222164426Ssam	/*
223164426Ssam	 * Clamp the timeout at a maximum value (about 32 seconds with
224164426Ssam	 * a 66MHz clock). *Nobody* should be delay()ing for anywhere
225164426Ssam	 * near that length of time and if they are, they should be hung
226164426Ssam	 * out to dry.
227164426Ssam	 */
228164426Ssam	if (n >= (0x80000000U / COUNTS_PER_USEC))
229164426Ssam		usecs = (0x80000000U / COUNTS_PER_USEC) - 1;
230164426Ssam	else
231164426Ssam		usecs = n * COUNTS_PER_USEC;
232164426Ssam
233164426Ssam	/* Note: Timestamp timer counts *up*, unlike the other timers */
234164426Ssam	first = GET_TS_VALUE();
235164426Ssam
236164426Ssam	while (usecs > 0) {
237164426Ssam		last = GET_TS_VALUE();
238164426Ssam		usecs -= (int)(last - first);
239164426Ssam		first = last;
240164426Ssam	}
241164426Ssam}
242164426Ssam
243164426Ssam/*
244164426Ssam * ixpclk_intr:
245164426Ssam *
246164426Ssam *	Handle the hardclock interrupt.
247164426Ssam */
248166901Spisoint
249164426Ssamixpclk_intr(void *arg)
250164426Ssam{
251164426Ssam	struct ixpclk_softc* sc = ixpclk_sc;
252164426Ssam	struct trapframe *frame = arg;
253164426Ssam
254164426Ssam	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS,
255164426Ssam			  OST_TIM0_INT);
256164426Ssam
257164426Ssam	hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
258166901Spiso	return (FILTER_HANDLED);
259164426Ssam}
260164426Ssam
261164426Ssamvoid
262164426Ssamcpu_startprofclock(void)
263164426Ssam{
264164426Ssam}
265164426Ssam
266164426Ssamvoid
267164426Ssamcpu_stopprofclock(void)
268164426Ssam{
269164426Ssam}
270