1098ca2bdSWarner Losh /*- 264de3fddSPedro F. Giffuni * SPDX-License-Identifier: Beerware 364de3fddSPedro F. Giffuni * 4507e2e44SPoul-Henning Kamp * ---------------------------------------------------------------------------- 5507e2e44SPoul-Henning Kamp * "THE BEER-WARE LICENSE" (Revision 42): 6507e2e44SPoul-Henning Kamp * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 7507e2e44SPoul-Henning Kamp * can do whatever you want with this stuff. If we meet some day, and you think 8507e2e44SPoul-Henning Kamp * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 9507e2e44SPoul-Henning Kamp * ---------------------------------------------------------------------------- 10507e2e44SPoul-Henning Kamp * 11389825d5SPoul-Henning Kamp * 12389825d5SPoul-Henning Kamp * This driver implements a draft-mogul-pps-api-02.txt PPS source. 13389825d5SPoul-Henning Kamp * 14389825d5SPoul-Henning Kamp * The input pin is pin#10 15389825d5SPoul-Henning Kamp * The echo output pin is pin#14 16507e2e44SPoul-Henning Kamp * 17507e2e44SPoul-Henning Kamp */ 18507e2e44SPoul-Henning Kamp 19aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 20aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 21aad970f1SDavid E. O'Brien 22507e2e44SPoul-Henning Kamp #include <sys/param.h> 232067d312SJohn Baldwin #include <sys/lock.h> 24507e2e44SPoul-Henning Kamp #include <sys/kernel.h> 25507e2e44SPoul-Henning Kamp #include <sys/systm.h> 260f210c92SNicolas Souchu #include <sys/module.h> 272067d312SJohn Baldwin #include <sys/sx.h> 280f210c92SNicolas Souchu #include <sys/bus.h> 29507e2e44SPoul-Henning Kamp #include <sys/conf.h> 308afeddf0SPoul-Henning Kamp #include <sys/timepps.h> 310f210c92SNicolas Souchu #include <machine/bus.h> 320f210c92SNicolas Souchu #include <machine/resource.h> 330f210c92SNicolas Souchu #include <sys/rman.h> 34507e2e44SPoul-Henning Kamp 35507e2e44SPoul-Henning Kamp #include <dev/ppbus/ppbconf.h> 360f210c92SNicolas Souchu #include "ppbus_if.h" 370f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h> 38507e2e44SPoul-Henning Kamp 391a03ce6cSPoul-Henning Kamp #define PPS_NAME "pps" /* our official name */ 40507e2e44SPoul-Henning Kamp 41daefef7cSPeter Wemm #define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg); 42d7e53105SWarner Losh 431a03ce6cSPoul-Henning Kamp struct pps_data { 4495f0f58cSPoul-Henning Kamp struct ppb_device pps_dev; 45bd61b8e8SPoul-Henning Kamp struct pps_state pps[9]; 4689c9c53dSPoul-Henning Kamp struct cdev *devs[9]; 47bd61b8e8SPoul-Henning Kamp device_t ppsdev; 48bd61b8e8SPoul-Henning Kamp device_t ppbus; 49bd61b8e8SPoul-Henning Kamp int busy; 502067d312SJohn Baldwin struct callout timeout; 51bd61b8e8SPoul-Henning Kamp int lastdata; 520f210c92SNicolas Souchu 532067d312SJohn Baldwin struct sx lock; 540f210c92SNicolas Souchu struct resource *intr_resource; /* interrupt resource */ 550f210c92SNicolas Souchu void *intr_cookie; /* interrupt registration cookie */ 561a03ce6cSPoul-Henning Kamp }; 57507e2e44SPoul-Henning Kamp 582067d312SJohn Baldwin static void ppsintr(void *arg); 59bd61b8e8SPoul-Henning Kamp static void ppshcpoll(void *arg); 60507e2e44SPoul-Henning Kamp 610f210c92SNicolas Souchu #define DEVTOSOFTC(dev) \ 620f210c92SNicolas Souchu ((struct pps_data *)device_get_softc(dev)) 63507e2e44SPoul-Henning Kamp 640f210c92SNicolas Souchu static devclass_t pps_devclass; 65507e2e44SPoul-Henning Kamp 6695f0f58cSPoul-Henning Kamp static d_open_t ppsopen; 6795f0f58cSPoul-Henning Kamp static d_close_t ppsclose; 688afeddf0SPoul-Henning Kamp static d_ioctl_t ppsioctl; 69507e2e44SPoul-Henning Kamp 704e2f199eSPoul-Henning Kamp static struct cdevsw pps_cdevsw = { 71dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 727ac40f5fSPoul-Henning Kamp .d_open = ppsopen, 737ac40f5fSPoul-Henning Kamp .d_close = ppsclose, 747ac40f5fSPoul-Henning Kamp .d_ioctl = ppsioctl, 757ac40f5fSPoul-Henning Kamp .d_name = PPS_NAME, 764e2f199eSPoul-Henning Kamp }; 77507e2e44SPoul-Henning Kamp 780f063508SPeter Wemm static void 790f063508SPeter Wemm ppsidentify(driver_t *driver, device_t parent) 800f063508SPeter Wemm { 810f063508SPeter Wemm 82a5c7e3bbSGuido van Rooij device_t dev; 83a5c7e3bbSGuido van Rooij 84ae6b868aSJohn Baldwin dev = device_find_child(parent, PPS_NAME, -1); 85a5c7e3bbSGuido van Rooij if (!dev) 86338cad62SBernd Walter BUS_ADD_CHILD(parent, 0, PPS_NAME, -1); 870f063508SPeter Wemm } 880f063508SPeter Wemm 890f210c92SNicolas Souchu static int 90bd61b8e8SPoul-Henning Kamp ppstry(device_t ppbus, int send, int expect) 91bd61b8e8SPoul-Henning Kamp { 92bd61b8e8SPoul-Henning Kamp int i; 93bd61b8e8SPoul-Henning Kamp 94bd61b8e8SPoul-Henning Kamp ppb_wdtr(ppbus, send); 95bd61b8e8SPoul-Henning Kamp i = ppb_rdtr(ppbus); 96d7e53105SWarner Losh PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i); 97bd61b8e8SPoul-Henning Kamp return (i != expect); 98bd61b8e8SPoul-Henning Kamp } 99bd61b8e8SPoul-Henning Kamp 100bd61b8e8SPoul-Henning Kamp static int 1010f210c92SNicolas Souchu ppsprobe(device_t ppsdev) 102507e2e44SPoul-Henning Kamp { 1030f210c92SNicolas Souchu device_set_desc(ppsdev, "Pulse per second Timing Interface"); 104507e2e44SPoul-Henning Kamp 105d7e53105SWarner Losh return (0); 106d7e53105SWarner Losh } 107d7e53105SWarner Losh 108d7e53105SWarner Losh static int 109d7e53105SWarner Losh ppsattach(device_t dev) 110d7e53105SWarner Losh { 111d7e53105SWarner Losh struct pps_data *sc = DEVTOSOFTC(dev); 112d7e53105SWarner Losh device_t ppbus = device_get_parent(dev); 11389c9c53dSPoul-Henning Kamp struct cdev *d; 1142067d312SJohn Baldwin int error, i, unit, rid = 0; 115d7e53105SWarner Losh 116d7e53105SWarner Losh /* declare our interrupt handler */ 117ca3d3795SJohn Baldwin sc->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 118ca3d3795SJohn Baldwin RF_SHAREABLE); 119ca3d3795SJohn Baldwin 120d7e53105SWarner Losh /* interrupts seem mandatory */ 1212067d312SJohn Baldwin if (sc->intr_resource == NULL) { 1222067d312SJohn Baldwin device_printf(dev, "Unable to allocate interrupt resource\n"); 123d7e53105SWarner Losh return (ENXIO); 1242067d312SJohn Baldwin } 125d7e53105SWarner Losh 1262067d312SJohn Baldwin error = bus_setup_intr(dev, sc->intr_resource, 1272067d312SJohn Baldwin INTR_TYPE_TTY | INTR_MPSAFE, NULL, ppsintr, 1282067d312SJohn Baldwin sc, &sc->intr_cookie); 1292067d312SJohn Baldwin if (error) { 1302067d312SJohn Baldwin bus_release_resource(dev, SYS_RES_IRQ, 0, sc->intr_resource); 1312067d312SJohn Baldwin device_printf(dev, "Unable to register interrupt handler\n"); 1322067d312SJohn Baldwin return (error); 1332067d312SJohn Baldwin } 1342067d312SJohn Baldwin 1352067d312SJohn Baldwin sx_init(&sc->lock, "pps"); 1362067d312SJohn Baldwin ppb_init_callout(ppbus, &sc->timeout, 0); 137d7e53105SWarner Losh sc->ppsdev = dev; 138d7e53105SWarner Losh sc->ppbus = ppbus; 139bd61b8e8SPoul-Henning Kamp unit = device_get_unit(ppbus); 140d7e53105SWarner Losh d = make_dev(&pps_cdevsw, unit, 1412066d8e7SRobert Watson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); 142d7e53105SWarner Losh sc->devs[0] = d; 143bd61b8e8SPoul-Henning Kamp sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 144*c62aa65bSWarner Losh sc->pps[0].driver_abi = PPS_ABI_VERSION; 145*c62aa65bSWarner Losh sc->pps[0].driver_mtx = ppb_get_lock(ppbus); 146d7e53105SWarner Losh d->si_drv1 = sc; 147d7e53105SWarner Losh d->si_drv2 = (void*)0; 148*c62aa65bSWarner Losh pps_init_abi(&sc->pps[0]); 149bd61b8e8SPoul-Henning Kamp 1502067d312SJohn Baldwin ppb_lock(ppbus); 1512067d312SJohn Baldwin if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 1522067d312SJohn Baldwin ppb_unlock(ppbus); 153bd61b8e8SPoul-Henning Kamp return (0); 1542067d312SJohn Baldwin } 155bd61b8e8SPoul-Henning Kamp 156bd61b8e8SPoul-Henning Kamp do { 157bd61b8e8SPoul-Henning Kamp i = ppb_set_mode(sc->ppbus, PPB_EPP); 158d7e53105SWarner Losh PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 159bd61b8e8SPoul-Henning Kamp if (i == -1) 160bd61b8e8SPoul-Henning Kamp break; 161bd61b8e8SPoul-Henning Kamp i = 0; 162bd61b8e8SPoul-Henning Kamp ppb_wctr(ppbus, i); 163bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0x00, 0x00)) 164bd61b8e8SPoul-Henning Kamp break; 165bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0x55, 0x55)) 166bd61b8e8SPoul-Henning Kamp break; 167bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0xaa, 0xaa)) 168bd61b8e8SPoul-Henning Kamp break; 169bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0xff, 0xff)) 170bd61b8e8SPoul-Henning Kamp break; 171bd61b8e8SPoul-Henning Kamp 172bd61b8e8SPoul-Henning Kamp i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; 173bd61b8e8SPoul-Henning Kamp ppb_wctr(ppbus, i); 174d7e53105SWarner Losh PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 175bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0x00, 0x00)) 176bd61b8e8SPoul-Henning Kamp break; 177bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0x55, 0x00)) 178bd61b8e8SPoul-Henning Kamp break; 179bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0xaa, 0x00)) 180bd61b8e8SPoul-Henning Kamp break; 181bd61b8e8SPoul-Henning Kamp if (ppstry(ppbus, 0xff, 0x00)) 182bd61b8e8SPoul-Henning Kamp break; 183bd61b8e8SPoul-Henning Kamp 184bd61b8e8SPoul-Henning Kamp i = IRQENABLE | PCD | nINIT | SELECTIN; 185bd61b8e8SPoul-Henning Kamp ppb_wctr(ppbus, i); 186d7e53105SWarner Losh PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 187bd61b8e8SPoul-Henning Kamp ppstry(ppbus, 0x00, 0xff); 188bd61b8e8SPoul-Henning Kamp ppstry(ppbus, 0x55, 0xff); 189bd61b8e8SPoul-Henning Kamp ppstry(ppbus, 0xaa, 0xff); 190bd61b8e8SPoul-Henning Kamp ppstry(ppbus, 0xff, 0xff); 1912067d312SJohn Baldwin ppb_unlock(ppbus); 192bd61b8e8SPoul-Henning Kamp 193bd61b8e8SPoul-Henning Kamp for (i = 1; i < 9; i++) { 194d7e53105SWarner Losh d = make_dev(&pps_cdevsw, unit + 0x10000 * i, 1952066d8e7SRobert Watson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); 196d7e53105SWarner Losh sc->devs[i] = d; 197bd61b8e8SPoul-Henning Kamp sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 198*c62aa65bSWarner Losh sc->pps[i].driver_abi = PPS_ABI_VERSION; 199*c62aa65bSWarner Losh sc->pps[i].driver_mtx = ppb_get_lock(ppbus); 200d7e53105SWarner Losh d->si_drv1 = sc; 201a885bb60SJohn Baldwin d->si_drv2 = (void *)(intptr_t)i; 202*c62aa65bSWarner Losh pps_init_abi(&sc->pps[i]); 203bd61b8e8SPoul-Henning Kamp } 2042067d312SJohn Baldwin ppb_lock(ppbus); 205bd61b8e8SPoul-Henning Kamp } while (0); 206bd61b8e8SPoul-Henning Kamp i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 207d7e53105SWarner Losh ppb_release_bus(ppbus, dev); 2082067d312SJohn Baldwin ppb_unlock(ppbus); 2090f210c92SNicolas Souchu 2100f210c92SNicolas Souchu return (0); 211507e2e44SPoul-Henning Kamp } 212507e2e44SPoul-Henning Kamp 213507e2e44SPoul-Henning Kamp static int 21489c9c53dSPoul-Henning Kamp ppsopen(struct cdev *dev, int flags, int fmt, struct thread *td) 215507e2e44SPoul-Henning Kamp { 216bd61b8e8SPoul-Henning Kamp struct pps_data *sc = dev->si_drv1; 2172067d312SJohn Baldwin device_t ppbus = sc->ppbus; 218a885bb60SJohn Baldwin int subdev = (intptr_t)dev->si_drv2; 2192067d312SJohn Baldwin int i; 220507e2e44SPoul-Henning Kamp 2212067d312SJohn Baldwin /* 2222067d312SJohn Baldwin * The sx lock is here solely to serialize open()'s to close 2232067d312SJohn Baldwin * the race of concurrent open()'s when pps(4) doesn't own the 2242067d312SJohn Baldwin * ppbus. 2252067d312SJohn Baldwin */ 2262067d312SJohn Baldwin sx_xlock(&sc->lock); 2272067d312SJohn Baldwin ppb_lock(ppbus); 228bd61b8e8SPoul-Henning Kamp if (!sc->busy) { 229bd61b8e8SPoul-Henning Kamp device_t ppsdev = sc->ppsdev; 230bd61b8e8SPoul-Henning Kamp 2312067d312SJohn Baldwin if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) { 2322067d312SJohn Baldwin ppb_unlock(ppbus); 2332067d312SJohn Baldwin sx_xunlock(&sc->lock); 234507e2e44SPoul-Henning Kamp return (EINTR); 2350f210c92SNicolas Souchu } 2360f210c92SNicolas Souchu 237bd61b8e8SPoul-Henning Kamp i = ppb_set_mode(sc->ppbus, PPB_PS2); 238d7e53105SWarner Losh PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 239507e2e44SPoul-Henning Kamp 240bd61b8e8SPoul-Henning Kamp i = IRQENABLE | PCD | nINIT | SELECTIN; 241bd61b8e8SPoul-Henning Kamp ppb_wctr(ppbus, i); 242bd61b8e8SPoul-Henning Kamp } 243bd61b8e8SPoul-Henning Kamp if (subdev > 0 && !(sc->busy & ~1)) { 2442067d312SJohn Baldwin /* XXX: Timeout of 1? hz/100 instead perhaps? */ 2452067d312SJohn Baldwin callout_reset(&sc->timeout, 1, ppshcpoll, sc); 246bd61b8e8SPoul-Henning Kamp sc->lastdata = ppb_rdtr(sc->ppbus); 247bd61b8e8SPoul-Henning Kamp } 248bd61b8e8SPoul-Henning Kamp sc->busy |= (1 << subdev); 2492067d312SJohn Baldwin ppb_unlock(ppbus); 2502067d312SJohn Baldwin sx_xunlock(&sc->lock); 251507e2e44SPoul-Henning Kamp return(0); 252507e2e44SPoul-Henning Kamp } 253507e2e44SPoul-Henning Kamp 254507e2e44SPoul-Henning Kamp static int 25589c9c53dSPoul-Henning Kamp ppsclose(struct cdev *dev, int flags, int fmt, struct thread *td) 256507e2e44SPoul-Henning Kamp { 257bd61b8e8SPoul-Henning Kamp struct pps_data *sc = dev->si_drv1; 258a885bb60SJohn Baldwin int subdev = (intptr_t)dev->si_drv2; 259507e2e44SPoul-Henning Kamp 2602067d312SJohn Baldwin sx_xlock(&sc->lock); 261bd61b8e8SPoul-Henning Kamp sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ 2622067d312SJohn Baldwin ppb_lock(sc->ppbus); 263bd61b8e8SPoul-Henning Kamp sc->busy &= ~(1 << subdev); 264bd61b8e8SPoul-Henning Kamp if (subdev > 0 && !(sc->busy & ~1)) 2652067d312SJohn Baldwin callout_stop(&sc->timeout); 266bd61b8e8SPoul-Henning Kamp if (!sc->busy) { 267bd61b8e8SPoul-Henning Kamp device_t ppsdev = sc->ppsdev; 268bd61b8e8SPoul-Henning Kamp device_t ppbus = sc->ppbus; 26920240fa3SNicolas Souchu 2700f210c92SNicolas Souchu ppb_wdtr(ppbus, 0); 2710f210c92SNicolas Souchu ppb_wctr(ppbus, 0); 27220240fa3SNicolas Souchu 273bd61b8e8SPoul-Henning Kamp ppb_set_mode(ppbus, PPB_COMPATIBLE); 2740f210c92SNicolas Souchu ppb_release_bus(ppbus, ppsdev); 275bd61b8e8SPoul-Henning Kamp } 2762067d312SJohn Baldwin ppb_unlock(sc->ppbus); 2772067d312SJohn Baldwin sx_xunlock(&sc->lock); 278507e2e44SPoul-Henning Kamp return(0); 279507e2e44SPoul-Henning Kamp } 280507e2e44SPoul-Henning Kamp 281507e2e44SPoul-Henning Kamp static void 282bd61b8e8SPoul-Henning Kamp ppshcpoll(void *arg) 283bd61b8e8SPoul-Henning Kamp { 284bd61b8e8SPoul-Henning Kamp struct pps_data *sc = arg; 285bd61b8e8SPoul-Henning Kamp int i, j, k, l; 286bd61b8e8SPoul-Henning Kamp 2872067d312SJohn Baldwin KASSERT(sc->busy & ~1, ("pps polling w/o opened devices")); 288bd61b8e8SPoul-Henning Kamp i = ppb_rdtr(sc->ppbus); 289bd61b8e8SPoul-Henning Kamp if (i == sc->lastdata) 290bd61b8e8SPoul-Henning Kamp return; 291bd61b8e8SPoul-Henning Kamp l = sc->lastdata ^ i; 292bd61b8e8SPoul-Henning Kamp k = 1; 293bd61b8e8SPoul-Henning Kamp for (j = 1; j < 9; j ++) { 2947bf758bfSPoul-Henning Kamp if (l & k) { 2957bf758bfSPoul-Henning Kamp pps_capture(&sc->pps[j]); 2967bf758bfSPoul-Henning Kamp pps_event(&sc->pps[j], 2977bf758bfSPoul-Henning Kamp i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 2987bf758bfSPoul-Henning Kamp } 299bd61b8e8SPoul-Henning Kamp k += k; 300bd61b8e8SPoul-Henning Kamp } 301bd61b8e8SPoul-Henning Kamp sc->lastdata = i; 3022067d312SJohn Baldwin callout_reset(&sc->timeout, 1, ppshcpoll, sc); 303bd61b8e8SPoul-Henning Kamp } 304bd61b8e8SPoul-Henning Kamp 3052067d312SJohn Baldwin static void 3060f210c92SNicolas Souchu ppsintr(void *arg) 307507e2e44SPoul-Henning Kamp { 3083aabc159SWarner Losh struct pps_data *sc = (struct pps_data *)arg; 309507e2e44SPoul-Henning Kamp 3102067d312SJohn Baldwin ppb_assert_locked(sc->ppbus); 3117bf758bfSPoul-Henning Kamp pps_capture(&sc->pps[0]); 3123aabc159SWarner Losh if (!(ppb_rstr(sc->ppbus) & nACK)) 3132067d312SJohn Baldwin return; 3142067d312SJohn Baldwin 315bd61b8e8SPoul-Henning Kamp if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 3163aabc159SWarner Losh ppb_wctr(sc->ppbus, IRQENABLE | AUTOFEED); 3177bf758bfSPoul-Henning Kamp pps_event(&sc->pps[0], PPS_CAPTUREASSERT); 3183aabc159SWarner Losh if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 3193aabc159SWarner Losh ppb_wctr(sc->ppbus, IRQENABLE); 320507e2e44SPoul-Henning Kamp } 321507e2e44SPoul-Henning Kamp 322507e2e44SPoul-Henning Kamp static int 32389c9c53dSPoul-Henning Kamp ppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) 324507e2e44SPoul-Henning Kamp { 325bd61b8e8SPoul-Henning Kamp struct pps_data *sc = dev->si_drv1; 326a885bb60SJohn Baldwin int subdev = (intptr_t)dev->si_drv2; 32776061701SWarner Losh int err; 328507e2e44SPoul-Henning Kamp 3292067d312SJohn Baldwin ppb_lock(sc->ppbus); 33076061701SWarner Losh err = pps_ioctl(cmd, data, &sc->pps[subdev]); 3312067d312SJohn Baldwin ppb_unlock(sc->ppbus); 33276061701SWarner Losh return (err); 3338afeddf0SPoul-Henning Kamp } 334507e2e44SPoul-Henning Kamp 3350f063508SPeter Wemm static device_method_t pps_methods[] = { 3360f063508SPeter Wemm /* device interface */ 3370f063508SPeter Wemm DEVMETHOD(device_identify, ppsidentify), 3380f063508SPeter Wemm DEVMETHOD(device_probe, ppsprobe), 3390f063508SPeter Wemm DEVMETHOD(device_attach, ppsattach), 3400f063508SPeter Wemm { 0, 0 } 3410f063508SPeter Wemm }; 3420f063508SPeter Wemm 3430f063508SPeter Wemm static driver_t pps_driver = { 3440f063508SPeter Wemm PPS_NAME, 3450f063508SPeter Wemm pps_methods, 3460f063508SPeter Wemm sizeof(struct pps_data), 3470f063508SPeter Wemm }; 3480f210c92SNicolas Souchu DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 349f5fd5611SRuslan Ermilov MODULE_DEPEND(pps, ppbus, 1, 1, 1); 350