/** * ice_ptp_find_pin_idx - Find pin index in ptp_pin_desc * @pf: Board private structure * @func: Pin function * @chan: GPIO channel * * Return: positive pin number when pin is present, -1 otherwise
*/ staticint ice_ptp_find_pin_idx(struct ice_pf *pf, enum ptp_pin_function func, unsignedint chan)
{ conststruct ptp_clock_info *info = &pf->ptp.info; int i;
for (i = 0; i < info->n_pins; i++) { if (info->pin_config[i].func == func &&
info->pin_config[i].chan == chan) return i;
}
return -1;
}
/** * ice_ptp_cfg_tx_interrupt - Configure Tx timestamp interrupt for the device * @pf: Board private structure * * Program the device to respond appropriately to the Tx timestamp interrupt * cause.
*/ staticvoid ice_ptp_cfg_tx_interrupt(struct ice_pf *pf)
{ struct ice_hw *hw = &pf->hw; bool enable;
u32 val;
switch (pf->ptp.tx_interrupt_mode) { case ICE_PTP_TX_INTERRUPT_ALL: /* React to interrupts across all quads. */
wr32(hw, PFINT_TSYN_MSK + (0x4 * hw->pf_id), (u32)0x1f);
enable = true; break; case ICE_PTP_TX_INTERRUPT_NONE: /* Do not react to interrupts on any quad. */
wr32(hw, PFINT_TSYN_MSK + (0x4 * hw->pf_id), (u32)0x0);
enable = false; break; case ICE_PTP_TX_INTERRUPT_SELF: default:
enable = pf->ptp.tstamp_config.tx_type == HWTSTAMP_TX_ON; break;
}
/* Configure the Tx timestamp interrupt */
val = rd32(hw, PFINT_OICR_ENA); if (enable)
val |= PFINT_OICR_TSYN_TX_M; else
val &= ~PFINT_OICR_TSYN_TX_M;
wr32(hw, PFINT_OICR_ENA, val);
}
/** * ice_set_rx_tstamp - Enable or disable Rx timestamping * @pf: The PF pointer to search in * @on: bool value for whether timestamps are enabled or disabled
*/ staticvoid ice_set_rx_tstamp(struct ice_pf *pf, bool on)
{ struct ice_vsi *vsi;
u16 i;
vsi = ice_get_main_vsi(pf); if (!vsi || !vsi->rx_rings) return;
/* Set the timestamp flag for all the Rx rings */
ice_for_each_rxq(vsi, i) { if (!vsi->rx_rings[i]) continue;
vsi->rx_rings[i]->ptp_rx = on;
}
}
/** * ice_ptp_disable_timestamp_mode - Disable current timestamp mode * @pf: Board private structure * * Called during preparation for reset to temporarily disable timestamping on * the device. Called during remove to disable timestamping while cleaning up * driver resources.
*/ staticvoid ice_ptp_disable_timestamp_mode(struct ice_pf *pf)
{ struct ice_hw *hw = &pf->hw;
u32 val;
val = rd32(hw, PFINT_OICR_ENA);
val &= ~PFINT_OICR_TSYN_TX_M;
wr32(hw, PFINT_OICR_ENA, val);
ice_set_rx_tstamp(pf, false);
}
/** * ice_ptp_restore_timestamp_mode - Restore timestamp configuration * @pf: Board private structure * * Called at the end of rebuild to restore timestamp configuration after * a device reset.
*/ void ice_ptp_restore_timestamp_mode(struct ice_pf *pf)
{ struct ice_hw *hw = &pf->hw; bool enable_rx;
/* Trigger an immediate software interrupt to ensure that timestamps * which occurred during reset are handled now.
*/
wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);
ice_flush(hw);
}
/** * ice_ptp_read_src_clk_reg - Read the source clock register * @pf: Board private structure * @sts: Optional parameter for holding a pair of system timestamps from * the system clock. Will be ignored if NULL is given.
*/
u64 ice_ptp_read_src_clk_reg(struct ice_pf *pf, struct ptp_system_timestamp *sts)
{ struct ice_hw *hw = &pf->hw;
u32 hi, lo, lo2;
u8 tmr_idx;
if (!ice_is_primary(hw))
hw = ice_get_primary_hw(pf);
tmr_idx = ice_get_ptp_src_clock_index(hw);
guard(spinlock)(&pf->adapter->ptp_gltsyn_time_lock); /* Read the system timestamp pre PHC read */
ptp_read_system_prets(sts);
if (hw->mac_type == ICE_MAC_E830) {
u64 clk_time = rd64(hw, E830_GLTSYN_TIME_L(tmr_idx));
/* Read the system timestamp post PHC read */
ptp_read_system_postts(sts);
return clk_time;
}
lo = rd32(hw, GLTSYN_TIME_L(tmr_idx));
/* Read the system timestamp post PHC read */
ptp_read_system_postts(sts);
hi = rd32(hw, GLTSYN_TIME_H(tmr_idx));
lo2 = rd32(hw, GLTSYN_TIME_L(tmr_idx));
if (lo2 < lo) { /* if TIME_L rolled over read TIME_L again and update * system timestamps
*/
ptp_read_system_prets(sts);
lo = rd32(hw, GLTSYN_TIME_L(tmr_idx));
ptp_read_system_postts(sts);
hi = rd32(hw, GLTSYN_TIME_H(tmr_idx));
}
return ((u64)hi << 32) | lo;
}
/** * ice_ptp_extend_32b_ts - Convert a 32b nanoseconds timestamp to 64b * @cached_phc_time: recently cached copy of PHC time * @in_tstamp: Ingress/egress 32b nanoseconds timestamp value * * Hardware captures timestamps which contain only 32 bits of nominal * nanoseconds, as opposed to the 64bit timestamps that the stack expects. * Note that the captured timestamp values may be 40 bits, but the lower * 8 bits are sub-nanoseconds and generally discarded. * * Extend the 32bit nanosecond timestamp using the following algorithm and * assumptions: * * 1) have a recently cached copy of the PHC time * 2) assume that the in_tstamp was captured 2^31 nanoseconds (~2.1 * seconds) before or after the PHC time was captured. * 3) calculate the delta between the cached time and the timestamp * 4) if the delta is smaller than 2^31 nanoseconds, then the timestamp was * captured after the PHC time. In this case, the full timestamp is just * the cached PHC time plus the delta. * 5) otherwise, if the delta is larger than 2^31 nanoseconds, then the * timestamp was captured *before* the PHC time, i.e. because the PHC * cache was updated after the timestamp was captured by hardware. In this * case, the full timestamp is the cached time minus the inverse delta. * * This algorithm works even if the PHC time was updated after a Tx timestamp * was requested, but before the Tx timestamp event was reported from * hardware. * * This calculation primarily relies on keeping the cached PHC time up to * date. If the timestamp was captured more than 2^31 nanoseconds after the * PHC time, it is possible that the lower 32bits of PHC time have * overflowed more than once, and we might generate an incorrect timestamp. * * This is prevented by (a) periodically updating the cached PHC time once * a second, and (b) discarding any Tx timestamp packet if it has waited for * a timestamp for more than one second.
*/ static u64 ice_ptp_extend_32b_ts(u64 cached_phc_time, u32 in_tstamp)
{
u32 delta, phc_time_lo;
u64 ns;
/* Extract the lower 32 bits of the PHC time */
phc_time_lo = (u32)cached_phc_time;
/* Calculate the delta between the lower 32bits of the cached PHC * time and the in_tstamp value
*/
delta = (in_tstamp - phc_time_lo);
/* Do not assume that the in_tstamp is always more recent than the * cached PHC time. If the delta is large, it indicates that the * in_tstamp was taken in the past, and should be converted * forward.
*/ if (delta > (U32_MAX / 2)) { /* reverse the delta calculation here */
delta = (phc_time_lo - in_tstamp);
ns = cached_phc_time - delta;
} else {
ns = cached_phc_time + delta;
}
return ns;
}
/** * ice_ptp_extend_40b_ts - Convert a 40b timestamp to 64b nanoseconds * @pf: Board private structure * @in_tstamp: Ingress/egress 40b timestamp value * * The Tx and Rx timestamps are 40 bits wide, including 32 bits of nominal * nanoseconds, 7 bits of sub-nanoseconds, and a valid bit. * * *--------------------------------------------------------------* * | 32 bits of nanoseconds | 7 high bits of sub ns underflow | v | * *--------------------------------------------------------------* * * The low bit is an indicator of whether the timestamp is valid. The next * 7 bits are a capture of the upper 7 bits of the sub-nanosecond underflow, * and the remaining 32 bits are the lower 32 bits of the PHC timer. * * It is assumed that the caller verifies the timestamp is valid prior to * calling this function. * * Extract the 32bit nominal nanoseconds and extend them. Use the cached PHC * time stored in the device private PTP structure as the basis for timestamp * extension. * * See ice_ptp_extend_32b_ts for a detailed explanation of the extension * algorithm.
*/ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp)
{ const u64 mask = GENMASK_ULL(31, 0); unsignedlong discard_time;
/* Discard the hardware timestamp if the cached PHC time is too old */
discard_time = pf->ptp.cached_phc_jiffies + msecs_to_jiffies(2000); if (time_is_before_jiffies(discard_time)) {
pf->ptp.tx_hwtstamp_discarded++; return 0;
}
/** * ice_ptp_is_tx_tracker_up - Check if Tx tracker is ready for new timestamps * @tx: the PTP Tx timestamp tracker to check * * Check that a given PTP Tx timestamp tracker is up, i.e. that it is ready * to accept new timestamp requests. * * Assumes the tx->lock spinlock is already held.
*/ staticbool
ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx)
{
lockdep_assert_held(&tx->lock);
return tx->init && !tx->calibrating;
}
/** * ice_ptp_req_tx_single_tstamp - Request Tx timestamp for a port from FW * @tx: the PTP Tx timestamp tracker * @idx: index of the timestamp to request
*/ void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx)
{ struct ice_e810_params *params; struct ice_ptp_port *ptp_port; unsignedlong flags; struct sk_buff *skb; struct ice_pf *pf;
/* Drop packets which have waited for more than 2 seconds */ if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) { /* Count the number of Tx timestamps that timed out */
pf->ptp.tx_hwtstamp_timeouts++;
/* Write TS index to read to the PF register so the FW can read it */
wr32(&pf->hw, REG_LL_PROXY_H,
REG_LL_PROXY_H_TS_INTR_ENA | FIELD_PREP(REG_LL_PROXY_H_TS_IDX, idx) |
REG_LL_PROXY_H_EXEC);
tx->last_ll_ts_idx_read = idx;
if (!(params->atqbal_flags & ATQBAL_FLAGS_INTR_IN_PROGRESS))
dev_dbg(dev, "%s: low latency interrupt request not in progress?\n",
__func__);
/* Read the low 32 bit value */
raw_tstamp = rd32(&pf->hw, REG_LL_PROXY_L); /* Read the status together with high TS part */
reg_ll_high = rd32(&pf->hw, REG_LL_PROXY_H);
/* Wake up threads waiting on low latency interface */
params->atqbal_flags &= ~ATQBAL_FLAGS_INTR_IN_PROGRESS;
/* When the bit is cleared, the TS is ready in the register */ if (reg_ll_high & REG_LL_PROXY_H_EXEC) {
dev_err(ice_pf_to_dev(pf), "Failed to get the Tx tstamp - FW not ready"); return;
}
/* High 8 bit value of the TS is on the bits 16:23 */
raw_tstamp |= ((u64)FIELD_GET(REG_LL_PROXY_H_TS_HIGH, reg_ll_high)) << 32;
/* Devices using this interface always verify the timestamp differs * relative to the last cached timestamp value.
*/ if (raw_tstamp == tx->tstamps[idx].cached_tstamp) return;
/** * ice_ptp_process_tx_tstamp - Process Tx timestamps for a port * @tx: the PTP Tx timestamp tracker * * Process timestamps captured by the PHY associated with this port. To do * this, loop over each index with a waiting skb. * * If a given index has a valid timestamp, perform the following steps: * * 1) check that the timestamp request is not stale * 2) check that a timestamp is ready and available in the PHY memory bank * 3) read and copy the timestamp out of the PHY register * 4) unlock the index by clearing the associated in_use bit * 5) check if the timestamp is stale, and discard if so * 6) extend the 40 bit timestamp value to get a 64 bit timestamp value * 7) send this 64 bit timestamp to the stack * * Note that we do not hold the tracking lock while reading the Tx timestamp. * This is because reading the timestamp requires taking a mutex that might * sleep. * * The only place where we set in_use is when a new timestamp is initiated * with a slot index. This is only called in the hard xmit routine where an * SKB has a request flag set. The only places where we clear this bit is this * function, or during teardown when the Tx timestamp tracker is being * removed. A timestamp index will never be re-used until the in_use bit for * that index is cleared. * * If a Tx thread starts a new timestamp, we might not begin processing it * right away but we will notice it at the end when we re-queue the task. * * If a Tx thread starts a new timestamp just after this function exits, the * interrupt for that timestamp should re-trigger this function once * a timestamp is ready. * * In cases where the PTP hardware clock was directly adjusted, some * timestamps may not be able to safely use the timestamp extension math. In * this case, software will set the stale bit for any outstanding Tx * timestamps when the clock is adjusted. Then this function will discard * those captured timestamps instead of sending them to the stack. * * If a Tx packet has been waiting for more than 2 seconds, it is not possible * to correctly extend the timestamp using the cached PHC time. It is * extremely unlikely that a packet will ever take this long to timestamp. If * we detect a Tx timestamp request that has waited for this long we assume * the packet will never be sent by hardware and discard it without reading * the timestamp register.
*/ staticvoid ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
{ struct ice_ptp_port *ptp_port; unsignedlong flags; struct ice_pf *pf; struct ice_hw *hw;
u64 tstamp_ready; bool link_up; int err;
u8 idx;
/* Drop packets which have waited for more than 2 seconds */ if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) {
drop_ts = true;
/* Count the number of Tx timestamps that timed out */
pf->ptp.tx_hwtstamp_timeouts++;
}
/* Only read a timestamp from the PHY if its marked as ready * by the tstamp_ready register. This avoids unnecessary * reading of timestamps which are not yet valid. This is * important as we must read all timestamps which are valid * and only timestamps which are valid during each interrupt. * If we do not, the hardware logic for generating a new * interrupt can get stuck on some devices.
*/ if (tx->has_ready_bitmap &&
!(tstamp_ready & BIT_ULL(phy_idx))) { if (drop_ts) goto skip_ts_read;
/* For PHYs which don't implement a proper timestamp ready * bitmap, verify that the timestamp value is different * from the last cached timestamp. If it is not, skip this for * now assuming it hasn't yet been captured by hardware.
*/ if (!drop_ts && !tx->has_ready_bitmap &&
raw_tstamp == tx->tstamps[idx].cached_tstamp) continue;
/* Discard any timestamp value without the valid bit set */ if (!(raw_tstamp & ICE_PTP_TS_VALID))
drop_ts = true;
for (i = 0; i < ICE_GET_QUAD_NUM(pf->hw.ptp.num_lports); i++) {
u64 tstamp_ready; int err;
/* Read the Tx ready status first */
err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready); if (err) break; elseif (tstamp_ready) return ICE_TX_TSTAMP_WORK_PENDING;
}
return ICE_TX_TSTAMP_WORK_DONE;
}
/** * ice_ptp_tx_tstamp - Process Tx timestamps for this function. * @tx: Tx tracking structure to initialize * * Returns: ICE_TX_TSTAMP_WORK_PENDING if there are any outstanding incomplete * Tx timestamps, or ICE_TX_TSTAMP_WORK_DONE otherwise.
*/ staticenum ice_tx_tstamp_work ice_ptp_tx_tstamp(struct ice_ptp_tx *tx)
{ bool more_timestamps; unsignedlong flags;
if (!tx->init) return ICE_TX_TSTAMP_WORK_DONE;
/* Process the Tx timestamp tracker */
ice_ptp_process_tx_tstamp(tx);
/* Check if there are outstanding Tx timestamps */
spin_lock_irqsave(&tx->lock, flags);
more_timestamps = tx->init && !bitmap_empty(tx->in_use, tx->len);
spin_unlock_irqrestore(&tx->lock, flags);
if (more_timestamps) return ICE_TX_TSTAMP_WORK_PENDING;
return ICE_TX_TSTAMP_WORK_DONE;
}
/** * ice_ptp_alloc_tx_tracker - Initialize tracking for Tx timestamps * @tx: Tx tracking structure to initialize * * Assumes that the length has already been initialized. Do not call directly, * use the ice_ptp_init_tx_* instead.
*/ staticint
ice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx)
{ unsignedlong *in_use, *stale; struct ice_tx_tstamp *tstamps;
/** * ice_ptp_flush_tx_tracker - Flush any remaining timestamps from the tracker * @pf: Board private structure * @tx: the tracker to flush * * Called during teardown when a Tx tracker is being removed.
*/ staticvoid
ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx)
{ struct ice_hw *hw = &pf->hw; unsignedlong flags;
u64 tstamp_ready; int err;
u8 idx;
err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready); if (err) {
dev_dbg(ice_pf_to_dev(pf), "Failed to get the Tx tstamp ready bitmap for block %u, err %d\n",
tx->block, err);
/* If we fail to read the Tx timestamp ready bitmap just * skip clearing the PHY timestamps.
*/
tstamp_ready = 0;
}
/* In case this timestamp is ready, we need to clear it. */ if (!hw->reset_ongoing && (tstamp_ready & BIT_ULL(phy_idx)))
ice_clear_phy_tstamp(hw, tx->block, phy_idx);
/* Count the number of Tx timestamps flushed */
pf->ptp.tx_hwtstamp_flushed++;
/* Free the SKB after we've cleared the bit */
dev_kfree_skb_any(skb);
}
}
/** * ice_ptp_mark_tx_tracker_stale - Mark unfinished timestamps as stale * @tx: the tracker to mark * * Mark currently outstanding Tx timestamps as stale. This prevents sending * their timestamp value to the stack. This is required to prevent extending * the 40bit hardware timestamp incorrectly. * * This should be called when the PTP clock is modified such as after a set * time request.
*/ staticvoid
ice_ptp_mark_tx_tracker_stale(struct ice_ptp_tx *tx)
{ unsignedlong flags;
/** * ice_ptp_flush_all_tx_tracker - Flush all timestamp trackers on this clock * @pf: Board private structure * * Called by the clock owner to flush all the Tx timestamp trackers associated * with the clock.
*/ staticvoid
ice_ptp_flush_all_tx_tracker(struct ice_pf *pf)
{ struct ice_ptp_port *port;
/* wait for potentially outstanding interrupt to complete */
synchronize_irq(pf->oicr_irq.virq);
ice_ptp_flush_tx_tracker(pf, tx);
kfree(tx->tstamps);
tx->tstamps = NULL;
bitmap_free(tx->in_use);
tx->in_use = NULL;
bitmap_free(tx->stale);
tx->stale = NULL;
tx->len = 0;
}
/** * ice_ptp_init_tx_e82x - Initialize tracking for Tx timestamps * @pf: Board private structure * @tx: the Tx tracking structure to initialize * @port: the port this structure tracks * * Initialize the Tx timestamp tracker for this port. For generic MAC devices, * the timestamp block is shared for all ports in the same quad. To avoid * ports using the same timestamp index, logically break the block of * registers into chunks based on the port number. * * Return: 0 on success, -ENOMEM when out of memory
*/ staticint ice_ptp_init_tx_e82x(struct ice_pf *pf, struct ice_ptp_tx *tx,
u8 port)
{
tx->block = ICE_GET_QUAD_NUM(port);
tx->offset = (port % ICE_PORTS_PER_QUAD) * INDEX_PER_PORT_E82X;
tx->len = INDEX_PER_PORT_E82X;
tx->has_ready_bitmap = 1;
return ice_ptp_alloc_tx_tracker(tx);
}
/** * ice_ptp_init_tx - Initialize tracking for Tx timestamps * @pf: Board private structure * @tx: the Tx tracking structure to initialize * @port: the port this structure tracks * * Initialize the Tx timestamp tracker for this PF. For all PHYs except E82X, * each port has its own block of timestamps, independent of the other ports. * * Return: 0 on success, -ENOMEM when out of memory
*/ staticint ice_ptp_init_tx(struct ice_pf *pf, struct ice_ptp_tx *tx, u8 port)
{
tx->block = port;
tx->offset = 0;
tx->len = INDEX_PER_PORT;
/* The E810 PHY does not provide a timestamp ready bitmap. Instead, * verify new timestamps against cached copy of the last read * timestamp.
*/
tx->has_ready_bitmap = pf->hw.mac_type != ICE_MAC_E810;
return ice_ptp_alloc_tx_tracker(tx);
}
/** * ice_ptp_update_cached_phctime - Update the cached PHC time values * @pf: Board specific private structure * * This function updates the system time values which are cached in the PF * structure and the Rx rings. * * This function must be called periodically to ensure that the cached value * is never more than 2 seconds old. * * Note that the cached copy in the PF PTP structure is always updated, even * if we can't update the copy in the Rx rings. * * Return: * * 0 - OK, successfully updated * * -EAGAIN - PF was busy, need to reschedule the update
*/ staticint ice_ptp_update_cached_phctime(struct ice_pf *pf)
{ struct device *dev = ice_pf_to_dev(pf); unsignedlong update_before;
u64 systime; int i;
dev_warn(dev, "%u msecs passed between update to cached PHC time\n",
jiffies_to_msecs(time_taken));
pf->ptp.late_cached_phc_updates++;
}
/* Read the current PHC time */
systime = ice_ptp_read_src_clk_reg(pf, NULL);
/* Update the cached PHC time stored in the PF structure */
WRITE_ONCE(pf->ptp.cached_phc_time, systime);
WRITE_ONCE(pf->ptp.cached_phc_jiffies, jiffies);
if (test_and_set_bit(ICE_CFG_BUSY, pf->state)) return -EAGAIN;
ice_for_each_vsi(pf, i) { struct ice_vsi *vsi = pf->vsi[i]; int j;
/** * ice_ptp_reset_cached_phctime - Reset cached PHC time after an update * @pf: Board specific private structure * * This function must be called when the cached PHC time is no longer valid, * such as after a time adjustment. It marks any currently outstanding Tx * timestamps as stale and updates the cached PHC time for both the PF and Rx * rings. * * If updating the PHC time cannot be done immediately, a warning message is * logged and the work item is scheduled immediately to minimize the window * with a wrong cached timestamp.
*/ staticvoid ice_ptp_reset_cached_phctime(struct ice_pf *pf)
{ struct device *dev = ice_pf_to_dev(pf); int err;
/* Update the cached PHC time immediately if possible, otherwise * schedule the work item to execute soon.
*/
err = ice_ptp_update_cached_phctime(pf); if (err) { /* If another thread is updating the Rx rings, we won't * properly reset them here. This could lead to reporting of * invalid timestamps, but there isn't much we can do.
*/
dev_warn(dev, "%s: ICE_CFG_BUSY, unable to immediately update cached PHC time\n",
__func__);
/* Queue the work item to update the Rx rings when possible */
kthread_queue_delayed_work(pf->ptp.kworker, &pf->ptp.work,
msecs_to_jiffies(10));
}
/* Mark any outstanding timestamps as stale, since they might have * been captured in hardware before the time update. This could lead * to us extending them with the wrong cached value resulting in * incorrect timestamp values.
*/
ice_ptp_mark_tx_tracker_stale(&pf->ptp.port.tx);
}
/** * ice_ptp_write_init - Set PHC time to provided value * @pf: Board private structure * @ts: timespec structure that holds the new time value * * Set the PHC time to the specified time provided in the timespec.
*/ staticint ice_ptp_write_init(struct ice_pf *pf, struct timespec64 *ts)
{
u64 ns = timespec64_to_ns(ts); struct ice_hw *hw = &pf->hw;
return ice_ptp_init_time(hw, ns);
}
/** * ice_ptp_write_adj - Adjust PHC clock time atomically * @pf: Board private structure * @adj: Adjustment in nanoseconds * * Perform an atomic adjustment of the PHC time by the specified number of * nanoseconds.
*/ staticint ice_ptp_write_adj(struct ice_pf *pf, s32 adj)
{ struct ice_hw *hw = &pf->hw;
return ice_ptp_adj_clock(hw, adj);
}
/** * ice_base_incval - Get base timer increment value * @pf: Board private structure * * Look up the base timer increment value for this device. The base increment * value is used to define the nominal clock tick rate. This increment value * is programmed during device initialization. It is also used as the basis * for calculating adjustments using scaled_ppm.
*/ static u64 ice_base_incval(struct ice_pf *pf)
{ struct ice_hw *hw = &pf->hw;
u64 incval;
incval = ice_get_base_incval(hw);
dev_dbg(ice_pf_to_dev(pf), "PTP: using base increment value of 0x%016llx\n",
incval);
return incval;
}
/** * ice_ptp_check_tx_fifo - Check whether Tx FIFO is in an OK state * @port: PTP port for which Tx FIFO is checked
*/ staticint ice_ptp_check_tx_fifo(struct ice_ptp_port *port)
{ int offs = port->port_num % ICE_PORTS_PER_QUAD; int quad = ICE_GET_QUAD_NUM(port->port_num); struct ice_pf *pf; struct ice_hw *hw;
u32 val, phy_sts; int err;
pf = ptp_port_to_pf(port);
hw = &pf->hw;
if (port->tx_fifo_busy_cnt == FIFO_OK) return 0;
/* need to read FIFO state */ if (offs == 0 || offs == 1)
err = ice_read_quad_reg_e82x(hw, quad, Q_REG_FIFO01_STATUS,
&val); else
err = ice_read_quad_reg_e82x(hw, quad, Q_REG_FIFO23_STATUS,
&val);
if (err) {
dev_err(ice_pf_to_dev(pf), "PTP failed to check port %d Tx FIFO, err %d\n",
port->port_num, err); return err;
}
dev_dbg(ice_pf_to_dev(pf), "Try %d, port %d FIFO not empty\n",
port->tx_fifo_busy_cnt, port->port_num);
if (port->tx_fifo_busy_cnt == ICE_PTP_FIFO_NUM_CHECKS) {
dev_dbg(ice_pf_to_dev(pf), "Port %d Tx FIFO still not empty; resetting quad %d\n",
port->port_num, quad);
ice_ptp_reset_ts_memory_quad_e82x(hw, quad);
port->tx_fifo_busy_cnt = FIFO_OK; return 0;
}
return -EAGAIN;
}
/** * ice_ptp_wait_for_offsets - Check for valid Tx and Rx offsets * @work: Pointer to the kthread_work structure for this task * * Check whether hardware has completed measuring the Tx and Rx offset values * used to configure and enable vernier timestamp calibration. * * Once the offset in either direction is measured, configure the associated * registers with the calibrated offset values and enable timestamping. The Tx * and Rx directions are configured independently as soon as their associated * offsets are known. * * This function reschedules itself until both Tx and Rx calibration have * completed.
*/ staticvoid ice_ptp_wait_for_offsets(struct kthread_work *work)
{ struct ice_ptp_port *port; struct ice_pf *pf; struct ice_hw *hw; int tx_err; int rx_err;
if (ice_is_reset_in_progress(pf->state)) { /* wait for device driver to complete reset */
kthread_queue_delayed_work(pf->ptp.kworker,
&port->ov_work,
msecs_to_jiffies(100)); return;
}
tx_err = ice_ptp_check_tx_fifo(port); if (!tx_err)
tx_err = ice_phy_cfg_tx_offset_e82x(hw, port->port_num);
rx_err = ice_phy_cfg_rx_offset_e82x(hw, port->port_num); if (tx_err || rx_err) { /* Tx and/or Rx offset not yet configured, try again later */
kthread_queue_delayed_work(pf->ptp.kworker,
&port->ov_work,
msecs_to_jiffies(100)); return;
}
}
/** * ice_ptp_port_phy_stop - Stop timestamping for a PHY port * @ptp_port: PTP port to stop
*/ staticint
ice_ptp_port_phy_stop(struct ice_ptp_port *ptp_port)
{ struct ice_pf *pf = ptp_port_to_pf(ptp_port);
u8 port = ptp_port->port_num; struct ice_hw *hw = &pf->hw; int err;
mutex_lock(&ptp_port->ps_lock);
switch (hw->mac_type) { case ICE_MAC_E810: case ICE_MAC_E830:
err = 0; break; case ICE_MAC_GENERIC:
kthread_cancel_delayed_work_sync(&ptp_port->ov_work);
err = ice_stop_phy_timer_e82x(hw, port, true); break; case ICE_MAC_GENERIC_3K_E825:
err = ice_stop_phy_timer_eth56g(hw, port, true); break; default:
err = -ENODEV;
} if (err && err != -EBUSY)
dev_err(ice_pf_to_dev(pf), "PTP failed to set PHY port %d down, err %d\n",
port, err);
mutex_unlock(&ptp_port->ps_lock);
return err;
}
/** * ice_ptp_port_phy_restart - (Re)start and calibrate PHY timestamping * @ptp_port: PTP port for which the PHY start is set * * Start the PHY timestamping block, and initiate Vernier timestamping * calibration. If timestamping cannot be calibrated (such as if link is down) * then disable the timestamping block instead.
*/ staticint
ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port)
{ struct ice_pf *pf = ptp_port_to_pf(ptp_port);
u8 port = ptp_port->port_num; struct ice_hw *hw = &pf->hw; unsignedlong flags; int err;
if (!ptp_port->link_up) return ice_ptp_port_phy_stop(ptp_port);
mutex_lock(&ptp_port->ps_lock);
switch (hw->mac_type) { case ICE_MAC_E810: case ICE_MAC_E830:
err = 0; break; case ICE_MAC_GENERIC: /* Start the PHY timer in Vernier mode */
kthread_cancel_delayed_work_sync(&ptp_port->ov_work);
if (err)
dev_err(ice_pf_to_dev(pf), "PTP failed to set PHY port %d up, err %d\n",
port, err);
mutex_unlock(&ptp_port->ps_lock);
return err;
}
/** * ice_ptp_link_change - Reconfigure PTP after link status change * @pf: Board private structure * @linkup: Link is up or down
*/ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
{ struct ice_ptp_port *ptp_port; struct ice_hw *hw = &pf->hw;
if (pf->ptp.state != ICE_PTP_READY) return;
ptp_port = &pf->ptp.port;
/* Update cached link status for this port immediately */
ptp_port->link_up = linkup;
/* Skip HW writes if reset is in progress */ if (pf->hw.reset_ongoing) return;
switch (hw->mac_type) { case ICE_MAC_E810: case ICE_MAC_E830: /* Do not reconfigure E810 or E830 PHY */ return; case ICE_MAC_GENERIC: case ICE_MAC_GENERIC_3K_E825:
ice_ptp_port_phy_restart(ptp_port); return; default:
dev_warn(ice_pf_to_dev(pf), "%s: Unknown PHY type\n", __func__);
}
}
/** * ice_ptp_cfg_phy_interrupt - Configure PHY interrupt settings * @pf: PF private structure * @ena: bool value to enable or disable interrupt * @threshold: Minimum number of packets at which intr is triggered * * Utility function to configure all the PHY interrupt settings, including * whether the PHY interrupt is enabled, and what threshold to use. Also * configures The E82X timestamp owner to react to interrupts from all PHYs. * * Return: 0 on success, -EOPNOTSUPP when PHY model incorrect, other error codes * when failed to configure PHY interrupt for E82X
*/ staticint ice_ptp_cfg_phy_interrupt(struct ice_pf *pf, bool ena, u32 threshold)
{ struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw;
ice_ptp_reset_ts_memory(hw);
switch (hw->mac_type) { case ICE_MAC_E810: case ICE_MAC_E830: return 0; case ICE_MAC_GENERIC: { int quad;
for (quad = 0; quad < ICE_GET_QUAD_NUM(hw->ptp.num_lports);
quad++) { int err;
err = ice_phy_cfg_intr_e82x(hw, quad, ena, threshold); if (err) {
dev_err(dev, "Failed to configure PHY interrupt for quad %d, err %d\n",
quad, err); return err;
}
}
return 0;
} case ICE_MAC_GENERIC_3K_E825: { int port;
for (port = 0; port < hw->ptp.num_lports; port++) { int err;
err = ice_phy_cfg_intr_eth56g(hw, port, ena, threshold); if (err) {
dev_err(dev, "Failed to configure PHY interrupt for port %d, err %d\n",
port, err); return err;
}
}
return 0;
} case ICE_MAC_UNKNOWN: default: return -EOPNOTSUPP;
}
}
if (port->link_up)
ice_ptp_port_phy_restart(port);
}
}
/** * ice_ptp_adjfine - Adjust clock increment rate * @info: the driver's PTP info structure * @scaled_ppm: Parts per million with 16-bit fractional field * * Adjust the frequency of the clock by the indicated scaled ppm from the * base frequency.
*/ staticint ice_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)
{ struct ice_pf *pf = ptp_info_to_pf(info); struct ice_hw *hw = &pf->hw;
u64 incval; int err;
incval = adjust_by_scaled_ppm(ice_base_incval(pf), scaled_ppm);
err = ice_ptp_write_incval_locked(hw, incval); if (err) {
dev_err(ice_pf_to_dev(pf), "PTP failed to set incval, err %d\n",
err); return -EIO;
}
/* Don't process timestamp events if PTP is not ready */ if (pf->ptp.state != ICE_PTP_READY) return;
tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; /* Event time is captured by one of the two matched registers * GLTSYN_EVNT_L: 32 LSB of sampled time event * GLTSYN_EVNT_H: 32 MSB of sampled time event * Event is defined in GLTSYN_EVNT_0 register
*/ for (chan = 0; chan < GLTSYN_EVNT_H_IDX_MAX; chan++) { int pin_desc_idx;
/* Check if channel is enabled */ if (!(pf->ptp.ext_ts_irq & (1 << chan))) continue;
lo = rd32(hw, GLTSYN_EVNT_L(chan, tmr_idx));
hi = rd32(hw, GLTSYN_EVNT_H(chan, tmr_idx));
event.timestamp = (u64)hi << 32 | lo;
if (hw->mac_type == ICE_MAC_GENERIC_3K_E825) { int err;
/* Enable/disable CGU 1PPS output for E825C */
err = ice_tspll_cfg_pps_out_e825c(hw, !!period); if (err) return err;
}
/* 1. Write perout with half of required period value. * HW toggles output when source clock hits the TGT and then adds * GLTSYN_CLKO value to the target, so it ends up with 50% duty cycle.
*/
period >>= 1;
/* For proper operation, GLTSYN_CLKO must be larger than clock tick and * period has to fit in 32 bit register.
*/ #define MIN_PULSE 3 if (!!period && (period <= MIN_PULSE || period > U32_MAX)) {
dev_err(ice_hw_to_dev(hw), "CLK period ticks must be >= %d && <= 2^32",
MIN_PULSE); return -EIO;
}
/* If we're disabling the output or period is 0, clear out CLKO and TGT * and keep output level low.
*/ if (!on || !period) return ice_ptp_write_perout(hw, rq->index, gpio_pin, 0, 0);
if (strncmp(pf->ptp.pin_desc[pin_desc_idx].name, "1PPS", 64) == 0 &&
period != NSEC_PER_SEC && hw->mac_type == ICE_MAC_GENERIC) {
dev_err(ice_pf_to_dev(pf), "1PPS pin supports only 1 s period\n"); return -EOPNOTSUPP;
}
if (period & 0x1) {
dev_err(ice_pf_to_dev(pf), "CLK Period must be an even value\n"); return -EIO;
}
/* If PTP_PEROUT_PHASE is set, rq has phase instead of start time */ if (rq->flags & PTP_PEROUT_PHASE)
phase = start; else
div64_u64_rem(start, period, &phase);
/* If we have only phase or start time is in the past, start the timer * at the next multiple of period, maintaining phase at least 0.5 second * from now, so we have time to write it to HW.
*/
clk = ice_ptp_read_src_clk_reg(pf, NULL) + NSEC_PER_MSEC * 500; if (rq->flags & PTP_PEROUT_PHASE || start <= clk - prop_delay_ns)
start = div64_u64(clk + period - 1, period) * period + phase;
/* Compensate for propagation delay from the generator to the pin. */
start -= prop_delay_ns;
/** * ice_ptp_disable_all_perout - Disable all currently configured outputs * @pf: Board private structure * * Disable all currently configured clock outputs. This is necessary before * certain changes to the PTP hardware clock. Use ice_ptp_enable_all_perout to * re-enable the clocks again.
*/ staticvoid ice_ptp_disable_all_perout(struct ice_pf *pf)
{ for (unsignedint i = 0; i < pf->ptp.info.n_per_out; i++) if (pf->ptp.perout_rqs[i].period.sec ||
pf->ptp.perout_rqs[i].period.nsec)
ice_ptp_cfg_perout(pf, &pf->ptp.perout_rqs[i], false);
}
/** * ice_ptp_enable_all_perout - Enable all configured periodic clock outputs * @pf: Board private structure * * Enable all currently configured clock outputs. Use this after * ice_ptp_disable_all_perout to reconfigure the output signals according to * their configuration.
*/ staticvoid ice_ptp_enable_all_perout(struct ice_pf *pf)
{ for (unsignedint i = 0; i < pf->ptp.info.n_per_out; i++) if (pf->ptp.perout_rqs[i].period.sec ||
pf->ptp.perout_rqs[i].period.nsec)
ice_ptp_cfg_perout(pf, &pf->ptp.perout_rqs[i], true);
}
/** * ice_verify_pin - verify if pin supports requested pin function * @info: the driver's PTP info structure * @pin: Pin index * @func: Assigned function * @chan: Assigned channel * * Return: 0 on success, -EOPNOTSUPP when function is not supported.
*/ staticint ice_verify_pin(struct ptp_clock_info *info, unsignedint pin, enum ptp_pin_function func, unsignedint chan)
{ struct ice_pf *pf = ptp_info_to_pf(info); conststruct ice_ptp_pin_desc *pin_desc;
pin_desc = &pf->ptp.ice_pin_desc[pin];
/* Is assigned function allowed? */ switch (func) { case PTP_PF_EXTTS: if (pin_desc->gpio[0] < 0) return -EOPNOTSUPP; break; case PTP_PF_PEROUT: if (pin_desc->gpio[1] < 0) return -EOPNOTSUPP; break; case PTP_PF_NONE: break; case PTP_PF_PHYSYNC: default: return -EOPNOTSUPP;
}
return 0;
}
/** * ice_ptp_gpio_enable - Enable/disable ancillary features of PHC * @info: The driver's PTP info structure * @rq: The requested feature to change * @on: Enable/disable flag * * Return: 0 on success, negative error code otherwise
*/ staticint ice_ptp_gpio_enable(struct ptp_clock_info *info, struct ptp_clock_request *rq, int on)
{ struct ice_pf *pf = ptp_info_to_pf(info); int err;
/** * ice_ptp_gettimex64 - Get the time of the clock * @info: the driver's PTP info structure * @ts: timespec64 structure to hold the current time value * @sts: Optional parameter for holding a pair of system timestamps from * the system clock. Will be ignored if NULL is given. * * Read the device clock and return the correct value on ns, after converting it * into a timespec struct.
*/ staticint
ice_ptp_gettimex64(struct ptp_clock_info *info, struct timespec64 *ts, struct ptp_system_timestamp *sts)
{ struct ice_pf *pf = ptp_info_to_pf(info);
u64 time_ns;
/** * ice_ptp_settime64 - Set the time of the clock * @info: the driver's PTP info structure * @ts: timespec64 structure that holds the new time value * * Set the device clock to the user input value. The conversion from timespec * to ns happens in the write function.
*/ staticint
ice_ptp_settime64(struct ptp_clock_info *info, conststruct timespec64 *ts)
{ struct ice_pf *pf = ptp_info_to_pf(info); struct timespec64 ts64 = *ts; struct ice_hw *hw = &pf->hw; int err;
/* For Vernier mode on E82X, we need to recalibrate after new settime. * Start with marking timestamps as invalid.
*/ if (hw->mac_type == ICE_MAC_GENERIC) {
err = ice_ptp_clear_phy_offset_ready_e82x(hw); if (err)
dev_warn(ice_pf_to_dev(pf), "Failed to mark timestamps as invalid before settime\n");
}
if (!ice_ptp_lock(hw)) {
err = -EBUSY; gotoexit;
}
/* Recalibrate and re-enable timestamp blocks for E822/E823 */ if (hw->mac_type == ICE_MAC_GENERIC)
ice_ptp_restart_all_phy(pf); exit: if (err) {
dev_err(ice_pf_to_dev(pf), "PTP failed to set time %d\n", err); return err;
}
return 0;
}
/** * ice_ptp_adjtime_nonatomic - Do a non-atomic clock adjustment * @info: the driver's PTP info structure * @delta: Offset in nanoseconds to adjust the time by
*/ staticint ice_ptp_adjtime_nonatomic(struct ptp_clock_info *info, s64 delta)
{ struct timespec64 now, then; int ret;
then = ns_to_timespec64(delta);
ret = ice_ptp_gettimex64(info, &now, NULL); if (ret) return ret;
now = timespec64_add(now, then);
/** * ice_ptp_adjtime - Adjust the time of the clock by the indicated delta * @info: the driver's PTP info structure * @delta: Offset in nanoseconds to adjust the time by
*/ staticint ice_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
{ struct ice_pf *pf = ptp_info_to_pf(info); struct ice_hw *hw = &pf->hw; struct device *dev; int err;
dev = ice_pf_to_dev(pf);
/* Hardware only supports atomic adjustments using signed 32-bit * integers. For any adjustment outside this range, perform * a non-atomic get->adjust->set flow.
*/ if (delta > S32_MAX || delta < S32_MIN) {
dev_dbg(dev, "delta = %lld, adjtime non-atomic\n", delta); return ice_ptp_adjtime_nonatomic(info, delta);
}
if (!ice_ptp_lock(hw)) {
dev_err(dev, "PTP failed to acquire semaphore in adjtime\n"); return -EBUSY;
}
if (err) {
dev_err(dev, "PTP failed to adjust time, err %d\n", err); return err;
}
ice_ptp_reset_cached_phctime(pf);
return 0;
}
/** * struct ice_crosststamp_cfg - Device cross timestamp configuration * @lock_reg: The hardware semaphore lock to use * @lock_busy: Bit in the semaphore lock indicating the lock is busy * @ctl_reg: The hardware register to request cross timestamp * @ctl_active: Bit in the control register to request cross timestamp * @art_time_l: Lower 32-bits of ART system time * @art_time_h: Upper 32-bits of ART system time * @dev_time_l: Lower 32-bits of device time (per timer index) * @dev_time_h: Upper 32-bits of device time (per timer index)
*/ struct ice_crosststamp_cfg { /* HW semaphore lock register */
u32 lock_reg;
u32 lock_busy;
/* Capture control register */
u32 ctl_reg;
u32 ctl_active;
#endif/* CONFIG_ICE_HWTS */ /** * struct ice_crosststamp_ctx - Device cross timestamp context * @snapshot: snapshot of system clocks for historic interpolation * @pf: pointer to the PF private structure * @cfg: pointer to hardware configuration for cross timestamp
*/ struct ice_crosststamp_ctx { struct system_time_snapshot snapshot; struct ice_pf *pf; conststruct ice_crosststamp_cfg *cfg;
};
/** * ice_capture_crosststamp - Capture a device/system cross timestamp * @device: Current device time * @system: System counter value read synchronously with device time * @__ctx: Context passed from ice_ptp_getcrosststamp * * Read device and system (ART) clock simultaneously and return the corrected * clock values in ns. * * Return: zero on success, or a negative error code on failure.
*/ staticint ice_capture_crosststamp(ktime_t *device, struct system_counterval_t *system, void *__ctx)
{ struct ice_crosststamp_ctx *ctx = __ctx; conststruct ice_crosststamp_cfg *cfg;
u32 lock, ctl, ts_lo, ts_hi, tmr_idx; struct ice_pf *pf; struct ice_hw *hw; int err;
u64 ts;
cfg = ctx->cfg;
pf = ctx->pf;
hw = &pf->hw;
tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc; if (tmr_idx > 1) return -EINVAL;
/* Poll until we obtain the cross-timestamp hardware semaphore */
err = rd32_poll_timeout(hw, cfg->lock_reg, lock,
!(lock & cfg->lock_busy),
10 * USEC_PER_MSEC, 50 * USEC_PER_MSEC); if (err) {
dev_err(ice_pf_to_dev(pf), "PTP failed to get cross timestamp lock\n"); return -EBUSY;
}
/* Snapshot system time for historic interpolation */
ktime_get_snapshot(&ctx->snapshot);
/* Program cmd to master timer */
ice_ptp_src_cmd(hw, ICE_PTP_READ_TIME);
/* Start the ART and device clock sync sequence */
ctl = rd32(hw, cfg->ctl_reg);
ctl |= cfg->ctl_active;
wr32(hw, cfg->ctl_reg, ctl);
/* Poll until hardware completes the capture */
err = rd32_poll_timeout(hw, cfg->ctl_reg, ctl, !(ctl & cfg->ctl_active),
5, 20 * USEC_PER_MSEC); if (err) goto err_timeout;
/* Read ART system time */
ts_lo = rd32(hw, cfg->art_time_l);
ts_hi = rd32(hw, cfg->art_time_h);
ts = ((u64)ts_hi << 32) | ts_lo;
system->cycles = ts;
system->cs_id = CSID_X86_ART;
system->use_nsecs = true;
/** * ice_ptp_getcrosststamp - Capture a device cross timestamp * @info: the driver's PTP info structure * @cts: The memory to fill the cross timestamp info * * Capture a cross timestamp between the ART and the device PTP hardware * clock. Fill the cross timestamp information and report it back to the * caller. * * In order to correctly correlate the ART timestamp back to the TSC time, the * CPU must have X86_FEATURE_TSC_KNOWN_FREQ. * * Return: zero on success, or a negative error code on failure.
*/ staticint ice_ptp_getcrosststamp(struct ptp_clock_info *info, struct system_device_crosststamp *cts)
{ struct ice_pf *pf = ptp_info_to_pf(info); struct ice_crosststamp_ctx ctx = {
.pf = pf,
};
switch (pf->hw.mac_type) { case ICE_MAC_GENERIC: case ICE_MAC_GENERIC_3K_E825:
ctx.cfg = &ice_crosststamp_cfg_e82x; break; #ifdef CONFIG_ICE_HWTS case ICE_MAC_E830:
ctx.cfg = &ice_crosststamp_cfg_e830; break; #endif/* CONFIG_ICE_HWTS */ default: return -EOPNOTSUPP;
}
switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE:
pf->ptp.tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: case HWTSTAMP_FILTER_NTP_ALL: case HWTSTAMP_FILTER_ALL:
pf->ptp.tstamp_config.rx_filter = HWTSTAMP_FILTER_ALL; break; default: return -ERANGE;
}
/* Immediately update the device timestamping mode */
ice_ptp_restore_timestamp_mode(pf);
return 0;
}
/** * ice_ptp_hwtstamp_set - interface to control the timestamping * @netdev: Pointer to network interface device structure * @config: Timestamping configuration structure * @extack: Netlink extended ack structure for error reporting * * Get the user config and store it
*/ int ice_ptp_hwtstamp_set(struct net_device *netdev, struct kernel_hwtstamp_config *config, struct netlink_ext_ack *extack)
{ struct ice_netdev_priv *np = netdev_priv(netdev); struct ice_pf *pf = np->vsi->back; int err;
if (pf->ptp.state != ICE_PTP_READY) return -EAGAIN;
err = ice_ptp_set_timestamp_mode(pf, config); if (err) return err;
/* Return the actual configuration set */
*config = pf->ptp.tstamp_config;
return 0;
}
/** * ice_ptp_get_rx_hwts - Get packet Rx timestamp in ns * @rx_desc: Receive descriptor * @pkt_ctx: Packet context to get the cached time * * The driver receives a notification in the receive descriptor with timestamp.
*/
u64 ice_ptp_get_rx_hwts(constunion ice_32b_rx_flex_desc *rx_desc, conststruct ice_pkt_ctx *pkt_ctx)
{
u64 ts_ns, cached_time;
u32 ts_high;
if (!(rx_desc->wb.time_stamp_low & ICE_PTP_TS_VALID)) return 0;
cached_time = READ_ONCE(pkt_ctx->cached_phctime);
/* Do not report a timestamp if we don't have a cached PHC time */ if (!cached_time) return 0;
/* Use ice_ptp_extend_32b_ts directly, using the ring-specific cached * PHC value, rather than accessing the PF. This also allows us to * simply pass the upper 32bits of nanoseconds directly. Calling * ice_ptp_extend_40b_ts is unnecessary as it would just discard these * bits itself.
*/
ts_high = le32_to_cpu(rx_desc->wb.flex_ts.ts_high);
ts_ns = ice_ptp_extend_32b_ts(cached_time, ts_high);
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.