Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+107 -34
+50 -24
drivers/net/ethernet/mscc/ocelot_ptp.c
··· 184 184 struct ptp_clock_request *rq, int on) 185 185 { 186 186 struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info); 187 - struct timespec64 ts_start, ts_period; 187 + struct timespec64 ts_phase, ts_period; 188 188 enum ocelot_ptp_pins ptp_pin; 189 189 unsigned long flags; 190 190 bool pps = false; 191 191 int pin = -1; 192 + s64 wf_high; 193 + s64 wf_low; 192 194 u32 val; 193 - s64 ns; 194 195 195 196 switch (rq->type) { 196 197 case PTP_CLK_REQ_PEROUT: 197 198 /* Reject requests with unsupported flags */ 198 - if (rq->perout.flags) 199 + if (rq->perout.flags & ~(PTP_PEROUT_DUTY_CYCLE | 200 + PTP_PEROUT_PHASE)) 199 201 return -EOPNOTSUPP; 200 202 201 203 pin = ptp_find_pin(ocelot->ptp_clock, PTP_PF_PEROUT, ··· 213 211 else 214 212 return -EBUSY; 215 213 216 - ts_start.tv_sec = rq->perout.start.sec; 217 - ts_start.tv_nsec = rq->perout.start.nsec; 218 214 ts_period.tv_sec = rq->perout.period.sec; 219 215 ts_period.tv_nsec = rq->perout.period.nsec; 220 216 221 217 if (ts_period.tv_sec == 1 && ts_period.tv_nsec == 0) 222 218 pps = true; 223 - 224 - if (ts_start.tv_sec || (ts_start.tv_nsec && !pps)) { 225 - dev_warn(ocelot->dev, 226 - "Absolute start time not supported!\n"); 227 - dev_warn(ocelot->dev, 228 - "Accept nsec for PPS phase adjustment, otherwise start time should be 0 0.\n"); 229 - return -EINVAL; 230 - } 231 219 232 220 /* Handle turning off */ 233 221 if (!on) { ··· 228 236 break; 229 237 } 230 238 239 + if (rq->perout.flags & PTP_PEROUT_PHASE) { 240 + ts_phase.tv_sec = rq->perout.phase.sec; 241 + ts_phase.tv_nsec = rq->perout.phase.nsec; 242 + } else { 243 + /* Compatibility */ 244 + ts_phase.tv_sec = rq->perout.start.sec; 245 + ts_phase.tv_nsec = rq->perout.start.nsec; 246 + } 247 + if (ts_phase.tv_sec || (ts_phase.tv_nsec && !pps)) { 248 + dev_warn(ocelot->dev, 249 + "Absolute start time not supported!\n"); 250 + dev_warn(ocelot->dev, 251 + "Accept nsec for PPS phase adjustment, otherwise start time should be 0 0.\n"); 252 + return -EINVAL; 253 + } 254 + 255 + /* Calculate waveform high and low times */ 256 + if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) { 257 + struct timespec64 ts_on; 258 + 259 + ts_on.tv_sec = rq->perout.on.sec; 260 + ts_on.tv_nsec = rq->perout.on.nsec; 261 + 262 + wf_high = timespec64_to_ns(&ts_on); 263 + } else { 264 + if (pps) { 265 + wf_high = 1000; 266 + } else { 267 + wf_high = timespec64_to_ns(&ts_period); 268 + wf_high = div_s64(wf_high, 2); 269 + } 270 + } 271 + 272 + wf_low = timespec64_to_ns(&ts_period); 273 + wf_low -= wf_high; 274 + 231 275 /* Handle PPS request */ 232 276 if (pps) { 233 277 spin_lock_irqsave(&ocelot->ptp_clock_lock, flags); 234 - /* Pulse generated perout.start.nsec after TOD has 235 - * increased seconds. 236 - * Pulse width is set to 1us. 237 - */ 238 - ocelot_write_rix(ocelot, ts_start.tv_nsec, 278 + ocelot_write_rix(ocelot, ts_phase.tv_nsec, 239 279 PTP_PIN_WF_LOW_PERIOD, ptp_pin); 240 - ocelot_write_rix(ocelot, 1000, 280 + ocelot_write_rix(ocelot, wf_high, 241 281 PTP_PIN_WF_HIGH_PERIOD, ptp_pin); 242 282 val = PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_CLOCK); 243 283 val |= PTP_PIN_CFG_SYNC; ··· 279 255 } 280 256 281 257 /* Handle periodic clock */ 282 - ns = timespec64_to_ns(&ts_period); 283 - ns = ns >> 1; 284 - if (ns > 0x3fffffff || ns <= 0x6) 258 + if (wf_high > 0x3fffffff || wf_high <= 0x6) 259 + return -EINVAL; 260 + if (wf_low > 0x3fffffff || wf_low <= 0x6) 285 261 return -EINVAL; 286 262 287 263 spin_lock_irqsave(&ocelot->ptp_clock_lock, flags); 288 - ocelot_write_rix(ocelot, ns, PTP_PIN_WF_LOW_PERIOD, ptp_pin); 289 - ocelot_write_rix(ocelot, ns, PTP_PIN_WF_HIGH_PERIOD, ptp_pin); 264 + ocelot_write_rix(ocelot, wf_low, PTP_PIN_WF_LOW_PERIOD, 265 + ptp_pin); 266 + ocelot_write_rix(ocelot, wf_high, PTP_PIN_WF_HIGH_PERIOD, 267 + ptp_pin); 290 268 val = PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_CLOCK); 291 269 ocelot_write_rix(ocelot, val, PTP_PIN_CFG, ptp_pin); 292 270 spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
+27 -6
drivers/ptp/ptp_chardev.c
··· 191 191 err = -EFAULT; 192 192 break; 193 193 } 194 - if (((req.perout.flags & ~PTP_PEROUT_VALID_FLAGS) || 195 - req.perout.rsv[0] || req.perout.rsv[1] || 196 - req.perout.rsv[2] || req.perout.rsv[3]) && 197 - cmd == PTP_PEROUT_REQUEST2) { 198 - err = -EINVAL; 199 - break; 194 + if (cmd == PTP_PEROUT_REQUEST2) { 195 + struct ptp_perout_request *perout = &req.perout; 196 + 197 + if (perout->flags & ~PTP_PEROUT_VALID_FLAGS) { 198 + err = -EINVAL; 199 + break; 200 + } 201 + /* 202 + * The "on" field has undefined meaning if 203 + * PTP_PEROUT_DUTY_CYCLE isn't set, we must still treat 204 + * it as reserved, which must be set to zero. 205 + */ 206 + if (!(perout->flags & PTP_PEROUT_DUTY_CYCLE) && 207 + (perout->rsv[0] || perout->rsv[1] || 208 + perout->rsv[2] || perout->rsv[3])) { 209 + err = -EINVAL; 210 + break; 211 + } 212 + if (perout->flags & PTP_PEROUT_DUTY_CYCLE) { 213 + /* The duty cycle must be subunitary. */ 214 + if (perout->on.sec > perout->period.sec || 215 + (perout->on.sec == perout->period.sec && 216 + perout->on.nsec > perout->period.nsec)) { 217 + err = -ERANGE; 218 + break; 219 + } 220 + } 200 221 } else if (cmd == PTP_PEROUT_REQUEST) { 201 222 req.perout.flags &= PTP_PEROUT_V1_VALID_FLAGS; 202 223 req.perout.rsv[0] = 0;
+30 -4
include/uapi/linux/ptp_clock.h
··· 53 53 /* 54 54 * Bits of the ptp_perout_request.flags field: 55 55 */ 56 - #define PTP_PEROUT_ONE_SHOT (1<<0) 56 + #define PTP_PEROUT_ONE_SHOT (1<<0) 57 + #define PTP_PEROUT_DUTY_CYCLE (1<<1) 58 + #define PTP_PEROUT_PHASE (1<<2) 57 59 58 60 /* 59 61 * flag fields valid for the new PTP_PEROUT_REQUEST2 ioctl. 60 62 */ 61 - #define PTP_PEROUT_VALID_FLAGS (PTP_PEROUT_ONE_SHOT) 63 + #define PTP_PEROUT_VALID_FLAGS (PTP_PEROUT_ONE_SHOT | \ 64 + PTP_PEROUT_DUTY_CYCLE | \ 65 + PTP_PEROUT_PHASE) 62 66 63 67 /* 64 68 * No flags are valid for the original PTP_PEROUT_REQUEST ioctl ··· 105 101 }; 106 102 107 103 struct ptp_perout_request { 108 - struct ptp_clock_time start; /* Absolute start time. */ 104 + union { 105 + /* 106 + * Absolute start time. 107 + * Valid only if (flags & PTP_PEROUT_PHASE) is unset. 108 + */ 109 + struct ptp_clock_time start; 110 + /* 111 + * Phase offset. The signal should start toggling at an 112 + * unspecified integer multiple of the period, plus this value. 113 + * The start time should be "as soon as possible". 114 + * Valid only if (flags & PTP_PEROUT_PHASE) is set. 115 + */ 116 + struct ptp_clock_time phase; 117 + }; 109 118 struct ptp_clock_time period; /* Desired period, zero means disable. */ 110 119 unsigned int index; /* Which channel to configure. */ 111 120 unsigned int flags; 112 - unsigned int rsv[4]; /* Reserved for future use. */ 121 + union { 122 + /* 123 + * The "on" time of the signal. 124 + * Must be lower than the period. 125 + * Valid only if (flags & PTP_PEROUT_DUTY_CYCLE) is set. 126 + */ 127 + struct ptp_clock_time on; 128 + /* Reserved for future use. */ 129 + unsigned int rsv[4]; 130 + }; 113 131 }; 114 132 115 133 #define PTP_MAX_SAMPLES 25 /* Maximum allowed offset measurement samples. */