Skip to contentSkip to conversation

ptp: dynamic pin control

RFC PATCH Superseded Richard Cochran 3 participants 21 messages Mar 8, 2014 – Mar 12, 2014

Subsystems (14)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Richard Cochran

author maintainer
#0 lore raw

cover ptp: dynamic pin control

This patch series introduces a way of changing the auxiliary PTP Hardware Clock functions (periodic output signals and time stamping external signals) at run time. In the past on the netdev list, we have discussed other ways to handle this, such as module parameters and ethtool. This series implements a new PHC ioctl because that is the most natural way. Users already activate the auxiliary functions via the ioctls. The sysfs interface has also been expanded so that the pin configuration can be programmed using shell scripts.

The first patch adds the new ioctls. The PHC subsystem does most of the work of maintaining the function-to-pin mapping. Drivers will only need to allocate and initialize a pin configuration table and also provide a new method that validates a particular assignment.

Patches 5 and 6 just clean up a couple of issues in the phyter driver, and the remaining patches actually hook the phyter's pins into the new system.

Comments and questions are most welcome.

Thanks,
Richard

Richard Cochran (9):
ptp: introduce programmable pins.
ptp: add the pin GET/SETFUNC ioctls to the testptp program.
ptp: expose the programmable pins via sysfs
ptp: drivers: set the number of programmable pins.
dp83640: trivial fixes
dp83640: correct the periodic output frequency
dp83640: implement programmable pin functions.
dp83640: let external input pins from the module parameters be
defaults.
dp83640: let the periodic pin from the module parameter be a default.

 Documentation/ABI/testing/sysfs-ptp              |   20 ++++
 Documentation/ptp/testptp.c                      |   58 +++++++++-
 drivers/net/ethernet/adi/bfin_mac.c              |    1 +
 drivers/net/ethernet/broadcom/tg3.c              |    1 +
 drivers/net/ethernet/freescale/fec_ptp.c         |    1 +
 drivers/net/ethernet/freescale/gianfar_ptp.c     |    1 +
 drivers/net/ethernet/intel/e1000e/ptp.c          |    1 +
 drivers/net/ethernet/mellanox/mlx4/en_clock.c    |    1 +
 drivers/net/ethernet/sfc/ptp.c                   |    1 +
 drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c |    1 +
 drivers/net/ethernet/ti/cpts.c                   |    1 +
 drivers/net/ethernet/tile/tilegx.c               |    1 +
 drivers/net/phy/dp83640.c                        |   93 ++++++++++++---
 drivers/ptp/ptp_chardev.c                        |  133 +++++++++++++++++++++-
 drivers/ptp/ptp_clock.c                          |   23 ++++
 drivers/ptp/ptp_ixp46x.c                         |    1 +
 drivers/ptp/ptp_pch.c                            |    1 +
 drivers/ptp/ptp_private.h                        |    7 ++
 drivers/ptp/ptp_sysfs.c                          |  109 ++++++++++++++++++
 include/linux/ptp_clock_kernel.h                 |   33 ++++++
 include/uapi/linux/ptp_clock.h                   |   39 ++++++-
 21 files changed, 507 insertions(+), 20 deletions(-)
Signature · 1 line
1.7.10.4
author maintainer
#1 lore raw

RFC PATCH 1/9 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.

---
 drivers/ptp/ptp_chardev.c        |  133 +++++++++++++++++++++++++++++++++++++-
 drivers/ptp/ptp_clock.c          |   23 +++++++
 drivers/ptp/ptp_private.h        |    4 ++
 include/linux/ptp_clock_kernel.h |   33 ++++++++++
 include/uapi/linux/ptp_clock.h   |   39 ++++++++++-
 5 files changed, 230 insertions(+), 2 deletions(-)
drivers/ptp/ptp_chardev.c +132 -1
index 34a0c60..dc13d1e 100644
@@ -25,6 +25,104 @@
 
 #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_setpin(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. */
+	if (mutex_lock_interruptible(&ptp->pincfg_mux))
+		return -ERESTARTSYS;
+	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;
+		}
+	}
+	mutex_unlock(&ptp->pincfg_mux);
+	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 (pin1 && pin1->func == PTP_PF_PHYSYNC) {
+		pr_err("sorry, cannot reprogram the calibration pin\n");
+		return -EINVAL;
+	}
+
+	if (info->setpin(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. */
+	ptp_disable_pinfunc(info, pin2->func, pin2->chan);
+	if (pin1)
+		ptp_disable_pinfunc(info, func, chan);
+
+	if (mutex_lock_interruptible(&ptp->pincfg_mux))
+		return -ERESTARTSYS;
+	pin2->func = func;
+	pin2->chan = chan;
+	if (pin1) {
+		pin1->func = PTP_PF_NONE;
+		pin1->chan = 0;
+	}
+	mutex_unlock(&ptp->pincfg_mux);
+
+	return 0;
+}
+
 int ptp_open(struct posix_clock *pc, fmode_t fmode)
 {
 	return 0;
@@ -35,12 +133,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 +150,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 +226,37 @@ 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;
+		}
+		err = ptp_setpin(ptp, pin_index, pd.func, pd.chan);
+		break;
+
 	default:
 		err = -ENOTTY;
 		break;
drivers/ptp/ptp_clock.c +23 -0
index a8319b2..3105d1d 100644
@@ -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 ? pin - ptp->info->pin_config : -1;
+}
+EXPORT_SYMBOL(ptp_find_pin);
+
 /* module operations */
 
 static void __exit ptp_exit(void)
drivers/ptp/ptp_private.h +4 -0
index df03f2e..253e942 100644
@@ -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,9 @@ static inline int queue_cnt(struct timestamp_event_queue *q)
  * see ptp_chardev.c
  */
 
+int ptp_setpin(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);
 
