blob: ef2e858f49bb0ed12302454177a2640b07ec7161 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2021, Intel Corporation. */
#include "ice.h"
#include "ice_lib.h"
#include "ice_trace.h"
#define E810_OUT_PROP_DELAY_NS 1
static const struct ptp_pin_desc ice_pin_desc_e810t[] = {
/* name idx func chan */
{ "GNSS", GNSS, PTP_PF_EXTTS, 0, { 0, } },
{ "SMA1", SMA1, PTP_PF_NONE, 1, { 0, } },
{ "U.FL1", UFL1, PTP_PF_NONE, 1, { 0, } },
{ "SMA2", SMA2, PTP_PF_NONE, 2, { 0, } },
{ "U.FL2", UFL2, PTP_PF_NONE, 2, { 0, } },
};
/**
* ice_get_sma_config_e810t
* @hw: pointer to the hw struct
* @ptp_pins: pointer to the ptp_pin_desc struture
*
* Read the configuration of the SMA control logic and put it into the
* ptp_pin_desc structure
*/
static int
ice_get_sma_config_e810t(struct ice_hw *hw, struct ptp_pin_desc *ptp_pins)
{
u8 data, i;
int status;
/* Read initial pin state */
status = ice_read_sma_ctrl_e810t(hw, &data);
if (status)
return status;
/* initialize with defaults */
for (i = 0; i < NUM_PTP_PINS_E810T; i++) {
strscpy(ptp_pins[i].name, ice_pin_desc_e810t[i].name,
sizeof(ptp_pins[i].name));
ptp_pins[i].index = ice_pin_desc_e810t[i].index;
ptp_pins[i].func = ice_pin_desc_e810t[i].func;
ptp_pins[i].chan = ice_pin_desc_e810t[i].chan;
}
/* Parse SMA1/UFL1 */
switch (data & ICE_SMA1_MASK_E810T) {
case ICE_SMA1_MASK_E810T:
default:
ptp_pins[SMA1].func = PTP_PF_NONE;
ptp_pins[UFL1].func = PTP_PF_NONE;
break;
case ICE_SMA1_DIR_EN_E810T:
ptp_pins[SMA1].func = PTP_PF_PEROUT;
ptp_pins[UFL1].func = PTP_PF_NONE;
break;
case ICE_SMA1_TX_EN_E810T:
ptp_pins[SMA1].func = PTP_PF_EXTTS;
ptp_pins[UFL1].func = PTP_PF_NONE;
break;
case 0:
ptp_pins[SMA1].func = PTP_PF_EXTTS;
ptp_pins[UFL1].func = PTP_PF_PEROUT;
break;
}
/* Parse SMA2/UFL2 */
switch (data & ICE_SMA2_MASK_E810T) {
case ICE_SMA2_MASK_E810T:
default:
ptp_pins[SMA2].func = PTP_PF_NONE;
ptp_pins[UFL2].func = PTP_PF_NONE;
break;
case (ICE_SMA2_TX_EN_E810T | ICE_SMA2_UFL2_RX_DIS_E810T):
ptp_pins[SMA2].func = PTP_PF_EXTTS;
ptp_pins[UFL2].func = PTP_PF_NONE;
break;
case (ICE_SMA2_DIR_EN_E810T | ICE_SMA2_UFL2_RX_DIS_E810T):
ptp_pins[SMA2].func = PTP_PF_PEROUT;
ptp_pins[UFL2].func = PTP_PF_NONE;
break;
case (ICE_SMA2_DIR_EN_E810T | ICE_SMA2_TX_EN_E810T):
ptp_pins[SMA2].func = PTP_PF_NONE;
ptp_pins[UFL2].func = PTP_PF_EXTTS;
break;
case ICE_SMA2_DIR_EN_E810T:
ptp_pins[SMA2].func = PTP_PF_PEROUT;
ptp_pins[UFL2].func = PTP_PF_EXTTS;
break;
}
return 0;
}
/**
* ice_ptp_set_sma_config_e810t
* @hw: pointer to the hw struct
* @ptp_pins: pointer to the ptp_pin_desc struture
*
* Set the configuration of the SMA control logic based on the configuration in
* num_pins parameter
*/
static int
ice_ptp_set_sma_config_e810t(struct ice_hw *hw,
const struct ptp_pin_desc *ptp_pins)
{
int status;
u8 data;
/* SMA1 and UFL1 cannot be set to TX at the same time */
if (ptp_pins[SMA1].func == PTP_PF_PEROUT &&
ptp_pins[UFL1].func == PTP_PF_PEROUT)
return -EINVAL;
/* SMA2 and UFL2 cannot be set to RX at the same time */
if (ptp_pins[SMA2].func == PTP_PF_EXTTS &&
ptp_pins[UFL2].func == PTP_PF_EXTTS)
return -EINVAL;
/* Read initial pin state value */
status = ice_read_sma_ctrl_e810t(hw, &data);
if (status)
return status;
/* Set the right sate based on the desired configuration */
data &= ~ICE_SMA1_MASK_E810T;
if (ptp_pins[SMA1].func == PTP_PF_NONE &&
ptp_pins[UFL1].func == PTP_PF_NONE) {
dev_info(ice_hw_to_dev(hw), "SMA1 + U.FL1 disabled");
data |= ICE_SMA1_MASK_E810T;
} else if (ptp_pins[SMA1].func == PTP_PF_EXTTS &&
ptp_pins[UFL1].func == PTP_PF_NONE) {
dev_info(ice_hw_to_dev(hw), "SMA1 RX");
data |= ICE_SMA1_TX_EN_E810T;
} else if (ptp_pins[SMA1].func == PTP_PF_NONE &&
ptp_pins[UFL1].func == PTP_PF_PEROUT) {
/* U.FL 1 TX will always enable SMA 1 RX */
dev_info(ice_hw_to_dev(hw), "SMA1 RX + U.FL1 TX");
} else if (ptp_pins[SMA1].func == PTP_PF_EXTTS &&
ptp_pins[UFL1].func == PTP_PF_PEROUT) {
dev_info(ice_hw_to_dev(hw), "SMA1 RX + U.FL1 TX");
} else if (ptp_pins[SMA1].func == PTP_PF_PEROUT &&
ptp_pins[UFL1].func == PTP_PF_NONE) {
dev_info(ice_hw_to_dev(hw), "SMA1 TX");
data |= ICE_SMA1_DIR_EN_E810T;
}
data &= ~ICE_SMA2_MASK_E810T;
if (ptp_pins[SMA2].func == PTP_PF_NONE &&
ptp_pins[UFL2].func == PTP_PF_NONE) {
dev_info(ice_hw_to_dev(hw), "SMA2 + U.FL2 disabled");
data |= ICE_SMA2_MASK_E810T;
} else if (ptp_pins[SMA2].func == PTP_PF_EXTTS &&
ptp_pins[UFL2].func == PTP_PF_NONE) {
dev_info(ice_hw_to_dev(hw), "SMA2 RX");
data |= (ICE_SMA2_TX_EN_E810T |
ICE_SMA2_UFL2_RX_DIS_E810T);
} else if (ptp_pins[SMA2].func == PTP_PF_NONE &&
ptp_pins[UFL2].func == PTP_PF_EXTTS) {
dev_info(ice_hw_to_dev(hw), "UFL2 RX");
data |= (ICE_SMA2_DIR_EN_E810T | ICE_SMA2_TX_EN_E810T);
} else if (ptp_pins[SMA2].func == PTP_PF_PEROUT &&
ptp_pins[UFL2].func == PTP_PF_NONE) {
dev_info(ice_hw_to_dev(hw), "SMA2 TX");
data |= (ICE_SMA2_DIR_EN_E810T |
ICE_SMA2_UFL2_RX_DIS_E810T);
} else if (ptp_pins[SMA2].func == PTP_PF_PEROUT &&
ptp_pins[UFL2].func == PTP_PF_EXTTS) {
dev_info(ice_hw_to_dev(hw), "SMA2 TX + U.FL2 RX");
data |= ICE_SMA2_DIR_EN_E810T;
}
return ice_write_sma_ctrl_e810t(hw, data);
}
/**
* ice_ptp_set_sma_e810t
* @info: the driver's PTP info structure
* @pin: pin index in kernel structure
* @func: Pin function to be set (PTP_PF_NONE, PTP_PF_EXTTS or PTP_PF_PEROUT)
*
* Set the configuration of a single SMA pin
*/
static int
ice_ptp_set_sma_e810t(struct ptp_clock_info *info, unsigned int pin,
enum ptp_pin_function func)
{
struct ptp_pin_desc ptp_pins[NUM_PTP_PINS_E810T];
struct ice_pf *pf = ptp_info_to_pf(info);
struct ice_hw *hw = &pf->hw;
int err;
if (pin < SMA1 || func > PTP_PF_PEROUT)
return -EOPNOTSUPP;
err = ice_get_sma_config_e810t(hw, ptp_pins);
if (err)
return err;
/* Disable the same function on the other pin sharing the channel */
if (pin == SMA1 && ptp_pins[UFL1].func == func)
ptp_pins[UFL1].func = PTP_PF_NONE;
if (pin == UFL1 && ptp_pins[SMA1].func == func)
ptp_pins[SMA1].func = PTP_PF_NONE;
if (pin == SMA2 && ptp_pins[UFL2].func == func)
ptp_pins[UFL2].func = PTP_PF_NONE;
if (pin == UFL2 && ptp_pins[SMA2].func == func)
ptp_pins[SMA2].func = PTP_PF_NONE;
/* Set up new pin function in the temp table */
ptp_pins[pin].func = func;
return ice_ptp_set_sma_config_e810t(hw, ptp_pins);
}
/**
* ice_verify_pin_e810t
* @info: the driver's PTP info structure
* @pin: Pin index
* @func: Assigned function
* @chan: Assigned channel
*
* Verify if pin supports requested pin function. If the Check pins consistency.
* Reconfigure the SMA logic attached to the given pin to enable its
* desired functionality
*/
static int
ice_verify_pin_e810t(struct ptp_clock_info *info, unsigned int pin,
enum ptp_pin_function func, unsigned int chan)
{
/* Don't allow channel reassignment */
if (chan != ice_pin_desc_e810t[pin].chan)
return -EOPNOTSUPP;
/* Check if functions are properly assigned */
switch (func) {
case PTP_PF_NONE:
break;
case PTP_PF_EXTTS:
if (pin == UFL1)
return -EOPNOTSUPP;
break;
case PTP_PF_PEROUT:
if (pin == UFL2 || pin == GNSS)
return -EOPNOTSUPP;
break;
case PTP_PF_PHYSYNC:
return -EOPNOTSUPP;
}
return ice_ptp_set_sma_e810t(info, pin, func);
}
/**
* 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.
*/
static void 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
*/
static void 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.
*/
static void 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;
ice_ptp_cfg_tx_interrupt(pf);
enable_rx = pf->ptp.tstamp_config.rx_filter == HWTSTAMP_FILTER_ALL;
ice_set_rx_tstamp(pf, 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.
*/
static 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;
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);
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);
unsigned long 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;
}
return ice_ptp_extend_32b_ts(pf->ptp.cached_phc_time,
(in_tstamp >> 8) & mask);
}
/**
* 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.
*/
static bool
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_ptp_port *ptp_port;
struct sk_buff *skb;
struct ice_pf *pf;
if (!tx->init)
return;
ptp_port = container_of(tx, struct ice_ptp_port, tx);
pf = ptp_port_to_pf(ptp_port);
/* 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++;
skb = tx->tstamps[idx].skb;
tx->tstamps[idx].skb = NULL;
clear_bit(idx, tx->in_use);
dev_kfree_skb_any(skb);
return;
}
ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx);
/* Write TS index to read to the PF register so the FW can read it */
wr32(&pf->hw, PF_SB_ATQBAL,
TS_LL_READ_TS_INTR | FIELD_PREP(TS_LL_READ_TS_IDX, idx) |
TS_LL_READ_TS);
tx->last_ll_ts_idx_read = idx;
}
/**
* ice_ptp_complete_tx_single_tstamp - Complete Tx timestamp for a port
* @tx: the PTP Tx timestamp tracker
*/
void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx)
{
struct skb_shared_hwtstamps shhwtstamps = {};
u8 idx = tx->last_ll_ts_idx_read;
struct ice_ptp_port *ptp_port;
u64 raw_tstamp, tstamp;
bool drop_ts = false;
struct sk_buff *skb;
struct ice_pf *pf;
u32 val;
if (!tx->init || tx->last_ll_ts_idx_read < 0)
return;
ptp_port = container_of(tx, struct ice_ptp_port, tx);
pf = ptp_port_to_pf(ptp_port);
ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx);
val = rd32(&pf->hw, PF_SB_ATQBAL);
/* When the bit is cleared, the TS is ready in the register */
if (val & TS_LL_READ_TS) {
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 = FIELD_GET(TS_LL_READ_TS_HIGH, val);
raw_tstamp <<= 32;
/* Read the low 32 bit value */
raw_tstamp |= (u64)rd32(&pf->hw, PF_SB_ATQBAH);
/* 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;
tx->tstamps[idx].cached_tstamp = raw_tstamp;
clear_bit(idx, tx->in_use);
skb = tx->tstamps[idx].skb;
tx->tstamps[idx].skb = NULL;
if (test_and_clear_bit(idx, tx->stale))
drop_ts = true;
if (!skb)
return;
if (drop_ts) {
dev_kfree_skb_any(skb);
return;
}
/* Extend the timestamp using cached PHC time */
tstamp = ice_ptp_extend_40b_ts(pf, raw_tstamp);
if (tstamp) {
shhwtstamps.hwtstamp = ns_to_ktime(tstamp);
ice_trace(tx_tstamp_complete, skb, idx);
}
skb_tstamp_tx(skb, &shhwtstamps);
dev_kfree_skb_any(skb);
}
/**
* 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.
*/
static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
{
struct ice_ptp_port *ptp_port;
unsigned long flags;
struct ice_pf *pf;
struct ice_hw *hw;
u64 tstamp_ready;
bool link_up;
int err;
u8 idx;
ptp_port = container_of(tx, struct ice_ptp_port, tx);
pf = ptp_port_to_pf(ptp_port);
hw = &pf->hw;
/* Read the Tx ready status first */
if (tx->has_ready_bitmap) {
err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready);
if (err)
return;
}
/* Drop packets if the link went down */
link_up = ptp_port->link_up;
for_each_set_bit(idx, tx->in_use, tx->len) {
struct skb_shared_hwtstamps shhwtstamps = {};
u8 phy_idx = idx + tx->offset;
u64 raw_tstamp = 0, tstamp;
bool drop_ts = !link_up;
struct sk_buff *skb;
/* 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;
continue;
}
ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx);
err = ice_read_phy_tstamp(hw, tx->block, phy_idx, &raw_tstamp);
if (err && !drop_ts)
continue;
ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx);
/* 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;
skip_ts_read:
spin_lock_irqsave(&tx->lock, flags);
if (!tx->has_ready_bitmap && raw_tstamp)
tx->tstamps[idx].cached_tstamp = raw_tstamp;
clear_bit(idx, tx->in_use);
skb = tx->tstamps[idx].skb;
tx->tstamps[idx].skb = NULL;
if (test_and_clear_bit(idx, tx->stale))
drop_ts = true;
spin_unlock_irqrestore(&tx->lock, flags);
/* It is unlikely but possible that the SKB will have been
* flushed at this point due to link change or teardown.
*/
if (!skb)
continue;
if (drop_ts) {
dev_kfree_skb_any(skb);
continue;
}
/* Extend the timestamp using cached PHC time */
tstamp = ice_ptp_extend_40b_ts(pf, raw_tstamp);
if (tstamp) {
shhwtstamps.hwtstamp = ns_to_ktime(tstamp);
ice_trace(tx_tstamp_complete, skb, idx);
}
skb_tstamp_tx(skb, &shhwtstamps);
dev_kfree_skb_any(skb);
}
}
/**
* ice_ptp_tx_tstamp_owner - Process Tx timestamps for all ports on the device
* @pf: Board private structure
*/
static enum ice_tx_tstamp_work ice_ptp_tx_tstamp_owner(struct ice_pf *pf)
{
struct ice_ptp_port *port;
unsigned int i;
mutex_lock(&pf->ptp.ports_owner.lock);
list_for_each_entry(port, &pf->ptp.ports_owner.ports, list_member) {
struct ice_ptp_tx *tx = &port->tx;
if (!tx || !tx->init)
continue;
ice_ptp_process_tx_tstamp(tx);
}
mutex_unlock(&pf->ptp.ports_owner.lock);
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;
else if (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.
*/
static enum ice_tx_tstamp_work ice_ptp_tx_tstamp(struct ice_ptp_tx *tx)
{
bool more_timestamps;
unsigned long 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.
*/
static int
ice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx)
{
unsigned long *in_use, *stale;
struct ice_tx_tstamp *tstamps;
tstamps = kcalloc(tx->len, sizeof(*tstamps), GFP_KERNEL);
in_use = bitmap_zalloc(tx->len, GFP_KERNEL);
stale = bitmap_zalloc(tx->len, GFP_KERNEL);
if (!tstamps || !in_use || !stale) {
kfree(tstamps);
bitmap_free(in_use);
bitmap_free(stale);
return -ENOMEM;
}
tx->tstamps = tstamps;
tx->in_use = in_use;
tx->stale = stale;
tx->init = 1;
tx->last_ll_ts_idx_read = -1;
spin_lock_init(&tx->lock);
return 0;
}
/**
* 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.
*/
static void
ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx)
{
struct ice_hw *hw = &pf->hw;
unsigned long 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;
}
for_each_set_bit(idx, tx->in_use, tx->len) {
u8 phy_idx = idx + tx->offset;
struct sk_buff *skb;
/* 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);
spin_lock_irqsave(&tx->lock, flags);
skb = tx->tstamps[idx].skb;
tx->tstamps[idx].skb = NULL;
clear_bit(idx, tx->in_use);
clear_bit(idx, tx->stale);
spin_unlock_irqrestore(&tx->lock, flags);
/* 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.
*/
static void
ice_ptp_mark_tx_tracker_stale(struct ice_ptp_tx *tx)
{
unsigned long flags;
spin_lock_irqsave(&tx->lock, flags);
bitmap_or(tx->stale, tx->stale, tx->in_use, tx->len);
spin_unlock_irqrestore(&tx->lock, 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.
*/
static void
ice_ptp_flush_all_tx_tracker(struct ice_pf *pf)
{
struct ice_ptp_port *port;
list_for_each_entry(port, &pf->ptp.ports_owner.ports, list_member)
ice_ptp_flush_tx_tracker(ptp_port_to_pf(port), &port->tx);
}
/**
* ice_ptp_release_tx_tracker - Release allocated memory for Tx tracker
* @pf: Board private structure
* @tx: Tx tracking structure to release
*
* Free memory associated with the Tx timestamp tracker.
*/
static void
ice_ptp_release_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx)
{
unsigned long flags;
spin_lock_irqsave(&tx->lock, flags);
tx->init = 0;
spin_unlock_irqrestore(&tx->lock, flags);
/* 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_eth56g - 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. ETH56G PHYs
* have independent memory blocks for all ports.
*
* Return: 0 for success, -ENOMEM when failed to allocate Tx tracker
*/
static int ice_ptp_init_tx_eth56g(struct ice_pf *pf, struct ice_ptp_tx *tx,
u8 port)
{
tx->block = port;
tx->offset = 0;
tx->len = INDEX_PER_PORT_ETH56G;
tx->has_ready_bitmap = 1;
return ice_ptp_alloc_tx_tracker(tx);
}
/**
* 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.
*/
static int
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_e810 - Initialize tracking for Tx timestamps
* @pf: Board private structure
* @tx: the Tx tracking structure to initialize
*
* Initialize the Tx timestamp tracker for this PF. For E810 devices, each
* port has its own block of timestamps, independent of the other ports.
*/
static int
ice_ptp_init_tx_e810(struct ice_pf *pf, struct ice_ptp_tx *tx)
{
tx->block = pf->hw.port_info->lport;
tx->offset = 0;
tx->len = INDEX_PER_PORT_E810;
/* 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 = 0;
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
*/
static int ice_ptp_update_cached_phctime(struct ice_pf *pf)
{
struct device *dev = ice_pf_to_dev(pf);
unsigned long update_before;
u64 systime;
int i;
update_before = pf->ptp.cached_phc_jiffies + msecs_to_jiffies(2000);
if (pf->ptp.cached_phc_time &&
time_is_before_jiffies(update_before)) {
unsigned long time_taken = jiffies - pf->ptp.cached_phc_jiffies;
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;
if (!vsi)
continue;
if (vsi->type != ICE_VSI_PF)
continue;
ice_for_each_rxq(vsi, j) {
if (!vsi->rx_rings[j])
continue;
WRITE_ONCE(vsi->rx_rings[j]->cached_phctime, systime);
}
}
clear_bit(ICE_CFG_BUSY, pf->state);
return 0;
}
/**
* 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.
*/
static void 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.
*/
static int 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.
*/
static int 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
*/
static int 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;
}
if (offs & 0x1)
phy_sts = FIELD_GET(Q_REG_FIFO13_M, val);
else
phy_sts = FIELD_GET(Q_REG_FIFO02_M, val);
if (phy_sts & FIFO_EMPTY) {
port->tx_fifo_busy_cnt = FIFO_OK;
return 0;
}
port->tx_fifo_busy_cnt++;
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.
*/
static void 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;
port = container_of(work, struct ice_ptp_port, ov_work.work);
pf = ptp_port_to_pf(port);
hw = &pf->hw;
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
*/
static int
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;
if (ice_is_e810(hw))
return 0;
mutex_lock(&ptp_port->ps_lock);
switch (hw->ptp.phy_model) {
case ICE_PHY_ETH56G:
err = ice_stop_phy_timer_eth56g(hw, port, true);
break;
case ICE_PHY_E82X:
kthread_cancel_delayed_work_sync(&ptp_port->ov_work);
err = ice_stop_phy_timer_e82x(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.
*/
static int
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;
unsigned long flags;
int err;
if (ice_is_e810(hw))
return 0;
if (!ptp_port->link_up)
return ice_ptp_port_phy_stop(ptp_port);
mutex_lock(&ptp_port->ps_lock);
switch (hw->ptp.phy_model) {
case ICE_PHY_ETH56G:
err = ice_start_phy_timer_eth56g(hw, port);
break;
case ICE_PHY_E82X:
/* Start the PHY timer in Vernier mode */
kthread_cancel_delayed_work_sync(&ptp_port->ov_work);
/* temporarily disable Tx timestamps while calibrating
* PHY offset
*/
spin_lock_irqsave(&ptp_port->tx.lock, flags);
ptp_port->tx.calibrating = true;
spin_unlock_irqrestore(&ptp_port->tx.lock, flags);
ptp_port->tx_fifo_busy_cnt = 0;
/* Start the PHY timer in Vernier mode */
err = ice_start_phy_timer_e82x(hw, port);
if (err)
break;
/* Enable Tx timestamps right away */
spin_lock_irqsave(&ptp_port->tx.lock, flags);
ptp_port->tx.calibrating = false;
spin_unlock_irqrestore(&ptp_port->tx.lock, flags);
kthread_queue_delayed_work(pf->ptp.kworker, &ptp_port->ov_work,
0);
break;
default:
err = -ENODEV;
}
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
* @port: Port for which the PHY start is set
* @linkup: Link is up or down
*/
void ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup)
{
struct ice_ptp_port *ptp_port;
struct ice_hw *hw = &pf->hw;
if (pf->ptp.state != ICE_PTP_READY)
return;
if (WARN_ON_ONCE(port >= hw->ptp.num_lports))
return;
ptp_port = &pf->ptp.port;
if (ice_is_e825c(hw) && hw->ptp.is_2x50g_muxed_topo)
port *= 2;
if (WARN_ON_ONCE(ptp_port->port_num != port))
return;
/* 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->ptp.phy_model) {
case ICE_PHY_E810:
/* Do not reconfigure E810 PHY */
return;
case ICE_PHY_ETH56G:
case ICE_PHY_E82X:
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
*/
static int 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->ptp.phy_model) {
case ICE_PHY_ETH56G: {
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_PHY_E82X: {
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_PHY_E810:
return 0;
case ICE_PHY_UNSUP:
default:
dev_warn(dev, "%s: Unexpected PHY model %d\n", __func__,
hw->ptp.phy_model);
return -EOPNOTSUPP;
}
}
/**
* ice_ptp_reset_phy_timestamping - Reset PHY timestamping block
* @pf: Board private structure
*/
static void ice_ptp_reset_phy_timestamping(struct ice_pf *pf)
{
ice_ptp_port_phy_restart(&pf->ptp.port);
}
/**
* ice_ptp_restart_all_phy - Restart all PHYs to recalibrate timestamping
* @pf: Board private structure
*/
static void ice_ptp_restart_all_phy(struct ice_pf *pf)
{
struct list_head *entry;
list_for_each(entry, &pf->ptp.ports_owner.ports) {
struct ice_ptp_port *port = list_entry(entry,
struct ice_ptp_port,
list_member);
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.
*/
static int 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;
}
return 0;
}
/**
* ice_ptp_extts_event - Process PTP external clock event
* @pf: Board private structure
*/
void ice_ptp_extts_event(struct ice_pf *pf)
{
struct ptp_clock_event event;
struct ice_hw *hw = &pf->hw;
u8 chan, tmr_idx;
u32 hi, lo;
/* 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++) {
/* Check if channel is enabled */
if (pf->ptp.ext_ts_irq & (1 << chan)) {
lo = rd32(hw, GLTSYN_EVNT_L(chan, tmr_idx));
hi = rd32(hw, GLTSYN_EVNT_H(chan, tmr_idx));
event.timestamp = (((u64)hi) << 32) | lo;
event.type = PTP_CLOCK_EXTTS;
event.index = chan;
/* Fire event */
ptp_clock_event(pf->ptp.clock, &event);
pf->ptp.ext_ts_irq &= ~(1 << chan);
}
}
}
/**
* ice_ptp_cfg_extts - Configure EXTTS pin and channel
* @pf: Board private structure
* @chan: GPIO channel (0-3)
* @config: desired EXTTS configuration.
* @store: If set to true, the values will be stored
*
* Configure an external timestamp event on the requested channel.
*
* Return: 0 on success, -EOPNOTUSPP on unsupported flags
*/
static int ice_ptp_cfg_extts(struct ice_pf *pf, unsigned int chan,
struct ice_extts_channel *config, bool store)
{
u32 func, aux_reg, gpio_reg, irq_reg;
struct ice_hw *hw = &pf->hw;
u8 tmr_idx;
/* Reject requests with unsupported flags */
if (config->flags & ~(PTP_ENABLE_FEATURE |
PTP_RISING_EDGE |
PTP_FALLING_EDGE |
PTP_STRICT_FLAGS))
return -EOPNOTSUPP;
tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
irq_reg = rd32(hw, PFINT_OICR_ENA);
if (config->ena) {
/* Enable the interrupt */
irq_reg |= PFINT_OICR_TSYN_EVNT_M;
aux_reg = GLTSYN_AUX_IN_0_INT_ENA_M;
#define GLTSYN_AUX_IN_0_EVNTLVL_RISING_EDGE BIT(0)
#define GLTSYN_AUX_IN_0_EVNTLVL_FALLING_EDGE BIT(1)
/* set event level to requested edge */
if (config->flags & PTP_FALLING_EDGE)
aux_reg |= GLTSYN_AUX_IN_0_EVNTLVL_FALLING_EDGE;
if (config->flags & PTP_RISING_EDGE)
aux_reg |= GLTSYN_AUX_IN_0_EVNTLVL_RISING_EDGE;
/* Write GPIO CTL reg.
* 0x1 is input sampled by EVENT register(channel)
* + num_in_channels * tmr_idx
*/
func = 1 + chan + (tmr_idx * 3);
gpio_reg = FIELD_PREP(GLGEN_GPIO_CTL_PIN_FUNC_M, func);
pf->ptp.ext_ts_chan |= (1 << chan);
} else {
/* clear the values we set to reset defaults */
aux_reg = 0;
gpio_reg = 0;
pf->ptp.ext_ts_chan &= ~(1 << chan);
if (!pf->ptp.ext_ts_chan)
irq_reg &= ~PFINT_OICR_TSYN_EVNT_M;
}
wr32(hw, PFINT_OICR_ENA, irq_reg);
wr32(hw, GLTSYN_AUX_IN(chan, tmr_idx), aux_reg);
wr32(hw, GLGEN_GPIO_CTL(config->gpio_pin), gpio_reg);
if (store)
memcpy(&pf->ptp.extts_channels[chan], config, sizeof(*config));
return 0;
}
/**
* ice_ptp_disable_all_extts - Disable all EXTTS channels
* @pf: Board private structure
*/
static void ice_ptp_disable_all_extts(struct ice_pf *pf)
{
struct ice_extts_channel extts_cfg = {};
int i;
for (i = 0; i < pf->ptp.info.n_ext_ts; i++) {
if (pf->ptp.extts_channels[i].ena) {
extts_cfg.gpio_pin = pf->ptp.extts_channels[i].gpio_pin;
extts_cfg.ena = false;
ice_ptp_cfg_extts(pf, i, &extts_cfg, false);
}
}
synchronize_irq(pf->oicr_irq.virq);
}
/**
* ice_ptp_enable_all_extts - Enable all EXTTS channels
* @pf: Board private structure
*
* Called during reset to restore user configuration.
*/
static void ice_ptp_enable_all_extts(struct ice_pf *pf)
{
int i;
for (i = 0; i < pf->ptp.info.n_ext_ts; i++) {
if (pf->ptp.extts_channels[i].ena)
ice_ptp_cfg_extts(pf, i, &pf->ptp.extts_channels[i],
false);
}
}
/**
* ice_ptp_cfg_clkout - Configure clock to generate periodic wave
* @pf: Board private structure
* @chan: GPIO channel (0-3)
* @config: desired periodic clk configuration. NULL will disable channel
* @store: If set to true the values will be stored
*
* Configure the internal clock generator modules to generate the clock wave of
* specified period.
*/
static int ice_ptp_cfg_clkout(struct ice_pf *pf, unsigned int chan,
struct ice_perout_channel *config, bool store)
{
u64 current_time, period, start_time, phase;
struct ice_hw *hw = &pf->hw;
u32 func, val, gpio_pin;
u8 tmr_idx;
if (config && config->flags & ~PTP_PEROUT_PHASE)
return -EOPNOTSUPP;
tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
/* 0. Reset mode & out_en in AUX_OUT */
wr32(hw, GLTSYN_AUX_OUT(chan, tmr_idx), 0);
/* If we're disabling the output, clear out CLKO and TGT and keep
* output level low
*/
if (!config || !config->ena) {
wr32(hw, GLTSYN_CLKO(chan, tmr_idx), 0);
wr32(hw, GLTSYN_TGT_L(chan, tmr_idx), 0);
wr32(hw, GLTSYN_TGT_H(chan, tmr_idx), 0);
val = GLGEN_GPIO_CTL_PIN_DIR_M;
gpio_pin = pf->ptp.perout_channels[chan].gpio_pin;
wr32(hw, GLGEN_GPIO_CTL(gpio_pin), val);
/* Store the value if requested */
if (store)
memset(&pf->ptp.perout_channels[chan], 0,
sizeof(struct ice_perout_channel));
return 0;
}
period = config->period;
start_time = config->start_time;
div64_u64_rem(start_time, period, &phase);
gpio_pin = config->gpio_pin;
/* 1. Write clkout with half of required period value */
if (period & 0x1) {
dev_err(ice_pf_to_dev(pf), "CLK Period must be an even value\n");
goto err;
}
period >>= 1;
/* For proper operation, the GLTSYN_CLKO must be larger than clock tick
*/
#define MIN_PULSE 3
if (period <= MIN_PULSE || period > U32_MAX) {
dev_err(ice_pf_to_dev(pf), "CLK Period must be > %d && < 2^33",
MIN_PULSE * 2);
goto err;
}
wr32(hw, GLTSYN_CLKO(chan, tmr_idx), lower_32_bits(period));
/* Allow time for programming before start_time is hit */
current_time = ice_ptp_read_src_clk_reg(pf, NULL);
/* if start time is in the past start the timer at the nearest second
* maintaining phase
*/
if (start_time < current_time)
start_time = roundup_u64(current_time, NSEC_PER_SEC) + phase;
if (ice_is_e810(hw))
start_time -= E810_OUT_PROP_DELAY_NS;
else
start_time -= ice_e82x_pps_delay(ice_e82x_time_ref(hw));
/* 2. Write TARGET time */
wr32(hw, GLTSYN_TGT_L(chan, tmr_idx), lower_32_bits(start_time));
wr32(hw, GLTSYN_TGT_H(chan, tmr_idx), upper_32_bits(start_time));
/* 3. Write AUX_OUT register */
val = GLTSYN_AUX_OUT_0_OUT_ENA_M | GLTSYN_AUX_OUT_0_OUTMOD_M;
wr32(hw, GLTSYN_AUX_OUT(chan, tmr_idx), val);
/* 4. write GPIO CTL reg */
func = 8 + chan + (tmr_idx * 4);
val = GLGEN_GPIO_CTL_PIN_DIR_M |
FIELD_PREP(GLGEN_GPIO_CTL_PIN_FUNC_M, func);
wr32(hw, GLGEN_GPIO_CTL(gpio_pin), val);
/* Store the value if requested */
if (store) {
memcpy(&pf->ptp.perout_channels[chan], config,
sizeof(struct ice_perout_channel));
pf->ptp.perout_channels[chan].start_time = phase;
}
return 0;
err:
dev_err(ice_pf_to_dev(pf), "PTP failed to cfg per_clk\n");
return -EFAULT;
}
/**
* ice_ptp_disable_all_clkout - Disable all currently configured outputs
* @pf: pointer to the PF structure
*
* Disable all currently configured clock outputs. This is necessary before
* certain changes to the PTP hardware clock. Use ice_ptp_enable_all_clkout to
* re-enable the clocks again.
*/
static void ice_ptp_disable_all_clkout(struct ice_pf *pf)
{
uint i;
for (i = 0; i < pf->ptp.info.n_per_out; i++)
if (pf->ptp.perout_channels[i].ena)
ice_ptp_cfg_clkout(pf, i, NULL, false);
}
/**
* ice_ptp_enable_all_clkout - Enable all configured periodic clock outputs
* @pf: pointer to the PF structure
*
* Enable all currently configured clock outputs. Use this after
* ice_ptp_disable_all_clkout to reconfigure the output signals according to
* their configuration.
*/
static void ice_ptp_enable_all_clkout(struct ice_pf *pf)
{
uint i;
for (i = 0; i < pf->ptp.info.n_per_out; i++)
if (pf->ptp.perout_channels[i].ena)
ice_ptp_cfg_clkout(pf, i, &pf->ptp.perout_channels[i],
false);
}
/**
* ice_ptp_gpio_enable_e810 - Enable/disable ancillary features of PHC
* @info: the driver's PTP info structure
* @rq: The requested feature to change
* @on: Enable/disable flag
*/
static int
ice_ptp_gpio_enable_e810(struct ptp_clock_info *info,
struct ptp_clock_request *rq, int on)
{
struct ice_pf *pf = ptp_info_to_pf(info);
bool sma_pres = false;
unsigned int chan;
u32 gpio_pin;
if (ice_is_feature_supported(pf, ICE_F_SMA_CTRL))
sma_pres = true;
switch (rq->type) {
case PTP_CLK_REQ_PEROUT:
{
struct ice_perout_channel clk_cfg = {};
chan = rq->perout.index;
if (sma_pres) {
if (chan == ice_pin_desc_e810t[SMA1].chan)
clk_cfg.gpio_pin = GPIO_20;
else if (chan == ice_pin_desc_e810t[SMA2].chan)
clk_cfg.gpio_pin = GPIO_22;
else
return -1;
} else if (ice_is_e810t(&pf->hw)) {
if (chan == 0)
clk_cfg.gpio_pin = GPIO_20;
else
clk_cfg.gpio_pin = GPIO_22;
} else if (chan == PPS_CLK_GEN_CHAN) {
clk_cfg.gpio_pin = PPS_PIN_INDEX;
} else {
clk_cfg.gpio_pin = chan;
}
clk_cfg.flags = rq->perout.flags;
clk_cfg.period = ((rq->perout.period.sec * NSEC_PER_SEC) +
rq->perout.period.nsec);
clk_cfg.start_time = ((rq->perout.start.sec * NSEC_PER_SEC) +
rq->perout.start.nsec);
clk_cfg.ena = !!on;
return ice_ptp_cfg_clkout(pf, chan, &clk_cfg, true);
}
case PTP_CLK_REQ_EXTTS:
{
struct ice_extts_channel extts_cfg = {};
chan = rq->extts.index;
if (sma_pres) {
if (chan < ice_pin_desc_e810t[SMA2].chan)
gpio_pin = GPIO_21;
else
gpio_pin = GPIO_23;
} else if (ice_is_e810t(&pf->hw)) {
if (chan == 0)
gpio_pin = GPIO_21;
else
gpio_pin = GPIO_23;
} else {
gpio_pin = chan;
}
extts_cfg.flags = rq->extts.flags;
extts_cfg.gpio_pin = gpio_pin;
extts_cfg.ena = !!on;
return ice_ptp_cfg_extts(pf, chan, &extts_cfg, true);
}
default:
return -EOPNOTSUPP;
}
}
/**
* ice_ptp_gpio_enable_e823 - Enable/disable ancillary features of PHC
* @info: the driver's PTP info structure
* @rq: The requested feature to change
* @on: Enable/disable flag
*/
static int ice_ptp_gpio_enable_e823(struct ptp_clock_info *info,
struct ptp_clock_request *rq, int on)
{
struct ice_pf *pf = ptp_info_to_pf(info);
switch (rq->type) {
case PTP_CLK_REQ_PPS:
{
struct ice_perout_channel clk_cfg = {};
clk_cfg.flags = rq->perout.flags;
clk_cfg.gpio_pin = PPS_PIN_INDEX;
clk_cfg.period = NSEC_PER_SEC;
clk_cfg.ena = !!on;
return ice_ptp_cfg_clkout(pf, PPS_CLK_GEN_CHAN, &clk_cfg, true);
}
case PTP_CLK_REQ_EXTTS:
{
struct ice_extts_channel extts_cfg = {};
extts_cfg.flags = rq->extts.flags;
extts_cfg.gpio_pin = TIME_SYNC_PIN_INDEX;
extts_cfg.ena = !!on;
return ice_ptp_cfg_extts(pf, rq->extts.index, &extts_cfg, true);
}
default:
return -EOPNOTSUPP;
}
}
/**
* 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.
*/
static int
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;
time_ns = ice_ptp_read_src_clk_reg(pf, sts);
*ts = ns_to_timespec64(time_ns);
return 0;
}
/**
* 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.
*/
static int
ice_ptp_settime64(struct ptp_clock_info *info, const struct 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->ptp.phy_model == ICE_PHY_E82X) {
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;
goto exit;
}
/* Disable periodic outputs */
ice_ptp_disable_all_clkout(pf);
err = ice_ptp_write_init(pf, &ts64);
ice_ptp_unlock(hw);
if (!err)
ice_ptp_reset_cached_phctime(pf);
/* Reenable periodic outputs */
ice_ptp_enable_all_clkout(pf);
/* Recalibrate and re-enable timestamp blocks for E822/E823 */
if (hw->ptp.phy_model == ICE_PHY_E82X)
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
*/
static int 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);
return ice_ptp_settime64(info, (const struct timespec64 *)&now);
}
/**
* 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
*/
static int 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;
}
/* Disable periodic outputs */
ice_ptp_disable_all_clkout(pf);
err = ice_ptp_write_adj(pf, delta);
/* Reenable periodic outputs */
ice_ptp_enable_all_clkout(pf);
ice_ptp_unlock(hw);
if (err) {
dev_err(dev, "PTP failed to adjust time, err %d\n", err);
return err;
}
ice_ptp_reset_cached_phctime(pf);
return 0;
}
#ifdef CONFIG_ICE_HWTS
/**
* ice_ptp_get_syncdevicetime - Get the cross time stamp info
* @device: Current device time
* @system: System counter value read synchronously with device time
* @ctx: Context provided by timekeeping code
*
* Read device and system (ART) clock simultaneously and return the corrected
* clock values in ns.
*/
static int
ice_ptp_get_syncdevicetime(ktime_t *device,
struct system_counterval_t *system,
void *ctx)
{
struct ice_pf *pf = (struct ice_pf *)ctx;
struct ice_hw *hw = &pf->hw;
u32 hh_lock, hh_art_ctl;
int i;
#define MAX_HH_HW_LOCK_TRIES 5
#define MAX_HH_CTL_LOCK_TRIES 100
for (i = 0; i < MAX_HH_HW_LOCK_TRIES; i++) {
/* Get the HW lock */
hh_lock = rd32(hw, PFHH_SEM + (PFTSYN_SEM_BYTES * hw->pf_id));
if (hh_lock & PFHH_SEM_BUSY_M) {
usleep_range(10000, 15000);
continue;
}
break;
}
if (hh_lock & PFHH_SEM_BUSY_M) {
dev_err(ice_pf_to_dev(pf), "PTP failed to get hh lock\n");
return -EBUSY;
}
/* Program cmd to master timer */
ice_ptp_src_cmd(hw, ICE_PTP_READ_TIME);
/* Start the ART and device clock sync sequence */
hh_art_ctl = rd32(hw, GLHH_ART_CTL);
hh_art_ctl = hh_art_ctl | GLHH_ART_CTL_ACTIVE_M;
wr32(hw, GLHH_ART_CTL, hh_art_ctl);
for (i = 0; i < MAX_HH_CTL_LOCK_TRIES; i++) {
/* Wait for sync to complete */
hh_art_ctl = rd32(hw, GLHH_ART_CTL);
if (hh_art_ctl & GLHH_ART_CTL_ACTIVE_M) {
udelay(1);
continue;
} else {
u32 hh_ts_lo, hh_ts_hi, tmr_idx;
u64 hh_ts;
tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
/* Read ART time */
hh_ts_lo = rd32(hw, GLHH_ART_TIME_L);
hh_ts_hi = rd32(hw, GLHH_ART_TIME_H);
hh_ts = ((u64)hh_ts_hi << 32) | hh_ts_lo;
system->cycles = hh_ts;
system->cs_id = CSID_X86_ART;
/* Read Device source clock time */
hh_ts_lo = rd32(hw, GLTSYN_HHTIME_L(tmr_idx));
hh_ts_hi = rd32(hw, GLTSYN_HHTIME_H(tmr_idx));
hh_ts = ((u64)hh_ts_hi << 32) | hh_ts_lo;
*device = ns_to_ktime(hh_ts);
break;
}
}
/* Clear the master timer */
ice_ptp_src_cmd(hw, ICE_PTP_NOP);
/* Release HW lock */
hh_lock = rd32(hw, PFHH_SEM + (PFTSYN_SEM_BYTES * hw->pf_id));
hh_lock = hh_lock & ~PFHH_SEM_BUSY_M;
wr32(hw, PFHH_SEM + (PFTSYN_SEM_BYTES * hw->pf_id), hh_lock);
if (i == MAX_HH_CTL_LOCK_TRIES)
return -ETIMEDOUT;
return 0;
}
/**
* ice_ptp_getcrosststamp_e82x - 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.
*
* This is only valid for E822 and E823 devices which have support for
* generating the cross timestamp via PCIe PTM.
*
* In order to correctly correlate the ART timestamp back to the TSC time, the
* CPU must have X86_FEATURE_TSC_KNOWN_FREQ.
*/
static int
ice_ptp_getcrosststamp_e82x(struct ptp_clock_info *info,
struct system_device_crosststamp *cts)
{
struct ice_pf *pf = ptp_info_to_pf(info);
return get_device_system_crosststamp(ice_ptp_get_syncdevicetime,
pf, NULL, cts);
}
#endif /* CONFIG_ICE_HWTS */
/**
* ice_ptp_get_ts_config - ioctl interface to read the timestamping config
* @pf: Board private structure
* @ifr: ioctl data
*
* Copy the timestamping config to user buffer
*/
int ice_ptp_get_ts_config(struct ice_pf *pf, struct ifreq *ifr)
{
struct hwtstamp_config *config;
if (pf->ptp.state != ICE_PTP_READY)
return -EIO;
config = &pf->ptp.tstamp_config;
return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
-EFAULT : 0;
}
/**
* ice_ptp_set_timestamp_mode - Setup driver for requested timestamp mode
* @pf: Board private structure
* @config: hwtstamp settings requested or saved
*/
static int
ice_ptp_set_timestamp_mode(struct ice_pf *pf, struct hwtstamp_config *config)
{
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
pf->ptp.tstamp_config.tx_type = HWTSTAMP_TX_OFF;
break;
case HWTSTAMP_TX_ON:
pf->ptp.tstamp_config.tx_type = HWTSTAMP_TX_ON;
break;
default:
return -ERANGE;
}
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_set_ts_config - ioctl interface to control the timestamping
* @pf: Board private structure
* @ifr: ioctl data
*
* Get the user config and store it
*/
int ice_ptp_set_ts_config(struct ice_pf *pf, struct ifreq *ifr)
{
struct hwtstamp_config config;
int err;
if (pf->ptp.state != ICE_PTP_READY)
return -EAGAIN;
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
err = ice_ptp_set_timestamp_mode(pf, &config);
if (err)
return err;
/* Return the actual configuration set */
config = pf->ptp.tstamp_config;
return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-EFAULT : 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(const union ice_32b_rx_flex_desc *rx_desc,
const struct 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);
return ts_ns;
}
/**
* ice_ptp_disable_sma_pins_e810t - Disable E810-T SMA pins
* @pf: pointer to the PF structure
* @info: PTP clock info structure
*
* Disable the OS access to the SMA pins. Called to clear out the OS
* indications of pin support when we fail to setup the E810-T SMA control
* register.
*/
static void
ice_ptp_disable_sma_pins_e810t(struct ice_pf *pf, struct ptp_clock_info *info)
{
struct device *dev = ice_pf_to_dev(pf);
dev_warn(dev, "Failed to configure E810-T SMA pin control\n");
info->enable = NULL;
info->verify = NULL;
info->n_pins = 0;
info->n_ext_ts = 0;
info->n_per_out = 0;
}
/**
* ice_ptp_setup_sma_pins_e810t - Setup the SMA pins
* @pf: pointer to the PF structure
* @info: PTP clock info structure
*
* Finish setting up the SMA pins by allocating pin_config, and setting it up
* according to the current status of the SMA. On failure, disable all of the
* extended SMA pin support.
*/
static void
ice_ptp_setup_sma_pins_e810t(struct ice_pf *pf, struct ptp_clock_info *info)
{
struct device *dev = ice_pf_to_dev(pf);
int err;
/* Allocate memory for kernel pins interface */
info->pin_config = devm_kcalloc(dev, info->n_pins,
sizeof(*info->pin_config), GFP_KERNEL);
if (!info->pin_config) {
ice_ptp_disable_sma_pins_e810t(pf, info);
return;
}
/* Read current SMA status */
err = ice_get_sma_config_e810t(&pf->hw, info->pin_config);
if (err)
ice_ptp_disable_sma_pins_e810t(pf, info);
}
/**
* ice_ptp_setup_pins_e810 - Setup PTP pins in sysfs
* @pf: pointer to the PF instance
* @info: PTP clock capabilities
*/
static void
ice_ptp_setup_pins_e810(struct ice_pf *pf, struct ptp_clock_info *info)
{
if (ice_is_feature_supported(pf, ICE_F_SMA_CTRL)) {
info->n_ext_ts = N_EXT_TS_E810;
info->n_per_out = N_PER_OUT_E810T;
info->n_pins = NUM_PTP_PINS_E810T;
info->verify = ice_verify_pin_e810t;
/* Complete setup of the SMA pins */
ice_ptp_setup_sma_pins_e810t(pf, info);
} else if (ice_is_e810t(&pf->hw)) {
info->n_ext_ts = N_EXT_TS_NO_SMA_E810T;
info->n_per_out = N_PER_OUT_NO_SMA_E810T;
} else {
info->n_per_out = N_PER_OUT_E810;
info->n_ext_ts = N_EXT_TS_E810;
}
}
/**
* ice_ptp_setup_pins_e823 - Setup PTP pins in sysfs
* @pf: pointer to the PF instance
* @info: PTP clock capabilities
*/
static void
ice_ptp_setup_pins_e823(struct ice_pf *pf, struct ptp_clock_info *info)
{
info->pps = 1;
info->n_per_out = 0;
info->n_ext_ts = 1;
}
/**
* ice_ptp_set_funcs_e82x - Set specialized functions for E82x support
* @pf: Board private structure
* @info: PTP info to fill
*
* Assign functions to the PTP capabiltiies structure for E82x devices.
* Functions which operate across all device families should be set directly
* in ice_ptp_set_caps. Only add functions here which are distinct for E82x
* devices.
*/
static void
ice_ptp_set_funcs_e82x(struct ice_pf *pf, struct ptp_clock_info *info)
{
#ifdef CONFIG_ICE_HWTS
if (boot_cpu_has(X86_FEATURE_ART) &&
boot_cpu_has(X86_FEATURE_TSC_KNOWN_FREQ))
info->getcrosststamp = ice_ptp_getcrosststamp_e82x;
#endif /* CONFIG_ICE_HWTS */
}
/**
* ice_ptp_set_funcs_e810 - Set specialized functions for E810 support
* @pf: Board private structure
* @info: PTP info to fill
*
* Assign functions to the PTP capabiltiies structure for E810 devices.
* Functions which operate across all device families should be set directly
* in ice_ptp_set_caps. Only add functions here which are distinct for e810
* devices.
*/
static void
ice_ptp_set_funcs_e810(struct ice_pf *pf, struct ptp_clock_info *info)
{
info->enable = ice_ptp_gpio_enable_e810;
ice_ptp_setup_pins_e810(pf, info);
}
/**
* ice_ptp_set_funcs_e823 - Set specialized functions for E823 support
* @pf: Board private structure
* @info: PTP info to fill
*
* Assign functions to the PTP capabiltiies structure for E823 devices.
* Functions which operate across all device families should be set directly
* in ice_ptp_set_caps. Only add functions here which are distinct for e823
* devices.
*/
static void
ice_ptp_set_funcs_e823(struct ice_pf *pf, struct ptp_clock_info *info)
{
ice_ptp_set_funcs_e82x(pf, info);
info->enable = ice_ptp_gpio_enable_e823;
ice_ptp_setup_pins_e823(pf, info);
}
/**
* ice_ptp_set_caps - Set PTP capabilities
* @pf: Board private structure
*/
static void ice_ptp_set_caps(struct ice_pf *pf)
{
struct ptp_clock_info *info = &pf->ptp.info;
struct device *dev = ice_pf_to_dev(pf);
snprintf(info->name, sizeof(info->name) - 1, "%s-%s-clk",
dev_driver_string(dev), dev_name(dev));
info->owner = THIS_MODULE;
info->max_adj = 100000000;
info->adjtime = ice_ptp_adjtime;
info->adjfine = ice_ptp_adjfine;
info->gettimex64 = ice_ptp_gettimex64;
info->settime64 = ice_ptp_settime64;
if (ice_is_e810(&pf->hw))
ice_ptp_set_funcs_e810(pf, info);
else if (ice_is_e823(&pf->hw))
ice_ptp_set_funcs_e823(pf, info);
else
ice_ptp_set_funcs_e82x(pf, info);
}
/**
* ice_ptp_create_clock - Create PTP clock device for userspace
* @pf: Board private structure
*
* This function creates a new PTP clock device. It only creates one if we
* don't already have one. Will return error if it can't create one, but success
* if we already have a device. Should be used by ice_ptp_init to create clock
* initially, and prevent global resets from creating new clock devices.
*/
static long ice_ptp_create_clock(struct ice_pf *pf)
{
struct ptp_clock_info *info;
struct device *dev;
/* No need to create a clock device if we already have one */
if (pf->ptp.clock)
return 0;
ice_ptp_set_caps(pf);
info = &pf->ptp.info;
dev = ice_pf_to_dev(pf);
/* Attempt to register the clock before enabling the hardware. */
pf->ptp.clock = ptp_clock_register(info, dev);
if (IS_ERR(pf->ptp.clock)) {
dev_err(ice_pf_to_dev(pf), "Failed to register PTP clock device");
return PTR_ERR(pf->ptp.clock);
}
return 0;
}
/**
* ice_ptp_request_ts - Request an available Tx timestamp index
* @tx: the PTP Tx timestamp tracker to request from
* @skb: the SKB to associate with this timestamp request
*/
s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb)
{
unsigned long flags;
u8 idx;
spin_lock_irqsave(&tx->lock, flags);
/* Check that this tracker is accepting new timestamp requests */
if (!ice_ptp_is_tx_tracker_up(tx)) {
spin_unlock_irqrestore(&tx->lock, flags);
return -1;
}
/* Find and set the first available index */
idx = find_next_zero_bit(tx->in_use, tx->len,
tx->last_ll_ts_idx_read + 1);
if (idx == tx->len)
idx = find_first_zero_bit(tx->in_use, tx->len);
if (idx < tx->len) {
/* We got a valid index that no other thread could have set. Store
* a reference to the skb and the start time to allow discarding old
* requests.
*/
set_bit(idx, tx->in_use);
clear_bit(idx, tx->stale);
tx->tstamps[idx].start = jiffies;
tx->tstamps[idx].skb = skb_get(skb);
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
ice_trace(tx_tstamp_request, skb, idx);
}
spin_unlock_irqrestore(&tx->lock, flags);
/* return the appropriate PHY timestamp register index, -1 if no
* indexes were available.
*/
if (idx >= tx->len)
return -1;
else
return idx + tx->offset;
}
/**
* ice_ptp_process_ts - Process the PTP Tx timestamps
* @pf: Board private structure
*
* Returns: ICE_TX_TSTAMP_WORK_PENDING if there are any outstanding Tx
* timestamps that need processing, and ICE_TX_TSTAMP_WORK_DONE otherwise.
*/
enum ice_tx_tstamp_work ice_ptp_process_ts(struct ice_pf *pf)
{
switch (pf->ptp.tx_interrupt_mode) {
case ICE_PTP_TX_INTERRUPT_NONE:
/* This device has the clock owner handle timestamps for it */
return ICE_TX_TSTAMP_WORK_DONE;
case ICE_PTP_TX_INTERRUPT_SELF:
/* This device handles its own timestamps */
return ice_ptp_tx_tstamp(&pf->ptp.port.tx);
case ICE_PTP_TX_INTERRUPT_ALL:
/* This device handles timestamps for all ports */
return ice_ptp_tx_tstamp_owner(pf);
default:
WARN_ONCE(1, "Unexpected Tx timestamp interrupt mode %u\n",
pf->ptp.tx_interrupt_mode);
return ICE_TX_TSTAMP_WORK_DONE;
}
}
/**
* ice_ptp_maybe_trigger_tx_interrupt - Trigger Tx timstamp interrupt
* @pf: Board private structure
*
* The device PHY issues Tx timestamp interrupts to the driver for processing
* timestamp data from the PHY. It will not interrupt again until all
* current timestamp data is read. In rare circumstances, it is possible that
* the driver fails to read all outstanding data.
*
* To avoid getting permanently stuck, periodically check if the PHY has
* outstanding timestamp data. If so, trigger an interrupt from software to
* process this data.
*/
static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf)
{
struct device *dev = ice_pf_to_dev(pf);
struct ice_hw *hw = &pf->hw;
bool trigger_oicr = false;
unsigned int i;
if (ice_is_e810(hw))
return;
if (!ice_pf_src_tmr_owned(pf))
return;
for (i = 0; i < ICE_GET_QUAD_NUM(hw->ptp.num_lports); i++) {
u64 tstamp_ready;
int err;
err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready);
if (!err && tstamp_ready) {
trigger_oicr = true;
break;
}
}
if (trigger_oicr) {
/* Trigger a software interrupt, to ensure this data
* gets processed.
*/
dev_dbg(dev, "PTP periodic task detected waiting timestamps. Triggering Tx timestamp interrupt now.\n");
wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);
ice_flush(hw);
}
}
static void ice_ptp_periodic_work(struct kthread_work *work)
{
struct ice_ptp *ptp = container_of(work, struct ice_ptp, work.work);
struct ice_pf *pf = container_of(ptp, struct ice_pf, ptp);
int err;
if (pf->ptp.state != ICE_PTP_READY)
return;
err = ice_ptp_update_cached_phctime(pf);
ice_ptp_maybe_trigger_tx_interrupt(pf);
/* Run twice a second or reschedule if phc update failed */
kthread_queue_delayed_work(ptp->kworker, &ptp->work,
msecs_to_jiffies(err ? 10 : 500));
}
/**
* ice_ptp_prepare_for_reset - Prepare PTP for reset
* @pf: Board private structure
* @reset_type: the reset type being performed
*/
void ice_ptp_prepare_for_reset(struct ice_pf *pf, enum ice_reset_req reset_type)
{
struct ice_ptp *ptp = &pf->ptp;
u8 src_tmr;
if (ptp->state != ICE_PTP_READY)
return;
ptp->state = ICE_PTP_RESETTING;
/* Disable timestamping for both Tx and Rx */
ice_ptp_disable_timestamp_mode(pf);
kthread_cancel_delayed_work_sync(&ptp->work);
if (reset_type == ICE_RESET_PFR)
return;
ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx);
/* Disable periodic outputs */
ice_ptp_disable_all_clkout(pf);
src_tmr = ice_get_ptp_src_clock_index(&pf->hw);
/* Disable source clock */
wr32(&pf->hw, GLTSYN_ENA(src_tmr), (u32)~GLTSYN_ENA_TSYN_ENA_M);
/* Acquire PHC and system timer to restore after reset */
ptp->reset_time = ktime_get_real_ns();
}
/**
* ice_ptp_rebuild_owner - Initialize PTP clock owner after reset
* @pf: Board private structure
*
* Companion function for ice_ptp_rebuild() which handles tasks that only the
* PTP clock owner instance should perform.
*/
static int ice_ptp_rebuild_owner(struct ice_pf *pf)
{
struct ice_ptp *ptp = &pf->ptp;
struct ice_hw *hw = &pf->hw;
struct timespec64 ts;
u64 time_diff;
int err;
err = ice_ptp_init_phc(hw);
if (err)
return err;
/* Acquire the global hardware lock */
if (!ice_ptp_lock(hw)) {
err = -EBUSY;
return err;
}
/* Write the increment time value to PHY and LAN */
err = ice_ptp_write_incval(hw, ice_base_incval(pf));
if (err) {
ice_ptp_unlock(hw);
return err;
}
/* Write the initial Time value to PHY and LAN using the cached PHC
* time before the reset and time difference between stopping and
* starting the clock.
*/
if (ptp->cached_phc_time) {
time_diff = ktime_get_real_ns() - ptp->reset_time;
ts = ns_to_timespec64(ptp->cached_phc_time + time_diff);
} else {
ts = ktime_to_timespec64(ktime_get_real());
}
err = ice_ptp_write_init(pf, &ts);
if (err) {
ice_ptp_unlock(hw);
return err;
}
/* Release the global hardware lock */
ice_ptp_unlock(hw);
/* Flush software tracking of any outstanding timestamps since we're
* about to flush the PHY timestamp block.
*/
ice_ptp_flush_all_tx_tracker(pf);
if (!ice_is_e810(hw)) {
/* Enable quad interrupts */
err = ice_ptp_cfg_phy_interrupt(pf, true, 1);
if (err)
return err;
ice_ptp_restart_all_phy(pf);
}
/* Re-enable all periodic outputs and external timestamp events */
ice_ptp_enable_all_clkout(pf);
ice_ptp_enable_all_extts(pf);
return 0;
}
/**
* ice_ptp_rebuild - Initialize PTP hardware clock support after reset
* @pf: Board private structure
* @reset_type: the reset type being performed
*/
void ice_ptp_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type)
{
struct ice_ptp *ptp = &pf->ptp;
int err;
if (ptp->state == ICE_PTP_READY) {
ice_ptp_prepare_for_reset(pf, reset_type);
} else if (ptp->state != ICE_PTP_RESETTING) {
err = -EINVAL;
dev_err(ice_pf_to_dev(pf), "PTP was not initialized\n");
goto err;
}
if (ice_pf_src_tmr_owned(pf) && reset_type != ICE_RESET_PFR) {
err = ice_ptp_rebuild_owner(pf);
if (err)
goto err;
}
ptp->state = ICE_PTP_READY;
/* Start periodic work going */
kthread_queue_delayed_work(ptp->kworker, &ptp->work, 0);
dev_info(ice_pf_to_dev(pf), "PTP reset successful\n");
return;
err:
ptp->state = ICE_PTP_ERROR;
dev_err(ice_pf_to_dev(pf), "PTP reset failed %d\n", err);
}
/**
* ice_ptp_aux_dev_to_aux_pf - Get auxiliary PF handle for the auxiliary device
* @aux_dev: auxiliary device to get the auxiliary PF for
*/
static struct ice_pf *
ice_ptp_aux_dev_to_aux_pf(struct auxiliary_device *aux_dev)
{
struct ice_ptp_port *aux_port;
struct ice_ptp *aux_ptp;
aux_port = container_of(aux_dev, struct ice_ptp_port, aux_dev);
aux_ptp = container_of(aux_port, struct ice_ptp, port);
return container_of(aux_ptp, struct ice_pf, ptp);
}
/**
* ice_ptp_aux_dev_to_owner_pf - Get PF handle for the auxiliary device
* @aux_dev: auxiliary device to get the PF for
*/
static struct ice_pf *
ice_ptp_aux_dev_to_owner_pf(struct auxiliary_device *aux_dev)
{
struct ice_ptp_port_owner *ports_owner;
const struct auxiliary_driver *aux_drv;
struct ice_ptp *owner_ptp;
if (!aux_dev->dev.driver)
return NULL;
aux_drv = to_auxiliary_drv(aux_dev->dev.driver);
ports_owner = container_of(aux_drv, struct ice_ptp_port_owner,
aux_driver);
owner_ptp = container_of(ports_owner, struct ice_ptp, ports_owner);
return container_of(owner_ptp, struct ice_pf, ptp);
}
/**
* ice_ptp_auxbus_probe - Probe auxiliary devices
* @aux_dev: PF's auxiliary device
* @id: Auxiliary device ID
*/
static int ice_ptp_auxbus_probe(struct auxiliary_device *aux_dev,
const struct auxiliary_device_id *id)
{
struct ice_pf *owner_pf = ice_ptp_aux_dev_to_owner_pf(aux_dev);
struct ice_pf *aux_pf = ice_ptp_aux_dev_to_aux_pf(aux_dev);
if (WARN_ON(!owner_pf))
return -ENODEV;
INIT_LIST_HEAD(&aux_pf->ptp.port.list_member);
mutex_lock(&owner_pf->ptp.ports_owner.lock);
list_add(&aux_pf->ptp.port.list_member,
&owner_pf->ptp.ports_owner.ports);
mutex_unlock(&owner_pf->ptp.ports_owner.lock);
return 0;
}
/**
* ice_ptp_auxbus_remove - Remove auxiliary devices from the bus
* @aux_dev: PF's auxiliary device
*/
static void ice_ptp_auxbus_remove(struct auxiliary_device *aux_dev)
{
struct ice_pf *owner_pf = ice_ptp_aux_dev_to_owner_pf(aux_dev);
struct ice_pf *aux_pf = ice_ptp_aux_dev_to_aux_pf(aux_dev);
mutex_lock(&owner_pf->ptp.ports_owner.lock);
list_del(&aux_pf->ptp.port.list_member);
mutex_unlock(&owner_pf->ptp.ports_owner.lock);
}
/**
* ice_ptp_auxbus_shutdown
* @aux_dev: PF's auxiliary device
*/
static void ice_ptp_auxbus_shutdown(struct auxiliary_device *aux_dev)
{
/* Doing nothing here, but handle to auxbus driver must be satisfied */
}
/**
* ice_ptp_auxbus_suspend
* @aux_dev: PF's auxiliary device
* @state: power management state indicator
*/
static int
ice_ptp_auxbus_suspend(struct auxiliary_device *aux_dev, pm_message_t state)
{
/* Doing nothing here, but handle to auxbus driver must be satisfied */
return 0;
}
/**
* ice_ptp_auxbus_resume
* @aux_dev: PF's auxiliary device
*/
static int ice_ptp_auxbus_resume(struct auxiliary_device *aux_dev)
{
/* Doing nothing here, but handle to auxbus driver must be satisfied */
return 0;
}
/**
* ice_ptp_auxbus_create_id_table - Create auxiliary device ID table
* @pf: Board private structure
* @name: auxiliary bus driver name
*/
static struct auxiliary_device_id *
ice_ptp_auxbus_create_id_table(struct ice_pf *pf, const char *name)
{
struct auxiliary_device_id *ids;
/* Second id left empty to terminate the array */
ids = devm_kcalloc(ice_pf_to_dev(pf), 2,
sizeof(struct auxiliary_device_id), GFP_KERNEL);
if (!ids)
return NULL;
snprintf(ids[0].name, sizeof(ids[0].name), "ice.%s", name);
return ids;
}
/**
* ice_ptp_register_auxbus_driver - Register PTP auxiliary bus driver
* @pf: Board private structure
*/
static int ice_ptp_register_auxbus_driver(struct ice_pf *pf)
{
struct auxiliary_driver *aux_driver;
struct ice_ptp *ptp;
struct device *dev;
char *name;
int err;
ptp = &pf->ptp;
dev = ice_pf_to_dev(pf);
aux_driver = &ptp->ports_owner.aux_driver;
INIT_LIST_HEAD(&ptp->ports_owner.ports);
mutex_init(&ptp->ports_owner.lock);
name = devm_kasprintf(dev, GFP_KERNEL, "ptp_aux_dev_%u_%u_clk%u",
pf->pdev->bus->number, PCI_SLOT(pf->pdev->devfn),
ice_get_ptp_src_clock_index(&pf->hw));
if (!name)
return -ENOMEM;
aux_driver->name = name;
aux_driver->shutdown = ice_ptp_auxbus_shutdown;
aux_driver->suspend = ice_ptp_auxbus_suspend;
aux_driver->remove = ice_ptp_auxbus_remove;
aux_driver->resume = ice_ptp_auxbus_resume;
aux_driver->probe = ice_ptp_auxbus_probe;
aux_driver->id_table = ice_ptp_auxbus_create_id_table(pf, name);
if (!aux_driver->id_table)
return -ENOMEM;
err = auxiliary_driver_register(aux_driver);
if (err) {
devm_kfree(dev, aux_driver->id_table);
dev_err(dev, "Failed registering aux_driver, name <%s>\n",
name);
}
return err;
}
/**
* ice_ptp_unregister_auxbus_driver - Unregister PTP auxiliary bus driver
* @pf: Board private structure
*/
static void ice_ptp_unregister_auxbus_driver(struct ice_pf *pf)
{
struct auxiliary_driver *aux_driver = &pf->ptp.ports_owner.aux_driver;
auxiliary_driver_unregister(aux_driver);
devm_kfree(ice_pf_to_dev(pf), aux_driver->id_table);
mutex_destroy(&pf->ptp.ports_owner.lock);
}
/**
* ice_ptp_clock_index - Get the PTP clock index for this device
* @pf: Board private structure
*
* Returns: the PTP clock index associated with this PF, or -1 if no PTP clock
* is associated.
*/
int ice_ptp_clock_index(struct ice_pf *pf)
{
struct auxiliary_device *aux_dev;
struct ice_pf *owner_pf;
struct ptp_clock *clock;
aux_dev = &pf->ptp.port.aux_dev;
owner_pf = ice_ptp_aux_dev_to_owner_pf(aux_dev);
if (!owner_pf)
return -1;
clock = owner_pf->ptp.clock;
return clock ? ptp_clock_index(clock) : -1;
}
/**
* ice_ptp_init_owner - Initialize PTP_1588_CLOCK device
* @pf: Board private structure
*
* Setup and initialize a PTP clock device that represents the device hardware
* clock. Save the clock index for other functions connected to the same
* hardware resource.
*/
static int ice_ptp_init_owner(struct ice_pf *pf)
{
struct ice_hw *hw = &pf->hw;
struct timespec64 ts;
int err;
err = ice_ptp_init_phc(hw);
if (err) {
dev_err(ice_pf_to_dev(pf), "Failed to initialize PHC, err %d\n",
err);
return err;
}
/* Acquire the global hardware lock */
if (!ice_ptp_lock(hw)) {
err = -EBUSY;
goto err_exit;
}
/* Write the increment time value to PHY and LAN */
err = ice_ptp_write_incval(hw, ice_base_incval(pf));
if (err) {
ice_ptp_unlock(hw);
goto err_exit;
}
ts = ktime_to_timespec64(ktime_get_real());
/* Write the initial Time value to PHY and LAN */
err = ice_ptp_write_init(pf, &ts);
if (err) {
ice_ptp_unlock(hw);
goto err_exit;
}
/* Release the global hardware lock */
ice_ptp_unlock(hw);
/* Configure PHY interrupt settings */
err = ice_ptp_cfg_phy_interrupt(pf, true, 1);
if (err)
goto err_exit;
/* Ensure we have a clock device */
err = ice_ptp_create_clock(pf);
if (err)
goto err_clk;
err = ice_ptp_register_auxbus_driver(pf);
if (err) {
dev_err(ice_pf_to_dev(pf), "Failed to register PTP auxbus driver");
goto err_aux;
}
return 0;
err_aux:
ptp_clock_unregister(pf->ptp.clock);
err_clk:
pf->ptp.clock = NULL;
err_exit:
return err;
}
/**
* ice_ptp_init_work - Initialize PTP work threads
* @pf: Board private structure
* @ptp: PF PTP structure
*/
static int ice_ptp_init_work(struct ice_pf *pf, struct ice_ptp *ptp)
{
struct kthread_worker *kworker;
/* Initialize work functions */
kthread_init_delayed_work(&ptp->work, ice_ptp_periodic_work);
/* Allocate a kworker for handling work required for the ports
* connected to the PTP hardware clock.
*/
kworker = kthread_create_worker(0, "ice-ptp-%s",
dev_name(ice_pf_to_dev(pf)));
if (IS_ERR(kworker))
return PTR_ERR(kworker);
ptp->kworker = kworker;
/* Start periodic work going */
kthread_queue_delayed_work(ptp->kworker, &ptp->work, 0);
return 0;
}
/**
* ice_ptp_init_port - Initialize PTP port structure
* @pf: Board private structure
* @ptp_port: PTP port structure
*/
static int ice_ptp_init_port(struct ice_pf *pf, struct ice_ptp_port *ptp_port)
{
struct ice_hw *hw = &pf->hw;
mutex_init(&ptp_port->ps_lock);
switch (hw->ptp.phy_model) {
case ICE_PHY_ETH56G:
return ice_ptp_init_tx_eth56g(pf, &ptp_port->tx,
ptp_port->port_num);
case ICE_PHY_E810:
return ice_ptp_init_tx_e810(pf, &ptp_port->tx);
case ICE_PHY_E82X:
kthread_init_delayed_work(&ptp_port->ov_work,
ice_ptp_wait_for_offsets);
return ice_ptp_init_tx_e82x(pf, &ptp_port->tx,
ptp_port->port_num);
default:
return -ENODEV;
}
}
/**
* ice_ptp_release_auxbus_device
* @dev: device that utilizes the auxbus
*/
static void ice_ptp_release_auxbus_device(struct device *dev)
{
/* Doing nothing here, but handle to auxbux device must be satisfied */
}
/**
* ice_ptp_create_auxbus_device - Create PTP auxiliary bus device
* @pf: Board private structure
*/
static int ice_ptp_create_auxbus_device(struct ice_pf *pf)
{
struct auxiliary_device *aux_dev;
struct ice_ptp *ptp;
struct device *dev;
char *name;
int err;
u32 id;
ptp = &pf->ptp;
id = ptp->port.port_num;
dev = ice_pf_to_dev(pf);
aux_dev = &ptp->port.aux_dev;
name = devm_kasprintf(dev, GFP_KERNEL, "ptp_aux_dev_%u_%u_clk%u",
pf->pdev->bus->number, PCI_SLOT(pf->pdev->devfn),
ice_get_ptp_src_clock_index(&pf->hw));
if (!name)
return -ENOMEM;
aux_dev->name = name;
aux_dev->id = id;
aux_dev->dev.release = ice_ptp_release_auxbus_device;
aux_dev->dev.parent = dev;
err = auxiliary_device_init(aux_dev);
if (err)
goto aux_err;
err = auxiliary_device_add(aux_dev);
if (err) {
auxiliary_device_uninit(aux_dev);
goto aux_err;
}
return 0;
aux_err:
dev_err(dev, "Failed to create PTP auxiliary bus device <%s>\n", name);
devm_kfree(dev, name);
return err;
}
/**
* ice_ptp_remove_auxbus_device - Remove PTP auxiliary bus device
* @pf: Board private structure
*/
static void ice_ptp_remove_auxbus_device(struct ice_pf *pf)
{
struct auxiliary_device *aux_dev = &pf->ptp.port.aux_dev;
auxiliary_device_delete(aux_dev);
auxiliary_device_uninit(aux_dev);
memset(aux_dev, 0, sizeof(*aux_dev));
}
/**
* ice_ptp_init_tx_interrupt_mode - Initialize device Tx interrupt mode
* @pf: Board private structure
*
* Initialize the Tx timestamp interrupt mode for this device. For most device
* types, each PF processes the interrupt and manages its own timestamps. For
* E822-based devices, only the clock owner processes the timestamps. Other
* PFs disable the interrupt and do not process their own timestamps.
*/
static void ice_ptp_init_tx_interrupt_mode(struct ice_pf *pf)
{
switch (pf->hw.ptp.phy_model) {
case ICE_PHY_E82X:
/* E822 based PHY has the clock owner process the interrupt
* for all ports.
*/
if (ice_pf_src_tmr_owned(pf))
pf->ptp.tx_interrupt_mode = ICE_PTP_TX_INTERRUPT_ALL;
else
pf->ptp.tx_interrupt_mode = ICE_PTP_TX_INTERRUPT_NONE;
break;
default:
/* other PHY types handle their own Tx interrupt */
pf->ptp.tx_interrupt_mode = ICE_PTP_TX_INTERRUPT_SELF;
}
}
/**
* ice_ptp_init - Initialize PTP hardware clock support
* @pf: Board private structure
*
* Set up the device for interacting with the PTP hardware clock for all
* functions, both the function that owns the clock hardware, and the
* functions connected to the clock hardware.
*
* The clock owner will allocate and register a ptp_clock with the
* PTP_1588_CLOCK infrastructure. All functions allocate a kthread and work
* items used for asynchronous work such as Tx timestamps and periodic work.
*/
void ice_ptp_init(struct ice_pf *pf)
{
struct ice_ptp *ptp = &pf->ptp;
struct ice_hw *hw = &pf->hw;
int err;
ptp->state = ICE_PTP_INITIALIZING;
ice_ptp_init_hw(hw);
ice_ptp_init_tx_interrupt_mode(pf);
/* If this function owns the clock hardware, it must allocate and
* configure the PTP clock device to represent it.
*/
if (ice_pf_src_tmr_owned(pf)) {
err = ice_ptp_init_owner(pf);
if (err)
goto err;
}
ptp->port.port_num = hw->pf_id;
if (ice_is_e825c(hw) && hw->ptp.is_2x50g_muxed_topo)
ptp->port.port_num = hw->pf_id * 2;
err = ice_ptp_init_port(pf, &ptp->port);
if (err)
goto err;
/* Start the PHY timestamping block */
ice_ptp_reset_phy_timestamping(pf);
/* Configure initial Tx interrupt settings */
ice_ptp_cfg_tx_interrupt(pf);
err = ice_ptp_create_auxbus_device(pf);
if (err)
goto err;
ptp->state = ICE_PTP_READY;
err = ice_ptp_init_work(pf, ptp);
if (err)
goto err;
dev_info(ice_pf_to_dev(pf), "PTP init successful\n");
return;
err:
/* If we registered a PTP clock, release it */
if (pf->ptp.clock) {
ptp_clock_unregister(ptp->clock);
pf->ptp.clock = NULL;
}
ptp->state = ICE_PTP_ERROR;
dev_err(ice_pf_to_dev(pf), "PTP failed %d\n", err);
}
/**
* ice_ptp_release - Disable the driver/HW support and unregister the clock
* @pf: Board private structure
*
* This function handles the cleanup work required from the initialization by
* clearing out the important information and unregistering the clock
*/
void ice_ptp_release(struct ice_pf *pf)
{
if (pf->ptp.state != ICE_PTP_READY)
return;
pf->ptp.state = ICE_PTP_UNINIT;
/* Disable timestamping for both Tx and Rx */
ice_ptp_disable_timestamp_mode(pf);
ice_ptp_remove_auxbus_device(pf);
ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx);
ice_ptp_disable_all_extts(pf);
kthread_cancel_delayed_work_sync(&pf->ptp.work);
ice_ptp_port_phy_stop(&pf->ptp.port);
mutex_destroy(&pf->ptp.port.ps_lock);
if (pf->ptp.kworker) {
kthread_destroy_worker(pf->ptp.kworker);
pf->ptp.kworker = NULL;
}
if (ice_pf_src_tmr_owned(pf))
ice_ptp_unregister_auxbus_driver(pf);
if (!pf->ptp.clock)
return;
/* Disable periodic outputs */
ice_ptp_disable_all_clkout(pf);
ptp_clock_unregister(pf->ptp.clock);
pf->ptp.clock = NULL;
dev_info(ice_pf_to_dev(pf), "Removed PTP clock\n");
}