Skip to content

Commit

Permalink
Merge branch 'ptp-support-hardware-clocks-with-additional-free-runnin…
Browse files Browse the repository at this point in the history
…g-cycle-counter'

Gerhard Engleder says:

====================
ptp: Support hardware clocks with additional free running cycle counter

ptp vclocks require a clock with free running time for the 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.

One year ago I thought for two time domains within a TSN network also
two physical clocks are required. This would lead to new kernel
interfaces for asking for the second clock, ... . But actually for a
time triggered system like TSN there can be only one time domain that
controls the system itself. All other time domains belong to other
layers, but not to the time triggered system itself. So other time
domains can be based on a free running counter if similar mechanisms
like 2 step synchroisation are used.

Synchronisation was tested with two time domains between two directly
connected hosts. Each host run two ptp4l instances, the first used the
physical clock and the second used the virtual clock. I used my FPGA
based network controller as network device. ptp4l was used in
combination with the virtual clock support patches from Miroslav
Lichvar.

v4:
- if_index of 0 is invalid (Jonathan Lemon)
- set if_index to 0 in the SOF_TIMESTAMPING_RAW_HARDWARE block (Jonathan
  Lemon)
- add helper function for netdev_get_tstamp() call (Jonathan Lemon)
- update SKBTX_ANY_TSTAMP (Paolo Abeni)
- use separate bits for new tx_flags (Richard Cochran)

v3:
- optimize ptp_convert_timestamp (Richard Cochran)
- call dev_get_by_napi_id() only if needed (Richard Cochran)
- use non-negated logical test (Richard Cochran)
- add comment for skipped output (Richard Cochran)
- add comment for SKBTX_HW_TSTAMP_USE_CYCLES masking (Richard Cochran)

v2:
- rename ptp_clock cycles to has_cycles (Richard Cochran)
- call it free running cycle counter (Richard Cochran)
- update struct skb_shared_hwtstamps kdoc (Richard Cochran)
- optimize timestamp address/cookie processing path (Richard Cochran,
  Vinicius Costa Gomes)

v1:
- complete rework based on suggestions (Richard Cochran)
====================

Link: https://lore.kernel.org/r/20220506200142.3329-1-gerhard@engleder-embedded.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
  • Loading branch information
Paolo Abeni committed May 10, 2022
2 parents b3552d6 + 0abb62b commit 8276345
Show file tree
Hide file tree
Showing 11 changed files with 282 additions and 63 deletions.
9 changes: 7 additions & 2 deletions drivers/net/ethernet/engleder/tsnep_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
#define ECM_RESET_CHANNEL 0x00000100
#define ECM_RESET_TXRX 0x00010000

/* counter */
#define ECM_COUNTER_LOW 0x0028
#define ECM_COUNTER_HIGH 0x002C

