]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/mc146818/mc146818.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / dev / mc146818 / mc146818.c
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$");
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 #include <sys/lock.h>
41 #include <sys/mutex.h>
42
43 #include <machine/bus.h>
44
45 #include <dev/mc146818/mc146818reg.h>
46 #include <dev/mc146818/mc146818var.h>
47
48 #include "clock_if.h"
49
50 static u_int    mc146818_def_getcent(device_t);
51 static void     mc146818_def_setcent(device_t, u_int);
52
53 int
54 mc146818_attach(device_t dev)
55 {
56         struct mc146818_softc *sc;
57
58         sc = device_get_softc(dev);
59
60         if (mtx_initialized(&sc->sc_mtx) == 0) {
61                 device_printf(dev, "%s: mutex not initialized\n", __func__);
62                 return (ENXIO);
63         }
64
65         if (sc->sc_mcread == NULL)
66                 sc->sc_mcread = mc146818_def_read;
67         if (sc->sc_mcwrite == NULL)
68                 sc->sc_mcwrite = mc146818_def_write;
69
70         if (sc->sc_flag & MC146818_NO_CENT_ADJUST) {
71                 /*
72                  * Note that setting MC146818_NO_CENT_ADJUST means that
73                  * the century has to be stored in NVRAM somewhere.
74                  */
75                 if (sc->sc_getcent == NULL)
76                         sc->sc_getcent = mc146818_def_getcent;
77                 if (sc->sc_setcent == NULL)
78                         sc->sc_setcent = mc146818_def_setcent;
79         }
80
81         mtx_lock_spin(&sc->sc_mtx);
82         if (!(*sc->sc_mcread)(dev, MC_REGD) & MC_REGD_VRT) {
83                 mtx_unlock_spin(&sc->sc_mtx);
84                 device_printf(dev, "%s: battery low\n", __func__);
85                 return (ENXIO);
86         }
87
88         sc->sc_rega = MC_BASE_32_KHz;
89         (*sc->sc_mcwrite)(dev, MC_REGA, sc->sc_rega);
90
91         sc->sc_regb = 0;
92         sc->sc_regb |= (sc->sc_flag & MC146818_BCD) ? 0 : MC_REGB_BINARY;
93         sc->sc_regb |= (sc->sc_flag & MC146818_12HR) ? 0 : MC_REGB_24HR;
94         (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb);
95         mtx_unlock_spin(&sc->sc_mtx);
96
97         clock_register(dev, 1000000);   /* 1 second resolution. */
98
99         return (0);
100 }
101
102 /*
103  * Get time of day and convert it to a struct timespec.
104  * Return 0 on success, an error number otherwise.
105  */
106 int
107 mc146818_gettime(device_t dev, struct timespec *ts)
108 {
109         struct mc146818_softc *sc;
110         struct clocktime ct;
111         int timeout, cent, year;
112
113         sc = device_get_softc(dev);
114
115         timeout = 1000000;      /* XXX how long should we wait? */
116
117         /*
118          * If MC_REGA_UIP is 0 we have at least 244us before the next
119          * update. If it's 1 an update is imminent.
120          */
121         for (;;) {
122                 mtx_lock_spin(&sc->sc_mtx);
123                 if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP))
124                         break;
125                 mtx_unlock_spin(&sc->sc_mtx);
126                 if (--timeout < 0) {
127                         device_printf(dev, "%s: timeout\n", __func__);
128                         return (EBUSY);
129                 }
130         }
131
132 #define FROMREG(x)      ((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x))
133
134         ct.nsec = 0;
135         ct.sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC));
136         ct.min = FROMREG((*sc->sc_mcread)(dev, MC_MIN));
137         ct.hour = FROMREG((*sc->sc_mcread)(dev, MC_HOUR));
138         /* Map dow from 1 - 7 to 0 - 6. */
139         ct.dow = FROMREG((*sc->sc_mcread)(dev, MC_DOW)) - 1;
140         ct.day = FROMREG((*sc->sc_mcread)(dev, MC_DOM));
141         ct.mon = FROMREG((*sc->sc_mcread)(dev, MC_MONTH));
142         year = FROMREG((*sc->sc_mcread)(dev, MC_YEAR));
143         year += sc->sc_year0;
144         if (sc->sc_flag & MC146818_NO_CENT_ADJUST) {
145                 cent = (*sc->sc_getcent)(dev);
146                 year += cent * 100;
147         } else if (year < POSIX_BASE_YEAR)
148                 year += 100;
149         mtx_unlock_spin(&sc->sc_mtx);
150
151         ct.year = year;
152
153         return (clock_ct_to_ts(&ct, ts));
154 }
155
156 #ifdef notyet
157 int
158 mc146818_getsecs(device_t dev, int *secp)
159 {
160         struct mc146818_softc *sc;
161         int sec, timeout;
162
163         sc = device_get_softc(dev);
164
165         timeout = 1000000;      /* XXX how long should we wait? */
166
167         for (;;) {
168                 mtx_lock_spin(&sc->sc_mtx);
169                 if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) {
170                         sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC));
171                         mtx_unlock_spin(&sc->sc_mtx);
172                         break;
173                 }
174                 mtx_unlock_spin(&sc->sc_mtx);
175                 if (--timeout == 0) {
176                         device_printf(dev, "%s: timeout\n", __func__);
177                         return (EBUSY);
178                 }
179         }
180
181 #undef FROMREG
182
183         *secp = sec;
184         return (0);
185 }
186 #endif
187
188 /*
189  * Set the time of day clock based on the value of the struct timespec arg.
190  * Return 0 on success, an error number otherwise.
191  */
192 int
193 mc146818_settime(device_t dev, struct timespec *ts)
194 {
195         struct mc146818_softc *sc;
196         struct clocktime ct;
197         int cent, year;
198
199         sc = device_get_softc(dev);
200
201         /* Accuracy is only one second. */
202         if (ts->tv_nsec >= 500000000)
203                 ts->tv_sec++;
204         ts->tv_nsec = 0;
205         clock_ts_to_ct(ts, &ct);
206
207         mtx_lock_spin(&sc->sc_mtx);
208         /* Disable RTC updates and interrupts (if enabled). */
209         (*sc->sc_mcwrite)(dev, MC_REGB,
210             ((sc->sc_regb & (MC_REGB_BINARY | MC_REGB_24HR)) | MC_REGB_SET));
211
212 #define TOREG(x)        ((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x))
213
214         (*sc->sc_mcwrite)(dev, MC_SEC, TOREG(ct.sec));
215         (*sc->sc_mcwrite)(dev, MC_MIN, TOREG(ct.min));
216         (*sc->sc_mcwrite)(dev, MC_HOUR, TOREG(ct.hour));
217         /* Map dow from 0 - 6 to 1 - 7. */
218         (*sc->sc_mcwrite)(dev, MC_DOW, TOREG(ct.dow + 1));
219         (*sc->sc_mcwrite)(dev, MC_DOM, TOREG(ct.day));
220         (*sc->sc_mcwrite)(dev, MC_MONTH, TOREG(ct.mon));
221
222         year = ct.year - sc->sc_year0;
223         if (sc->sc_flag & MC146818_NO_CENT_ADJUST) {
224                 cent = year / 100;
225                 (*sc->sc_setcent)(dev, cent);
226                 year -= cent * 100;
227         } else if (year > 99)
228                 year -= 100;
229         (*sc->sc_mcwrite)(dev, MC_YEAR, TOREG(year));
230
231         /* Reenable RTC updates and interrupts. */
232         (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb);
233         mtx_unlock_spin(&sc->sc_mtx);
234
235 #undef TOREG
236
237         return (0);
238 }
239
240 #define MC_ADDR 0
241 #define MC_DATA 1
242
243 u_int
244 mc146818_def_read(device_t dev, u_int reg)
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         return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, MC_DATA));
251 }
252
253 void
254 mc146818_def_write(device_t dev, u_int reg, u_int val)
255 {
256         struct mc146818_softc *sc;
257
258         sc = device_get_softc(dev);
259         bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg);
260         bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_DATA, val);
261 }
262
263 /*
264  * Looks like it's common even across platforms to store the century at
265  * 0x32 in the NVRAM of the mc146818.
266  */
267 #define MC_CENT 0x32
268
269 static u_int
270 mc146818_def_getcent(device_t dev)
271 {
272         struct mc146818_softc *sc;
273
274         sc = device_get_softc(dev);
275         return ((*sc->sc_mcread)(dev, MC_CENT));
276 }
277
278 static void
279 mc146818_def_setcent(device_t dev, u_int cent)
280 {
281         struct mc146818_softc *sc;
282
283         sc = device_get_softc(dev);
284         (*sc->sc_mcwrite)(dev, MC_CENT, cent);
285 }