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