igc_pin_perout(igc, i, pin, use_freq);
igc_ptp_read(igc, &safe_start);
/* PPS output start time is triggered by Target time(TT) * register. Programming any past time value into TT * register will cause PPS to never start. Need to make * sure we program the TT register a time ahead in * future. There isn't a stringent need to fire PPS out * right away. Adding +2 seconds should take care of * corner cases. Let's say if the SYSTIML is close to * wrap up and the timer keeps ticking as we program the * register, adding +2seconds is safe bet.
*/
safe_start.tv_sec += 2;
staticint igc_ptp_verify_pin(struct ptp_clock_info *ptp, unsignedint pin, enum ptp_pin_function func, unsignedint chan)
{ switch (func) { case PTP_PF_NONE: case PTP_PF_EXTTS: case PTP_PF_PEROUT: break; case PTP_PF_PHYSYNC: return -1;
} return 0;
}
/** * igc_ptp_systim_to_hwtstamp - convert system time value to HW timestamp * @adapter: board private structure * @hwtstamps: timestamp structure to update * @systim: unsigned 64bit system time value * * We need to convert the system time value stored in the RX/TXSTMP registers * into a hwtstamp which can be used by the upper level timestamping functions. * * Returns 0 on success.
**/ staticint igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, struct skb_shared_hwtstamps *hwtstamps,
u64 systim)
{ switch (adapter->hw.mac.type) { case igc_i225:
memset(hwtstamps, 0, sizeof(*hwtstamps)); /* Upper 32 bits contain s, lower 32 bits contain ns. */
hwtstamps->hwtstamp = ktime_set(systim >> 32,
systim & 0xFFFFFFFF); break; default: return -EINVAL;
} return 0;
}
/** * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer * @adapter: Pointer to adapter the packet buffer belongs to * @buf: Pointer to start of timestamp in HW format (2 32-bit words) * * This function retrieves and converts the timestamp stored at @buf * to ktime_t, adjusting for hardware latencies. * * Returns timestamp value.
*/
ktime_t igc_ptp_rx_pktstamp(struct igc_adapter *adapter, __le32 *buf)
{
ktime_t timestamp;
u32 secs, nsecs; int adjust;
val = rd32(IGC_RXPBS);
val |= IGC_RXPBS_CFG_TS_EN;
wr32(IGC_RXPBS, val);
for (i = 0; i < adapter->num_rx_queues; i++) {
val = rd32(IGC_SRRCTL(i)); /* Enable retrieving timestamps from timer 0, the * "adjustable clock" and timer 1 the "free running * clock".
*/
val |= IGC_SRRCTL_TIMER1SEL(1) | IGC_SRRCTL_TIMER0SEL(0) |
IGC_SRRCTL_TIMESTAMP;
wr32(IGC_SRRCTL(i), val);
}
val = IGC_TSYNCRXCTL_ENABLED | IGC_TSYNCRXCTL_TYPE_ALL |
IGC_TSYNCRXCTL_RXSYNSIG;
wr32(IGC_TSYNCRXCTL, val);
}
/* Note: tstamp->skb and tstamp->xsk_tx_buffer are in union. * By setting tstamp->xsk_tx_buffer to NULL, tstamp->skb will * become NULL as well.
*/
tstamp->xsk_tx_buffer = NULL;
tstamp->buffer_type = 0;
/* Clear the flags first to avoid new packets to be enqueued * for TX timestamping.
*/ for (i = 0; i < adapter->num_tx_queues; i++) { struct igc_ring *tx_ring = adapter->tx_ring[i];
/* Read TXSTMP registers to discard any timestamp previously stored. */
rd32(IGC_TXSTMPL);
rd32(IGC_TXSTMPH);
/* The hardware is ready to accept TX timestamp requests, * notify the transmit path.
*/ for (i = 0; i < adapter->num_tx_queues; i++) { struct igc_ring *tx_ring = adapter->tx_ring[i];
/** * igc_ptp_set_timestamp_mode - setup hardware for timestamping * @adapter: networking device structure * @config: hwtstamp configuration * * Return: 0 in case of success, negative errno code otherwise.
*/ staticint igc_ptp_set_timestamp_mode(struct igc_adapter *adapter, struct kernel_hwtstamp_config *config)
{ switch (config->tx_type) { case HWTSTAMP_TX_OFF:
igc_ptp_disable_tx_timestamp(adapter); break; case HWTSTAMP_TX_ON:
igc_ptp_enable_tx_timestamp(adapter); break; default: return -ERANGE;
}
switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE:
igc_ptp_disable_rx_timestamp(adapter); break; 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_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_NTP_ALL: case HWTSTAMP_FILTER_ALL:
igc_ptp_enable_rx_timestamp(adapter);
config->rx_filter = HWTSTAMP_FILTER_ALL; break; default: return -ERANGE;
}
return 0;
}
/* Requires adapter->ptp_tx_lock held by caller. */ staticvoid igc_ptp_tx_timeout(struct igc_adapter *adapter, struct igc_tx_timestamp_request *tstamp)
{ if (tstamp->skb)
igc_ptp_free_tx_buffer(adapter, tstamp);
for (i = 0; i < IGC_MAX_TX_TSTAMP_REGS; i++) {
tstamp = &adapter->tx_tstamp[i];
if (!tstamp->skb) continue;
if (time_is_after_jiffies(tstamp->start + IGC_PTP_TX_TIMEOUT)) continue;
igc_ptp_tx_timeout(adapter, tstamp);
found = true;
}
if (found) { /* Reading the high register of the first set of timestamp registers * clears all the equivalent bits in the TSYNCTXCTL register.
*/
rd32(IGC_TXSTMPH_0);
}
/** * igc_ptp_tx_hwtstamp - utility function which checks for TX time stamp * @adapter: Board private structure * * Check against the ready mask for which of the timestamp register * sets are ready to be retrieved, then retrieve that and notify the * rest of the stack. * * Context: Expects adapter->ptp_tx_lock to be held by caller.
*/ staticvoid igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
{ struct igc_hw *hw = &adapter->hw;
u64 regval;
u32 mask; int i;
mask = rd32(IGC_TSYNCTXCTL) & IGC_TSYNCTXCTL_TXTT_ANY; if (mask & IGC_TSYNCTXCTL_TXTT_0) {
regval = rd32(IGC_TXSTMPL);
regval |= (u64)rd32(IGC_TXSTMPH) << 32;
} else { /* There's a bug in the hardware that could cause * missing interrupts for TX timestamping. The issue * is that for new interrupts to be triggered, the * IGC_TXSTMPH_0 register must be read. * * To avoid discarding a valid timestamp that just * happened at the "wrong" time, we need to confirm * that there was no timestamp captured, we do that by * assuming that no two timestamps in sequence have * the same nanosecond value. * * So, we read the "low" register, read the "high" * register (to latch a new timestamp) and read the * "low" register again, if "old" and "new" versions * of the "low" register are different, a valid * timestamp was captured, we can read the "high" * register again.
*/
u32 txstmpl_old, txstmpl_new;
done: /* Now that the problematic first register was handled, we can * use retrieve the timestamps from the other registers * (starting from '1') with less complications.
*/ for (i = 1; i < IGC_MAX_TX_TSTAMP_REGS; i++) { struct igc_tx_timestamp_request *tstamp = &adapter->tx_tstamp[i];
/** * igc_ptp_tx_tstamp_event * @adapter: board private structure * * Called when a TX timestamp interrupt happens to retrieve the * timestamp and send it up to the socket.
*/ void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter)
{ unsignedlong flags;
/** * igc_ptp_hwtstamp_set - set hardware time stamping config * @netdev: network interface device structure * @config: timestamping configuration structure * @extack: netlink extended ack structure for error reporting *
**/ int igc_ptp_hwtstamp_set(struct net_device *netdev, struct kernel_hwtstamp_config *config, struct netlink_ext_ack *extack)
{ struct igc_adapter *adapter = netdev_priv(netdev); int err;
err = igc_ptp_set_timestamp_mode(adapter, config); if (err) return err;
/* save these settings for future reference */
adapter->tstamp_config = *config;
return 0;
}
/** * igc_ptp_hwtstamp_get - get hardware time stamping config * @netdev: network interface device structure * @config: timestamping configuration structure * * Get the hwtstamp_config settings to return to the user. Rather than attempt * to deconstruct the settings from the registers, just return a shadow copy * of the last known settings.
**/ int igc_ptp_hwtstamp_get(struct net_device *netdev, struct kernel_hwtstamp_config *config)
{ struct igc_adapter *adapter = netdev_priv(netdev);
*config = adapter->tstamp_config;
return 0;
}
/* The two conditions below must be met for cross timestamping via * PCIe PTM: * * 1. We have an way to convert the timestamps in the PTM messages * to something related to the system clocks (right now, only * X86 systems with support for the Always Running Timer allow that); * * 2. We have PTM enabled in the path from the device to the PCIe root port.
*/ staticbool igc_is_crosststamp_supported(struct igc_adapter *adapter)
{ if (!IS_ENABLED(CONFIG_X86_TSC)) returnfalse;
/* FIXME: it was noticed that enabling support for PCIe PTM in * some i225-V models could cause lockups when bringing the * interface up/down. There should be no downsides to * disabling crosstimestamping support for i225-V, as it * doesn't have any PTP support. That way we gain some time * while root causing the issue.
*/ if (adapter->pdev->device == IGC_DEV_ID_I225_V) returnfalse;
switch (ptm_stat) { case IGC_PTM_STAT_RET_ERR:
netdev_err(netdev, "PTM Error: Root port timeout\n"); break; case IGC_PTM_STAT_BAD_PTM_RES:
netdev_err(netdev, "PTM Error: Bad response, PTM Response Data expected\n"); break; case IGC_PTM_STAT_T4M1_OVFL:
netdev_err(netdev, "PTM Error: T4 minus T1 overflow\n"); break; case IGC_PTM_STAT_ADJUST_1ST:
netdev_err(netdev, "PTM Error: 1588 timer adjusted during first PTM cycle\n"); break; case IGC_PTM_STAT_ADJUST_CYC:
netdev_err(netdev, "PTM Error: 1588 timer adjusted during non-first PTM cycle\n"); break; default:
netdev_err(netdev, "PTM Error: Unknown error (%#x)\n", ptm_stat); break;
}
}
/* The PTM lock: adapter->ptm_lock must be held when calling igc_ptm_trigger() */ staticvoid igc_ptm_trigger(struct igc_hw *hw)
{
u32 ctrl;
/* To "manually" start the PTM cycle we need to set the * trigger (TRIG) bit
*/
ctrl = rd32(IGC_PTM_CTRL);
ctrl |= IGC_PTM_CTRL_TRIG;
wr32(IGC_PTM_CTRL, ctrl); /* Perform flush after write to CTRL register otherwise * transaction may not start
*/
wrfl();
}
/* The PTM lock: adapter->ptm_lock must be held when calling igc_ptm_reset() */ staticvoid igc_ptm_reset(struct igc_hw *hw)
{
u32 ctrl;
ctrl = rd32(IGC_PTM_CTRL);
ctrl &= ~IGC_PTM_CTRL_TRIG;
wr32(IGC_PTM_CTRL, ctrl); /* Write to clear all status */
wr32(IGC_PTM_STAT, IGC_PTM_STAT_ALL);
}
/* Doing this in a loop because in the event of a * badly timed (ha!) system clock adjustment, we may * get PTM errors from the PCI root, but these errors * are transitory. Repeating the process returns valid * data eventually.
*/ do { /* Get a snapshot of system clocks to use as historic value. */
ktime_get_snapshot(&adapter->snapshot);
/* FIXME: When the register that tells the endianness of the * PTM registers are implemented, check them here and add the * appropriate conversion.
*/
t2_curr_h = swab32(t2_curr_h);
/** * igc_ptp_suspend - Disable PTP work items and prepare for suspend * @adapter: Board private structure * * This function stops the overflow check work and PTP Tx timestamp work, and * will prepare the device for OS suspend.
*/ void igc_ptp_suspend(struct igc_adapter *adapter)
{ if (!(adapter->ptp_flags & IGC_PTP_ENABLED)) return;
igc_ptp_clear_tx_tstamp(adapter);
if (pci_device_is_present(adapter->pdev)) {
igc_ptp_time_save(adapter);
igc_ptm_stop(adapter);
}
}
/** * igc_ptp_stop - Disable PTP device and stop the overflow check. * @adapter: Board private structure. * * This function stops the PTP support and cancels the delayed work.
**/ void igc_ptp_stop(struct igc_adapter *adapter)
{ if (!(adapter->ptp_flags & IGC_PTP_ENABLED)) return;
/** * igc_ptp_reset - Re-enable the adapter for PTP following a reset. * @adapter: Board private structure. * * This function handles the reset work required to re-enable the PTP device.
**/ void igc_ptp_reset(struct igc_adapter *adapter)
{ struct igc_hw *hw = &adapter->hw;
u32 cycle_ctrl, ctrl, stat; unsignedlong flags;
u32 timadj;
if (!(adapter->ptp_flags & IGC_PTP_ENABLED)) return;
/* reset the tstamp_config */
igc_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
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.