aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Cochran <richardcochran@gmail.com>2014-03-20 17:21:52 -0400
committerDavid S. Miller <davem@davemloft.net>2014-03-21 14:21:13 -0400
commit6092315dfdec5185881605d15a0e200d6e90eb66 (patch)
tree8b018970396a2e2380fe51ed14d2f860ce6010e1
parenta85ae0e97879f51bccd8511668b07d346d98b3eb (diff)
ptp: introduce programmable pins.
This patch adds a pair of new ioctls to the PTP Hardware Clock device interface. Using the ioctls, user space programs can query each pin to find out its current function and also reprogram a different function if desired. Signed-off-by: Richard Cochran <richardcochran@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/ptp/ptp_chardev.c128
-rw-r--r--drivers/ptp/ptp_clock.c23
-rw-r--r--drivers/ptp/ptp_private.h5
-rw-r--r--include/linux/ptp_clock_kernel.h33
-rw-r--r--include/uapi/linux/ptp_clock.h39
5 files changed, 226 insertions, 2 deletions
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index 34a0c607318e..419056d7887e 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -25,6 +25,96 @@
25 25
26#include "ptp_private.h" 26#include "ptp_private.h"
27 27
28static int ptp_disable_pinfunc(struct ptp_clock_info *ops,
29 enum ptp_pin_function func, unsigned int chan)
30{
31 struct ptp_clock_request rq;
32 int err = 0;
33
34 memset(&rq, 0, sizeof(rq));
35
36 switch (func) {
37 case PTP_PF_NONE:
38 break;
39 case PTP_PF_EXTTS:
40 rq.type = PTP_CLK_REQ_EXTTS;
41 rq.extts.index = chan;
42 err = ops->enable(ops, &rq, 0);
43 break;
44 case PTP_PF_PEROUT:
45 rq.type = PTP_CLK_REQ_PEROUT;
46 rq.perout.index = chan;
47 err = ops->enable(ops, &rq, 0);
48 break;
49 case PTP_PF_PHYSYNC:
50 break;
51 default:
52 return -EINVAL;
53 }
54
55 return err;
56}
57
58int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin,
59 enum ptp_pin_function func, unsigned int chan)
60{
61 struct ptp_clock_info *info = ptp->info;
62 struct ptp_pin_desc *pin1 = NULL, *pin2 = &info->pin_config[pin];
63 unsigned int i;
64
65 /* Check to see if any other pin previously had this function. */
66 for (i = 0; i < info->n_pins; i++) {
67 if (info->pin_config[i].func == func &&
68 info->pin_config[i].chan == chan) {
69 pin1 = &info->pin_config[i];
70 break;
71 }
72 }
73 if (pin1 && i == pin)
74 return 0;
75
76 /* Check the desired function and channel. */
77 switch (func) {
78 case PTP_PF_NONE:
79 break;
80 case PTP_PF_EXTTS:
81 if (chan >= info->n_ext_ts)
82 return -EINVAL;
83 break;
84 case PTP_PF_PEROUT:
85 if (chan >= info->n_per_out)
86 return -EINVAL;
87 break;
88 case PTP_PF_PHYSYNC:
89 pr_err("sorry, cannot reassign the calibration pin\n");
90 return -EINVAL;
91 default:
92 return -EINVAL;
93 }
94
95 if (pin2->func == PTP_PF_PHYSYNC) {
96 pr_err("sorry, cannot reprogram the calibration pin\n");
97 return -EINVAL;
98 }
99
100 if (info->verify(info, pin, func, chan)) {
101 pr_err("driver cannot use function %u on pin %u\n", func, chan);
102 return -EOPNOTSUPP;
103 }
104
105 /* Disable whatever function was previously assigned. */
106 if (pin1) {
107 ptp_disable_pinfunc(info, func, chan);
108 pin1->func = PTP_PF_NONE;
109 pin1->chan = 0;
110 }
111 ptp_disable_pinfunc(info, pin2->func, pin2->chan);
112 pin2->func = func;
113 pin2->chan = chan;
114
115 return 0;
116}
117
28int ptp_open(struct posix_clock *pc, fmode_t fmode) 118int ptp_open(struct posix_clock *pc, fmode_t fmode)
29{ 119{
30 return 0; 120 return 0;
@@ -35,12 +125,13 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
35 struct ptp_clock_caps caps; 125 struct ptp_clock_caps caps;
36 struct ptp_clock_request req; 126 struct ptp_clock_request req;
37 struct ptp_sys_offset *sysoff = NULL; 127 struct ptp_sys_offset *sysoff = NULL;
128 struct ptp_pin_desc pd;
38 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 129 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
39 struct ptp_clock_info *ops = ptp->info; 130 struct ptp_clock_info *ops = ptp->info;
40 struct ptp_clock_time *pct; 131 struct ptp_clock_time *pct;
41 struct timespec ts; 132 struct timespec ts;
42 int enable, err = 0; 133 int enable, err = 0;
43 unsigned int i; 134 unsigned int i, pin_index;
44 135
45 switch (cmd) { 136 switch (cmd) {
46 137
@@ -51,6 +142,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
51 caps.n_ext_ts = ptp->info->n_ext_ts; 142 caps.n_ext_ts = ptp->info->n_ext_ts;
52 caps.n_per_out = ptp->info->n_per_out; 143 caps.n_per_out = ptp->info->n_per_out;
53 caps.pps = ptp->info->pps; 144 caps.pps = ptp->info->pps;
145 caps.n_pins = ptp->info->n_pins;
54 if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) 146 if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
55 err = -EFAULT; 147 err = -EFAULT;
56 break; 148 break;
@@ -126,6 +218,40 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
126 err = -EFAULT; 218 err = -EFAULT;
127 break; 219 break;
128 220
221 case PTP_PIN_GETFUNC:
222 if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) {
223 err = -EFAULT;
224 break;
225 }
226 pin_index = pd.index;
227 if (pin_index >= ops->n_pins) {
228 err = -EINVAL;
229 break;
230 }
231 if (mutex_lock_interruptible(&ptp->pincfg_mux))
232 return -ERESTARTSYS;
233 pd = ops->pin_config[pin_index];
234 mutex_unlock(&ptp->pincfg_mux);
235 if (!err && copy_to_user((void __user *)arg, &pd, sizeof(pd)))
236 err = -EFAULT;
237 break;
238
239 case PTP_PIN_SETFUNC:
240 if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) {
241 err = -EFAULT;
242 break;
243 }
244 pin_index = pd.index;
245 if (pin_index >= ops->n_pins) {
246 err = -EINVAL;
247 break;
248 }
249 if (mutex_lock_interruptible(&ptp->pincfg_mux))
250 return -ERESTARTSYS;
251 err = ptp_set_pinfunc(ptp, pin_index, pd.func, pd.chan);
252 mutex_unlock(&ptp->pincfg_mux);
253 break;
254
129 default: 255 default:
130 err = -ENOTTY; 256 err = -ENOTTY;
131 break; 257 break;
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index a8319b266643..e25d2bc898e5 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -169,6 +169,7 @@ static void delete_ptp_clock(struct posix_clock *pc)
169 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 169 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
170 170
171 mutex_destroy(&ptp->tsevq_mux); 171 mutex_destroy(&ptp->tsevq_mux);
172 mutex_destroy(&ptp->pincfg_mux);
172 ida_simple_remove(&ptp_clocks_map, ptp->index); 173 ida_simple_remove(&ptp_clocks_map, ptp->index);
173 kfree(ptp); 174 kfree(ptp);
174} 175}
@@ -203,6 +204,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
203 ptp->index = index; 204 ptp->index = index;
204 spin_lock_init(&ptp->tsevq.lock); 205 spin_lock_init(&ptp->tsevq.lock);
205 mutex_init(&ptp->tsevq_mux); 206 mutex_init(&ptp->tsevq_mux);
207 mutex_init(&ptp->pincfg_mux);
206 init_waitqueue_head(&ptp->tsev_wq); 208 init_waitqueue_head(&ptp->tsev_wq);
207 209
208 /* Create a new device in our class. */ 210 /* Create a new device in our class. */
@@ -249,6 +251,7 @@ no_sysfs:
249 device_destroy(ptp_class, ptp->devid); 251 device_destroy(ptp_class, ptp->devid);
250no_device: 252no_device:
251 mutex_destroy(&ptp->tsevq_mux); 253 mutex_destroy(&ptp->tsevq_mux);
254 mutex_destroy(&ptp->pincfg_mux);
252no_slot: 255no_slot:
253 kfree(ptp); 256 kfree(ptp);
254no_memory: 257no_memory:
@@ -305,6 +308,26 @@ int ptp_clock_index(struct ptp_clock *ptp)
305} 308}
306EXPORT_SYMBOL(ptp_clock_index); 309EXPORT_SYMBOL(ptp_clock_index);
307 310
311int ptp_find_pin(struct ptp_clock *ptp,
312 enum ptp_pin_function func, unsigned int chan)
313{
314 struct ptp_pin_desc *pin = NULL;
315 int i;
316
317 mutex_lock(&ptp->pincfg_mux);
318 for (i = 0; i < ptp->info->n_pins; i++) {
319 if (ptp->info->pin_config[i].func == func &&
320 ptp->info->pin_config[i].chan == chan) {
321 pin = &ptp->info->pin_config[i];
322 break;
323 }
324 }
325 mutex_unlock(&ptp->pincfg_mux);
326
327 return pin ? i : -1;
328}
329EXPORT_SYMBOL(ptp_find_pin);
330
308/* module operations */ 331/* module operations */
309 332
310static void __exit ptp_exit(void) 333static void __exit ptp_exit(void)
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index df03f2e30ad9..b114a84c63c7 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -48,6 +48,7 @@ struct ptp_clock {
48 long dialed_frequency; /* remembers the frequency adjustment */ 48 long dialed_frequency; /* remembers the frequency adjustment */
49 struct timestamp_event_queue tsevq; /* simple fifo for time stamps */ 49 struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
50 struct mutex tsevq_mux; /* one process at a time reading the fifo */ 50 struct mutex tsevq_mux; /* one process at a time reading the fifo */
51 struct mutex pincfg_mux; /* protect concurrent info->pin_config access */
51 wait_queue_head_t tsev_wq; 52 wait_queue_head_t tsev_wq;
52 int defunct; /* tells readers to go away when clock is being removed */ 53 int defunct; /* tells readers to go away when clock is being removed */
53}; 54};
@@ -69,6 +70,10 @@ static inline int queue_cnt(struct timestamp_event_queue *q)
69 * see ptp_chardev.c 70 * see ptp_chardev.c
70 */ 71 */
71 72
73/* caller must hold pincfg_mux */
74int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin,
75 enum ptp_pin_function func, unsigned int chan);
76
72long ptp_ioctl(struct posix_clock *pc, 77long ptp_ioctl(struct posix_clock *pc,
73 unsigned int cmd, unsigned long arg); 78 unsigned int cmd, unsigned long arg);
74 79
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
index 38a993508327..0d8ff3fb84ba 100644
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h
@@ -49,7 +49,11 @@ struct ptp_clock_request {
49 * @n_alarm: The number of programmable alarms. 49 * @n_alarm: The number of programmable alarms.
50 * @n_ext_ts: The number of external time stamp channels. 50 * @n_ext_ts: The number of external time stamp channels.
51 * @n_per_out: The number of programmable periodic signals. 51 * @n_per_out: The number of programmable periodic signals.
52 * @n_pins: The number of programmable pins.
52 * @pps: Indicates whether the clock supports a PPS callback. 53 * @pps: Indicates whether the clock supports a PPS callback.
54 * @pin_config: Array of length 'n_pins'. If the number of
55 * programmable pins is nonzero, then drivers must
56 * allocate and initialize this array.
53 * 57 *
54 * clock operations 58 * clock operations
55 * 59 *
@@ -70,6 +74,18 @@ struct ptp_clock_request {
70 * parameter request: Desired resource to enable or disable. 74 * parameter request: Desired resource to enable or disable.
71 * parameter on: Caller passes one to enable or zero to disable. 75 * parameter on: Caller passes one to enable or zero to disable.
72 * 76 *
77 * @verify: Confirm that a pin can perform a given function. The PTP
78 * Hardware Clock subsystem maintains the 'pin_config'
79 * array on behalf of the drivers, but the PHC subsystem
80 * assumes that every pin can perform every function. This
81 * hook gives drivers a way of telling the core about
82 * limitations on specific pins. This function must return
83 * zero if the function can be assigned to this pin, and
84 * nonzero otherwise.
85 * parameter pin: index of the pin in question.
86 * parameter func: the desired function to use.
87 * parameter chan: the function channel index to use.
88 *
73 * Drivers should embed their ptp_clock_info within a private 89 * Drivers should embed their ptp_clock_info within a private
74 * structure, obtaining a reference to it using container_of(). 90 * structure, obtaining a reference to it using container_of().
75 * 91 *
@@ -83,13 +99,17 @@ struct ptp_clock_info {
83 int n_alarm; 99 int n_alarm;
84 int n_ext_ts; 100 int n_ext_ts;
85 int n_per_out; 101 int n_per_out;
102 int n_pins;
86 int pps; 103 int pps;
104 struct ptp_pin_desc *pin_config;
87 int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta); 105 int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);
88 int (*adjtime)(struct ptp_clock_info *ptp, s64 delta); 106 int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
89 int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts); 107 int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts);
90 int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts); 108 int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts);
91 int (*enable)(struct ptp_clock_info *ptp, 109 int (*enable)(struct ptp_clock_info *ptp,
92 struct ptp_clock_request *request, int on); 110 struct ptp_clock_request *request, int on);
111 int (*verify)(struct ptp_clock_info *ptp, unsigned int pin,
112 enum ptp_pin_function func, unsigned int chan);
93}; 113};
94 114
95struct ptp_clock; 115struct ptp_clock;
@@ -156,4 +176,17 @@ extern void ptp_clock_event(struct ptp_clock *ptp,
156 176
157extern int ptp_clock_index(struct ptp_clock *ptp); 177extern int ptp_clock_index(struct ptp_clock *ptp);
158 178
179/**
180 * ptp_find_pin() - obtain the pin index of a given auxiliary function
181 *
182 * @ptp: The clock obtained from ptp_clock_register().
183 * @func: One of the ptp_pin_function enumerated values.
184 * @chan: The particular functional channel to find.
185 * Return: Pin index in the range of zero to ptp_clock_caps.n_pins - 1,
186 * or -1 if the auxiliary function cannot be found.
187 */
188
189int ptp_find_pin(struct ptp_clock *ptp,
190 enum ptp_pin_function func, unsigned int chan);
191
159#endif 192#endif
diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h
index b65c834f83e9..f0b7bfe5da92 100644
--- a/include/uapi/linux/ptp_clock.h
+++ b/include/uapi/linux/ptp_clock.h
@@ -50,7 +50,8 @@ struct ptp_clock_caps {
50 int n_ext_ts; /* Number of external time stamp channels. */ 50 int n_ext_ts; /* Number of external time stamp channels. */
51 int n_per_out; /* Number of programmable periodic signals. */ 51 int n_per_out; /* Number of programmable periodic signals. */
52 int pps; /* Whether the clock supports a PPS callback. */ 52 int pps; /* Whether the clock supports a PPS callback. */
53 int rsv[15]; /* Reserved for future use. */ 53 int n_pins; /* Number of input/output pins. */
54 int rsv[14]; /* Reserved for future use. */
54}; 55};
55 56
56struct ptp_extts_request { 57struct ptp_extts_request {
@@ -80,6 +81,40 @@ struct ptp_sys_offset {
80 struct ptp_clock_time ts[2 * PTP_MAX_SAMPLES + 1]; 81 struct ptp_clock_time ts[2 * PTP_MAX_SAMPLES + 1];
81}; 82};
82 83
84enum ptp_pin_function {
85 PTP_PF_NONE,
86 PTP_PF_EXTTS,
87 PTP_PF_PEROUT,
88 PTP_PF_PHYSYNC,
89};
90
91struct ptp_pin_desc {
92 /*
93 * Hardware specific human readable pin name. This field is
94 * set by the kernel during the PTP_PIN_GETFUNC ioctl and is
95 * ignored for the PTP_PIN_SETFUNC ioctl.
96 */
97 char name[64];
98 /*
99 * Pin index in the range of zero to ptp_clock_caps.n_pins - 1.
100 */
101 unsigned int index;
102 /*
103 * Which of the PTP_PF_xxx functions to use on this pin.
104 */
105 unsigned int func;
106 /*
107 * The specific channel to use for this function.
108 * This corresponds to the 'index' field of the
109 * PTP_EXTTS_REQUEST and PTP_PEROUT_REQUEST ioctls.
110 */
111 unsigned int chan;
112 /*
113 * Reserved for future use.
114 */
115 unsigned int rsv[5];
116};
117
83#define PTP_CLK_MAGIC '=' 118#define PTP_CLK_MAGIC '='
84 119
85#define PTP_CLOCK_GETCAPS _IOR(PTP_CLK_MAGIC, 1, struct ptp_clock_caps) 120#define PTP_CLOCK_GETCAPS _IOR(PTP_CLK_MAGIC, 1, struct ptp_clock_caps)
@@ -87,6 +122,8 @@ struct ptp_sys_offset {
87#define PTP_PEROUT_REQUEST _IOW(PTP_CLK_MAGIC, 3, struct ptp_perout_request) 122#define PTP_PEROUT_REQUEST _IOW(PTP_CLK_MAGIC, 3, struct ptp_perout_request)
88#define PTP_ENABLE_PPS _IOW(PTP_CLK_MAGIC, 4, int) 123#define PTP_ENABLE_PPS _IOW(PTP_CLK_MAGIC, 4, int)
89#define PTP_SYS_OFFSET _IOW(PTP_CLK_MAGIC, 5, struct ptp_sys_offset) 124#define PTP_SYS_OFFSET _IOW(PTP_CLK_MAGIC, 5, struct ptp_sys_offset)
125#define PTP_PIN_GETFUNC _IOWR(PTP_CLK_MAGIC, 6, struct ptp_pin_desc)
126#define PTP_PIN_SETFUNC _IOW(PTP_CLK_MAGIC, 7, struct ptp_pin_desc)
90 127
91struct ptp_extts_event { 128struct ptp_extts_event {
92 struct ptp_clock_time t; /* Time event occured. */ 129 struct ptp_clock_time t; /* Time event occured. */