/* The XL710 timesync is very much like Intel's 82599 design when it comes to *thefundamentalclockdesign.However,theclockoperationsaremuchsimpler *intheXL710becausethedevicesupportsafull64bitsofnanoseconds. *Becausethefieldissowide,wecanforgothecyclecounterandjust *operatewiththenanosecondfielddirectlywithoutfearofoverflow. * *Muchlikethe82599,theupdateperiodisdependentuponthelinkspeed: *At40Gb,25Gb,ornolink,theperiodis1.6ns. *At10Gbor5Gblink,theperiodismultipliedby2.(3.2ns) *At1Gblink,theperiodismultipliedby20.(32ns) *1588functionalityisnotsupportedat100Mbps.
*/ #define I40E_PTP_40GB_INCVAL 0x0199999999ULL #define I40E_PTP_10GB_INCVAL_MULT 2 #define I40E_PTP_5GB_INCVAL_MULT 2 #define I40E_PTP_1GB_INCVAL_MULT 20 #define I40E_ISGN 0x80000000
/* Event time is captured by one of the two matched registers *PRTTSYN_EVNT_L:32LSBofsampledtimeevent *PRTTSYN_EVNT_H:32MSBofsampledtimeevent *EventisdefinedinPRTTSYN_EVNT_0register
*/
lo = rd32(hw, I40E_PRTTSYN_EVNT_L(0));
hi = rd32(hw, I40E_PRTTSYN_EVNT_H(0));
/* The timer latches on the lowest register read. */
ptp_read_system_prets(sts);
lo = rd32(hw, I40E_PRTTSYN_TIME_L);
ptp_read_system_postts(sts);
hi = rd32(hw, I40E_PRTTSYN_TIME_H);
/* The timer will not update until the high register is written, so *writethelowregisterfirst.
*/
wr32(hw, I40E_PRTTSYN_TIME_L, ns & 0xFFFFFFFF);
wr32(hw, I40E_PRTTSYN_TIME_H, ns >> 32);
}
/* Use PF0 to set pins. Return success for user space tools */ if (pf->hw.pf_id) return0;
/* Preserve previous state of pins that we don't touch */
pins.sdp3_2 = pf->ptp_pins->sdp3_2;
pins.sdp3_3 = pf->ptp_pins->sdp3_3;
pins.gpio_4 = pf->ptp_pins->gpio_4;
/* To turn on the pin - find the corresponding one based on *thegivenindex.Toturnthefunctionoff-find *whichpinhaditassigned.Don'tuseptp_find_pinhere *becauseittriestolockthepincfg_muxwhichislockedby *ptp_pin_store()thatcallshere.
*/ if (on) {
pin_index = ptp_find_pin(pf->ptp_clock, func, chan); if (pin_index < 0) return -EBUSY;
switch (pin_index) { case SDP3_2:
pin = &pins.sdp3_2; break; case SDP3_3:
pin = &pins.sdp3_3; break; case GPIO_4:
pin = &pins.gpio_4; break; default: return -EINVAL;
}
/* Update the jiffies time for any newly latched timestamp. This *ensuresthatwestorethetimethatwefirstdiscoveredatimestamp *waslatchedbythehardware.Theservicetaskwilllaterdetermine *ifweshouldfreethelatchanddropthattimestampshouldtoomuch *timepass.Thisflowensuresthatweonlyupdatejiffiesfornew *eventslatchedsincethelasttimewechecked,andnotallevents *currentlylatched,sothattheservicetaskaccountingremains *accurate.
*/ for (i = 0; i < 4; i++) { if (new_latch_events & BIT(i))
pf->latch_events[i] = jiffies;
}
/* Finally, we store the current status of the Rx timestamp latches */
pf->latch_event_flags = prttsyn_stat;
/* Since we cannot turn off the Rx timestamp logic if the device is *configuredforTxtimestamping,wecheckifRxtimestampingis *configured.Wedon'twanttospuriouslywarnaboutRxtimestamp *hangsifwedon'tcareaboutthetimestamps.
*/ if (!test_bit(I40E_FLAG_PTP_ENA, pf->flags) || !pf->ptp_rx) return;
spin_lock_bh(&pf->ptp_rx_lock);
/* Update current latch times for Rx events */
i40e_ptp_get_rx_events(pf);
/* Check all the currently latched Rx events and see whether they have *beenlatchedforoverasecond.Itisassumedthatanytimestamp *shouldhavebeenclearedwithinthistime,orelseitwascaptured *foradroppedframethatthedriverneverreceived.Thus,wewill *clearanytimestampthathasbeenlatchedforover1second.
*/ for (i = 0; i < 4; i++) { if ((pf->latch_event_flags & BIT(i)) &&
time_is_before_jiffies(pf->latch_events[i] + HZ)) {
rd32(hw, I40E_PRTTSYN_RXTIME_H(i));
pf->latch_event_flags &= ~BIT(i);
cleared++;
}
}
spin_unlock_bh(&pf->ptp_rx_lock);
/* Log a warning if more than 2 timestamps got dropped in the same *check.Wedon'twanttowarnaboutalldropsbecauseitcanoccur *innormalscenariossuchasPTPframesonmulticastaddresseswe *aren'tlisteningto.However,administratorshouldknowifthisis *thereasonpacketsaren'treceivingtimestamps.
*/ if (cleared > 2)
dev_dbg(&pf->pdev->dev, "Dropped %d missed RXTIME timestamp events\n",
cleared);
if (!test_bit(I40E_FLAG_PTP_ENA, pf->flags) || !pf->ptp_tx) return;
/* Nothing to do if we're not already waiting for a timestamp */ if (!test_bit(__I40E_PTP_TX_IN_PROGRESS, pf->state)) return;
/* We already have a handler routine which is run when we are notified *ofaTxtimestampinthehardware.Ifwedon'tgetaninterrupt *withinaseconditisreasonabletoassumethatweneverwill.
*/ if (time_is_before_jiffies(pf->ptp_tx_start + HZ)) {
skb = pf->ptp_tx_skb;
pf->ptp_tx_skb = NULL;
clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state);
/* Free the skb after we clear the bitlock */
dev_kfree_skb_any(skb);
pf->tx_hwtstamp_timeouts++;
}
}
/* Clear the bit lock as soon as possible after reading the register, *andpriortonotifyingthestackviaskb_tstamp_tx().Otherwise *applicationsmightwakeupandattempttorequestanothertransmit *timestamppriortothebitlockbeingcleared.
*/
pf->ptp_tx_skb = NULL;
clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state);
/* Notify the stack and free the skb after we've unlocked */
skb_tstamp_tx(skb, &shhwtstamps);
dev_kfree_skb_any(skb);
}
/* Since we cannot turn off the Rx timestamp logic if the device is *doingTxtimestamping,checkifRxtimestampingisconfigured.
*/ if (!test_bit(I40E_FLAG_PTP_ENA, pf->flags) || !pf->ptp_rx) return;
hw = &pf->hw;
spin_lock_bh(&pf->ptp_rx_lock);
/* Get current Rx events and update latch times */
prttsyn_stat = i40e_ptp_get_rx_events(pf);
/* TODO: Should we warn about missing Rx timestamp event? */ if (!(prttsyn_stat & BIT(index))) {
spin_unlock_bh(&pf->ptp_rx_lock); return;
}
/* Clear the latched event since we're about to read its register */
pf->latch_event_flags &= ~BIT(index);
lo = rd32(hw, I40E_PRTTSYN_RXTIME_L(index));
hi = rd32(hw, I40E_PRTTSYN_RXTIME_H(index));
switch (hw_link_info->link_speed) { case I40E_LINK_SPEED_10GB:
mult = I40E_PTP_10GB_INCVAL_MULT; break; case I40E_LINK_SPEED_5GB:
mult = I40E_PTP_5GB_INCVAL_MULT; break; case I40E_LINK_SPEED_1GB:
mult = I40E_PTP_1GB_INCVAL_MULT; break; case I40E_LINK_SPEED_100MB:
{ staticint warn_once;
if (!warn_once) {
dev_warn(&pf->pdev->dev, "1588 functionality is not supported at 100 Mbps. Stopping the PHC.\n");
warn_once++;
}
mult = 0; break;
} case I40E_LINK_SPEED_40GB: default:
mult = 1; break;
}
/* The increment value is calculated by taking the base 40GbE incvalue *andmultiplyingitbyafactorbasedonthelinkspeed.
*/
incval = I40E_PTP_40GB_INCVAL * mult;
/* Write the new increment value into the increment register. The *hardwarewillnotupdatetheclockuntilbothregistershavebeen *written.
*/
wr32(hw, I40E_PRTTSYN_INC_L, incval & 0xFFFFFFFF);
wr32(hw, I40E_PRTTSYN_INC_H, incval >> 32);
/* Update the base adjustement value. */
WRITE_ONCE(pf->ptp_adj_mult, mult);
smp_mb(); /* Force the above update. */
}
/* pin must be disabled before it may be used */
i40e_ptp_set_pin_hw(hw, I40E_SDP3_2, off);
i40e_ptp_set_pin_hw(hw, I40E_SDP3_3, off);
i40e_ptp_set_pin_hw(hw, I40E_GPIO_4, off);
switch (config->tx_type) { case HWTSTAMP_TX_OFF:
pf->ptp_tx = false; break; case HWTSTAMP_TX_ON:
pf->ptp_tx = true; break; default: return -ERANGE;
}
switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE:
pf->ptp_rx = false; /* We set the type to V1, but do not enable UDP packet *recognition.Inthisway,weshouldbeascloseto *disablingPTPRxtimestampsaspossiblesinceV1packets *arealwaysUDP,sinceL2packetsareaV2feature.
*/
tsyntype = I40E_PRTTSYN_CTL1_TSYNTYPE_V1; break; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: if (!test_bit(I40E_HW_CAP_PTP_L4, pf->hw.caps)) return -ERANGE;
pf->ptp_rx = true;
tsyntype = I40E_PRTTSYN_CTL1_V1MESSTYPE0_MASK |
I40E_PRTTSYN_CTL1_TSYNTYPE_V1 |
I40E_PRTTSYN_CTL1_UDP_ENA_MASK;
config->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; break; case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: if (!test_bit(I40E_HW_CAP_PTP_L4, pf->hw.caps)) return -ERANGE;
fallthrough; case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
pf->ptp_rx = true;
tsyntype = I40E_PRTTSYN_CTL1_V2MESSTYPE0_MASK |
I40E_PRTTSYN_CTL1_TSYNTYPE_V2; if (test_bit(I40E_HW_CAP_PTP_L4, pf->hw.caps)) {
tsyntype |= I40E_PRTTSYN_CTL1_UDP_ENA_MASK;
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
} else {
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
} break; case HWTSTAMP_FILTER_NTP_ALL: case HWTSTAMP_FILTER_ALL: default: return -ERANGE;
}
/* Clear out all 1588-related registers to clear and unlatch them. */
spin_lock_bh(&pf->ptp_rx_lock);
rd32(hw, I40E_PRTTSYN_STAT_0);
rd32(hw, I40E_PRTTSYN_TXTIME_H);
rd32(hw, I40E_PRTTSYN_RXTIME_H(0));
rd32(hw, I40E_PRTTSYN_RXTIME_H(1));
rd32(hw, I40E_PRTTSYN_RXTIME_H(2));
rd32(hw, I40E_PRTTSYN_RXTIME_H(3));
pf->latch_event_flags = 0;
spin_unlock_bh(&pf->ptp_rx_lock);
/* Enable/disable the Tx timestamp interrupt based on user input. */
regval = rd32(hw, I40E_PRTTSYN_CTL0); if (pf->ptp_tx)
regval |= I40E_PRTTSYN_CTL0_TXTIME_INT_ENA_MASK; else
regval &= ~I40E_PRTTSYN_CTL0_TXTIME_INT_ENA_MASK;
wr32(hw, I40E_PRTTSYN_CTL0, regval);
/* Although there is no simple on/off switch for Rx, we "disable" Rx *timestampsbysettingtoV1onlymodeandcleartheUDP *recognition.ThisoughttodisableallPTPRxtimestampsasV1 *packetsarealwaysoverUDP.Notethatsoftwareisconfiguredto *ignoreRxtimestampsviathepf->ptp_rxflag.
*/
regval = rd32(hw, I40E_PRTTSYN_CTL1); /* clear everything but the enable bit */
regval &= I40E_PRTTSYN_CTL1_TSYNENA_MASK; /* now enable bits for desired Rx timestamps */
regval |= tsyntype;
wr32(hw, I40E_PRTTSYN_CTL1, regval);
/** *i40e_ptp_create_clock-CreatePTPclockdeviceforuserspace *@pf:Boardprivatestructure * *ThisfunctioncreatesanewPTPclockdevice.Itonlycreatesoneifwe *don'talreadyhaveone,soitissafetocall.Willreturnerrorifit *can'tcreateone,butsuccessifwealreadyhaveadevice.Shouldbeused *byi40e_ptp_inittocreateclockinitially,andpreventglobalresetsfrom *creatingnewclockdevices.
**/ staticlong i40e_ptp_create_clock(struct i40e_pf *pf)
{ /* no need to create a clock device if we already have one */ if (!IS_ERR_OR_NULL(pf->ptp_clock)) return0;
/* Attempt to register the clock before enabling the hardware. */
pf->ptp_clock = ptp_clock_register(&pf->ptp_caps, &pf->pdev->dev); if (IS_ERR(pf->ptp_clock)) return PTR_ERR(pf->ptp_clock);
/* clear the hwtstamp settings here during clock create, instead of *duringregularinit,sothatwecanmaintainsettingsacrossa *resetorsuspend.
*/
pf->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
pf->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
/* Set the previous "reset" time to the current Kernel clock time */
ktime_get_real_ts64(&pf->ptp_prev_hw_time);
pf->ptp_reset_start = ktime_get();
return0;
}
/** *i40e_ptp_save_hw_time-SavethecurrentPTPtimeasptp_prev_hw_time *@pf:Boardprivatestructure * *ReadthecurrentPTPtimeandsaveitintopf->ptp_prev_hw_time.Thisshould *becalledattheendofpreparingtoreset,justbeforehardwarereset *occurs,inordertopreservethePTPtimeascloseaspossibleacross *resets.
*/ void i40e_ptp_save_hw_time(struct i40e_pf *pf)
{ /* don't try to access the PTP clock if it's not enabled */ if (!test_bit(I40E_FLAG_PTP_ENA, pf->flags)) return;
i40e_ptp_gettimex(&pf->ptp_caps, &pf->ptp_prev_hw_time, NULL); /* Get a monotonic starting time for this reset */
pf->ptp_reset_start = ktime_get();
}
/* Only one PF is assigned to control 1588 logic per port. Do not *enableanysupportforPFsnotassignedviaPRTTSYN_CTL0.PF_ID
*/
pf_id = FIELD_GET(I40E_PRTTSYN_CTL0_PF_ID_MASK,
rd32(hw, I40E_PRTTSYN_CTL0)); if (hw->pf_id != pf_id) {
clear_bit(I40E_FLAG_PTP_ENA, pf->flags);
dev_info(&pf->pdev->dev, "%s: PTP not supported on %s\n",
__func__,
netdev->name); return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.