Commit 120c7dd5 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents eba75c58 ecf9f9b7
......@@ -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);
......
......@@ -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;
......
......@@ -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. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment