The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ppbus/pps.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * ----------------------------------------------------------------------------
    3  * "THE BEER-WARE LICENSE" (Revision 42):
    4  * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
    5  * can do whatever you want with this stuff. If we meet some day, and you think
    6  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
    7  * ----------------------------------------------------------------------------
    8  *
    9  *
   10  * This driver implements a draft-mogul-pps-api-02.txt PPS source.
   11  *
   12  * The input pin is pin#10 
   13  * The echo output pin is pin#14
   14  *
   15  */
   16 
   17 #include <sys/cdefs.h>
   18 __FBSDID("$FreeBSD: releng/7.3/sys/dev/ppbus/pps.c 166901 2007-02-23 12:19:07Z piso $");
   19 
   20 #include <sys/param.h>
   21 #include <sys/kernel.h>
   22 #include <sys/systm.h>
   23 #include <sys/module.h>
   24 #include <sys/bus.h>
   25 #include <sys/conf.h>
   26 #include <sys/timepps.h>
   27 #include <machine/bus.h>
   28 #include <machine/resource.h>
   29 #include <sys/rman.h>
   30 
   31 #include <dev/ppbus/ppbconf.h>
   32 #include "ppbus_if.h"
   33 #include <dev/ppbus/ppbio.h>
   34 
   35 #define PPS_NAME        "pps"           /* our official name */
   36 
   37 #define PRVERBOSE(fmt, arg...)  if (bootverbose) printf(fmt, ##arg);
   38 
   39 struct pps_data {
   40         struct  ppb_device pps_dev;
   41         struct  pps_state pps[9];
   42         struct cdev *devs[9];
   43         device_t ppsdev;
   44         device_t ppbus;
   45         int     busy;
   46         struct callout_handle timeout;
   47         int     lastdata;
   48 
   49         struct mtx      mtx;
   50         struct resource *intr_resource; /* interrupt resource */
   51         void *intr_cookie;              /* interrupt registration cookie */
   52 };
   53 
   54 static int      ppsintr(void *arg);
   55 static void     ppshcpoll(void *arg);
   56 
   57 #define DEVTOSOFTC(dev) \
   58         ((struct pps_data *)device_get_softc(dev))
   59 
   60 static devclass_t pps_devclass;
   61 
   62 static  d_open_t        ppsopen;
   63 static  d_close_t       ppsclose;
   64 static  d_ioctl_t       ppsioctl;
   65 
   66 static struct cdevsw pps_cdevsw = {
   67         .d_version =    D_VERSION,
   68         .d_open =       ppsopen,
   69         .d_close =      ppsclose,
   70         .d_ioctl =      ppsioctl,
   71         .d_name =       PPS_NAME,
   72 };
   73 
   74 static void
   75 ppsidentify(driver_t *driver, device_t parent)
   76 {
   77 
   78         device_t dev;
   79 
   80         dev = device_find_child(parent, PPS_NAME, 0);
   81         if (!dev)
   82                 BUS_ADD_CHILD(parent, 0, PPS_NAME, -1);
   83 }
   84 
   85 static int
   86 ppstry(device_t ppbus, int send, int expect)
   87 {
   88         int i;
   89 
   90         ppb_wdtr(ppbus, send);
   91         i = ppb_rdtr(ppbus);
   92         PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i);
   93         return (i != expect);
   94 }
   95 
   96 static int
   97 ppsprobe(device_t ppsdev)
   98 {
   99         device_set_desc(ppsdev, "Pulse per second Timing Interface");
  100 
  101         return (0);
  102 }
  103 
  104 static int
  105 ppsattach(device_t dev)
  106 {
  107         struct pps_data *sc = DEVTOSOFTC(dev);
  108         device_t ppbus = device_get_parent(dev);
  109         struct cdev *d;
  110         intptr_t irq;
  111         int i, unit, zero = 0;
  112 
  113         mtx_init(&sc->mtx, device_get_nameunit(dev), "pps", MTX_SPIN);
  114         /* retrieve the ppbus irq */
  115         BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
  116 
  117         if (irq > 0) {
  118                 /* declare our interrupt handler */
  119                 sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
  120                     &zero, irq, irq, 1, RF_SHAREABLE);
  121         }
  122         /* interrupts seem mandatory */
  123         if (sc->intr_resource == NULL)
  124                 return (ENXIO);
  125 
  126         sc->ppsdev = dev;
  127         sc->ppbus = ppbus;
  128         unit = device_get_unit(ppbus);
  129         d = make_dev(&pps_cdevsw, unit,
  130             UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit);
  131         sc->devs[0] = d;
  132         sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
  133         d->si_drv1 = sc;
  134         d->si_drv2 = (void*)0;
  135         pps_init(&sc->pps[0]);
  136 
  137         if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT))
  138                 return (0);
  139 
  140         do {
  141                 i = ppb_set_mode(sc->ppbus, PPB_EPP);
  142                 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
  143                 if (i == -1)
  144                         break;
  145                 i = 0;
  146                 ppb_wctr(ppbus, i);
  147                 if (ppstry(ppbus, 0x00, 0x00))
  148                         break;
  149                 if (ppstry(ppbus, 0x55, 0x55))
  150                         break;
  151                 if (ppstry(ppbus, 0xaa, 0xaa))
  152                         break;
  153                 if (ppstry(ppbus, 0xff, 0xff))
  154                         break;
  155 
  156                 i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN;
  157                 ppb_wctr(ppbus, i);
  158                 PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
  159                 if (ppstry(ppbus, 0x00, 0x00))
  160                         break;
  161                 if (ppstry(ppbus, 0x55, 0x00))
  162                         break;
  163                 if (ppstry(ppbus, 0xaa, 0x00))
  164                         break;
  165                 if (ppstry(ppbus, 0xff, 0x00))
  166                         break;
  167 
  168                 i = IRQENABLE | PCD | nINIT | SELECTIN;
  169                 ppb_wctr(ppbus, i);
  170                 PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
  171                 ppstry(ppbus, 0x00, 0xff);
  172                 ppstry(ppbus, 0x55, 0xff);
  173                 ppstry(ppbus, 0xaa, 0xff);
  174                 ppstry(ppbus, 0xff, 0xff);
  175 
  176                 for (i = 1; i < 9; i++) {
  177                         d = make_dev(&pps_cdevsw, unit + 0x10000 * i,
  178                           UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1);
  179                         sc->devs[i] = d;
  180                         sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR;
  181                         d->si_drv1 = sc;
  182                         d->si_drv2 = (void *)(intptr_t)i;
  183                         pps_init(&sc->pps[i]);
  184                 }
  185         } while (0);
  186         i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE);
  187         ppb_release_bus(ppbus, dev);
  188 
  189         return (0);
  190 }
  191 
  192 static  int
  193 ppsopen(struct cdev *dev, int flags, int fmt, struct thread *td)
  194 {
  195         struct pps_data *sc = dev->si_drv1;
  196         int subdev = (intptr_t)dev->si_drv2;
  197         int error, i;
  198 
  199         if (!sc->busy) {
  200                 device_t ppsdev = sc->ppsdev;
  201                 device_t ppbus = sc->ppbus;
  202 
  203                 if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR))
  204                         return (EINTR);
  205 
  206                 /* attach the interrupt handler */
  207                 if ((error = bus_setup_intr(ppsdev, sc->intr_resource,
  208                     (INTR_TYPE_TTY | INTR_MPSAFE), ppsintr, NULL,
  209                     sc, &sc->intr_cookie))) {
  210                         ppb_release_bus(ppbus, ppsdev);
  211                         return (error);
  212                 }
  213 
  214                 i = ppb_set_mode(sc->ppbus, PPB_PS2);
  215                 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
  216 
  217                 i = IRQENABLE | PCD | nINIT | SELECTIN;
  218                 ppb_wctr(ppbus, i);
  219         }
  220         if (subdev > 0 && !(sc->busy & ~1)) {
  221                 sc->timeout = timeout(ppshcpoll, sc, 1);
  222                 sc->lastdata = ppb_rdtr(sc->ppbus);
  223         }
  224         sc->busy |= (1 << subdev);
  225         return(0);
  226 }
  227 
  228 static  int
  229 ppsclose(struct cdev *dev, int flags, int fmt, struct thread *td)
  230 {
  231         struct pps_data *sc = dev->si_drv1;
  232         int subdev = (intptr_t)dev->si_drv2;
  233 
  234         sc->pps[subdev].ppsparam.mode = 0;      /* PHK ??? */
  235         sc->busy &= ~(1 << subdev);
  236         if (subdev > 0 && !(sc->busy & ~1))
  237                 untimeout(ppshcpoll, sc, sc->timeout);
  238         if (!sc->busy) {
  239                 device_t ppsdev = sc->ppsdev;
  240                 device_t ppbus = sc->ppbus;
  241 
  242                 ppb_wdtr(ppbus, 0);
  243                 ppb_wctr(ppbus, 0);
  244 
  245                 /* Note: the interrupt handler is automatically detached */
  246                 ppb_set_mode(ppbus, PPB_COMPATIBLE);
  247                 ppb_release_bus(ppbus, ppsdev);
  248         }
  249         return(0);
  250 }
  251 
  252 static void
  253 ppshcpoll(void *arg)
  254 {
  255         struct pps_data *sc = arg;
  256         int i, j, k, l;
  257 
  258         if (!(sc->busy & ~1))
  259                 return;
  260         mtx_lock_spin(&sc->mtx);
  261         sc->timeout = timeout(ppshcpoll, sc, 1);
  262         i = ppb_rdtr(sc->ppbus);
  263         if (i == sc->lastdata) 
  264                 return;
  265         l = sc->lastdata ^ i;
  266         k = 1;
  267         for (j = 1; j < 9; j ++) {
  268                 if (l & k) {
  269                         pps_capture(&sc->pps[j]);
  270                         pps_event(&sc->pps[j],
  271                             i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
  272                 }
  273                 k += k;
  274         }
  275         sc->lastdata = i;
  276         mtx_unlock_spin(&sc->mtx);
  277 }
  278 
  279 static int
  280 ppsintr(void *arg)
  281 {
  282         struct pps_data *sc = (struct pps_data *)arg;
  283 
  284         pps_capture(&sc->pps[0]);
  285         if (!(ppb_rstr(sc->ppbus) & nACK))
  286                 return (FILTER_STRAY);
  287         if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 
  288                 ppb_wctr(sc->ppbus, IRQENABLE | AUTOFEED);
  289         mtx_lock_spin(&sc->mtx);
  290         pps_event(&sc->pps[0], PPS_CAPTUREASSERT);
  291         mtx_unlock_spin(&sc->mtx);
  292         if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) 
  293                 ppb_wctr(sc->ppbus, IRQENABLE);
  294         return (FILTER_HANDLED);
  295 }
  296 
  297 static int
  298 ppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
  299 {
  300         struct pps_data *sc = dev->si_drv1;
  301         int subdev = (intptr_t)dev->si_drv2;
  302         int err;
  303 
  304         mtx_lock_spin(&sc->mtx);
  305         err = pps_ioctl(cmd, data, &sc->pps[subdev]);
  306         mtx_unlock_spin(&sc->mtx);
  307         return (err);
  308 }
  309 
  310 static device_method_t pps_methods[] = {
  311         /* device interface */
  312         DEVMETHOD(device_identify,      ppsidentify),
  313         DEVMETHOD(device_probe,         ppsprobe),
  314         DEVMETHOD(device_attach,        ppsattach),
  315 
  316         { 0, 0 }
  317 };
  318 
  319 static driver_t pps_driver = {
  320         PPS_NAME,
  321         pps_methods,
  322         sizeof(struct pps_data),
  323 };
  324 DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0);
  325 MODULE_DEPEND(pps, ppbus, 1, 1, 1);

Cache object: eef69c7d90ac6b59888d718e24ddd3ad


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.