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

tsnep: Add free running cycle counter support

The TSN endpoint Ethernet MAC supports a free running counter
additionally to its clock. This free running counter can be read and
hardware timestamps are supported. As the name implies, this counter
cannot be set and its frequency cannot be adjusted.

Add free running cycle counter support based on this free running
counter to physical clock. This also requires hardware time stamps
based on that free running counter.

Signed-off-by: Gerhard Engleder <gerhard@engleder-embedded.com>
Acked-by: Jonathan Lemon <jonathan.lemon@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Gerhard Engleder and committed by
Paolo Abeni
0abb62b6 fcf308e5

+63 -7
+7 -2
drivers/net/ethernet/engleder/tsnep_hw.h
··· 43 43 #define ECM_RESET_CHANNEL 0x00000100 44 44 #define ECM_RESET_TXRX 0x00010000 45 45 46 + /* counter */ 47 + #define ECM_COUNTER_LOW 0x0028 48 + #define ECM_COUNTER_HIGH 0x002C 49 + 46 50 /* control and status */ 47 51 #define ECM_STATUS 0x0080 48 52 #define ECM_LINK_MODE_OFF 0x01000000 ··· 194 190 /* tsnep TX descriptor writeback */ 195 191 struct tsnep_tx_desc_wb { 196 192 __le32 properties; 197 - __le32 reserved1[3]; 193 + __le32 reserved1; 194 + __le64 counter; 198 195 __le64 timestamp; 199 196 __le32 dma_delay; 200 197 __le32 reserved2; ··· 226 221 227 222 /* tsnep RX inline meta */ 228 223 struct tsnep_rx_inline { 229 - __le64 reserved; 224 + __le64 counter; 230 225 __le64 timestamp; 231 226 }; 232 227
+28 -5
drivers/net/ethernet/engleder/tsnep_main.c
··· 470 470 (__le32_to_cpu(entry->desc_wb->properties) & 471 471 TSNEP_DESC_EXTENDED_WRITEBACK_FLAG)) { 472 472 struct skb_shared_hwtstamps hwtstamps; 473 - u64 timestamp = 474 - __le64_to_cpu(entry->desc_wb->timestamp); 473 + u64 timestamp; 474 + 475 + if (skb_shinfo(entry->skb)->tx_flags & 476 + SKBTX_HW_TSTAMP_USE_CYCLES) 477 + timestamp = 478 + __le64_to_cpu(entry->desc_wb->counter); 479 + else 480 + timestamp = 481 + __le64_to_cpu(entry->desc_wb->timestamp); 475 482 476 483 memset(&hwtstamps, 0, sizeof(hwtstamps)); 477 484 hwtstamps.hwtstamp = ns_to_ktime(timestamp); ··· 711 704 skb_hwtstamps(skb); 712 705 struct tsnep_rx_inline *rx_inline = 713 706 (struct tsnep_rx_inline *)skb->data; 714 - u64 timestamp = 715 - __le64_to_cpu(rx_inline->timestamp); 716 707 708 + skb_shinfo(skb)->tx_flags |= 709 + SKBTX_HW_TSTAMP_NETDEV; 717 710 memset(hwtstamps, 0, sizeof(*hwtstamps)); 718 - hwtstamps->hwtstamp = ns_to_ktime(timestamp); 711 + hwtstamps->netdev_data = rx_inline; 719 712 } 720 713 skb_pull(skb, TSNEP_RX_INLINE_METADATA_SIZE); 721 714 skb->protocol = eth_type_trans(skb, ··· 1017 1010 return 0; 1018 1011 } 1019 1012 1013 + static ktime_t tsnep_netdev_get_tstamp(struct net_device *netdev, 1014 + const struct skb_shared_hwtstamps *hwtstamps, 1015 + bool cycles) 1016 + { 1017 + struct tsnep_rx_inline *rx_inline = hwtstamps->netdev_data; 1018 + u64 timestamp; 1019 + 1020 + if (cycles) 1021 + timestamp = __le64_to_cpu(rx_inline->counter); 1022 + else 1023 + timestamp = __le64_to_cpu(rx_inline->timestamp); 1024 + 1025 + return ns_to_ktime(timestamp); 1026 + } 1027 + 1020 1028 static const struct net_device_ops tsnep_netdev_ops = { 1021 1029 .ndo_open = tsnep_netdev_open, 1022 1030 .ndo_stop = tsnep_netdev_close, ··· 1041 1019 1042 1020 .ndo_get_stats64 = tsnep_netdev_get_stats64, 1043 1021 .ndo_set_mac_address = tsnep_netdev_set_mac_address, 1022 + .ndo_get_tstamp = tsnep_netdev_get_tstamp, 1044 1023 .ndo_setup_tc = tsnep_tc_setup, 1045 1024 }; 1046 1025
+28
drivers/net/ethernet/engleder/tsnep_ptp.c
··· 175 175 return 0; 176 176 } 177 177 178 + static int tsnep_ptp_getcyclesx64(struct ptp_clock_info *ptp, 179 + struct timespec64 *ts, 180 + struct ptp_system_timestamp *sts) 181 + { 182 + struct tsnep_adapter *adapter = container_of(ptp, struct tsnep_adapter, 183 + ptp_clock_info); 184 + u32 high_before; 185 + u32 low; 186 + u32 high; 187 + u64 counter; 188 + 189 + /* read high dword twice to detect overrun */ 190 + high = ioread32(adapter->addr + ECM_COUNTER_HIGH); 191 + do { 192 + ptp_read_system_prets(sts); 193 + low = ioread32(adapter->addr + ECM_COUNTER_LOW); 194 + ptp_read_system_postts(sts); 195 + high_before = high; 196 + high = ioread32(adapter->addr + ECM_COUNTER_HIGH); 197 + } while (high != high_before); 198 + counter = (((u64)high) << 32) | ((u64)low); 199 + 200 + *ts = ns_to_timespec64(counter); 201 + 202 + return 0; 203 + } 204 + 178 205 int tsnep_ptp_init(struct tsnep_adapter *adapter) 179 206 { 180 207 int retval = 0; ··· 219 192 adapter->ptp_clock_info.adjtime = tsnep_ptp_adjtime; 220 193 adapter->ptp_clock_info.gettimex64 = tsnep_ptp_gettimex64; 221 194 adapter->ptp_clock_info.settime64 = tsnep_ptp_settime64; 195 + adapter->ptp_clock_info.getcyclesx64 = tsnep_ptp_getcyclesx64; 222 196 223 197 spin_lock_init(&adapter->ptp_lock); 224 198