pcrtc.c revision 296373
1/*- 2 * Copyright (c) 2008 TAKAHASHI Yoshihiro 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: releng/10.3/sys/pc98/cbus/pcrtc.c 178315 2008-04-19 08:18:47Z nyan $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/bus.h> 34#include <sys/clock.h> 35#include <sys/kernel.h> 36#include <sys/module.h> 37 38#include <pc98/cbus/cbus.h> 39#include <isa/isavar.h> 40 41/* 42 * modified for PC98 by Kakefuda 43 */ 44 45/* 46 * RTC support routines 47 */ 48 49static void rtc_serialcombit(int); 50static void rtc_serialcom(int); 51static int rtc_inb(void); 52static void rtc_outb(int); 53 54static void 55rtc_serialcombit(int i) 56{ 57 outb(IO_RTC, ((i&0x01)<<5)|0x07); 58 DELAY(1); 59 outb(IO_RTC, ((i&0x01)<<5)|0x17); 60 DELAY(1); 61 outb(IO_RTC, ((i&0x01)<<5)|0x07); 62 DELAY(1); 63} 64 65static void 66rtc_serialcom(int i) 67{ 68 rtc_serialcombit(i&0x01); 69 rtc_serialcombit((i&0x02)>>1); 70 rtc_serialcombit((i&0x04)>>2); 71 rtc_serialcombit((i&0x08)>>3); 72 outb(IO_RTC, 0x07); 73 DELAY(1); 74 outb(IO_RTC, 0x0f); 75 DELAY(1); 76 outb(IO_RTC, 0x07); 77 DELAY(1); 78} 79 80static void 81rtc_outb(int val) 82{ 83 int s; 84 int sa = 0; 85 86 for (s=0;s<8;s++) { 87 sa = ((val >> s) & 0x01) ? 0x27 : 0x07; 88 outb(IO_RTC, sa); /* set DI & CLK 0 */ 89 DELAY(1); 90 outb(IO_RTC, sa | 0x10); /* CLK 1 */ 91 DELAY(1); 92 } 93 outb(IO_RTC, sa & 0xef); /* CLK 0 */ 94} 95 96static int 97rtc_inb(void) 98{ 99 int s; 100 int sa = 0; 101 102 for (s=0;s<8;s++) { 103 sa |= ((inb(0x33) & 0x01) << s); 104 outb(IO_RTC, 0x17); /* CLK 1 */ 105 DELAY(1); 106 outb(IO_RTC, 0x07); /* CLK 0 */ 107 DELAY(2); 108 } 109 return sa; 110} 111 112/********************************************************************** 113 * RTC driver for subr_rtc 114 */ 115 116#include "clock_if.h" 117 118#include <sys/rman.h> 119 120struct pcrtc_softc { 121 int port_rid1, port_rid2; 122 struct resource *port_res1, *port_res2; 123}; 124 125/* 126 * Attach to the ISA PnP descriptors for the timer and realtime clock. 127 */ 128static struct isa_pnp_id pcrtc_ids[] = { 129 { 0x000bd041 /* PNP0B00 */, "AT realtime clock" }, 130 { 0 } 131}; 132 133static int 134pcrtc_probe(device_t dev) 135{ 136 int result; 137 138 device_set_desc(dev, "PC Real Time Clock"); 139 result = ISA_PNP_PROBE(device_get_parent(dev), dev, pcrtc_ids); 140 /* ENXIO if wrong PnP-ID, ENOENT ifno PnP-ID, zero if good PnP-iD */ 141 if (result != ENOENT) 142 return(result); 143 /* All PC's have an RTC, and we're hosed without it, so... */ 144 return (BUS_PROBE_LOW_PRIORITY); 145} 146 147static int 148pcrtc_attach(device_t dev) 149{ 150 struct pcrtc_softc *sc; 151 152 /* 153 * Not that we need them or anything, but grab our resources 154 * so they show up, correctly attributed, in the big picture. 155 */ 156 sc = device_get_softc(dev); 157 sc->port_rid1 = 0; 158 bus_set_resource(dev, SYS_RES_IOPORT, sc->port_rid1, IO_RTC, 1); 159 if (!(sc->port_res1 = bus_alloc_resource(dev, SYS_RES_IOPORT, 160 &sc->port_rid1, IO_RTC, IO_RTC, 1, RF_ACTIVE))) 161 device_printf(dev, "Warning: Couldn't map I/O.\n"); 162 sc->port_rid2 = 1; 163 bus_set_resource(dev, SYS_RES_IOPORT, sc->port_rid2, 0x33, 1); 164 if (!(sc->port_res2 = bus_alloc_resource(dev, SYS_RES_IOPORT, 165 &sc->port_rid2, 0x33, 0x33, 1, RF_ACTIVE))) 166 device_printf(dev, "Warning: Couldn't map I/O.\n"); 167 168 clock_register(dev, 1000000); 169 return(0); 170} 171 172static int 173pcrtc_settime(device_t dev __unused, struct timespec *ts) 174{ 175 struct clocktime ct; 176 177 clock_ts_to_ct(ts, &ct); 178 179 rtc_serialcom(0x01); /* Register shift command. */ 180 181 rtc_outb(bin2bcd(ct.sec)); /* Write back Seconds */ 182 rtc_outb(bin2bcd(ct.min)); /* Write back Minutes */ 183 rtc_outb(bin2bcd(ct.hour)); /* Write back Hours */ 184 185 rtc_outb(bin2bcd(ct.day)); /* Write back Day */ 186 rtc_outb((ct.mon << 4) | ct.dow); /* Write back Month and DOW */ 187 rtc_outb(bin2bcd(ct.year % 100)); /* Write back Year */ 188 189 rtc_serialcom(0x02); /* Time set & Counter hold command. */ 190 rtc_serialcom(0x00); /* Register hold command. */ 191 192 return (0); 193} 194 195static int 196pcrtc_gettime(device_t dev, struct timespec *ts) 197{ 198 struct clocktime ct; 199 int i; 200 201 rtc_serialcom(0x03); /* Time Read */ 202 rtc_serialcom(0x01); /* Register shift command. */ 203 DELAY(20); 204 205 ct.nsec = 0; 206 ct.sec = bcd2bin(rtc_inb() & 0xff); /* sec */ 207 ct.min = bcd2bin(rtc_inb() & 0xff); /* min */ 208 ct.hour = bcd2bin(rtc_inb() & 0xff); /* hour */ 209 ct.day = bcd2bin(rtc_inb() & 0xff); /* date */ 210 i = rtc_inb(); 211 ct.dow = i & 0x0f; /* dow */ 212 ct.mon = (i >> 4) & 0x0f; /* month */ 213 ct.year = bcd2bin(rtc_inb() & 0xff) + 1900; /* year */ 214 if (ct.year < 1995) 215 ct.year += 100; 216 217 /* Set dow = -1 because some clocks don't set it correctly. */ 218 ct.dow = -1; 219 220 return (clock_ct_to_ts(&ct, ts)); 221} 222 223static device_method_t pcrtc_methods[] = { 224 /* Device interface */ 225 DEVMETHOD(device_probe, pcrtc_probe), 226 DEVMETHOD(device_attach, pcrtc_attach), 227 DEVMETHOD(device_detach, bus_generic_detach), 228 DEVMETHOD(device_shutdown, bus_generic_shutdown), 229 DEVMETHOD(device_suspend, bus_generic_suspend), 230 /* XXX stop statclock? */ 231 DEVMETHOD(device_resume, bus_generic_resume), 232 /* XXX restart statclock? */ 233 234 /* clock interface */ 235 DEVMETHOD(clock_gettime, pcrtc_gettime), 236 DEVMETHOD(clock_settime, pcrtc_settime), 237 238 { 0, 0 } 239}; 240 241static driver_t pcrtc_driver = { 242 "pcrtc", 243 pcrtc_methods, 244 sizeof(struct pcrtc_softc), 245}; 246 247static devclass_t pcrtc_devclass; 248 249DRIVER_MODULE(pcrtc, isa, pcrtc_driver, pcrtc_devclass, 0, 0); 250