include/linux/ptp_clock_kernel.h +33 -0
index 38a9935..04b0d42 100644
@@ -49,7 +49,11 @@ struct ptp_clock_request {
  * @n_alarm:   The number of programmable alarms.
  * @n_ext_ts:  The number of external time stamp channels.
  * @n_per_out: The number of programmable periodic signals.
+ * @n_pins:    The number of programmable pins.
  * @pps:       Indicates whether the clock supports a PPS callback.
+ * @pin_config: Array of length 'n_pins'. If the number of
+ *              programmable pins is nonzero, then drivers must
+ *              allocate and initialize this array.
  *
  * clock operations
  *
@@ -70,6 +74,18 @@ struct ptp_clock_request {
  *            parameter request: Desired resource to enable or disable.
  *            parameter on: Caller passes one to enable or zero to disable.
  *
+ * @setpin:   Confirm that a pin can perform a given function. The PTP
+ *            Hardware Clock subsystem maintains the 'pin_config'
+ *            array on behalf of the drivers, but the PHC subsystem
+ *            assumes that every pin can perform every function. This
+ *            hook gives drivers a way of telling the core about
+ *            limitations on specific pins. This function must return
+ *            zero if the function can be assigned to this pin, and
+ *            nonzero otherwise.
+ *            parameter pin: index of the pin in question.
+ *            parameter func: the desired function to use.
+ *            parameter chan: the function channel index to use.
+ *
  * Drivers should embed their ptp_clock_info within a private
  * structure, obtaining a reference to it using container_of().
  *
@@ -83,13 +99,17 @@ struct ptp_clock_info {
 	int n_alarm;
 	int n_ext_ts;
 	int n_per_out;
+	int n_pins;
 	int pps;
+	struct ptp_pin_desc *pin_config;
 	int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);
 	int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
 	int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts);
 	int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts);
 	int (*enable)(struct ptp_clock_info *ptp,
 		      struct ptp_clock_request *request, int on);
+	int (*setpin)(struct ptp_clock_info *ptp, unsigned int pin,
+		      enum ptp_pin_function func, unsigned int chan);
 };
 
 struct ptp_clock;
