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>
This commit is contained in:

committato da
David S. Miller

parent
a85ae0e978
commit
6092315dfd
@@ -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;
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
||||
|
Fai riferimento in un nuovo problema
Block a user