1 /* $NetBSD: m41st84.c,v 1.14 2009/01/09 16:09:43 briggs Exp $ */
4 * Copyright (c) 2003 Wasabi Systems, Inc.
7 * Written by Steve C. Woodford and Jason R. Thorpe for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: m41st84.c,v 1.14 2009/01/09 16:09:43 briggs Exp $");
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 #include <sys/kernel.h>
45 #include <sys/fcntl.h>
48 #include <sys/event.h>
50 #include <dev/clock_subr.h>
52 #include <dev/i2c/i2cvar.h>
53 #include <dev/i2c/m41st84reg.h>
54 #include <dev/i2c/m41st84var.h>
61 struct todr_chip_handle sc_todr
;
64 static void strtc_attach(device_t
, device_t
, void *);
65 static int strtc_match(device_t
, cfdata_t
, void *);
67 CFATTACH_DECL_NEW(strtc
, sizeof(struct strtc_softc
),
68 strtc_match
, strtc_attach
, NULL
, NULL
);
69 extern struct cfdriver strtc_cd
;
71 dev_type_open(strtc_open
);
72 dev_type_close(strtc_close
);
73 dev_type_read(strtc_read
);
74 dev_type_write(strtc_write
);
76 const struct cdevsw strtc_cdevsw
= {
77 strtc_open
, strtc_close
, strtc_read
, strtc_write
, noioctl
,
78 nostop
, notty
, nopoll
, nommap
, nokqfilter
, D_OTHER
81 static int strtc_clock_read(struct strtc_softc
*, struct clock_ymdhms
*);
82 static int strtc_clock_write(struct strtc_softc
*, struct clock_ymdhms
*);
83 static int strtc_gettime(struct todr_chip_handle
*, struct timeval
*);
84 static int strtc_settime(struct todr_chip_handle
*, struct timeval
*);
87 strtc_match(device_t parent
, cfdata_t cf
, void *arg
)
89 struct i2c_attach_args
*ia
= arg
;
91 if (ia
->ia_addr
== M41ST84_ADDR
)
98 strtc_attach(device_t parent
, device_t self
, void *arg
)
100 struct strtc_softc
*sc
= device_private(self
);
101 struct i2c_attach_args
*ia
= arg
;
103 aprint_naive(": Real-time Clock/NVRAM\n");
104 aprint_normal(": M41ST84 Real-time Clock/NVRAM\n");
106 sc
->sc_tag
= ia
->ia_tag
;
107 sc
->sc_address
= ia
->ia_addr
;
110 sc
->sc_todr
.cookie
= sc
;
111 sc
->sc_todr
.todr_gettime
= strtc_gettime
;
112 sc
->sc_todr
.todr_settime
= strtc_settime
;
113 sc
->sc_todr
.todr_setwen
= NULL
;
115 todr_attach(&sc
->sc_todr
);
120 strtc_open(dev_t dev
, int flag
, int fmt
, struct lwp
*l
)
122 struct strtc_softc
*sc
;
124 if ((sc
= device_lookup_private(&strtc_cd
, minor(dev
))) == NULL
)
138 strtc_close(dev_t dev
, int flag
, int fmt
, struct lwp
*l
)
140 struct strtc_softc
*sc
;
142 if ((sc
= device_lookup_private(&strtc_cd
, minor(dev
))) == NULL
)
151 strtc_read(dev_t dev
, struct uio
*uio
, int flags
)
153 struct strtc_softc
*sc
;
154 u_int8_t ch
, cmdbuf
[1];
157 if ((sc
= device_lookup_private(&strtc_cd
, minor(dev
))) == NULL
)
160 if (uio
->uio_offset
>= M41ST84_USER_RAM_SIZE
)
163 if ((error
= iic_acquire_bus(sc
->sc_tag
, 0)) != 0)
166 while (uio
->uio_resid
&& uio
->uio_offset
< M41ST84_USER_RAM_SIZE
) {
167 a
= (int)uio
->uio_offset
;
168 cmdbuf
[0] = a
+ M41ST84_USER_RAM
;
169 if ((error
= iic_exec(sc
->sc_tag
, I2C_OP_READ_WITH_STOP
,
170 sc
->sc_address
, cmdbuf
, 1,
172 iic_release_bus(sc
->sc_tag
, 0);
173 aprint_error_dev(sc
->sc_dev
,
174 "strtc_read: read failed at 0x%x\n", a
);
177 if ((error
= uiomove(&ch
, 1, uio
)) != 0) {
178 iic_release_bus(sc
->sc_tag
, 0);
183 iic_release_bus(sc
->sc_tag
, 0);
190 strtc_write(dev_t dev
, struct uio
*uio
, int flags
)
192 struct strtc_softc
*sc
;
196 if ((sc
= device_lookup_private(&strtc_cd
, minor(dev
))) == NULL
)
199 if (uio
->uio_offset
>= M41ST84_USER_RAM_SIZE
)
202 if ((error
= iic_acquire_bus(sc
->sc_tag
, 0)) != 0)
205 while (uio
->uio_resid
&& uio
->uio_offset
< M41ST84_USER_RAM_SIZE
) {
206 a
= (int)uio
->uio_offset
;
207 cmdbuf
[0] = a
+ M41ST84_USER_RAM
;
208 if ((error
= uiomove(&cmdbuf
[1], 1, uio
)) != 0)
211 if ((error
= iic_exec(sc
->sc_tag
,
212 uio
->uio_resid
? I2C_OP_WRITE
: I2C_OP_WRITE_WITH_STOP
,
213 sc
->sc_address
, cmdbuf
, 1, &cmdbuf
[1], 1, 0)) != 0) {
214 aprint_error_dev(sc
->sc_dev
,
215 "strtc_write: write failed at 0x%x\n", a
);
220 iic_release_bus(sc
->sc_tag
, 0);
226 strtc_gettime(struct todr_chip_handle
*ch
, struct timeval
*tv
)
228 struct strtc_softc
*sc
= ch
->cookie
;
229 struct clock_ymdhms dt
, check
;
232 memset(&dt
, 0, sizeof(dt
));
233 memset(&check
, 0, sizeof(check
));
236 * Since we don't support Burst Read, we have to read the clock twice
237 * until we get two consecutive identical results.
241 strtc_clock_read(sc
, &dt
);
242 strtc_clock_read(sc
, &check
);
243 } while (memcmp(&dt
, &check
, sizeof(check
)) != 0 && --retries
);
245 tv
->tv_sec
= clock_ymdhms_to_secs(&dt
);
252 strtc_settime(struct todr_chip_handle
*ch
, struct timeval
*tv
)
254 struct strtc_softc
*sc
= ch
->cookie
;
255 struct clock_ymdhms dt
;
257 clock_secs_to_ymdhms(tv
->tv_sec
, &dt
);
259 if (strtc_clock_write(sc
, &dt
) == 0)
266 strtc_clock_read(struct strtc_softc
*sc
, struct clock_ymdhms
*dt
)
268 u_int8_t bcd
[M41ST84_REG_DATE_BYTES
], cmdbuf
[1];
271 if (iic_acquire_bus(sc
->sc_tag
, I2C_F_POLL
)) {
272 aprint_error_dev(sc
->sc_dev
,
273 "strtc_clock_read: failed to acquire I2C bus\n");
278 * Check for the HT bit -- if set, then clock lost power & stopped
279 * If that happened, then clear the bit so that the clock will have
280 * a chance to run again.
282 cmdbuf
[0] = M41ST84_REG_AL_HOUR
;
283 if (iic_exec(sc
->sc_tag
, I2C_OP_READ
, sc
->sc_address
,
284 cmdbuf
, 1, &cmdbuf
[1], 1, I2C_F_POLL
)) {
285 iic_release_bus(sc
->sc_tag
, I2C_F_POLL
);
286 aprint_error_dev(sc
->sc_dev
,
287 "strtc_clock_read: failed to read HT\n");
290 if (cmdbuf
[1] & M41ST84_AL_HOUR_HT
) {
291 cmdbuf
[1] &= ~M41ST84_AL_HOUR_HT
;
292 if (iic_exec(sc
->sc_tag
, I2C_OP_WRITE
, sc
->sc_address
,
293 cmdbuf
, 1, &cmdbuf
[1], 1, I2C_F_POLL
)) {
294 iic_release_bus(sc
->sc_tag
, I2C_F_POLL
);
295 aprint_error_dev(sc
->sc_dev
,
296 "strtc_clock_read: failed to reset HT\n");
301 /* Read each RTC register in order. */
302 for (i
= M41ST84_REG_CSEC
; i
< M41ST84_REG_DATE_BYTES
; i
++) {
305 if (iic_exec(sc
->sc_tag
, I2C_OP_READ_WITH_STOP
,
306 sc
->sc_address
, cmdbuf
, 1,
307 &bcd
[i
], 1, I2C_F_POLL
)) {
308 iic_release_bus(sc
->sc_tag
, I2C_F_POLL
);
309 aprint_error_dev(sc
->sc_dev
,
310 "strtc_clock_read: failed to read rtc "
317 iic_release_bus(sc
->sc_tag
, I2C_F_POLL
);
320 * Convert the M41ST84's register values into something useable
322 dt
->dt_sec
= FROMBCD(bcd
[M41ST84_REG_SEC
] & M41ST84_SEC_MASK
);
323 dt
->dt_min
= FROMBCD(bcd
[M41ST84_REG_MIN
] & M41ST84_MIN_MASK
);
324 dt
->dt_hour
= FROMBCD(bcd
[M41ST84_REG_CENHR
] & M41ST84_HOUR_MASK
);
325 dt
->dt_day
= FROMBCD(bcd
[M41ST84_REG_DATE
] & M41ST84_DATE_MASK
);
326 dt
->dt_mon
= FROMBCD(bcd
[M41ST84_REG_MONTH
] & M41ST84_MONTH_MASK
);
328 /* XXX: Should be an MD way to specify EPOCH used by BIOS/Firmware */
329 dt
->dt_year
= FROMBCD(bcd
[M41ST84_REG_YEAR
]) + POSIX_BASE_YEAR
;
335 strtc_clock_write(struct strtc_softc
*sc
, struct clock_ymdhms
*dt
)
337 uint8_t bcd
[M41ST84_REG_DATE_BYTES
], cmdbuf
[2];
341 * Convert our time representation into something the M41ST84
344 bcd
[M41ST84_REG_CSEC
] = TOBCD(0); /* must always write as 0 */
345 bcd
[M41ST84_REG_SEC
] = TOBCD(dt
->dt_sec
);
346 bcd
[M41ST84_REG_MIN
] = TOBCD(dt
->dt_min
);
347 bcd
[M41ST84_REG_CENHR
] = TOBCD(dt
->dt_hour
);
348 bcd
[M41ST84_REG_DATE
] = TOBCD(dt
->dt_day
);
349 bcd
[M41ST84_REG_DAY
] = TOBCD(dt
->dt_wday
);
350 bcd
[M41ST84_REG_MONTH
] = TOBCD(dt
->dt_mon
);
351 bcd
[M41ST84_REG_YEAR
] = TOBCD((dt
->dt_year
- POSIX_BASE_YEAR
) % 100);
353 if (iic_acquire_bus(sc
->sc_tag
, I2C_F_POLL
)) {
354 aprint_error_dev(sc
->sc_dev
,
355 "strtc_clock_write: failed to acquire I2C bus\n");
360 cmdbuf
[0] = M41ST84_REG_SEC
;
361 cmdbuf
[1] = M41ST84_SEC_ST
;
363 if (iic_exec(sc
->sc_tag
, I2C_OP_WRITE
, sc
->sc_address
,
364 cmdbuf
, 1, &cmdbuf
[1], 1, I2C_F_POLL
)) {
365 iic_release_bus(sc
->sc_tag
, I2C_F_POLL
);
366 aprint_error_dev(sc
->sc_dev
,
367 "strtc_clock_write: failed to Hold Clock\n");
372 * Check for the HT bit -- if set, then clock lost power & stopped
373 * If that happened, then clear the bit so that the clock will have
374 * a chance to run again.
376 cmdbuf
[0] = M41ST84_REG_AL_HOUR
;
377 if (iic_exec(sc
->sc_tag
, I2C_OP_READ
, sc
->sc_address
,
378 cmdbuf
, 1, &cmdbuf
[1], 1, I2C_F_POLL
)) {
379 iic_release_bus(sc
->sc_tag
, I2C_F_POLL
);
380 aprint_error_dev(sc
->sc_dev
,
381 "strtc_clock_write: failed to read HT\n");
384 if (cmdbuf
[1] & M41ST84_AL_HOUR_HT
) {
385 cmdbuf
[1] &= ~M41ST84_AL_HOUR_HT
;
386 if (iic_exec(sc
->sc_tag
, I2C_OP_WRITE
, sc
->sc_address
,
387 cmdbuf
, 1, &cmdbuf
[1], 1, I2C_F_POLL
)) {
388 iic_release_bus(sc
->sc_tag
, I2C_F_POLL
);
389 aprint_error_dev(sc
->sc_dev
,
390 "strtc_clock_write: failed to reset HT\n");
396 * Write registers in reverse order. The last write (to the Seconds
397 * register) will undo the Clock Hold, above.
399 for (i
= M41ST84_REG_DATE_BYTES
- 1; i
>= 0; i
--) {
401 if (iic_exec(sc
->sc_tag
,
402 i
? I2C_OP_WRITE
: I2C_OP_WRITE_WITH_STOP
,
403 sc
->sc_address
, cmdbuf
, 1, &bcd
[i
], 1,
405 iic_release_bus(sc
->sc_tag
, I2C_F_POLL
);
406 aprint_error_dev(sc
->sc_dev
,
407 "strtc_clock_write: failed to write rtc "
409 /* XXX: Clock Hold is likely still asserted! */
414 iic_release_bus(sc
->sc_tag
, I2C_F_POLL
);
420 strtc_wdog_config(void *arg
, uint8_t wd
)
422 struct strtc_softc
*sc
= arg
;
425 if (iic_acquire_bus(sc
->sc_tag
, I2C_F_POLL
)) {
426 aprint_error_dev(sc
->sc_dev
,
427 "strtc_wdog_config: failed to acquire I2C bus\n");
431 cmdbuf
[0] = M41ST84_REG_WATCHDOG
;
434 if (iic_exec(sc
->sc_tag
, I2C_OP_WRITE_WITH_STOP
, sc
->sc_address
,
435 cmdbuf
, 1, &cmdbuf
[1], 1, I2C_F_POLL
)) {
436 aprint_error_dev(sc
->sc_dev
,
437 "strtc_wdog_config: failed to write watchdog\n");
441 iic_release_bus(sc
->sc_tag
, I2C_F_POLL
);