diff options
| author | Marius Strobl <marius@FreeBSD.org> | 2004-11-17 16:37:25 +0000 |
|---|---|---|
| committer | Marius Strobl <marius@FreeBSD.org> | 2004-11-17 16:37:25 +0000 |
| commit | f46d4a6741816df510acf03ea6b0beee7ca650e3 (patch) | |
| tree | 3deba980ae5fd248e1090df36d074fd98e19ca7f /sys/dev/mc146818 | |
| parent | aafc104c8a605289ea297ddb1dde9ef48e35c2bf (diff) | |
Notes
Diffstat (limited to 'sys/dev/mc146818')
| -rw-r--r-- | sys/dev/mc146818/mc146818.c | 275 | ||||
| -rw-r--r-- | sys/dev/mc146818/mc146818reg.h | 52 | ||||
| -rw-r--r-- | sys/dev/mc146818/mc146818var.h | 60 |
3 files changed, 340 insertions, 47 deletions
diff --git a/sys/dev/mc146818/mc146818.c b/sys/dev/mc146818/mc146818.c new file mode 100644 index 000000000000..48d36a729a92 --- /dev/null +++ b/sys/dev/mc146818/mc146818.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2003 Izumi Tsutsui. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * from: NetBSD: mc146818.c,v 1.4 2003/11/24 06:20:40 tsutsui Exp + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * mc146818 and compatible time of day chip subroutines + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/clock.h> + +#include <machine/bus.h> + +#include <dev/mc146818/mc146818reg.h> +#include <dev/mc146818/mc146818var.h> + +#include "clock_if.h" + +static u_int mc146818_def_getcent(device_t); +static void mc146818_def_setcent(device_t, u_int); +static u_int mc146818_def_read(device_t, u_int); +static void mc146818_def_write(device_t, u_int, u_int); + +int +mc146818_attach(device_t dev) +{ + struct mc146818_softc *sc; + + sc = device_get_softc(dev); + + if (sc->sc_mcread == NULL) + sc->sc_mcread = mc146818_def_read; + if (sc->sc_mcwrite == NULL) + sc->sc_mcwrite = mc146818_def_write; + + if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { + /* + * Note that setting MC146818_NO_CENT_ADJUST means that + * the century has to be stored in NVRAM somewhere. + */ + if (sc->sc_getcent == NULL) + sc->sc_getcent = mc146818_def_getcent; + if (sc->sc_setcent == NULL) + sc->sc_setcent = mc146818_def_setcent; + } + + if (!(*sc->sc_mcread)(dev, MC_REGD) & MC_REGD_VRT) { + device_printf(dev, "mc146818_attach: battery low\n"); + return (ENXIO); + } + + sc->sc_rega = MC_BASE_32_KHz; + (*sc->sc_mcwrite)(dev, MC_REGA, sc->sc_rega); + + sc->sc_regb = 0; + sc->sc_regb |= (sc->sc_flag & MC146818_BCD) ? 0 : MC_REGB_BINARY; + sc->sc_regb |= (sc->sc_flag & MC146818_12HR) ? 0 : MC_REGB_24HR; + (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); + + clock_register(dev, 1000000); /* 1 second resolution. */ + + return (0); +} + +/* + * Get time of day and convert it to a struct timespec. + * Return 0 on success, an error number otherwise. + */ +int +mc146818_gettime(device_t dev, struct timespec *ts) +{ + struct mc146818_softc *sc; + struct clocktime ct; + int timeout, cent, year; + + sc = device_get_softc(dev); + + timeout = 1000000; /* XXX how long should we wait? */ + + /* + * XXX: Use a spinlock to mutex register access and increase the + * likelihood that all registers are read before an update + * occurs. + */ + + /* + * If MC_REGA_UIP is 0 we have at least 244us before the next + * update. If it's 1 an update is imminent. + */ + for (;;) { + if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) + break; + if (--timeout < 0) { + device_printf(dev, "mc146818_gettime: timeout\n"); + return (EBUSY); + } + } + +#define FROMREG(x) ((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x)) + + ct.nsec = 0; + ct.sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); + ct.min = FROMREG((*sc->sc_mcread)(dev, MC_MIN)); + ct.hour = FROMREG((*sc->sc_mcread)(dev, MC_HOUR)); + ct.dow = FROMREG((*sc->sc_mcread)(dev, MC_DOW)) - 1; + ct.day = FROMREG((*sc->sc_mcread)(dev, MC_DOM)); + ct.mon = FROMREG((*sc->sc_mcread)(dev, MC_MONTH)); + year = FROMREG((*sc->sc_mcread)(dev, MC_YEAR)); + if (sc->sc_getcent) { + cent = (*sc->sc_getcent)(dev); + year += cent * 100; + } + + year += sc->sc_year0; + if (year < POSIX_BASE_YEAR && !(sc->sc_flag & MC146818_NO_CENT_ADJUST)) + year += 100; + ct.year = year; + + return (clock_ct_to_ts(&ct, ts)); +} + +#ifdef notyet +int +mc146818_getsecs(device_t dev, int *secp) +{ + struct mc146818_softc *sc; + int sec, timeout; + + sc = device_get_softc(dev); + + timeout = 1000000; /* XXX how long should we wait? */ + + for (;;) { + if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) { + sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); + break; + } + if (--timeout == 0) { + device_printf(dev, "mc146818_getsecs: timeout\n"); + return (EBUSY); + } + } + +#undef FROMREG + + *secp = sec; + return (0); +} +#endif + +/* + * Set the time of day clock based on the value of the struct timespec arg. + * Return 0 on success, an error number otherwise. + */ +int +mc146818_settime(device_t dev, struct timespec *ts) +{ + struct mc146818_softc *sc; + struct clocktime ct; + int cent, year; + + sc = device_get_softc(dev); + + /* Accuracy is only one second. */ + if (ts->tv_nsec >= 500000000) + ts->tv_sec++; + ts->tv_nsec = 0; + clock_ts_to_ct(ts, &ct); + + /* Disable RTC updates and interrupts (if enabled). */ + (*sc->sc_mcwrite)(dev, MC_REGB, + ((sc->sc_regb & (MC_REGB_BINARY | MC_REGB_24HR)) | MC_REGB_SET)); + +#define TOREG(x) ((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x)) + + (*sc->sc_mcwrite)(dev, MC_SEC, TOREG(ct.sec)); + (*sc->sc_mcwrite)(dev, MC_MIN, TOREG(ct.min)); + (*sc->sc_mcwrite)(dev, MC_HOUR, TOREG(ct.hour)); + (*sc->sc_mcwrite)(dev, MC_DOW, TOREG(ct.dow + 1)); + (*sc->sc_mcwrite)(dev, MC_DOM, TOREG(ct.day)); + (*sc->sc_mcwrite)(dev, MC_MONTH, TOREG(ct.mon)); + + year = ct.year - sc->sc_year0; + if (sc->sc_setcent) { + cent = year / 100; + (*sc->sc_setcent)(dev, cent); + year -= cent * 100; + } + if (year > 99 && (sc->sc_flag & MC146818_NO_CENT_ADJUST) == 0) + year -= 100; + (*sc->sc_mcwrite)(dev, MC_YEAR, TOREG(year)); + + /* Reenable RTC updates and interrupts. */ + (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); + +#undef TOREG + + return (0); +} + +#define MC_ADDR 0 +#define MC_DATA 1 + +static u_int +mc146818_def_read(device_t dev, u_int reg) +{ + struct mc146818_softc *sc; + + sc = device_get_softc(dev); + bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); + return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, MC_DATA)); +} + +static void +mc146818_def_write(device_t dev, u_int reg, u_int val) +{ + struct mc146818_softc *sc; + + sc = device_get_softc(dev); + bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); + bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_DATA, val); +} + +/* + * Looks like it's common even across platforms to store the century at + * 0x32 in the NVRAM of the mc146818. + */ +#define MC_CENT 0x32 + +static u_int +mc146818_def_getcent(device_t dev) +{ + struct mc146818_softc *sc; + + sc = device_get_softc(dev); + return ((*sc->sc_mcread)(dev, MC_CENT)); +} + +static void +mc146818_def_setcent(device_t dev, u_int cent) +{ + struct mc146818_softc *sc; + + sc = device_get_softc(dev); + (*sc->sc_mcwrite)(dev, MC_CENT, cent); +} diff --git a/sys/dev/mc146818/mc146818reg.h b/sys/dev/mc146818/mc146818reg.h index e60d043f13dd..48d558cc78d4 100644 --- a/sys/dev/mc146818/mc146818reg.h +++ b/sys/dev/mc146818/mc146818reg.h @@ -1,6 +1,3 @@ -/* $FreeBSD$ */ -/* $NetBSD: mc146818reg.h,v 1.2 1997/03/12 06:53:42 cgd Exp $ */ - /* * Copyright (c) 1995 Carnegie-Mellon University. * All rights reserved. @@ -24,11 +21,15 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. + * + * from: NetBSD: mc146818reg.h,v 1.5 2003/11/02 11:07:45 wiz Exp + * + * $FreeBSD$ */ /* * Definitions for the Motorola MC146818A Real Time Clock. - * They also apply for the (compatible) Dallas Semicontuctor DS1287A RTC. + * They also apply for the (compatible) Dallas Semiconductor DS1287A RTC. * * Though there are undoubtedly other (better) sources, this material was * culled from the DEC "KN121 System Module Programmer's Reference @@ -142,46 +143,3 @@ #define MC_BASE_32_KHz 0x20 /* 32KHz crystal */ #define MC_BASE_NONE 0x60 /* actually, both of these reset */ #define MC_BASE_RESET 0x70 - -/* - * A collection of TOD/Alarm registers. - */ -typedef u_int mc_todregs[MC_NTODREGS]; - -/* - * Get all of the TOD/Alarm registers - * Must be called at splhigh(), and with the RTC properly set up. - */ -#define MC146818_GETTOD(dev, regs) \ - do { \ - int i; \ - \ - /* update in progress; spin loop */ \ - while (MCCLOCK_READ(dev, MC_REGA) & MC_REGA_UIP) \ - ; \ - \ - /* read all of the tod/alarm regs */ \ - for (i = 0; i < MC_NTODREGS; i++) \ - (*regs)[i] = MCCLOCK_READ(dev, i); \ - } while (0); - -/* - * Set all of the TOD/Alarm registers - * Must be called at splhigh(), and with the RTC properly set up. - */ -#define MC146818_PUTTOD(dev, regs) \ - do { \ - int i; \ - \ - /* stop updates while setting */ \ - MCCLOCK_WRITE(dev, MC_REGB, \ - MCCLOCK_READ(dev, MC_REGB) | MC_REGB_SET); \ - \ - /* write all of the tod/alarm regs */ \ - for (i = 0; i < MC_NTODREGS; i++) \ - MCCLOCK_WRITE(dev, i, (*regs)[i]); \ - \ - /* reenable updates */ \ - MCCLOCK_WRITE(dev, MC_REGB, \ - MCCLOCK_READ(dev, MC_REGB) & ~MC_REGB_SET); \ - } while (0); diff --git a/sys/dev/mc146818/mc146818var.h b/sys/dev/mc146818/mc146818var.h new file mode 100644 index 000000000000..78d9945f686f --- /dev/null +++ b/sys/dev/mc146818/mc146818var.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2003 Izumi Tsutsui. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * from: NetBSD: mc146818var.h,v 1.3 2003/11/24 06:20:40 tsutsui Exp + * + * $FreeBSD$ + */ + +struct mc146818_softc { + bus_space_tag_t sc_bst; /* bus space tag */ + bus_space_handle_t sc_bsh; /* bus space handle */ + + u_char sc_rega; /* register A */ + u_char sc_regb; /* register B */ + + u_int sc_year0; /* year counter offset */ + u_int sc_flag; /* MD flags */ +#define MC146818_NO_CENT_ADJUST 0x0001 /* don't adjust century */ +#define MC146818_BCD 0x0002 /* use BCD mode */ +#define MC146818_12HR 0x0004 /* use AM/PM mode */ + + /* MD chip register read/write functions */ + u_int (*sc_mcread)(device_t, u_int); + void (*sc_mcwrite)(device_t, u_int, u_int); + /* MD century get/set functions */ + u_int (*sc_getcent)(device_t); + void (*sc_setcent)(device_t, u_int); +}; + +/* Chip attach function */ +int mc146818_attach(device_t); + +/* Methods for the clock interface */ +#ifdef notyet +int mc146818_getsecs(device_t, int *); +#endif +int mc146818_gettime(device_t, struct timespec *); +int mc146818_settime(device_t, struct timespec *); |
