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