1139749Simp/*- 233323Sphk * ---------------------------------------------------------------------------- 333323Sphk * "THE BEER-WARE LICENSE" (Revision 42): 433323Sphk * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 533323Sphk * can do whatever you want with this stuff. If we meet some day, and you think 633323Sphk * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 733323Sphk * ---------------------------------------------------------------------------- 833323Sphk * 933323Sphk * 1036938Sphk * This driver implements a draft-mogul-pps-api-02.txt PPS source. 1136938Sphk * 12185003Sjhb * The input pin is pin#10 1336938Sphk * The echo output pin is pin#14 1436938Sphk * 1533323Sphk */ 1633323Sphk 17119418Sobrien#include <sys/cdefs.h> 18119418Sobrien__FBSDID("$FreeBSD$"); 19119418Sobrien 2033323Sphk#include <sys/param.h> 21187576Sjhb#include <sys/lock.h> 2233323Sphk#include <sys/kernel.h> 2333323Sphk#include <sys/systm.h> 2455939Snsouch#include <sys/module.h> 25187576Sjhb#include <sys/sx.h> 2655939Snsouch#include <sys/bus.h> 2733323Sphk#include <sys/conf.h> 2836739Sphk#include <sys/timepps.h> 2955939Snsouch#include <machine/bus.h> 3055939Snsouch#include <machine/resource.h> 3155939Snsouch#include <sys/rman.h> 3233323Sphk 3333323Sphk#include <dev/ppbus/ppbconf.h> 3455939Snsouch#include "ppbus_if.h" 3555939Snsouch#include <dev/ppbus/ppbio.h> 3633323Sphk 3749550Sphk#define PPS_NAME "pps" /* our official name */ 3833323Sphk 3997228Speter#define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg); 4088220Simp 4149550Sphkstruct pps_data { 42143390Simp struct ppb_device pps_dev; 4383818Sphk struct pps_state pps[9]; 44130585Sphk struct cdev *devs[9]; 4583818Sphk device_t ppsdev; 4683818Sphk device_t ppbus; 4783818Sphk int busy; 48187576Sjhb struct callout timeout; 4983818Sphk int lastdata; 5055939Snsouch 51187576Sjhb struct sx lock; 5255939Snsouch struct resource *intr_resource; /* interrupt resource */ 5355939Snsouch void *intr_cookie; /* interrupt registration cookie */ 5449550Sphk}; 5533323Sphk 56187576Sjhbstatic void ppsintr(void *arg); 5783818Sphkstatic void ppshcpoll(void *arg); 5833323Sphk 5955939Snsouch#define DEVTOSOFTC(dev) \ 6055939Snsouch ((struct pps_data *)device_get_softc(dev)) 6133323Sphk 6255939Snsouchstatic devclass_t pps_devclass; 6333323Sphk 6433396Sphkstatic d_open_t ppsopen; 6533396Sphkstatic d_close_t ppsclose; 6636739Sphkstatic d_ioctl_t ppsioctl; 6733323Sphk 6847625Sphkstatic struct cdevsw pps_cdevsw = { 69126080Sphk .d_version = D_VERSION, 70111815Sphk .d_open = ppsopen, 71111815Sphk .d_close = ppsclose, 72111815Sphk .d_ioctl = ppsioctl, 73111815Sphk .d_name = PPS_NAME, 7447625Sphk}; 7533323Sphk 7656455Speterstatic void 7756455Speterppsidentify(driver_t *driver, device_t parent) 7856455Speter{ 7956455Speter 80127189Sguido device_t dev; 81127189Sguido 82184130Sjhb dev = device_find_child(parent, PPS_NAME, -1); 83127189Sguido if (!dev) 84127189Sguido BUS_ADD_CHILD(parent, 0, PPS_NAME, -1); 8556455Speter} 8656455Speter 8755939Snsouchstatic int 8883818Sphkppstry(device_t ppbus, int send, int expect) 8983818Sphk{ 9083818Sphk int i; 9183818Sphk 9283818Sphk ppb_wdtr(ppbus, send); 9383818Sphk i = ppb_rdtr(ppbus); 9488220Simp PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i); 9583818Sphk return (i != expect); 9683818Sphk} 9783818Sphk 9883818Sphkstatic int 9955939Snsouchppsprobe(device_t ppsdev) 10033323Sphk{ 10188220Simp device_set_desc(ppsdev, "Pulse per second Timing Interface"); 10288220Simp 10388220Simp return (0); 10488220Simp} 10588220Simp 10688220Simpstatic int 10788220Simpppsattach(device_t dev) 10888220Simp{ 10988220Simp struct pps_data *sc = DEVTOSOFTC(dev); 11088220Simp device_t ppbus = device_get_parent(dev); 111130585Sphk struct cdev *d; 112187576Sjhb int error, i, unit, rid = 0; 11333323Sphk 114183053Sjhb /* declare our interrupt handler */ 115183053Sjhb sc->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 116183053Sjhb RF_SHAREABLE); 117183053Sjhb 11888220Simp /* interrupts seem mandatory */ 119187576Sjhb if (sc->intr_resource == NULL) { 120187576Sjhb device_printf(dev, "Unable to allocate interrupt resource\n"); 12188220Simp return (ENXIO); 122187576Sjhb } 12388220Simp 124187576Sjhb error = bus_setup_intr(dev, sc->intr_resource, 125187576Sjhb INTR_TYPE_TTY | INTR_MPSAFE, NULL, ppsintr, 126187576Sjhb sc, &sc->intr_cookie); 127187576Sjhb if (error) { 128187576Sjhb bus_release_resource(dev, SYS_RES_IRQ, 0, sc->intr_resource); 129187576Sjhb device_printf(dev, "Unable to register interrupt handler\n"); 130187576Sjhb return (error); 131187576Sjhb } 132187576Sjhb 133187576Sjhb sx_init(&sc->lock, "pps"); 134187576Sjhb ppb_init_callout(ppbus, &sc->timeout, 0); 13588220Simp sc->ppsdev = dev; 13688220Simp sc->ppbus = ppbus; 13783818Sphk unit = device_get_unit(ppbus); 13888220Simp d = make_dev(&pps_cdevsw, unit, 139108322Srwatson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); 14088220Simp sc->devs[0] = d; 14183818Sphk sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 14288220Simp d->si_drv1 = sc; 14388220Simp d->si_drv2 = (void*)0; 14483818Sphk pps_init(&sc->pps[0]); 14533323Sphk 146187576Sjhb ppb_lock(ppbus); 147187576Sjhb if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 148187576Sjhb ppb_unlock(ppbus); 14983818Sphk return (0); 150187576Sjhb } 15133323Sphk 15283818Sphk do { 15383818Sphk i = ppb_set_mode(sc->ppbus, PPB_EPP); 15488220Simp PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 15583818Sphk if (i == -1) 15683818Sphk break; 15783818Sphk i = 0; 15883818Sphk ppb_wctr(ppbus, i); 15983818Sphk if (ppstry(ppbus, 0x00, 0x00)) 16083818Sphk break; 16183818Sphk if (ppstry(ppbus, 0x55, 0x55)) 16283818Sphk break; 16383818Sphk if (ppstry(ppbus, 0xaa, 0xaa)) 16483818Sphk break; 16583818Sphk if (ppstry(ppbus, 0xff, 0xff)) 16683818Sphk break; 16783818Sphk 16883818Sphk i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; 16983818Sphk ppb_wctr(ppbus, i); 17088220Simp PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 17183818Sphk if (ppstry(ppbus, 0x00, 0x00)) 17283818Sphk break; 17383818Sphk if (ppstry(ppbus, 0x55, 0x00)) 17483818Sphk break; 17583818Sphk if (ppstry(ppbus, 0xaa, 0x00)) 17683818Sphk break; 17783818Sphk if (ppstry(ppbus, 0xff, 0x00)) 17883818Sphk break; 17983818Sphk 18083818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 18183818Sphk ppb_wctr(ppbus, i); 18288220Simp PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 18383818Sphk ppstry(ppbus, 0x00, 0xff); 18483818Sphk ppstry(ppbus, 0x55, 0xff); 18583818Sphk ppstry(ppbus, 0xaa, 0xff); 18683818Sphk ppstry(ppbus, 0xff, 0xff); 187187576Sjhb ppb_unlock(ppbus); 18883818Sphk 18983818Sphk for (i = 1; i < 9; i++) { 19088220Simp d = make_dev(&pps_cdevsw, unit + 0x10000 * i, 191108322Srwatson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); 19288220Simp sc->devs[i] = d; 19383818Sphk sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 19488220Simp d->si_drv1 = sc; 195106563Sjhb d->si_drv2 = (void *)(intptr_t)i; 19683818Sphk pps_init(&sc->pps[i]); 19783818Sphk } 198187576Sjhb ppb_lock(ppbus); 19983818Sphk } while (0); 20083818Sphk i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 20188220Simp ppb_release_bus(ppbus, dev); 202187576Sjhb ppb_unlock(ppbus); 20333323Sphk 20455939Snsouch return (0); 20533323Sphk} 20633323Sphk 20733323Sphkstatic int 208130585Sphkppsopen(struct cdev *dev, int flags, int fmt, struct thread *td) 20933323Sphk{ 21083818Sphk struct pps_data *sc = dev->si_drv1; 211187576Sjhb device_t ppbus = sc->ppbus; 212106563Sjhb int subdev = (intptr_t)dev->si_drv2; 213187576Sjhb int i; 21433323Sphk 215187576Sjhb /* 216187576Sjhb * The sx lock is here solely to serialize open()'s to close 217187576Sjhb * the race of concurrent open()'s when pps(4) doesn't own the 218187576Sjhb * ppbus. 219187576Sjhb */ 220187576Sjhb sx_xlock(&sc->lock); 221187576Sjhb ppb_lock(ppbus); 22283818Sphk if (!sc->busy) { 22383818Sphk device_t ppsdev = sc->ppsdev; 22483818Sphk 225187576Sjhb if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) { 226187576Sjhb ppb_unlock(ppbus); 227187576Sjhb sx_xunlock(&sc->lock); 22846053Sphk return (EINTR); 22955939Snsouch } 23055939Snsouch 23183818Sphk i = ppb_set_mode(sc->ppbus, PPB_PS2); 23288220Simp PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 23383818Sphk 23483818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 23583818Sphk ppb_wctr(ppbus, i); 236143390Simp } 23783818Sphk if (subdev > 0 && !(sc->busy & ~1)) { 238187576Sjhb /* XXX: Timeout of 1? hz/100 instead perhaps? */ 239187576Sjhb callout_reset(&sc->timeout, 1, ppshcpoll, sc); 24083818Sphk sc->lastdata = ppb_rdtr(sc->ppbus); 24146053Sphk } 24283818Sphk sc->busy |= (1 << subdev); 243187576Sjhb ppb_unlock(ppbus); 244187576Sjhb sx_xunlock(&sc->lock); 24533323Sphk return(0); 24633323Sphk} 24733323Sphk 24833323Sphkstatic int 249130585Sphkppsclose(struct cdev *dev, int flags, int fmt, struct thread *td) 25033323Sphk{ 25183818Sphk struct pps_data *sc = dev->si_drv1; 252106563Sjhb int subdev = (intptr_t)dev->si_drv2; 25333323Sphk 254187576Sjhb sx_xlock(&sc->lock); 25583818Sphk sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ 256187576Sjhb ppb_lock(sc->ppbus); 25783818Sphk sc->busy &= ~(1 << subdev); 258143390Simp if (subdev > 0 && !(sc->busy & ~1)) 259187576Sjhb callout_stop(&sc->timeout); 26083818Sphk if (!sc->busy) { 26183818Sphk device_t ppsdev = sc->ppsdev; 26283818Sphk device_t ppbus = sc->ppbus; 26343433Snsouch 26483818Sphk ppb_wdtr(ppbus, 0); 26583818Sphk ppb_wctr(ppbus, 0); 26643433Snsouch 26783818Sphk ppb_set_mode(ppbus, PPB_COMPATIBLE); 26883818Sphk ppb_release_bus(ppbus, ppsdev); 26983818Sphk } 270187576Sjhb ppb_unlock(sc->ppbus); 271187576Sjhb sx_xunlock(&sc->lock); 27233323Sphk return(0); 27333323Sphk} 27433323Sphk 27533323Sphkstatic void 27683818Sphkppshcpoll(void *arg) 27783818Sphk{ 27883818Sphk struct pps_data *sc = arg; 27983818Sphk int i, j, k, l; 28083818Sphk 281187576Sjhb KASSERT(sc->busy & ~1, ("pps polling w/o opened devices")); 28283818Sphk i = ppb_rdtr(sc->ppbus); 283185003Sjhb if (i == sc->lastdata) 28483818Sphk return; 28583818Sphk l = sc->lastdata ^ i; 28683818Sphk k = 1; 28783818Sphk for (j = 1; j < 9; j ++) { 28895523Sphk if (l & k) { 28995523Sphk pps_capture(&sc->pps[j]); 29095523Sphk pps_event(&sc->pps[j], 29195523Sphk i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 29295523Sphk } 29383818Sphk k += k; 29483818Sphk } 29583818Sphk sc->lastdata = i; 296187576Sjhb callout_reset(&sc->timeout, 1, ppshcpoll, sc); 29783818Sphk} 29883818Sphk 299187576Sjhbstatic void 30055939Snsouchppsintr(void *arg) 30133323Sphk{ 302145075Simp struct pps_data *sc = (struct pps_data *)arg; 30333323Sphk 304187576Sjhb ppb_assert_locked(sc->ppbus); 30595523Sphk pps_capture(&sc->pps[0]); 306145075Simp if (!(ppb_rstr(sc->ppbus) & nACK)) 307187576Sjhb return; 308187576Sjhb 309185003Sjhb if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 310145075Simp ppb_wctr(sc->ppbus, IRQENABLE | AUTOFEED); 31195523Sphk pps_event(&sc->pps[0], PPS_CAPTUREASSERT); 312185003Sjhb if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 313145075Simp ppb_wctr(sc->ppbus, IRQENABLE); 31433323Sphk} 31533323Sphk 31636739Sphkstatic int 317130585Sphkppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) 31833323Sphk{ 31983818Sphk struct pps_data *sc = dev->si_drv1; 320106563Sjhb int subdev = (intptr_t)dev->si_drv2; 321143398Simp int err; 32233323Sphk 323187576Sjhb ppb_lock(sc->ppbus); 324143398Simp err = pps_ioctl(cmd, data, &sc->pps[subdev]); 325187576Sjhb ppb_unlock(sc->ppbus); 326143398Simp return (err); 32733323Sphk} 32833323Sphk 32956455Speterstatic device_method_t pps_methods[] = { 33056455Speter /* device interface */ 33156455Speter DEVMETHOD(device_identify, ppsidentify), 33256455Speter DEVMETHOD(device_probe, ppsprobe), 33356455Speter DEVMETHOD(device_attach, ppsattach), 33456455Speter 33556455Speter { 0, 0 } 33656455Speter}; 33756455Speter 33856455Speterstatic driver_t pps_driver = { 33956455Speter PPS_NAME, 34056455Speter pps_methods, 34156455Speter sizeof(struct pps_data), 34256455Speter}; 34355939SnsouchDRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 344153610SruMODULE_DEPEND(pps, ppbus, 1, 1, 1); 345