pps.c revision 88220
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 88220 2001-12-19 19:37:31Z imp $ 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> 2458377Sphk#include <sys/timetc.h> 2536739Sphk#include <sys/timepps.h> 2655939Snsouch#include <machine/bus.h> 2755939Snsouch#include <machine/resource.h> 2855939Snsouch#include <sys/rman.h> 2933323Sphk 3033323Sphk#include <dev/ppbus/ppbconf.h> 3155939Snsouch#include "ppbus_if.h" 3255939Snsouch#include <dev/ppbus/ppbio.h> 3333323Sphk 3449550Sphk#define PPS_NAME "pps" /* our official name */ 3533323Sphk 3688220Simp#define PRVERBOSE(arg...) if (bootverbose) printf(##arg); 3788220Simp 3849550Sphkstruct pps_data { 3933396Sphk struct ppb_device pps_dev; 4083818Sphk struct pps_state pps[9]; 4183818Sphk dev_t devs[9]; 4283818Sphk device_t ppsdev; 4383818Sphk device_t ppbus; 4483818Sphk int busy; 4583818Sphk struct callout_handle timeout; 4683818Sphk int lastdata; 4755939Snsouch 4855939Snsouch struct resource *intr_resource; /* interrupt resource */ 4955939Snsouch void *intr_cookie; /* interrupt registration cookie */ 5049550Sphk}; 5133323Sphk 5255939Snsouchstatic void ppsintr(void *arg); 5383818Sphkstatic void ppshcpoll(void *arg); 5433323Sphk 5555939Snsouch#define DEVTOSOFTC(dev) \ 5655939Snsouch ((struct pps_data *)device_get_softc(dev)) 5733323Sphk 5855939Snsouchstatic devclass_t pps_devclass; 5933323Sphk 6033396Sphkstatic d_open_t ppsopen; 6133396Sphkstatic d_close_t ppsclose; 6236739Sphkstatic d_ioctl_t ppsioctl; 6333323Sphk 6433323Sphk#define CDEV_MAJOR 89 6547625Sphkstatic struct cdevsw pps_cdevsw = { 6647625Sphk /* open */ ppsopen, 6747625Sphk /* close */ ppsclose, 6847625Sphk /* read */ noread, 6947625Sphk /* write */ nowrite, 7047625Sphk /* ioctl */ ppsioctl, 7147625Sphk /* poll */ nopoll, 7247625Sphk /* mmap */ nommap, 7347625Sphk /* strategy */ nostrategy, 7447625Sphk /* name */ PPS_NAME, 7547625Sphk /* maj */ CDEV_MAJOR, 7647625Sphk /* dump */ nodump, 7747625Sphk /* psize */ nopsize, 7847625Sphk /* flags */ 0, 7947625Sphk}; 8033323Sphk 8156455Speterstatic void 8256455Speterppsidentify(driver_t *driver, device_t parent) 8356455Speter{ 8456455Speter 8556455Speter BUS_ADD_CHILD(parent, 0, PPS_NAME, 0); 8656455Speter} 8756455Speter 8855939Snsouchstatic int 8983818Sphkppstry(device_t ppbus, int send, int expect) 9083818Sphk{ 9183818Sphk int i; 9283818Sphk 9383818Sphk ppb_wdtr(ppbus, send); 9483818Sphk i = ppb_rdtr(ppbus); 9588220Simp PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i); 9683818Sphk return (i != expect); 9783818Sphk} 9883818Sphk 9983818Sphkstatic int 10055939Snsouchppsprobe(device_t ppsdev) 10133323Sphk{ 10288220Simp device_set_desc(ppsdev, "Pulse per second Timing Interface"); 10388220Simp 10488220Simp return (0); 10588220Simp} 10688220Simp 10788220Simpstatic int 10888220Simpppsattach(device_t dev) 10988220Simp{ 11088220Simp struct pps_data *sc = DEVTOSOFTC(dev); 11188220Simp device_t ppbus = device_get_parent(dev); 11288220Simp int irq, zero = 0; 11388220Simp dev_t d; 11483818Sphk int unit, i; 11533323Sphk 11688220Simp bzero(sc, sizeof(struct pps_data)); /* XXX doesn't newbus do this? */ 11783818Sphk 11888220Simp /* retrieve the ppbus irq */ 11988220Simp BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq); 12088220Simp 12188220Simp if (irq > 0) { 12288220Simp /* declare our interrupt handler */ 12388220Simp sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ, 12488220Simp &zero, irq, irq, 1, RF_SHAREABLE); 12588220Simp } 12688220Simp /* interrupts seem mandatory */ 12788220Simp if (sc->intr_resource == NULL) 12888220Simp return (ENXIO); 12988220Simp 13088220Simp sc->ppsdev = dev; 13188220Simp sc->ppbus = ppbus; 13283818Sphk unit = device_get_unit(ppbus); 13388220Simp d = make_dev(&pps_cdevsw, unit, 13455939Snsouch UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%d", unit); 13588220Simp sc->devs[0] = d; 13683818Sphk sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 13788220Simp d->si_drv1 = sc; 13888220Simp d->si_drv2 = (void*)0; 13983818Sphk pps_init(&sc->pps[0]); 14033323Sphk 14188220Simp if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) 14283818Sphk return (0); 14333323Sphk 14483818Sphk do { 14583818Sphk i = ppb_set_mode(sc->ppbus, PPB_EPP); 14688220Simp PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 14783818Sphk if (i == -1) 14883818Sphk break; 14983818Sphk i = 0; 15083818Sphk ppb_wctr(ppbus, i); 15183818Sphk if (ppstry(ppbus, 0x00, 0x00)) 15283818Sphk break; 15383818Sphk if (ppstry(ppbus, 0x55, 0x55)) 15483818Sphk break; 15583818Sphk if (ppstry(ppbus, 0xaa, 0xaa)) 15683818Sphk break; 15783818Sphk if (ppstry(ppbus, 0xff, 0xff)) 15883818Sphk break; 15983818Sphk 16083818Sphk i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; 16183818Sphk ppb_wctr(ppbus, i); 16288220Simp PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 16383818Sphk if (ppstry(ppbus, 0x00, 0x00)) 16483818Sphk break; 16583818Sphk if (ppstry(ppbus, 0x55, 0x00)) 16683818Sphk break; 16783818Sphk if (ppstry(ppbus, 0xaa, 0x00)) 16883818Sphk break; 16983818Sphk if (ppstry(ppbus, 0xff, 0x00)) 17083818Sphk break; 17183818Sphk 17283818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 17383818Sphk ppb_wctr(ppbus, i); 17488220Simp PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 17583818Sphk ppstry(ppbus, 0x00, 0xff); 17683818Sphk ppstry(ppbus, 0x55, 0xff); 17783818Sphk ppstry(ppbus, 0xaa, 0xff); 17883818Sphk ppstry(ppbus, 0xff, 0xff); 17983818Sphk 18083818Sphk for (i = 1; i < 9; i++) { 18188220Simp d = make_dev(&pps_cdevsw, unit + 0x10000 * i, 18288220Simp UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%db%d", unit, i - 1); 18388220Simp sc->devs[i] = d; 18483818Sphk sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 18588220Simp d->si_drv1 = sc; 18688220Simp d->si_drv2 = (void*)i; 18783818Sphk pps_init(&sc->pps[i]); 18883818Sphk } 18983818Sphk } while (0); 19083818Sphk i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 19188220Simp ppb_release_bus(ppbus, dev); 19233323Sphk 19355939Snsouch return (0); 19433323Sphk} 19533323Sphk 19633323Sphkstatic int 19783366Sjulianppsopen(dev_t dev, int flags, int fmt, struct thread *td) 19833323Sphk{ 19983818Sphk struct pps_data *sc = dev->si_drv1; 20083818Sphk int subdev = (int)dev->si_drv2; 20183818Sphk int error, i; 20233323Sphk 20383818Sphk if (!sc->busy) { 20483818Sphk device_t ppsdev = sc->ppsdev; 20583818Sphk device_t ppbus = sc->ppbus; 20683818Sphk 20755939Snsouch if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) 20846053Sphk return (EINTR); 20933323Sphk 21055939Snsouch /* attach the interrupt handler */ 21155939Snsouch if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource, 21255939Snsouch INTR_TYPE_TTY, ppsintr, ppsdev, 21355939Snsouch &sc->intr_cookie))) { 21455939Snsouch ppb_release_bus(ppbus, ppsdev); 21555939Snsouch return (error); 21655939Snsouch } 21755939Snsouch 21883818Sphk i = ppb_set_mode(sc->ppbus, PPB_PS2); 21988220Simp PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 22083818Sphk 22183818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 22283818Sphk ppb_wctr(ppbus, i); 22383818Sphk } 22483818Sphk if (subdev > 0 && !(sc->busy & ~1)) { 22583818Sphk sc->timeout = timeout(ppshcpoll, sc, 1); 22683818Sphk sc->lastdata = ppb_rdtr(sc->ppbus); 22746053Sphk } 22883818Sphk sc->busy |= (1 << subdev); 22933323Sphk return(0); 23033323Sphk} 23133323Sphk 23233323Sphkstatic int 23383366Sjulianppsclose(dev_t dev, int flags, int fmt, struct thread *td) 23433323Sphk{ 23583818Sphk struct pps_data *sc = dev->si_drv1; 23683818Sphk int subdev = (int)dev->si_drv2; 23733323Sphk 23883818Sphk sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ 23983818Sphk sc->busy &= ~(1 << subdev); 24083818Sphk if (subdev > 0 && !(sc->busy & ~1)) 24183818Sphk untimeout(ppshcpoll, sc, sc->timeout); 24283818Sphk if (!sc->busy) { 24383818Sphk device_t ppsdev = sc->ppsdev; 24483818Sphk device_t ppbus = sc->ppbus; 24543433Snsouch 24683818Sphk ppb_wdtr(ppbus, 0); 24783818Sphk ppb_wctr(ppbus, 0); 24843433Snsouch 24983818Sphk /* Note: the interrupt handler is automatically detached */ 25083818Sphk ppb_set_mode(ppbus, PPB_COMPATIBLE); 25183818Sphk ppb_release_bus(ppbus, ppsdev); 25283818Sphk } 25333323Sphk return(0); 25433323Sphk} 25533323Sphk 25633323Sphkstatic void 25783818Sphkppshcpoll(void *arg) 25883818Sphk{ 25983818Sphk struct pps_data *sc = arg; 26083818Sphk int i, j, k, l; 26183818Sphk struct timecounter *tc; 26283818Sphk unsigned count; 26383818Sphk 26483818Sphk if (!(sc->busy & ~1)) 26583818Sphk return; 26683818Sphk sc->timeout = timeout(ppshcpoll, sc, 1); 26783818Sphk i = ppb_rdtr(sc->ppbus); 26883818Sphk if (i == sc->lastdata) 26983818Sphk return; 27083818Sphk tc = timecounter; 27183818Sphk count = timecounter->tc_get_timecount(tc); 27283818Sphk l = sc->lastdata ^ i; 27383818Sphk k = 1; 27483818Sphk for (j = 1; j < 9; j ++) { 27583818Sphk if (l & k) 27683818Sphk pps_event(&sc->pps[j], tc, count, 27783818Sphk i & k ? 27883818Sphk PPS_CAPTUREASSERT : PPS_CAPTURECLEAR 27983818Sphk ); 28083818Sphk k += k; 28183818Sphk } 28283818Sphk sc->lastdata = i; 28383818Sphk} 28483818Sphk 28583818Sphkstatic void 28655939Snsouchppsintr(void *arg) 28733323Sphk{ 28855939Snsouch device_t ppsdev = (device_t)arg; 28955939Snsouch struct pps_data *sc = DEVTOSOFTC(ppsdev); 29083818Sphk device_t ppbus = sc->ppbus; 29144666Sphk struct timecounter *tc; 29244666Sphk unsigned count; 29333323Sphk 29444666Sphk tc = timecounter; 29544666Sphk count = timecounter->tc_get_timecount(tc); 29655939Snsouch if (!(ppb_rstr(ppbus) & nACK)) 29733323Sphk return; 29883818Sphk if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 29955939Snsouch ppb_wctr(ppbus, IRQENABLE | AUTOFEED); 30083818Sphk pps_event(&sc->pps[0], tc, count, PPS_CAPTUREASSERT); 30183818Sphk if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 30255939Snsouch ppb_wctr(ppbus, IRQENABLE); 30333323Sphk} 30433323Sphk 30536739Sphkstatic int 30683366Sjulianppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td) 30733323Sphk{ 30883818Sphk struct pps_data *sc = dev->si_drv1; 30983818Sphk int subdev = (int)dev->si_drv2; 31033323Sphk 31183818Sphk return (pps_ioctl(cmd, data, &sc->pps[subdev])); 31233323Sphk} 31333323Sphk 31456455Speterstatic device_method_t pps_methods[] = { 31556455Speter /* device interface */ 31656455Speter DEVMETHOD(device_identify, ppsidentify), 31756455Speter DEVMETHOD(device_probe, ppsprobe), 31856455Speter DEVMETHOD(device_attach, ppsattach), 31956455Speter 32056455Speter { 0, 0 } 32156455Speter}; 32256455Speter 32356455Speterstatic driver_t pps_driver = { 32456455Speter PPS_NAME, 32556455Speter pps_methods, 32656455Speter sizeof(struct pps_data), 32756455Speter}; 32855939SnsouchDRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 329