123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * PPS kernel consumer API
- *
- * Copyright (C) 2009-2010 Alexander Gordeev <lasaine@lvk.cs.msu.su>
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/device.h>
- #include <linux/init.h>
- #include <linux/spinlock.h>
- #include <linux/pps_kernel.h>
- #include "kc.h"
- /*
- * Global variables
- */
- /* state variables to bind kernel consumer */
- static DEFINE_SPINLOCK(pps_kc_hardpps_lock);
- /* PPS API (RFC 2783): current source and mode for kernel consumer */
- static struct pps_device *pps_kc_hardpps_dev; /* unique pointer to device */
- static int pps_kc_hardpps_mode; /* mode bits for kernel consumer */
- /* pps_kc_bind - control PPS kernel consumer binding
- * @pps: the PPS source
- * @bind_args: kernel consumer bind parameters
- *
- * This function is used to bind or unbind PPS kernel consumer according to
- * supplied parameters. Should not be called in interrupt context.
- */
- int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args)
- {
- /* Check if another consumer is already bound */
- spin_lock_irq(&pps_kc_hardpps_lock);
- if (bind_args->edge == 0)
- if (pps_kc_hardpps_dev == pps) {
- pps_kc_hardpps_mode = 0;
- pps_kc_hardpps_dev = NULL;
- spin_unlock_irq(&pps_kc_hardpps_lock);
- dev_info(pps->dev, "unbound kernel"
- " consumer\n");
- } else {
- spin_unlock_irq(&pps_kc_hardpps_lock);
- dev_err(pps->dev, "selected kernel consumer"
- " is not bound\n");
- return -EINVAL;
- }
- else
- if (pps_kc_hardpps_dev == NULL ||
- pps_kc_hardpps_dev == pps) {
- pps_kc_hardpps_mode = bind_args->edge;
- pps_kc_hardpps_dev = pps;
- spin_unlock_irq(&pps_kc_hardpps_lock);
- dev_info(pps->dev, "bound kernel consumer: "
- "edge=0x%x\n", bind_args->edge);
- } else {
- spin_unlock_irq(&pps_kc_hardpps_lock);
- dev_err(pps->dev, "another kernel consumer"
- " is already bound\n");
- return -EINVAL;
- }
- return 0;
- }
- /* pps_kc_remove - unbind kernel consumer on PPS source removal
- * @pps: the PPS source
- *
- * This function is used to disable kernel consumer on PPS source removal
- * if this source was bound to PPS kernel consumer. Can be called on any
- * source safely. Should not be called in interrupt context.
- */
- void pps_kc_remove(struct pps_device *pps)
- {
- spin_lock_irq(&pps_kc_hardpps_lock);
- if (pps == pps_kc_hardpps_dev) {
- pps_kc_hardpps_mode = 0;
- pps_kc_hardpps_dev = NULL;
- spin_unlock_irq(&pps_kc_hardpps_lock);
- dev_info(pps->dev, "unbound kernel consumer"
- " on device removal\n");
- } else
- spin_unlock_irq(&pps_kc_hardpps_lock);
- }
- /* pps_kc_event - call hardpps() on PPS event
- * @pps: the PPS source
- * @ts: PPS event timestamp
- * @event: PPS event edge
- *
- * This function calls hardpps() when an event from bound PPS source occurs.
- */
- void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts,
- int event)
- {
- unsigned long flags;
- /* Pass some events to kernel consumer if activated */
- spin_lock_irqsave(&pps_kc_hardpps_lock, flags);
- if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode)
- hardpps(&ts->ts_real, &ts->ts_raw);
- spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags);
- }
|