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 */ 24 static DEFINE_SPINLOCK(pps_kc_hardpps_lock); 25 /* PPS API (RFC 2783): current source and mode for kernel consumer */ 26 static struct pps_device *pps_kc_hardpps_dev; /* unique pointer to device */ 27 static 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 */ 36 int 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 /* 42 * Don't allow PPS_KC_BIND on a removed device. 43 */ 44 if (pps->kc_removed) { 45 spin_unlock_irq(&pps_kc_hardpps_lock); 46 return -ENODEV; 47 } 48 49 if (bind_args->edge == 0) 50 if (pps_kc_hardpps_dev == pps) { 51 pps_kc_hardpps_mode = 0; 52 pps_kc_hardpps_dev = NULL; 53 spin_unlock_irq(&pps_kc_hardpps_lock); 54 dev_info(&pps->dev, "unbound kernel" 55 " consumer\n"); 56 } else { 57 spin_unlock_irq(&pps_kc_hardpps_lock); 58 dev_err(&pps->dev, "selected kernel consumer" 59 " is not bound\n"); 60 return -EINVAL; 61 } 62 else 63 if (pps_kc_hardpps_dev == NULL || 64 pps_kc_hardpps_dev == pps) { 65 pps_kc_hardpps_mode = bind_args->edge; 66 pps_kc_hardpps_dev = pps; 67 spin_unlock_irq(&pps_kc_hardpps_lock); 68 dev_info(&pps->dev, "bound kernel consumer: " 69 "edge=0x%x\n", bind_args->edge); 70 } else { 71 spin_unlock_irq(&pps_kc_hardpps_lock); 72 dev_err(&pps->dev, "another kernel consumer" 73 " is already bound\n"); 74 return -EINVAL; 75 } 76 77 return 0; 78 } 79 80 /* pps_kc_remove - unbind kernel consumer on PPS source removal 81 * @pps: the PPS source 82 * 83 * This function is used to disable kernel consumer on PPS source removal 84 * if this source was bound to PPS kernel consumer. Can be called on any 85 * source safely. Should not be called in interrupt context. 86 */ 87 void pps_kc_remove(struct pps_device *pps) 88 { 89 spin_lock_irq(&pps_kc_hardpps_lock); 90 91 pps->kc_removed = true; 92 if (pps == pps_kc_hardpps_dev) { 93 pps_kc_hardpps_mode = 0; 94 pps_kc_hardpps_dev = NULL; 95 spin_unlock_irq(&pps_kc_hardpps_lock); 96 dev_info(&pps->dev, "unbound kernel consumer" 97 " on device removal\n"); 98 } else 99 spin_unlock_irq(&pps_kc_hardpps_lock); 100 } 101 102 /* pps_kc_event - call hardpps() on PPS event 103 * @pps: the PPS source 104 * @ts: PPS event timestamp 105 * @event: PPS event edge 106 * 107 * This function calls hardpps() when an event from bound PPS source occurs. 108 */ 109 void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts, 110 int event) 111 { 112 unsigned long flags; 113 114 /* Pass some events to kernel consumer if activated */ 115 spin_lock_irqsave(&pps_kc_hardpps_lock, flags); 116 if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode) 117 hardpps(&ts->ts_real, &ts->ts_raw); 118 spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags); 119 } 120