pps.c revision 126080
1/* 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7 * ---------------------------------------------------------------------------- 8 * 9 * 10 * This driver implements a draft-mogul-pps-api-02.txt PPS source. 11 * 12 * The input pin is pin#10 13 * The echo output pin is pin#14 14 * 15 */ 16 17#include <sys/cdefs.h> 18__FBSDID("$FreeBSD: head/sys/dev/ppbus/pps.c 126080 2004-02-21 21:10:55Z phk $"); 19 20#include <sys/param.h> 21#include <sys/kernel.h> 22#include <sys/systm.h> 23#include <sys/module.h> 24#include <sys/bus.h> 25#include <sys/conf.h> 26#include <sys/timepps.h> 27#include <machine/bus.h> 28#include <machine/resource.h> 29#include <sys/rman.h> 30 31#include <dev/ppbus/ppbconf.h> 32#include "ppbus_if.h" 33#include <dev/ppbus/ppbio.h> 34 35#define PPS_NAME "pps" /* our official name */ 36 37#define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg); 38 39struct pps_data { 40 struct ppb_device pps_dev; 41 struct pps_state pps[9]; 42 dev_t devs[9]; 43 device_t ppsdev; 44 device_t ppbus; 45 int busy; 46 struct callout_handle timeout; 47 int lastdata; 48 49 struct resource *intr_resource; /* interrupt resource */ 50 void *intr_cookie; /* interrupt registration cookie */ 51}; 52 53static void ppsintr(void *arg); 54static void ppshcpoll(void *arg); 55 56#define DEVTOSOFTC(dev) \ 57 ((struct pps_data *)device_get_softc(dev)) 58 59static devclass_t pps_devclass; 60 61static d_open_t ppsopen; 62static d_close_t ppsclose; 63static d_ioctl_t ppsioctl; 64 65static struct cdevsw pps_cdevsw = { 66 .d_version = D_VERSION, 67 .d_flags = D_NEEDGIANT, 68 .d_open = ppsopen, 69 .d_close = ppsclose, 70 .d_ioctl = ppsioctl, 71 .d_name = PPS_NAME, 72}; 73 74static void 75ppsidentify(driver_t *driver, device_t parent) 76{ 77 78 BUS_ADD_CHILD(parent, 0, PPS_NAME, -1); 79} 80 81static int 82ppstry(device_t ppbus, int send, int expect) 83{ 84 int i; 85 86 ppb_wdtr(ppbus, send); 87 i = ppb_rdtr(ppbus); 88 PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i); 89 return (i != expect); 90} 91 92static int 93ppsprobe(device_t ppsdev) 94{ 95 device_set_desc(ppsdev, "Pulse per second Timing Interface"); 96 97 return (0); 98} 99 100static int 101ppsattach(device_t dev) 102{ 103 struct pps_data *sc = DEVTOSOFTC(dev); 104 device_t ppbus = device_get_parent(dev); 105 dev_t d; 106 intptr_t irq; 107 int i, unit, zero = 0; 108 109 bzero(sc, sizeof(struct pps_data)); /* XXX doesn't newbus do this? */ 110 111 /* retrieve the ppbus irq */ 112 BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq); 113 114 if (irq > 0) { 115 /* declare our interrupt handler */ 116 sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ, 117 &zero, irq, irq, 1, RF_SHAREABLE); 118 } 119 /* interrupts seem mandatory */ 120 if (sc->intr_resource == NULL) 121 return (ENXIO); 122 123 sc->ppsdev = dev; 124 sc->ppbus = ppbus; 125 unit = device_get_unit(ppbus); 126 d = make_dev(&pps_cdevsw, unit, 127 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); 128 sc->devs[0] = d; 129 sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 130 d->si_drv1 = sc; 131 d->si_drv2 = (void*)0; 132 pps_init(&sc->pps[0]); 133 134 if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) 135 return (0); 136 137 do { 138 i = ppb_set_mode(sc->ppbus, PPB_EPP); 139 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 140 if (i == -1) 141 break; 142 i = 0; 143 ppb_wctr(ppbus, i); 144 if (ppstry(ppbus, 0x00, 0x00)) 145 break; 146 if (ppstry(ppbus, 0x55, 0x55)) 147 break; 148 if (ppstry(ppbus, 0xaa, 0xaa)) 149 break; 150 if (ppstry(ppbus, 0xff, 0xff)) 151 break; 152 153 i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; 154 ppb_wctr(ppbus, i); 155 PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 156 if (ppstry(ppbus, 0x00, 0x00)) 157 break; 158 if (ppstry(ppbus, 0x55, 0x00)) 159 break; 160 if (ppstry(ppbus, 0xaa, 0x00)) 161 break; 162 if (ppstry(ppbus, 0xff, 0x00)) 163 break; 164 165 i = IRQENABLE | PCD | nINIT | SELECTIN; 166 ppb_wctr(ppbus, i); 167 PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 168 ppstry(ppbus, 0x00, 0xff); 169 ppstry(ppbus, 0x55, 0xff); 170 ppstry(ppbus, 0xaa, 0xff); 171 ppstry(ppbus, 0xff, 0xff); 172 173 for (i = 1; i < 9; i++) { 174 d = make_dev(&pps_cdevsw, unit + 0x10000 * i, 175 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); 176 sc->devs[i] = d; 177 sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 178 d->si_drv1 = sc; 179 d->si_drv2 = (void *)(intptr_t)i; 180 pps_init(&sc->pps[i]); 181 } 182 } while (0); 183 i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 184 ppb_release_bus(ppbus, dev); 185 186 return (0); 187} 188 189static int 190ppsopen(dev_t dev, int flags, int fmt, struct thread *td) 191{ 192 struct pps_data *sc = dev->si_drv1; 193 int subdev = (intptr_t)dev->si_drv2; 194 int error, i; 195 196 if (!sc->busy) { 197 device_t ppsdev = sc->ppsdev; 198 device_t ppbus = sc->ppbus; 199 200 if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) 201 return (EINTR); 202 203 /* attach the interrupt handler */ 204 if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource, 205 INTR_TYPE_TTY, ppsintr, ppsdev, 206 &sc->intr_cookie))) { 207 ppb_release_bus(ppbus, ppsdev); 208 return (error); 209 } 210 211 i = ppb_set_mode(sc->ppbus, PPB_PS2); 212 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 213 214 i = IRQENABLE | PCD | nINIT | SELECTIN; 215 ppb_wctr(ppbus, i); 216 } 217 if (subdev > 0 && !(sc->busy & ~1)) { 218 sc->timeout = timeout(ppshcpoll, sc, 1); 219 sc->lastdata = ppb_rdtr(sc->ppbus); 220 } 221 sc->busy |= (1 << subdev); 222 return(0); 223} 224 225static int 226ppsclose(dev_t dev, int flags, int fmt, struct thread *td) 227{ 228 struct pps_data *sc = dev->si_drv1; 229 int subdev = (intptr_t)dev->si_drv2; 230 231 sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ 232 sc->busy &= ~(1 << subdev); 233 if (subdev > 0 && !(sc->busy & ~1)) 234 untimeout(ppshcpoll, sc, sc->timeout); 235 if (!sc->busy) { 236 device_t ppsdev = sc->ppsdev; 237 device_t ppbus = sc->ppbus; 238 239 ppb_wdtr(ppbus, 0); 240 ppb_wctr(ppbus, 0); 241 242 /* Note: the interrupt handler is automatically detached */ 243 ppb_set_mode(ppbus, PPB_COMPATIBLE); 244 ppb_release_bus(ppbus, ppsdev); 245 } 246 return(0); 247} 248 249static void 250ppshcpoll(void *arg) 251{ 252 struct pps_data *sc = arg; 253 int i, j, k, l; 254 255 if (!(sc->busy & ~1)) 256 return; 257 sc->timeout = timeout(ppshcpoll, sc, 1); 258 i = ppb_rdtr(sc->ppbus); 259 if (i == sc->lastdata) 260 return; 261 l = sc->lastdata ^ i; 262 k = 1; 263 for (j = 1; j < 9; j ++) { 264 if (l & k) { 265 pps_capture(&sc->pps[j]); 266 pps_event(&sc->pps[j], 267 i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 268 } 269 k += k; 270 } 271 sc->lastdata = i; 272} 273 274static void 275ppsintr(void *arg) 276{ 277 device_t ppsdev = (device_t)arg; 278 struct pps_data *sc = DEVTOSOFTC(ppsdev); 279 device_t ppbus = sc->ppbus; 280 281 pps_capture(&sc->pps[0]); 282 if (!(ppb_rstr(ppbus) & nACK)) 283 return; 284 if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 285 ppb_wctr(ppbus, IRQENABLE | AUTOFEED); 286 pps_event(&sc->pps[0], PPS_CAPTUREASSERT); 287 if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 288 ppb_wctr(ppbus, IRQENABLE); 289} 290 291static int 292ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td) 293{ 294 struct pps_data *sc = dev->si_drv1; 295 int subdev = (intptr_t)dev->si_drv2; 296 297 return (pps_ioctl(cmd, data, &sc->pps[subdev])); 298} 299 300static device_method_t pps_methods[] = { 301 /* device interface */ 302 DEVMETHOD(device_identify, ppsidentify), 303 DEVMETHOD(device_probe, ppsprobe), 304 DEVMETHOD(device_attach, ppsattach), 305 306 { 0, 0 } 307}; 308 309static driver_t pps_driver = { 310 PPS_NAME, 311 pps_methods, 312 sizeof(struct pps_data), 313}; 314DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 315