@@ -156,4 +176,17 @@ extern void ptp_clock_event(struct ptp_clock *ptp,
 
 extern int ptp_clock_index(struct ptp_clock *ptp);
 
+/**
+ * ptp_find_pin() - obtain the pin index of a given auxiliary function
+ *
+ * @ptp:    The clock obtained from ptp_clock_register().
+ * @func:   One of the ptp_pin_function enumerated values.
+ * @chan:   The particular functional channel to find.
+ * Return:  Pin index in the range of zero to ptp_clock_caps.n_pins - 1,
+ *          or -1 if the auxiliary function cannot be found.
+ */
+
+int ptp_find_pin(struct ptp_clock *ptp,
+		 enum ptp_pin_function func, unsigned int chan);
+
 #endif
include/uapi/linux/ptp_clock.h +38 -1
index b65c834..f0b7bfe 100644
@@ -50,7 +50,8 @@ struct ptp_clock_caps {
 	int n_ext_ts;  /* Number of external time stamp channels. */
 	int n_per_out; /* Number of programmable periodic signals. */
 	int pps;       /* Whether the clock supports a PPS callback. */
-	int rsv[15];   /* Reserved for future use. */
+	int n_pins;    /* Number of input/output pins. */
+	int rsv[14];   /* Reserved for future use. */
 };
 
 struct ptp_extts_request {
@@ -80,6 +81,40 @@ struct ptp_sys_offset {
 	struct ptp_clock_time ts[2 * PTP_MAX_SAMPLES + 1];
 };
 
+enum ptp_pin_function {
+	PTP_PF_NONE,
+	PTP_PF_EXTTS,
+	PTP_PF_PEROUT,
+	PTP_PF_PHYSYNC,
+};
+
+struct ptp_pin_desc {
+	/*
+	 * Hardware specific human readable pin name. This field is
+	 * set by the kernel during the PTP_PIN_GETFUNC ioctl and is
+	 * ignored for the PTP_PIN_SETFUNC ioctl.
+	 */
+	char name[64];
+	/*
+	 * Pin index in the range of zero to ptp_clock_caps.n_pins - 1.
+	 */
+	unsigned int index;
+	/*
+	 * Which of the PTP_PF_xxx functions to use on this pin.
+	 */
+	unsigned int func;
+	/*
+	 * The specific channel to use for this function.
+	 * This corresponds to the 'index' field of the
+	 * PTP_EXTTS_REQUEST and PTP_PEROUT_REQUEST ioctls.
+	 */
+	unsigned int chan;
+	/*
+	 * Reserved for future use.
+	 */
+	unsigned int rsv[5];
+};
+
 #define PTP_CLK_MAGIC '='
 
 #define PTP_CLOCK_GETCAPS  _IOR(PTP_CLK_MAGIC, 1, struct ptp_clock_caps)
@@ -87,6 +122,8 @@ struct ptp_sys_offset {
 #define PTP_PEROUT_REQUEST _IOW(PTP_CLK_MAGIC, 3, struct ptp_perout_request)
 #define PTP_ENABLE_PPS     _IOW(PTP_CLK_MAGIC, 4, int)
 #define PTP_SYS_OFFSET     _IOW(PTP_CLK_MAGIC, 5, struct ptp_sys_offset)
+#define PTP_PIN_GETFUNC    _IOWR(PTP_CLK_MAGIC, 6, struct ptp_pin_desc)
+#define PTP_PIN_SETFUNC    _IOW(PTP_CLK_MAGIC, 7, struct ptp_pin_desc)
 
 struct ptp_extts_event {
 	struct ptp_clock_time t; /* Time event occured. */
Signature · 1 line
1.7.10.4
#2 lore raw

Re: [PATCH RFC net-next v1 1/9] ptp: introduce programmable pins.

drivers/ptp/ptp_chardev.c line 59

Quoted 2 lines · Richard Cochran
drivers/ptp/ptp_chardev.c +2 -0
+int ptp_setpin(struct ptp_clock *ptp, unsigned int pin,
+               enum ptp_pin_function func, unsigned int chan)

From the name I would think that this would set the state of the pin. Rename to ptp_set_pinfunc?

Quoted 8 lines
drivers/ptp/ptp_clock.c +8 -0
+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++) {

drivers/ptp/ptp_clock.c int ptp_clock_index(struct ptp_clock *ptp) · line 328

Quoted 10 lines
drivers/ptp/ptp_clock.c +10 -0
+		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 ? pin - ptp->info->pin_config : -1;
+}

Maybe replace 'pin - ptp->info->pin_config' with 'i'?

drivers/ptp/ptp_private.h struct ptp_clock { · line 51

Quoted 6 lines

+++ b/drivers/ptp/ptp_private.h

drivers/ptp/ptp_private.h +1 -0
@@ -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 */

Line over 80 chars.

include/linux/ptp_clock_kernel.h struct ptp_clock_request { · line 84

Quoted 8 lines
include/linux/ptp_clock_kernel.h +8 -0
+ * @setpin:   Confirm that a pin can perform a given function. The PTP
+ *            Hardware Clock subsystem maintains the 'pin_config'
+ *            array on behalf of the drivers, but the PHC subsystem
+ *            assumes that every pin can perform every function. This
+ *            hook gives drivers a way of telling the core about
+ *            limitations on specific pins. This function must return
+ *            zero if the function can be assigned to this pin, and
+ *            nonzero otherwise.

The function does not actually set the pin. Rename to verify_pinfunc?

Stefan

author maintainer
#3 lore raw

Re: [PATCH RFC net-next v1 1/9] ptp: introduce programmable pins.

Quoted 14 lines · Stefan
Quoted 12 lines · Richard Cochran
drivers/ptp/ptp_clock.c +12 -0
+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;
Quoted 8 lines

drivers/ptp/ptp_clock.c int ptp_clock_index(struct ptp_clock *ptp) · line 328

Quoted 6 lines
drivers/ptp/ptp_clock.c +6 -0
+		}
+	}
+	mutex_unlock(&ptp->pincfg_mux);
+
+	return pin ? pin - ptp->info->pin_config : -1;
+}

Maybe replace 'pin - ptp->info->pin_config' with 'i'?

Yes, of course.

Thanks,
Richard

#4 lore raw

Re: [PATCH RFC net-next v1 1/9] ptp: introduce programmable pins.

In reply to Richard Cochran

Hi Richard,

Quoted 19 lines · --On March 08, 2014 20:42 +0100 Richard Cochran <[email protected]>…

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.

---
 drivers/ptp/ptp_chardev.c        |  133
+++++++++++++++++++++++++++++++++++++-  drivers/ptp/ptp_clock.c
|   23 +++++++
 drivers/ptp/ptp_private.h        |    4 ++
 include/linux/ptp_clock_kernel.h |   33 ++++++++++
 include/uapi/linux/ptp_clock.h   |   39 ++++++++++-
 5 files changed, 230 insertions(+), 2 deletions(-)
drivers/ptp/ptp_chardev.c +0 -0
index 34a0c60..dc13d1e 100644

[...]

Quoted 33 lines
drivers/ptp/ptp_chardev.c +33 -0
+int ptp_setpin(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. */
+	if (mutex_lock_interruptible(&ptp->pincfg_mux))
+		return -ERESTARTSYS;
+	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;
+		}
+	}
+	mutex_unlock(&ptp->pincfg_mux);
+	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;

drivers/ptp/ptp_chardev.c line 100

Quoted 10 lines
drivers/ptp/ptp_chardev.c +10 -0
+	case PTP_PF_PHYSYNC:
+		pr_err("sorry, cannot reassign the calibration pin\n");
+		return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+
+	if (pin1 && pin1->func == PTP_PF_PHYSYNC) {
+		pr_err("sorry, cannot reprogram the calibration pin\n");
+		return -EINVAL;
           ^^^^

Will this ever happen? pin1 && pin1->func == PTP_PF_PHYSYNC means that func == PTP_PF_PHYSYNC, but in this case you already return -EINVAL a few lines above.

Christian

author maintainer
#5 lore raw

Re: [PATCH RFC net-next v1 1/9] ptp: introduce programmable pins.

Quoted 8 lines · Christian Riesch

drivers/ptp/ptp_chardev.c line 100

Quoted 3 lines
drivers/ptp/ptp_chardev.c +3 -0
+	if (pin1 && pin1->func == PTP_PF_PHYSYNC) {
+		pr_err("sorry, cannot reprogram the calibration pin\n");
+		return -EINVAL;
           ^^^^

Will this ever happen? pin1 && pin1->func == PTP_PF_PHYSYNC means that func == PTP_PF_PHYSYNC, but in this case you already return -EINVAL a few lines above.

This a bug. I really meant to test (pin2->func == PTP_PF_PHYSYNC) to prevent clobbering the calibration function with some other function. I'll fix it for v2.

Thanks,
Richard

author maintainer
#6 lore raw

RFC PATCH 2/9 ptp: add the pin GET/SETFUNC ioctls to the testptp program.

In reply to Richard Cochran

This patch adds a option to the test program that lists the programmable pins on a PTP Hardware Clock device, assuming there are any such pins. A second option lets the user reprogram the auxiliary function of a single pin.

---
 Documentation/ptp/testptp.c |   58 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 55 insertions(+), 3 deletions(-)
Documentation/ptp/testptp.c +55 -3
index 4aba043..e9eaee6 100644
@@ -120,6 +120,13 @@ static void usage(char *progname)
 		" -i val     index for event/trigger\n"
 		" -k val     measure the time offset between system and phc clock\n"
 		"            for 'val' times (Maximum 25)\n"
+		" -l         list the current pin configuration\n"
+		" -L pin,val configure pin index 'pin' with function 'val'\n"
+		"            the channel index is taken from the '-i' option\n"
+		"            'val' specifies the auxiliary function:\n"
+		"            0 - none\n"
+		"            1 - external time stamp\n"
+		"            2 - periodic output\n"
 		" -p val     enable output with a period of 'val' nanoseconds\n"
 		" -P val     enable or disable (val=1|0) the system clock PPS\n"
 		" -s         set the ptp clock time from the system time\n"
@@ -134,6 +141,7 @@ int main(int argc, char *argv[])
 	struct ptp_extts_event event;
 	struct ptp_extts_request extts_request;
 	struct ptp_perout_request perout_request;
+	struct ptp_pin_desc desc;
 	struct timespec ts;
 	struct timex tx;
 
@@ -156,11 +164,13 @@ int main(int argc, char *argv[])
 	int extts = 0;
 	int gettime = 0;
 	int index = 0;
+	int list_pins = 0;
 	int oneshot = 0;
 	int pct_offset = 0;
 	int n_samples = 0;
 	int periodic = 0;
 	int perout = -1;
+	int pin_index = -1, pin_func;
 	int pps = -1;
 	int settime = 0;
 
@@ -169,7 +179,7 @@ int main(int argc, char *argv[])
 
 	progname = strrchr(argv[0], '/');
 	progname = progname ? 1+progname : argv[0];
-	while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:p:P:sSt:v"))) {
+	while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:lL:p:P:sSt:v"))) {
 		switch (c) {
 		case 'a':
 			oneshot = atoi(optarg);
@@ -199,6 +209,16 @@ int main(int argc, char *argv[])
 			pct_offset = 1;
 			n_samples = atoi(optarg);
 			break;
+		case 'l':
+			list_pins = 1;
+			break;
+		case 'L':
+			cnt = sscanf(optarg, "%d,%d", &pin_index, &pin_func);
+			if (cnt != 2) {
+				usage(progname);
+				return -1;
+			}
+			break;
 		case 'p':
 			perout = atoi(optarg);
 			break;
@@ -245,12 +265,14 @@ int main(int argc, char *argv[])
 			       "  %d programmable alarms\n"
 			       "  %d external time stamp channels\n"
 			       "  %d programmable periodic signals\n"
-			       "  %d pulse per second\n",
+			       "  %d pulse per second\n"
+			       "  %d programmable pins\n",
 			       caps.max_adj,
 			       caps.n_alarm,
 			       caps.n_ext_ts,
 			       caps.n_per_out,
-			       caps.pps);
+			       caps.pps,
+			       caps.n_pins);
 		}
 	}
 
