summaryrefslogtreecommitdiff
path: root/sys/dev/mc146818
diff options
context:
space:
mode:
authorMarius Strobl <marius@FreeBSD.org>2004-11-17 16:37:25 +0000
committerMarius Strobl <marius@FreeBSD.org>2004-11-17 16:37:25 +0000
commitf46d4a6741816df510acf03ea6b0beee7ca650e3 (patch)
tree3deba980ae5fd248e1090df36d074fd98e19ca7f /sys/dev/mc146818
parentaafc104c8a605289ea297ddb1dde9ef48e35c2bf (diff)
Notes
Diffstat (limited to 'sys/dev/mc146818')
-rw-r--r--sys/dev/mc146818/mc146818.c275
-rw-r--r--sys/dev/mc146818/mc146818reg.h52
-rw-r--r--sys/dev/mc146818/mc146818var.h60
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 *);