]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/mk48txx/mk48txx.c
This commit was generated by cvs2svn to compensate for changes in r141098,
[FreeBSD/FreeBSD.git] / sys / dev / mk48txx / mk48txx.c
1 /*-
2  * Copyright (c) 2000 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Paul Kranenburg.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *        This product includes software developed by the NetBSD
19  *        Foundation, Inc. and its contributors.
20  * 4. Neither the name of The NetBSD Foundation nor the names of its
21  *    contributors may be used to endorse or promote products derived
22  *    from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  *      from: NetBSD: mk48txx.c,v 1.15 2004/07/05 09:24:31 pk Exp
37  */
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 /*
43  * Mostek MK48T02, MK48T08, MK48T18, MK48T59 time-of-day chip subroutines.
44  */
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/bus.h>
49 #include <sys/clock.h>
50
51 #include <machine/bus.h>
52
53 #include <dev/mk48txx/mk48txxreg.h>
54 #include <dev/mk48txx/mk48txxvar.h>
55
56 #include "clock_if.h"
57
58 static uint8_t  mk48txx_def_nvrd(device_t, int);
59 static void     mk48txx_def_nvwr(device_t, int, u_int8_t);
60
61 struct {
62         const char *name;
63         bus_size_t nvramsz;
64         bus_size_t clkoff;
65         int flags;
66 #define MK48TXX_EXT_REGISTERS   1       /* Has extended register set */
67 } mk48txx_models[] = {
68         { "mk48t02", MK48T02_CLKSZ, MK48T02_CLKOFF, 0 },
69         { "mk48t08", MK48T08_CLKSZ, MK48T08_CLKOFF, 0 },
70         { "mk48t18", MK48T18_CLKSZ, MK48T18_CLKOFF, 0 },
71         { "mk48t59", MK48T59_CLKSZ, MK48T59_CLKOFF, MK48TXX_EXT_REGISTERS },
72 };
73
74 int
75 mk48txx_attach(device_t dev)
76 {
77         struct mk48txx_softc *sc;
78         int i;
79
80         sc = device_get_softc(dev);
81
82         device_printf(dev, "model %s", sc->sc_model);
83         i = sizeof(mk48txx_models) / sizeof(mk48txx_models[0]);
84         while (--i >= 0) {
85                 if (strcmp(sc->sc_model, mk48txx_models[i].name) == 0) {
86                         break;
87                 }
88         }
89         if (i < 0) {
90                 device_printf(dev, " (unsupported)\n");
91                 return (ENXIO);
92         }
93         printf("\n");
94         sc->sc_nvramsz = mk48txx_models[i].nvramsz;
95         sc->sc_clkoffset = mk48txx_models[i].clkoff;
96
97         if (sc->sc_nvrd == NULL)
98                 sc->sc_nvrd = mk48txx_def_nvrd;
99         if (sc->sc_nvwr == NULL)
100                 sc->sc_nvwr = mk48txx_def_nvwr;
101
102         if ((mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS) &&
103             ((*sc->sc_nvrd)(dev, sc->sc_clkoffset + MK48TXX_FLAGS) &
104             MK48TXX_FLAGS_BL)) {
105                 device_printf(dev, "mk48txx_attach: battery low\n");
106                 return (ENXIO);
107         }
108
109         clock_register(dev, 1000000);   /* 1 second resolution. */
110
111         return (0);
112 }
113
114 /*
115  * Get time-of-day and convert to a `struct timespec'
116  * Return 0 on success; an error number otherwise.
117  */
118 int
119 mk48txx_gettime(device_t dev, struct timespec *ts)
120 {
121         struct mk48txx_softc *sc;
122         bus_size_t clkoff;
123         struct clocktime ct;
124         int year;
125         u_int8_t csr;
126
127         sc = device_get_softc(dev);
128         clkoff = sc->sc_clkoffset;
129
130         /* enable read (stop time) */
131         csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR);
132         csr |= MK48TXX_CSR_READ;
133         (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr);
134
135 #define FROMREG(reg, mask)      ((*sc->sc_nvrd)(dev, clkoff + (reg)) & (mask))
136
137         ct.nsec = 0;
138         ct.sec = FROMBCD(FROMREG(MK48TXX_ISEC, MK48TXX_SEC_MASK));
139         ct.min = FROMBCD(FROMREG(MK48TXX_IMIN, MK48TXX_MIN_MASK));
140         ct.hour = FROMBCD(FROMREG(MK48TXX_IHOUR, MK48TXX_HOUR_MASK));
141         ct.day = FROMBCD(FROMREG(MK48TXX_IDAY, MK48TXX_DAY_MASK));
142         /* Map dow from 1 - 7 to 0 - 6; FROMBCD() isn't necessary here. */
143         ct.dow = FROMREG(MK48TXX_IWDAY, MK48TXX_WDAY_MASK) - 1;
144         ct.mon = FROMBCD(FROMREG(MK48TXX_IMON, MK48TXX_MON_MASK));
145         year = FROMBCD(FROMREG(MK48TXX_IYEAR, MK48TXX_YEAR_MASK));
146
147         /*
148          * XXX: At least the MK48T59 (probably all MK48Txx models with
149          *      extended registers) has a century bit in the MK48TXX_IWDAY
150          *      register which should be used here to make up the century
151          *      when MK48TXX_NO_CENT_ADJUST (which actually means don't
152          *      _manually_ adjust the century in the driver) is set to 1.
153          *      Sun/Solaris doesn't use this bit (probably for backwards
154          *      compatibility with Sun hardware equipped with older MK48Txx
155          *      models) and at present this driver is only used on sparc64
156          *      so not respecting the century bit doesn't really matter at
157          *      the moment but generally this should be implemented.
158          */
159
160 #undef FROMREG
161
162         year += sc->sc_year0;
163         if (year < POSIX_BASE_YEAR &&
164             (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) == 0)
165                 year += 100;
166
167         ct.year = year;
168
169         /* time wears on */
170         csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR);
171         csr &= ~MK48TXX_CSR_READ;
172         (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr);
173
174         return (clock_ct_to_ts(&ct, ts));
175 }
176
177 /*
178  * Set the time-of-day clock based on the value of the `struct timespec' arg.
179  * Return 0 on success; an error number otherwise.
180  */
181 int
182 mk48txx_settime(device_t dev, struct timespec *ts)
183 {
184         struct mk48txx_softc *sc;
185         bus_size_t clkoff;
186         struct clocktime ct;
187         u_int8_t csr;
188         int year;
189
190         sc = device_get_softc(dev);
191         clkoff = sc->sc_clkoffset;
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         year = ct.year - sc->sc_year0;
200         if (year > 99 && (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) == 0)
201                 year -= 100;
202
203         /* enable write */
204         csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR);
205         csr |= MK48TXX_CSR_WRITE;
206         (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr);
207
208 #define TOREG(reg, mask, val)                                           \
209         ((*sc->sc_nvwr)(dev, clkoff + (reg),                            \
210         ((*sc->sc_nvrd)(dev, clkoff + (reg)) & ~(mask)) |               \
211         ((val) & (mask))))
212
213         TOREG(MK48TXX_ISEC, MK48TXX_SEC_MASK, TOBCD(ct.sec));
214         TOREG(MK48TXX_IMIN, MK48TXX_MIN_MASK, TOBCD(ct.min));
215         TOREG(MK48TXX_IHOUR, MK48TXX_HOUR_MASK, TOBCD(ct.hour));
216         /* Map dow from 0 - 6 to 1 - 7; TOBCD() isn't necessary here. */
217         TOREG(MK48TXX_IWDAY, MK48TXX_WDAY_MASK, ct.dow + 1);
218         TOREG(MK48TXX_IDAY, MK48TXX_DAY_MASK, TOBCD(ct.day));
219         TOREG(MK48TXX_IMON, MK48TXX_MON_MASK, TOBCD(ct.mon));
220         TOREG(MK48TXX_IYEAR, MK48TXX_YEAR_MASK, TOBCD(year));
221
222         /*
223          * XXX: Use the century bit for storing the century when
224          *      MK48TXX_NO_CENT_ADJUST is set to 1.
225          */
226
227 #undef TOREG
228
229         /* load them up */
230         csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR);
231         csr &= ~MK48TXX_CSR_WRITE;
232         (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr);
233         return (0);
234 }
235
236 static u_int8_t
237 mk48txx_def_nvrd(device_t dev, int off)
238 {
239         struct mk48txx_softc *sc;
240
241         sc = device_get_softc(dev);
242         return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, off));
243 }
244
245 static void
246 mk48txx_def_nvwr(device_t dev, int off, u_int8_t v)
247 {
248         struct mk48txx_softc *sc;
249
250         sc = device_get_softc(dev);
251         bus_space_write_1(sc->sc_bst, sc->sc_bsh, off, v);
252 }