@@ -331,6 +353,24 @@ int main(int argc, char *argv[])
 		}
 	}
 
+	if (list_pins) {
+		int n_pins = 0;
+		if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
+			perror("PTP_CLOCK_GETCAPS");
+		} else {
+			n_pins = caps.n_pins;
+		}
+		for (i = 0; i < n_pins; i++) {
+			desc.index = i;
+			if (ioctl(fd, PTP_PIN_GETFUNC, &desc)) {
+				perror("PTP_PIN_GETFUNC");
+				break;
+			}
+			printf("name %s index %u func %u chan %u\n",
+			       desc.name, desc.index, desc.func, desc.chan);
+		}
+	}
+
 	if (oneshot) {
 		install_handler(SIGALRM, handle_alarm);
 		/* Create a timer. */
@@ -392,6 +432,18 @@ int main(int argc, char *argv[])
 		}
 	}
 
+	if (pin_index >= 0) {
+		memset(&desc, 0, sizeof(desc));
+		desc.index = pin_index;
+		desc.func = pin_func;
+		desc.chan = index;
+		if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) {
+			perror("PTP_PIN_SETFUNC");
+		} else {
+			puts("set pin function okay");
+		}
+	}
+
 	if (pps != -1) {
 		int enable = pps ? 1 : 0;
 		if (ioctl(fd, PTP_ENABLE_PPS, enable)) {
Signature · 1 line
1.7.10.4
author maintainer
#7 lore raw

RFC PATCH 3/9 ptp: expose the programmable pins via sysfs

In reply to Richard Cochran

This patch adds the sysfs hooks needed in order to get and set the programmable pin settings.

---
 Documentation/ABI/testing/sysfs-ptp |   20 +++++++
 drivers/ptp/ptp_private.h           |    3 +
 drivers/ptp/ptp_sysfs.c             |  109 +++++++++++++++++++++++++++++++++++
 3 files changed, 132 insertions(+)
Documentation/ABI/testing/sysfs-ptp +20 -0
index 05aeedf..44806a6 100644
@@ -54,6 +54,26 @@ Description:
 		This file contains the number of programmable periodic
 		output channels offered by the PTP hardware clock.
 
+What:		/sys/class/ptp/ptpN/n_pins
+Date:		March 2014
+Contact:	Richard Cochran <[email protected]>
+Description:
+		This file contains the number of programmable pins
+		offered by the PTP hardware clock.
+
+What:		/sys/class/ptp/ptpN/pins
+Date:		March 2014
+Contact:	Richard Cochran <[email protected]>
+Description:
+		This directory contains one file for each programmable
+		pin offered by the PTP hardware clock. The file name
+		is the hardware dependent pin name. Reading from this
+		file produces two numbers, the assigned function (see
+		the PTP_PF_ enumeration values in linux/ptp_clock.h)
+		and the channel number. The function and channel
+		assignment may be changed by two writing numbers into
+		the file.
+
 What:		/sys/class/ptp/ptpN/pps_avaiable
 Date:		September 2010
 Contact:	Richard Cochran <[email protected]>
drivers/ptp/ptp_private.h +3 -0
index 253e942..fb7c6b1 100644
@@ -51,6 +51,9 @@ struct ptp_clock {
 	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 */
+	struct device_attribute *pin_dev_attr;
+	struct attribute **pin_attr;
+	struct attribute_group pin_attr_group;
 };
 
 /*
drivers/ptp/ptp_sysfs.c +109 -0

146 lines not on this page · raw message

Signature · 1 line
1.7.10.4
author maintainer
#8 lore raw

RFC PATCH 4/9 ptp: drivers: set the number of programmable pins.

In reply to Richard Cochran

This patch updates the many PTP Hardware Clock drivers with the newly introduced field that advertises the number of programmable pins. Some of these devices do have programmable pins, but the implementation will have to wait for follow on patches.

---
 drivers/net/ethernet/adi/bfin_mac.c              |    1 +
 drivers/net/ethernet/broadcom/tg3.c              |    1 +
 drivers/net/ethernet/freescale/fec_ptp.c         |    1 +
 drivers/net/ethernet/freescale/gianfar_ptp.c     |    1 +
 drivers/net/ethernet/intel/e1000e/ptp.c          |    1 +
 drivers/net/ethernet/mellanox/mlx4/en_clock.c    |    1 +
 drivers/net/ethernet/sfc/ptp.c                   |    1 +
 drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c |    1 +
 drivers/net/ethernet/ti/cpts.c                   |    1 +
 drivers/net/ethernet/tile/tilegx.c               |    1 +
 drivers/ptp/ptp_ixp46x.c                         |    1 +
 drivers/ptp/ptp_pch.c                            |    1 +
 12 files changed, 12 insertions(+)
drivers/net/ethernet/adi/bfin_mac.c +1 -0
index c0f68dc..83a8cdb 100644
@@ -1040,6 +1040,7 @@ static struct ptp_clock_info bfin_ptp_caps = {
 	.n_alarm	= 0,
 	.n_ext_ts	= 0,
 	.n_per_out	= 0,
+	.n_pins		= 0,
 	.pps		= 0,
 	.adjfreq	= bfin_ptp_adjfreq,
 	.adjtime	= bfin_ptp_adjtime,
drivers/net/ethernet/broadcom/tg3.c +1 -0
index e12735f..5130c58 100644
@@ -6322,6 +6322,7 @@ static const struct ptp_clock_info tg3_ptp_caps = {
 	.n_alarm	= 0,
 	.n_ext_ts	= 0,
 	.n_per_out	= 1,
+	.n_pins		= 0,
 	.pps		= 0,
 	.adjfreq	= tg3_ptp_adjfreq,
 	.adjtime	= tg3_ptp_adjtime,
drivers/net/ethernet/freescale/fec_ptp.c +1 -0
index 89ccb5b..82386b2 100644
@@ -372,6 +372,7 @@ void fec_ptp_init(struct platform_device *pdev)
 	fep->ptp_caps.n_alarm = 0;
 	fep->ptp_caps.n_ext_ts = 0;
 	fep->ptp_caps.n_per_out = 0;
+	fep->ptp_caps.n_pins = 0;
 	fep->ptp_caps.pps = 0;
 	fep->ptp_caps.adjfreq = fec_ptp_adjfreq;
 	fep->ptp_caps.adjtime = fec_ptp_adjtime;
drivers/net/ethernet/freescale/gianfar_ptp.c +1 -0
index abc28da..bb56800 100644
@@ -414,6 +414,7 @@ static struct ptp_clock_info ptp_gianfar_caps = {
 	.n_alarm	= 0,
 	.n_ext_ts	= N_EXT_TS,
 	.n_per_out	= 0,
+	.n_pins		= 0,
 	.pps		= 1,
 	.adjfreq	= ptp_gianfar_adjfreq,
 	.adjtime	= ptp_gianfar_adjtime,
drivers/net/ethernet/intel/e1000e/ptp.c +1 -0
index 065f8c8..299ab50 100644
@@ -191,6 +191,7 @@ static const struct ptp_clock_info e1000e_ptp_clock_info = {
 	.n_alarm	= 0,
 	.n_ext_ts	= 0,
 	.n_per_out	= 0,
+	.n_pins		= 0,
 	.pps		= 0,
 	.adjfreq	= e1000e_phc_adjfreq,
 	.adjtime	= e1000e_phc_adjtime,
drivers/net/ethernet/mellanox/mlx4/en_clock.c +1 -0
index abaf6bb..57dda95 100644
@@ -276,6 +276,7 @@ static const struct ptp_clock_info mlx4_en_ptp_clock_info = {
 	.n_alarm	= 0,
 	.n_ext_ts	= 0,
 	.n_per_out	= 0,
+	.n_pins		= 0,
 	.pps		= 0,
 	.adjfreq	= mlx4_en_phc_adjfreq,
 	.adjtime	= mlx4_en_phc_adjtime,
drivers/net/ethernet/sfc/ptp.c +1 -0
index 722344f..6b861e3 100644
@@ -1194,6 +1194,7 @@ static const struct ptp_clock_info efx_phc_clock_info = {
 	.n_alarm	= 0,
 	.n_ext_ts	= 0,
 	.n_per_out	= 0,
+	.n_pins		= 0,
 	.pps		= 1,
 	.adjfreq	= efx_phc_adjfreq,
 	.adjtime	= efx_phc_adjtime,
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +1 -0
index 7680581..b7ad356 100644
@@ -164,6 +164,7 @@ static struct ptp_clock_info stmmac_ptp_clock_ops = {
 	.n_alarm = 0,
 	.n_ext_ts = 0,
 	.n_per_out = 0,
+	.n_pins = 0,
 	.pps = 0,
 	.adjfreq = stmmac_adjust_freq,
 	.adjtime = stmmac_adjust_time,
drivers/net/ethernet/ti/cpts.c +1 -0
index 8c351f1..372cb19 100644
@@ -217,6 +217,7 @@ static struct ptp_clock_info cpts_info = {
 	.name		= "CTPS timer",
 	.max_adj	= 1000000,
 	.n_ext_ts	= 0,
+	.n_pins		= 0,
 	.pps		= 0,
 	.adjfreq	= cpts_ptp_adjfreq,
 	.adjtime	= cpts_ptp_adjtime,
drivers/net/ethernet/tile/tilegx.c +1 -0
index 17503da..298d1c3 100644
@@ -870,6 +870,7 @@ static struct ptp_clock_info ptp_mpipe_caps = {
 	.name		= "mPIPE clock",
 	.max_adj	= 999999999,
 	.n_ext_ts	= 0,
+	.n_pins		= 0,
 	.pps		= 0,
 	.adjfreq	= ptp_mpipe_adjfreq,
 	.adjtime	= ptp_mpipe_adjtime,
drivers/ptp/ptp_ixp46x.c +1 -0
index 4a08727..604d340 100644
@@ -244,6 +244,7 @@ static struct ptp_clock_info ptp_ixp_caps = {
 	.name		= "IXP46X timer",
 	.max_adj	= 66666655,
 	.n_ext_ts	= N_EXT_TS,
+	.n_pins		= 0,
 	.pps		= 0,
 	.adjfreq	= ptp_ixp_adjfreq,
 	.adjtime	= ptp_ixp_adjtime,
drivers/ptp/ptp_pch.c +1 -0
index 71a2559..90a1063 100644
@@ -514,6 +514,7 @@ static struct ptp_clock_info ptp_pch_caps = {
 	.name		= "PCH timer",
 	.max_adj	= 50000000,
 	.n_ext_ts	= N_EXT_TS,
+	.n_pins		= 0,
 	.pps		= 0,
 	.adjfreq	= ptp_pch_adjfreq,
 	.adjtime	= ptp_pch_adjtime,
Signature · 1 line
1.7.10.4
author maintainer
#9 lore raw

RFC PATCH 5/9 dp83640: trivial fixes

In reply to Richard Cochran

This patch cleans up the input checking code on the external time stamp function by using an unsigned rather than a signed channel index.

Also, this patch corrects the author's email address. When this macro was last changed, the top level domain part of the email address was left behind.

---
 drivers/net/phy/dp83640.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/net/phy/dp83640.c +3 -3
index 98e7cbf..d6c1061 100644
@@ -424,13 +424,13 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
 	struct dp83640_clock *clock =
 		container_of(ptp, struct dp83640_clock, caps);
 	struct phy_device *phydev = clock->chosen->phydev;
-	int index;
+	unsigned int index;
 	u16 evnt, event_num, gpio_num;
 
 	switch (rq->type) {
 	case PTP_CLK_REQ_EXTTS:
 		index = rq->extts.index;
-		if (index < 0 || index >= N_EXT_TS)
+		if (index >= N_EXT_TS)
 			return -EINVAL;
 		event_num = EXT_EVENT + index;
 		evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
@@ -1363,7 +1363,7 @@ static void __exit dp83640_exit(void)
 }
 
 MODULE_DESCRIPTION("National Semiconductor DP83640 PHY driver");
-MODULE_AUTHOR("Richard Cochran <[email protected]>");
+MODULE_AUTHOR("Richard Cochran <[email protected]>");
 MODULE_LICENSE("GPL");
 
 module_init(dp83640_init);
Signature · 1 line
1.7.10.4
author maintainer
#10 lore raw

RFC PATCH 6/9 dp83640: correct the periodic output frequency

In reply to Richard Cochran

The phyter driver incorrectly feeds the value of the period into what is in fact a pulse width register, resulting in the actual period being twice the dialed value. This patch fixes the issue and renames a variable to make the code at bit more clear.

---
 drivers/net/phy/dp83640.c |   11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
drivers/net/phy/dp83640.c +6 -5
index d6c1061..9e26555 100644
@@ -271,7 +271,7 @@ static void periodic_output(struct dp83640_clock *clock,
 {
 	struct dp83640_private *dp83640 = clock->chosen;
 	struct phy_device *phydev = dp83640->phydev;
-	u32 sec, nsec, period;
+	u32 sec, nsec, pwidth;
 	u16 gpio, ptp_trig, trigger, val;
 
 	gpio = on ? gpio_tab[PEROUT_GPIO] : 0;
@@ -296,8 +296,9 @@ static void periodic_output(struct dp83640_clock *clock,
 
 	sec = clkreq->perout.start.sec;
 	nsec = clkreq->perout.start.nsec;
-	period = clkreq->perout.period.sec * 1000000000UL;
-	period += clkreq->perout.period.nsec;
+	pwidth = clkreq->perout.period.sec * 1000000000UL;
+	pwidth += clkreq->perout.period.nsec;
+	pwidth /= 2;
 
 	mutex_lock(&clock->extreg_lock);
 
@@ -310,8 +311,8 @@ static void periodic_output(struct dp83640_clock *clock,
 	ext_write(0, phydev, PAGE4, PTP_TDR, nsec >> 16);      /* ns[31:16] */
 	ext_write(0, phydev, PAGE4, PTP_TDR, sec & 0xffff);    /* sec[15:0] */
 	ext_write(0, phydev, PAGE4, PTP_TDR, sec >> 16);       /* sec[31:16] */
-	ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff); /* ns[15:0] */
-	ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16);    /* ns[31:16] */
+	ext_write(0, phydev, PAGE4, PTP_TDR, pwidth & 0xffff); /* ns[15:0] */
+	ext_write(0, phydev, PAGE4, PTP_TDR, pwidth >> 16);    /* ns[31:16] */
 
 	/*enable trigger*/
 	val &= ~TRIG_LOAD;
