Deleted Added
full compact
mk48txx.c (137813) mk48txx.c (146416)
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

--- 23 unchanged lines hidden (view full) ---

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>
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

--- 23 unchanged lines hidden (view full) ---

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: head/sys/dev/mk48txx/mk48txx.c 137813 2004-11-17 12:54:12Z marius $");
40__FBSDID("$FreeBSD: head/sys/dev/mk48txx/mk48txx.c 146416 2005-05-19 21:16:50Z marius $");
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>
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#include <sys/eventhandler.h>
51#include <sys/lock.h>
52#include <sys/mutex.h>
53#include <sys/watchdog.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
58static uint8_t mk48txx_def_nvrd(device_t, int);
54
55#include <machine/bus.h>
56
57#include <dev/mk48txx/mk48txxreg.h>
58#include <dev/mk48txx/mk48txxvar.h>
59
60#include "clock_if.h"
61
62static uint8_t mk48txx_def_nvrd(device_t, int);
59static void mk48txx_def_nvwr(device_t, int, u_int8_t);
63static void mk48txx_def_nvwr(device_t, int, uint8_t);
64static void mk48txx_watchdog(void *, u_int, int *);
60
61struct {
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
74int
75mk48txx_attach(device_t dev)
76{
77 struct mk48txx_softc *sc;
78 int i;
65
66struct {
67 const char *name;
68 bus_size_t nvramsz;
69 bus_size_t clkoff;
70 int flags;
71#define MK48TXX_EXT_REGISTERS 1 /* Has extended register set */
72} mk48txx_models[] = {
73 { "mk48t02", MK48T02_CLKSZ, MK48T02_CLKOFF, 0 },
74 { "mk48t08", MK48T08_CLKSZ, MK48T08_CLKOFF, 0 },
75 { "mk48t18", MK48T18_CLKSZ, MK48T18_CLKOFF, 0 },
76 { "mk48t59", MK48T59_CLKSZ, MK48T59_CLKOFF, MK48TXX_EXT_REGISTERS },
77};
78
79int
80mk48txx_attach(device_t dev)
81{
82 struct mk48txx_softc *sc;
83 int i;
84 uint8_t wday;
79
80 sc = device_get_softc(dev);
81
85
86 sc = device_get_softc(dev);
87
88 if (mtx_initialized(&sc->sc_mtx) == 0) {
89 device_printf(dev, "%s: mutex not initialized\n", __func__);
90 return (ENXIO);
91 }
92
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) {

--- 4 unchanged lines hidden (view full) ---

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
93 device_printf(dev, "model %s", sc->sc_model);
94 i = sizeof(mk48txx_models) / sizeof(mk48txx_models[0]);
95 while (--i >= 0) {
96 if (strcmp(sc->sc_model, mk48txx_models[i].name) == 0) {
97 break;
98 }
99 }
100 if (i < 0) {

--- 4 unchanged lines hidden (view full) ---

105 sc->sc_nvramsz = mk48txx_models[i].nvramsz;
106 sc->sc_clkoffset = mk48txx_models[i].clkoff;
107
108 if (sc->sc_nvrd == NULL)
109 sc->sc_nvrd = mk48txx_def_nvrd;
110 if (sc->sc_nvwr == NULL)
111 sc->sc_nvwr = mk48txx_def_nvwr;
112
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);
113 if (mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS) {
114 mtx_lock(&sc->sc_mtx);
115 if ((*sc->sc_nvrd)(dev, sc->sc_clkoffset + MK48TXX_FLAGS) &
116 MK48TXX_FLAGS_BL) {
117 mtx_unlock(&sc->sc_mtx);
118 device_printf(dev, "%s: battery low\n", __func__);
119 return (ENXIO);
120 }
121 mtx_unlock(&sc->sc_mtx);
107 }
108
122 }
123
124 if (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) {
125 /*
126 * Use MK48TXX_WDAY_CB instead of manually adjusting the
127 * century.
128 */
129 if (!(mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS)) {
130 device_printf(dev, "%s: no century bit\n", __func__);
131 return (ENXIO);
132 } else {
133 mtx_lock(&sc->sc_mtx);
134 wday = (*sc->sc_nvrd)
135 (dev, sc->sc_clkoffset + MK48TXX_IWDAY);
136 wday |= MK48TXX_WDAY_CEB;
137 (*sc->sc_nvwr)
138 (dev, sc->sc_clkoffset + MK48TXX_IWDAY, wday);
139 mtx_unlock(&sc->sc_mtx);
140 }
141 }
142
109 clock_register(dev, 1000000); /* 1 second resolution. */
110
143 clock_register(dev, 1000000); /* 1 second resolution. */
144
145 if ((sc->sc_flag & MK48TXX_WDOG_REGISTER) &&
146 (mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS)) {
147 sc->sc_wet = EVENTHANDLER_REGISTER(watchdog_list,
148 mk48txx_watchdog, dev, 0);
149 device_printf(dev,
150 "watchdog registered, timeout intervall max. 128 sec\n");
151 }
152
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 */
118int
119mk48txx_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;
153 return (0);
154}
155
156/*
157 * Get time-of-day and convert to a `struct timespec'
158 * Return 0 on success; an error number otherwise.
159 */
160int
161mk48txx_gettime(device_t dev, struct timespec *ts)
162{
163 struct mk48txx_softc *sc;
164 bus_size_t clkoff;
165 struct clocktime ct;
166 int year;
125 u_int8_t csr;
167 uint8_t csr;
126
127 sc = device_get_softc(dev);
128 clkoff = sc->sc_clkoffset;
129
168
169 sc = device_get_softc(dev);
170 clkoff = sc->sc_clkoffset;
171
172 mtx_lock(&sc->sc_mtx);
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));
173 /* enable read (stop time) */
174 csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR);
175 csr |= MK48TXX_CSR_READ;
176 (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr);
177
178#define FROMREG(reg, mask) ((*sc->sc_nvrd)(dev, clkoff + (reg)) & (mask))
179
180 ct.nsec = 0;
181 ct.sec = FROMBCD(FROMREG(MK48TXX_ISEC, MK48TXX_SEC_MASK));
182 ct.min = FROMBCD(FROMREG(MK48TXX_IMIN, MK48TXX_MIN_MASK));
183 ct.hour = FROMBCD(FROMREG(MK48TXX_IHOUR, MK48TXX_HOUR_MASK));
184 ct.day = FROMBCD(FROMREG(MK48TXX_IDAY, MK48TXX_DAY_MASK));
185 /* Map dow from 1 - 7 to 0 - 6; FROMBCD() isn't necessary here. */
186 ct.dow = FROMREG(MK48TXX_IWDAY, MK48TXX_WDAY_MASK) - 1;
187 ct.mon = FROMBCD(FROMREG(MK48TXX_IMON, MK48TXX_MON_MASK));
188 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;
189 year += sc->sc_year0;
163 if (year < POSIX_BASE_YEAR &&
164 (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) == 0)
190 if (sc->sc_flag & MK48TXX_NO_CENT_ADJUST)
191 year += (FROMREG(MK48TXX_IWDAY, MK48TXX_WDAY_CB) >>
192 MK48TXX_WDAY_CB_SHIFT) * 100;
193 else if (year < POSIX_BASE_YEAR)
165 year += 100;
166
194 year += 100;
195
196#undef FROMREG
197
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);
198 ct.year = year;
199
200 /* time wears on */
201 csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR);
202 csr &= ~MK48TXX_CSR_READ;
203 (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr);
204 mtx_unlock(&sc->sc_mtx);
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 */
181int
182mk48txx_settime(device_t dev, struct timespec *ts)
183{
184 struct mk48txx_softc *sc;
185 bus_size_t clkoff;
186 struct clocktime ct;
205
206 return (clock_ct_to_ts(&ct, ts));
207}
208
209/*
210 * Set the time-of-day clock based on the value of the `struct timespec' arg.
211 * Return 0 on success; an error number otherwise.
212 */
213int
214mk48txx_settime(device_t dev, struct timespec *ts)
215{
216 struct mk48txx_softc *sc;
217 bus_size_t clkoff;
218 struct clocktime ct;
187 u_int8_t csr;
188 int year;
219 uint8_t csr;
220 int cent, 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
221
222 sc = device_get_softc(dev);
223 clkoff = sc->sc_clkoffset;
224
225 /* Accuracy is only one second. */
226 if (ts->tv_nsec >= 500000000)
227 ts->tv_sec++;
228 ts->tv_nsec = 0;
229 clock_ts_to_ct(ts, &ct);
230
199 year = ct.year - sc->sc_year0;
200 if (year > 99 && (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) == 0)
201 year -= 100;
202
231 mtx_lock(&sc->sc_mtx);
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));
232 /* enable write */
233 csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR);
234 csr |= MK48TXX_CSR_WRITE;
235 (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr);
236
237#define TOREG(reg, mask, val) \
238 ((*sc->sc_nvwr)(dev, clkoff + (reg), \
239 ((*sc->sc_nvrd)(dev, clkoff + (reg)) & ~(mask)) | \
240 ((val) & (mask))))
241
242 TOREG(MK48TXX_ISEC, MK48TXX_SEC_MASK, TOBCD(ct.sec));
243 TOREG(MK48TXX_IMIN, MK48TXX_MIN_MASK, TOBCD(ct.min));
244 TOREG(MK48TXX_IHOUR, MK48TXX_HOUR_MASK, TOBCD(ct.hour));
245 /* Map dow from 0 - 6 to 1 - 7; TOBCD() isn't necessary here. */
246 TOREG(MK48TXX_IWDAY, MK48TXX_WDAY_MASK, ct.dow + 1);
247 TOREG(MK48TXX_IDAY, MK48TXX_DAY_MASK, TOBCD(ct.day));
248 TOREG(MK48TXX_IMON, MK48TXX_MON_MASK, TOBCD(ct.mon));
249
250 year = ct.year - sc->sc_year0;
251 if (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) {
252 cent = year / 100;
253 TOREG(MK48TXX_IWDAY, MK48TXX_WDAY_CB,
254 cent << MK48TXX_WDAY_CB_SHIFT);
255 year -= cent * 100;
256 } else if (year > 99)
257 year -= 100;
220 TOREG(MK48TXX_IYEAR, MK48TXX_YEAR_MASK, TOBCD(year));
221
258 TOREG(MK48TXX_IYEAR, MK48TXX_YEAR_MASK, TOBCD(year));
259
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);
260#undef TOREG
261
262 /* load them up */
263 csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR);
264 csr &= ~MK48TXX_CSR_WRITE;
265 (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr);
266 mtx_unlock(&sc->sc_mtx);
233 return (0);
234}
235
267 return (0);
268}
269
236static u_int8_t
270static uint8_t
237mk48txx_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
245static void
271mk48txx_def_nvrd(device_t dev, int off)
272{
273 struct mk48txx_softc *sc;
274
275 sc = device_get_softc(dev);
276 return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, off));
277}
278
279static void
246mk48txx_def_nvwr(device_t dev, int off, u_int8_t v)
280mk48txx_def_nvwr(device_t dev, int off, uint8_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}
281{
282 struct mk48txx_softc *sc;
283
284 sc = device_get_softc(dev);
285 bus_space_write_1(sc->sc_bst, sc->sc_bsh, off, v);
286}
287
288static void
289mk48txx_watchdog(void *arg, u_int cmd, int *error)
290{
291 device_t dev;
292 struct mk48txx_softc *sc;
293 uint8_t t, wdog;
294
295 dev = arg;
296 sc = device_get_softc(dev);
297
298 wdog = 0;
299 t = cmd & WD_INTERVAL;
300 if (cmd != 0 && t >= 26 && t <= 37) {
301 if (t <= WD_TO_2SEC) {
302 wdog |= MK48TXX_WDOG_RB_1_16;
303 t -= 26;
304 } else if (t <= WD_TO_8SEC) {
305 wdog |= MK48TXX_WDOG_RB_1_4;
306 t -= WD_TO_250MS;
307 } else if (t <= WD_TO_32SEC) {
308 wdog |= MK48TXX_WDOG_RB_1;
309 t -= WD_TO_1SEC;
310 } else {
311 wdog |= MK48TXX_WDOG_RB_4;
312 t -= WD_TO_4SEC;
313 }
314 wdog |= (min(1 << t,
315 MK48TXX_WDOG_BMB_MASK >> MK48TXX_WDOG_BMB_SHIFT)) <<
316 MK48TXX_WDOG_BMB_SHIFT;
317 if (sc->sc_flag & MK48TXX_WDOG_ENABLE_WDS)
318 wdog |= MK48TXX_WDOG_WDS;
319 *error = 0;
320 }
321 mtx_lock(&sc->sc_mtx);
322 (*sc->sc_nvwr)(dev, sc->sc_clkoffset + MK48TXX_WDOG, wdog);
323 mtx_unlock(&sc->sc_mtx);
324}