Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * PPS kernel consumer API
0004  *
0005  * Copyright (C) 2009-2010   Alexander Gordeev <lasaine@lvk.cs.msu.su>
0006  */
0007 
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/device.h>
0013 #include <linux/init.h>
0014 #include <linux/spinlock.h>
0015 #include <linux/pps_kernel.h>
0016 
0017 #include "kc.h"
0018 
0019 /*
0020  * Global variables
0021  */
0022 
0023 /* state variables to bind kernel consumer */
0024 static DEFINE_SPINLOCK(pps_kc_hardpps_lock);
0025 /* PPS API (RFC 2783): current source and mode for kernel consumer */
0026 static struct pps_device *pps_kc_hardpps_dev;   /* unique pointer to device */
0027 static int pps_kc_hardpps_mode;     /* mode bits for kernel consumer */
0028 
0029 /* pps_kc_bind - control PPS kernel consumer binding
0030  * @pps: the PPS source
0031  * @bind_args: kernel consumer bind parameters
0032  *
0033  * This function is used to bind or unbind PPS kernel consumer according to
0034  * supplied parameters. Should not be called in interrupt context.
0035  */
0036 int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args)
0037 {
0038     /* Check if another consumer is already bound */
0039     spin_lock_irq(&pps_kc_hardpps_lock);
0040 
0041     if (bind_args->edge == 0)
0042         if (pps_kc_hardpps_dev == pps) {
0043             pps_kc_hardpps_mode = 0;
0044             pps_kc_hardpps_dev = NULL;
0045             spin_unlock_irq(&pps_kc_hardpps_lock);
0046             dev_info(pps->dev, "unbound kernel"
0047                     " consumer\n");
0048         } else {
0049             spin_unlock_irq(&pps_kc_hardpps_lock);
0050             dev_err(pps->dev, "selected kernel consumer"
0051                     " is not bound\n");
0052             return -EINVAL;
0053         }
0054     else
0055         if (pps_kc_hardpps_dev == NULL ||
0056                 pps_kc_hardpps_dev == pps) {
0057             pps_kc_hardpps_mode = bind_args->edge;
0058             pps_kc_hardpps_dev = pps;
0059             spin_unlock_irq(&pps_kc_hardpps_lock);
0060             dev_info(pps->dev, "bound kernel consumer: "
0061                 "edge=0x%x\n", bind_args->edge);
0062         } else {
0063             spin_unlock_irq(&pps_kc_hardpps_lock);
0064             dev_err(pps->dev, "another kernel consumer"
0065                     " is already bound\n");
0066             return -EINVAL;
0067         }
0068 
0069     return 0;
0070 }
0071 
0072 /* pps_kc_remove - unbind kernel consumer on PPS source removal
0073  * @pps: the PPS source
0074  *
0075  * This function is used to disable kernel consumer on PPS source removal
0076  * if this source was bound to PPS kernel consumer. Can be called on any
0077  * source safely. Should not be called in interrupt context.
0078  */
0079 void pps_kc_remove(struct pps_device *pps)
0080 {
0081     spin_lock_irq(&pps_kc_hardpps_lock);
0082     if (pps == pps_kc_hardpps_dev) {
0083         pps_kc_hardpps_mode = 0;
0084         pps_kc_hardpps_dev = NULL;
0085         spin_unlock_irq(&pps_kc_hardpps_lock);
0086         dev_info(pps->dev, "unbound kernel consumer"
0087                 " on device removal\n");
0088     } else
0089         spin_unlock_irq(&pps_kc_hardpps_lock);
0090 }
0091 
0092 /* pps_kc_event - call hardpps() on PPS event
0093  * @pps: the PPS source
0094  * @ts: PPS event timestamp
0095  * @event: PPS event edge
0096  *
0097  * This function calls hardpps() when an event from bound PPS source occurs.
0098  */
0099 void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts,
0100         int event)
0101 {
0102     unsigned long flags;
0103 
0104     /* Pass some events to kernel consumer if activated */
0105     spin_lock_irqsave(&pps_kc_hardpps_lock, flags);
0106     if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode)
0107         hardpps(&ts->ts_real, &ts->ts_raw);
0108     spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags);
0109 }