pps.c revision 83818
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 83818 2001-09-22 16:34:59Z phk $ 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 3649550Sphkstruct pps_data { 3733396Sphk struct ppb_device pps_dev; 3883818Sphk struct pps_state pps[9]; 3983818Sphk dev_t devs[9]; 4083818Sphk device_t ppsdev; 4183818Sphk device_t ppbus; 4283818Sphk int busy; 4383818Sphk struct callout_handle timeout; 4483818Sphk int lastdata; 4555939Snsouch 4655939Snsouch struct resource *intr_resource; /* interrupt resource */ 4755939Snsouch void *intr_cookie; /* interrupt registration cookie */ 4849550Sphk}; 4933323Sphk 5055939Snsouchstatic void ppsintr(void *arg); 5183818Sphkstatic void ppshcpoll(void *arg); 5233323Sphk 5355939Snsouch#define DEVTOSOFTC(dev) \ 5455939Snsouch ((struct pps_data *)device_get_softc(dev)) 5533323Sphk 5655939Snsouchstatic devclass_t pps_devclass; 5733323Sphk 5833396Sphkstatic d_open_t ppsopen; 5933396Sphkstatic d_close_t ppsclose; 6036739Sphkstatic d_ioctl_t ppsioctl; 6133323Sphk 6233323Sphk#define CDEV_MAJOR 89 6347625Sphkstatic struct cdevsw pps_cdevsw = { 6447625Sphk /* open */ ppsopen, 6547625Sphk /* close */ ppsclose, 6647625Sphk /* read */ noread, 6747625Sphk /* write */ nowrite, 6847625Sphk /* ioctl */ ppsioctl, 6947625Sphk /* poll */ nopoll, 7047625Sphk /* mmap */ nommap, 7147625Sphk /* strategy */ nostrategy, 7247625Sphk /* name */ PPS_NAME, 7347625Sphk /* maj */ CDEV_MAJOR, 7447625Sphk /* dump */ nodump, 7547625Sphk /* psize */ nopsize, 7647625Sphk /* flags */ 0, 7747625Sphk}; 7833323Sphk 7956455Speterstatic void 8056455Speterppsidentify(driver_t *driver, device_t parent) 8156455Speter{ 8256455Speter 8356455Speter BUS_ADD_CHILD(parent, 0, PPS_NAME, 0); 8456455Speter} 8556455Speter 8655939Snsouchstatic int 8783818Sphkppstry(device_t ppbus, int send, int expect) 8883818Sphk{ 8983818Sphk int i; 9083818Sphk 9183818Sphk ppb_wdtr(ppbus, send); 9283818Sphk i = ppb_rdtr(ppbus); 9383818Sphk printf("S: %02x E: %02x G: %02x\n", send, expect, i); 9483818Sphk return (i != expect); 9583818Sphk} 9683818Sphk 9783818Sphkstatic int 9855939Snsouchppsprobe(device_t ppsdev) 9933323Sphk{ 10033396Sphk struct pps_data *sc; 10149550Sphk dev_t dev; 10283818Sphk device_t ppbus; 10383818Sphk int unit, i; 10433323Sphk 10583818Sphk device_set_desc(ppsdev, "Pulse per second Timing Interface"); 10683818Sphk 10755939Snsouch sc = DEVTOSOFTC(ppsdev); 10833396Sphk bzero(sc, sizeof(struct pps_data)); 10983818Sphk sc->ppsdev = ppsdev; 11083818Sphk ppbus = sc->ppbus = device_get_parent(ppsdev); 11183818Sphk unit = device_get_unit(ppbus); 11255939Snsouch dev = make_dev(&pps_cdevsw, unit, 11355939Snsouch UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%d", unit); 11483818Sphk sc->devs[0] = dev; 11583818Sphk sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 11683818Sphk dev->si_drv1 = sc; 11783818Sphk dev->si_drv2 = (void*)0; 11883818Sphk pps_init(&sc->pps[0]); 11933323Sphk 12083818Sphk if (ppb_request_bus(ppbus, ppsdev, PPB_DONTWAIT)) 12183818Sphk return (0); 12233323Sphk 12383818Sphk 12483818Sphk do { 12583818Sphk i = ppb_set_mode(sc->ppbus, PPB_EPP); 12683818Sphk printf("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 12783818Sphk if (i == -1) 12883818Sphk break; 12983818Sphk i = 0; 13083818Sphk ppb_wctr(ppbus, i); 13183818Sphk if (ppstry(ppbus, 0x00, 0x00)) 13283818Sphk break; 13383818Sphk if (ppstry(ppbus, 0x55, 0x55)) 13483818Sphk break; 13583818Sphk if (ppstry(ppbus, 0xaa, 0xaa)) 13683818Sphk break; 13783818Sphk if (ppstry(ppbus, 0xff, 0xff)) 13883818Sphk break; 13983818Sphk 14083818Sphk i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; 14183818Sphk ppb_wctr(ppbus, i); 14283818Sphk printf("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 14383818Sphk if (ppstry(ppbus, 0x00, 0x00)) 14483818Sphk break; 14583818Sphk if (ppstry(ppbus, 0x55, 0x00)) 14683818Sphk break; 14783818Sphk if (ppstry(ppbus, 0xaa, 0x00)) 14883818Sphk break; 14983818Sphk if (ppstry(ppbus, 0xff, 0x00)) 15083818Sphk break; 15183818Sphk 15283818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 15383818Sphk ppb_wctr(ppbus, i); 15483818Sphk printf("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 15583818Sphk ppstry(ppbus, 0x00, 0xff); 15683818Sphk ppstry(ppbus, 0x55, 0xff); 15783818Sphk ppstry(ppbus, 0xaa, 0xff); 15883818Sphk ppstry(ppbus, 0xff, 0xff); 15983818Sphk 16083818Sphk for (i = 1; i < 9; i++) { 16183818Sphk dev = make_dev(&pps_cdevsw, unit + 0x10000 * i, 16283818Sphk UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%db%d", unit, i - 1); 16383818Sphk sc->devs[i] = dev; 16483818Sphk sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 16583818Sphk dev->si_drv1 = sc; 16683818Sphk dev->si_drv2 = (void*)i; 16783818Sphk pps_init(&sc->pps[i]); 16883818Sphk } 16983818Sphk } while (0); 17083818Sphk i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 17183818Sphk ppb_release_bus(ppbus, ppsdev); 17255939Snsouch return (0); 17333323Sphk} 17433323Sphk 17533323Sphkstatic int 17655939Snsouchppsattach(device_t dev) 17733323Sphk{ 17855939Snsouch struct pps_data *sc = DEVTOSOFTC(dev); 17983818Sphk device_t ppbus = sc->ppbus; 18055939Snsouch int irq, zero = 0; 18144666Sphk 18255939Snsouch /* retrieve the ppbus irq */ 18355939Snsouch BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq); 18433323Sphk 18555939Snsouch if (irq > 0) { 18655939Snsouch /* declare our interrupt handler */ 18755939Snsouch sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ, 18855939Snsouch &zero, irq, irq, 1, RF_SHAREABLE); 18955939Snsouch } 19055939Snsouch /* interrupts seem mandatory */ 19155939Snsouch if (sc->intr_resource == 0) 19255939Snsouch return (ENXIO); 19355939Snsouch 19455939Snsouch return (0); 19533323Sphk} 19633323Sphk 19733323Sphkstatic int 19883366Sjulianppsopen(dev_t dev, int flags, int fmt, struct thread *td) 19933323Sphk{ 20083818Sphk struct pps_data *sc = dev->si_drv1; 20183818Sphk int subdev = (int)dev->si_drv2; 20283818Sphk int error, i; 20333323Sphk 20483818Sphk if (!sc->busy) { 20583818Sphk device_t ppsdev = sc->ppsdev; 20683818Sphk device_t ppbus = sc->ppbus; 20783818Sphk 20855939Snsouch if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) 20946053Sphk return (EINTR); 21033323Sphk 21155939Snsouch /* attach the interrupt handler */ 21255939Snsouch if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource, 21355939Snsouch INTR_TYPE_TTY, ppsintr, ppsdev, 21455939Snsouch &sc->intr_cookie))) { 21555939Snsouch ppb_release_bus(ppbus, ppsdev); 21655939Snsouch return (error); 21755939Snsouch } 21855939Snsouch 21983818Sphk i = ppb_set_mode(sc->ppbus, PPB_PS2); 22083818Sphk printf("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 22183818Sphk 22283818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 22383818Sphk ppb_wctr(ppbus, i); 22483818Sphk } 22583818Sphk if (subdev > 0 && !(sc->busy & ~1)) { 22683818Sphk sc->timeout = timeout(ppshcpoll, sc, 1); 22783818Sphk sc->lastdata = ppb_rdtr(sc->ppbus); 22846053Sphk } 22983818Sphk sc->busy |= (1 << subdev); 23033323Sphk return(0); 23133323Sphk} 23233323Sphk 23333323Sphkstatic int 23483366Sjulianppsclose(dev_t dev, int flags, int fmt, struct thread *td) 23533323Sphk{ 23683818Sphk struct pps_data *sc = dev->si_drv1; 23783818Sphk int subdev = (int)dev->si_drv2; 23833323Sphk 23983818Sphk sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ 24083818Sphk sc->busy &= ~(1 << subdev); 24183818Sphk if (subdev > 0 && !(sc->busy & ~1)) 24283818Sphk untimeout(ppshcpoll, sc, sc->timeout); 24383818Sphk if (!sc->busy) { 24483818Sphk device_t ppsdev = sc->ppsdev; 24583818Sphk device_t ppbus = sc->ppbus; 24643433Snsouch 24783818Sphk ppb_wdtr(ppbus, 0); 24883818Sphk ppb_wctr(ppbus, 0); 24943433Snsouch 25083818Sphk /* Note: the interrupt handler is automatically detached */ 25183818Sphk ppb_set_mode(ppbus, PPB_COMPATIBLE); 25283818Sphk ppb_release_bus(ppbus, ppsdev); 25383818Sphk } 25433323Sphk return(0); 25533323Sphk} 25633323Sphk 25733323Sphkstatic void 25883818Sphkppshcpoll(void *arg) 25983818Sphk{ 26083818Sphk struct pps_data *sc = arg; 26183818Sphk int i, j, k, l; 26283818Sphk struct timecounter *tc; 26383818Sphk unsigned count; 26483818Sphk 26583818Sphk if (!(sc->busy & ~1)) 26683818Sphk return; 26783818Sphk sc->timeout = timeout(ppshcpoll, sc, 1); 26883818Sphk i = ppb_rdtr(sc->ppbus); 26983818Sphk if (i == sc->lastdata) 27083818Sphk return; 27183818Sphk tc = timecounter; 27283818Sphk count = timecounter->tc_get_timecount(tc); 27383818Sphk l = sc->lastdata ^ i; 27483818Sphk k = 1; 27583818Sphk for (j = 1; j < 9; j ++) { 27683818Sphk if (l & k) 27783818Sphk pps_event(&sc->pps[j], tc, count, 27883818Sphk i & k ? 27983818Sphk PPS_CAPTUREASSERT : PPS_CAPTURECLEAR 28083818Sphk ); 28183818Sphk k += k; 28283818Sphk } 28383818Sphk sc->lastdata = i; 28483818Sphk} 28583818Sphk 28683818Sphkstatic void 28755939Snsouchppsintr(void *arg) 28833323Sphk{ 28955939Snsouch device_t ppsdev = (device_t)arg; 29055939Snsouch struct pps_data *sc = DEVTOSOFTC(ppsdev); 29183818Sphk device_t ppbus = sc->ppbus; 29244666Sphk struct timecounter *tc; 29344666Sphk unsigned count; 29433323Sphk 29544666Sphk tc = timecounter; 29644666Sphk count = timecounter->tc_get_timecount(tc); 29755939Snsouch if (!(ppb_rstr(ppbus) & nACK)) 29833323Sphk return; 29983818Sphk if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 30055939Snsouch ppb_wctr(ppbus, IRQENABLE | AUTOFEED); 30183818Sphk pps_event(&sc->pps[0], tc, count, PPS_CAPTUREASSERT); 30283818Sphk if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 30355939Snsouch ppb_wctr(ppbus, IRQENABLE); 30433323Sphk} 30533323Sphk 30636739Sphkstatic int 30783366Sjulianppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td) 30833323Sphk{ 30983818Sphk struct pps_data *sc = dev->si_drv1; 31083818Sphk int subdev = (int)dev->si_drv2; 31133323Sphk 31283818Sphk return (pps_ioctl(cmd, data, &sc->pps[subdev])); 31333323Sphk} 31433323Sphk 31556455Speterstatic device_method_t pps_methods[] = { 31656455Speter /* device interface */ 31756455Speter DEVMETHOD(device_identify, ppsidentify), 31856455Speter DEVMETHOD(device_probe, ppsprobe), 31956455Speter DEVMETHOD(device_attach, ppsattach), 32056455Speter 32156455Speter { 0, 0 } 32256455Speter}; 32356455Speter 32456455Speterstatic driver_t pps_driver = { 32556455Speter PPS_NAME, 32656455Speter pps_methods, 32756455Speter sizeof(struct pps_data), 32856455Speter}; 32955939SnsouchDRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); 330