summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2020-07-20 05:22:57 +0300
committerDavid S. Miller <davem@davemloft.net>2020-07-20 05:23:33 +0300
commit120c7dd52213d86ca65a76127b9a69b1cae132d0 (patch)
tree6303c3b6958fdc5286c46b9ec3db56c0ab651a68
parenteba75c587e811d3249c8bd50d22bb2266ccd3c0f (diff)
parentecf9f9b77c637a3d9347a7c85b33964abac989ac (diff)
downloadlinux-120c7dd52213d86ca65a76127b9a69b1cae132d0.tar.xz
Merge branch 'Fully-describe-the-waveform-for-PTP-periodic-output'
Vladimir Oltean says: ==================== Fully describe the waveform for PTP periodic output While using the ancillary pin functionality of PTP hardware clocks to synchronize multiple DSA switches on a board, a need arised to be able to configure the duty cycle of the master of this PPS hierarchy. Also, the PPS master is not able to emit PPS starting from arbitrary absolute times, so a new flag is introduced to support such hardware without making guesses. With these patches, struct ptp_perout_request now basically describes a general-purpose square wave. Changes in v2: Made sure this applies to net-next. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/mscc/ocelot_ptp.c74
-rw-r--r--drivers/ptp/ptp_chardev.c33
-rw-r--r--include/uapi/linux/ptp_clock.h34
3 files changed, 107 insertions, 34 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.c b/drivers/net/ethernet/mscc/ocelot_ptp.c
index a3088a1676ed..1e08fe4daaef 100644
--- a/drivers/net/ethernet/mscc/ocelot_ptp.c
+++ b/drivers/net/ethernet/mscc/ocelot_ptp.c
@@ -184,18 +184,20 @@ int ocelot_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
- struct timespec64 ts_start, ts_period;
+ struct timespec64 ts_phase, ts_period;
enum ocelot_ptp_pins ptp_pin;
unsigned long flags;
bool pps = false;
int pin = -1;
+ s64 wf_high;
+ s64 wf_low;
u32 val;
- s64 ns;
switch (rq->type) {
case PTP_CLK_REQ_PEROUT:
/* Reject requests with unsupported flags */
- if (rq->perout.flags)
+ if (rq->perout.flags & ~(PTP_PEROUT_DUTY_CYCLE |
+ PTP_PEROUT_PHASE))
return -EOPNOTSUPP;
pin = ptp_find_pin(ocelot->ptp_clock, PTP_PF_PEROUT,
@@ -211,22 +213,12 @@ int ocelot_ptp_enable(struct ptp_clock_info *ptp,
else
return -EBUSY;
- ts_start.tv_sec = rq->perout.start.sec;
- ts_start.tv_nsec = rq->perout.start.nsec;
ts_period.tv_sec = rq->perout.period.sec;
ts_period.tv_nsec = rq->perout.period.nsec;
if (ts_period.tv_sec == 1 && ts_period.tv_nsec == 0)
pps = true;
- if (ts_start.tv_sec || (ts_start.tv_nsec && !pps)) {
- dev_warn(ocelot->dev,
- "Absolute start time not supported!\n");
- dev_warn(ocelot->dev,
- "Accept nsec for PPS phase adjustment, otherwise start time should be 0 0.\n");
- return -EINVAL;
- }
-
/* Handle turning off */
if (!on) {
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
@@ -236,16 +228,48 @@ int ocelot_ptp_enable(struct ptp_clock_info *ptp,
break;
}
+ if (rq->perout.flags & PTP_PEROUT_PHASE) {
+ ts_phase.tv_sec = rq->perout.phase.sec;
+ ts_phase.tv_nsec = rq->perout.phase.nsec;
+ } else {
+ /* Compatibility */
+ ts_phase.tv_sec = rq->perout.start.sec;
+ ts_phase.tv_nsec = rq->perout.start.nsec;
+ }
+ if (ts_phase.tv_sec || (ts_phase.tv_nsec && !pps)) {
+ dev_warn(ocelot->dev,
+ "Absolute start time not supported!\n");
+ dev_warn(ocelot->dev,
+ "Accept nsec for PPS phase adjustment, otherwise start time should be 0 0.\n");
+ return -EINVAL;
+ }
+
+ /* Calculate waveform high and low times */
+ if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) {
+ struct timespec64 ts_on;
+
+ ts_on.tv_sec = rq->perout.on.sec;
+ ts_on.tv_nsec = rq->perout.on.nsec;
+
+ wf_high = timespec64_to_ns(&ts_on);
+ } else {
+ if (pps) {
+ wf_high = 1000;
+ } else {
+ wf_high = timespec64_to_ns(&ts_period);
+ wf_high = div_s64(wf_high, 2);
+ }
+ }
+
+ wf_low = timespec64_to_ns(&ts_period);
+ wf_low -= wf_high;
+
/* Handle PPS request */
if (pps) {
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
- /* Pulse generated perout.start.nsec after TOD has
- * increased seconds.
- * Pulse width is set to 1us.
- */
- ocelot_write_rix(ocelot, ts_start.tv_nsec,
+ ocelot_write_rix(ocelot, ts_phase.tv_nsec,
PTP_PIN_WF_LOW_PERIOD, ptp_pin);
- ocelot_write_rix(ocelot, 1000,
+ ocelot_write_rix(ocelot, wf_high,
PTP_PIN_WF_HIGH_PERIOD, ptp_pin);
val = PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_CLOCK);
val |= PTP_PIN_CFG_SYNC;
@@ -255,14 +279,16 @@ int ocelot_ptp_enable(struct ptp_clock_info *ptp,
}
/* Handle periodic clock */
- ns = timespec64_to_ns(&ts_period);
- ns = ns >> 1;
- if (ns > 0x3fffffff || ns <= 0x6)
+ if (wf_high > 0x3fffffff || wf_high <= 0x6)
+ return -EINVAL;
+ if (wf_low > 0x3fffffff || wf_low <= 0x6)
return -EINVAL;
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
- ocelot_write_rix(ocelot, ns, PTP_PIN_WF_LOW_PERIOD, ptp_pin);
- ocelot_write_rix(ocelot, ns, PTP_PIN_WF_HIGH_PERIOD, ptp_pin);
+ ocelot_write_rix(ocelot, wf_low, PTP_PIN_WF_LOW_PERIOD,
+ ptp_pin);
+ ocelot_write_rix(ocelot, wf_high, PTP_PIN_WF_HIGH_PERIOD,
+ ptp_pin);
val = PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_CLOCK);
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, ptp_pin);
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index 375cd6e4aade..e0e6f85966e1 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -191,12 +191,33 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
err = -EFAULT;
break;
}
- if (((req.perout.flags & ~PTP_PEROUT_VALID_FLAGS) ||
- req.perout.rsv[0] || req.perout.rsv[1] ||
- req.perout.rsv[2] || req.perout.rsv[3]) &&
- cmd == PTP_PEROUT_REQUEST2) {
- err = -EINVAL;
- break;
+ if (cmd == PTP_PEROUT_REQUEST2) {
+ struct ptp_perout_request *perout = &req.perout;
+
+ if (perout->flags & ~PTP_PEROUT_VALID_FLAGS) {
+ err = -EINVAL;
+ break;
+ }
+ /*
+ * The "on" field has undefined meaning if
+ * PTP_PEROUT_DUTY_CYCLE isn't set, we must still treat
+ * it as reserved, which must be set to zero.
+ */
+ if (!(perout->flags & PTP_PEROUT_DUTY_CYCLE) &&
+ (perout->rsv[0] || perout->rsv[1] ||
+ perout->rsv[2] || perout->rsv[3])) {
+ err = -EINVAL;
+ break;
+ }
+ if (perout->flags & PTP_PEROUT_DUTY_CYCLE) {
+ /* The duty cycle must be subunitary. */
+ if (perout->on.sec > perout->period.sec ||
+ (perout->on.sec == perout->period.sec &&
+ perout->on.nsec > perout->period.nsec)) {
+ err = -ERANGE;
+ break;
+ }
+ }
} else if (cmd == PTP_PEROUT_REQUEST) {
req.perout.flags &= PTP_PEROUT_V1_VALID_FLAGS;
req.perout.rsv[0] = 0;
diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h
index ff070aa64278..1d108d597f66 100644
--- a/include/uapi/linux/ptp_clock.h
+++ b/include/uapi/linux/ptp_clock.h
@@ -53,12 +53,16 @@
/*
* Bits of the ptp_perout_request.flags field:
*/
-#define PTP_PEROUT_ONE_SHOT (1<<0)
+#define PTP_PEROUT_ONE_SHOT (1<<0)
+#define PTP_PEROUT_DUTY_CYCLE (1<<1)
+#define PTP_PEROUT_PHASE (1<<2)
/*
* flag fields valid for the new PTP_PEROUT_REQUEST2 ioctl.
*/
-#define PTP_PEROUT_VALID_FLAGS (PTP_PEROUT_ONE_SHOT)
+#define PTP_PEROUT_VALID_FLAGS (PTP_PEROUT_ONE_SHOT | \
+ PTP_PEROUT_DUTY_CYCLE | \
+ PTP_PEROUT_PHASE)
/*
* No flags are valid for the original PTP_PEROUT_REQUEST ioctl
@@ -101,11 +105,33 @@ struct ptp_extts_request {
};
struct ptp_perout_request {
- struct ptp_clock_time start; /* Absolute start time. */
+ union {
+ /*
+ * Absolute start time.
+ * Valid only if (flags & PTP_PEROUT_PHASE) is unset.
+ */
+ struct ptp_clock_time start;
+ /*
+ * Phase offset. The signal should start toggling at an
+ * unspecified integer multiple of the period, plus this value.
+ * The start time should be "as soon as possible".
+ * Valid only if (flags & PTP_PEROUT_PHASE) is set.
+ */
+ struct ptp_clock_time phase;
+ };
struct ptp_clock_time period; /* Desired period, zero means disable. */
unsigned int index; /* Which channel to configure. */
unsigned int flags;
- unsigned int rsv[4]; /* Reserved for future use. */
+ union {
+ /*
+ * The "on" time of the signal.
+ * Must be lower than the period.
+ * Valid only if (flags & PTP_PEROUT_DUTY_CYCLE) is set.
+ */
+ struct ptp_clock_time on;
+ /* Reserved for future use. */
+ unsigned int rsv[4];
+ };
};
#define PTP_MAX_SAMPLES 25 /* Maximum allowed offset measurement samples. */