mc146818.c revision 170844
1139749Simp/*-
2137821Smarius * Copyright (c) 2003 Izumi Tsutsui.  All rights reserved.
3137821Smarius *
4137821Smarius * Redistribution and use in source and binary forms, with or without
5137821Smarius * modification, are permitted provided that the following conditions
6137821Smarius * are met:
7137821Smarius * 1. Redistributions of source code must retain the above copyright
8137821Smarius *    notice, this list of conditions and the following disclaimer.
9137821Smarius * 2. Redistributions in binary form must reproduce the above copyright
10137821Smarius *    notice, this list of conditions and the following disclaimer in the
11137821Smarius *    documentation and/or other materials provided with the distribution.
12137821Smarius * 3. The name of the author may not be used to endorse or promote products
13137821Smarius *    derived from this software without specific prior written permission.
14137821Smarius *
15137821Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16137821Smarius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17137821Smarius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18137821Smarius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19137821Smarius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20137821Smarius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21137821Smarius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22137821Smarius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23137821Smarius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24137821Smarius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25137821Smarius *
26137821Smarius *	from: NetBSD: mc146818.c,v 1.4 2003/11/24 06:20:40 tsutsui Exp
27137821Smarius */
28137821Smarius
29137821Smarius#include <sys/cdefs.h>
30137821Smarius__FBSDID("$FreeBSD: head/sys/dev/mc146818/mc146818.c 170844 2007-06-16 23:10:00Z marius $");
31137821Smarius
32137821Smarius/*
33137821Smarius * mc146818 and compatible time of day chip subroutines
34137821Smarius */
35137821Smarius
36137821Smarius#include <sys/param.h>
37137821Smarius#include <sys/systm.h>
38137821Smarius#include <sys/bus.h>
39137821Smarius#include <sys/clock.h>
40146417Smarius#include <sys/lock.h>
41146417Smarius#include <sys/mutex.h>
42137821Smarius
43137821Smarius#include <machine/bus.h>
44137821Smarius
45137821Smarius#include <dev/mc146818/mc146818reg.h>
46137821Smarius#include <dev/mc146818/mc146818var.h>
47137821Smarius
48137821Smarius#include "clock_if.h"
49137821Smarius
50137821Smariusstatic u_int	mc146818_def_getcent(device_t);
51137821Smariusstatic void	mc146818_def_setcent(device_t, u_int);
52137821Smarius
53137821Smariusint
54137821Smariusmc146818_attach(device_t dev)
55137821Smarius{
56137821Smarius	struct mc146818_softc *sc;
57137821Smarius
58137821Smarius	sc = device_get_softc(dev);
59137821Smarius
60146417Smarius	if (mtx_initialized(&sc->sc_mtx) == 0) {
61146417Smarius		device_printf(dev, "%s: mutex not initialized\n", __func__);
62146417Smarius		return (ENXIO);
63146417Smarius	}
64146417Smarius
65137821Smarius	if (sc->sc_mcread == NULL)
66137821Smarius		sc->sc_mcread = mc146818_def_read;
67137821Smarius	if (sc->sc_mcwrite == NULL)
68137821Smarius		sc->sc_mcwrite = mc146818_def_write;
69137821Smarius
70137821Smarius	if (sc->sc_flag & MC146818_NO_CENT_ADJUST) {
71137821Smarius		/*
72137821Smarius		 * Note that setting MC146818_NO_CENT_ADJUST means that
73137821Smarius		 * the century has to be stored in NVRAM somewhere.
74137821Smarius		 */
75137821Smarius		if (sc->sc_getcent == NULL)
76137821Smarius			sc->sc_getcent = mc146818_def_getcent;
77137821Smarius		if (sc->sc_setcent == NULL)
78137821Smarius			sc->sc_setcent = mc146818_def_setcent;
79137821Smarius	}
80137821Smarius
81146982Smarius	mtx_lock_spin(&sc->sc_mtx);
82137821Smarius	if (!(*sc->sc_mcread)(dev, MC_REGD) & MC_REGD_VRT) {
83146982Smarius		mtx_unlock_spin(&sc->sc_mtx);
84146417Smarius		device_printf(dev, "%s: battery low\n", __func__);
85137821Smarius		return (ENXIO);
86137821Smarius	}
87137821Smarius
88137821Smarius	sc->sc_rega = MC_BASE_32_KHz;
89137821Smarius	(*sc->sc_mcwrite)(dev, MC_REGA, sc->sc_rega);
90137821Smarius
91137821Smarius	sc->sc_regb = 0;
92137821Smarius	sc->sc_regb |= (sc->sc_flag & MC146818_BCD) ? 0 : MC_REGB_BINARY;
93137821Smarius	sc->sc_regb |= (sc->sc_flag & MC146818_12HR) ? 0 : MC_REGB_24HR;
94137821Smarius	(*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb);
95146982Smarius	mtx_unlock_spin(&sc->sc_mtx);
96137821Smarius
97137821Smarius	clock_register(dev, 1000000);	/* 1 second resolution. */
98137821Smarius
99137821Smarius	return (0);
100137821Smarius}
101137821Smarius
102137821Smarius/*
103137821Smarius * Get time of day and convert it to a struct timespec.
104137821Smarius * Return 0 on success, an error number otherwise.
105137821Smarius */
106137821Smariusint
107137821Smariusmc146818_gettime(device_t dev, struct timespec *ts)
108137821Smarius{
109137821Smarius	struct mc146818_softc *sc;
110137821Smarius	struct clocktime ct;
111137821Smarius	int timeout, cent, year;
112137821Smarius
113137821Smarius	sc = device_get_softc(dev);
114137821Smarius
115137821Smarius	timeout = 1000000;	/* XXX how long should we wait? */
116137821Smarius
117137821Smarius	/*
118137821Smarius	 * If MC_REGA_UIP is 0 we have at least 244us before the next
119137821Smarius	 * update. If it's 1 an update is imminent.
120137821Smarius	 */
121137821Smarius	for (;;) {
122146982Smarius		mtx_lock_spin(&sc->sc_mtx);
123137821Smarius		if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP))
124137821Smarius			break;
125146982Smarius		mtx_unlock_spin(&sc->sc_mtx);
126137821Smarius		if (--timeout < 0) {
127146417Smarius			device_printf(dev, "%s: timeout\n", __func__);
128137821Smarius			return (EBUSY);
129137821Smarius		}
130137821Smarius	}
131137821Smarius
132137821Smarius#define	FROMREG(x)	((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x))
133137821Smarius
134137821Smarius	ct.nsec = 0;
135137821Smarius	ct.sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC));
136137821Smarius	ct.min = FROMREG((*sc->sc_mcread)(dev, MC_MIN));
137137821Smarius	ct.hour = FROMREG((*sc->sc_mcread)(dev, MC_HOUR));
138146417Smarius	/* Map dow from 1 - 7 to 0 - 6. */
139137821Smarius	ct.dow = FROMREG((*sc->sc_mcread)(dev, MC_DOW)) - 1;
140137821Smarius	ct.day = FROMREG((*sc->sc_mcread)(dev, MC_DOM));
141137821Smarius	ct.mon = FROMREG((*sc->sc_mcread)(dev, MC_MONTH));
142137821Smarius	year = FROMREG((*sc->sc_mcread)(dev, MC_YEAR));
143146417Smarius	year += sc->sc_year0;
144146417Smarius	if (sc->sc_flag & MC146818_NO_CENT_ADJUST) {
145137821Smarius		cent = (*sc->sc_getcent)(dev);
146137821Smarius		year += cent * 100;
147146417Smarius	} else if (year < POSIX_BASE_YEAR)
148146417Smarius		year += 100;
149146982Smarius	mtx_unlock_spin(&sc->sc_mtx);
150137821Smarius
151137821Smarius	ct.year = year;
152137821Smarius
153137821Smarius	return (clock_ct_to_ts(&ct, ts));
154137821Smarius}
155137821Smarius
156137821Smarius#ifdef notyet
157137821Smariusint
158137821Smariusmc146818_getsecs(device_t dev, int *secp)
159137821Smarius{
160137821Smarius	struct mc146818_softc *sc;
161137821Smarius	int sec, timeout;
162137821Smarius
163137821Smarius	sc = device_get_softc(dev);
164137821Smarius
165137821Smarius	timeout = 1000000;	/* XXX how long should we wait? */
166137821Smarius
167137821Smarius	for (;;) {
168146982Smarius		mtx_lock_spin(&sc->sc_mtx);
169137821Smarius		if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) {
170137821Smarius			sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC));
171146982Smarius			mtx_unlock_spin(&sc->sc_mtx);
172137821Smarius			break;
173137821Smarius		}
174146982Smarius		mtx_unlock_spin(&sc->sc_mtx);
175137821Smarius		if (--timeout == 0) {
176146417Smarius			device_printf(dev, "%s: timeout\n", __func__);
177137821Smarius			return (EBUSY);
178137821Smarius		}
179137821Smarius	}
180137821Smarius
181137821Smarius#undef FROMREG
182137821Smarius
183137821Smarius	*secp = sec;
184137821Smarius	return (0);
185137821Smarius}
186137821Smarius#endif
187137821Smarius
188137821Smarius/*
189137821Smarius * Set the time of day clock based on the value of the struct timespec arg.
190137821Smarius * Return 0 on success, an error number otherwise.
191137821Smarius */
192137821Smariusint
193137821Smariusmc146818_settime(device_t dev, struct timespec *ts)
194137821Smarius{
195137821Smarius	struct mc146818_softc *sc;
196137821Smarius	struct clocktime ct;
197137821Smarius	int cent, year;
198137821Smarius
199137821Smarius	sc = device_get_softc(dev);
200137821Smarius
201137821Smarius	/* Accuracy is only one second. */
202137821Smarius	if (ts->tv_nsec >= 500000000)
203137821Smarius		ts->tv_sec++;
204137821Smarius	ts->tv_nsec = 0;
205137821Smarius	clock_ts_to_ct(ts, &ct);
206137821Smarius
207146982Smarius	mtx_lock_spin(&sc->sc_mtx);
208137821Smarius	/* Disable RTC updates and interrupts (if enabled). */
209137821Smarius	(*sc->sc_mcwrite)(dev, MC_REGB,
210137821Smarius	    ((sc->sc_regb & (MC_REGB_BINARY | MC_REGB_24HR)) | MC_REGB_SET));
211137821Smarius
212137821Smarius#define	TOREG(x)	((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x))
213137821Smarius
214137821Smarius	(*sc->sc_mcwrite)(dev, MC_SEC, TOREG(ct.sec));
215137821Smarius	(*sc->sc_mcwrite)(dev, MC_MIN, TOREG(ct.min));
216137821Smarius	(*sc->sc_mcwrite)(dev, MC_HOUR, TOREG(ct.hour));
217146417Smarius	/* Map dow from 0 - 6 to 1 - 7. */
218137821Smarius	(*sc->sc_mcwrite)(dev, MC_DOW, TOREG(ct.dow + 1));
219137821Smarius	(*sc->sc_mcwrite)(dev, MC_DOM, TOREG(ct.day));
220137821Smarius	(*sc->sc_mcwrite)(dev, MC_MONTH, TOREG(ct.mon));
221137821Smarius
222137821Smarius	year = ct.year - sc->sc_year0;
223146417Smarius	if (sc->sc_flag & MC146818_NO_CENT_ADJUST) {
224137821Smarius		cent = year / 100;
225137821Smarius		(*sc->sc_setcent)(dev, cent);
226137821Smarius		year -= cent * 100;
227146417Smarius	} else if (year > 99)
228137821Smarius		year -= 100;
229137821Smarius	(*sc->sc_mcwrite)(dev, MC_YEAR, TOREG(year));
230137821Smarius
231137821Smarius	/* Reenable RTC updates and interrupts. */
232137821Smarius	(*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb);
233146982Smarius	mtx_unlock_spin(&sc->sc_mtx);
234137821Smarius
235137821Smarius#undef TOREG
236137821Smarius
237137821Smarius	return (0);
238137821Smarius}
239137821Smarius
240137821Smarius#define	MC_ADDR	0
241137821Smarius#define	MC_DATA	1
242137821Smarius
243170844Smariusu_int
244137821Smariusmc146818_def_read(device_t dev, u_int reg)
245137821Smarius{
246137821Smarius	struct mc146818_softc *sc;
247137821Smarius
248137821Smarius	sc = device_get_softc(dev);
249137821Smarius	bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg);
250137821Smarius	return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, MC_DATA));
251137821Smarius}
252137821Smarius
253170844Smariusvoid
254137821Smariusmc146818_def_write(device_t dev, u_int reg, u_int val)
255137821Smarius{
256137821Smarius	struct mc146818_softc *sc;
257137821Smarius
258137821Smarius	sc = device_get_softc(dev);
259137821Smarius	bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg);
260137821Smarius	bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_DATA, val);
261137821Smarius}
262137821Smarius
263137821Smarius/*
264137821Smarius * Looks like it's common even across platforms to store the century at
265137821Smarius * 0x32 in the NVRAM of the mc146818.
266137821Smarius */
267137821Smarius#define	MC_CENT	0x32
268137821Smarius
269137821Smariusstatic u_int
270137821Smariusmc146818_def_getcent(device_t dev)
271137821Smarius{
272137821Smarius	struct mc146818_softc *sc;
273137821Smarius
274137821Smarius	sc = device_get_softc(dev);
275137821Smarius	return ((*sc->sc_mcread)(dev, MC_CENT));
276137821Smarius}
277137821Smarius
278137821Smariusstatic void
279137821Smariusmc146818_def_setcent(device_t dev, u_int cent)
280137821Smarius{
281137821Smarius	struct mc146818_softc *sc;
282137821Smarius
283137821Smarius	sc = device_get_softc(dev);
284137821Smarius	(*sc->sc_mcwrite)(dev, MC_CENT, cent);
285137821Smarius}
286