pps.c revision 184130
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 184130 2008-10-21 18:30:10Z jhb $"); 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 54166901Spisostatic int 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 80184130Sjhb dev = device_find_child(parent, PPS_NAME, -1); 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; 110183053Sjhb int i, unit, rid = 0; 11133323Sphk 112143398Simp mtx_init(&sc->mtx, device_get_nameunit(dev), "pps", MTX_SPIN); 11388220Simp 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 */ 11988220Simp if (sc->intr_resource == NULL) 12088220Simp return (ENXIO); 12188220Simp 12288220Simp sc->ppsdev = dev; 12388220Simp sc->ppbus = ppbus; 12483818Sphk unit = device_get_unit(ppbus); 12588220Simp d = make_dev(&pps_cdevsw, unit, 126108322Srwatson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); 12788220Simp sc->devs[0] = d; 12883818Sphk sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; 12988220Simp d->si_drv1 = sc; 13088220Simp d->si_drv2 = (void*)0; 13183818Sphk pps_init(&sc->pps[0]); 13233323Sphk 13388220Simp if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) 13483818Sphk return (0); 13533323Sphk 13683818Sphk do { 13783818Sphk i = ppb_set_mode(sc->ppbus, PPB_EPP); 13888220Simp PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 13983818Sphk if (i == -1) 14083818Sphk break; 14183818Sphk i = 0; 14283818Sphk ppb_wctr(ppbus, i); 14383818Sphk if (ppstry(ppbus, 0x00, 0x00)) 14483818Sphk break; 14583818Sphk if (ppstry(ppbus, 0x55, 0x55)) 14683818Sphk break; 14783818Sphk if (ppstry(ppbus, 0xaa, 0xaa)) 14883818Sphk break; 14983818Sphk if (ppstry(ppbus, 0xff, 0xff)) 15083818Sphk break; 15183818Sphk 15283818Sphk i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; 15383818Sphk ppb_wctr(ppbus, i); 15488220Simp PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 15583818Sphk if (ppstry(ppbus, 0x00, 0x00)) 15683818Sphk break; 15783818Sphk if (ppstry(ppbus, 0x55, 0x00)) 15883818Sphk break; 15983818Sphk if (ppstry(ppbus, 0xaa, 0x00)) 16083818Sphk break; 16183818Sphk if (ppstry(ppbus, 0xff, 0x00)) 16283818Sphk break; 16383818Sphk 16483818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 16583818Sphk ppb_wctr(ppbus, i); 16688220Simp PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); 16783818Sphk ppstry(ppbus, 0x00, 0xff); 16883818Sphk ppstry(ppbus, 0x55, 0xff); 16983818Sphk ppstry(ppbus, 0xaa, 0xff); 17083818Sphk ppstry(ppbus, 0xff, 0xff); 17183818Sphk 17283818Sphk for (i = 1; i < 9; i++) { 17388220Simp d = make_dev(&pps_cdevsw, unit + 0x10000 * i, 174108322Srwatson UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); 17588220Simp sc->devs[i] = d; 17683818Sphk sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 17788220Simp d->si_drv1 = sc; 178106563Sjhb d->si_drv2 = (void *)(intptr_t)i; 17983818Sphk pps_init(&sc->pps[i]); 18083818Sphk } 18183818Sphk } while (0); 18283818Sphk i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); 18388220Simp ppb_release_bus(ppbus, dev); 18433323Sphk 18555939Snsouch return (0); 18633323Sphk} 18733323Sphk 18833323Sphkstatic int 189130585Sphkppsopen(struct cdev *dev, int flags, int fmt, struct thread *td) 19033323Sphk{ 19183818Sphk struct pps_data *sc = dev->si_drv1; 192106563Sjhb int subdev = (intptr_t)dev->si_drv2; 19383818Sphk int error, i; 19433323Sphk 19583818Sphk if (!sc->busy) { 19683818Sphk device_t ppsdev = sc->ppsdev; 19783818Sphk device_t ppbus = sc->ppbus; 19883818Sphk 19955939Snsouch if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) 20046053Sphk return (EINTR); 20133323Sphk 20255939Snsouch /* attach the interrupt handler */ 203143398Simp if ((error = bus_setup_intr(ppsdev, sc->intr_resource, 204166901Spiso (INTR_TYPE_TTY | INTR_MPSAFE), ppsintr, NULL, 205145075Simp sc, &sc->intr_cookie))) { 20655939Snsouch ppb_release_bus(ppbus, ppsdev); 20755939Snsouch return (error); 20855939Snsouch } 20955939Snsouch 21083818Sphk i = ppb_set_mode(sc->ppbus, PPB_PS2); 21188220Simp PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); 21283818Sphk 21383818Sphk i = IRQENABLE | PCD | nINIT | SELECTIN; 21483818Sphk ppb_wctr(ppbus, i); 215143390Simp } 21683818Sphk if (subdev > 0 && !(sc->busy & ~1)) { 21783818Sphk sc->timeout = timeout(ppshcpoll, sc, 1); 21883818Sphk sc->lastdata = ppb_rdtr(sc->ppbus); 21946053Sphk } 22083818Sphk sc->busy |= (1 << subdev); 22133323Sphk return(0); 22233323Sphk} 22333323Sphk 22433323Sphkstatic int 225130585Sphkppsclose(struct cdev *dev, int flags, int fmt, struct thread *td) 22633323Sphk{ 22783818Sphk struct pps_data *sc = dev->si_drv1; 228106563Sjhb int subdev = (intptr_t)dev->si_drv2; 22933323Sphk 23083818Sphk sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ 23183818Sphk sc->busy &= ~(1 << subdev); 232143390Simp if (subdev > 0 && !(sc->busy & ~1)) 23383818Sphk untimeout(ppshcpoll, sc, sc->timeout); 23483818Sphk if (!sc->busy) { 23583818Sphk device_t ppsdev = sc->ppsdev; 23683818Sphk device_t ppbus = sc->ppbus; 23743433Snsouch 23883818Sphk ppb_wdtr(ppbus, 0); 23983818Sphk ppb_wctr(ppbus, 0); 24043433Snsouch 24183818Sphk /* Note: the interrupt handler is automatically detached */ 24283818Sphk ppb_set_mode(ppbus, PPB_COMPATIBLE); 24383818Sphk ppb_release_bus(ppbus, ppsdev); 24483818Sphk } 24533323Sphk return(0); 24633323Sphk} 24733323Sphk 24833323Sphkstatic void 24983818Sphkppshcpoll(void *arg) 25083818Sphk{ 25183818Sphk struct pps_data *sc = arg; 25283818Sphk int i, j, k, l; 25383818Sphk 25483818Sphk if (!(sc->busy & ~1)) 25583818Sphk return; 256143766Simp mtx_lock_spin(&sc->mtx); 25783818Sphk sc->timeout = timeout(ppshcpoll, sc, 1); 25883818Sphk i = ppb_rdtr(sc->ppbus); 25983818Sphk if (i == sc->lastdata) 26083818Sphk return; 26183818Sphk l = sc->lastdata ^ i; 26283818Sphk k = 1; 26383818Sphk for (j = 1; j < 9; j ++) { 26495523Sphk if (l & k) { 26595523Sphk pps_capture(&sc->pps[j]); 26695523Sphk pps_event(&sc->pps[j], 26795523Sphk i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 26895523Sphk } 26983818Sphk k += k; 27083818Sphk } 27183818Sphk sc->lastdata = i; 272143766Simp mtx_unlock_spin(&sc->mtx); 27383818Sphk} 27483818Sphk 275166901Spisostatic int 27655939Snsouchppsintr(void *arg) 27733323Sphk{ 278145075Simp struct pps_data *sc = (struct pps_data *)arg; 27933323Sphk 28095523Sphk pps_capture(&sc->pps[0]); 281145075Simp if (!(ppb_rstr(sc->ppbus) & nACK)) 282166901Spiso return (FILTER_STRAY); 28383818Sphk if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 284145075Simp ppb_wctr(sc->ppbus, IRQENABLE | AUTOFEED); 285145075Simp mtx_lock_spin(&sc->mtx); 28695523Sphk pps_event(&sc->pps[0], PPS_CAPTUREASSERT); 287145075Simp mtx_unlock_spin(&sc->mtx); 28883818Sphk if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 289145075Simp ppb_wctr(sc->ppbus, IRQENABLE); 290166901Spiso return (FILTER_HANDLED); 29133323Sphk} 29233323Sphk 29336739Sphkstatic int 294130585Sphkppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) 29533323Sphk{ 29683818Sphk struct pps_data *sc = dev->si_drv1; 297106563Sjhb int subdev = (intptr_t)dev->si_drv2; 298143398Simp int err; 29933323Sphk 300143766Simp mtx_lock_spin(&sc->mtx); 301143398Simp err = pps_ioctl(cmd, data, &sc->pps[subdev]); 302143766Simp mtx_unlock_spin(&sc->mtx); 303143398Simp return (err); 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); 321153610SruMODULE_DEPEND(pps, ppbus, 1, 1, 1); 322