summaryrefslogtreecommitdiffstats
path: root/sys/pc98/cbus
diff options
context:
space:
mode:
authornyan <nyan@FreeBSD.org>2008-04-13 06:18:34 +0000
committernyan <nyan@FreeBSD.org>2008-04-13 06:18:34 +0000
commit2eacd7b476600b3e66f2802706ee6d4b7c1a8fcd (patch)
tree5b8190c37a43fce75714365930aa1ef016d5a2a1 /sys/pc98/cbus
parentc75b6e5deaa6b6d7d248ad0d6bc10617367b80dd (diff)
downloadFreeBSD-src-2eacd7b476600b3e66f2802706ee6d4b7c1a8fcd.zip
FreeBSD-src-2eacd7b476600b3e66f2802706ee6d4b7c1a8fcd.tar.gz
MFi386: RTC related cleanups.
- Use generic RTC handling code. - Make clock_if.m and subr_rtc.c standard. - Nuke MD inittodr(), resettodr() functions. - Add new "pcrtc" device driver. - Add hints for "pcrtc" driver.
Diffstat (limited to 'sys/pc98/cbus')
-rw-r--r--sys/pc98/cbus/clock.c355
-rw-r--r--sys/pc98/cbus/pcrtc.c355
2 files changed, 428 insertions, 282 deletions
diff --git a/sys/pc98/cbus/clock.c b/sys/pc98/cbus/clock.c
index 05d34c5..81f61b9 100644
--- a/sys/pc98/cbus/clock.c
+++ b/sys/pc98/cbus/clock.c
@@ -119,10 +119,6 @@ static int using_lapic_timer;
#define ACQUIRE_PENDING 3
static u_char timer1_state;
-static void rtc_serialcombit(int);
-static void rtc_serialcom(int);
-static int rtc_inb(void);
-static void rtc_outb(int);
static unsigned i8254_get_timecount(struct timecounter *tc);
static unsigned i8254_simple_get_timecount(struct timecounter *tc);
@@ -400,137 +396,6 @@ startrtclock()
init_TSC();
}
-static void
-rtc_serialcombit(int i)
-{
- outb(IO_RTC, ((i&0x01)<<5)|0x07);
- DELAY(1);
- outb(IO_RTC, ((i&0x01)<<5)|0x17);
- DELAY(1);
- outb(IO_RTC, ((i&0x01)<<5)|0x07);
- DELAY(1);
-}
-
-static void
-rtc_serialcom(int i)
-{
- rtc_serialcombit(i&0x01);
- rtc_serialcombit((i&0x02)>>1);
- rtc_serialcombit((i&0x04)>>2);
- rtc_serialcombit((i&0x08)>>3);
- outb(IO_RTC, 0x07);
- DELAY(1);
- outb(IO_RTC, 0x0f);
- DELAY(1);
- outb(IO_RTC, 0x07);
- DELAY(1);
-}
-
-static void
-rtc_outb(int val)
-{
- int s;
- int sa = 0;
-
- for (s=0;s<8;s++) {
- sa = ((val >> s) & 0x01) ? 0x27 : 0x07;
- outb(IO_RTC, sa); /* set DI & CLK 0 */
- DELAY(1);
- outb(IO_RTC, sa | 0x10); /* CLK 1 */
- DELAY(1);
- }
- outb(IO_RTC, sa & 0xef); /* CLK 0 */
-}
-
-static int
-rtc_inb(void)
-{
- int s;
- int sa = 0;
-
- for (s=0;s<8;s++) {
- sa |= ((inb(0x33) & 0x01) << s);
- outb(IO_RTC, 0x17); /* CLK 1 */
- DELAY(1);
- outb(IO_RTC, 0x07); /* CLK 0 */
- DELAY(2);
- }
- return sa;
-}
-
-/*
- * Initialize the time of day register, based on the time base which is, e.g.
- * from a filesystem.
- */
-void
-inittodr(time_t base)
-{
- struct timespec ts;
- struct clocktime ct;
- int i;
-
- if (base) {
- ts.tv_sec = base;
- ts.tv_nsec = 0;
- tc_setclock(&ts);
- }
-
- rtc_serialcom(0x03); /* Time Read */
- rtc_serialcom(0x01); /* Register shift command. */
- DELAY(20);
-
- ct.nsec = 0;
- ct.sec = bcd2bin(rtc_inb() & 0xff); /* sec */
- ct.min = bcd2bin(rtc_inb() & 0xff); /* min */
- ct.hour = bcd2bin(rtc_inb() & 0xff); /* hour */
- ct.day = bcd2bin(rtc_inb() & 0xff); /* date */
- i = rtc_inb();
- ct.dow = i & 0x0f; /* dow */
- ct.mon = (i >> 4) & 0x0f; /* month */
- ct.year = bcd2bin(rtc_inb() & 0xff) + 1900; /* year */
- if (ct.year < 1995)
- ct.year += 100;
- /* Set dow = -1 because some clocks don't set it correctly. */
- ct.dow = -1;
- if (clock_ct_to_ts(&ct, &ts)) {
- printf("Invalid time in clock: check and reset the date!\n");
- return;
- }
- ts.tv_sec += utc_offset();
- tc_setclock(&ts);
-}
-
-/*
- * Write system time back to RTC
- */
-void
-resettodr()
-{
- struct timespec ts;
- struct clocktime ct;
-
- if (disable_rtc_set)
- return;
-
- getnanotime(&ts);
- ts.tv_sec -= utc_offset();
- clock_ts_to_ct(&ts, &ct);
-
- rtc_serialcom(0x01); /* Register shift command. */
-
- rtc_outb(bin2bcd(ct.sec)); /* Write back Seconds */
- rtc_outb(bin2bcd(ct.min)); /* Write back Minutes */
- rtc_outb(bin2bcd(ct.hour)); /* Write back Hours */
-
- rtc_outb(bin2bcd(ct.day)); /* Write back Day */
- rtc_outb((ct.mon << 4) | ct.dow); /* Write back Month and DOW */
- rtc_outb(bin2bcd(ct.year % 100)); /* Write back Year */
-
- rtc_serialcom(0x02); /* Time set & Counter hold command. */
- rtc_serialcom(0x00); /* Register hold command. */
-}
-
-
/*
* Start both clocks running.
*/
@@ -619,7 +484,8 @@ i8254_get_timecount(struct timecounter *tc)
count = i8254_max_count - ((high << 8) | low);
if (count < i8254_lastcount ||
(!i8254_ticked && (clkintr_pending ||
- ((count < 20 || (!(eflags & PSL_I) && count < i8254_max_count / 2u)) &&
+ ((count < 20 || (!(eflags & PSL_I) &&
+ count < i8254_max_count / 2u)) &&
i8254_pending != NULL && i8254_pending(i8254_intsrc))))) {
i8254_ticked = 1;
i8254_offset += i8254_max_count;
@@ -632,11 +498,10 @@ i8254_get_timecount(struct timecounter *tc)
#ifdef DEV_ISA
/*
- * Attach to the ISA PnP descriptors for the timer and realtime clock.
+ * Attach to the ISA PnP descriptors for the timer
*/
static struct isa_pnp_id attimer_ids[] = {
{ 0x0001d041 /* PNP0100 */, "AT timer" },
- { 0x000bd041 /* PNP0B00 */, "AT realtime clock" },
{ 0 }
};
@@ -645,7 +510,8 @@ attimer_probe(device_t dev)
{
int result;
- if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, attimer_ids)) <= 0)
+ result = ISA_PNP_PROBE(device_get_parent(dev), dev, attimer_ids);
+ if (result <= 0)
device_quiet(dev);
return(result);
}
@@ -662,8 +528,8 @@ static device_method_t attimer_methods[] = {
DEVMETHOD(device_attach, attimer_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
- DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX stop statclock? */
- DEVMETHOD(device_resume, bus_generic_resume), /* XXX restart statclock? */
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
{ 0, 0 }
};
@@ -676,4 +542,211 @@ static driver_t attimer_driver = {
static devclass_t attimer_devclass;
DRIVER_MODULE(attimer, isa, attimer_driver, attimer_devclass, 0, 0);
+
+#endif /* DEV_ISA */
+
+#ifdef DEV_ISA
+
+/**********************************************************************
+ * RTC driver for subr_rtc
+ */
+
+#include "clock_if.h"
+
+#include <sys/rman.h>
+
+static void rtc_serialcombit(int);
+static void rtc_serialcom(int);
+static int rtc_inb(void);
+static void rtc_outb(int);
+
+static void
+rtc_serialcombit(int i)
+{
+ outb(IO_RTC, ((i&0x01)<<5)|0x07);
+ DELAY(1);
+ outb(IO_RTC, ((i&0x01)<<5)|0x17);
+ DELAY(1);
+ outb(IO_RTC, ((i&0x01)<<5)|0x07);
+ DELAY(1);
+}
+
+static void
+rtc_serialcom(int i)
+{
+ rtc_serialcombit(i&0x01);
+ rtc_serialcombit((i&0x02)>>1);
+ rtc_serialcombit((i&0x04)>>2);
+ rtc_serialcombit((i&0x08)>>3);
+ outb(IO_RTC, 0x07);
+ DELAY(1);
+ outb(IO_RTC, 0x0f);
+ DELAY(1);
+ outb(IO_RTC, 0x07);
+ DELAY(1);
+}
+
+static void
+rtc_outb(int val)
+{
+ int s;
+ int sa = 0;
+
+ for (s=0;s<8;s++) {
+ sa = ((val >> s) & 0x01) ? 0x27 : 0x07;
+ outb(IO_RTC, sa); /* set DI & CLK 0 */
+ DELAY(1);
+ outb(IO_RTC, sa | 0x10); /* CLK 1 */
+ DELAY(1);
+ }
+ outb(IO_RTC, sa & 0xef); /* CLK 0 */
+}
+
+static int
+rtc_inb(void)
+{
+ int s;
+ int sa = 0;
+
+ for (s=0;s<8;s++) {
+ sa |= ((inb(0x33) & 0x01) << s);
+ outb(IO_RTC, 0x17); /* CLK 1 */
+ DELAY(1);
+ outb(IO_RTC, 0x07); /* CLK 0 */
+ DELAY(2);
+ }
+ return sa;
+}
+
+struct pcrtc_softc {
+ int port_rid1, port_rid2;
+ struct resource *port_res1, *port_res2;
+};
+
+/*
+ * Attach to the ISA PnP descriptors for the timer and realtime clock.
+ */
+static struct isa_pnp_id pcrtc_ids[] = {
+ { 0x000bd041 /* PNP0B00 */, "AT realtime clock" },
+ { 0 }
+};
+
+static int
+pcrtc_probe(device_t dev)
+{
+ int result;
+
+ device_set_desc(dev, "PC Real Time Clock");
+ result = ISA_PNP_PROBE(device_get_parent(dev), dev, pcrtc_ids);
+ /* ENXIO if wrong PnP-ID, ENOENT ifno PnP-ID, zero if good PnP-iD */
+ if (result != ENOENT)
+ return(result);
+ /* All PC's have an RTC, and we're hosed without it, so... */
+ return (BUS_PROBE_LOW_PRIORITY);
+}
+
+static int
+pcrtc_attach(device_t dev)
+{
+ struct pcrtc_softc *sc;
+
+ /*
+ * Not that we need them or anything, but grab our resources
+ * so they show up, correctly attributed, in the big picture.
+ */
+ sc = device_get_softc(dev);
+ sc->port_rid1 = 0;
+ bus_set_resource(dev, SYS_RES_IOPORT, sc->port_rid1, IO_RTC, 1);
+ if (!(sc->port_res1 = bus_alloc_resource(dev, SYS_RES_IOPORT,
+ &sc->port_rid1, IO_RTC, IO_RTC, 1, RF_ACTIVE)))
+ device_printf(dev, "Warning: Couldn't map I/O.\n");
+ sc->port_rid2 = 1;
+ bus_set_resource(dev, SYS_RES_IOPORT, sc->port_rid2, 0x33, 1);
+ if (!(sc->port_res2 = bus_alloc_resource(dev, SYS_RES_IOPORT,
+ &sc->port_rid2, 0x33, 0x33, 1, RF_ACTIVE)))
+ device_printf(dev, "Warning: Couldn't map I/O.\n");
+
+ clock_register(dev, 1000000);
+ return(0);
+}
+
+static int
+pcrtc_settime(device_t dev __unused, struct timespec *ts)
+{
+ struct clocktime ct;
+
+ clock_ts_to_ct(ts, &ct);
+
+ rtc_serialcom(0x01); /* Register shift command. */
+
+ rtc_outb(bin2bcd(ct.sec)); /* Write back Seconds */
+ rtc_outb(bin2bcd(ct.min)); /* Write back Minutes */
+ rtc_outb(bin2bcd(ct.hour)); /* Write back Hours */
+
+ rtc_outb(bin2bcd(ct.day)); /* Write back Day */
+ rtc_outb((ct.mon << 4) | ct.dow); /* Write back Month and DOW */
+ rtc_outb(bin2bcd(ct.year % 100)); /* Write back Year */
+
+ rtc_serialcom(0x02); /* Time set & Counter hold command. */
+ rtc_serialcom(0x00); /* Register hold command. */
+
+ return (0);
+}
+
+static int
+pcrtc_gettime(device_t dev, struct timespec *ts)
+{
+ struct clocktime ct;
+ int i;
+
+ rtc_serialcom(0x03); /* Time Read */
+ rtc_serialcom(0x01); /* Register shift command. */
+ DELAY(20);
+
+ ct.nsec = 0;
+ ct.sec = bcd2bin(rtc_inb() & 0xff); /* sec */
+ ct.min = bcd2bin(rtc_inb() & 0xff); /* min */
+ ct.hour = bcd2bin(rtc_inb() & 0xff); /* hour */
+ ct.day = bcd2bin(rtc_inb() & 0xff); /* date */
+ i = rtc_inb();
+ ct.dow = i & 0x0f; /* dow */
+ ct.mon = (i >> 4) & 0x0f; /* month */
+ ct.year = bcd2bin(rtc_inb() & 0xff) + 1900; /* year */
+ if (ct.year < 1995)
+ ct.year += 100;
+
+ /* Set dow = -1 because some clocks don't set it correctly. */
+ ct.dow = -1;
+
+ return (clock_ct_to_ts(&ct, ts));
+}
+
+static device_method_t pcrtc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pcrtc_probe),
+ DEVMETHOD(device_attach, pcrtc_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ /* XXX stop statclock? */
+ DEVMETHOD(device_resume, bus_generic_resume),
+ /* XXX restart statclock? */
+
+ /* clock interface */
+ DEVMETHOD(clock_gettime, pcrtc_gettime),
+ DEVMETHOD(clock_settime, pcrtc_settime),
+
+ { 0, 0 }
+};
+
+static driver_t pcrtc_driver = {
+ "pcrtc",
+ pcrtc_methods,
+ sizeof(struct pcrtc_softc),
+};
+
+static devclass_t pcrtc_devclass;
+
+DRIVER_MODULE(pcrtc, isa, pcrtc_driver, pcrtc_devclass, 0, 0);
+
#endif /* DEV_ISA */
diff --git a/sys/pc98/cbus/pcrtc.c b/sys/pc98/cbus/pcrtc.c
index 05d34c5..81f61b9 100644
--- a/sys/pc98/cbus/pcrtc.c
+++ b/sys/pc98/cbus/pcrtc.c
@@ -119,10 +119,6 @@ static int using_lapic_timer;
#define ACQUIRE_PENDING 3
static u_char timer1_state;
-static void rtc_serialcombit(int);
-static void rtc_serialcom(int);
-static int rtc_inb(void);
-static void rtc_outb(int);
static unsigned i8254_get_timecount(struct timecounter *tc);
static unsigned i8254_simple_get_timecount(struct timecounter *tc);
@@ -400,137 +396,6 @@ startrtclock()
init_TSC();
}
-static void
-rtc_serialcombit(int i)
-{
- outb(IO_RTC, ((i&0x01)<<5)|0x07);
- DELAY(1);
- outb(IO_RTC, ((i&0x01)<<5)|0x17);
- DELAY(1);
- outb(IO_RTC, ((i&0x01)<<5)|0x07);
- DELAY(1);
-}
-
-static void
-rtc_serialcom(int i)
-{
- rtc_serialcombit(i&0x01);
- rtc_serialcombit((i&0x02)>>1);
- rtc_serialcombit((i&0x04)>>2);
- rtc_serialcombit((i&0x08)>>3);
- outb(IO_RTC, 0x07);
- DELAY(1);
- outb(IO_RTC, 0x0f);
- DELAY(1);
- outb(IO_RTC, 0x07);
- DELAY(1);
-}
-
-static void
-rtc_outb(int val)
-{
- int s;
- int sa = 0;
-
- for (s=0;s<8;s++) {
- sa = ((val >> s) & 0x01) ? 0x27 : 0x07;
- outb(IO_RTC, sa); /* set DI & CLK 0 */
- DELAY(1);
- outb(IO_RTC, sa | 0x10); /* CLK 1 */
- DELAY(1);
- }
- outb(IO_RTC, sa & 0xef); /* CLK 0 */
-}
-
-static int
-rtc_inb(void)
-{
- int s;
- int sa = 0;
-
- for (s=0;s<8;s++) {
- sa |= ((inb(0x33) & 0x01) << s);
- outb(IO_RTC, 0x17); /* CLK 1 */
- DELAY(1);
- outb(IO_RTC, 0x07); /* CLK 0 */
- DELAY(2);
- }
- return sa;
-}
-
-/*
- * Initialize the time of day register, based on the time base which is, e.g.
- * from a filesystem.
- */
-void
-inittodr(time_t base)
-{
- struct timespec ts;
- struct clocktime ct;
- int i;
-
- if (base) {
- ts.tv_sec = base;
- ts.tv_nsec = 0;
- tc_setclock(&ts);
- }
-
- rtc_serialcom(0x03); /* Time Read */
- rtc_serialcom(0x01); /* Register shift command. */
- DELAY(20);
-
- ct.nsec = 0;
- ct.sec = bcd2bin(rtc_inb() & 0xff); /* sec */
- ct.min = bcd2bin(rtc_inb() & 0xff); /* min */
- ct.hour = bcd2bin(rtc_inb() & 0xff); /* hour */
- ct.day = bcd2bin(rtc_inb() & 0xff); /* date */
- i = rtc_inb();
- ct.dow = i & 0x0f; /* dow */
- ct.mon = (i >> 4) & 0x0f; /* month */
- ct.year = bcd2bin(rtc_inb() & 0xff) + 1900; /* year */
- if (ct.year < 1995)
- ct.year += 100;
- /* Set dow = -1 because some clocks don't set it correctly. */
- ct.dow = -1;
- if (clock_ct_to_ts(&ct, &ts)) {
- printf("Invalid time in clock: check and reset the date!\n");
- return;
- }
- ts.tv_sec += utc_offset();
- tc_setclock(&ts);
-}
-
-/*
- * Write system time back to RTC
- */
-void
-resettodr()
-{
- struct timespec ts;
- struct clocktime ct;
-
- if (disable_rtc_set)
- return;
-
- getnanotime(&ts);
- ts.tv_sec -= utc_offset();
- clock_ts_to_ct(&ts, &ct);
-
- rtc_serialcom(0x01); /* Register shift command. */
-
- rtc_outb(bin2bcd(ct.sec)); /* Write back Seconds */
- rtc_outb(bin2bcd(ct.min)); /* Write back Minutes */
- rtc_outb(bin2bcd(ct.hour)); /* Write back Hours */
-
- rtc_outb(bin2bcd(ct.day)); /* Write back Day */
- rtc_outb((ct.mon << 4) | ct.dow); /* Write back Month and DOW */
- rtc_outb(bin2bcd(ct.year % 100)); /* Write back Year */
-
- rtc_serialcom(0x02); /* Time set & Counter hold command. */
- rtc_serialcom(0x00); /* Register hold command. */
-}
-
-
/*
* Start both clocks running.
*/
@@ -619,7 +484,8 @@ i8254_get_timecount(struct timecounter *tc)
count = i8254_max_count - ((high << 8) | low);
if (count < i8254_lastcount ||
(!i8254_ticked && (clkintr_pending ||
- ((count < 20 || (!(eflags & PSL_I) && count < i8254_max_count / 2u)) &&
+ ((count < 20 || (!(eflags & PSL_I) &&
+ count < i8254_max_count / 2u)) &&
i8254_pending != NULL && i8254_pending(i8254_intsrc))))) {
i8254_ticked = 1;
i8254_offset += i8254_max_count;
@@ -632,11 +498,10 @@ i8254_get_timecount(struct timecounter *tc)
#ifdef DEV_ISA
/*
- * Attach to the ISA PnP descriptors for the timer and realtime clock.
+ * Attach to the ISA PnP descriptors for the timer
*/
static struct isa_pnp_id attimer_ids[] = {
{ 0x0001d041 /* PNP0100 */, "AT timer" },
- { 0x000bd041 /* PNP0B00 */, "AT realtime clock" },
{ 0 }
};
@@ -645,7 +510,8 @@ attimer_probe(device_t dev)
{
int result;
- if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, attimer_ids)) <= 0)
+ result = ISA_PNP_PROBE(device_get_parent(dev), dev, attimer_ids);
+ if (result <= 0)
device_quiet(dev);
return(result);
}
@@ -662,8 +528,8 @@ static device_method_t attimer_methods[] = {
DEVMETHOD(device_attach, attimer_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
- DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX stop statclock? */
- DEVMETHOD(device_resume, bus_generic_resume), /* XXX restart statclock? */
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
{ 0, 0 }
};
@@ -676,4 +542,211 @@ static driver_t attimer_driver = {
static devclass_t attimer_devclass;
DRIVER_MODULE(attimer, isa, attimer_driver, attimer_devclass, 0, 0);
+
+#endif /* DEV_ISA */
+
+#ifdef DEV_ISA
+
+/**********************************************************************
+ * RTC driver for subr_rtc
+ */
+
+#include "clock_if.h"
+
+#include <sys/rman.h>
+
+static void rtc_serialcombit(int);
+static void rtc_serialcom(int);
+static int rtc_inb(void);
+static void rtc_outb(int);
+
+static void
+rtc_serialcombit(int i)
+{
+ outb(IO_RTC, ((i&0x01)<<5)|0x07);
+ DELAY(1);
+ outb(IO_RTC, ((i&0x01)<<5)|0x17);
+ DELAY(1);
+ outb(IO_RTC, ((i&0x01)<<5)|0x07);
+ DELAY(1);
+}
+
+static void
+rtc_serialcom(int i)
+{
+ rtc_serialcombit(i&0x01);
+ rtc_serialcombit((i&0x02)>>1);
+ rtc_serialcombit((i&0x04)>>2);
+ rtc_serialcombit((i&0x08)>>3);
+ outb(IO_RTC, 0x07);
+ DELAY(1);
+ outb(IO_RTC, 0x0f);
+ DELAY(1);
+ outb(IO_RTC, 0x07);
+ DELAY(1);
+}
+
+static void
+rtc_outb(int val)
+{
+ int s;
+ int sa = 0;
+
+ for (s=0;s<8;s++) {
+ sa = ((val >> s) & 0x01) ? 0x27 : 0x07;
+ outb(IO_RTC, sa); /* set DI & CLK 0 */
+ DELAY(1);
+ outb(IO_RTC, sa | 0x10); /* CLK 1 */
+ DELAY(1);
+ }
+ outb(IO_RTC, sa & 0xef); /* CLK 0 */
+}
+
+static int
+rtc_inb(void)
+{
+ int s;
+ int sa = 0;
+
+ for (s=0;s<8;s++) {
+ sa |= ((inb(0x33) & 0x01) << s);
+ outb(IO_RTC, 0x17); /* CLK 1 */
+ DELAY(1);
+ outb(IO_RTC, 0x07); /* CLK 0 */
+ DELAY(2);
+ }
+ return sa;
+}
+
+struct pcrtc_softc {
+ int port_rid1, port_rid2;
+ struct resource *port_res1, *port_res2;
+};
+
+/*
+ * Attach to the ISA PnP descriptors for the timer and realtime clock.
+ */
+static struct isa_pnp_id pcrtc_ids[] = {
+ { 0x000bd041 /* PNP0B00 */, "AT realtime clock" },
+ { 0 }
+};
+
+static int
+pcrtc_probe(device_t dev)
+{
+ int result;
+
+ device_set_desc(dev, "PC Real Time Clock");
+ result = ISA_PNP_PROBE(device_get_parent(dev), dev, pcrtc_ids);
+ /* ENXIO if wrong PnP-ID, ENOENT ifno PnP-ID, zero if good PnP-iD */
+ if (result != ENOENT)
+ return(result);
+ /* All PC's have an RTC, and we're hosed without it, so... */
+ return (BUS_PROBE_LOW_PRIORITY);
+}
+
+static int
+pcrtc_attach(device_t dev)
+{
+ struct pcrtc_softc *sc;
+
+ /*
+ * Not that we need them or anything, but grab our resources
+ * so they show up, correctly attributed, in the big picture.
+ */
+ sc = device_get_softc(dev);
+ sc->port_rid1 = 0;
+ bus_set_resource(dev, SYS_RES_IOPORT, sc->port_rid1, IO_RTC, 1);
+ if (!(sc->port_res1 = bus_alloc_resource(dev, SYS_RES_IOPORT,
+ &sc->port_rid1, IO_RTC, IO_RTC, 1, RF_ACTIVE)))
+ device_printf(dev, "Warning: Couldn't map I/O.\n");
+ sc->port_rid2 = 1;
+ bus_set_resource(dev, SYS_RES_IOPORT, sc->port_rid2, 0x33, 1);
+ if (!(sc->port_res2 = bus_alloc_resource(dev, SYS_RES_IOPORT,
+ &sc->port_rid2, 0x33, 0x33, 1, RF_ACTIVE)))
+ device_printf(dev, "Warning: Couldn't map I/O.\n");
+
+ clock_register(dev, 1000000);
+ return(0);
+}
+
+static int
+pcrtc_settime(device_t dev __unused, struct timespec *ts)
+{
+ struct clocktime ct;
+
+ clock_ts_to_ct(ts, &ct);
+
+ rtc_serialcom(0x01); /* Register shift command. */
+
+ rtc_outb(bin2bcd(ct.sec)); /* Write back Seconds */
+ rtc_outb(bin2bcd(ct.min)); /* Write back Minutes */
+ rtc_outb(bin2bcd(ct.hour)); /* Write back Hours */
+
+ rtc_outb(bin2bcd(ct.day)); /* Write back Day */
+ rtc_outb((ct.mon << 4) | ct.dow); /* Write back Month and DOW */
+ rtc_outb(bin2bcd(ct.year % 100)); /* Write back Year */
+
+ rtc_serialcom(0x02); /* Time set & Counter hold command. */
+ rtc_serialcom(0x00); /* Register hold command. */
+
+ return (0);
+}
+
+static int
+pcrtc_gettime(device_t dev, struct timespec *ts)
+{
+ struct clocktime ct;
+ int i;
+
+ rtc_serialcom(0x03); /* Time Read */
+ rtc_serialcom(0x01); /* Register shift command. */
+ DELAY(20);
+
+ ct.nsec = 0;
+ ct.sec = bcd2bin(rtc_inb() & 0xff); /* sec */
+ ct.min = bcd2bin(rtc_inb() & 0xff); /* min */
+ ct.hour = bcd2bin(rtc_inb() & 0xff); /* hour */
+ ct.day = bcd2bin(rtc_inb() & 0xff); /* date */
+ i = rtc_inb();
+ ct.dow = i & 0x0f; /* dow */
+ ct.mon = (i >> 4) & 0x0f; /* month */
+ ct.year = bcd2bin(rtc_inb() & 0xff) + 1900; /* year */
+ if (ct.year < 1995)
+ ct.year += 100;
+
+ /* Set dow = -1 because some clocks don't set it correctly. */
+ ct.dow = -1;
+
+ return (clock_ct_to_ts(&ct, ts));
+}
+
+static device_method_t pcrtc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pcrtc_probe),
+ DEVMETHOD(device_attach, pcrtc_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ /* XXX stop statclock? */
+ DEVMETHOD(device_resume, bus_generic_resume),
+ /* XXX restart statclock? */
+
+ /* clock interface */
+ DEVMETHOD(clock_gettime, pcrtc_gettime),
+ DEVMETHOD(clock_settime, pcrtc_settime),
+
+ { 0, 0 }
+};
+
+static driver_t pcrtc_driver = {
+ "pcrtc",
+ pcrtc_methods,
+ sizeof(struct pcrtc_softc),
+};
+
+static devclass_t pcrtc_devclass;
+
+DRIVER_MODULE(pcrtc, isa, pcrtc_driver, pcrtc_devclass, 0, 0);
+
#endif /* DEV_ISA */
OpenPOWER on IntegriCloud