/* control and status */
#define ECM_STATUS 0x0080
#define ECM_LINK_MODE_OFF 0x01000000
Expand Down Expand Up @@ -190,7 +194,8 @@ struct tsnep_tx_desc {
/* tsnep TX descriptor writeback */
struct tsnep_tx_desc_wb {
__le32 properties;
__le32 reserved1[3];
__le32 reserved1;
__le64 counter;
__le64 timestamp;
__le32 dma_delay;
__le32 reserved2;
Expand Down Expand Up @@ -221,7 +226,7 @@ struct tsnep_rx_desc_wb {

/* tsnep RX inline meta */
struct tsnep_rx_inline {
__le64 reserved;
__le64 counter;
__le64 timestamp;
};

Expand Down
33 changes: 28 additions & 5 deletions drivers/net/ethernet/engleder/tsnep_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,15 @@ static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget)
(__le32_to_cpu(entry->desc_wb->properties) &
TSNEP_DESC_EXTENDED_WRITEBACK_FLAG)) {
struct skb_shared_hwtstamps hwtstamps;
u64 timestamp =
__le64_to_cpu(entry->desc_wb->timestamp);
u64 timestamp;

if (skb_shinfo(entry->skb)->tx_flags &
SKBTX_HW_TSTAMP_USE_CYCLES)
timestamp =
__le64_to_cpu(entry->desc_wb->counter);
else
timestamp =
__le64_to_cpu(entry->desc_wb->timestamp);

memset(&hwtstamps, 0, sizeof(hwtstamps));
hwtstamps.hwtstamp = ns_to_ktime(timestamp);
Expand Down Expand Up @@ -704,11 +711,11 @@ static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi,
skb_hwtstamps(skb);
struct tsnep_rx_inline *rx_inline =
(struct tsnep_rx_inline *)skb->data;
u64 timestamp =
__le64_to_cpu(rx_inline->timestamp);

skb_shinfo(skb)->tx_flags |=
SKBTX_HW_TSTAMP_NETDEV;
memset(hwtstamps, 0, sizeof(*hwtstamps));
hwtstamps->hwtstamp = ns_to_ktime(timestamp);
hwtstamps->netdev_data = rx_inline;
}
skb_pull(skb, TSNEP_RX_INLINE_METADATA_SIZE);
skb->protocol = eth_type_trans(skb,
Expand Down Expand Up @@ -1010,6 +1017,21 @@ static int tsnep_netdev_set_mac_address(struct net_device *netdev, void *addr)
return 0;
}

static ktime_t tsnep_netdev_get_tstamp(struct net_device *netdev,
const struct skb_shared_hwtstamps *hwtstamps,
bool cycles)
{
struct tsnep_rx_inline *rx_inline = hwtstamps->netdev_data;
u64 timestamp;

if (cycles)
timestamp = __le64_to_cpu(rx_inline->counter);
else
timestamp = __le64_to_cpu(rx_inline->timestamp);

return ns_to_ktime(timestamp);
}

static const struct net_device_ops tsnep_netdev_ops = {
.ndo_open = tsnep_netdev_open,
.ndo_stop = tsnep_netdev_close,
Expand All @@ -1019,6 +1041,7 @@ static const struct net_device_ops tsnep_netdev_ops = {

.ndo_get_stats64 = tsnep_netdev_get_stats64,
.ndo_set_mac_address = tsnep_netdev_set_mac_address,
.ndo_get_tstamp = tsnep_netdev_get_tstamp,
.ndo_setup_tc = tsnep_tc_setup,
};

Expand Down
28 changes: 28 additions & 0 deletions drivers/net/ethernet/engleder/tsnep_ptp.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,33 @@ static int tsnep_ptp_settime64(struct ptp_clock_info *ptp,
return 0;
}

static int tsnep_ptp_getcyclesx64(struct ptp_clock_info *ptp,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
struct tsnep_adapter *adapter = container_of(ptp, struct tsnep_adapter,
ptp_clock_info);
u32 high_before;
u32 low;
u32 high;
u64 counter;

/* read high dword twice to detect overrun */
high = ioread32(adapter->addr + ECM_COUNTER_HIGH);
do {
ptp_read_system_prets(sts);
low = ioread32(adapter->addr + ECM_COUNTER_LOW);
ptp_read_system_postts(sts);
high_before = high;
high = ioread32(adapter->addr + ECM_COUNTER_HIGH);
} while (high != high_before);
counter = (((u64)high) << 32) | ((u64)low);

*ts = ns_to_timespec64(counter);

return 0;
}

int tsnep_ptp_init(struct tsnep_adapter *adapter)
{
int retval = 0;
Expand All @@ -192,6 +219,7 @@ int tsnep_ptp_init(struct tsnep_adapter *adapter)
adapter->ptp_clock_info.adjtime = tsnep_ptp_adjtime;
adapter->ptp_clock_info.gettimex64 = tsnep_ptp_gettimex64;
adapter->ptp_clock_info.settime64 = tsnep_ptp_settime64;
adapter->ptp_clock_info.getcyclesx64 = tsnep_ptp_getcyclesx64;

spin_lock_init(&adapter->ptp_lock);

Expand Down
31 changes: 27 additions & 4 deletions drivers/ptp/ptp_clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ static int ptp_clock_settime(struct posix_clock *pc, const struct timespec64 *tp
{
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);

if (ptp_vclock_in_use(ptp)) {
pr_err("ptp: virtual clock in use\n");
if (ptp_clock_freerun(ptp)) {
pr_err("ptp: physical clock is free running\n");
return -EBUSY;
}

Expand All @@ -103,8 +103,8 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
struct ptp_clock_info *ops;
int err = -EOPNOTSUPP;

if (ptp_vclock_in_use(ptp)) {
pr_err("ptp: virtual clock in use\n");
if (ptp_clock_freerun(ptp)) {
pr_err("ptp: physical clock is free running\n");
return -EBUSY;
}

Expand Down Expand Up @@ -178,6 +178,14 @@ static void ptp_clock_release(struct device *dev)
kfree(ptp);
}

static int ptp_getcycles64(struct ptp_clock_info *info, struct timespec64 *ts)
{
if (info->getcyclesx64)
return info->getcyclesx64(info, ts, NULL);
else
return info->gettime64(info, ts);
}

static void ptp_aux_kworker(struct kthread_work *work)
{
struct ptp_clock *ptp = container_of(work, struct ptp_clock,
Expand Down Expand Up @@ -225,6 +233,21 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
mutex_init(&ptp->n_vclocks_mux);
init_waitqueue_head(&ptp->tsev_wq);

if (ptp->info->getcycles64 || ptp->info->getcyclesx64) {
ptp->has_cycles = true;
if (!ptp->info->getcycles64 && ptp->info->getcyclesx64)
ptp->info->getcycles64 = ptp_getcycles64;
} else {
/* Free running cycle counter not supported, use time. */
ptp->info->getcycles64 = ptp_getcycles64;

if (ptp->info->gettimex64)
ptp->info->getcyclesx64 = ptp->info->gettimex64;

if (ptp->info->getcrosststamp)
ptp->info->getcrosscycles = ptp->info->getcrosststamp;
}

if (ptp->info->do_aux_work) {
kthread_init_delayed_work(&ptp->aux_work, ptp_aux_kworker);
ptp->kworker = kthread_create_worker(0, "ptp%d", ptp->index);
Expand Down
11 changes: 11 additions & 0 deletions drivers/ptp/ptp_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct ptp_clock {
int *vclock_index;
struct mutex n_vclocks_mux; /* protect concurrent n_vclocks access */
bool is_virtual_clock;
bool has_cycles;
};

#define info_to_vclock(d) container_of((d), struct ptp_vclock, info)
Expand All @@ -62,6 +63,7 @@ struct ptp_vclock {
struct ptp_clock *pclock;
struct ptp_clock_info info;
struct ptp_clock *clock;
struct hlist_node vclock_hash_node;
struct cyclecounter cc;
struct timecounter tc;
spinlock_t lock; /* protects tc/cc */
Expand Down Expand Up @@ -96,6 +98,15 @@ static inline bool ptp_vclock_in_use(struct ptp_clock *ptp)
return in_use;
}

/* Check if ptp clock shall be free running */
static inline bool ptp_clock_freerun(struct ptp_clock *ptp)
{
if (ptp->has_cycles)
return false;

return ptp_vclock_in_use(ptp);
}

extern struct class *ptp_class;

/*
Expand Down
11 changes: 7 additions & 4 deletions drivers/ptp/ptp_sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,13 @@ static ssize_t n_vclocks_store(struct device *dev,
*(ptp->vclock_index + ptp->n_vclocks - i) = -1;
}

if (num == 0)
dev_info(dev, "only physical clock in use now\n");
else
dev_info(dev, "guarantee physical clock free running\n");
/* Need to inform about changed physical clock behavior */
if (!ptp->has_cycles) {
if (num == 0)
dev_info(dev, "only physical clock in use now\n");
else
dev_info(dev, "guarantee physical clock free running\n");
}

ptp->n_vclocks = num;
mutex_unlock(&ptp->n_vclocks_mux);
Expand Down
Loading

0 comments on commit 8276345

Please sign in to comment.