.. | .. |
---|
11 | 11 | * operate with the nanosecond field directly without fear of overflow. |
---|
12 | 12 | * |
---|
13 | 13 | * Much like the 82599, the update period is dependent upon the link speed: |
---|
14 | | - * At 40Gb link or no link, the period is 1.6ns. |
---|
15 | | - * At 10Gb link, the period is multiplied by 2. (3.2ns) |
---|
| 14 | + * At 40Gb, 25Gb, or no link, the period is 1.6ns. |
---|
| 15 | + * At 10Gb or 5Gb link, the period is multiplied by 2. (3.2ns) |
---|
16 | 16 | * At 1Gb link, the period is multiplied by 20. (32ns) |
---|
17 | 17 | * 1588 functionality is not supported at 100Mbps. |
---|
18 | 18 | */ |
---|
19 | 19 | #define I40E_PTP_40GB_INCVAL 0x0199999999ULL |
---|
20 | 20 | #define I40E_PTP_10GB_INCVAL_MULT 2 |
---|
| 21 | +#define I40E_PTP_5GB_INCVAL_MULT 2 |
---|
21 | 22 | #define I40E_PTP_1GB_INCVAL_MULT 20 |
---|
22 | 23 | |
---|
23 | 24 | #define I40E_PRTTSYN_CTL1_TSYNTYPE_V1 BIT(I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT) |
---|
.. | .. |
---|
28 | 29 | * i40e_ptp_read - Read the PHC time from the device |
---|
29 | 30 | * @pf: Board private structure |
---|
30 | 31 | * @ts: timespec structure to hold the current time value |
---|
| 32 | + * @sts: structure to hold the system time before and after reading the PHC |
---|
31 | 33 | * |
---|
32 | 34 | * This function reads the PRTTSYN_TIME registers and stores them in a |
---|
33 | 35 | * timespec. However, since the registers are 64 bits of nanoseconds, we must |
---|
34 | 36 | * convert the result to a timespec before we can return. |
---|
35 | 37 | **/ |
---|
36 | | -static void i40e_ptp_read(struct i40e_pf *pf, struct timespec64 *ts) |
---|
| 38 | +static void i40e_ptp_read(struct i40e_pf *pf, struct timespec64 *ts, |
---|
| 39 | + struct ptp_system_timestamp *sts) |
---|
37 | 40 | { |
---|
38 | 41 | struct i40e_hw *hw = &pf->hw; |
---|
39 | 42 | u32 hi, lo; |
---|
40 | 43 | u64 ns; |
---|
41 | 44 | |
---|
42 | 45 | /* The timer latches on the lowest register read. */ |
---|
| 46 | + ptp_read_system_prets(sts); |
---|
43 | 47 | lo = rd32(hw, I40E_PRTTSYN_TIME_L); |
---|
| 48 | + ptp_read_system_postts(sts); |
---|
44 | 49 | hi = rd32(hw, I40E_PRTTSYN_TIME_H); |
---|
45 | 50 | |
---|
46 | 51 | ns = (((u64)hi) << 32) | lo; |
---|
.. | .. |
---|
136 | 141 | * @ptp: The PTP clock structure |
---|
137 | 142 | * @delta: Offset in nanoseconds to adjust the PHC time by |
---|
138 | 143 | * |
---|
139 | | - * Adjust the frequency of the PHC by the indicated parts per billion from the |
---|
140 | | - * base frequency. |
---|
| 144 | + * Adjust the current clock time by a delta specified in nanoseconds. |
---|
141 | 145 | **/ |
---|
142 | 146 | static int i40e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) |
---|
143 | 147 | { |
---|
144 | 148 | struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps); |
---|
145 | | - struct timespec64 now; |
---|
| 149 | + struct timespec64 now, then; |
---|
146 | 150 | |
---|
| 151 | + then = ns_to_timespec64(delta); |
---|
147 | 152 | mutex_lock(&pf->tmreg_lock); |
---|
148 | 153 | |
---|
149 | | - i40e_ptp_read(pf, &now); |
---|
150 | | - timespec64_add_ns(&now, delta); |
---|
| 154 | + i40e_ptp_read(pf, &now, NULL); |
---|
| 155 | + now = timespec64_add(now, then); |
---|
151 | 156 | i40e_ptp_write(pf, (const struct timespec64 *)&now); |
---|
152 | 157 | |
---|
153 | 158 | mutex_unlock(&pf->tmreg_lock); |
---|
.. | .. |
---|
156 | 161 | } |
---|
157 | 162 | |
---|
158 | 163 | /** |
---|
159 | | - * i40e_ptp_gettime - Get the time of the PHC |
---|
| 164 | + * i40e_ptp_gettimex - Get the time of the PHC |
---|
160 | 165 | * @ptp: The PTP clock structure |
---|
161 | 166 | * @ts: timespec structure to hold the current time value |
---|
| 167 | + * @sts: structure to hold the system time before and after reading the PHC |
---|
162 | 168 | * |
---|
163 | 169 | * Read the device clock and return the correct value on ns, after converting it |
---|
164 | 170 | * into a timespec struct. |
---|
165 | 171 | **/ |
---|
166 | | -static int i40e_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) |
---|
| 172 | +static int i40e_ptp_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts, |
---|
| 173 | + struct ptp_system_timestamp *sts) |
---|
167 | 174 | { |
---|
168 | 175 | struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps); |
---|
169 | 176 | |
---|
170 | 177 | mutex_lock(&pf->tmreg_lock); |
---|
171 | | - i40e_ptp_read(pf, ts); |
---|
| 178 | + i40e_ptp_read(pf, ts, sts); |
---|
172 | 179 | mutex_unlock(&pf->tmreg_lock); |
---|
173 | 180 | |
---|
174 | 181 | return 0; |
---|
.. | .. |
---|
253 | 260 | /** |
---|
254 | 261 | * i40e_ptp_rx_hang - Detect error case when Rx timestamp registers are hung |
---|
255 | 262 | * @pf: The PF private data structure |
---|
256 | | - * @vsi: The VSI with the rings relevant to 1588 |
---|
257 | 263 | * |
---|
258 | 264 | * This watchdog task is scheduled to detect error case where hardware has |
---|
259 | 265 | * dropped an Rx packet that was timestamped when the ring is full. The |
---|
.. | .. |
---|
460 | 466 | case I40E_LINK_SPEED_10GB: |
---|
461 | 467 | mult = I40E_PTP_10GB_INCVAL_MULT; |
---|
462 | 468 | break; |
---|
| 469 | + case I40E_LINK_SPEED_5GB: |
---|
| 470 | + mult = I40E_PTP_5GB_INCVAL_MULT; |
---|
| 471 | + break; |
---|
463 | 472 | case I40E_LINK_SPEED_1GB: |
---|
464 | 473 | mult = I40E_PTP_1GB_INCVAL_MULT; |
---|
465 | 474 | break; |
---|
.. | .. |
---|
580 | 589 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: |
---|
581 | 590 | if (!(pf->hw_features & I40E_HW_PTP_L4_CAPABLE)) |
---|
582 | 591 | return -ERANGE; |
---|
583 | | - /* fall through */ |
---|
| 592 | + fallthrough; |
---|
584 | 593 | case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: |
---|
585 | 594 | case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: |
---|
586 | 595 | case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: |
---|
.. | .. |
---|
694 | 703 | if (!IS_ERR_OR_NULL(pf->ptp_clock)) |
---|
695 | 704 | return 0; |
---|
696 | 705 | |
---|
697 | | - strncpy(pf->ptp_caps.name, i40e_driver_name, |
---|
| 706 | + strlcpy(pf->ptp_caps.name, i40e_driver_name, |
---|
698 | 707 | sizeof(pf->ptp_caps.name) - 1); |
---|
699 | 708 | pf->ptp_caps.owner = THIS_MODULE; |
---|
700 | 709 | pf->ptp_caps.max_adj = 999999999; |
---|
.. | .. |
---|
702 | 711 | pf->ptp_caps.pps = 0; |
---|
703 | 712 | pf->ptp_caps.adjfreq = i40e_ptp_adjfreq; |
---|
704 | 713 | pf->ptp_caps.adjtime = i40e_ptp_adjtime; |
---|
705 | | - pf->ptp_caps.gettime64 = i40e_ptp_gettime; |
---|
| 714 | + pf->ptp_caps.gettimex64 = i40e_ptp_gettimex; |
---|
706 | 715 | pf->ptp_caps.settime64 = i40e_ptp_settime; |
---|
707 | 716 | pf->ptp_caps.enable = i40e_ptp_feature_enable; |
---|
708 | 717 | |
---|
.. | .. |
---|
718 | 727 | pf->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; |
---|
719 | 728 | pf->tstamp_config.tx_type = HWTSTAMP_TX_OFF; |
---|
720 | 729 | |
---|
| 730 | + /* Set the previous "reset" time to the current Kernel clock time */ |
---|
| 731 | + ktime_get_real_ts64(&pf->ptp_prev_hw_time); |
---|
| 732 | + pf->ptp_reset_start = ktime_get(); |
---|
| 733 | + |
---|
721 | 734 | return 0; |
---|
| 735 | +} |
---|
| 736 | + |
---|
| 737 | +/** |
---|
| 738 | + * i40e_ptp_save_hw_time - Save the current PTP time as ptp_prev_hw_time |
---|
| 739 | + * @pf: Board private structure |
---|
| 740 | + * |
---|
| 741 | + * Read the current PTP time and save it into pf->ptp_prev_hw_time. This should |
---|
| 742 | + * be called at the end of preparing to reset, just before hardware reset |
---|
| 743 | + * occurs, in order to preserve the PTP time as close as possible across |
---|
| 744 | + * resets. |
---|
| 745 | + */ |
---|
| 746 | +void i40e_ptp_save_hw_time(struct i40e_pf *pf) |
---|
| 747 | +{ |
---|
| 748 | + /* don't try to access the PTP clock if it's not enabled */ |
---|
| 749 | + if (!(pf->flags & I40E_FLAG_PTP)) |
---|
| 750 | + return; |
---|
| 751 | + |
---|
| 752 | + i40e_ptp_gettimex(&pf->ptp_caps, &pf->ptp_prev_hw_time, NULL); |
---|
| 753 | + /* Get a monotonic starting time for this reset */ |
---|
| 754 | + pf->ptp_reset_start = ktime_get(); |
---|
| 755 | +} |
---|
| 756 | + |
---|
| 757 | +/** |
---|
| 758 | + * i40e_ptp_restore_hw_time - Restore the ptp_prev_hw_time + delta to PTP regs |
---|
| 759 | + * @pf: Board private structure |
---|
| 760 | + * |
---|
| 761 | + * Restore the PTP hardware clock registers. We previously cached the PTP |
---|
| 762 | + * hardware time as pf->ptp_prev_hw_time. To be as accurate as possible, |
---|
| 763 | + * update this value based on the time delta since the time was saved, using |
---|
| 764 | + * CLOCK_MONOTONIC (via ktime_get()) to calculate the time difference. |
---|
| 765 | + * |
---|
| 766 | + * This ensures that the hardware clock is restored to nearly what it should |
---|
| 767 | + * have been if a reset had not occurred. |
---|
| 768 | + */ |
---|
| 769 | +void i40e_ptp_restore_hw_time(struct i40e_pf *pf) |
---|
| 770 | +{ |
---|
| 771 | + ktime_t delta = ktime_sub(ktime_get(), pf->ptp_reset_start); |
---|
| 772 | + |
---|
| 773 | + /* Update the previous HW time with the ktime delta */ |
---|
| 774 | + timespec64_add_ns(&pf->ptp_prev_hw_time, ktime_to_ns(delta)); |
---|
| 775 | + |
---|
| 776 | + /* Restore the hardware clock registers */ |
---|
| 777 | + i40e_ptp_settime(&pf->ptp_caps, &pf->ptp_prev_hw_time); |
---|
722 | 778 | } |
---|
723 | 779 | |
---|
724 | 780 | /** |
---|
.. | .. |
---|
728 | 784 | * This function sets device up for 1588 support. The first time it is run, it |
---|
729 | 785 | * will create a PHC clock device. It does not create a clock device if one |
---|
730 | 786 | * already exists. It also reconfigures the device after a reset. |
---|
| 787 | + * |
---|
| 788 | + * The first time a clock is created, i40e_ptp_create_clock will set |
---|
| 789 | + * pf->ptp_prev_hw_time to the current system time. During resets, it is |
---|
| 790 | + * expected that this timespec will be set to the last known PTP clock time, |
---|
| 791 | + * in order to preserve the clock time as close as possible across a reset. |
---|
731 | 792 | **/ |
---|
732 | 793 | void i40e_ptp_init(struct i40e_pf *pf) |
---|
733 | 794 | { |
---|
.. | .. |
---|
759 | 820 | dev_err(&pf->pdev->dev, "%s: ptp_clock_register failed\n", |
---|
760 | 821 | __func__); |
---|
761 | 822 | } else if (pf->ptp_clock) { |
---|
762 | | - struct timespec64 ts; |
---|
763 | 823 | u32 regval; |
---|
764 | 824 | |
---|
765 | 825 | if (pf->hw.debug_mask & I40E_DEBUG_LAN) |
---|
.. | .. |
---|
780 | 840 | /* reset timestamping mode */ |
---|
781 | 841 | i40e_ptp_set_timestamp_mode(pf, &pf->tstamp_config); |
---|
782 | 842 | |
---|
783 | | - /* Set the clock value. */ |
---|
784 | | - ts = ktime_to_timespec64(ktime_get_real()); |
---|
785 | | - i40e_ptp_settime(&pf->ptp_caps, &ts); |
---|
| 843 | + /* Restore the clock time based on last known value */ |
---|
| 844 | + i40e_ptp_restore_hw_time(pf); |
---|
786 | 845 | } |
---|
787 | 846 | } |
---|
788 | 847 | |
---|