pps.c revision 143398
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 * 1236938Sphk * 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: head/sys/dev/ppbus/pps.c 143398 2005-03-11 07:03:46Z imp $"); 19119418Sobrien 2033323Sphk#include <sys/param.h> 2133323Sphk#include <sys/kernel.h> 2233323Sphk#include <sys/systm.h> 2355939Snsouch#include <sys/module.h> 2455939Snsouch#include <sys/bus.h> 2533323Sphk#include <sys/conf.h> 2636739Sphk#include <sys/timepps.h> 2755939Snsouch#include <machine/bus.h> 2855939Snsouch#include <machine/resource.h> 2955939Snsouch#include <sys/rman.h> 3033323Sphk 3133323Sphk#include <dev/ppbus/ppbconf.h> 3255939Snsouch#include "ppbus_if.h" 3355939Snsouch#include <dev/ppbus/ppbio.h> 3433323Sphk 3549550Sphk#define PPS_NAME "pps" /* our official name */ 3633323Sphk 3797228Speter#define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg); 3888220Simp 3949550Sphkstruct pps_data { 40143390Simp struct ppb_device pps_dev; 4183818Sphk struct pps_state pps[9]; 42130585Sphk struct cdev *devs[9]; 4383818Sphk device_t ppsdev; 4483818Sphk device_t ppbus; 4583818Sphk int busy; 4683818Sphk struct callout_handle timeout; 4783818Sphk int lastdata; 4855939Snsouch 49143398Simp struct mtx mtx; 5055939Snsouch struct resource *intr_resource; /* interrupt resource */ 5155939Snsouch void *intr_cookie; /* interrupt registration cookie */ 5249550Sphk}; 5333323Sphk 5455939Snsouchstatic void ppsintr(void *arg); 5583818Sphkstatic void ppshcpoll(void *arg); 5633323Sphk 5755939Snsouch#define DEVTOSOFTC(dev) \ 5855939Snsouch ((struct pps_data *)device_get_softc(dev)) 5933323Sphk 6055939Snsouchstatic devclass_t pps_devclass; 6133323Sphk 6233396Sphkstatic d_open_t ppsopen; 6333396Sphkstatic d_close_t ppsclose; 6436739Sphkstatic d_ioctl_t ppsioctl; 6533323Sphk 6647625Sphkstatic struct cdevsw pps_cdevsw = { 67126080Sphk .d_version = D_VERSION, 68111815Sphk .d_open = ppsopen, 69111815Sphk .d_close = ppsclose, 70111815Sphk .d_ioctl = ppsioctl, 71111815Sphk .d_name = PPS_NAME, 7247625Sphk}; 7333323Sphk 7456455Speterstatic void 7556455Speterppsidentify(driver_t *driver, device_t parent) 7656455Speter{ 7756455Speter 78127189Sguido device_t dev; 79127189Sguido 80127189Sguido dev = device_find_child(parent, PPS_NAME, 0); 81127189Sguido if (!dev) 82127189Sguido BUS_ADD_CHILD(parent, 0, PPS_NAME, -1); 8356455Speter} 8456455Speter 8555939Snsouchstatic int 8683818Sphkppstry(device_t ppbus, int send, int expect) 8783818Sphk{ 8883818Sphk int i; 8983818Sphk 9083818Sphk ppb_wdtr(ppbus, send); 9183818Sphk i = ppb_rdtr(ppbus); 9288220Simp PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i); 9383818Sphk return (i != expect); 9483818Sphk} 9583818Sphk 9683818Sphkstatic int 9755939Snsouchppsprobe(device_t ppsdev) 9833323Sphk{ 9988220Simp device_set_desc(ppsdev, "Pulse per second Timing Interface"); 10088220Simp 10188220Simp return (0); 10288220Simp} 10388220Simp 10488220Simpstatic int 10588220Simpppsattach(device_t dev) 10688220Simp{ 10788220Simp struct pps_data *sc = DEVTOSOFTC(dev); 10888220Simp device_t ppbus = device_get_parent(dev); 109130585Sphk struct cdev *d; 110106563Sjhb intptr_t irq; 111106563Sjhb int i, unit, zero = 0; 11233323Sphk 113143398Simp mtx_init(&sc->mtx, device_get_nameunit(dev), "pps", MTX_SPIN); 11488220Simp /* retrieve the ppbus irq */ 11588220Simp BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq); 11688220Simp 11788220Simp if (irq > 0) { 11888220Simp /* declare our interrupt handler */ 11988220Simp sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ, 12088220Simp &zero, irq, irq, 1, RF_SHAREABLE); 12188220Simp } 12288220Simp /* interrupts seem mandatory */ 12388220Simp if (sc->intr_resource == NULL) 12488220Simp return (ENXIO); 12588220Simp 12688220Simp sc->ppsdev = dev; 12788220Simp sc->ppbus = ppbus; 12883818Sphk unit = device_get_unit(ppbus); 12988220Simp d = make_dev(&pps_cdevsw, unit, 130108322Srwatson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); 13188220Simp sc->devs[0] = d; 13283818Sphk sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 13388220Simp d->si_drv1 = sc; 13488220Simp d->si_drv2 = (void*)0; 13583818Sphk pps_init(&sc->pps[0]); 13633323Sphk 13788220Simp if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) 13883818Sphk return (0); 13933323Sphk 14083818Sphk do { 14183818Sphk i = ppb_set_mode(sc->ppbus, PPB_EPP); 14288220Simp PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 14383818Sphk if (i == -1) 14483818Sphk break; 14583818Sphk i = 0; 14683818Sphk ppb_wctr(ppbus, i); 14783818Sphk if (ppstry(ppbus, 0x00, 0x00)) 14883818Sphk break; 14983818Sphk if (ppstry(ppbus, 0x55, 0x55)) 15083818Sphk break; 15183818Sphk if (ppstry(ppbus, 0xaa, 0xaa)) 15283818Sphk break; 15383818Sphk if (ppstry(ppbus, 0xff, 0xff)) 15483818Sphk break; 15583818Sphk 15683818Sphk i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; 15783818Sphk ppb_wctr(ppbus, i); 15888220Simp PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 15983818Sphk if (ppstry(ppbus, 0x00, 0x00)) 16083818Sphk break; 16183818Sphk if (ppstry(ppbus, 0x55, 0x00)) 16283818Sphk break; 16383818Sphk if (ppstry(ppbus, 0xaa, 0x00)) 16483818Sphk break; 16583818Sphk if (ppstry(ppbus, 0xff, 0x00)) 16683818Sphk break; 16783818Sphk 16883818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 16983818Sphk ppb_wctr(ppbus, i); 17088220Simp PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 17183818Sphk ppstry(ppbus, 0x00, 0xff); 17283818Sphk ppstry(ppbus, 0x55, 0xff); 17383818Sphk ppstry(ppbus, 0xaa, 0xff); 17483818Sphk ppstry(ppbus, 0xff, 0xff); 17583818Sphk 17683818Sphk for (i = 1; i < 9; i++) { 17788220Simp d = make_dev(&pps_cdevsw, unit + 0x10000 * i, 178108322Srwatson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); 17988220Simp sc->devs[i] = d; 18083818Sphk sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 18188220Simp d->si_drv1 = sc; 182106563Sjhb d->si_drv2 = (void *)(intptr_t)i; 18383818Sphk pps_init(&sc->pps[i]); 18483818Sphk } 18583818Sphk } while (0); 18683818Sphk i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 18788220Simp ppb_release_bus(ppbus, dev); 18833323Sphk 18955939Snsouch return (0); 19033323Sphk} 19133323Sphk 19233323Sphkstatic int 193130585Sphkppsopen(struct cdev *dev, int flags, int fmt, struct thread *td) 19433323Sphk{ 19583818Sphk struct pps_data *sc = dev->si_drv1; 196106563Sjhb int subdev = (intptr_t)dev->si_drv2; 19783818Sphk int error, i; 19833323Sphk 19983818Sphk if (!sc->busy) { 20083818Sphk device_t ppsdev = sc->ppsdev; 20183818Sphk device_t ppbus = sc->ppbus; 20283818Sphk 20355939Snsouch if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) 20446053Sphk return (EINTR); 20533323Sphk 20655939Snsouch /* attach the interrupt handler */ 207143398Simp if ((error = bus_setup_intr(ppsdev, sc->intr_resource, 208143398Simp (INTR_TYPE_TTY | INTR_MPSAFE | INTR_FAST), ppsintr, 209143398Simp ppsdev, &sc->intr_cookie))) { 21055939Snsouch ppb_release_bus(ppbus, ppsdev); 21155939Snsouch return (error); 21255939Snsouch } 21355939Snsouch 21483818Sphk i = ppb_set_mode(sc->ppbus, PPB_PS2); 21588220Simp PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 21683818Sphk 21783818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 21883818Sphk ppb_wctr(ppbus, i); 219143390Simp } 22083818Sphk if (subdev > 0 && !(sc->busy & ~1)) { 22183818Sphk sc->timeout = timeout(ppshcpoll, sc, 1); 22283818Sphk sc->lastdata = ppb_rdtr(sc->ppbus); 22346053Sphk } 22483818Sphk sc->busy |= (1 << subdev); 22533323Sphk return(0); 22633323Sphk} 22733323Sphk 22833323Sphkstatic int 229130585Sphkppsclose(struct cdev *dev, int flags, int fmt, struct thread *td) 23033323Sphk{ 23183818Sphk struct pps_data *sc = dev->si_drv1; 232106563Sjhb int subdev = (intptr_t)dev->si_drv2; 23333323Sphk 23483818Sphk sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ 23583818Sphk sc->busy &= ~(1 << subdev); 236143390Simp if (subdev > 0 && !(sc->busy & ~1)) 23783818Sphk untimeout(ppshcpoll, sc, sc->timeout); 23883818Sphk if (!sc->busy) { 23983818Sphk device_t ppsdev = sc->ppsdev; 24083818Sphk device_t ppbus = sc->ppbus; 24143433Snsouch 24283818Sphk ppb_wdtr(ppbus, 0); 24383818Sphk ppb_wctr(ppbus, 0); 24443433Snsouch 24583818Sphk /* Note: the interrupt handler is automatically detached */ 24683818Sphk ppb_set_mode(ppbus, PPB_COMPATIBLE); 24783818Sphk ppb_release_bus(ppbus, ppsdev); 24883818Sphk } 24933323Sphk return(0); 25033323Sphk} 25133323Sphk 25233323Sphkstatic void 25383818Sphkppshcpoll(void *arg) 25483818Sphk{ 25583818Sphk struct pps_data *sc = arg; 25683818Sphk int i, j, k, l; 25783818Sphk 25883818Sphk if (!(sc->busy & ~1)) 25983818Sphk return; 26083818Sphk sc->timeout = timeout(ppshcpoll, sc, 1); 26183818Sphk i = ppb_rdtr(sc->ppbus); 26283818Sphk if (i == sc->lastdata) 26383818Sphk return; 26483818Sphk l = sc->lastdata ^ i; 26583818Sphk k = 1; 26683818Sphk for (j = 1; j < 9; j ++) { 26795523Sphk if (l & k) { 26895523Sphk pps_capture(&sc->pps[j]); 26995523Sphk pps_event(&sc->pps[j], 27095523Sphk i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 27195523Sphk } 27283818Sphk k += k; 27383818Sphk } 27483818Sphk sc->lastdata = i; 27583818Sphk} 27683818Sphk 27783818Sphkstatic void 27855939Snsouchppsintr(void *arg) 27933323Sphk{ 28055939Snsouch device_t ppsdev = (device_t)arg; 28155939Snsouch struct pps_data *sc = DEVTOSOFTC(ppsdev); 28283818Sphk device_t ppbus = sc->ppbus; 28333323Sphk 284143398Simp mtx_lock(&sc->mtx); 28595523Sphk pps_capture(&sc->pps[0]); 286143398Simp if (!(ppb_rstr(ppbus) & nACK)) { 287143398Simp mtx_unlock(&sc->mtx); 28833323Sphk return; 289143398Simp } 29083818Sphk if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 29155939Snsouch ppb_wctr(ppbus, IRQENABLE | AUTOFEED); 29295523Sphk pps_event(&sc->pps[0], PPS_CAPTUREASSERT); 29383818Sphk if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 29455939Snsouch ppb_wctr(ppbus, IRQENABLE); 295143398Simp mtx_unlock(&sc->mtx); 29633323Sphk} 29733323Sphk 29836739Sphkstatic int 299130585Sphkppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) 30033323Sphk{ 30183818Sphk struct pps_data *sc = dev->si_drv1; 302106563Sjhb int subdev = (intptr_t)dev->si_drv2; 303143398Simp int err; 30433323Sphk 305143398Simp mtx_lock(&sc->mtx); 306143398Simp err = pps_ioctl(cmd, data, &sc->pps[subdev]); 307143398Simp mtx_unlock(&sc->mtx); 308143398Simp return (err); 30933323Sphk} 31033323Sphk 31156455Speterstatic device_method_t pps_methods[] = { 31256455Speter /* device interface */ 31356455Speter DEVMETHOD(device_identify, ppsidentify), 31456455Speter DEVMETHOD(device_probe, ppsprobe), 31556455Speter DEVMETHOD(device_attach, ppsattach), 31656455Speter 31756455Speter { 0, 0 } 31856455Speter}; 31956455Speter 32056455Speterstatic driver_t pps_driver = { 32156455Speter PPS_NAME, 32256455Speter pps_methods, 32356455Speter sizeof(struct pps_data), 32456455Speter}; 32555939SnsouchDRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 326