Signature · 1 line
1.7.10.4
author maintainer
#11 lore raw

RFC PATCH 7/9 dp83640: implement programmable pin functions.

In reply to Richard Cochran

This patch adapts the dp83640 driver to allow reconfiguration of which auxiliary function goes on which pin. The functions may be reassigned freely with the one exception of the calibration function.

---
 drivers/net/phy/dp83640.c |   52 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)
drivers/net/phy/dp83640.c +52 -0
index 9e26555..f98998f 100644
@@ -47,6 +47,7 @@
 #define CAL_EVENT	7
 #define CAL_TRIGGER	7
 #define PER_TRIGGER	6
+#define DP83640_N_PINS	12
 
 #define MII_DP83640_MICR 0x11
 #define MII_DP83640_MISR 0x12
@@ -173,6 +174,37 @@ MODULE_PARM_DESC(chosen_phy, \
 MODULE_PARM_DESC(gpio_tab, \
 	"Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6");
 
+static void dp83640_gpio_defaults(struct ptp_pin_desc *pd)
+{
+	int i, index;
+
+	for (i = 0; i < DP83640_N_PINS; i++) {
+		snprintf(pd[i].name, sizeof(pd[i].name), "GPIO%d", 1 + i);
+		pd[i].index = i;
+	}
+
+	for (i = 0; i < GPIO_TABLE_SIZE; i++) {
+		if (gpio_tab[i] < 1 || gpio_tab[i] > DP83640_N_PINS) {
+			pr_err("gpio_tab[%d]=%hu out of range", i, gpio_tab[i]);
+			return;
+		}
+	}
+
+	index = gpio_tab[CALIBRATE_GPIO] - 1;
+	pd[index].func = PTP_PF_PHYSYNC;
+	pd[index].chan = 0;
+
+	index = gpio_tab[PEROUT_GPIO] - 1;
+	pd[index].func = PTP_PF_PEROUT;
+	pd[index].chan = 0;
+
+	for (i = EXTTS0_GPIO; i < GPIO_TABLE_SIZE; i++) {
+		index = gpio_tab[i] - 1;
+		pd[index].func = PTP_PF_EXTTS;
+		pd[index].chan = i - EXTTS0_GPIO;
+	}
+}
+
 /* a list of clocks and a mutex to protect it */
 static LIST_HEAD(phyter_clocks);
 static DEFINE_MUTEX(phyter_clocks_lock);
@@ -459,6 +491,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
 	return -EOPNOTSUPP;
 }
 
