1170530Ssam/*	$NetBSD: tx39clock.c,v 1.25 2011/03/16 14:43:37 tsutsui Exp $ */
2178354Ssam
3170530Ssam/*-
4170530Ssam * Copyright (c) 1999-2002 The NetBSD Foundation, Inc.
5170530Ssam * All rights reserved.
6170530Ssam *
7170530Ssam * This code is derived from software contributed to The NetBSD Foundation
8170530Ssam * by UCHIYAMA Yasushi.
9170530Ssam *
10170530Ssam * Redistribution and use in source and binary forms, with or without
11170530Ssam * modification, are permitted provided that the following conditions
12170530Ssam * are met:
13170530Ssam * 1. Redistributions of source code must retain the above copyright
14170530Ssam *    notice, this list of conditions and the following disclaimer.
15170530Ssam * 2. Redistributions in binary form must reproduce the above copyright
16170530Ssam *    notice, this list of conditions and the following disclaimer in the
17170530Ssam *    documentation and/or other materials provided with the distribution.
18170530Ssam *
19170530Ssam * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20170530Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21170530Ssam * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22170530Ssam * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23170530Ssam * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24170530Ssam * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25170530Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26170530Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27170530Ssam * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28170530Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29170530Ssam * POSSIBILITY OF SUCH DAMAGE.
30170530Ssam */
31170530Ssam
32170530Ssam#include <sys/cdefs.h>
33170530Ssam__KERNEL_RCSID(0, "$NetBSD: tx39clock.c,v 1.25 2011/03/16 14:43:37 tsutsui Exp $");
34170530Ssam
35170530Ssam#include "opt_tx39clock_debug.h"
36178354Ssam
37170530Ssam#include <sys/param.h>
38170530Ssam#include <sys/systm.h>
39170530Ssam#include <sys/timetc.h>
40170530Ssam#include <sys/device.h>
41170530Ssam#include <sys/bus.h>
42170530Ssam
43170530Ssam#include <dev/clock_subr.h>
44170530Ssam
45170530Ssam#include <machine/sysconf.h>
46170530Ssam
47170530Ssam#include <hpcmips/tx/tx39var.h>
48170530Ssam#include <hpcmips/tx/tx39icureg.h>
49170530Ssam#include <hpcmips/tx/tx39clockvar.h>
50195377Ssam#include <hpcmips/tx/tx39clockreg.h>
51178354Ssam#include <hpcmips/tx/tx39timerreg.h>
52170530Ssam
53170530Ssam#ifdef	TX39CLOCK_DEBUG
54170530Ssam#define DPRINTF_ENABLE
55170530Ssam#define DPRINTF_DEBUG	tx39clock_debug
56170530Ssam#endif
57178354Ssam#include <machine/debug.h>
58205277Srpaulo
59178354Ssam#define ISSETPRINT(r, m)						\
60178354Ssam	dbg_bitmask_print(r, TX39_CLOCK_EN ## m ## CLK, #m)
61178354Ssam
62178354Ssamvoid	tx39clock_init(device_t);
63178354Ssam
64178354Ssamstruct platform_clock tx39_clock = {
65178354Ssam#define CLOCK_RATE	100
66178354Ssam	CLOCK_RATE, tx39clock_init,
67178354Ssam};
68178354Ssam
69178354Ssamstruct txtime {
70178354Ssam	uint32_t t_hi;
71178354Ssam	uint32_t t_lo;
72178354Ssam};
73178354Ssam
74178354Ssamstruct tx39clock_softc {
75170530Ssam	struct	device sc_dev;
76170530Ssam	tx_chipset_tag_t sc_tc;
77170530Ssam
78170530Ssam	int sc_alarm;
79170530Ssam	int sc_enabled;
80170530Ssam	int sc_year;
81170530Ssam	struct clock_ymdhms sc_epoch;
82170530Ssam	struct timecounter sc_tcounter;
83173273Ssam};
84193115Ssam
85193115Ssamint	tx39clock_match(struct device *, struct cfdata *, void *);
86193115Ssamvoid	tx39clock_attach(struct device *, struct device *, void *);
87193115Ssam#ifdef TX39CLOCK_DEBUG
88173273Ssamvoid	tx39clock_dump(tx_chipset_tag_t);
89173273Ssam#endif
90193115Ssam
91193115Ssamvoid	tx39clock_cpuspeed(int *, int *);
92193115Ssam
93193115Ssamvoid	__tx39timer_rtcfreeze(tx_chipset_tag_t);
94193115Ssamvoid	__tx39timer_rtcreset(tx_chipset_tag_t);
95193115Ssamvoid	__tx39timer_rtcget(struct txtime *);
96193115Ssamtime_t __tx39timer_rtc2sec(struct txtime *);
97193115Ssamuint32_t tx39_timecount(struct timecounter *);
98193115Ssam
99193115SsamCFATTACH_DECL(tx39clock, sizeof(struct tx39clock_softc),
100193115Ssam    tx39clock_match, tx39clock_attach, NULL, NULL);
101193115Ssam
102193115Ssamint
103193115Ssamtx39clock_match(struct device *parent, struct cfdata *cf, void *aux)
104193115Ssam{
105193115Ssam
106193115Ssam	return ATTACH_FIRST;
107193115Ssam}
108193115Ssam
109195377Ssamvoid
110195377Ssamtx39clock_attach(struct device *parent, struct device *self, void *aux)
111195377Ssam{
112195377Ssam	struct txsim_attach_args *ta = aux;
113195377Ssam	struct tx39clock_softc *sc = device_private(self);
114195377Ssam	tx_chipset_tag_t tc;
115195377Ssam	txreg_t reg;
116195377Ssam
117195377Ssam	tc = sc->sc_tc = ta->ta_tc;
118195377Ssam	tx_conf_register_clock(tc, self);
119178354Ssam
120195377Ssam	/* Reset timer module */
121178354Ssam	tx_conf_write(tc, TX39_TIMERCONTROL_REG, 0);
122195377Ssam
123195377Ssam	/* Enable periodic timer */
124195377Ssam	reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG);
125178354Ssam	reg |= TX39_TIMERCONTROL_ENPERTIMER;
126178354Ssam	tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg);
127178354Ssam
128178354Ssam	sc->sc_enabled = 0;
129178354Ssam	/*
130184280Ssam	 * RTC and ALARM
131195377Ssam	 *    RTCINT    ... INTR5 bit 31  (roll over)
132195377Ssam	 *    ALARMINT  ... INTR5 bit 30
133195377Ssam	 *    PERINT    ... INTR5 bit 29
134195377Ssam	 */
135195377Ssam
136195377Ssam	platform_clock_attach(self, &tx39_clock);
137195377Ssam
138195377Ssam#ifdef TX39CLOCK_DEBUG
139195377Ssam	tx39clock_dump(tc);
140195377Ssam#endif /* TX39CLOCK_DEBUG */
141195377Ssam}
142195377Ssam
143195377Ssam/*
144195377Ssam * cpuclock ... CPU clock (Hz)
145195377Ssam * cpuspeed ... instructions-per-microsecond
146195377Ssam */
147195377Ssamvoid
148195377Ssamtx39clock_cpuspeed(int *cpuclock, int *cpu_speed)
149195377Ssam{
150195377Ssam	struct txtime t0, t1;
151195377Ssam	int elapsed;
152195377Ssam
153178354Ssam	__tx39timer_rtcget(&t0);
154195377Ssam	__asm volatile(
155170530Ssam		".set	noreorder;		\n\t"
156178354Ssam		"li	$8, 10000000;		\n"
157178354Ssam	"1:	nop;				\n\t"
158170530Ssam		"nop;				\n\t"
159170530Ssam		"nop;				\n\t"
160170530Ssam		"nop;				\n\t"
161170530Ssam		"nop;				\n\t"
162170530Ssam		"nop;				\n\t"
163170530Ssam		"nop;				\n\t"
164170530Ssam		"add	$8, $8, -1;		\n\t"
165170530Ssam		"bnez	$8, 1b;			\n\t"
166184280Ssam		"nop;				\n\t"
167184280Ssam		".set	reorder;");
168184280Ssam	__tx39timer_rtcget(&t1);
169184280Ssam
170191552Ssam	elapsed = t1.t_lo - t0.t_lo;
171191552Ssam
172191552Ssam	*cpuclock = (100000000 / elapsed) * TX39_RTCLOCK;
173170530Ssam	*cpu_speed = *cpuclock / 1000000;
174170530Ssam}
175170530Ssam
176170530Ssamvoid
177170530Ssam__tx39timer_rtcfreeze(tx_chipset_tag_t tc)
178195377Ssam{
179170530Ssam	txreg_t reg;
180178354Ssam
181170530Ssam	reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG);
182170530Ssam
183170530Ssam	/* Freeze RTC */
184184280Ssam	reg |= TX39_TIMERCONTROL_FREEZEPRE; /* Upper 8bit */
185191552Ssam	reg |= TX39_TIMERCONTROL_FREEZERTC; /* Lower 32bit */
186191552Ssam
187170530Ssam	/* Freeze periodic timer */
188173273Ssam	reg |= TX39_TIMERCONTROL_FREEZETIMER;
189173273Ssam	reg &= ~TX39_TIMERCONTROL_ENPERTIMER;
190178354Ssam	tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg);
191173273Ssam}
192178354Ssam
193178354Ssamvoid
194178354Ssam__tx39timer_rtcget(struct txtime *t)
195178354Ssam{
196173273Ssam	tx_chipset_tag_t tc;
197178354Ssam	txreg_t reghi, reglo, oreghi, oreglo;
198178354Ssam	int retry;
199178354Ssam
200178354Ssam	tc = tx_conf_get_tag();
201178354Ssam
202178354Ssam	retry = 10;
203178354Ssam
204178354Ssam	do {
205178354Ssam		oreglo = tx_conf_read(tc, TX39_TIMERRTCLO_REG);
206178354Ssam		reglo = tx_conf_read(tc, TX39_TIMERRTCLO_REG);
207178354Ssam
208178354Ssam		oreghi = tx_conf_read(tc, TX39_TIMERRTCHI_REG);
209178354Ssam		reghi = tx_conf_read(tc, TX39_TIMERRTCHI_REG);
210178354Ssam	} while ((reghi != oreghi || reglo != oreglo) && (--retry > 0));
211178354Ssam
212178354Ssam	if (retry < 0) {
213170530Ssam		printf("RTC timer read error.\n");
214173273Ssam	}
215173273Ssam
216170530Ssam	t->t_hi = TX39_TIMERRTCHI(reghi);
217170530Ssam	t->t_lo = reglo;
218193655Ssam}
219193655Ssam
220193655Ssamvoid
221178354Ssam__tx39timer_rtcreset(tx_chipset_tag_t tc)
222193655Ssam{
223173273Ssam	txreg_t reg;
224178354Ssam
225193655Ssam	reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG);
226178354Ssam
227193655Ssam	/* Reset counter and stop */
228170530Ssam	reg |= TX39_TIMERCONTROL_RTCCLR;
229183256Ssam	tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg);
230183256Ssam
231193655Ssam	/* Count again */
232183256Ssam	reg &= ~TX39_TIMERCONTROL_RTCCLR;
233170530Ssam	tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg);
234193655Ssam}
235178354Ssam
236193655Ssamuint32_t
237193655Ssamtx39_timecount(struct timecounter *tch)
238178354Ssam{
239193655Ssam	tx_chipset_tag_t tc = tch->tc_priv;
240170530Ssam
241178354Ssam	/*
242178354Ssam	 * since we're only reading the low register, we don't care about
243193655Ssam	 * if the chip increments it.  we assume that the single read will
244170530Ssam	 * always be consistent.  This is much faster than the routine which
245170530Ssam	 * has to get both values, improving the quality.
246170530Ssam	 */
247178354Ssam	return tx_conf_read(tc, TX39_TIMERRTCLO_REG);
248170530Ssam}
249170530Ssam
250170530Ssamvoid
251170530Ssamtx39clock_init(device_t self)
252205277Srpaulo{
253205277Srpaulo	struct tx39clock_softc *sc = device_private(self);
254170530Ssam	tx_chipset_tag_t tc = sc->sc_tc;
255170530Ssam	txreg_t reg;
256170530Ssam	int pcnt;
257205277Srpaulo
258172226Ssam	/*
259172226Ssam	 * Setup periodic timer (interrupting hz times per second.)
260170530Ssam	 */
261170530Ssam	pcnt = TX39_TIMERCLK / CLOCK_RATE - 1;
262205277Srpaulo	reg = tx_conf_read(tc, TX39_TIMERPERIODIC_REG);
263205277Srpaulo	TX39_TIMERPERIODIC_PERVAL_CLR(reg);
264205277Srpaulo	reg = TX39_TIMERPERIODIC_PERVAL_SET(reg, pcnt);
265205277Srpaulo	tx_conf_write(tc, TX39_TIMERPERIODIC_REG, reg);
266205277Srpaulo
267205277Srpaulo	/*
268205277Srpaulo	 * Enable periodic timer
269205277Srpaulo	 */
270205277Srpaulo	reg = tx_conf_read(tc, TX39_INTRENABLE6_REG);
271205277Srpaulo	reg |= TX39_INTRPRI13_TIMER_PERIODIC_BIT;
272205277Srpaulo	tx_conf_write(tc, TX39_INTRENABLE6_REG, reg);
273205277Srpaulo
274205277Srpaulo	sc->sc_tcounter.tc_name = "tx39rtc";
275205277Srpaulo	sc->sc_tcounter.tc_get_timecount = tx39_timecount;
276205277Srpaulo	sc->sc_tcounter.tc_priv = tc;
277205277Srpaulo	sc->sc_tcounter.tc_counter_mask = 0xffffffff;
278205277Srpaulo	sc->sc_tcounter.tc_frequency = TX39_RTCLOCK;
279205277Srpaulo	sc->sc_tcounter.tc_quality = 100;
280170530Ssam	tc_init(&sc->sc_tcounter);
281170530Ssam}
282170530Ssam
283170530Ssamint
284170530Ssamtx39clock_alarm_set(tx_chipset_tag_t tc, int msec)
285170530Ssam{
286205277Srpaulo	struct tx39clock_softc *sc = tc->tc_clockt;
287205277Srpaulo
288205277Srpaulo	sc->sc_alarm = TX39_MSEC2RTC(msec);
289205277Srpaulo	tx39clock_alarm_refill(tc);
290205277Srpaulo
291205277Srpaulo	return 0;
292205277Srpaulo}
293205277Srpaulo
294205277Srpaulovoid
295205277Srpaulotx39clock_alarm_refill(tx_chipset_tag_t tc)
296205277Srpaulo{
297205277Srpaulo	struct tx39clock_softc *sc = tc->tc_clockt;
298205277Srpaulo	struct txtime t;
299205277Srpaulo	uint64_t mytime;
300205277Srpaulo
301205277Srpaulo	__tx39timer_rtcget(&t);
302205277Srpaulo
303205277Srpaulo	mytime = ((uint64_t)t.t_hi << 32) | (uint64_t)t.t_lo;
304205277Srpaulo	mytime += (uint64_t)sc->sc_alarm;
305205277Srpaulo
306170530Ssam	t.t_hi = (uint32_t)((mytime >> 32) & TX39_TIMERALARMHI_MASK);
307170530Ssam	t.t_lo = (uint32_t)(mytime & 0xffffffff);
308170530Ssam
309170530Ssam	tx_conf_write(tc, TX39_TIMERALARMHI_REG, t.t_hi);
310170530Ssam	tx_conf_write(tc, TX39_TIMERALARMLO_REG, t.t_lo);
311170530Ssam}
312170530Ssam
313170530Ssam#ifdef TX39CLOCK_DEBUG
314170530Ssamvoid
315170530Ssamtx39clock_dump(tx_chipset_tag_t tc)
316170530Ssam{
317170530Ssam	txreg_t reg;
318170530Ssam
319173273Ssam	reg = tx_conf_read(tc, TX39_CLOCKCTRL_REG);
320170530Ssam
321170530Ssam	printf(" ");
322170530Ssam	ISSETPRINT(reg, CHIM);
323170530Ssam#ifdef TX391X
324170530Ssam	ISSETPRINT(reg, VID);
325170530Ssam	ISSETPRINT(reg, MBUS);
326170530Ssam#endif /* TX391X */
327170530Ssam#ifdef TX392X
328170530Ssam	ISSETPRINT(reg, IRDA);
329170530Ssam#endif /* TX392X */
330170530Ssam	ISSETPRINT(reg, SPI);
331170530Ssam	ISSETPRINT(reg, TIMER);
332170530Ssam	ISSETPRINT(reg, FASTTIMER);
333170530Ssam#ifdef TX392X
334178354Ssam	ISSETPRINT(reg, C48MOUT);
335173462Ssam#endif /* TX392X */
336170530Ssam	ISSETPRINT(reg, SIBM);
337170530Ssam	ISSETPRINT(reg, CSER);
338170530Ssam	ISSETPRINT(reg, IR);
339170530Ssam	ISSETPRINT(reg, UARTA);
340170530Ssam	ISSETPRINT(reg, UARTB);
341178354Ssam	printf("\n");
342170530Ssam}
343170530Ssam#endif /* TX39CLOCK_DEBUG */
344170530Ssam