1/*	$NetBSD: dpclock.c,v 1.2 2009/12/12 14:44:09 tsutsui Exp $	*/
2
3/*
4 * Copyright (c) 2001 Erik Reid
5 * Copyright (c) 2001 Rafal K. Boni
6 * Copyright (c) 2001 Christopher Sekiya
7 * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
8 * All rights reserved.
9 *
10 * Portions of this code are derived from software contributed to The
11 * NetBSD Foundation by Jason R. Thorpe of the Numerical Aerospace
12 * Simulation Facility, NASA Ames Research Center.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 *    notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 * 3. The name of the author may not be used to endorse or promote products
23 *    derived from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include <sys/param.h>
38#include <sys/kernel.h>
39#include <sys/systm.h>
40#include <sys/device.h>
41
42#include <sys/bus.h>
43#include <machine/autoconf.h>
44#include <machine/sysconf.h>
45#include <machine/machtype.h>
46
47#include <dev/clock_subr.h>
48#include <sgimips/dev/dp8573areg.h>
49
50#include <sgimips/sgimips/clockvar.h>
51
52struct dpclock_softc {
53	struct device sc_dev;
54
55	struct todr_chip_handle sc_todrch;
56
57	/* RTC registers */
58	bus_space_tag_t		sc_rtct;
59	bus_space_handle_t	sc_rtch;
60};
61
62static int	dpclock_match(struct device *, struct cfdata *, void *);
63static void	dpclock_attach(struct device *, struct device *, void *);
64static int	dpclock_gettime(struct todr_chip_handle *, struct timeval *);
65static int	dpclock_settime(struct todr_chip_handle *, struct timeval *);
66
67CFATTACH_DECL(dpclock, sizeof(struct dpclock_softc),
68    dpclock_match, dpclock_attach, NULL, NULL);
69
70static int
71dpclock_match(struct device *parent, struct cfdata *cf, void *aux)
72{
73	struct mainbus_attach_args *ma = aux;
74
75	switch (mach_type) {
76	case MACH_SGI_IP6 | MACH_SGI_IP10:
77		if (ma->ma_addr == 0x1fbc0000)
78			return (1);
79		break;
80
81	case MACH_SGI_IP12:
82	case MACH_SGI_IP20:
83		if (ma->ma_addr == 0x1fb80e00)
84			return (1);
85		break;
86	}
87
88	return (0);
89}
90
91static void
92dpclock_attach(struct device *parent, struct device *self, void *aux)
93{
94	struct dpclock_softc *sc = (void *)self;
95	struct mainbus_attach_args *ma = aux;
96	int err;
97
98	printf("\n");
99
100	/*
101	 * All machines have one byte register per word. IP6/IP10 use
102	 * the MSB, others the LSB.
103	 */
104	if (mach_type == MACH_SGI_IP12 || mach_type == MACH_SGI_IP20)
105		sc->sc_rtct = SGIMIPS_BUS_SPACE_HPC;
106	else
107		sc->sc_rtct = SGIMIPS_BUS_SPACE_IP6_DPCLOCK;
108
109	if ((err = bus_space_map(sc->sc_rtct, ma->ma_addr, 0x1ffff,
110	    BUS_SPACE_MAP_LINEAR, &sc->sc_rtch)) != 0) {
111		printf(": unable to map RTC registers, error = %d\n", err);
112		return;
113	}
114
115	sc->sc_todrch.cookie = sc;
116	sc->sc_todrch.todr_gettime = dpclock_gettime;
117	sc->sc_todrch.todr_settime = dpclock_settime;
118	sc->sc_todrch.todr_setwen = NULL;
119
120	todr_attach(&sc->sc_todrch);
121}
122
123/*
124 * Get the time of day, based on the clock's value and/or the base value.
125 */
126static int
127dpclock_gettime(struct todr_chip_handle *todrch, struct timeval *tv)
128{
129	struct dpclock_softc *sc = (struct dpclock_softc *)todrch->cookie;
130	struct clock_ymdhms dt;
131	int s;
132	u_int8_t i, j;
133	u_int8_t regs[32];
134
135	s = splhigh();
136	i = bus_space_read_1(sc->sc_rtct, sc->sc_rtch, DP8573A_TIMESAVE_CTL);
137	j = i | DP8573A_TIMESAVE_CTL_EN;
138	bus_space_write_1(sc->sc_rtct, sc->sc_rtch, DP8573A_TIMESAVE_CTL, j);
139	bus_space_write_1(sc->sc_rtct, sc->sc_rtch, DP8573A_TIMESAVE_CTL, i);
140	splx(s);
141
142	for (i = 0; i < 32; i++)
143		regs[i] = bus_space_read_1(sc->sc_rtct, sc->sc_rtch, i);
144
145	dt.dt_sec = FROMBCD(regs[DP8573A_SAVE_SEC]);
146	dt.dt_min = FROMBCD(regs[DP8573A_SAVE_MIN]);
147
148	if (regs[DP8573A_RT_MODE] & DP8573A_RT_MODE_1224) {
149		dt.dt_hour = FROMBCD(regs[DP8573A_SAVE_HOUR] &
150						DP8573A_HOUR_12HR_MASK) +
151		    ((regs[DP8573A_SAVE_HOUR] & DP8573A_RT_MODE_1224) ? 0 : 12);
152
153		/*
154		 * In AM/PM mode, hour range is 01-12, so adding in 12 hours
155		 * for PM gives us 01-24, whereas we want 00-23, so map hour
156		 * 24 to hour 0.
157		 */
158
159		if (dt.dt_hour == 24)
160			dt.dt_hour = 0;
161	} else {
162		dt.dt_hour = FROMBCD(regs[DP8573A_SAVE_HOUR] &
163							DP8573A_HOUR_24HR_MASK);
164	}
165
166	dt.dt_wday = FROMBCD(regs[DP8573A_DOW]);    /* Not from time saved */
167	dt.dt_day = FROMBCD(regs[DP8573A_SAVE_DOM]);
168	dt.dt_mon = FROMBCD(regs[DP8573A_SAVE_MONTH]);
169	dt.dt_year = FROM_IRIX_YEAR(FROMBCD(regs[DP8573A_YEAR]));
170
171	/* simple sanity checks */
172	if (dt.dt_mon > 12 || dt.dt_day > 31 ||
173	    dt.dt_hour >= 24 || dt.dt_min >= 60 || dt.dt_sec >= 60)
174		return (EIO);
175
176	tv->tv_sec = (long)clock_ymdhms_to_secs(&dt);
177	if (tv->tv_sec == -1)
178		return (ERANGE);
179	tv->tv_usec = 0;
180
181	return (0);
182}
183
184/*
185 * Reset the TODR based on the time value.
186 */
187static int
188dpclock_settime(struct todr_chip_handle *todrch, struct timeval *tv)
189{
190	struct dpclock_softc *sc = (struct dpclock_softc *)todrch->cookie;
191	struct clock_ymdhms dt;
192	int s;
193	u_int8_t i, j;
194	u_int8_t regs[32];
195
196	clock_secs_to_ymdhms((time_t)(tv->tv_sec + (tv->tv_usec > 500000)),&dt);
197
198	s = splhigh();
199	i = bus_space_read_1(sc->sc_rtct, sc->sc_rtch, DP8573A_TIMESAVE_CTL);
200	j = i | DP8573A_TIMESAVE_CTL_EN;
201	bus_space_write_1(sc->sc_rtct, sc->sc_rtch, DP8573A_TIMESAVE_CTL, j);
202	bus_space_write_1(sc->sc_rtct, sc->sc_rtch, DP8573A_TIMESAVE_CTL, i);
203	splx(s);
204
205	for (i = 0; i < 32; i++)
206		regs[i] = bus_space_read_1(sc->sc_rtct, sc->sc_rtch, i);
207
208	regs[DP8573A_SUBSECOND] = 0;
209	regs[DP8573A_SECOND] = TOBCD(dt.dt_sec);
210	regs[DP8573A_MINUTE] = TOBCD(dt.dt_min);
211	regs[DP8573A_HOUR] = TOBCD(dt.dt_hour) & DP8573A_HOUR_24HR_MASK;
212	regs[DP8573A_DOW] = TOBCD(dt.dt_wday);
213	regs[DP8573A_DOM] = TOBCD(dt.dt_day);
214	regs[DP8573A_MONTH] = TOBCD(dt.dt_mon);
215	regs[DP8573A_YEAR] = TOBCD(TO_IRIX_YEAR(dt.dt_year));
216
217	s = splhigh();
218	i = bus_space_read_1(sc->sc_rtct, sc->sc_rtch, DP8573A_RT_MODE);
219	j = i & ~DP8573A_RT_MODE_CLKSS;
220	bus_space_write_1(sc->sc_rtct, sc->sc_rtch, DP8573A_RT_MODE, j);
221
222	for (i = 0; i < 10; i++)
223		bus_space_write_1(sc->sc_rtct, sc->sc_rtch, DP8573A_COUNTERS +i,
224						    regs[DP8573A_COUNTERS + i]);
225
226	bus_space_write_1(sc->sc_rtct, sc->sc_rtch, DP8573A_RT_MODE, i);
227	splx(s);
228
229	return (0);
230}
231