diff options
author | Richard Cochran <richardcochran@gmail.com> | 2014-03-20 17:21:52 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-03-21 14:21:13 -0400 |
commit | 6092315dfdec5185881605d15a0e200d6e90eb66 (patch) | |
tree | 8b018970396a2e2380fe51ed14d2f860ce6010e1 /drivers/ptp | |
parent | a85ae0e97879f51bccd8511668b07d346d98b3eb (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>
Diffstat (limited to 'drivers/ptp')
-rw-r--r-- | drivers/ptp/ptp_chardev.c | 128 | ||||
-rw-r--r-- | drivers/ptp/ptp_clock.c | 23 | ||||
-rw-r--r-- | drivers/ptp/ptp_private.h | 5 |
3 files changed, 155 insertions, 1 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 | ||
28 | static 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 | |||
58 | int 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 | |||
28 | int ptp_open(struct posix_clock *pc, fmode_t fmode) | 118 | int 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); |
250 | no_device: | 252 | no_device: |
251 | mutex_destroy(&ptp->tsevq_mux); | 253 | mutex_destroy(&ptp->tsevq_mux); |
254 | mutex_destroy(&ptp->pincfg_mux); | ||
252 | no_slot: | 255 | no_slot: |
253 | kfree(ptp); | 256 | kfree(ptp); |
254 | no_memory: | 257 | no_memory: |
@@ -305,6 +308,26 @@ int ptp_clock_index(struct ptp_clock *ptp) | |||
305 | } | 308 | } |
306 | EXPORT_SYMBOL(ptp_clock_index); | 309 | EXPORT_SYMBOL(ptp_clock_index); |
307 | 310 | ||
311 | int 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 | } | ||
329 | EXPORT_SYMBOL(ptp_find_pin); | ||
330 | |||
308 | /* module operations */ | 331 | /* module operations */ |
309 | 332 | ||
310 | static void __exit ptp_exit(void) | 333 | static 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 */ | ||
74 | int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, | ||
75 | enum ptp_pin_function func, unsigned int chan); | ||
76 | |||
72 | long ptp_ioctl(struct posix_clock *pc, | 77 | long ptp_ioctl(struct posix_clock *pc, |
73 | unsigned int cmd, unsigned long arg); | 78 | unsigned int cmd, unsigned long arg); |
74 | 79 | ||