diff options
author | Richard Cochran <richardcochran@gmail.com> | 2014-03-20 22:21:52 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-03-21 14:21:13 -0400 |
commit | 6092315dfdec5185881605d15a0e200d6e90eb66 (patch) | |
tree | 8b018970396a2e2380fe51ed14d2f860ce6010e1 /drivers/ptp | |
parent | a85ae0e97879f51bccd8511668b07d346d98b3eb (diff) | |
download | linux-stable-6092315dfdec5185881605d15a0e200d6e90eb66.tar.gz linux-stable-6092315dfdec5185881605d15a0e200d6e90eb66.tar.bz2 linux-stable-6092315dfdec5185881605d15a0e200d6e90eb66.zip |
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 @@ #include "ptp_private.h" +static int ptp_disable_pinfunc(struct ptp_clock_info *ops, + enum ptp_pin_function func, unsigned int chan) +{ + struct ptp_clock_request rq; + int err = 0; + + memset(&rq, 0, sizeof(rq)); + + switch (func) { + case PTP_PF_NONE: + break; + case PTP_PF_EXTTS: + rq.type = PTP_CLK_REQ_EXTTS; + rq.extts.index = chan; + err = ops->enable(ops, &rq, 0); + break; + case PTP_PF_PEROUT: + rq.type = PTP_CLK_REQ_PEROUT; + rq.perout.index = chan; + err = ops->enable(ops, &rq, 0); + break; + case PTP_PF_PHYSYNC: + break; + default: + return -EINVAL; + } + + return err; +} + +int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, + enum ptp_pin_function func, unsigned int chan) +{ + struct ptp_clock_info *info = ptp->info; + struct ptp_pin_desc *pin1 = NULL, *pin2 = &info->pin_config[pin]; + unsigned int i; + + /* Check to see if any other pin previously had this function. */ + for (i = 0; i < info->n_pins; i++) { + if (info->pin_config[i].func == func && + info->pin_config[i].chan == chan) { + pin1 = &info->pin_config[i]; + break; + } + } + if (pin1 && i == pin) + return 0; + + /* Check the desired function and channel. */ + switch (func) { + case PTP_PF_NONE: + break; + case PTP_PF_EXTTS: + if (chan >= info->n_ext_ts) + return -EINVAL; + break; + case PTP_PF_PEROUT: + if (chan >= info->n_per_out) + return -EINVAL; + break; + case PTP_PF_PHYSYNC: + pr_err("sorry, cannot reassign the calibration pin\n"); + return -EINVAL; + default: + return -EINVAL; + } + + if (pin2->func == PTP_PF_PHYSYNC) { + pr_err("sorry, cannot reprogram the calibration pin\n"); + return -EINVAL; + } + + if (info->verify(info, pin, func, chan)) { + pr_err("driver cannot use function %u on pin %u\n", func, chan); + return -EOPNOTSUPP; + } + + /* Disable whatever function was previously assigned. */ + if (pin1) { + ptp_disable_pinfunc(info, func, chan); + pin1->func = PTP_PF_NONE; + pin1->chan = 0; + } + ptp_disable_pinfunc(info, pin2->func, pin2->chan); + pin2->func = func; + pin2->chan = chan; + + return 0; +} + int ptp_open(struct posix_clock *pc, fmode_t fmode) { return 0; @@ -35,12 +125,13 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) struct ptp_clock_caps caps; struct ptp_clock_request req; struct ptp_sys_offset *sysoff = NULL; + struct ptp_pin_desc pd; struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); struct ptp_clock_info *ops = ptp->info; struct ptp_clock_time *pct; struct timespec ts; int enable, err = 0; - unsigned int i; + unsigned int i, pin_index; switch (cmd) { @@ -51,6 +142,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) caps.n_ext_ts = ptp->info->n_ext_ts; caps.n_per_out = ptp->info->n_per_out; caps.pps = ptp->info->pps; + caps.n_pins = ptp->info->n_pins; if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) err = -EFAULT; break; @@ -126,6 +218,40 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) err = -EFAULT; break; + case PTP_PIN_GETFUNC: + if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) { + err = -EFAULT; + break; + } + pin_index = pd.index; + if (pin_index >= ops->n_pins) { + err = -EINVAL; + break; + } + if (mutex_lock_interruptible(&ptp->pincfg_mux)) + return -ERESTARTSYS; + pd = ops->pin_config[pin_index]; + mutex_unlock(&ptp->pincfg_mux); + if (!err && copy_to_user((void __user *)arg, &pd, sizeof(pd))) + err = -EFAULT; + break; + + case PTP_PIN_SETFUNC: + if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) { + err = -EFAULT; + break; + } + pin_index = pd.index; + if (pin_index >= ops->n_pins) { + err = -EINVAL; + break; + } + if (mutex_lock_interruptible(&ptp->pincfg_mux)) + return -ERESTARTSYS; + err = ptp_set_pinfunc(ptp, pin_index, pd.func, pd.chan); + mutex_unlock(&ptp->pincfg_mux); + break; + default: err = -ENOTTY; 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) struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); mutex_destroy(&ptp->tsevq_mux); + mutex_destroy(&ptp->pincfg_mux); ida_simple_remove(&ptp_clocks_map, ptp->index); kfree(ptp); } @@ -203,6 +204,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, ptp->index = index; spin_lock_init(&ptp->tsevq.lock); mutex_init(&ptp->tsevq_mux); + mutex_init(&ptp->pincfg_mux); init_waitqueue_head(&ptp->tsev_wq); /* Create a new device in our class. */ @@ -249,6 +251,7 @@ no_sysfs: device_destroy(ptp_class, ptp->devid); no_device: mutex_destroy(&ptp->tsevq_mux); + mutex_destroy(&ptp->pincfg_mux); no_slot: kfree(ptp); no_memory: @@ -305,6 +308,26 @@ int ptp_clock_index(struct ptp_clock *ptp) } EXPORT_SYMBOL(ptp_clock_index); +int ptp_find_pin(struct ptp_clock *ptp, + enum ptp_pin_function func, unsigned int chan) +{ + struct ptp_pin_desc *pin = NULL; + int i; + + mutex_lock(&ptp->pincfg_mux); + for (i = 0; i < ptp->info->n_pins; i++) { + if (ptp->info->pin_config[i].func == func && + ptp->info->pin_config[i].chan == chan) { + pin = &ptp->info->pin_config[i]; + break; + } + } + mutex_unlock(&ptp->pincfg_mux); + + return pin ? i : -1; +} +EXPORT_SYMBOL(ptp_find_pin); + /* module operations */ 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 { long dialed_frequency; /* remembers the frequency adjustment */ struct timestamp_event_queue tsevq; /* simple fifo for time stamps */ struct mutex tsevq_mux; /* one process at a time reading the fifo */ + struct mutex pincfg_mux; /* protect concurrent info->pin_config access */ wait_queue_head_t tsev_wq; int defunct; /* tells readers to go away when clock is being removed */ }; @@ -69,6 +70,10 @@ static inline int queue_cnt(struct timestamp_event_queue *q) * see ptp_chardev.c */ +/* caller must hold pincfg_mux */ +int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, + enum ptp_pin_function func, unsigned int chan); + long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg); |