xref: /NextBSD/sys/dev/mc146818/mc146818.c (revision 39c8125591b55233b6903033f1555880a8cd2725)
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  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  *	$NetBSD: mc146818.c,v 1.16 2008/05/14 13:29:28 tsutsui Exp $
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 /*
31  * mc146818 and compatible time of day chip subroutines
32  */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37 #include <sys/clock.h>
38 #include <sys/lock.h>
39 #include <sys/mutex.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 
48 static u_int	mc146818_def_getcent(device_t);
49 static void	mc146818_def_setcent(device_t, u_int);
50 
51 int
mc146818_attach(device_t dev)52 mc146818_attach(device_t dev)
53 {
54 	struct mc146818_softc *sc;
55 
56 	sc = device_get_softc(dev);
57 
58 	if (mtx_initialized(&sc->sc_mtx) == 0) {
59 		device_printf(dev, "%s: mutex not initialized\n", __func__);
60 		return (ENXIO);
61 	}
62 
63 	if (sc->sc_mcread == NULL)
64 		sc->sc_mcread = mc146818_def_read;
65 	if (sc->sc_mcwrite == NULL)
66 		sc->sc_mcwrite = mc146818_def_write;
67 
68 	if (sc->sc_flag & MC146818_NO_CENT_ADJUST) {
69 		/*
70 		 * Note that setting MC146818_NO_CENT_ADJUST means that
71 		 * the century has to be stored in NVRAM somewhere.
72 		 */
73 		if (sc->sc_getcent == NULL)
74 			sc->sc_getcent = mc146818_def_getcent;
75 		if (sc->sc_setcent == NULL)
76 			sc->sc_setcent = mc146818_def_setcent;
77 	}
78 
79 	mtx_lock_spin(&sc->sc_mtx);
80 	if (((*sc->sc_mcread)(dev, MC_REGD) & MC_REGD_VRT) == 0) {
81 		mtx_unlock_spin(&sc->sc_mtx);
82 		device_printf(dev, "%s: battery low\n", __func__);
83 		return (ENXIO);
84 	}
85 
86 	sc->sc_rega = MC_BASE_32_KHz;
87 	(*sc->sc_mcwrite)(dev, MC_REGA, sc->sc_rega);
88 
89 	sc->sc_regb = 0;
90 	sc->sc_regb |= (sc->sc_flag & MC146818_BCD) ? 0 : MC_REGB_BINARY;
91 	sc->sc_regb |= (sc->sc_flag & MC146818_12HR) ? 0 : MC_REGB_24HR;
92 	(*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb);
93 	mtx_unlock_spin(&sc->sc_mtx);
94 
95 	clock_register(dev, 1000000);	/* 1 second resolution */
96 
97 	return (0);
98 }
99 
100 /*
101  * Get time of day and convert it to a struct timespec.
102  * Return 0 on success, an error number otherwise.
103  */
104 int
mc146818_gettime(device_t dev,struct timespec * ts)105 mc146818_gettime(device_t dev, struct timespec *ts)
106 {
107 	struct mc146818_softc *sc;
108 	struct clocktime ct;
109 	int timeout, cent, year;
110 
111 	sc = device_get_softc(dev);
112 
113 	timeout = 1000000;	/* XXX how long should we wait? */
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 		mtx_lock_spin(&sc->sc_mtx);
121 		if (((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP) == 0)
122 			break;
123 		mtx_unlock_spin(&sc->sc_mtx);
124 		if (--timeout < 0) {
125 			device_printf(dev, "%s: timeout\n", __func__);
126 			return (EBUSY);
127 		}
128 	}
129 
130 #define	FROMREG(x)	((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x))
131 
132 	ct.nsec = 0;
133 	ct.sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC));
134 	ct.min = FROMREG((*sc->sc_mcread)(dev, MC_MIN));
135 	ct.hour = FROMREG((*sc->sc_mcread)(dev, MC_HOUR));
136 	/* Map dow from 1 - 7 to 0 - 6. */
137 	ct.dow = FROMREG((*sc->sc_mcread)(dev, MC_DOW)) - 1;
138 	ct.day = FROMREG((*sc->sc_mcread)(dev, MC_DOM));
139 	ct.mon = FROMREG((*sc->sc_mcread)(dev, MC_MONTH));
140 	year = FROMREG((*sc->sc_mcread)(dev, MC_YEAR));
141 	year += sc->sc_year0;
142 	if (sc->sc_flag & MC146818_NO_CENT_ADJUST) {
143 		cent = (*sc->sc_getcent)(dev);
144 		year += cent * 100;
145 	} else if (year < POSIX_BASE_YEAR)
146 		year += 100;
147 	mtx_unlock_spin(&sc->sc_mtx);
148 
149 	ct.year = year;
150 
151 	return (clock_ct_to_ts(&ct, ts));
152 }
153 
154 #ifdef notyet
155 int
mc146818_getsecs(device_t dev,int * secp)156 mc146818_getsecs(device_t dev, int *secp)
157 {
158 	struct mc146818_softc *sc;
159 	int sec, timeout;
160 
161 	sc = device_get_softc(dev);
162 
163 	timeout = 1000000;	/* XXX how long should we wait? */
164 
165 	for (;;) {
166 		mtx_lock_spin(&sc->sc_mtx);
167 		if (((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP) == 0) {
168 			sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC));
169 			mtx_unlock_spin(&sc->sc_mtx);
170 			break;
171 		}
172 		mtx_unlock_spin(&sc->sc_mtx);
173 		if (--timeout == 0) {
174 			device_printf(dev, "%s: timeout\n", __func__);
175 			return (EBUSY);
176 		}
177 	}
178 
179 #undef FROMREG
180 
181 	*secp = sec;
182 	return (0);
183 }
184 #endif
185 
186 /*
187  * Set the time of day clock based on the value of the struct timespec arg.
188  * Return 0 on success, an error number otherwise.
189  */
190 int
mc146818_settime(device_t dev,struct timespec * ts)191 mc146818_settime(device_t dev, struct timespec *ts)
192 {
193 	struct mc146818_softc *sc;
194 	struct clocktime ct;
195 	int cent, year;
196 
197 	sc = device_get_softc(dev);
198 
199 	/* Accuracy is only one second. */
200 	if (ts->tv_nsec >= 500000000)
201 		ts->tv_sec++;
202 	ts->tv_nsec = 0;
203 	clock_ts_to_ct(ts, &ct);
204 
205 	mtx_lock_spin(&sc->sc_mtx);
206 	/* Disable RTC updates and interrupts (if enabled). */
207 	(*sc->sc_mcwrite)(dev, MC_REGB,
208 	    ((sc->sc_regb & (MC_REGB_BINARY | MC_REGB_24HR)) | MC_REGB_SET));
209 
210 #define	TOREG(x)	((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x))
211 
212 	(*sc->sc_mcwrite)(dev, MC_SEC, TOREG(ct.sec));
213 	(*sc->sc_mcwrite)(dev, MC_MIN, TOREG(ct.min));
214 	(*sc->sc_mcwrite)(dev, MC_HOUR, TOREG(ct.hour));
215 	/* Map dow from 0 - 6 to 1 - 7. */
216 	(*sc->sc_mcwrite)(dev, MC_DOW, TOREG(ct.dow + 1));
217 	(*sc->sc_mcwrite)(dev, MC_DOM, TOREG(ct.day));
218 	(*sc->sc_mcwrite)(dev, MC_MONTH, TOREG(ct.mon));
219 
220 	year = ct.year - sc->sc_year0;
221 	if (sc->sc_flag & MC146818_NO_CENT_ADJUST) {
222 		cent = year / 100;
223 		(*sc->sc_setcent)(dev, cent);
224 		year -= cent * 100;
225 	} else if (year > 99)
226 		year -= 100;
227 	(*sc->sc_mcwrite)(dev, MC_YEAR, TOREG(year));
228 
229 	/* Reenable RTC updates and interrupts. */
230 	(*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb);
231 	mtx_unlock_spin(&sc->sc_mtx);
232 
233 #undef TOREG
234 
235 	return (0);
236 }
237 
238 #define	MC_ADDR	0
239 #define	MC_DATA	1
240 
241 u_int
mc146818_def_read(device_t dev,u_int reg)242 mc146818_def_read(device_t dev, u_int reg)
243 {
244 	struct mc146818_softc *sc;
245 
246 	sc = device_get_softc(dev);
247 	bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg);
248 	return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, MC_DATA));
249 }
250 
251 void
mc146818_def_write(device_t dev,u_int reg,u_int val)252 mc146818_def_write(device_t dev, u_int reg, u_int val)
253 {
254 	struct mc146818_softc *sc;
255 
256 	sc = device_get_softc(dev);
257 	bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg);
258 	bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_DATA, val);
259 }
260 
261 #undef MC_ADDR
262 #undef MC_DATA
263 
264 /*
265  * Looks like it's common even across platforms to store the century at
266  * 0x32 in the NVRAM of the mc146818.
267  */
268 #define	MC_CENT	0x32
269 
270 static u_int
mc146818_def_getcent(device_t dev)271 mc146818_def_getcent(device_t dev)
272 {
273 	struct mc146818_softc *sc;
274 
275 	sc = device_get_softc(dev);
276 	return ((*sc->sc_mcread)(dev, MC_CENT));
277 }
278 
279 static void
mc146818_def_setcent(device_t dev,u_int cent)280 mc146818_def_setcent(device_t dev, u_int cent)
281 {
282 	struct mc146818_softc *sc;
283 
284 	sc = device_get_softc(dev);
285 	(*sc->sc_mcwrite)(dev, MC_CENT, cent);
286 }
287 
288 #undef MC_CENT
289