[net-next, v2, 3/7] ptp: support domains and timestamp conversion
From: Yangbo Lu <yangbo.lu@nxp.com>
Date: 2021-05-21 04:27:22
Subsystem:
networking drivers, ptp hardware clock support, ptp virtual clock support, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran, Yangbo Lu, Linus Torvalds
PTP virtual clocks support is actually for multiple domains synchronization. This patch is to support configuring domain value for PTP virtual clock via sysfs and to support hardware timestamp conversion to domain time. The idea is driver identifying PTP message domain number, matching with PTP virtual clock which has same domain number configured, and then converting to the PTP virtual clock time. The identifying could be in MAC driver, the matching and converting is through the API this patch added which can be called by MAC driver, - ptp_clock_domain_tstamp() Different domain values should be configured for multiple ptp virtual clocks based on same one free running ptp physical clock. If ptp message domain value has no PTP clock matched, the original hardware timestamp will be used. Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> --- Changes for v2: - Split from v1 patch #1 and #2. - Fixed build warning. --- Documentation/ABI/testing/sysfs-ptp | 12 +++++++ drivers/ptp/ptp_clock.c | 1 + drivers/ptp/ptp_private.h | 6 ++++ drivers/ptp/ptp_sysfs.c | 55 +++++++++++++++++++++++++++++ drivers/ptp/ptp_vclock.c | 33 +++++++++++++++++ include/linux/ptp_clock_kernel.h | 13 +++++++ 6 files changed, 120 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-ptp b/Documentation/ABI/testing/sysfs-ptp
index 6403e746eeb4..7a518185cb11 100644
--- a/Documentation/ABI/testing/sysfs-ptp
+++ b/Documentation/ABI/testing/sysfs-ptp@@ -25,6 +25,18 @@ Description: MAC based ones. The string does not necessarily have to be any kind of unique id. +What: /sys/class/ptp/ptpN/domain +Date: May 2021 +Contact: Yangbo Lu <yangbo.lu@nxp.com> +Description: + This file contains the domain value that the PTP virtual + clock serves. PTP virtual clock will provide timestamp + to PTP messages which have same domain matched. + Write value 0 ~ 255 into this file to change the domain. + Writing -1 means serving no domain. If PTP message domain + value does not have a PTP virtual clock matched, the + original hardware time stamp will be used. + What: /sys/class/ptp/ptpN/max_adjustment Date: September 2010 Contact: Richard Cochran <richardcochran@gmail.com>
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index 9b8ab1e6625f..4ad20b2aae57 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c@@ -216,6 +216,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, ptp->info = info; ptp->devid = MKDEV(major, index); ptp->index = index; + ptp->domain = -1; /* No domain set */ spin_lock_init(&ptp->tsevq.lock); mutex_init(&ptp->tsevq_mux); mutex_init(&ptp->pincfg_mux);
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index da24d0c83799..fe8f976b7b75 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h@@ -48,6 +48,7 @@ struct ptp_clock { struct kthread_worker *kworker; struct kthread_delayed_work aux_work; u8 num_vclocks; + int16_t domain; }; #define info_to_vclock(d) container_of((d), struct ptp_vclock, info)
@@ -70,6 +71,11 @@ struct ptp_vclock { u32 div_factor; }; +struct domain_tstamp { + u64 tstamp; + u8 domain; +}; + /* * The function queue_cnt() is safe for readers to call without * holding q->lock. Readers use this function to verify that the queue
diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c
index 5e1b5947dbff..b854661aadeb 100644
--- a/drivers/ptp/ptp_sysfs.c
+++ b/drivers/ptp/ptp_sysfs.c@@ -223,6 +223,57 @@ static ssize_t num_vclocks_store(struct device *dev, } static DEVICE_ATTR_RW(num_vclocks); +static int check_domain_avail(struct device *dev, void *data) +{ + struct ptp_clock *ptp = dev_get_drvdata(dev); + int16_t *domain = data; + + if (ptp->domain == *domain) + return -EINVAL; + + return 0; +} + +static ssize_t domain_show(struct device *dev, + struct device_attribute *attr, char *page) +{ + struct ptp_clock *ptp = dev_get_drvdata(dev); + + return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->domain); +} + +static ssize_t domain_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_clock *ptp = dev_get_drvdata(dev); + int err = -EINVAL; + int16_t domain; + + if (kstrtos16(buf, 0, &domain)) + goto out; + + if (domain > 255 || domain < -1) + goto out; + + if (domain == -1) { + ptp->domain = -1; + return count; + } + + if (device_for_each_child(dev->parent, &domain, check_domain_avail)) { + dev_err(dev, "the domain value already in used\n"); + goto out; + } + + ptp->domain = domain; + + return count; +out: + return err; +} +static DEVICE_ATTR_RW(domain); + static struct attribute *ptp_attrs[] = { &dev_attr_clock_name.attr,
@@ -238,6 +289,7 @@ static struct attribute *ptp_attrs[] = { &dev_attr_period.attr, &dev_attr_pps_enable.attr, &dev_attr_num_vclocks.attr, + &dev_attr_domain.attr, NULL };
@@ -262,6 +314,9 @@ static umode_t ptp_is_attribute_visible(struct kobject *kobj, } else if (attr == &dev_attr_num_vclocks.attr) { if (info->vclock_flag || !info->vclock_cc) mode = 0; + } else if (attr == &dev_attr_domain.attr) { + if (!info->vclock_flag) + mode = 0; } return mode;
diff --git a/drivers/ptp/ptp_vclock.c b/drivers/ptp/ptp_vclock.c
index fce09f10ae69..6e7d1403d024 100644
--- a/drivers/ptp/ptp_vclock.c
+++ b/drivers/ptp/ptp_vclock.c@@ -87,6 +87,39 @@ static void ptp_vclock_refresh(struct work_struct *work) schedule_delayed_work(&vclock->refresh_work, vclock->refresh_interval); } +static int ptp_convert_domain_tstamp(struct device *dev, void *data) +{ + struct ptp_clock *ptp = dev_get_drvdata(dev); + struct ptp_clock_info *info = ptp->info; + struct domain_tstamp *domain_ts = data; + struct ptp_vclock *vclock; + unsigned long flags; + + /* Convert to domain tstamp if there is a domain matched */ + if (ptp->domain == domain_ts->domain) { + vclock = info_to_vclock(info); + spin_lock_irqsave(&vclock->lock, flags); + domain_ts->tstamp = timecounter_cyc2time(&vclock->tc, + domain_ts->tstamp); + spin_unlock_irqrestore(&vclock->lock, flags); + return -EINVAL; /* For break. Not error. */ + } + + return 0; +} + +void ptp_clock_domain_tstamp(struct device *ptp_dev, u64 *tstamp, u8 domain) +{ + struct domain_tstamp domain_ts; + + domain_ts.tstamp = *tstamp; + domain_ts.domain = domain; + + device_for_each_child(ptp_dev, &domain_ts, ptp_convert_domain_tstamp); + *tstamp = domain_ts.tstamp; +} +EXPORT_SYMBOL(ptp_clock_domain_tstamp); + struct ptp_clock_info *ptp_get_pclock_info(const struct cyclecounter *cc) { struct ptp_vclock *vclock = cc_to_vclock(cc);
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
index 8f34f192f2cf..edc816cdfb2c 100644
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h@@ -343,6 +343,15 @@ void ptp_cancel_worker_sync(struct ptp_clock *ptp); * @cc: cyclecounter pointer of ptp virtual clock. */ struct ptp_clock_info *ptp_get_pclock_info(const struct cyclecounter *cc); + +/** + * ptp_clock_domain_tstamp() - convert to domain timestamp + * + * @ptp_dev: device pointer of current ptp clock. + * @tstamp: time stamp pointer to hardware time stamp + * @domain: domain number to convert + */ +void ptp_clock_domain_tstamp(struct device *ptp_dev, u64 *tstamp, u8 domain); #else static inline struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, struct device *parent)
@@ -366,6 +375,10 @@ static inline void ptp_cancel_worker_sync(struct ptp_clock *ptp) static inline struct ptp_clock_info *ptp_get_pclock_info( const struct cyclecounter *cc) { return NULL; } + +static inline void ptp_clock_domain_tstamp(struct device *dev, u64 *tstamp, + u8 domain) +{ } #endif static inline void ptp_read_system_prets(struct ptp_system_timestamp *sts)
--
2.25.1