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

ptp: support ptp physical/virtual clocks conversion

Support ptp physical/virtual clocks conversion via sysfs.
There will be a new attribute n_vclocks under ptp physical
clock sysfs.

- In default, the value is 0 meaning only ptp physical clock
is in use.
- Setting the value can create corresponding number of ptp
virtual clocks to use. But current physical clock is guaranteed
to stay free running.
- Setting the value back to 0 can delete virtual clocks and back
use physical clock again.

Another new attribute max_vclocks control the maximum number of
ptp vclocks.

Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Yangbo Lu and committed by
David S. Miller
73f37068 5d43f951

+205
+20
Documentation/ABI/testing/sysfs-ptp
··· 33 33 frequency adjustment value (a positive integer) in 34 34 parts per billion. 35 35 36 + What: /sys/class/ptp/ptpN/max_vclocks 37 + Date: May 2021 38 + Contact: Yangbo Lu <yangbo.lu@nxp.com> 39 + Description: 40 + This file contains the maximum number of ptp vclocks. 41 + Write integer to re-configure it. 42 + 36 43 What: /sys/class/ptp/ptpN/n_alarms 37 44 Date: September 2010 38 45 Contact: Richard Cochran <richardcochran@gmail.com> ··· 67 60 Description: 68 61 This file contains the number of programmable pins 69 62 offered by the PTP hardware clock. 63 + 64 + What: /sys/class/ptp/ptpN/n_vclocks 65 + Date: May 2021 66 + Contact: Yangbo Lu <yangbo.lu@nxp.com> 67 + Description: 68 + This file contains the number of virtual PTP clocks in 69 + use. By default, the value is 0 meaning that only the 70 + physical clock is in use. Setting the value creates 71 + the corresponding number of virtual clocks and causes 72 + the physical clock to become free running. Setting the 73 + value back to 0 deletes the virtual clocks and 74 + switches the physical clock back to normal, adjustable 75 + operation. 70 76 71 77 What: /sys/class/ptp/ptpN/pins 72 78 Date: March 2014
+26
drivers/ptp/ptp_clock.c
··· 76 76 { 77 77 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 78 78 79 + if (ptp_vclock_in_use(ptp)) { 80 + pr_err("ptp: virtual clock in use\n"); 81 + return -EBUSY; 82 + } 83 + 79 84 return ptp->info->settime64(ptp->info, tp); 80 85 } 81 86 ··· 101 96 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 102 97 struct ptp_clock_info *ops; 103 98 int err = -EOPNOTSUPP; 99 + 100 + if (ptp_vclock_in_use(ptp)) { 101 + pr_err("ptp: virtual clock in use\n"); 102 + return -EBUSY; 103 + } 104 104 105 105 ops = ptp->info; 106 106 ··· 171 161 ptp_cleanup_pin_groups(ptp); 172 162 mutex_destroy(&ptp->tsevq_mux); 173 163 mutex_destroy(&ptp->pincfg_mux); 164 + mutex_destroy(&ptp->n_vclocks_mux); 174 165 ida_simple_remove(&ptp_clocks_map, ptp->index); 175 166 kfree(ptp); 176 167 } ··· 219 208 spin_lock_init(&ptp->tsevq.lock); 220 209 mutex_init(&ptp->tsevq_mux); 221 210 mutex_init(&ptp->pincfg_mux); 211 + mutex_init(&ptp->n_vclocks_mux); 222 212 init_waitqueue_head(&ptp->tsev_wq); 223 213 224 214 if (ptp->info->do_aux_work) { ··· 232 220 } 233 221 ptp->pps_source->lookup_cookie = ptp; 234 222 } 223 + 224 + /* PTP virtual clock is being registered under physical clock */ 225 + if (parent->class && parent->class->name && 226 + strcmp(parent->class->name, "ptp") == 0) 227 + ptp->is_virtual_clock = true; 228 + 229 + if (!ptp->is_virtual_clock) 230 + ptp->max_vclocks = PTP_DEFAULT_MAX_VCLOCKS; 235 231 236 232 err = ptp_populate_pin_groups(ptp); 237 233 if (err) ··· 290 270 kworker_err: 291 271 mutex_destroy(&ptp->tsevq_mux); 292 272 mutex_destroy(&ptp->pincfg_mux); 273 + mutex_destroy(&ptp->n_vclocks_mux); 293 274 ida_simple_remove(&ptp_clocks_map, index); 294 275 no_slot: 295 276 kfree(ptp); ··· 301 280 302 281 int ptp_clock_unregister(struct ptp_clock *ptp) 303 282 { 283 + if (ptp_vclock_in_use(ptp)) { 284 + pr_err("ptp: virtual clock in use\n"); 285 + return -EBUSY; 286 + } 287 + 304 288 ptp->defunct = 1; 305 289 wake_up_interruptible(&ptp->tsev_wq); 306 290
+21
drivers/ptp/ptp_private.h
··· 18 18 19 19 #define PTP_MAX_TIMESTAMPS 128 20 20 #define PTP_BUF_TIMESTAMPS 30 21 + #define PTP_DEFAULT_MAX_VCLOCKS 20 21 22 22 23 struct timestamp_event_queue { 23 24 struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS]; ··· 47 46 const struct attribute_group *pin_attr_groups[2]; 48 47 struct kthread_worker *kworker; 49 48 struct kthread_delayed_work aux_work; 49 + unsigned int max_vclocks; 50 + unsigned int n_vclocks; 51 + struct mutex n_vclocks_mux; /* protect concurrent n_vclocks access */ 52 + bool is_virtual_clock; 50 53 }; 51 54 52 55 #define info_to_vclock(d) container_of((d), struct ptp_vclock, info) ··· 77 72 { 78 73 int cnt = q->tail - q->head; 79 74 return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt; 75 + } 76 + 77 + /* Check if ptp virtual clock is in use */ 78 + static inline bool ptp_vclock_in_use(struct ptp_clock *ptp) 79 + { 80 + bool in_use = false; 81 + 82 + if (mutex_lock_interruptible(&ptp->n_vclocks_mux)) 83 + return true; 84 + 85 + if (!ptp->is_virtual_clock && ptp->n_vclocks) 86 + in_use = true; 87 + 88 + mutex_unlock(&ptp->n_vclocks_mux); 89 + 90 + return in_use; 80 91 } 81 92 82 93 /*
+138
drivers/ptp/ptp_sysfs.c
··· 3 3 * PTP 1588 clock support - sysfs interface. 4 4 * 5 5 * Copyright (C) 2010 OMICRON electronics GmbH 6 + * Copyright 2021 NXP 6 7 */ 7 8 #include <linux/capability.h> 8 9 #include <linux/slab.h> ··· 149 148 } 150 149 static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store); 151 150 151 + static int unregister_vclock(struct device *dev, void *data) 152 + { 153 + struct ptp_clock *ptp = dev_get_drvdata(dev); 154 + struct ptp_clock_info *info = ptp->info; 155 + struct ptp_vclock *vclock; 156 + u8 *num = data; 157 + 158 + vclock = info_to_vclock(info); 159 + dev_info(dev->parent, "delete virtual clock ptp%d\n", 160 + vclock->clock->index); 161 + 162 + ptp_vclock_unregister(vclock); 163 + (*num)--; 164 + 165 + /* For break. Not error. */ 166 + if (*num == 0) 167 + return -EINVAL; 168 + 169 + return 0; 170 + } 171 + 172 + static ssize_t n_vclocks_show(struct device *dev, 173 + struct device_attribute *attr, char *page) 174 + { 175 + struct ptp_clock *ptp = dev_get_drvdata(dev); 176 + ssize_t size; 177 + 178 + if (mutex_lock_interruptible(&ptp->n_vclocks_mux)) 179 + return -ERESTARTSYS; 180 + 181 + size = snprintf(page, PAGE_SIZE - 1, "%d\n", ptp->n_vclocks); 182 + 183 + mutex_unlock(&ptp->n_vclocks_mux); 184 + 185 + return size; 186 + } 187 + 188 + static ssize_t n_vclocks_store(struct device *dev, 189 + struct device_attribute *attr, 190 + const char *buf, size_t count) 191 + { 192 + struct ptp_clock *ptp = dev_get_drvdata(dev); 193 + struct ptp_vclock *vclock; 194 + int err = -EINVAL; 195 + u32 num, i; 196 + 197 + if (kstrtou32(buf, 0, &num)) 198 + return err; 199 + 200 + if (mutex_lock_interruptible(&ptp->n_vclocks_mux)) 201 + return -ERESTARTSYS; 202 + 203 + if (num > ptp->max_vclocks) { 204 + dev_err(dev, "max value is %d\n", ptp->max_vclocks); 205 + goto out; 206 + } 207 + 208 + /* Need to create more vclocks */ 209 + if (num > ptp->n_vclocks) { 210 + for (i = 0; i < num - ptp->n_vclocks; i++) { 211 + vclock = ptp_vclock_register(ptp); 212 + if (!vclock) 213 + goto out; 214 + 215 + dev_info(dev, "new virtual clock ptp%d\n", 216 + vclock->clock->index); 217 + } 218 + } 219 + 220 + /* Need to delete vclocks */ 221 + if (num < ptp->n_vclocks) { 222 + i = ptp->n_vclocks - num; 223 + device_for_each_child_reverse(dev, &i, 224 + unregister_vclock); 225 + } 226 + 227 + if (num == 0) 228 + dev_info(dev, "only physical clock in use now\n"); 229 + else 230 + dev_info(dev, "guarantee physical clock free running\n"); 231 + 232 + ptp->n_vclocks = num; 233 + mutex_unlock(&ptp->n_vclocks_mux); 234 + 235 + return count; 236 + out: 237 + mutex_unlock(&ptp->n_vclocks_mux); 238 + return err; 239 + } 240 + static DEVICE_ATTR_RW(n_vclocks); 241 + 242 + static ssize_t max_vclocks_show(struct device *dev, 243 + struct device_attribute *attr, char *page) 244 + { 245 + struct ptp_clock *ptp = dev_get_drvdata(dev); 246 + ssize_t size; 247 + 248 + size = snprintf(page, PAGE_SIZE - 1, "%d\n", ptp->max_vclocks); 249 + 250 + return size; 251 + } 252 + 253 + static ssize_t max_vclocks_store(struct device *dev, 254 + struct device_attribute *attr, 255 + const char *buf, size_t count) 256 + { 257 + struct ptp_clock *ptp = dev_get_drvdata(dev); 258 + u32 max; 259 + 260 + if (kstrtou32(buf, 0, &max) || max == 0) 261 + return -EINVAL; 262 + 263 + if (max == ptp->max_vclocks) 264 + return count; 265 + 266 + if (mutex_lock_interruptible(&ptp->n_vclocks_mux)) 267 + return -ERESTARTSYS; 268 + 269 + if (max < ptp->n_vclocks) { 270 + mutex_unlock(&ptp->n_vclocks_mux); 271 + return -EINVAL; 272 + } 273 + 274 + ptp->max_vclocks = max; 275 + 276 + mutex_unlock(&ptp->n_vclocks_mux); 277 + 278 + return count; 279 + } 280 + static DEVICE_ATTR_RW(max_vclocks); 281 + 152 282 static struct attribute *ptp_attrs[] = { 153 283 &dev_attr_clock_name.attr, 154 284 ··· 294 162 &dev_attr_fifo.attr, 295 163 &dev_attr_period.attr, 296 164 &dev_attr_pps_enable.attr, 165 + &dev_attr_n_vclocks.attr, 166 + &dev_attr_max_vclocks.attr, 297 167 NULL 298 168 }; 299 169 ··· 316 182 mode = 0; 317 183 } else if (attr == &dev_attr_pps_enable.attr) { 318 184 if (!info->pps) 185 + mode = 0; 186 + } else if (attr == &dev_attr_n_vclocks.attr || 187 + attr == &dev_attr_max_vclocks.attr) { 188 + if (ptp->is_virtual_clock) 319 189 mode = 0; 320 190 } 321 191