pps.c revision 108322
133323Sphk/* 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 * 950477Speter * $FreeBSD: head/sys/dev/ppbus/pps.c 108322 2002-12-27 16:34:12Z rwatson $ 1033323Sphk * 1136938Sphk * This driver implements a draft-mogul-pps-api-02.txt PPS source. 1236938Sphk * 1336938Sphk * The input pin is pin#10 1436938Sphk * The echo output pin is pin#14 1536938Sphk * 1633323Sphk */ 1733323Sphk 1833323Sphk#include <sys/param.h> 1933323Sphk#include <sys/kernel.h> 2033323Sphk#include <sys/systm.h> 2155939Snsouch#include <sys/module.h> 2255939Snsouch#include <sys/bus.h> 2333323Sphk#include <sys/conf.h> 2436739Sphk#include <sys/timepps.h> 2555939Snsouch#include <machine/bus.h> 2655939Snsouch#include <machine/resource.h> 2755939Snsouch#include <sys/rman.h> 2833323Sphk 2933323Sphk#include <dev/ppbus/ppbconf.h> 3055939Snsouch#include "ppbus_if.h" 3155939Snsouch#include <dev/ppbus/ppbio.h> 3233323Sphk 3349550Sphk#define PPS_NAME "pps" /* our official name */ 3433323Sphk 3597228Speter#define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg); 3688220Simp 3749550Sphkstruct pps_data { 3833396Sphk struct ppb_device pps_dev; 3983818Sphk struct pps_state pps[9]; 4083818Sphk dev_t devs[9]; 4183818Sphk device_t ppsdev; 4283818Sphk device_t ppbus; 4383818Sphk int busy; 4483818Sphk struct callout_handle timeout; 4583818Sphk int lastdata; 4655939Snsouch 4755939Snsouch struct resource *intr_resource; /* interrupt resource */ 4855939Snsouch void *intr_cookie; /* interrupt registration cookie */ 4949550Sphk}; 5033323Sphk 5155939Snsouchstatic void ppsintr(void *arg); 5283818Sphkstatic void ppshcpoll(void *arg); 5333323Sphk 5455939Snsouch#define DEVTOSOFTC(dev) \ 5555939Snsouch ((struct pps_data *)device_get_softc(dev)) 5633323Sphk 5755939Snsouchstatic devclass_t pps_devclass; 5833323Sphk 5933396Sphkstatic d_open_t ppsopen; 6033396Sphkstatic d_close_t ppsclose; 6136739Sphkstatic d_ioctl_t ppsioctl; 6233323Sphk 6333323Sphk#define CDEV_MAJOR 89 6447625Sphkstatic struct cdevsw pps_cdevsw = { 6547625Sphk /* open */ ppsopen, 6647625Sphk /* close */ ppsclose, 6747625Sphk /* read */ noread, 6847625Sphk /* write */ nowrite, 6947625Sphk /* ioctl */ ppsioctl, 7047625Sphk /* poll */ nopoll, 7147625Sphk /* mmap */ nommap, 7247625Sphk /* strategy */ nostrategy, 7347625Sphk /* name */ PPS_NAME, 7447625Sphk /* maj */ CDEV_MAJOR, 7547625Sphk /* dump */ nodump, 7647625Sphk /* psize */ nopsize, 7747625Sphk /* flags */ 0, 7847625Sphk}; 7933323Sphk 8056455Speterstatic void 8156455Speterppsidentify(driver_t *driver, device_t parent) 8256455Speter{ 8356455Speter 8494154Sticso 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); 11188220Simp dev_t d; 112106563Sjhb intptr_t irq; 113106563Sjhb int i, unit, zero = 0; 11433323Sphk 11588220Simp bzero(sc, sizeof(struct pps_data)); /* XXX doesn't newbus do this? */ 11683818Sphk 11788220Simp /* retrieve the ppbus irq */ 11888220Simp BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq); 11988220Simp 12088220Simp if (irq > 0) { 12188220Simp /* declare our interrupt handler */ 12288220Simp sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ, 12388220Simp &zero, irq, irq, 1, RF_SHAREABLE); 12488220Simp } 12588220Simp /* interrupts seem mandatory */ 12688220Simp if (sc->intr_resource == NULL) 12788220Simp return (ENXIO); 12888220Simp 12988220Simp sc->ppsdev = dev; 13088220Simp sc->ppbus = ppbus; 13183818Sphk unit = device_get_unit(ppbus); 13288220Simp d = make_dev(&pps_cdevsw, unit, 133108322Srwatson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); 13488220Simp sc->devs[0] = d; 13583818Sphk sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 13688220Simp d->si_drv1 = sc; 13788220Simp d->si_drv2 = (void*)0; 13883818Sphk pps_init(&sc->pps[0]); 13933323Sphk 14088220Simp if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) 14183818Sphk return (0); 14233323Sphk 14383818Sphk do { 14483818Sphk i = ppb_set_mode(sc->ppbus, PPB_EPP); 14588220Simp PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 14683818Sphk if (i == -1) 14783818Sphk break; 14883818Sphk i = 0; 14983818Sphk ppb_wctr(ppbus, i); 15083818Sphk if (ppstry(ppbus, 0x00, 0x00)) 15183818Sphk break; 15283818Sphk if (ppstry(ppbus, 0x55, 0x55)) 15383818Sphk break; 15483818Sphk if (ppstry(ppbus, 0xaa, 0xaa)) 15583818Sphk break; 15683818Sphk if (ppstry(ppbus, 0xff, 0xff)) 15783818Sphk break; 15883818Sphk 15983818Sphk i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; 16083818Sphk ppb_wctr(ppbus, i); 16188220Simp PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 16283818Sphk if (ppstry(ppbus, 0x00, 0x00)) 16383818Sphk break; 16483818Sphk if (ppstry(ppbus, 0x55, 0x00)) 16583818Sphk break; 16683818Sphk if (ppstry(ppbus, 0xaa, 0x00)) 16783818Sphk break; 16883818Sphk if (ppstry(ppbus, 0xff, 0x00)) 16983818Sphk break; 17083818Sphk 17183818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 17283818Sphk ppb_wctr(ppbus, i); 17388220Simp PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 17483818Sphk ppstry(ppbus, 0x00, 0xff); 17583818Sphk ppstry(ppbus, 0x55, 0xff); 17683818Sphk ppstry(ppbus, 0xaa, 0xff); 17783818Sphk ppstry(ppbus, 0xff, 0xff); 17883818Sphk 17983818Sphk for (i = 1; i < 9; i++) { 18088220Simp d = make_dev(&pps_cdevsw, unit + 0x10000 * i, 181108322Srwatson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); 18288220Simp sc->devs[i] = d; 18383818Sphk sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 18488220Simp d->si_drv1 = sc; 185106563Sjhb d->si_drv2 = (void *)(intptr_t)i; 18683818Sphk pps_init(&sc->pps[i]); 18783818Sphk } 18883818Sphk } while (0); 18983818Sphk i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 19088220Simp ppb_release_bus(ppbus, dev); 19133323Sphk 19255939Snsouch return (0); 19333323Sphk} 19433323Sphk 19533323Sphkstatic int 19683366Sjulianppsopen(dev_t dev, int flags, int fmt, struct thread *td) 19733323Sphk{ 19883818Sphk struct pps_data *sc = dev->si_drv1; 199106563Sjhb int subdev = (intptr_t)dev->si_drv2; 20083818Sphk int error, i; 20133323Sphk 20283818Sphk if (!sc->busy) { 20383818Sphk device_t ppsdev = sc->ppsdev; 20483818Sphk device_t ppbus = sc->ppbus; 20583818Sphk 20655939Snsouch if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) 20746053Sphk return (EINTR); 20833323Sphk 20955939Snsouch /* attach the interrupt handler */ 21055939Snsouch if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource, 21155939Snsouch INTR_TYPE_TTY, ppsintr, ppsdev, 21255939Snsouch &sc->intr_cookie))) { 21355939Snsouch ppb_release_bus(ppbus, ppsdev); 21455939Snsouch return (error); 21555939Snsouch } 21655939Snsouch 21783818Sphk i = ppb_set_mode(sc->ppbus, PPB_PS2); 21888220Simp PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 21983818Sphk 22083818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 22183818Sphk ppb_wctr(ppbus, i); 22283818Sphk } 22383818Sphk if (subdev > 0 && !(sc->busy & ~1)) { 22483818Sphk sc->timeout = timeout(ppshcpoll, sc, 1); 22583818Sphk sc->lastdata = ppb_rdtr(sc->ppbus); 22646053Sphk } 22783818Sphk sc->busy |= (1 << subdev); 22833323Sphk return(0); 22933323Sphk} 23033323Sphk 23133323Sphkstatic int 23283366Sjulianppsclose(dev_t dev, int flags, int fmt, struct thread *td) 23333323Sphk{ 23483818Sphk struct pps_data *sc = dev->si_drv1; 235106563Sjhb int subdev = (intptr_t)dev->si_drv2; 23633323Sphk 23783818Sphk sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ 23883818Sphk sc->busy &= ~(1 << subdev); 23983818Sphk if (subdev > 0 && !(sc->busy & ~1)) 24083818Sphk untimeout(ppshcpoll, sc, sc->timeout); 24183818Sphk if (!sc->busy) { 24283818Sphk device_t ppsdev = sc->ppsdev; 24383818Sphk device_t ppbus = sc->ppbus; 24443433Snsouch 24583818Sphk ppb_wdtr(ppbus, 0); 24683818Sphk ppb_wctr(ppbus, 0); 24743433Snsouch 24883818Sphk /* Note: the interrupt handler is automatically detached */ 24983818Sphk ppb_set_mode(ppbus, PPB_COMPATIBLE); 25083818Sphk ppb_release_bus(ppbus, ppsdev); 25183818Sphk } 25233323Sphk return(0); 25333323Sphk} 25433323Sphk 25533323Sphkstatic void 25683818Sphkppshcpoll(void *arg) 25783818Sphk{ 25883818Sphk struct pps_data *sc = arg; 25983818Sphk int i, j, k, l; 26083818Sphk 26183818Sphk if (!(sc->busy & ~1)) 26283818Sphk return; 26383818Sphk sc->timeout = timeout(ppshcpoll, sc, 1); 26483818Sphk i = ppb_rdtr(sc->ppbus); 26583818Sphk if (i == sc->lastdata) 26683818Sphk return; 26783818Sphk l = sc->lastdata ^ i; 26883818Sphk k = 1; 26983818Sphk for (j = 1; j < 9; j ++) { 27095523Sphk if (l & k) { 27195523Sphk pps_capture(&sc->pps[j]); 27295523Sphk pps_event(&sc->pps[j], 27395523Sphk i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 27495523Sphk } 27583818Sphk k += k; 27683818Sphk } 27783818Sphk sc->lastdata = i; 27883818Sphk} 27983818Sphk 28083818Sphkstatic void 28155939Snsouchppsintr(void *arg) 28233323Sphk{ 28355939Snsouch device_t ppsdev = (device_t)arg; 28455939Snsouch struct pps_data *sc = DEVTOSOFTC(ppsdev); 28583818Sphk device_t ppbus = sc->ppbus; 28633323Sphk 28795523Sphk pps_capture(&sc->pps[0]); 28855939Snsouch if (!(ppb_rstr(ppbus) & nACK)) 28933323Sphk return; 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); 29533323Sphk} 29633323Sphk 29736739Sphkstatic int 29883366Sjulianppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td) 29933323Sphk{ 30083818Sphk struct pps_data *sc = dev->si_drv1; 301106563Sjhb int subdev = (intptr_t)dev->si_drv2; 30233323Sphk 30383818Sphk return (pps_ioctl(cmd, data, &sc->pps[subdev])); 30433323Sphk} 30533323Sphk 30656455Speterstatic device_method_t pps_methods[] = { 30756455Speter /* device interface */ 30856455Speter DEVMETHOD(device_identify, ppsidentify), 30956455Speter DEVMETHOD(device_probe, ppsprobe), 31056455Speter DEVMETHOD(device_attach, ppsattach), 31156455Speter 31256455Speter { 0, 0 } 31356455Speter}; 31456455Speter 31556455Speterstatic driver_t pps_driver = { 31656455Speter PPS_NAME, 31756455Speter pps_methods, 31856455Speter sizeof(struct pps_data), 31956455Speter}; 32055939SnsouchDRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 321