aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2022-05-10 09:48:11 +0200
committerPaolo Abeni <pabeni@redhat.com>2022-05-10 09:48:11 +0200
commit827634531e344850106c7ac618a3acb5ac40f6e6 (patch)
tree43baba2e7a4bf6193a9f47686f3af405a3923989 /drivers/net
parenteth: dpaa2-mac: remove a dead-code NULL check on fwnode parent (diff)
parenttsnep: Add free running cycle counter support (diff)
downloadlinux-dev-827634531e344850106c7ac618a3acb5ac40f6e6.tar.xz
linux-dev-827634531e344850106c7ac618a3acb5ac40f6e6.zip
Merge branch 'ptp-support-hardware-clocks-with-additional-free-running-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>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/ethernet/engleder/tsnep_hw.h9
-rw-r--r--drivers/net/ethernet/engleder/tsnep_main.c33
-rw-r--r--drivers/net/ethernet/engleder/tsnep_ptp.c28
3 files changed, 63 insertions, 7 deletions
diff --git a/drivers/net/ethernet/engleder/tsnep_hw.h b/drivers/net/ethernet/engleder/tsnep_hw.h
index 71cc8577d640..916ceac3ada2 100644
--- a/drivers/net/ethernet/engleder/tsnep_hw.h
+++ b/drivers/net/ethernet/engleder/tsnep_hw.h
@@ -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
@@ -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;
@@ -221,7 +226,7 @@ struct tsnep_rx_desc_wb {
/* tsnep RX inline meta */
struct tsnep_rx_inline {
- __le64 reserved;
+ __le64 counter;
__le64 timestamp;
};
diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c
index 49c93aa38862..cb069a0af7b9 100644
--- a/drivers/net/ethernet/engleder/tsnep_main.c
+++ b/drivers/net/ethernet/engleder/tsnep_main.c
@@ -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);
@@ -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,
@@ -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,
@@ -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,
};
diff --git a/drivers/net/ethernet/engleder/tsnep_ptp.c b/drivers/net/ethernet/engleder/tsnep_ptp.c
index eaad453d487e..54fbf0126815 100644
--- a/drivers/net/ethernet/engleder/tsnep_ptp.c
+++ b/drivers/net/ethernet/engleder/tsnep_ptp.c
@@ -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;
@@ -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);