+static int ptp_dp83640_setpin(struct ptp_clock_info *ptp, unsigned int pin,
+			      enum ptp_pin_function func, unsigned int chan)
+{
+	return 0;
+}
+
 static u8 status_frame_dst[6] = { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 };
 static u8 status_frame_src[6] = { 0x08, 0x00, 0x17, 0x0B, 0x6B, 0x0F };
 
@@ -876,6 +914,7 @@ static void dp83640_free_clocks(void)
 		mutex_destroy(&clock->extreg_lock);
 		mutex_destroy(&clock->clock_lock);
 		put_device(&clock->bus->dev);
+		kfree(clock->caps.pin_config);
 		kfree(clock);
 	}
 
@@ -895,12 +934,18 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
 	clock->caps.n_alarm	= 0;
 	clock->caps.n_ext_ts	= N_EXT_TS;
 	clock->caps.n_per_out	= 1;
+	clock->caps.n_pins	= DP83640_N_PINS;
 	clock->caps.pps		= 0;
 	clock->caps.adjfreq	= ptp_dp83640_adjfreq;
 	clock->caps.adjtime	= ptp_dp83640_adjtime;
 	clock->caps.gettime	= ptp_dp83640_gettime;
 	clock->caps.settime	= ptp_dp83640_settime;
 	clock->caps.enable	= ptp_dp83640_enable;
