1 /* 2 * PPS kernel consumer API 3 * 4 * Copyright (C) 2009-2010 Alexander Gordeev <lasaine@lvk.cs.msu.su> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 22 23 #include <linux/kernel.h> 24 #include <linux/module.h> 25 #include <linux/device.h> 26 #include <linux/init.h> 27 #include <linux/spinlock.h> 28 #include <linux/pps_kernel.h> 29 30 #include "kc.h" 31 32 /* 33 * Global variables 34 */ 35 36 /* state variables to bind kernel consumer */ 37 static DEFINE_SPINLOCK(pps_kc_hardpps_lock); 38 /* PPS API (RFC 2783): current source and mode for kernel consumer */ 39 static struct pps_device *pps_kc_hardpps_dev; /* unique pointer to device */ 40 static int pps_kc_hardpps_mode; /* mode bits for kernel consumer */ 41 42 /* pps_kc_bind - control PPS kernel consumer binding 43 * @pps: the PPS source 44 * @bind_args: kernel consumer bind parameters 45 * 46 * This function is used to bind or unbind PPS kernel consumer according to 47 * supplied parameters. Should not be called in interrupt context. 48 */ 49 int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args) 50 { 51 /* Check if another consumer is already bound */ 52 spin_lock_irq(&pps_kc_hardpps_lock); 53 54 if (bind_args->edge == 0) 55 if (pps_kc_hardpps_dev == pps) { 56 pps_kc_hardpps_mode = 0; 57 pps_kc_hardpps_dev = NULL; 58 spin_unlock_irq(&pps_kc_hardpps_lock); 59 dev_info(pps->dev, "unbound kernel" 60 " consumer\n"); 61 } else { 62 spin_unlock_irq(&pps_kc_hardpps_lock); 63 dev_err(pps->dev, "selected kernel consumer" 64 " is not bound\n"); 65 return -EINVAL; 66 } 67 else 68 if (pps_kc_hardpps_dev == NULL || 69 pps_kc_hardpps_dev == pps) { 70 pps_kc_hardpps_mode = bind_args->edge; 71 pps_kc_hardpps_dev = pps; 72 spin_unlock_irq(&pps_kc_hardpps_lock); 73 dev_info(pps->dev, "bound kernel consumer: " 74 "edge=0x%x\n", bind_args->edge); 75 } else { 76 spin_unlock_irq(&pps_kc_hardpps_lock); 77 dev_err(pps->dev, "another kernel consumer" 78 " is already bound\n"); 79 return -EINVAL; 80 } 81 82 return 0; 83 } 84 85 /* pps_kc_remove - unbind kernel consumer on PPS source removal 86 * @pps: the PPS source 87 * 88 * This function is used to disable kernel consumer on PPS source removal 89 * if this source was bound to PPS kernel consumer. Can be called on any 90 * source safely. Should not be called in interrupt context. 91 */ 92 void pps_kc_remove(struct pps_device *pps) 93 { 94 spin_lock_irq(&pps_kc_hardpps_lock); 95 if (pps == pps_kc_hardpps_dev) { 96 pps_kc_hardpps_mode = 0; 97 pps_kc_hardpps_dev = NULL; 98 spin_unlock_irq(&pps_kc_hardpps_lock); 99 dev_info(pps->dev, "unbound kernel consumer" 100 " on device removal\n"); 101 } else 102 spin_unlock_irq(&pps_kc_hardpps_lock); 103 } 104 105 /* pps_kc_event - call hardpps() on PPS event 106 * @pps: the PPS source 107 * @ts: PPS event timestamp 108 * @event: PPS event edge 109 * 110 * This function calls hardpps() when an event from bound PPS source occurs. 111 */ 112 void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts, 113 int event) 114 { 115 unsigned long flags; 116 117 /* Pass some events to kernel consumer if activated */ 118 spin_lock_irqsave(&pps_kc_hardpps_lock, flags); 119 if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode) 120 hardpps(&ts->ts_real, &ts->ts_raw); 121 spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags); 122 } 123