1/*	$NetBSD: tx39clock.c,v 1.26 2011/08/08 17:45:13 matt Exp $ */
2
3/*-
4 * Copyright (c) 1999-2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: tx39clock.c,v 1.26 2011/08/08 17:45:13 matt Exp $");
34
35#include "opt_tx39clock_debug.h"
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/timetc.h>
40#include <sys/device.h>
41#include <sys/bus.h>
42
43#include <dev/clock_subr.h>
44
45#include <machine/sysconf.h>
46
47#include <hpcmips/tx/tx39var.h>
48#include <hpcmips/tx/tx39icureg.h>
49#include <hpcmips/tx/tx39clockvar.h>
50#include <hpcmips/tx/tx39clockreg.h>
51#include <hpcmips/tx/tx39timerreg.h>
52
53#ifdef	TX39CLOCK_DEBUG
54#define DPRINTF_ENABLE
55#define DPRINTF_DEBUG	tx39clock_debug
56#endif
57#include <machine/debug.h>
58
59#define ISSETPRINT(r, m)						\
60	dbg_bitmask_print(r, TX39_CLOCK_EN ## m ## CLK, #m)
61
62void	tx39clock_init(device_t);
63
64struct platform_clock tx39_clock = {
65#define CLOCK_RATE	100
66	CLOCK_RATE, tx39clock_init,
67};
68
69struct txtime {
70	uint32_t t_hi;
71	uint32_t t_lo;
72};
73
74struct tx39clock_softc {
75	tx_chipset_tag_t sc_tc;
76
77	int sc_alarm;
78	int sc_enabled;
79	int sc_year;
80	struct clock_ymdhms sc_epoch;
81	struct timecounter sc_tcounter;
82};
83
84int	tx39clock_match(device_t, cfdata_t, void *);
85void	tx39clock_attach(device_t, device_t, void *);
86#ifdef TX39CLOCK_DEBUG
87void	tx39clock_dump(tx_chipset_tag_t);
88#endif
89
90void	tx39clock_cpuspeed(int *, int *);
91
92void	__tx39timer_rtcfreeze(tx_chipset_tag_t);
93void	__tx39timer_rtcreset(tx_chipset_tag_t);
94void	__tx39timer_rtcget(struct txtime *);
95time_t __tx39timer_rtc2sec(struct txtime *);
96uint32_t tx39_timecount(struct timecounter *);
97
98CFATTACH_DECL_NEW(tx39clock, sizeof(struct tx39clock_softc),
99    tx39clock_match, tx39clock_attach, NULL, NULL);
100
101int
102tx39clock_match(device_t parent, cfdata_t cf, void *aux)
103{
104
105	return ATTACH_FIRST;
106}
107
108void
109tx39clock_attach(device_t parent, device_t self, void *aux)
110{
111	struct txsim_attach_args *ta = aux;
112	struct tx39clock_softc *sc = device_private(self);
113	tx_chipset_tag_t tc;
114	txreg_t reg;
115
116	tc = sc->sc_tc = ta->ta_tc;
117	tx_conf_register_clock(tc, self);
118
119	/* Reset timer module */
120	tx_conf_write(tc, TX39_TIMERCONTROL_REG, 0);
121
122	/* Enable periodic timer */
123	reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG);
124	reg |= TX39_TIMERCONTROL_ENPERTIMER;
125	tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg);
126
127	sc->sc_enabled = 0;
128	/*
129	 * RTC and ALARM
130	 *    RTCINT    ... INTR5 bit 31  (roll over)
131	 *    ALARMINT  ... INTR5 bit 30
132	 *    PERINT    ... INTR5 bit 29
133	 */
134
135	platform_clock_attach(self, &tx39_clock);
136
137#ifdef TX39CLOCK_DEBUG
138	tx39clock_dump(tc);
139#endif /* TX39CLOCK_DEBUG */
140}
141
142/*
143 * cpuclock ... CPU clock (Hz)
144 * cpuspeed ... instructions-per-microsecond
145 */
146void
147tx39clock_cpuspeed(int *cpuclock, int *cpu_speed)
148{
149	struct txtime t0, t1;
150	int elapsed;
151
152	__tx39timer_rtcget(&t0);
153	__asm volatile(
154		".set	noreorder;		\n\t"
155		"li	$8, 10000000;		\n"
156	"1:	nop;				\n\t"
157		"nop;				\n\t"
158		"nop;				\n\t"
159		"nop;				\n\t"
160		"nop;				\n\t"
161		"nop;				\n\t"
162		"nop;				\n\t"
163		"add	$8, $8, -1;		\n\t"
164		"bnez	$8, 1b;			\n\t"
165		"nop;				\n\t"
166		".set	reorder;");
167	__tx39timer_rtcget(&t1);
168
169	elapsed = t1.t_lo - t0.t_lo;
170
171	*cpuclock = (100000000 / elapsed) * TX39_RTCLOCK;
172	*cpu_speed = *cpuclock / 1000000;
173}
174
175void
176__tx39timer_rtcfreeze(tx_chipset_tag_t tc)
177{
178	txreg_t reg;
179
180	reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG);
181
182	/* Freeze RTC */
183	reg |= TX39_TIMERCONTROL_FREEZEPRE; /* Upper 8bit */
184	reg |= TX39_TIMERCONTROL_FREEZERTC; /* Lower 32bit */
185
186	/* Freeze periodic timer */
187	reg |= TX39_TIMERCONTROL_FREEZETIMER;
188	reg &= ~TX39_TIMERCONTROL_ENPERTIMER;
189	tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg);
190}
191
192void
193__tx39timer_rtcget(struct txtime *t)
194{
195	tx_chipset_tag_t tc;
196	txreg_t reghi, reglo, oreghi, oreglo;
197	int retry;
198
199	tc = tx_conf_get_tag();
200
201	retry = 10;
202
203	do {
204		oreglo = tx_conf_read(tc, TX39_TIMERRTCLO_REG);
205		reglo = tx_conf_read(tc, TX39_TIMERRTCLO_REG);
206
207		oreghi = tx_conf_read(tc, TX39_TIMERRTCHI_REG);
208		reghi = tx_conf_read(tc, TX39_TIMERRTCHI_REG);
209	} while ((reghi != oreghi || reglo != oreglo) && (--retry > 0));
210
211	if (retry < 0) {
212		printf("RTC timer read error.\n");
213	}
214
215	t->t_hi = TX39_TIMERRTCHI(reghi);
216	t->t_lo = reglo;
217}
218
219void
220__tx39timer_rtcreset(tx_chipset_tag_t tc)
221{
222	txreg_t reg;
223
224	reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG);
225
226	/* Reset counter and stop */
227	reg |= TX39_TIMERCONTROL_RTCCLR;
228	tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg);
229
230	/* Count again */
231	reg &= ~TX39_TIMERCONTROL_RTCCLR;
232	tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg);
233}
234
235uint32_t
236tx39_timecount(struct timecounter *tch)
237{
238	tx_chipset_tag_t tc = tch->tc_priv;
239
240	/*
241	 * since we're only reading the low register, we don't care about
242	 * if the chip increments it.  we assume that the single read will
243	 * always be consistent.  This is much faster than the routine which
244	 * has to get both values, improving the quality.
245	 */
246	return tx_conf_read(tc, TX39_TIMERRTCLO_REG);
247}
248
249void
250tx39clock_init(device_t self)
251{
252	struct tx39clock_softc *sc = device_private(self);
253	tx_chipset_tag_t tc = sc->sc_tc;
254	txreg_t reg;
255	int pcnt;
256
257	/*
258	 * Setup periodic timer (interrupting hz times per second.)
259	 */
260	pcnt = TX39_TIMERCLK / CLOCK_RATE - 1;
261	reg = tx_conf_read(tc, TX39_TIMERPERIODIC_REG);
262	TX39_TIMERPERIODIC_PERVAL_CLR(reg);
263	reg = TX39_TIMERPERIODIC_PERVAL_SET(reg, pcnt);
264	tx_conf_write(tc, TX39_TIMERPERIODIC_REG, reg);
265
266	/*
267	 * Enable periodic timer
268	 */
269	reg = tx_conf_read(tc, TX39_INTRENABLE6_REG);
270	reg |= TX39_INTRPRI13_TIMER_PERIODIC_BIT;
271	tx_conf_write(tc, TX39_INTRENABLE6_REG, reg);
272
273	sc->sc_tcounter.tc_name = "tx39rtc";
274	sc->sc_tcounter.tc_get_timecount = tx39_timecount;
275	sc->sc_tcounter.tc_priv = tc;
276	sc->sc_tcounter.tc_counter_mask = 0xffffffff;
277	sc->sc_tcounter.tc_frequency = TX39_RTCLOCK;
278	sc->sc_tcounter.tc_quality = 100;
279	tc_init(&sc->sc_tcounter);
280}
281
282int
283tx39clock_alarm_set(tx_chipset_tag_t tc, int msec)
284{
285	struct tx39clock_softc *sc = tc->tc_clockt;
286
287	sc->sc_alarm = TX39_MSEC2RTC(msec);
288	tx39clock_alarm_refill(tc);
289
290	return 0;
291}
292
293void
294tx39clock_alarm_refill(tx_chipset_tag_t tc)
295{
296	struct tx39clock_softc *sc = tc->tc_clockt;
297	struct txtime t;
298	uint64_t mytime;
299
300	__tx39timer_rtcget(&t);
301
302	mytime = ((uint64_t)t.t_hi << 32) | (uint64_t)t.t_lo;
303	mytime += (uint64_t)sc->sc_alarm;
304
305	t.t_hi = (uint32_t)((mytime >> 32) & TX39_TIMERALARMHI_MASK);
306	t.t_lo = (uint32_t)(mytime & 0xffffffff);
307
308	tx_conf_write(tc, TX39_TIMERALARMHI_REG, t.t_hi);
309	tx_conf_write(tc, TX39_TIMERALARMLO_REG, t.t_lo);
310}
311
312#ifdef TX39CLOCK_DEBUG
313void
314tx39clock_dump(tx_chipset_tag_t tc)
315{
316	txreg_t reg;
317
318	reg = tx_conf_read(tc, TX39_CLOCKCTRL_REG);
319
320	printf(" ");
321	ISSETPRINT(reg, CHIM);
322#ifdef TX391X
323	ISSETPRINT(reg, VID);
324	ISSETPRINT(reg, MBUS);
325#endif /* TX391X */
326#ifdef TX392X
327	ISSETPRINT(reg, IRDA);
328#endif /* TX392X */
329	ISSETPRINT(reg, SPI);
330	ISSETPRINT(reg, TIMER);
331	ISSETPRINT(reg, FASTTIMER);
332#ifdef TX392X
333	ISSETPRINT(reg, C48MOUT);
334#endif /* TX392X */
335	ISSETPRINT(reg, SIBM);
336	ISSETPRINT(reg, CSER);
337	ISSETPRINT(reg, IR);
338	ISSETPRINT(reg, UARTA);
339	ISSETPRINT(reg, UARTB);
340	printf("\n");
341}
342#endif /* TX39CLOCK_DEBUG */
343