linux/drivers/pps/kc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * PPS kernel consumer API
   4 *
   5 * Copyright (C) 2009-2010   Alexander Gordeev <lasaine@lvk.cs.msu.su>
   6 */
   7
   8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/device.h>
  13#include <linux/init.h>
  14#include <linux/spinlock.h>
  15#include <linux/pps_kernel.h>
  16
  17#include "kc.h"
  18
  19/*
  20 * Global variables
  21 */
  22
  23/* state variables to bind kernel consumer */
  24static DEFINE_SPINLOCK(pps_kc_hardpps_lock);
  25/* PPS API (RFC 2783): current source and mode for kernel consumer */
  26static struct pps_device *pps_kc_hardpps_dev;   /* unique pointer to device */
  27static int pps_kc_hardpps_mode;         /* mode bits for kernel consumer */
  28
  29/* pps_kc_bind - control PPS kernel consumer binding
  30 * @pps: the PPS source
  31 * @bind_args: kernel consumer bind parameters
  32 *
  33 * This function is used to bind or unbind PPS kernel consumer according to
  34 * supplied parameters. Should not be called in interrupt context.
  35 */
  36int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args)
  37{
  38        /* Check if another consumer is already bound */
  39        spin_lock_irq(&pps_kc_hardpps_lock);
  40
  41        if (bind_args->edge == 0)
  42                if (pps_kc_hardpps_dev == pps) {
  43                        pps_kc_hardpps_mode = 0;
  44                        pps_kc_hardpps_dev = NULL;
  45                        spin_unlock_irq(&pps_kc_hardpps_lock);
  46                        dev_info(pps->dev, "unbound kernel"
  47                                        " consumer\n");
  48                } else {
  49                        spin_unlock_irq(&pps_kc_hardpps_lock);
  50                        dev_err(pps->dev, "selected kernel consumer"
  51                                        " is not bound\n");
  52                        return -EINVAL;
  53                }
  54        else
  55                if (pps_kc_hardpps_dev == NULL ||
  56                                pps_kc_hardpps_dev == pps) {
  57                        pps_kc_hardpps_mode = bind_args->edge;
  58                        pps_kc_hardpps_dev = pps;
  59                        spin_unlock_irq(&pps_kc_hardpps_lock);
  60                        dev_info(pps->dev, "bound kernel consumer: "
  61                                "edge=0x%x\n", bind_args->edge);
  62                } else {
  63                        spin_unlock_irq(&pps_kc_hardpps_lock);
  64                        dev_err(pps->dev, "another kernel consumer"
  65                                        " is already bound\n");
  66                        return -EINVAL;
  67                }
  68
  69        return 0;
  70}
  71
  72/* pps_kc_remove - unbind kernel consumer on PPS source removal
  73 * @pps: the PPS source
  74 *
  75 * This function is used to disable kernel consumer on PPS source removal
  76 * if this source was bound to PPS kernel consumer. Can be called on any
  77 * source safely. Should not be called in interrupt context.
  78 */
  79void pps_kc_remove(struct pps_device *pps)
  80{
  81        spin_lock_irq(&pps_kc_hardpps_lock);
  82        if (pps == pps_kc_hardpps_dev) {
  83                pps_kc_hardpps_mode = 0;
  84                pps_kc_hardpps_dev = NULL;
  85                spin_unlock_irq(&pps_kc_hardpps_lock);
  86                dev_info(pps->dev, "unbound kernel consumer"
  87                                " on device removal\n");
  88        } else
  89                spin_unlock_irq(&pps_kc_hardpps_lock);
  90}
  91
  92/* pps_kc_event - call hardpps() on PPS event
  93 * @pps: the PPS source
  94 * @ts: PPS event timestamp
  95 * @event: PPS event edge
  96 *
  97 * This function calls hardpps() when an event from bound PPS source occurs.
  98 */
  99void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts,
 100                int event)
 101{
 102        unsigned long flags;
 103
 104        /* Pass some events to kernel consumer if activated */
 105        spin_lock_irqsave(&pps_kc_hardpps_lock, flags);
 106        if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode)
 107                hardpps(&ts->ts_real, &ts->ts_raw);
 108        spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags);
 109}
 110