Blame view

drivers/pps/kc.c 3.77 KB
717c03366   Alexander Gordeev   pps: add kernel c...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  /*
   * PPS kernel consumer API
   *
   * Copyright (C) 2009-2010   Alexander Gordeev <lasaine@lvk.cs.msu.su>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  #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 */
97439d0f8   Fengguang Wu   pps: pps_kc_hardp...
37
  static DEFINE_SPINLOCK(pps_kc_hardpps_lock);
717c03366   Alexander Gordeev   pps: add kernel c...
38
  /* PPS API (RFC 2783): current source and mode for kernel consumer */
97439d0f8   Fengguang Wu   pps: pps_kc_hardp...
39
40
  static struct pps_device *pps_kc_hardpps_dev;	/* unique pointer to device */
  static int pps_kc_hardpps_mode;		/* mode bits for kernel consumer */
717c03366   Alexander Gordeev   pps: add kernel c...
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  
  /* 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
  ");
  		} else {
  			spin_unlock_irq(&pps_kc_hardpps_lock);
  			dev_err(pps->dev, "selected kernel consumer"
  					" is not bound
  ");
  			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
  ", bind_args->edge);
  		} else {
  			spin_unlock_irq(&pps_kc_hardpps_lock);
  			dev_err(pps->dev, "another kernel consumer"
  					" is already bound
  ");
  			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
  ");
  	} 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);
  }