linux/drivers/pps/kc.c
<<
>>
Prefs
   1/*
   2 * PPS kernel consumer API
   3 *
   4 * Copyright (C) 2009-2010   Alexander Gordeev <lasaine@lvk.cs.msu.su>
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19 */
  20
  21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  22
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/device.h>
  26#include <linux/init.h>
  27#include <linux/spinlock.h>
  28#include <linux/pps_kernel.h>
  29
  30#include "kc.h"
  31
  32/*
  33 * Global variables
  34 */
  35
  36/* state variables to bind kernel consumer */
  37static DEFINE_SPINLOCK(pps_kc_hardpps_lock);
  38/* PPS API (RFC 2783): current source and mode for kernel consumer */
  39static struct pps_device *pps_kc_hardpps_dev;   /* unique pointer to device */
  40static int pps_kc_hardpps_mode;         /* mode bits for kernel consumer */
  41
  42/* pps_kc_bind - control PPS kernel consumer binding
  43 * @pps: the PPS source
  44 * @bind_args: kernel consumer bind parameters
  45 *
  46 * This function is used to bind or unbind PPS kernel consumer according to
  47 * supplied parameters. Should not be called in interrupt context.
  48 */
  49int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args)
  50{
  51        /* Check if another consumer is already bound */
  52        spin_lock_irq(&pps_kc_hardpps_lock);
  53
  54        if (bind_args->edge == 0)
  55                if (pps_kc_hardpps_dev == pps) {
  56                        pps_kc_hardpps_mode = 0;
  57                        pps_kc_hardpps_dev = NULL;
  58                        spin_unlock_irq(&pps_kc_hardpps_lock);
  59                        dev_info(pps->dev, "unbound kernel"
  60                                        " consumer\n");
  61                } else {
  62                        spin_unlock_irq(&pps_kc_hardpps_lock);
  63                        dev_err(pps->dev, "selected kernel consumer"
  64                                        " is not bound\n");
  65                        return -EINVAL;
  66                }
  67        else
  68                if (pps_kc_hardpps_dev == NULL ||
  69                                pps_kc_hardpps_dev == pps) {
  70                        pps_kc_hardpps_mode = bind_args->edge;
  71                        pps_kc_hardpps_dev = pps;
  72                        spin_unlock_irq(&pps_kc_hardpps_lock);
  73                        dev_info(pps->dev, "bound kernel consumer: "
  74                                "edge=0x%x\n", bind_args->edge);
  75                } else {
  76                        spin_unlock_irq(&pps_kc_hardpps_lock);
  77                        dev_err(pps->dev, "another kernel consumer"
  78                                        " is already bound\n");
  79                        return -EINVAL;
  80                }
  81
  82        return 0;
  83}
  84
  85/* pps_kc_remove - unbind kernel consumer on PPS source removal
  86 * @pps: the PPS source
  87 *
  88 * This function is used to disable kernel consumer on PPS source removal
  89 * if this source was bound to PPS kernel consumer. Can be called on any
  90 * source safely. Should not be called in interrupt context.
  91 */
  92void pps_kc_remove(struct pps_device *pps)
  93{
  94        spin_lock_irq(&pps_kc_hardpps_lock);
  95        if (pps == pps_kc_hardpps_dev) {
  96                pps_kc_hardpps_mode = 0;
  97                pps_kc_hardpps_dev = NULL;
  98                spin_unlock_irq(&pps_kc_hardpps_lock);
  99                dev_info(pps->dev, "unbound kernel consumer"
 100                                " on device removal\n");
 101        } else
 102                spin_unlock_irq(&pps_kc_hardpps_lock);
 103}
 104
 105/* pps_kc_event - call hardpps() on PPS event
 106 * @pps: the PPS source
 107 * @ts: PPS event timestamp
 108 * @event: PPS event edge
 109 *
 110 * This function calls hardpps() when an event from bound PPS source occurs.
 111 */
 112void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts,
 113                int event)
 114{
 115        unsigned long flags;
 116
 117        /* Pass some events to kernel consumer if activated */
 118        spin_lock_irqsave(&pps_kc_hardpps_lock, flags);
 119        if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode)
 120                hardpps(&ts->ts_real, &ts->ts_raw);
 121        spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags);
 122}
 123