+	clock->caps.setpin	= ptp_dp83640_setpin;
+	/*
+	 * Convert the module param defaults into a dynamic pin configuration.
+	 */
+	dp83640_gpio_defaults(clock->caps.pin_config);
 	/*
 	 * Get a reference to this bus instance.
 	 */
@@ -951,6 +996,13 @@ static struct dp83640_clock *dp83640_clock_get_bus(struct mii_bus *bus)
 	if (!clock)
 		goto out;
 
+	clock->caps.pin_config = kzalloc(sizeof(struct ptp_pin_desc) *
+					 DP83640_N_PINS, GFP_KERNEL);
+	if (!clock->caps.pin_config) {
+		kfree(clock);
+		clock = NULL;
+		goto out;
+	}
 	dp83640_clock_init(clock, bus);
 	list_add_tail(&phyter_clocks, &clock->list);
 out:
Signature · 1 line
1.7.10.4
author maintainer
#12 lore raw

RFC PATCH 8/9 dp83640: let external input pins from the module parameters be defaults.

In reply to Richard Cochran

This patch changes the driver to use the new pin configuration method when programming the external time stamp input signals.

---
 drivers/net/phy/dp83640.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/net/phy/dp83640.c +4 -1
index f98998f..d60b90c 100644
@@ -468,7 +468,10 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
 		event_num = EXT_EVENT + index;
 		evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
 		if (on) {
-			gpio_num = gpio_tab[EXTTS0_GPIO + index];
+			gpio_num = 1 + ptp_find_pin(clock->ptp_clock,
+						    PTP_PF_EXTTS, index);
+			if (gpio_num < 1)
+				return -EINVAL;
 			evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
 			if (rq->extts.flags & PTP_FALLING_EDGE)
 				evnt |= EVNT_FALL;
Signature · 1 line
1.7.10.4
author maintainer
#13 lore raw

RFC PATCH 9/9 dp83640: let the periodic pin from the module parameter be a default.

In reply to Richard Cochran

This patch changes the driver use the new pin configuration method when programming the periodic output signal.

---
 drivers/net/phy/dp83640.c |   19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/net/phy/dp83640.c +13 -6
index d60b90c..a829fd5 100644
@@ -298,15 +298,22 @@ static u64 phy2txts(struct phy_txts *p)
 	return ns;
 }
 
