1507e2e44SPoul-Henning Kamp /* 2507e2e44SPoul-Henning Kamp * ---------------------------------------------------------------------------- 3507e2e44SPoul-Henning Kamp * "THE BEER-WARE LICENSE" (Revision 42): 4507e2e44SPoul-Henning Kamp * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 5507e2e44SPoul-Henning Kamp * can do whatever you want with this stuff. If we meet some day, and you think 6507e2e44SPoul-Henning Kamp * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7507e2e44SPoul-Henning Kamp * ---------------------------------------------------------------------------- 8507e2e44SPoul-Henning Kamp * 9389825d5SPoul-Henning Kamp * 10389825d5SPoul-Henning Kamp * This driver implements a draft-mogul-pps-api-02.txt PPS source. 11389825d5SPoul-Henning Kamp * 12389825d5SPoul-Henning Kamp * The input pin is pin#10 13389825d5SPoul-Henning Kamp * The echo output pin is pin#14 14507e2e44SPoul-Henning Kamp * 15507e2e44SPoul-Henning Kamp */ 16507e2e44SPoul-Henning Kamp 17aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 18aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 19aad970f1SDavid E. O'Brien 20507e2e44SPoul-Henning Kamp #include <sys/param.h> 21507e2e44SPoul-Henning Kamp #include <sys/kernel.h> 22507e2e44SPoul-Henning Kamp #include <sys/systm.h> 230f210c92SNicolas Souchu #include <sys/module.h> 240f210c92SNicolas Souchu #include <sys/bus.h> 25507e2e44SPoul-Henning Kamp #include <sys/conf.h> 268afeddf0SPoul-Henning Kamp #include <sys/timepps.h> 270f210c92SNicolas Souchu #include <machine/bus.h> 280f210c92SNicolas Souchu #include <machine/resource.h> 290f210c92SNicolas Souchu #include <sys/rman.h> 30507e2e44SPoul-Henning Kamp 31507e2e44SPoul-Henning Kamp #include <dev/ppbus/ppbconf.h> 320f210c92SNicolas Souchu #include "ppbus_if.h" 330f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h> 34507e2e44SPoul-Henning Kamp 351a03ce6cSPoul-Henning Kamp #define PPS_NAME "pps" /* our official name */ 36507e2e44SPoul-Henning Kamp 37daefef7cSPeter Wemm #define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg); 38d7e53105SWarner Losh 391a03ce6cSPoul-Henning Kamp struct pps_data { 4095f0f58cSPoul-Henning Kamp struct ppb_device pps_dev; 41bd61b8e8SPoul-Henning Kamp struct pps_state pps[9]; 42bd61b8e8SPoul-Henning Kamp dev_t devs[9]; 43bd61b8e8SPoul-Henning Kamp device_t ppsdev; 44bd61b8e8SPoul-Henning Kamp device_t ppbus; 45bd61b8e8SPoul-Henning Kamp int busy; 46bd61b8e8SPoul-Henning Kamp struct callout_handle timeout; 47bd61b8e8SPoul-Henning Kamp int lastdata; 480f210c92SNicolas Souchu 490f210c92SNicolas Souchu struct resource *intr_resource; /* interrupt resource */ 500f210c92SNicolas Souchu void *intr_cookie; /* interrupt registration cookie */ 511a03ce6cSPoul-Henning Kamp }; 52507e2e44SPoul-Henning Kamp 530f210c92SNicolas Souchu static void ppsintr(void *arg); 54bd61b8e8SPoul-Henning Kamp static void ppshcpoll(void *arg); 55507e2e44SPoul-Henning Kamp 560f210c92SNicolas Souchu #define DEVTOSOFTC(dev) \ 570f210c92SNicolas Souchu ((struct pps_data *)device_get_softc(dev)) 58507e2e44SPoul-Henning Kamp 590f210c92SNicolas Souchu static devclass_t pps_devclass; 60507e2e44SPoul-Henning Kamp 6195f0f58cSPoul-Henning Kamp static d_open_t ppsopen; 6295f0f58cSPoul-Henning Kamp static d_close_t ppsclose; 638afeddf0SPoul-Henning Kamp static d_ioctl_t ppsioctl; 64507e2e44SPoul-Henning Kamp 654e2f199eSPoul-Henning Kamp static struct cdevsw pps_cdevsw = { 66dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 67dc08ffecSPoul-Henning Kamp .d_flags = D_NEEDGIANT, 687ac40f5fSPoul-Henning Kamp .d_open = ppsopen, 697ac40f5fSPoul-Henning Kamp .d_close = ppsclose, 707ac40f5fSPoul-Henning Kamp .d_ioctl = ppsioctl, 717ac40f5fSPoul-Henning Kamp .d_name = PPS_NAME, 724e2f199eSPoul-Henning Kamp }; 73507e2e44SPoul-Henning Kamp 740f063508SPeter Wemm static void 750f063508SPeter Wemm ppsidentify(driver_t *driver, device_t parent) 760f063508SPeter Wemm { 770f063508SPeter Wemm 78a5c7e3bbSGuido van Rooij device_t dev; 79a5c7e3bbSGuido van Rooij 80a5c7e3bbSGuido van Rooij dev = device_find_child(parent, PPS_NAME, 0); 81a5c7e3bbSGuido van Rooij if (!dev) 82338cad62SBernd Walter BUS_ADD_CHILD(parent, 0, PPS_NAME, -1); 830f063508SPeter Wemm } 840f063508SPeter Wemm 850f210c92SNicolas Souchu static int 86bd61b8e8SPoul-Henning Kamp ppstry(device_t ppbus, int send, int expect) 87bd61b8e8SPoul-Henning Kamp { 88bd61b8e8SPoul-Henning Kamp int i; 89bd61b8e8SPoul-Henning Kamp 90bd61b8e8SPoul-Henning Kamp ppb_wdtr(ppbus, send); 91bd61b8e8SPoul-Henning Kamp i = ppb_rdtr(ppbus); 92d7e53105SWarner Losh PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i); 93bd61b8e8SPoul-Henning Kamp return (i != expect); 94bd61b8e8SPoul-Henning Kamp } 95bd61b8e8SPoul-Henning Kamp 96bd61b8e8SPoul-Henning Kamp static int 970f210c92SNicolas Souchu ppsprobe(device_t ppsdev) 98507e2e44SPoul-Henning Kamp { 990f210c92SNicolas Souchu device_set_desc(ppsdev, "Pulse per second Timing Interface"); 100507e2e44SPoul-Henning Kamp 101d7e53105SWarner Losh return (0); 102d7e53105SWarner Losh } 103d7e53105SWarner Losh 104d7e53105SWarner Losh static int 105d7e53105SWarner Losh ppsattach(device_t dev) 106d7e53105SWarner Losh { 107d7e53105SWarner Losh struct pps_data *sc = DEVTOSOFTC(dev); 108d7e53105SWarner Losh device_t ppbus = device_get_parent(dev); 109d7e53105SWarner Losh dev_t d; 110a885bb60SJohn Baldwin intptr_t irq; 111a885bb60SJohn Baldwin int i, unit, zero = 0; 112d7e53105SWarner Losh 113d7e53105SWarner Losh bzero(sc, sizeof(struct pps_data)); /* XXX doesn't newbus do this? */ 114d7e53105SWarner Losh 115d7e53105SWarner Losh /* retrieve the ppbus irq */ 116d7e53105SWarner Losh BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq); 117d7e53105SWarner Losh 118d7e53105SWarner Losh if (irq > 0) { 119d7e53105SWarner Losh /* declare our interrupt handler */ 120d7e53105SWarner Losh sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ, 121d7e53105SWarner Losh &zero, irq, irq, 1, RF_SHAREABLE); 122d7e53105SWarner Losh } 123d7e53105SWarner Losh /* interrupts seem mandatory */ 124d7e53105SWarner Losh if (sc->intr_resource == NULL) 125d7e53105SWarner Losh return (ENXIO); 126d7e53105SWarner Losh 127d7e53105SWarner Losh sc->ppsdev = dev; 128d7e53105SWarner Losh sc->ppbus = ppbus; 129bd61b8e8SPoul-Henning Kamp unit = device_get_unit(ppbus); 130d7e53105SWarner Losh d = make_dev(&pps_cdevsw, unit, 1312066d8e7SRobert Watson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); 132d7e53105SWarner Losh sc->devs[0] = d; 133bd61b8e8SPoul-Henning Kamp sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 134d7e53105SWarner Losh d->si_drv1 = sc; 135d7e53105SWarner Losh d->si_drv2 = (void*)0; 136bd61b8e8SPoul-Henning Kamp pps_init(&sc->pps[0]); 137bd61b8e8SPoul-Henning Kamp 138d7e53105SWarner Losh if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) 139bd61b8e8SPoul-Henning Kamp return (0); 140bd61b8e8SPoul-Henning Kamp 141bd61b8e8SPoul-Henning Kamp do { 142bd61b8e8SPoul-Henning Kamp i = ppb_set_mode(sc->ppbus, PPB_EPP); 143d7e53105SWarner Losh PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 144bd61b8e8SPoul-Henning Kamp if (i == -1) 145bd61b8e8SPoul-Henning Kamp break; 146bd61b8e8SPoul-Henning Kamp i = 0; 147bd61b8e8SPoul-Henning Kamp ppb_wctr(ppbus, i); 148bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0x00, 0x00)) 149bd61b8e8SPoul-Henning Kamp break; 150bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0x55, 0x55)) 151bd61b8e8SPoul-Henning Kamp break; 152bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0xaa, 0xaa)) 153bd61b8e8SPoul-Henning Kamp break; 154bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0xff, 0xff)) 155bd61b8e8SPoul-Henning Kamp break; 156bd61b8e8SPoul-Henning Kamp 157bd61b8e8SPoul-Henning Kamp i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; 158bd61b8e8SPoul-Henning Kamp ppb_wctr(ppbus, i); 159d7e53105SWarner Losh PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 160bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0x00, 0x00)) 161bd61b8e8SPoul-Henning Kamp break; 162bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0x55, 0x00)) 163bd61b8e8SPoul-Henning Kamp break; 164bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0xaa, 0x00)) 165bd61b8e8SPoul-Henning Kamp break; 166bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0xff, 0x00)) 167bd61b8e8SPoul-Henning Kamp break; 168bd61b8e8SPoul-Henning Kamp 169bd61b8e8SPoul-Henning Kamp i = IRQENABLE | PCD | nINIT | SELECTIN; 170bd61b8e8SPoul-Henning Kamp ppb_wctr(ppbus, i); 171d7e53105SWarner Losh PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 172bd61b8e8SPoul-Henning Kamp ppstry(ppbus, 0x00, 0xff); 173bd61b8e8SPoul-Henning Kamp ppstry(ppbus, 0x55, 0xff); 174bd61b8e8SPoul-Henning Kamp ppstry(ppbus, 0xaa, 0xff); 175bd61b8e8SPoul-Henning Kamp ppstry(ppbus, 0xff, 0xff); 176bd61b8e8SPoul-Henning Kamp 177bd61b8e8SPoul-Henning Kamp for (i = 1; i < 9; i++) { 178d7e53105SWarner Losh d = make_dev(&pps_cdevsw, unit + 0x10000 * i, 1792066d8e7SRobert Watson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); 180d7e53105SWarner Losh sc->devs[i] = d; 181bd61b8e8SPoul-Henning Kamp sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 182d7e53105SWarner Losh d->si_drv1 = sc; 183a885bb60SJohn Baldwin d->si_drv2 = (void *)(intptr_t)i; 184bd61b8e8SPoul-Henning Kamp pps_init(&sc->pps[i]); 185bd61b8e8SPoul-Henning Kamp } 186bd61b8e8SPoul-Henning Kamp } while (0); 187bd61b8e8SPoul-Henning Kamp i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 188d7e53105SWarner Losh ppb_release_bus(ppbus, dev); 1890f210c92SNicolas Souchu 1900f210c92SNicolas Souchu return (0); 191507e2e44SPoul-Henning Kamp } 192507e2e44SPoul-Henning Kamp 193507e2e44SPoul-Henning Kamp static int 194b40ce416SJulian Elischer ppsopen(dev_t dev, int flags, int fmt, struct thread *td) 195507e2e44SPoul-Henning Kamp { 196bd61b8e8SPoul-Henning Kamp struct pps_data *sc = dev->si_drv1; 197a885bb60SJohn Baldwin int subdev = (intptr_t)dev->si_drv2; 198bd61b8e8SPoul-Henning Kamp int error, i; 199507e2e44SPoul-Henning Kamp 200bd61b8e8SPoul-Henning Kamp if (!sc->busy) { 201bd61b8e8SPoul-Henning Kamp device_t ppsdev = sc->ppsdev; 202bd61b8e8SPoul-Henning Kamp device_t ppbus = sc->ppbus; 203bd61b8e8SPoul-Henning Kamp 2040f210c92SNicolas Souchu if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) 205507e2e44SPoul-Henning Kamp return (EINTR); 206507e2e44SPoul-Henning Kamp 2070f210c92SNicolas Souchu /* attach the interrupt handler */ 2080f210c92SNicolas Souchu if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource, 2090f210c92SNicolas Souchu INTR_TYPE_TTY, ppsintr, ppsdev, 2100f210c92SNicolas Souchu &sc->intr_cookie))) { 2110f210c92SNicolas Souchu ppb_release_bus(ppbus, ppsdev); 2120f210c92SNicolas Souchu return (error); 2130f210c92SNicolas Souchu } 2140f210c92SNicolas Souchu 215bd61b8e8SPoul-Henning Kamp i = ppb_set_mode(sc->ppbus, PPB_PS2); 216d7e53105SWarner Losh PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 217507e2e44SPoul-Henning Kamp 218bd61b8e8SPoul-Henning Kamp i = IRQENABLE | PCD | nINIT | SELECTIN; 219bd61b8e8SPoul-Henning Kamp ppb_wctr(ppbus, i); 220bd61b8e8SPoul-Henning Kamp } 221bd61b8e8SPoul-Henning Kamp if (subdev > 0 && !(sc->busy & ~1)) { 222bd61b8e8SPoul-Henning Kamp sc->timeout = timeout(ppshcpoll, sc, 1); 223bd61b8e8SPoul-Henning Kamp sc->lastdata = ppb_rdtr(sc->ppbus); 224bd61b8e8SPoul-Henning Kamp } 225bd61b8e8SPoul-Henning Kamp sc->busy |= (1 << subdev); 226507e2e44SPoul-Henning Kamp return(0); 227507e2e44SPoul-Henning Kamp } 228507e2e44SPoul-Henning Kamp 229507e2e44SPoul-Henning Kamp static int 230b40ce416SJulian Elischer ppsclose(dev_t dev, int flags, int fmt, struct thread *td) 231507e2e44SPoul-Henning Kamp { 232bd61b8e8SPoul-Henning Kamp struct pps_data *sc = dev->si_drv1; 233a885bb60SJohn Baldwin int subdev = (intptr_t)dev->si_drv2; 234507e2e44SPoul-Henning Kamp 235bd61b8e8SPoul-Henning Kamp sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ 236bd61b8e8SPoul-Henning Kamp sc->busy &= ~(1 << subdev); 237bd61b8e8SPoul-Henning Kamp if (subdev > 0 && !(sc->busy & ~1)) 238bd61b8e8SPoul-Henning Kamp untimeout(ppshcpoll, sc, sc->timeout); 239bd61b8e8SPoul-Henning Kamp if (!sc->busy) { 240bd61b8e8SPoul-Henning Kamp device_t ppsdev = sc->ppsdev; 241bd61b8e8SPoul-Henning Kamp device_t ppbus = sc->ppbus; 24220240fa3SNicolas Souchu 2430f210c92SNicolas Souchu ppb_wdtr(ppbus, 0); 2440f210c92SNicolas Souchu ppb_wctr(ppbus, 0); 24520240fa3SNicolas Souchu 2460f210c92SNicolas Souchu /* Note: the interrupt handler is automatically detached */ 247bd61b8e8SPoul-Henning Kamp ppb_set_mode(ppbus, PPB_COMPATIBLE); 2480f210c92SNicolas Souchu ppb_release_bus(ppbus, ppsdev); 249bd61b8e8SPoul-Henning Kamp } 250507e2e44SPoul-Henning Kamp return(0); 251507e2e44SPoul-Henning Kamp } 252507e2e44SPoul-Henning Kamp 253507e2e44SPoul-Henning Kamp static void 254bd61b8e8SPoul-Henning Kamp ppshcpoll(void *arg) 255bd61b8e8SPoul-Henning Kamp { 256bd61b8e8SPoul-Henning Kamp struct pps_data *sc = arg; 257bd61b8e8SPoul-Henning Kamp int i, j, k, l; 258bd61b8e8SPoul-Henning Kamp 259bd61b8e8SPoul-Henning Kamp if (!(sc->busy & ~1)) 260bd61b8e8SPoul-Henning Kamp return; 261bd61b8e8SPoul-Henning Kamp sc->timeout = timeout(ppshcpoll, sc, 1); 262bd61b8e8SPoul-Henning Kamp i = ppb_rdtr(sc->ppbus); 263bd61b8e8SPoul-Henning Kamp if (i == sc->lastdata) 264bd61b8e8SPoul-Henning Kamp return; 265bd61b8e8SPoul-Henning Kamp l = sc->lastdata ^ i; 266bd61b8e8SPoul-Henning Kamp k = 1; 267bd61b8e8SPoul-Henning Kamp for (j = 1; j < 9; j ++) { 2687bf758bfSPoul-Henning Kamp if (l & k) { 2697bf758bfSPoul-Henning Kamp pps_capture(&sc->pps[j]); 2707bf758bfSPoul-Henning Kamp pps_event(&sc->pps[j], 2717bf758bfSPoul-Henning Kamp i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 2727bf758bfSPoul-Henning Kamp } 273bd61b8e8SPoul-Henning Kamp k += k; 274bd61b8e8SPoul-Henning Kamp } 275bd61b8e8SPoul-Henning Kamp sc->lastdata = i; 276bd61b8e8SPoul-Henning Kamp } 277bd61b8e8SPoul-Henning Kamp 278bd61b8e8SPoul-Henning Kamp static void 2790f210c92SNicolas Souchu ppsintr(void *arg) 280507e2e44SPoul-Henning Kamp { 2810f210c92SNicolas Souchu device_t ppsdev = (device_t)arg; 2820f210c92SNicolas Souchu struct pps_data *sc = DEVTOSOFTC(ppsdev); 283bd61b8e8SPoul-Henning Kamp device_t ppbus = sc->ppbus; 284507e2e44SPoul-Henning Kamp 2857bf758bfSPoul-Henning Kamp pps_capture(&sc->pps[0]); 2860f210c92SNicolas Souchu if (!(ppb_rstr(ppbus) & nACK)) 287507e2e44SPoul-Henning Kamp return; 288bd61b8e8SPoul-Henning Kamp if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 2890f210c92SNicolas Souchu ppb_wctr(ppbus, IRQENABLE | AUTOFEED); 2907bf758bfSPoul-Henning Kamp pps_event(&sc->pps[0], PPS_CAPTUREASSERT); 291bd61b8e8SPoul-Henning Kamp if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 2920f210c92SNicolas Souchu ppb_wctr(ppbus, IRQENABLE); 293507e2e44SPoul-Henning Kamp } 294507e2e44SPoul-Henning Kamp 295507e2e44SPoul-Henning Kamp static int 296b40ce416SJulian Elischer ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td) 297507e2e44SPoul-Henning Kamp { 298bd61b8e8SPoul-Henning Kamp struct pps_data *sc = dev->si_drv1; 299a885bb60SJohn Baldwin int subdev = (intptr_t)dev->si_drv2; 300507e2e44SPoul-Henning Kamp 301bd61b8e8SPoul-Henning Kamp return (pps_ioctl(cmd, data, &sc->pps[subdev])); 3028afeddf0SPoul-Henning Kamp } 303507e2e44SPoul-Henning Kamp 3040f063508SPeter Wemm static device_method_t pps_methods[] = { 3050f063508SPeter Wemm /* device interface */ 3060f063508SPeter Wemm DEVMETHOD(device_identify, ppsidentify), 3070f063508SPeter Wemm DEVMETHOD(device_probe, ppsprobe), 3080f063508SPeter Wemm DEVMETHOD(device_attach, ppsattach), 3090f063508SPeter Wemm 3100f063508SPeter Wemm { 0, 0 } 3110f063508SPeter Wemm }; 3120f063508SPeter Wemm 3130f063508SPeter Wemm static driver_t pps_driver = { 3140f063508SPeter Wemm PPS_NAME, 3150f063508SPeter Wemm pps_methods, 3160f063508SPeter Wemm sizeof(struct pps_data), 3170f063508SPeter Wemm }; 3180f210c92SNicolas Souchu DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 319