Skip to content

Commit d484dc2

Browse files
committed
Merge branch '1GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue
Tony Nguyen says: ==================== 1GbE Intel Wired LAN Driver Updates 2021-08-24 Vinicius Costa Gomes says: This adds support for PCIe PTM (Precision Time Measurement) to the igc driver. PCIe PTM allows the NIC and Host clocks to be compared more precisely, improving the clock synchronization accuracy. Patch 1/4 reverts a commit that made pci_enable_ptm() private to the PCI subsystem, reverting makes it possible for it to be called from the drivers. Patch 2/4 adds the pcie_ptm_enabled() helper. Patch 3/4 calls pci_enable_ptm() from the igc driver. Patch 4/4 implements the PCIe PTM support. Exposing it via the .getcrosststamp() API implies that the time measurements are made synchronously with the ioctl(). The hardware was implemented so the most convenient way to retrieve that information would be asynchronously. So, to follow the expectations of the ioctl() we have to use less convenient ways, triggering an PCIe PTM dialog every time a ioctl() is received. Some questions are raised (also pointed out in the commit message): 1. Using convert_art_ns_to_tsc() is too x86 specific, there should be a common way to create a 'system_counterval_t' from a timestamp. 2. convert_art_ns_to_tsc() says that it should only be used when X86_FEATURE_TSC_KNOWN_FREQ is true, but during tests it works even when it returns false. Should that check be done? ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
2 parents 38cbd6e + a90ec84 commit d484dc2

File tree

8 files changed

+259
-3
lines changed

8 files changed

+259
-3
lines changed