-static void periodic_output(struct dp83640_clock *clock,
-			    struct ptp_clock_request *clkreq, bool on)
+static int periodic_output(struct dp83640_clock *clock,
+			   struct ptp_clock_request *clkreq, bool on)
 {
 	struct dp83640_private *dp83640 = clock->chosen;
 	struct phy_device *phydev = dp83640->phydev;
 	u32 sec, nsec, pwidth;
 	u16 gpio, ptp_trig, trigger, val;
 
-	gpio = on ? gpio_tab[PEROUT_GPIO] : 0;
+	if (on) {
+		gpio = 1 + ptp_find_pin(clock->ptp_clock, PTP_PF_PEROUT, 0);
+		if (gpio < 1)
+			return -EINVAL;
+	} else {
+		gpio = 0;
+	}
+
 	trigger = PER_TRIGGER;
 
 	ptp_trig = TRIG_WR |
@@ -323,7 +330,7 @@ static void periodic_output(struct dp83640_clock *clock,
 		ext_write(0, phydev, PAGE5, PTP_TRIG, ptp_trig);
 		ext_write(0, phydev, PAGE4, PTP_CTL, val);
 		mutex_unlock(&clock->extreg_lock);
-		return;
+		return 0;
 	}
 
 	sec = clkreq->perout.start.sec;
@@ -352,6 +359,7 @@ static void periodic_output(struct dp83640_clock *clock,
 	ext_write(0, phydev, PAGE4, PTP_CTL, val);
 
 	mutex_unlock(&clock->extreg_lock);
+	return 0;
 }
 
 /* ptp clock methods */
@@ -484,8 +492,7 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
 	case PTP_CLK_REQ_PEROUT:
 		if (rq->perout.index != 0)
 			return -EINVAL;
-		periodic_output(clock, rq, on);
-		return 0;
+		return periodic_output(clock, rq, on);
 
 	default:
 		break;
Signature · 1 line
1.7.10.4
#14 lore raw
In reply to Richard Cochran
Quoted 8 lines · Richard Cochran

This patch series introduces a way of changing the auxiliary PTP Hardware Clock functions (periodic output signals and time stamping external signals) at run time. In the past on the netdev list, we have discussed other ways to handle this, such as module parameters and ethtool. This series implements a new PHC ioctl because that is the most natural way. Users already activate the auxiliary functions via the ioctls. The sysfs interface has also been expanded so that the pin configuration can be programmed using shell scripts.

A few general questions / thoughts:

What are the n_ext_ts and n_per_out supposed to be set to now? The number of pins configured for the relevant function or the number of channels that are available for the function?

The implementation is limited to a single function for each pin - the dp83640 supports an ext_ts and several periodic outputs on the same pin, but I do not see that many real-world uses.

I have done a few short run-time tests, and so far everything is working as expected.

Stefan

author maintainer
#15 lore raw
Quoted 4 lines · Stefan

What are the n_ext_ts and n_per_out supposed to be set to now? The number of pins configured for the relevant function or the number of channels that are available for the function?

They are the number of available functions. This is not necessarily the same as the number of pins. Many chips have more functions than pins.

Quoted 3 lines

The implementation is limited to a single function for each pin - the dp83640 supports an ext_ts and several periodic outputs on the same pin, but I do not see that many real-world uses.

Yes, it does support different functions on a single pin, but not at the same time. You have to choose what function you want on each pin, and that is what the new ioctl lets you do.

I did not change the n_per_out in this series, but once this basic support gets merged, then you will be able to increase n_per_out for the dp83640 with little extra effort.

Thanks,
Richard

#16 lore raw

Hi Richard,

Quoted 13 lines · --On March 10, 2014 14:42 +0100 Richard Cochran <[email protected]>…
Quoted 4 lines · Stefan

What are the n_ext_ts and n_per_out supposed to be set to now? The number of pins configured for the relevant function or the number of channels that are available for the function?

They are the number of available functions. This is not necessarily the same as the number of pins. Many chips have more functions than pins.

Quoted 3 lines

The implementation is limited to a single function for each pin - the dp83640 supports an ext_ts and several periodic outputs on the same pin, but I do not see that many real-world uses.

Quoted 5 lines

Yes, it does support different functions on a single pin, but not at the same time. You have to choose what function you want on each pin, and that is what the new ioctl lets you do.

I did not change the n_per_out in this series,

Why not? I think it would be a good idea to set both n_ext_ts and n_per_out to the correct value right from the start (8, right?). Christian

Quoted 3 lines

but once this basic
support gets merged, then you will be able to increase n_per_out for the dp83640 with little extra effort.

author maintainer
#17 lore raw
Quoted 5 lines · Christian Riesch
Quoted 1 line

I did not change the n_per_out in this series,

Why not? I think it would be a good idea to set both n_ext_ts and n_per_out to the correct value right from the start (8, right?). Christian

Yes, it is a good idea, but it is a little extra work, especially testing it. Channels 0 and 1 must be programmed differently, IIRC.

Right now, I am focusing on:

1. getting the new ioctl interface right
2. making it work for the phyter while keeping the module params.

After that, as I said before:

Quoted 3 lines
Quoted 3 lines

but once this basic
support gets merged, then you will be able to increase n_per_out for the dp83640 with little extra effort.

Thanks,
Richard

author maintainer
#18 lore raw
In reply to Sørensen, Stefan
Quoted 3 lines · Stefan

The implementation is limited to a single function for each pin - the dp83640 supports an ext_ts and several periodic outputs on the same pin, but I do not see that many real-world uses.

So the main use case is to allow users to configure the auxiliary functions according to their needs. This series provides this without relying on DT, ACPI, etc. The sysfs control lets you put this into a boot script, for example.

In addition, there are also less common use cases for re-configuring pins at run time. One use would be to switch from a PPS output to a time stamping input during automated PTP testing.

Thanks,
Richard

#19 lore raw
In reply to Richard Cochran

Hi Richard,

Quoted 8 lines · --On March 08, 2014 20:42 +0100 Richard Cochran <[email protected]>…

This patch series introduces a way of changing the auxiliary PTP Hardware Clock functions (periodic output signals and time stamping external signals) at run time. In the past on the netdev list, we have discussed other ways to handle this, such as module parameters and ethtool. This series implements a new PHC ioctl because that is the most natural way. Users already activate the auxiliary functions via the ioctls. The sysfs interface has also been expanded so that the pin configuration can be programmed using shell scripts.

I did a few tests on one of my boards, everything works so far! Thanks for the patchset, I like it!

Quoted 10 lines

The first patch adds the new ioctls. The PHC subsystem does most of the work of maintaining the function-to-pin mapping. Drivers will only need to allocate and initialize a pin configuration table and also provide a new method that validates a particular assignment.

Patches 5 and 6 just clean up a couple of issues in the phyter driver, and the remaining patches actually hook the phyter's pins into the new system.

Comments and questions are most welcome.

Do you think it is possible to extend this in the future, e.g. for selecting the polarity of periodic output signals or for time stamping of external signals (rising edge/falling edge), or duty cycles of the periodic signal other than 50%? How could this be done? Using the reserved fields in struct ptp_pin_desc?

Do you think the concept allows an extension for single pulse output, e.g. programming a pin to output a single pulse at a given time, as supported by the DP83640?

If several DP83640 are connected together with the calibration function, only the GPIOs of the master device can be used, right? I guess this could also be extended in the future to use the GPIOs of all DP83640, right? Or do you see a problem with your concept here?

Best regards,
Christian

author maintainer
#20 lore raw
Quoted 6 lines · Christian Riesch

Do you think it is possible to extend this in the future, e.g. for selecting the polarity of periodic output signals or for time stamping of external signals (rising edge/falling edge), or duty cycles of the periodic signal other than 50%? How could this be done? Using the reserved fields in struct ptp_pin_desc?

Yes, this can be done, but this relates to the ptp_xyz_request ioctls and not to the ptp_pin_desc. You asked for three things. Here is how one might implement them.

1. selecting the polarity of periodic output signals

ptp_perout_request.flags (new flag)

2. time stamping of external signals (rising edge/falling edge)

This is already supported in the ptp_extts_request.flags field. The drivers just need to implement it.

3. duty cycles of the periodic signal other than 50%?

Maybe using one of the ptp_perout_request.rsv fields.

Quoted 3 lines

Do you think the concept allows an extension for single pulse output, e.g. programming a pin to output a single pulse at a given time, as supported by the DP83640?

Yes, either a new ioctl or maybe ptp_perout_request.flags with a ONE-SHOT flag.

Quoted 4 lines

If several DP83640 are connected together with the calibration function, only the GPIOs of the master device can be used, right? I guess this could also be extended in the future to use the GPIOs of all DP83640, right? Or do you see a problem with your concept here?

If the driver would combine all of the pins and functions over all the devices, that would be best. I think it would be tricky to implement, since the driver probe() function doesn't know how many more phyters to expect.

Thanks,
Richard