linux/drivers/pps/clients/pps_parport.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * pps_parport.c -- kernel parallel port PPS client
   4 *
   5 * Copyright (C) 2009   Alexander Gordeev <lasaine@lvk.cs.msu.su>
   6 */
   7
   8
   9/*
  10 * TODO:
  11 * implement echo over SEL pin
  12 */
  13
  14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  15
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/init.h>
  19#include <linux/irqnr.h>
  20#include <linux/time.h>
  21#include <linux/slab.h>
  22#include <linux/parport.h>
  23#include <linux/pps_kernel.h>
  24
  25#define DRVDESC "parallel port PPS client"
  26
  27/* module parameters */
  28
  29#define CLEAR_WAIT_MAX          100
  30#define CLEAR_WAIT_MAX_ERRORS   5
  31
  32static unsigned int clear_wait = 100;
  33MODULE_PARM_DESC(clear_wait,
  34        "Maximum number of port reads when polling for signal clear,"
  35        " zero turns clear edge capture off entirely");
  36module_param(clear_wait, uint, 0);
  37
  38static DEFINE_IDA(pps_client_index);
  39
  40/* internal per port structure */
  41struct pps_client_pp {
  42        struct pardevice *pardev;       /* parport device */
  43        struct pps_device *pps;         /* PPS device */
  44        unsigned int cw;                /* port clear timeout */
  45        unsigned int cw_err;            /* number of timeouts */
  46        int index;                      /* device number */
  47};
  48
  49static inline int signal_is_set(struct parport *port)
  50{
  51        return (port->ops->read_status(port) & PARPORT_STATUS_ACK) != 0;
  52}
  53
  54/* parport interrupt handler */
  55static void parport_irq(void *handle)
  56{
  57        struct pps_event_time ts_assert, ts_clear;
  58        struct pps_client_pp *dev = handle;
  59        struct parport *port = dev->pardev->port;
  60        unsigned int i;
  61        unsigned long flags;
  62
  63        /* first of all we get the time stamp... */
  64        pps_get_ts(&ts_assert);
  65
  66        if (dev->cw == 0)
  67                /* clear edge capture disabled */
  68                goto out_assert;
  69
  70        /* try capture the clear edge */
  71
  72        /* We have to disable interrupts here. The idea is to prevent
  73         * other interrupts on the same processor to introduce random
  74         * lags while polling the port. Reading from IO port is known
  75         * to take approximately 1us while other interrupt handlers can
  76         * take much more potentially.
  77         *
  78         * Interrupts won't be disabled for a long time because the
  79         * number of polls is limited by clear_wait parameter which is
  80         * kept rather low. So it should never be an issue.
  81         */
  82        local_irq_save(flags);
  83        /* check the signal (no signal means the pulse is lost this time) */
  84        if (!signal_is_set(port)) {
  85                local_irq_restore(flags);
  86                dev_err(dev->pps->dev, "lost the signal\n");
  87                goto out_assert;
  88        }
  89
  90        /* poll the port until the signal is unset */
  91        for (i = dev->cw; i; i--)
  92                if (!signal_is_set(port)) {
  93                        pps_get_ts(&ts_clear);
  94                        local_irq_restore(flags);
  95                        dev->cw_err = 0;
  96                        goto out_both;
  97                }
  98        local_irq_restore(flags);
  99
 100        /* timeout */
 101        dev->cw_err++;
 102        if (dev->cw_err >= CLEAR_WAIT_MAX_ERRORS) {
 103                dev_err(dev->pps->dev, "disabled clear edge capture after %d"
 104                                " timeouts\n", dev->cw_err);
 105                dev->cw = 0;
 106                dev->cw_err = 0;
 107        }
 108
 109out_assert:
 110        /* fire assert event */
 111        pps_event(dev->pps, &ts_assert,
 112                        PPS_CAPTUREASSERT, NULL);
 113        return;
 114
 115out_both:
 116        /* fire assert event */
 117        pps_event(dev->pps, &ts_assert,
 118                        PPS_CAPTUREASSERT, NULL);
 119        /* fire clear event */
 120        pps_event(dev->pps, &ts_clear,
 121                        PPS_CAPTURECLEAR, NULL);
 122        return;
 123}
 124
 125static void parport_attach(struct parport *port)
 126{
 127        struct pardev_cb pps_client_cb;
 128        int index;
 129        struct pps_client_pp *device;
 130        struct pps_source_info info = {
 131                .name           = KBUILD_MODNAME,
 132                .path           = "",
 133                .mode           = PPS_CAPTUREBOTH | \
 134                                  PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
 135                                  PPS_ECHOASSERT | PPS_ECHOCLEAR | \
 136                                  PPS_CANWAIT | PPS_TSFMT_TSPEC,
 137                .owner          = THIS_MODULE,
 138                .dev            = NULL
 139        };
 140
 141        device = kzalloc(sizeof(struct pps_client_pp), GFP_KERNEL);
 142        if (!device) {
 143                pr_err("memory allocation failed, not attaching\n");
 144                return;
 145        }
 146
 147        index = ida_simple_get(&pps_client_index, 0, 0, GFP_KERNEL);
 148        memset(&pps_client_cb, 0, sizeof(pps_client_cb));
 149        pps_client_cb.private = device;
 150        pps_client_cb.irq_func = parport_irq;
 151        pps_client_cb.flags = PARPORT_FLAG_EXCL;
 152        device->pardev = parport_register_dev_model(port,
 153                                                    KBUILD_MODNAME,
 154                                                    &pps_client_cb,
 155                                                    index);
 156        if (!device->pardev) {
 157                pr_err("couldn't register with %s\n", port->name);
 158                goto err_free;
 159        }
 160
 161        if (parport_claim_or_block(device->pardev) < 0) {
 162                pr_err("couldn't claim %s\n", port->name);
 163                goto err_unregister_dev;
 164        }
 165
 166        device->pps = pps_register_source(&info,
 167                        PPS_CAPTUREBOTH | PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
 168        if (IS_ERR(device->pps)) {
 169                pr_err("couldn't register PPS source\n");
 170                goto err_release_dev;
 171        }
 172
 173        device->cw = clear_wait;
 174
 175        port->ops->enable_irq(port);
 176        device->index = index;
 177
 178        pr_info("attached to %s\n", port->name);
 179
 180        return;
 181
 182err_release_dev:
 183        parport_release(device->pardev);
 184err_unregister_dev:
 185        parport_unregister_device(device->pardev);
 186err_free:
 187        ida_simple_remove(&pps_client_index, index);
 188        kfree(device);
 189}
 190
 191static void parport_detach(struct parport *port)
 192{
 193        struct pardevice *pardev = port->cad;
 194        struct pps_client_pp *device;
 195
 196        /* FIXME: oooh, this is ugly! */
 197        if (!pardev || strcmp(pardev->name, KBUILD_MODNAME))
 198                /* not our port */
 199                return;
 200
 201        device = pardev->private;
 202
 203        port->ops->disable_irq(port);
 204        pps_unregister_source(device->pps);
 205        parport_release(pardev);
 206        parport_unregister_device(pardev);
 207        ida_simple_remove(&pps_client_index, device->index);
 208        kfree(device);
 209}
 210
 211static struct parport_driver pps_parport_driver = {
 212        .name = KBUILD_MODNAME,
 213        .match_port = parport_attach,
 214        .detach = parport_detach,
 215        .devmodel = true,
 216};
 217
 218/* module staff */
 219
 220static int __init pps_parport_init(void)
 221{
 222        int ret;
 223
 224        pr_info(DRVDESC "\n");
 225
 226        if (clear_wait > CLEAR_WAIT_MAX) {
 227                pr_err("clear_wait value should be not greater"
 228                                " then %d\n", CLEAR_WAIT_MAX);
 229                return -EINVAL;
 230        }
 231
 232        ret = parport_register_driver(&pps_parport_driver);
 233        if (ret) {
 234                pr_err("unable to register with parport\n");
 235                return ret;
 236        }
 237
 238        return  0;
 239}
 240
 241static void __exit pps_parport_exit(void)
 242{
 243        parport_unregister_driver(&pps_parport_driver);
 244}
 245
 246module_init(pps_parport_init);
 247module_exit(pps_parport_exit);
 248
 249MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>");
 250MODULE_DESCRIPTION(DRVDESC);
 251MODULE_LICENSE("GPL");
 252