1 /* 2 * Copyright (c) 2000 Sascha Schumann. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY SASCHA SCHUMANN ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 16 * EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 18 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 19 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 20 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 22 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * 24 * $FreeBSD: src/sys/dev/ppbus/pcfclock.c,v 1.3.2.1 2000/05/24 00:20:57 n_hibma Exp $ 25 * $DragonFly: src/sys/dev/misc/pcfclock/pcfclock.c,v 1.12 2006/12/22 23:26:18 swildner Exp $ 26 * 27 */ 28 29 #include "opt_pcfclock.h" 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/conf.h> 35 #include <sys/device.h> 36 #include <sys/sockio.h> 37 #include <sys/mbuf.h> 38 #include <sys/kernel.h> 39 #include <sys/fcntl.h> 40 #include <sys/uio.h> 41 42 #include <machine/clock.h> /* for DELAY */ 43 44 #include <bus/ppbus/ppbconf.h> 45 #include <bus/ppbus/ppb_msq.h> 46 #include <bus/ppbus/ppbio.h> 47 48 #include "ppbus_if.h" 49 50 #define PCFCLOCK_NAME "pcfclock" 51 52 struct pcfclock_data { 53 int count; 54 struct ppb_device pcfclock_dev; 55 }; 56 57 #define DEVTOSOFTC(dev) \ 58 ((struct pcfclock_data *)device_get_softc(dev)) 59 #define UNITOSOFTC(unit) \ 60 ((struct pcfclock_data *)devclass_get_softc(pcfclock_devclass, (unit))) 61 #define UNITODEVICE(unit) \ 62 (devclass_get_device(pcfclock_devclass, (unit))) 63 64 static devclass_t pcfclock_devclass; 65 66 static d_open_t pcfclock_open; 67 static d_close_t pcfclock_close; 68 static d_read_t pcfclock_read; 69 70 #define CDEV_MAJOR 140 71 static struct dev_ops pcfclock_ops = { 72 { PCFCLOCK_NAME, CDEV_MAJOR, 0 }, 73 .d_open = pcfclock_open, 74 .d_close = pcfclock_close, 75 .d_read = pcfclock_read, 76 }; 77 78 #ifndef PCFCLOCK_MAX_RETRIES 79 #define PCFCLOCK_MAX_RETRIES 10 80 #endif 81 82 #define AFC_HI 0 83 #define AFC_LO AUTOFEED 84 85 /* AUTO FEED is used as clock */ 86 #define AUTOFEED_CLOCK(val) \ 87 ctr = (ctr & ~(AUTOFEED)) ^ (val); ppb_wctr(ppbus, ctr) 88 89 /* SLCT is used as clock */ 90 #define CLOCK_OK \ 91 ((ppb_rstr(ppbus) & SELECT) == (i & 1 ? SELECT : 0)) 92 93 /* PE is used as data */ 94 #define BIT_SET (ppb_rstr(ppbus)&PERROR) 95 96 /* the first byte sent as reply must be 00001001b */ 97 #define PCFCLOCK_CORRECT_SYNC(buf) (buf[0] == 9) 98 99 #define NR(buf, off) (buf[off+1]*10+buf[off]) 100 101 /* check for correct input values */ 102 #define PCFCLOCK_CORRECT_FORMAT(buf) (\ 103 NR(buf, 14) <= 99 && \ 104 NR(buf, 12) <= 12 && \ 105 NR(buf, 10) <= 31 && \ 106 NR(buf, 6) <= 23 && \ 107 NR(buf, 4) <= 59 && \ 108 NR(buf, 2) <= 59) 109 110 #define PCFCLOCK_BATTERY_STATUS_LOW(buf) (buf[8] & 4) 111 112 #define PCFCLOCK_CMD_TIME 0 /* send current time */ 113 #define PCFCLOCK_CMD_COPY 7 /* copy received signal to PC */ 114 115 static int 116 pcfclock_probe(device_t dev) 117 { 118 struct pcfclock_data *sc; 119 120 device_set_desc(dev, "PCF-1.0"); 121 122 sc = DEVTOSOFTC(dev); 123 bzero(sc, sizeof(struct pcfclock_data)); 124 125 return (0); 126 } 127 128 static int 129 pcfclock_attach(device_t dev) 130 { 131 int unit; 132 133 unit = device_get_unit(dev); 134 135 dev_ops_add(&pcfclock_ops, -1, unit); 136 make_dev(&pcfclock_ops, unit, 137 UID_ROOT, GID_WHEEL, 0444, PCFCLOCK_NAME "%d", unit); 138 139 return (0); 140 } 141 142 static int 143 pcfclock_open(struct dev_open_args *ap) 144 { 145 cdev_t dev = ap->a_head.a_dev; 146 u_int unit = minor(dev); 147 struct pcfclock_data *sc = UNITOSOFTC(unit); 148 device_t pcfclockdev = UNITODEVICE(unit); 149 device_t ppbus = device_get_parent(pcfclockdev); 150 int res; 151 152 if (!sc) 153 return (ENXIO); 154 155 if ((res = ppb_request_bus(ppbus, pcfclockdev, 156 (ap->a_oflags & O_NONBLOCK) ? PPB_DONTWAIT : PPB_WAIT))) 157 return (res); 158 159 sc->count++; 160 161 return (0); 162 } 163 164 static int 165 pcfclock_close(struct dev_close_args *ap) 166 { 167 cdev_t dev = ap->a_head.a_dev; 168 u_int unit = minor(dev); 169 struct pcfclock_data *sc = UNITOSOFTC(unit); 170 device_t pcfclockdev = UNITODEVICE(unit); 171 device_t ppbus = device_get_parent(pcfclockdev); 172 173 sc->count--; 174 if (sc->count == 0) { 175 ppb_release_bus(ppbus, pcfclockdev); 176 } 177 178 return (0); 179 } 180 181 static void 182 pcfclock_write_cmd(cdev_t dev, unsigned char command) 183 { 184 u_int unit = minor(dev); 185 device_t ppidev = UNITODEVICE(unit); 186 device_t ppbus = device_get_parent(ppidev); 187 unsigned char ctr = 14; 188 char i; 189 190 for (i = 0; i <= 7; i++) { 191 ppb_wdtr(ppbus, i); 192 AUTOFEED_CLOCK(i & 1 ? AFC_HI : AFC_LO); 193 DELAY(3000); 194 } 195 ppb_wdtr(ppbus, command); 196 AUTOFEED_CLOCK(AFC_LO); 197 DELAY(3000); 198 AUTOFEED_CLOCK(AFC_HI); 199 } 200 201 static void 202 pcfclock_display_data(cdev_t dev, char buf[18]) 203 { 204 u_int unit = minor(dev); 205 #ifdef PCFCLOCK_VERBOSE 206 int year; 207 208 year = NR(buf, 14); 209 if (year < 70) 210 year += 100; 211 kprintf(PCFCLOCK_NAME "%d: %02d.%02d.%4d %02d:%02d:%02d, " 212 "battery status: %s\n", 213 unit, 214 NR(buf, 10), NR(buf, 12), 1900 + year, 215 NR(buf, 6), NR(buf, 4), NR(buf, 2), 216 PCFCLOCK_BATTERY_STATUS_LOW(buf) ? "LOW" : "ok"); 217 #else 218 if (PCFCLOCK_BATTERY_STATUS_LOW(buf)) 219 kprintf(PCFCLOCK_NAME "%d: BATTERY STATUS LOW ON\n", 220 unit); 221 #endif 222 } 223 224 static int 225 pcfclock_read_data(cdev_t dev, char *buf, ssize_t bits) 226 { 227 u_int unit = minor(dev); 228 device_t ppidev = UNITODEVICE(unit); 229 device_t ppbus = device_get_parent(ppidev); 230 int i; 231 char waitfor; 232 int offset; 233 234 /* one byte per four bits */ 235 bzero(buf, ((bits + 3) >> 2) + 1); 236 237 waitfor = 100; 238 for (i = 0; i <= bits; i++) { 239 /* wait for clock, maximum (waitfor*100) usec */ 240 while(!CLOCK_OK && --waitfor > 0) 241 DELAY(100); 242 243 /* timed out? */ 244 if (!waitfor) 245 return (EIO); 246 247 waitfor = 100; /* reload */ 248 249 /* give it some time */ 250 DELAY(500); 251 252 /* calculate offset into buffer */ 253 offset = i >> 2; 254 buf[offset] <<= 1; 255 256 if (BIT_SET) 257 buf[offset] |= 1; 258 } 259 260 return (0); 261 } 262 263 static int 264 pcfclock_read_dev(cdev_t dev, char *buf, int maxretries) 265 { 266 u_int unit = minor(dev); 267 device_t ppidev = UNITODEVICE(unit); 268 device_t ppbus = device_get_parent(ppidev); 269 int error = 0; 270 271 ppb_set_mode(ppbus, PPB_COMPATIBLE); 272 273 while (--maxretries > 0) { 274 pcfclock_write_cmd(dev, PCFCLOCK_CMD_TIME); 275 if (pcfclock_read_data(dev, buf, 68)) 276 continue; 277 278 if (!PCFCLOCK_CORRECT_SYNC(buf)) 279 continue; 280 281 if (!PCFCLOCK_CORRECT_FORMAT(buf)) 282 continue; 283 284 break; 285 } 286 287 if (!maxretries) 288 error = EIO; 289 290 return (error); 291 } 292 293 static int 294 pcfclock_read(struct dev_read_args *ap) 295 { 296 cdev_t dev = ap->a_head.a_dev; 297 u_int unit = minor(dev); 298 char buf[18]; 299 int error = 0; 300 301 if (ap->a_uio->uio_resid < 18) 302 return (ERANGE); 303 304 error = pcfclock_read_dev(dev, buf, PCFCLOCK_MAX_RETRIES); 305 306 if (error) { 307 kprintf(PCFCLOCK_NAME "%d: no PCF found\n", unit); 308 } else { 309 pcfclock_display_data(dev, buf); 310 311 uiomove(buf, 18, ap->a_uio); 312 } 313 314 return (error); 315 } 316 317 /* 318 * Because pcfclock is a static device that always exists under any 319 * attached ppbus, and not scanned by the ppbus, we need an identify function 320 * to create the device. 321 */ 322 static device_method_t pcfclock_methods[] = { 323 /* device interface */ 324 DEVMETHOD(device_identify, bus_generic_identify), 325 DEVMETHOD(device_probe, pcfclock_probe), 326 DEVMETHOD(device_attach, pcfclock_attach), 327 328 { 0, 0 } 329 }; 330 331 static driver_t pcfclock_driver = { 332 PCFCLOCK_NAME, 333 pcfclock_methods, 334 sizeof(struct pcfclock_data), 335 }; 336 337 DRIVER_MODULE(pcfclock, ppbus, pcfclock_driver, pcfclock_devclass, 0, 0); 338 339