pcfclock.c revision 56293
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: head/sys/dev/ppbus/pcfclock.c 56293 2000-01-19 18:19:16Z jkh $ 25 * 26 */ 27 28#include "pcfclock.h" 29 30#if NPCFCLOCK > 0 31 32#include "opt_pcfclock.h" 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/bus.h> 37#include <sys/sockio.h> 38#include <sys/mbuf.h> 39#include <sys/malloc.h> 40#include <sys/kernel.h> 41#include <sys/conf.h> 42#include <sys/fcntl.h> 43#include <sys/uio.h> 44 45#include <machine/bus.h> 46#include <machine/resource.h> 47#include <machine/clock.h> /* for DELAY */ 48 49#include <dev/ppbus/ppbconf.h> 50#include <dev/ppbus/ppb_msq.h> 51#include <dev/ppbus/ppbio.h> 52 53#include "ppbus_if.h" 54 55#define PCFCLOCK_NAME "pcfclock" 56 57struct pcfclock_data { 58 int count; 59 struct ppb_device pcfclock_dev; 60}; 61 62#define DEVTOSOFTC(dev) \ 63 ((struct pcfclock_data *)device_get_softc(dev)) 64#define UNITOSOFTC(unit) \ 65 ((struct pcfclock_data *)devclass_get_softc(pcfclock_devclass, (unit))) 66#define UNITODEVICE(unit) \ 67 (devclass_get_device(pcfclock_devclass, (unit))) 68 69static devclass_t pcfclock_devclass; 70 71static int pcfclock_probe(device_t); 72static int pcfclock_attach(device_t); 73 74static device_method_t pcfclock_methods[] = { 75 /* device interface */ 76 DEVMETHOD(device_probe, pcfclock_probe), 77 DEVMETHOD(device_attach, pcfclock_attach), 78 79 { 0, 0 } 80}; 81 82static driver_t pcfclock_driver = { 83 PCFCLOCK_NAME, 84 pcfclock_methods, 85 sizeof(struct pcfclock_data), 86}; 87 88static d_open_t pcfclock_open; 89static d_close_t pcfclock_close; 90static d_read_t pcfclock_read; 91 92#define CDEV_MAJOR 140 93static struct cdevsw pcfclock_cdevsw = { 94 /* open */ pcfclock_open, 95 /* close */ pcfclock_close, 96 /* read */ pcfclock_read, 97 /* write */ nowrite, 98 /* ioctl */ noioctl, 99 /* poll */ nopoll, 100 /* mmap */ nommap, 101 /* strategy */ nostrategy, 102 /* name */ PCFCLOCK_NAME, 103 /* maj */ CDEV_MAJOR, 104 /* dump */ nodump, 105 /* psize */ nopsize, 106 /* flags */ 0, 107 /* bmaj */ -1 108}; 109 110#ifndef PCFCLOCK_MAX_RETRIES 111#define PCFCLOCK_MAX_RETRIES 10 112#endif 113 114#define AFC_HI 0 115#define AFC_LO AUTOFEED 116 117/* AUTO FEED is used as clock */ 118#define AUTOFEED_CLOCK(val) \ 119 ctr = (ctr & ~(AUTOFEED)) ^ (val); ppb_wctr(ppbus, ctr) 120 121/* SLCT is used as clock */ 122#define CLOCK_OK \ 123 ((ppb_rstr(ppbus) & SELECT) == (i & 1 ? SELECT : 0)) 124 125/* PE is used as data */ 126#define BIT_SET (ppb_rstr(ppbus)&PERROR) 127 128/* the first byte sent as reply must be 00001001b */ 129#define PCFCLOCK_CORRECT_SYNC(buf) (buf[0] == 9) 130 131#define NR(buf, off) (buf[off+1]*10+buf[off]) 132 133/* check for correct input values */ 134#define PCFCLOCK_CORRECT_FORMAT(buf) (\ 135 NR(buf, 14) <= 99 && \ 136 NR(buf, 12) <= 12 && \ 137 NR(buf, 10) <= 31 && \ 138 NR(buf, 6) <= 23 && \ 139 NR(buf, 4) <= 59 && \ 140 NR(buf, 2) <= 59) 141 142#define PCFCLOCK_BATTERY_STATUS_LOW(buf) (buf[8] & 4) 143 144#define PCFCLOCK_CMD_TIME 0 /* send current time */ 145#define PCFCLOCK_CMD_COPY 7 /* copy received signal to PC */ 146 147static int 148pcfclock_probe(device_t dev) 149{ 150 struct pcfclock_data *sc; 151 static int once; 152 153 device_set_desc(dev, "PCF-1.0"); 154 155 if (!once++) 156 cdevsw_add(&pcfclock_cdevsw); 157 158 sc = DEVTOSOFTC(dev); 159 bzero(sc, sizeof(struct pcfclock_data)); 160 161 return (0); 162} 163 164static int 165pcfclock_attach(device_t dev) 166{ 167 int unit; 168 169 unit = device_get_unit(dev); 170 171 make_dev(&pcfclock_cdevsw, unit, 172 UID_ROOT, GID_WHEEL, 0444, PCFCLOCK_NAME "%d", unit); 173 174 return (0); 175} 176 177static int 178pcfclock_open(dev_t dev, int flag, int fms, struct proc *p) 179{ 180 u_int unit = minor(dev); 181 struct pcfclock_data *sc = UNITOSOFTC(unit); 182 device_t pcfclockdev = UNITODEVICE(unit); 183 device_t ppbus = device_get_parent(pcfclockdev); 184 int res; 185 186 if (!sc) 187 return (ENXIO); 188 189 if ((res = ppb_request_bus(ppbus, pcfclockdev, 190 (flag & O_NONBLOCK) ? PPB_DONTWAIT : PPB_WAIT))) 191 return (res); 192 193 sc->count++; 194 195 return (0); 196} 197 198static int 199pcfclock_close(dev_t dev, int flags, int fmt, struct proc *p) 200{ 201 u_int unit = minor(dev); 202 struct pcfclock_data *sc = UNITOSOFTC(unit); 203 device_t pcfclockdev = UNITODEVICE(unit); 204 device_t ppbus = device_get_parent(pcfclockdev); 205 206 sc->count--; 207 if (sc->count == 0) { 208 ppb_release_bus(ppbus, pcfclockdev); 209 } 210 211 return (0); 212} 213 214static void 215pcfclock_write_cmd(dev_t dev, unsigned char command) 216{ 217 u_int unit = minor(dev); 218 device_t ppidev = UNITODEVICE(unit); 219 device_t ppbus = device_get_parent(ppidev); 220 unsigned char ctr = 14; 221 char i; 222 223 for (i = 0; i <= 7; i++) { 224 ppb_wdtr(ppbus, i); 225 AUTOFEED_CLOCK(i & 1 ? AFC_HI : AFC_LO); 226 DELAY(3000); 227 } 228 ppb_wdtr(ppbus, command); 229 AUTOFEED_CLOCK(AFC_LO); 230 DELAY(3000); 231 AUTOFEED_CLOCK(AFC_HI); 232} 233 234static void 235pcfclock_display_data(dev_t dev, char buf[18]) 236{ 237 u_int unit = minor(dev); 238#ifdef PCFCLOCK_VERBOSE 239 int year; 240 241 year = NR(buf, 14); 242 if (year < 70) 243 year += 100; 244 printf(PCFCLOCK_NAME "%d: %02d.%02d.%4d %02d:%02d:%02d, " 245 "battery status: %s\n", 246 unit, 247 NR(buf, 10), NR(buf, 12), 1900 + year, 248 NR(buf, 6), NR(buf, 4), NR(buf, 2), 249 PCFCLOCK_BATTERY_STATUS_LOW(buf) ? "LOW" : "ok"); 250#else 251 if (PCFCLOCK_BATTERY_STATUS_LOW(buf)) 252 printf(PCFCLOCK_NAME "%d: BATTERY STATUS LOW ON\n", 253 unit); 254#endif 255} 256 257static int 258pcfclock_read_data(dev_t dev, char *buf, ssize_t bits) 259{ 260 u_int unit = minor(dev); 261 device_t ppidev = UNITODEVICE(unit); 262 device_t ppbus = device_get_parent(ppidev); 263 int i; 264 char waitfor; 265 int offset; 266 267 /* one byte per four bits */ 268 bzero(buf, ((bits + 3) >> 2) + 1); 269 270 waitfor = 100; 271 for (i = 0; i <= bits; i++) { 272 /* wait for clock, maximum (waitfor*100) usec */ 273 while(!CLOCK_OK && --waitfor > 0) 274 DELAY(100); 275 276 /* timed out? */ 277 if (!waitfor) 278 return (EIO); 279 280 waitfor = 100; /* reload */ 281 282 /* give it some time */ 283 DELAY(500); 284 285 /* calculate offset into buffer */ 286 offset = i >> 2; 287 buf[offset] <<= 1; 288 289 if (BIT_SET) 290 buf[offset] |= 1; 291 } 292 293 return (0); 294} 295 296static int 297pcfclock_read_dev(dev_t dev, char *buf, int maxretries) 298{ 299 u_int unit = minor(dev); 300 device_t ppidev = UNITODEVICE(unit); 301 device_t ppbus = device_get_parent(ppidev); 302 int error = 0; 303 304 ppb_set_mode(ppbus, PPB_COMPATIBLE); 305 306 while (--maxretries > 0) { 307 pcfclock_write_cmd(dev, PCFCLOCK_CMD_TIME); 308 if (pcfclock_read_data(dev, buf, 68)) 309 continue; 310 311 if (!PCFCLOCK_CORRECT_SYNC(buf)) 312 continue; 313 314 if (!PCFCLOCK_CORRECT_FORMAT(buf)) 315 continue; 316 317 break; 318 } 319 320 if (!maxretries) 321 error = EIO; 322 323 return (error); 324} 325 326static ssize_t 327pcfclock_read(dev_t dev, struct uio *uio, int ioflag) 328{ 329 u_int unit = minor(dev); 330 char buf[18]; 331 int error = 0; 332 333 error = pcfclock_read_dev(dev, buf, PCFCLOCK_MAX_RETRIES); 334 335 if (error) { 336 printf(PCFCLOCK_NAME "%d: no PCF found\n", unit); 337 } else { 338 pcfclock_display_data(dev, buf); 339 340 uiomove(buf, 18, uio); 341 } 342 343 return (error); 344} 345 346DRIVER_MODULE(pcfclock, ppbus, pcfclock_driver, pcfclock_devclass, 0, 0); 347 348#endif /* NPCFCLOCK */ 349