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

ptp: Add cycles support for virtual clocks

ptp vclocks require a free running time for their timecounter.
Currently only a physical clock forced to free running is supported.
If vclocks are used, then the physical clock cannot be synchronized
anymore. The synchronized time is not available in hardware in this
case. As a result, timed transmission with TAPRIO hardware support
is not possible anymore.

If hardware would support a free running time additionally to the
physical clock, then the physical clock does not need to be forced to
free running. Thus, the physical clocks can still be synchronized
while vclocks are in use.

The physical clock could be used to synchronize the time domain of the
TSN network and trigger TAPRIO. In parallel vclocks can be used to
synchronize other time domains.

Introduce support for a free running cycle counter called cycles to
physical clocks. Rework ptp vclocks to use this free running cycle
counter. Default implementation is based on time of physical clock.
Thus, behavior of ptp vclocks based on physical clocks without free
running cycle counter is identical to previous behavior.

Signed-off-by: Gerhard Engleder <gerhard@engleder-embedded.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Gerhard Engleder and committed by
Paolo Abeni
42704b26 b3552d6a

+80 -16
+27 -4
drivers/ptp/ptp_clock.c
··· 77 77 { 78 78 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 79 79 80 - if (ptp_vclock_in_use(ptp)) { 81 - pr_err("ptp: virtual clock in use\n"); 80 + if (ptp_clock_freerun(ptp)) { 81 + pr_err("ptp: physical clock is free running\n"); 82 82 return -EBUSY; 83 83 } 84 84 ··· 103 103 struct ptp_clock_info *ops; 104 104 int err = -EOPNOTSUPP; 105 105 106 - if (ptp_vclock_in_use(ptp)) { 107 - pr_err("ptp: virtual clock in use\n"); 106 + if (ptp_clock_freerun(ptp)) { 107 + pr_err("ptp: physical clock is free running\n"); 108 108 return -EBUSY; 109 109 } 110 110 ··· 178 178 kfree(ptp); 179 179 } 180 180 181 + static int ptp_getcycles64(struct ptp_clock_info *info, struct timespec64 *ts) 182 + { 183 + if (info->getcyclesx64) 184 + return info->getcyclesx64(info, ts, NULL); 185 + else 186 + return info->gettime64(info, ts); 187 + } 188 + 181 189 static void ptp_aux_kworker(struct kthread_work *work) 182 190 { 183 191 struct ptp_clock *ptp = container_of(work, struct ptp_clock, ··· 232 224 mutex_init(&ptp->pincfg_mux); 233 225 mutex_init(&ptp->n_vclocks_mux); 234 226 init_waitqueue_head(&ptp->tsev_wq); 227 + 228 + if (ptp->info->getcycles64 || ptp->info->getcyclesx64) { 229 + ptp->has_cycles = true; 230 + if (!ptp->info->getcycles64 && ptp->info->getcyclesx64) 231 + ptp->info->getcycles64 = ptp_getcycles64; 232 + } else { 233 + /* Free running cycle counter not supported, use time. */ 234 + ptp->info->getcycles64 = ptp_getcycles64; 235 + 236 + if (ptp->info->gettimex64) 237 + ptp->info->getcyclesx64 = ptp->info->gettimex64; 238 + 239 + if (ptp->info->getcrosststamp) 240 + ptp->info->getcrosscycles = ptp->info->getcrosststamp; 241 + } 235 242 236 243 if (ptp->info->do_aux_work) { 237 244 kthread_init_delayed_work(&ptp->aux_work, ptp_aux_kworker);
+10
drivers/ptp/ptp_private.h
··· 52 52 int *vclock_index; 53 53 struct mutex n_vclocks_mux; /* protect concurrent n_vclocks access */ 54 54 bool is_virtual_clock; 55 + bool has_cycles; 55 56 }; 56 57 57 58 #define info_to_vclock(d) container_of((d), struct ptp_vclock, info) ··· 95 94 mutex_unlock(&ptp->n_vclocks_mux); 96 95 97 96 return in_use; 97 + } 98 + 99 + /* Check if ptp clock shall be free running */ 100 + static inline bool ptp_clock_freerun(struct ptp_clock *ptp) 101 + { 102 + if (ptp->has_cycles) 103 + return false; 104 + 105 + return ptp_vclock_in_use(ptp); 98 106 } 99 107 100 108 extern struct class *ptp_class;
+7 -4
drivers/ptp/ptp_sysfs.c
··· 231 231 *(ptp->vclock_index + ptp->n_vclocks - i) = -1; 232 232 } 233 233 234 - if (num == 0) 235 - dev_info(dev, "only physical clock in use now\n"); 236 - else 237 - dev_info(dev, "guarantee physical clock free running\n"); 234 + /* Need to inform about changed physical clock behavior */ 235 + if (!ptp->has_cycles) { 236 + if (num == 0) 237 + dev_info(dev, "only physical clock in use now\n"); 238 + else 239 + dev_info(dev, "guarantee physical clock free running\n"); 240 + } 238 241 239 242 ptp->n_vclocks = num; 240 243 mutex_unlock(&ptp->n_vclocks_mux);
+5 -8
drivers/ptp/ptp_vclock.c
··· 68 68 int err; 69 69 u64 ns; 70 70 71 - err = pptp->info->gettimex64(pptp->info, &pts, sts); 71 + err = pptp->info->getcyclesx64(pptp->info, &pts, sts); 72 72 if (err) 73 73 return err; 74 74 ··· 104 104 int err; 105 105 u64 ns; 106 106 107 - err = pptp->info->getcrosststamp(pptp->info, xtstamp); 107 + err = pptp->info->getcrosscycles(pptp->info, xtstamp); 108 108 if (err) 109 109 return err; 110 110 ··· 143 143 struct ptp_clock *ptp = vclock->pclock; 144 144 struct timespec64 ts = {}; 145 145 146 - if (ptp->info->gettimex64) 147 - ptp->info->gettimex64(ptp->info, &ts, NULL); 148 - else 149 - ptp->info->gettime64(ptp->info, &ts); 146 + ptp->info->getcycles64(ptp->info, &ts); 150 147 151 148 return timespec64_to_ns(&ts); 152 149 } ··· 165 168 166 169 vclock->pclock = pclock; 167 170 vclock->info = ptp_vclock_info; 168 - if (pclock->info->gettimex64) 171 + if (pclock->info->getcyclesx64) 169 172 vclock->info.gettimex64 = ptp_vclock_gettimex; 170 173 else 171 174 vclock->info.gettime64 = ptp_vclock_gettime; 172 - if (pclock->info->getcrosststamp) 175 + if (pclock->info->getcrosscycles) 173 176 vclock->info.getcrosststamp = ptp_vclock_getcrosststamp; 174 177 vclock->cc = ptp_vclock_cc; 175 178
+31
include/linux/ptp_clock_kernel.h
··· 108 108 * @settime64: Set the current time on the hardware clock. 109 109 * parameter ts: Time value to set. 110 110 * 111 + * @getcycles64: Reads the current free running cycle counter from the hardware 112 + * clock. 113 + * If @getcycles64 and @getcyclesx64 are not supported, then 114 + * @gettime64 or @gettimex64 will be used as default 115 + * implementation. 116 + * parameter ts: Holds the result. 117 + * 118 + * @getcyclesx64: Reads the current free running cycle counter from the 119 + * hardware clock and optionally also the system clock. 120 + * If @getcycles64 and @getcyclesx64 are not supported, then 121 + * @gettimex64 will be used as default implementation if 122 + * available. 123 + * parameter ts: Holds the PHC timestamp. 124 + * parameter sts: If not NULL, it holds a pair of timestamps 125 + * from the system clock. The first reading is made right before 126 + * reading the lowest bits of the PHC timestamp and the second 127 + * reading immediately follows that. 128 + * 129 + * @getcrosscycles: Reads the current free running cycle counter from the 130 + * hardware clock and system clock simultaneously. 131 + * If @getcycles64 and @getcyclesx64 are not supported, then 132 + * @getcrosststamp will be used as default implementation if 133 + * available. 134 + * parameter cts: Contains timestamp (device,system) pair, 135 + * where system time is realtime and monotonic. 136 + * 111 137 * @enable: Request driver to enable or disable an ancillary feature. 112 138 * parameter request: Desired resource to enable or disable. 113 139 * parameter on: Caller passes one to enable or zero to disable. ··· 181 155 int (*getcrosststamp)(struct ptp_clock_info *ptp, 182 156 struct system_device_crosststamp *cts); 183 157 int (*settime64)(struct ptp_clock_info *p, const struct timespec64 *ts); 158 + int (*getcycles64)(struct ptp_clock_info *ptp, struct timespec64 *ts); 159 + int (*getcyclesx64)(struct ptp_clock_info *ptp, struct timespec64 *ts, 160 + struct ptp_system_timestamp *sts); 161 + int (*getcrosscycles)(struct ptp_clock_info *ptp, 162 + struct system_device_crosststamp *cts); 184 163 int (*enable)(struct ptp_clock_info *ptp, 185 164 struct ptp_clock_request *request, int on); 186 165 int (*verify)(struct ptp_clock_info *ptp, unsigned int pin,