drivers/net/ethernet/intel/igc/igc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ struct igc_adapter {
227227
struct timecounter tc;
228228
struct timespec64 prev_ptp_time; /* Pre-reset PTP clock */
229229
ktime_t ptp_reset_start; /* Reset time in clock mono */
230+
struct system_time_snapshot snapshot;
230231

231232
char fw_version[32];
232233

drivers/net/ethernet/intel/igc/igc_defines.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,37 @@
523523
#define IGC_RXCSUM_CRCOFL 0x00000800 /* CRC32 offload enable */
524524
#define IGC_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */
525525

526+
/* PCIe PTM Control */
527+
#define IGC_PTM_CTRL_START_NOW BIT(29) /* Start PTM Now */
528+
#define IGC_PTM_CTRL_EN BIT(30) /* Enable PTM */
529+
#define IGC_PTM_CTRL_TRIG BIT(31) /* PTM Cycle trigger */
530+
#define IGC_PTM_CTRL_SHRT_CYC(usec) (((usec) & 0x2f) << 2)
531+
#define IGC_PTM_CTRL_PTM_TO(usec) (((usec) & 0xff) << 8)
532+
533+
#define IGC_PTM_SHORT_CYC_DEFAULT 10 /* Default Short/interrupted cycle interval */
534+
#define IGC_PTM_CYC_TIME_DEFAULT 5 /* Default PTM cycle time */
535+
#define IGC_PTM_TIMEOUT_DEFAULT 255 /* Default timeout for PTM errors */
536+
537+
/* PCIe Digital Delay */
538+
#define IGC_PCIE_DIG_DELAY_DEFAULT 0x01440000
539+
540+
/* PCIe PHY Delay */
541+
#define IGC_PCIE_PHY_DELAY_DEFAULT 0x40900000
542+
543+
#define IGC_TIMADJ_ADJUST_METH 0x40000000
544+
545+
/* PCIe PTM Status */
546+
#define IGC_PTM_STAT_VALID BIT(0) /* PTM Status */
547+
#define IGC_PTM_STAT_RET_ERR BIT(1) /* Root port timeout */
548+
#define IGC_PTM_STAT_BAD_PTM_RES BIT(2) /* PTM Response msg instead of PTM Response Data */
549+
#define IGC_PTM_STAT_T4M1_OVFL BIT(3) /* T4 minus T1 overflow */
550+
#define IGC_PTM_STAT_ADJUST_1ST BIT(4) /* 1588 timer adjusted during 1st PTM cycle */
551+
#define IGC_PTM_STAT_ADJUST_CYC BIT(5) /* 1588 timer adjusted during non-1st PTM cycle */
552+
553+
/* PCIe PTM Cycle Control */
554+
#define IGC_PTM_CYCLE_CTRL_CYC_TIME(msec) ((msec) & 0x3ff) /* PTM Cycle Time (msec) */
555+
#define IGC_PTM_CYCLE_CTRL_AUTO_CYC_EN BIT(31) /* PTM Cycle Control */
556+
526557
/* GPY211 - I225 defines */
527558
#define GPY_MMD_MASK 0xFFFF0000
528559
#define GPY_MMD_SHIFT 16

drivers/net/ethernet/intel/igc/igc_main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <net/pkt_sched.h>
1313
#include <linux/bpf_trace.h>
1414
#include <net/xdp_sock_drv.h>
15+
#include <linux/pci.h>
16+
1517
#include <net/ipv6.h>
1618

1719
#include "igc.h"
@@ -6174,6 +6176,10 @@ static int igc_probe(struct pci_dev *pdev,
61746176

61756177
pci_enable_pcie_error_reporting(pdev);
61766178

6179+
err = pci_enable_ptm(pdev, NULL);
6180+
if (err < 0)
6181+
dev_info(&pdev->dev, "PCIe PTM not supported by PCIe bus/controller\n");
6182+
61776183
pci_set_master(pdev);
61786184

61796185
err = -ENOMEM;

drivers/net/ethernet/intel/igc/igc_ptp.c

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,18 @@
99
#include <linux/ptp_classify.h>
1010
#include <linux/clocksource.h>
1111
#include <linux/ktime.h>
12+
#include <linux/delay.h>
13+
#include <linux/iopoll.h>
1214

1315
#define INCVALUE_MASK 0x7fffffff
1416
#define ISGN 0x80000000
1517

1618
#define IGC_SYSTIM_OVERFLOW_PERIOD (HZ * 60 * 9)
1719
#define IGC_PTP_TX_TIMEOUT (HZ * 15)
1820

21+
#define IGC_PTM_STAT_SLEEP 2
22+
#define IGC_PTM_STAT_TIMEOUT 100
23+
1924
/* SYSTIM read access for I225 */
2025
void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts)
2126
{
@@ -752,6 +757,147 @@ int igc_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
752757
-EFAULT : 0;
753758
}
754759

760+
/* The two conditions below must be met for cross timestamping via
761+
* PCIe PTM:
762+
*
763+
* 1. We have an way to convert the timestamps in the PTM messages
764+
* to something related to the system clocks (right now, only
765+
* X86 systems with support for the Always Running Timer allow that);
766+
*
767+
* 2. We have PTM enabled in the path from the device to the PCIe root port.
768+
*/
769+
static bool igc_is_crosststamp_supported(struct igc_adapter *adapter)
770+
{
771+
return IS_ENABLED(CONFIG_X86_TSC) ? pcie_ptm_enabled(adapter->pdev) : false;
772+
}
773+
774+
static struct system_counterval_t igc_device_tstamp_to_system(u64 tstamp)
775+
{
776+
#if IS_ENABLED(CONFIG_X86_TSC)
777+
return convert_art_ns_to_tsc(tstamp);
778+
#else
779+
return (struct system_counterval_t) { };
780+
#endif
781+
}
782+
783+
static void igc_ptm_log_error(struct igc_adapter *adapter, u32 ptm_stat)
784+
{
785+
struct net_device *netdev = adapter->netdev;
786+
787+
switch (ptm_stat) {
788+
case IGC_PTM_STAT_RET_ERR:
789+
netdev_err(netdev, "PTM Error: Root port timeout\n");
790+
break;
791+
case IGC_PTM_STAT_BAD_PTM_RES:
792+
netdev_err(netdev, "PTM Error: Bad response, PTM Response Data expected\n");
793+
break;
794+
case IGC_PTM_STAT_T4M1_OVFL:
795+
netdev_err(netdev, "PTM Error: T4 minus T1 overflow\n");
796+
break;
797+
case IGC_PTM_STAT_ADJUST_1ST:
798+
netdev_err(netdev, "PTM Error: 1588 timer adjusted during first PTM cycle\n");
799+
break;
800+
case IGC_PTM_STAT_ADJUST_CYC:
801+
netdev_err(netdev, "PTM Error: 1588 timer adjusted during non-first PTM cycle\n");
802+
break;
803+
default:
804+
netdev_err(netdev, "PTM Error: Unknown error (%#x)\n", ptm_stat);
805+
break;
806+
}
807+
}
808+
809+
static int igc_phc_get_syncdevicetime(ktime_t *device,
810+
struct system_counterval_t *system,
811+
void *ctx)
812+
{
813+
u32 stat, t2_curr_h, t2_curr_l, ctrl;
814+
struct igc_adapter *adapter = ctx;
815+
struct igc_hw *hw = &adapter->hw;
816+
int err, count = 100;
817+
ktime_t t1, t2_curr;
818+
819+
/* Get a snapshot of system clocks to use as historic value. */
820+
ktime_get_snapshot(&adapter->snapshot);
821+
822+
do {
823+
/* Doing this in a loop because in the event of a
824+
* badly timed (ha!) system clock adjustment, we may
825+
* get PTM errors from the PCI root, but these errors
826+
* are transitory. Repeating the process returns valid
827+
* data eventually.
828+
*/
829+
830+
/* To "manually" start the PTM cycle we need to clear and
831+
* then set again the TRIG bit.
832+
*/
833+
ctrl = rd32(IGC_PTM_CTRL);
834+
ctrl &= ~IGC_PTM_CTRL_TRIG;
835+
wr32(IGC_PTM_CTRL, ctrl);
836+
ctrl |= IGC_PTM_CTRL_TRIG;
837+
wr32(IGC_PTM_CTRL, ctrl);
838+
839+
/* The cycle only starts "for real" when software notifies
840+
* that it has read the registers, this is done by setting
841+
* VALID bit.
842+
*/
843+
wr32(IGC_PTM_STAT, IGC_PTM_STAT_VALID);
844+
845+
err = readx_poll_timeout(rd32, IGC_PTM_STAT, stat,
846+
stat, IGC_PTM_STAT_SLEEP,
847+
IGC_PTM_STAT_TIMEOUT);
848+
if (err < 0) {
849+
netdev_err(adapter->netdev, "Timeout reading IGC_PTM_STAT register\n");
850+
return err;
851+
}
852+
853+
if ((stat & IGC_PTM_STAT_VALID) == IGC_PTM_STAT_VALID)
854+
break;
855+
856+
if (stat & ~IGC_PTM_STAT_VALID) {
857+
/* An error occurred, log it. */
858+
igc_ptm_log_error(adapter, stat);
859+
/* The STAT register is write-1-to-clear (W1C),
860+
* so write the previous error status to clear it.
861+
*/
862+
wr32(IGC_PTM_STAT, stat);
863+
continue;
864+
}
865+
} while (--count);
866+
867+
if (!count) {
868+
netdev_err(adapter->netdev, "Exceeded number of tries for PTM cycle\n");
869+
return -ETIMEDOUT;
870+
}
871+
872+
t1 = ktime_set(rd32(IGC_PTM_T1_TIM0_H), rd32(IGC_PTM_T1_TIM0_L));
873+
874+
t2_curr_l = rd32(IGC_PTM_CURR_T2_L);
875+
t2_curr_h = rd32(IGC_PTM_CURR_T2_H);
876+
877+
/* FIXME: When the register that tells the endianness of the
878+
* PTM registers are implemented, check them here and add the
879+
* appropriate conversion.
880+
*/
881+
t2_curr_h = swab32(t2_curr_h);
882+
883+
t2_curr = ((s64)t2_curr_h << 32 | t2_curr_l);
884+
885+
*device = t1;
886+
*system = igc_device_tstamp_to_system(t2_curr);
887+
888+
return 0;
889+
}
890+
891+
static int igc_ptp_getcrosststamp(struct ptp_clock_info *ptp,
892+
struct system_device_crosststamp *cts)
893+
{
894+
struct igc_adapter *adapter = container_of(ptp, struct igc_adapter,
895+
ptp_caps);
896+
897+
return get_device_system_crosststamp(igc_phc_get_syncdevicetime,
898+
adapter, &adapter->snapshot, cts);
899+
}
900+
755901
/**
756902
* igc_ptp_init - Initialize PTP functionality
757903
* @adapter: Board private structure
@@ -788,6 +934,11 @@ void igc_ptp_init(struct igc_adapter *adapter)
788934
adapter->ptp_caps.n_per_out = IGC_N_PEROUT;
789935
adapter->ptp_caps.n_pins = IGC_N_SDP;
790936
adapter->ptp_caps.verify = igc_ptp_verify_pin;
937+
938+
if (!igc_is_crosststamp_supported(adapter))
939+
break;
940+
941+
adapter->ptp_caps.getcrosststamp = igc_ptp_getcrosststamp;
791942
break;
792943
default:
793944
adapter->ptp_clock = NULL;
@@ -878,7 +1029,9 @@ void igc_ptp_stop(struct igc_adapter *adapter)
8781029
void igc_ptp_reset(struct igc_adapter *adapter)
8791030
{
8801031
struct igc_hw *hw = &adapter->hw;
1032+
u32 cycle_ctrl, ctrl;
8811033
unsigned long flags;
1034+
u32 timadj;
8821035

8831036
/* reset the tstamp_config */
8841037
igc_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
@@ -887,12 +1040,38 @@ void igc_ptp_reset(struct igc_adapter *adapter)
8871040

8881041
switch (adapter->hw.mac.type) {
8891042
case igc_i225:
1043+
timadj = rd32(IGC_TIMADJ);
1044+
timadj |= IGC_TIMADJ_ADJUST_METH;
1045+
wr32(IGC_TIMADJ, timadj);
1046+
8901047
wr32(IGC_TSAUXC, 0x0);
8911048
wr32(IGC_TSSDP, 0x0);
8921049
wr32(IGC_TSIM,
8931050
IGC_TSICR_INTERRUPTS |
8941051
(adapter->pps_sys_wrap_on ? IGC_TSICR_SYS_WRAP : 0));
8951052
wr32(IGC_IMS, IGC_IMS_TS);
1053+
1054+
if (!igc_is_crosststamp_supported(adapter))
1055+
break;
1056+
1057+
wr32(IGC_PCIE_DIG_DELAY, IGC_PCIE_DIG_DELAY_DEFAULT);
1058+
wr32(IGC_PCIE_PHY_DELAY, IGC_PCIE_PHY_DELAY_DEFAULT);
1059+
1060+
cycle_ctrl = IGC_PTM_CYCLE_CTRL_CYC_TIME(IGC_PTM_CYC_TIME_DEFAULT);
1061+
1062+
wr32(IGC_PTM_CYCLE_CTRL, cycle_ctrl);
1063+
1064+
ctrl = IGC_PTM_CTRL_EN |
1065+
IGC_PTM_CTRL_START_NOW |
1066+
IGC_PTM_CTRL_SHRT_CYC(IGC_PTM_SHORT_CYC_DEFAULT) |
1067+
IGC_PTM_CTRL_PTM_TO(IGC_PTM_TIMEOUT_DEFAULT) |
1068+
IGC_PTM_CTRL_TRIG;
1069+
1070+
wr32(IGC_PTM_CTRL, ctrl);
1071+
1072+
/* Force the first cycle to run. */
1073+
wr32(IGC_PTM_STAT, IGC_PTM_STAT_VALID);
1074+
8961075
break;
8971076
default:
8981077
/* No work to do. */

drivers/net/ethernet/intel/igc/igc_regs.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,29 @@
245245
#define IGC_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */
246246
#define IGC_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */
247247

248+
#define IGC_TIMADJ 0x0B60C /* Time Adjustment Offset Register */
249+
250+
/* PCIe Registers */
251+
#define IGC_PTM_CTRL 0x12540 /* PTM Control */
252+
#define IGC_PTM_STAT 0x12544 /* PTM Status */
253+
#define IGC_PTM_CYCLE_CTRL 0x1254C /* PTM Cycle Control */
254+
255+
/* PTM Time registers */
256+
#define IGC_PTM_T1_TIM0_L 0x12558 /* T1 on Timer 0 Low */
257+
#define IGC_PTM_T1_TIM0_H 0x1255C /* T1 on Timer 0 High */
258+
259+
#define IGC_PTM_CURR_T2_L 0x1258C /* Current T2 Low */
260+
#define IGC_PTM_CURR_T2_H 0x12590 /* Current T2 High */
261+
#define IGC_PTM_PREV_T2_L 0x12584 /* Previous T2 Low */
262+
#define IGC_PTM_PREV_T2_H 0x12588 /* Previous T2 High */
263+
#define IGC_PTM_PREV_T4M1 0x12578 /* T4 Minus T1 on previous PTM Cycle */
264+
#define IGC_PTM_CURR_T4M1 0x1257C /* T4 Minus T1 on this PTM Cycle */
265+
#define IGC_PTM_PREV_T3M2 0x12580 /* T3 Minus T2 on previous PTM Cycle */
266+
#define IGC_PTM_TDELAY 0x12594 /* PTM PCIe Link Delay */
267+
268+
#define IGC_PCIE_DIG_DELAY 0x12550 /* PCIe Digital Delay */
269+
#define IGC_PCIE_PHY_DELAY 0x12554 /* PCIe PHY Delay */
270+
248271
/* Management registers */
249272
#define IGC_MANC 0x05820 /* Management Control - RW */
250273

drivers/pci/pci.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -597,11 +597,8 @@ static inline void pcie_ecrc_get_policy(char *str) { }
597597

598598
#ifdef CONFIG_PCIE_PTM
599599
void pci_ptm_init(struct pci_dev *dev);
600-
int pci_enable_ptm(struct pci_dev *dev, u8 *granularity);
601600
#else
602601
static inline void pci_ptm_init(struct pci_dev *dev) { }
603-
static inline int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
604-
{ return -EINVAL; }
605602
#endif
606603

607604
struct pci_dev_reset_methods {

drivers/pci/pcie/ptm.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,12 @@ int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
204204
return 0;
205205
}
206206
EXPORT_SYMBOL(pci_enable_ptm);
207+
208+
bool pcie_ptm_enabled(struct pci_dev *dev)
209+
{
210+
if (!dev)
211+
return false;
212+
213+
return dev->ptm_enabled;
214+
}
215+
EXPORT_SYMBOL(pcie_ptm_enabled);

include/linux/pci.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,6 +1620,16 @@ static inline bool pci_aer_available(void) { return false; }
16201620

16211621
bool pci_ats_disabled(void);
16221622

1623+
#ifdef CONFIG_PCIE_PTM
1624+
int pci_enable_ptm(struct pci_dev *dev, u8 *granularity);
1625+
bool pcie_ptm_enabled(struct pci_dev *dev);
1626+
#else
1627+
static inline int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
1628+
{ return -EINVAL; }
1629+
static inline bool pcie_ptm_enabled(struct pci_dev *dev)
1630+
{ return false; }
1631+
#endif
1632+
16231633
void pci_cfg_access_lock(struct pci_dev *dev);
16241634
bool pci_cfg_access_trylock(struct pci_dev *dev);
16251635
void pci_cfg_access_unlock(struct pci_dev *dev);

0 commit comments

Comments
 (0)