mc146818.c revision 139749
1/*-
2 * Copyright (c) 2003 Izumi Tsutsui.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 *	from: NetBSD: mc146818.c,v 1.4 2003/11/24 06:20:40 tsutsui Exp
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/dev/mc146818/mc146818.c 139749 2005-01-06 01:43:34Z imp $");
31
32/*
33 * mc146818 and compatible time of day chip subroutines
34 */
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/bus.h>
39#include <sys/clock.h>
40
41#include <machine/bus.h>
42
43#include <dev/mc146818/mc146818reg.h>
44#include <dev/mc146818/mc146818var.h>
45
46#include "clock_if.h"
47
48static u_int	mc146818_def_getcent(device_t);
49static void	mc146818_def_setcent(device_t, u_int);
50static u_int	mc146818_def_read(device_t, u_int);
51static void	mc146818_def_write(device_t, u_int, u_int);
52
53int
54mc146818_attach(device_t dev)
55{
56	struct mc146818_softc *sc;
57
58	sc = device_get_softc(dev);
59
60	if (sc->sc_mcread == NULL)
61		sc->sc_mcread = mc146818_def_read;
62	if (sc->sc_mcwrite == NULL)
63		sc->sc_mcwrite = mc146818_def_write;
64
65	if (sc->sc_flag & MC146818_NO_CENT_ADJUST) {
66		/*
67		 * Note that setting MC146818_NO_CENT_ADJUST means that
68		 * the century has to be stored in NVRAM somewhere.
69		 */
70		if (sc->sc_getcent == NULL)
71			sc->sc_getcent = mc146818_def_getcent;
72		if (sc->sc_setcent == NULL)
73			sc->sc_setcent = mc146818_def_setcent;
74	}
75
76	if (!(*sc->sc_mcread)(dev, MC_REGD) & MC_REGD_VRT) {
77		device_printf(dev, "mc146818_attach: battery low\n");
78		return (ENXIO);
79	}
80
81	sc->sc_rega = MC_BASE_32_KHz;
82	(*sc->sc_mcwrite)(dev, MC_REGA, sc->sc_rega);
83
84	sc->sc_regb = 0;
85	sc->sc_regb |= (sc->sc_flag & MC146818_BCD) ? 0 : MC_REGB_BINARY;
86	sc->sc_regb |= (sc->sc_flag & MC146818_12HR) ? 0 : MC_REGB_24HR;
87	(*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb);
88
89	clock_register(dev, 1000000);	/* 1 second resolution. */
90
91	return (0);
92}
93
94/*
95 * Get time of day and convert it to a struct timespec.
96 * Return 0 on success, an error number otherwise.
97 */
98int
99mc146818_gettime(device_t dev, struct timespec *ts)
100{
101	struct mc146818_softc *sc;
102	struct clocktime ct;
103	int timeout, cent, year;
104
105	sc = device_get_softc(dev);
106
107	timeout = 1000000;	/* XXX how long should we wait? */
108
109	/*
110	 * XXX:	Use a spinlock to mutex register access and increase the
111	 *	likelihood that all registers are read before an update
112	 *	occurs.
113	 */
114
115	/*
116	 * If MC_REGA_UIP is 0 we have at least 244us before the next
117	 * update. If it's 1 an update is imminent.
118	 */
119	for (;;) {
120		if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP))
121			break;
122		if (--timeout < 0) {
123			device_printf(dev, "mc146818_gettime: timeout\n");
124			return (EBUSY);
125		}
126	}
127
128#define	FROMREG(x)	((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x))
129
130	ct.nsec = 0;
131	ct.sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC));
132	ct.min = FROMREG((*sc->sc_mcread)(dev, MC_MIN));
133	ct.hour = FROMREG((*sc->sc_mcread)(dev, MC_HOUR));
134	ct.dow = FROMREG((*sc->sc_mcread)(dev, MC_DOW)) - 1;
135	ct.day = FROMREG((*sc->sc_mcread)(dev, MC_DOM));
136	ct.mon = FROMREG((*sc->sc_mcread)(dev, MC_MONTH));
137	year = FROMREG((*sc->sc_mcread)(dev, MC_YEAR));
138	if (sc->sc_getcent) {
139		cent = (*sc->sc_getcent)(dev);
140		year += cent * 100;
141	}
142
143	year += sc->sc_year0;
144	if (year < POSIX_BASE_YEAR && !(sc->sc_flag & MC146818_NO_CENT_ADJUST))
145		year += 100;
146	ct.year = year;
147
148	return (clock_ct_to_ts(&ct, ts));
149}
150
151#ifdef notyet
152int
153mc146818_getsecs(device_t dev, int *secp)
154{
155	struct mc146818_softc *sc;
156	int sec, timeout;
157
158	sc = device_get_softc(dev);
159
160	timeout = 1000000;	/* XXX how long should we wait? */
161
162	for (;;) {
163		if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) {
164			sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC));
165			break;
166		}
167		if (--timeout == 0) {
168			device_printf(dev, "mc146818_getsecs: timeout\n");
169			return (EBUSY);
170		}
171	}
172
173#undef FROMREG
174
175	*secp = sec;
176	return (0);
177}
178#endif
179
180/*
181 * Set the time of day clock based on the value of the struct timespec arg.
182 * Return 0 on success, an error number otherwise.
183 */
184int
185mc146818_settime(device_t dev, struct timespec *ts)
186{
187	struct mc146818_softc *sc;
188	struct clocktime ct;
189	int cent, year;
190
191	sc = device_get_softc(dev);
192
193	/* Accuracy is only one second. */
194	if (ts->tv_nsec >= 500000000)
195		ts->tv_sec++;
196	ts->tv_nsec = 0;
197	clock_ts_to_ct(ts, &ct);
198
199	/* Disable RTC updates and interrupts (if enabled). */
200	(*sc->sc_mcwrite)(dev, MC_REGB,
201	    ((sc->sc_regb & (MC_REGB_BINARY | MC_REGB_24HR)) | MC_REGB_SET));
202
203#define	TOREG(x)	((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x))
204
205	(*sc->sc_mcwrite)(dev, MC_SEC, TOREG(ct.sec));
206	(*sc->sc_mcwrite)(dev, MC_MIN, TOREG(ct.min));
207	(*sc->sc_mcwrite)(dev, MC_HOUR, TOREG(ct.hour));
208	(*sc->sc_mcwrite)(dev, MC_DOW, TOREG(ct.dow + 1));
209	(*sc->sc_mcwrite)(dev, MC_DOM, TOREG(ct.day));
210	(*sc->sc_mcwrite)(dev, MC_MONTH, TOREG(ct.mon));
211
212	year = ct.year - sc->sc_year0;
213	if (sc->sc_setcent) {
214		cent = year / 100;
215		(*sc->sc_setcent)(dev, cent);
216		year -= cent * 100;
217	}
218	if (year > 99 && (sc->sc_flag & MC146818_NO_CENT_ADJUST) == 0)
219		year -= 100;
220	(*sc->sc_mcwrite)(dev, MC_YEAR, TOREG(year));
221
222	/* Reenable RTC updates and interrupts. */
223	(*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb);
224
225#undef TOREG
226
227	return (0);
228}
229
230#define	MC_ADDR	0
231#define	MC_DATA	1
232
233static u_int
234mc146818_def_read(device_t dev, u_int reg)
235{
236	struct mc146818_softc *sc;
237
238	sc = device_get_softc(dev);
239	bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg);
240	return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, MC_DATA));
241}
242
243static void
244mc146818_def_write(device_t dev, u_int reg, u_int val)
245{
246	struct mc146818_softc *sc;
247
248	sc = device_get_softc(dev);
249	bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg);
250	bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_DATA, val);
251}
252
253/*
254 * Looks like it's common even across platforms to store the century at
255 * 0x32 in the NVRAM of the mc146818.
256 */
257#define	MC_CENT	0x32
258
259static u_int
260mc146818_def_getcent(device_t dev)
261{
262	struct mc146818_softc *sc;
263
264	sc = device_get_softc(dev);
265	return ((*sc->sc_mcread)(dev, MC_CENT));
266}
267
268static void
269mc146818_def_setcent(device_t dev, u_int cent)
270{
271	struct mc146818_softc *sc;
272
273	sc = device_get_softc(dev);
274	(*sc->sc_mcwrite)(dev, MC_CENT, cent);
275}
276