162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (C) 2021, Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "ice.h" 562306a36Sopenharmony_ci#include "ice_lib.h" 662306a36Sopenharmony_ci#include "ice_trace.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define E810_OUT_PROP_DELAY_NS 1 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define UNKNOWN_INCVAL_E822 0x100000000ULL 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic const struct ptp_pin_desc ice_pin_desc_e810t[] = { 1362306a36Sopenharmony_ci /* name idx func chan */ 1462306a36Sopenharmony_ci { "GNSS", GNSS, PTP_PF_EXTTS, 0, { 0, } }, 1562306a36Sopenharmony_ci { "SMA1", SMA1, PTP_PF_NONE, 1, { 0, } }, 1662306a36Sopenharmony_ci { "U.FL1", UFL1, PTP_PF_NONE, 1, { 0, } }, 1762306a36Sopenharmony_ci { "SMA2", SMA2, PTP_PF_NONE, 2, { 0, } }, 1862306a36Sopenharmony_ci { "U.FL2", UFL2, PTP_PF_NONE, 2, { 0, } }, 1962306a36Sopenharmony_ci}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/** 2262306a36Sopenharmony_ci * ice_get_sma_config_e810t 2362306a36Sopenharmony_ci * @hw: pointer to the hw struct 2462306a36Sopenharmony_ci * @ptp_pins: pointer to the ptp_pin_desc struture 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Read the configuration of the SMA control logic and put it into the 2762306a36Sopenharmony_ci * ptp_pin_desc structure 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistatic int 3062306a36Sopenharmony_ciice_get_sma_config_e810t(struct ice_hw *hw, struct ptp_pin_desc *ptp_pins) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci u8 data, i; 3362306a36Sopenharmony_ci int status; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* Read initial pin state */ 3662306a36Sopenharmony_ci status = ice_read_sma_ctrl_e810t(hw, &data); 3762306a36Sopenharmony_ci if (status) 3862306a36Sopenharmony_ci return status; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* initialize with defaults */ 4162306a36Sopenharmony_ci for (i = 0; i < NUM_PTP_PINS_E810T; i++) { 4262306a36Sopenharmony_ci snprintf(ptp_pins[i].name, sizeof(ptp_pins[i].name), 4362306a36Sopenharmony_ci "%s", ice_pin_desc_e810t[i].name); 4462306a36Sopenharmony_ci ptp_pins[i].index = ice_pin_desc_e810t[i].index; 4562306a36Sopenharmony_ci ptp_pins[i].func = ice_pin_desc_e810t[i].func; 4662306a36Sopenharmony_ci ptp_pins[i].chan = ice_pin_desc_e810t[i].chan; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* Parse SMA1/UFL1 */ 5062306a36Sopenharmony_ci switch (data & ICE_SMA1_MASK_E810T) { 5162306a36Sopenharmony_ci case ICE_SMA1_MASK_E810T: 5262306a36Sopenharmony_ci default: 5362306a36Sopenharmony_ci ptp_pins[SMA1].func = PTP_PF_NONE; 5462306a36Sopenharmony_ci ptp_pins[UFL1].func = PTP_PF_NONE; 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci case ICE_SMA1_DIR_EN_E810T: 5762306a36Sopenharmony_ci ptp_pins[SMA1].func = PTP_PF_PEROUT; 5862306a36Sopenharmony_ci ptp_pins[UFL1].func = PTP_PF_NONE; 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci case ICE_SMA1_TX_EN_E810T: 6162306a36Sopenharmony_ci ptp_pins[SMA1].func = PTP_PF_EXTTS; 6262306a36Sopenharmony_ci ptp_pins[UFL1].func = PTP_PF_NONE; 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci case 0: 6562306a36Sopenharmony_ci ptp_pins[SMA1].func = PTP_PF_EXTTS; 6662306a36Sopenharmony_ci ptp_pins[UFL1].func = PTP_PF_PEROUT; 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Parse SMA2/UFL2 */ 7162306a36Sopenharmony_ci switch (data & ICE_SMA2_MASK_E810T) { 7262306a36Sopenharmony_ci case ICE_SMA2_MASK_E810T: 7362306a36Sopenharmony_ci default: 7462306a36Sopenharmony_ci ptp_pins[SMA2].func = PTP_PF_NONE; 7562306a36Sopenharmony_ci ptp_pins[UFL2].func = PTP_PF_NONE; 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci case (ICE_SMA2_TX_EN_E810T | ICE_SMA2_UFL2_RX_DIS_E810T): 7862306a36Sopenharmony_ci ptp_pins[SMA2].func = PTP_PF_EXTTS; 7962306a36Sopenharmony_ci ptp_pins[UFL2].func = PTP_PF_NONE; 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case (ICE_SMA2_DIR_EN_E810T | ICE_SMA2_UFL2_RX_DIS_E810T): 8262306a36Sopenharmony_ci ptp_pins[SMA2].func = PTP_PF_PEROUT; 8362306a36Sopenharmony_ci ptp_pins[UFL2].func = PTP_PF_NONE; 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci case (ICE_SMA2_DIR_EN_E810T | ICE_SMA2_TX_EN_E810T): 8662306a36Sopenharmony_ci ptp_pins[SMA2].func = PTP_PF_NONE; 8762306a36Sopenharmony_ci ptp_pins[UFL2].func = PTP_PF_EXTTS; 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci case ICE_SMA2_DIR_EN_E810T: 9062306a36Sopenharmony_ci ptp_pins[SMA2].func = PTP_PF_PEROUT; 9162306a36Sopenharmony_ci ptp_pins[UFL2].func = PTP_PF_EXTTS; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/** 9962306a36Sopenharmony_ci * ice_ptp_set_sma_config_e810t 10062306a36Sopenharmony_ci * @hw: pointer to the hw struct 10162306a36Sopenharmony_ci * @ptp_pins: pointer to the ptp_pin_desc struture 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Set the configuration of the SMA control logic based on the configuration in 10462306a36Sopenharmony_ci * num_pins parameter 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistatic int 10762306a36Sopenharmony_ciice_ptp_set_sma_config_e810t(struct ice_hw *hw, 10862306a36Sopenharmony_ci const struct ptp_pin_desc *ptp_pins) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int status; 11162306a36Sopenharmony_ci u8 data; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* SMA1 and UFL1 cannot be set to TX at the same time */ 11462306a36Sopenharmony_ci if (ptp_pins[SMA1].func == PTP_PF_PEROUT && 11562306a36Sopenharmony_ci ptp_pins[UFL1].func == PTP_PF_PEROUT) 11662306a36Sopenharmony_ci return -EINVAL; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* SMA2 and UFL2 cannot be set to RX at the same time */ 11962306a36Sopenharmony_ci if (ptp_pins[SMA2].func == PTP_PF_EXTTS && 12062306a36Sopenharmony_ci ptp_pins[UFL2].func == PTP_PF_EXTTS) 12162306a36Sopenharmony_ci return -EINVAL; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Read initial pin state value */ 12462306a36Sopenharmony_ci status = ice_read_sma_ctrl_e810t(hw, &data); 12562306a36Sopenharmony_ci if (status) 12662306a36Sopenharmony_ci return status; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Set the right sate based on the desired configuration */ 12962306a36Sopenharmony_ci data &= ~ICE_SMA1_MASK_E810T; 13062306a36Sopenharmony_ci if (ptp_pins[SMA1].func == PTP_PF_NONE && 13162306a36Sopenharmony_ci ptp_pins[UFL1].func == PTP_PF_NONE) { 13262306a36Sopenharmony_ci dev_info(ice_hw_to_dev(hw), "SMA1 + U.FL1 disabled"); 13362306a36Sopenharmony_ci data |= ICE_SMA1_MASK_E810T; 13462306a36Sopenharmony_ci } else if (ptp_pins[SMA1].func == PTP_PF_EXTTS && 13562306a36Sopenharmony_ci ptp_pins[UFL1].func == PTP_PF_NONE) { 13662306a36Sopenharmony_ci dev_info(ice_hw_to_dev(hw), "SMA1 RX"); 13762306a36Sopenharmony_ci data |= ICE_SMA1_TX_EN_E810T; 13862306a36Sopenharmony_ci } else if (ptp_pins[SMA1].func == PTP_PF_NONE && 13962306a36Sopenharmony_ci ptp_pins[UFL1].func == PTP_PF_PEROUT) { 14062306a36Sopenharmony_ci /* U.FL 1 TX will always enable SMA 1 RX */ 14162306a36Sopenharmony_ci dev_info(ice_hw_to_dev(hw), "SMA1 RX + U.FL1 TX"); 14262306a36Sopenharmony_ci } else if (ptp_pins[SMA1].func == PTP_PF_EXTTS && 14362306a36Sopenharmony_ci ptp_pins[UFL1].func == PTP_PF_PEROUT) { 14462306a36Sopenharmony_ci dev_info(ice_hw_to_dev(hw), "SMA1 RX + U.FL1 TX"); 14562306a36Sopenharmony_ci } else if (ptp_pins[SMA1].func == PTP_PF_PEROUT && 14662306a36Sopenharmony_ci ptp_pins[UFL1].func == PTP_PF_NONE) { 14762306a36Sopenharmony_ci dev_info(ice_hw_to_dev(hw), "SMA1 TX"); 14862306a36Sopenharmony_ci data |= ICE_SMA1_DIR_EN_E810T; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci data &= ~ICE_SMA2_MASK_E810T; 15262306a36Sopenharmony_ci if (ptp_pins[SMA2].func == PTP_PF_NONE && 15362306a36Sopenharmony_ci ptp_pins[UFL2].func == PTP_PF_NONE) { 15462306a36Sopenharmony_ci dev_info(ice_hw_to_dev(hw), "SMA2 + U.FL2 disabled"); 15562306a36Sopenharmony_ci data |= ICE_SMA2_MASK_E810T; 15662306a36Sopenharmony_ci } else if (ptp_pins[SMA2].func == PTP_PF_EXTTS && 15762306a36Sopenharmony_ci ptp_pins[UFL2].func == PTP_PF_NONE) { 15862306a36Sopenharmony_ci dev_info(ice_hw_to_dev(hw), "SMA2 RX"); 15962306a36Sopenharmony_ci data |= (ICE_SMA2_TX_EN_E810T | 16062306a36Sopenharmony_ci ICE_SMA2_UFL2_RX_DIS_E810T); 16162306a36Sopenharmony_ci } else if (ptp_pins[SMA2].func == PTP_PF_NONE && 16262306a36Sopenharmony_ci ptp_pins[UFL2].func == PTP_PF_EXTTS) { 16362306a36Sopenharmony_ci dev_info(ice_hw_to_dev(hw), "UFL2 RX"); 16462306a36Sopenharmony_ci data |= (ICE_SMA2_DIR_EN_E810T | ICE_SMA2_TX_EN_E810T); 16562306a36Sopenharmony_ci } else if (ptp_pins[SMA2].func == PTP_PF_PEROUT && 16662306a36Sopenharmony_ci ptp_pins[UFL2].func == PTP_PF_NONE) { 16762306a36Sopenharmony_ci dev_info(ice_hw_to_dev(hw), "SMA2 TX"); 16862306a36Sopenharmony_ci data |= (ICE_SMA2_DIR_EN_E810T | 16962306a36Sopenharmony_ci ICE_SMA2_UFL2_RX_DIS_E810T); 17062306a36Sopenharmony_ci } else if (ptp_pins[SMA2].func == PTP_PF_PEROUT && 17162306a36Sopenharmony_ci ptp_pins[UFL2].func == PTP_PF_EXTTS) { 17262306a36Sopenharmony_ci dev_info(ice_hw_to_dev(hw), "SMA2 TX + U.FL2 RX"); 17362306a36Sopenharmony_ci data |= ICE_SMA2_DIR_EN_E810T; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return ice_write_sma_ctrl_e810t(hw, data); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/** 18062306a36Sopenharmony_ci * ice_ptp_set_sma_e810t 18162306a36Sopenharmony_ci * @info: the driver's PTP info structure 18262306a36Sopenharmony_ci * @pin: pin index in kernel structure 18362306a36Sopenharmony_ci * @func: Pin function to be set (PTP_PF_NONE, PTP_PF_EXTTS or PTP_PF_PEROUT) 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * Set the configuration of a single SMA pin 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_cistatic int 18862306a36Sopenharmony_ciice_ptp_set_sma_e810t(struct ptp_clock_info *info, unsigned int pin, 18962306a36Sopenharmony_ci enum ptp_pin_function func) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct ptp_pin_desc ptp_pins[NUM_PTP_PINS_E810T]; 19262306a36Sopenharmony_ci struct ice_pf *pf = ptp_info_to_pf(info); 19362306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 19462306a36Sopenharmony_ci int err; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (pin < SMA1 || func > PTP_PF_PEROUT) 19762306a36Sopenharmony_ci return -EOPNOTSUPP; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci err = ice_get_sma_config_e810t(hw, ptp_pins); 20062306a36Sopenharmony_ci if (err) 20162306a36Sopenharmony_ci return err; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Disable the same function on the other pin sharing the channel */ 20462306a36Sopenharmony_ci if (pin == SMA1 && ptp_pins[UFL1].func == func) 20562306a36Sopenharmony_ci ptp_pins[UFL1].func = PTP_PF_NONE; 20662306a36Sopenharmony_ci if (pin == UFL1 && ptp_pins[SMA1].func == func) 20762306a36Sopenharmony_ci ptp_pins[SMA1].func = PTP_PF_NONE; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (pin == SMA2 && ptp_pins[UFL2].func == func) 21062306a36Sopenharmony_ci ptp_pins[UFL2].func = PTP_PF_NONE; 21162306a36Sopenharmony_ci if (pin == UFL2 && ptp_pins[SMA2].func == func) 21262306a36Sopenharmony_ci ptp_pins[SMA2].func = PTP_PF_NONE; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Set up new pin function in the temp table */ 21562306a36Sopenharmony_ci ptp_pins[pin].func = func; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return ice_ptp_set_sma_config_e810t(hw, ptp_pins); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/** 22162306a36Sopenharmony_ci * ice_verify_pin_e810t 22262306a36Sopenharmony_ci * @info: the driver's PTP info structure 22362306a36Sopenharmony_ci * @pin: Pin index 22462306a36Sopenharmony_ci * @func: Assigned function 22562306a36Sopenharmony_ci * @chan: Assigned channel 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * Verify if pin supports requested pin function. If the Check pins consistency. 22862306a36Sopenharmony_ci * Reconfigure the SMA logic attached to the given pin to enable its 22962306a36Sopenharmony_ci * desired functionality 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_cistatic int 23262306a36Sopenharmony_ciice_verify_pin_e810t(struct ptp_clock_info *info, unsigned int pin, 23362306a36Sopenharmony_ci enum ptp_pin_function func, unsigned int chan) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci /* Don't allow channel reassignment */ 23662306a36Sopenharmony_ci if (chan != ice_pin_desc_e810t[pin].chan) 23762306a36Sopenharmony_ci return -EOPNOTSUPP; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Check if functions are properly assigned */ 24062306a36Sopenharmony_ci switch (func) { 24162306a36Sopenharmony_ci case PTP_PF_NONE: 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci case PTP_PF_EXTTS: 24462306a36Sopenharmony_ci if (pin == UFL1) 24562306a36Sopenharmony_ci return -EOPNOTSUPP; 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci case PTP_PF_PEROUT: 24862306a36Sopenharmony_ci if (pin == UFL2 || pin == GNSS) 24962306a36Sopenharmony_ci return -EOPNOTSUPP; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci case PTP_PF_PHYSYNC: 25262306a36Sopenharmony_ci return -EOPNOTSUPP; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return ice_ptp_set_sma_e810t(info, pin, func); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/** 25962306a36Sopenharmony_ci * ice_set_tx_tstamp - Enable or disable Tx timestamping 26062306a36Sopenharmony_ci * @pf: The PF pointer to search in 26162306a36Sopenharmony_ci * @on: bool value for whether timestamps are enabled or disabled 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic void ice_set_tx_tstamp(struct ice_pf *pf, bool on) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct ice_vsi *vsi; 26662306a36Sopenharmony_ci u32 val; 26762306a36Sopenharmony_ci u16 i; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci vsi = ice_get_main_vsi(pf); 27062306a36Sopenharmony_ci if (!vsi) 27162306a36Sopenharmony_ci return; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Set the timestamp enable flag for all the Tx rings */ 27462306a36Sopenharmony_ci ice_for_each_txq(vsi, i) { 27562306a36Sopenharmony_ci if (!vsi->tx_rings[i]) 27662306a36Sopenharmony_ci continue; 27762306a36Sopenharmony_ci vsi->tx_rings[i]->ptp_tx = on; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Configure the Tx timestamp interrupt */ 28162306a36Sopenharmony_ci val = rd32(&pf->hw, PFINT_OICR_ENA); 28262306a36Sopenharmony_ci if (on) 28362306a36Sopenharmony_ci val |= PFINT_OICR_TSYN_TX_M; 28462306a36Sopenharmony_ci else 28562306a36Sopenharmony_ci val &= ~PFINT_OICR_TSYN_TX_M; 28662306a36Sopenharmony_ci wr32(&pf->hw, PFINT_OICR_ENA, val); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci pf->ptp.tstamp_config.tx_type = on ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/** 29262306a36Sopenharmony_ci * ice_set_rx_tstamp - Enable or disable Rx timestamping 29362306a36Sopenharmony_ci * @pf: The PF pointer to search in 29462306a36Sopenharmony_ci * @on: bool value for whether timestamps are enabled or disabled 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_cistatic void ice_set_rx_tstamp(struct ice_pf *pf, bool on) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct ice_vsi *vsi; 29962306a36Sopenharmony_ci u16 i; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci vsi = ice_get_main_vsi(pf); 30262306a36Sopenharmony_ci if (!vsi) 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Set the timestamp flag for all the Rx rings */ 30662306a36Sopenharmony_ci ice_for_each_rxq(vsi, i) { 30762306a36Sopenharmony_ci if (!vsi->rx_rings[i]) 30862306a36Sopenharmony_ci continue; 30962306a36Sopenharmony_ci vsi->rx_rings[i]->ptp_rx = on; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci pf->ptp.tstamp_config.rx_filter = on ? HWTSTAMP_FILTER_ALL : 31362306a36Sopenharmony_ci HWTSTAMP_FILTER_NONE; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/** 31762306a36Sopenharmony_ci * ice_ptp_cfg_timestamp - Configure timestamp for init/deinit 31862306a36Sopenharmony_ci * @pf: Board private structure 31962306a36Sopenharmony_ci * @ena: bool value to enable or disable time stamp 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * This function will configure timestamping during PTP initialization 32262306a36Sopenharmony_ci * and deinitialization 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_civoid ice_ptp_cfg_timestamp(struct ice_pf *pf, bool ena) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci ice_set_tx_tstamp(pf, ena); 32762306a36Sopenharmony_ci ice_set_rx_tstamp(pf, ena); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/** 33162306a36Sopenharmony_ci * ice_get_ptp_clock_index - Get the PTP clock index 33262306a36Sopenharmony_ci * @pf: the PF pointer 33362306a36Sopenharmony_ci * 33462306a36Sopenharmony_ci * Determine the clock index of the PTP clock associated with this device. If 33562306a36Sopenharmony_ci * this is the PF controlling the clock, just use the local access to the 33662306a36Sopenharmony_ci * clock device pointer. 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * Otherwise, read from the driver shared parameters to determine the clock 33962306a36Sopenharmony_ci * index value. 34062306a36Sopenharmony_ci * 34162306a36Sopenharmony_ci * Returns: the index of the PTP clock associated with this device, or -1 if 34262306a36Sopenharmony_ci * there is no associated clock. 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ciint ice_get_ptp_clock_index(struct ice_pf *pf) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct device *dev = ice_pf_to_dev(pf); 34762306a36Sopenharmony_ci enum ice_aqc_driver_params param_idx; 34862306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 34962306a36Sopenharmony_ci u8 tmr_idx; 35062306a36Sopenharmony_ci u32 value; 35162306a36Sopenharmony_ci int err; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* Use the ptp_clock structure if we're the main PF */ 35462306a36Sopenharmony_ci if (pf->ptp.clock) 35562306a36Sopenharmony_ci return ptp_clock_index(pf->ptp.clock); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc; 35862306a36Sopenharmony_ci if (!tmr_idx) 35962306a36Sopenharmony_ci param_idx = ICE_AQC_DRIVER_PARAM_CLK_IDX_TMR0; 36062306a36Sopenharmony_ci else 36162306a36Sopenharmony_ci param_idx = ICE_AQC_DRIVER_PARAM_CLK_IDX_TMR1; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci err = ice_aq_get_driver_param(hw, param_idx, &value, NULL); 36462306a36Sopenharmony_ci if (err) { 36562306a36Sopenharmony_ci dev_err(dev, "Failed to read PTP clock index parameter, err %d aq_err %s\n", 36662306a36Sopenharmony_ci err, ice_aq_str(hw->adminq.sq_last_status)); 36762306a36Sopenharmony_ci return -1; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* The PTP clock index is an integer, and will be between 0 and 37162306a36Sopenharmony_ci * INT_MAX. The highest bit of the driver shared parameter is used to 37262306a36Sopenharmony_ci * indicate whether or not the currently stored clock index is valid. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci if (!(value & PTP_SHARED_CLK_IDX_VALID)) 37562306a36Sopenharmony_ci return -1; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return value & ~PTP_SHARED_CLK_IDX_VALID; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/** 38162306a36Sopenharmony_ci * ice_set_ptp_clock_index - Set the PTP clock index 38262306a36Sopenharmony_ci * @pf: the PF pointer 38362306a36Sopenharmony_ci * 38462306a36Sopenharmony_ci * Set the PTP clock index for this device into the shared driver parameters, 38562306a36Sopenharmony_ci * so that other PFs associated with this device can read it. 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * If the PF is unable to store the clock index, it will log an error, but 38862306a36Sopenharmony_ci * will continue operating PTP. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_cistatic void ice_set_ptp_clock_index(struct ice_pf *pf) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct device *dev = ice_pf_to_dev(pf); 39362306a36Sopenharmony_ci enum ice_aqc_driver_params param_idx; 39462306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 39562306a36Sopenharmony_ci u8 tmr_idx; 39662306a36Sopenharmony_ci u32 value; 39762306a36Sopenharmony_ci int err; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (!pf->ptp.clock) 40062306a36Sopenharmony_ci return; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc; 40362306a36Sopenharmony_ci if (!tmr_idx) 40462306a36Sopenharmony_ci param_idx = ICE_AQC_DRIVER_PARAM_CLK_IDX_TMR0; 40562306a36Sopenharmony_ci else 40662306a36Sopenharmony_ci param_idx = ICE_AQC_DRIVER_PARAM_CLK_IDX_TMR1; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci value = (u32)ptp_clock_index(pf->ptp.clock); 40962306a36Sopenharmony_ci if (value > INT_MAX) { 41062306a36Sopenharmony_ci dev_err(dev, "PTP Clock index is too large to store\n"); 41162306a36Sopenharmony_ci return; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci value |= PTP_SHARED_CLK_IDX_VALID; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci err = ice_aq_set_driver_param(hw, param_idx, value, NULL); 41662306a36Sopenharmony_ci if (err) { 41762306a36Sopenharmony_ci dev_err(dev, "Failed to set PTP clock index parameter, err %d aq_err %s\n", 41862306a36Sopenharmony_ci err, ice_aq_str(hw->adminq.sq_last_status)); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/** 42362306a36Sopenharmony_ci * ice_clear_ptp_clock_index - Clear the PTP clock index 42462306a36Sopenharmony_ci * @pf: the PF pointer 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * Clear the PTP clock index for this device. Must be called when 42762306a36Sopenharmony_ci * unregistering the PTP clock, in order to ensure other PFs stop reporting 42862306a36Sopenharmony_ci * a clock object that no longer exists. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_cistatic void ice_clear_ptp_clock_index(struct ice_pf *pf) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct device *dev = ice_pf_to_dev(pf); 43362306a36Sopenharmony_ci enum ice_aqc_driver_params param_idx; 43462306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 43562306a36Sopenharmony_ci u8 tmr_idx; 43662306a36Sopenharmony_ci int err; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Do not clear the index if we don't own the timer */ 43962306a36Sopenharmony_ci if (!hw->func_caps.ts_func_info.src_tmr_owned) 44062306a36Sopenharmony_ci return; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc; 44362306a36Sopenharmony_ci if (!tmr_idx) 44462306a36Sopenharmony_ci param_idx = ICE_AQC_DRIVER_PARAM_CLK_IDX_TMR0; 44562306a36Sopenharmony_ci else 44662306a36Sopenharmony_ci param_idx = ICE_AQC_DRIVER_PARAM_CLK_IDX_TMR1; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci err = ice_aq_set_driver_param(hw, param_idx, 0, NULL); 44962306a36Sopenharmony_ci if (err) { 45062306a36Sopenharmony_ci dev_dbg(dev, "Failed to clear PTP clock index parameter, err %d aq_err %s\n", 45162306a36Sopenharmony_ci err, ice_aq_str(hw->adminq.sq_last_status)); 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/** 45662306a36Sopenharmony_ci * ice_ptp_read_src_clk_reg - Read the source clock register 45762306a36Sopenharmony_ci * @pf: Board private structure 45862306a36Sopenharmony_ci * @sts: Optional parameter for holding a pair of system timestamps from 45962306a36Sopenharmony_ci * the system clock. Will be ignored if NULL is given. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_cistatic u64 46262306a36Sopenharmony_ciice_ptp_read_src_clk_reg(struct ice_pf *pf, struct ptp_system_timestamp *sts) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 46562306a36Sopenharmony_ci u32 hi, lo, lo2; 46662306a36Sopenharmony_ci u8 tmr_idx; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci tmr_idx = ice_get_ptp_src_clock_index(hw); 46962306a36Sopenharmony_ci /* Read the system timestamp pre PHC read */ 47062306a36Sopenharmony_ci ptp_read_system_prets(sts); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci lo = rd32(hw, GLTSYN_TIME_L(tmr_idx)); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* Read the system timestamp post PHC read */ 47562306a36Sopenharmony_ci ptp_read_system_postts(sts); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci hi = rd32(hw, GLTSYN_TIME_H(tmr_idx)); 47862306a36Sopenharmony_ci lo2 = rd32(hw, GLTSYN_TIME_L(tmr_idx)); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (lo2 < lo) { 48162306a36Sopenharmony_ci /* if TIME_L rolled over read TIME_L again and update 48262306a36Sopenharmony_ci * system timestamps 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci ptp_read_system_prets(sts); 48562306a36Sopenharmony_ci lo = rd32(hw, GLTSYN_TIME_L(tmr_idx)); 48662306a36Sopenharmony_ci ptp_read_system_postts(sts); 48762306a36Sopenharmony_ci hi = rd32(hw, GLTSYN_TIME_H(tmr_idx)); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return ((u64)hi << 32) | lo; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/** 49462306a36Sopenharmony_ci * ice_ptp_extend_32b_ts - Convert a 32b nanoseconds timestamp to 64b 49562306a36Sopenharmony_ci * @cached_phc_time: recently cached copy of PHC time 49662306a36Sopenharmony_ci * @in_tstamp: Ingress/egress 32b nanoseconds timestamp value 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * Hardware captures timestamps which contain only 32 bits of nominal 49962306a36Sopenharmony_ci * nanoseconds, as opposed to the 64bit timestamps that the stack expects. 50062306a36Sopenharmony_ci * Note that the captured timestamp values may be 40 bits, but the lower 50162306a36Sopenharmony_ci * 8 bits are sub-nanoseconds and generally discarded. 50262306a36Sopenharmony_ci * 50362306a36Sopenharmony_ci * Extend the 32bit nanosecond timestamp using the following algorithm and 50462306a36Sopenharmony_ci * assumptions: 50562306a36Sopenharmony_ci * 50662306a36Sopenharmony_ci * 1) have a recently cached copy of the PHC time 50762306a36Sopenharmony_ci * 2) assume that the in_tstamp was captured 2^31 nanoseconds (~2.1 50862306a36Sopenharmony_ci * seconds) before or after the PHC time was captured. 50962306a36Sopenharmony_ci * 3) calculate the delta between the cached time and the timestamp 51062306a36Sopenharmony_ci * 4) if the delta is smaller than 2^31 nanoseconds, then the timestamp was 51162306a36Sopenharmony_ci * captured after the PHC time. In this case, the full timestamp is just 51262306a36Sopenharmony_ci * the cached PHC time plus the delta. 51362306a36Sopenharmony_ci * 5) otherwise, if the delta is larger than 2^31 nanoseconds, then the 51462306a36Sopenharmony_ci * timestamp was captured *before* the PHC time, i.e. because the PHC 51562306a36Sopenharmony_ci * cache was updated after the timestamp was captured by hardware. In this 51662306a36Sopenharmony_ci * case, the full timestamp is the cached time minus the inverse delta. 51762306a36Sopenharmony_ci * 51862306a36Sopenharmony_ci * This algorithm works even if the PHC time was updated after a Tx timestamp 51962306a36Sopenharmony_ci * was requested, but before the Tx timestamp event was reported from 52062306a36Sopenharmony_ci * hardware. 52162306a36Sopenharmony_ci * 52262306a36Sopenharmony_ci * This calculation primarily relies on keeping the cached PHC time up to 52362306a36Sopenharmony_ci * date. If the timestamp was captured more than 2^31 nanoseconds after the 52462306a36Sopenharmony_ci * PHC time, it is possible that the lower 32bits of PHC time have 52562306a36Sopenharmony_ci * overflowed more than once, and we might generate an incorrect timestamp. 52662306a36Sopenharmony_ci * 52762306a36Sopenharmony_ci * This is prevented by (a) periodically updating the cached PHC time once 52862306a36Sopenharmony_ci * a second, and (b) discarding any Tx timestamp packet if it has waited for 52962306a36Sopenharmony_ci * a timestamp for more than one second. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic u64 ice_ptp_extend_32b_ts(u64 cached_phc_time, u32 in_tstamp) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci u32 delta, phc_time_lo; 53462306a36Sopenharmony_ci u64 ns; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Extract the lower 32 bits of the PHC time */ 53762306a36Sopenharmony_ci phc_time_lo = (u32)cached_phc_time; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* Calculate the delta between the lower 32bits of the cached PHC 54062306a36Sopenharmony_ci * time and the in_tstamp value 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci delta = (in_tstamp - phc_time_lo); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* Do not assume that the in_tstamp is always more recent than the 54562306a36Sopenharmony_ci * cached PHC time. If the delta is large, it indicates that the 54662306a36Sopenharmony_ci * in_tstamp was taken in the past, and should be converted 54762306a36Sopenharmony_ci * forward. 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ci if (delta > (U32_MAX / 2)) { 55062306a36Sopenharmony_ci /* reverse the delta calculation here */ 55162306a36Sopenharmony_ci delta = (phc_time_lo - in_tstamp); 55262306a36Sopenharmony_ci ns = cached_phc_time - delta; 55362306a36Sopenharmony_ci } else { 55462306a36Sopenharmony_ci ns = cached_phc_time + delta; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return ns; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/** 56162306a36Sopenharmony_ci * ice_ptp_extend_40b_ts - Convert a 40b timestamp to 64b nanoseconds 56262306a36Sopenharmony_ci * @pf: Board private structure 56362306a36Sopenharmony_ci * @in_tstamp: Ingress/egress 40b timestamp value 56462306a36Sopenharmony_ci * 56562306a36Sopenharmony_ci * The Tx and Rx timestamps are 40 bits wide, including 32 bits of nominal 56662306a36Sopenharmony_ci * nanoseconds, 7 bits of sub-nanoseconds, and a valid bit. 56762306a36Sopenharmony_ci * 56862306a36Sopenharmony_ci * *--------------------------------------------------------------* 56962306a36Sopenharmony_ci * | 32 bits of nanoseconds | 7 high bits of sub ns underflow | v | 57062306a36Sopenharmony_ci * *--------------------------------------------------------------* 57162306a36Sopenharmony_ci * 57262306a36Sopenharmony_ci * The low bit is an indicator of whether the timestamp is valid. The next 57362306a36Sopenharmony_ci * 7 bits are a capture of the upper 7 bits of the sub-nanosecond underflow, 57462306a36Sopenharmony_ci * and the remaining 32 bits are the lower 32 bits of the PHC timer. 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * It is assumed that the caller verifies the timestamp is valid prior to 57762306a36Sopenharmony_ci * calling this function. 57862306a36Sopenharmony_ci * 57962306a36Sopenharmony_ci * Extract the 32bit nominal nanoseconds and extend them. Use the cached PHC 58062306a36Sopenharmony_ci * time stored in the device private PTP structure as the basis for timestamp 58162306a36Sopenharmony_ci * extension. 58262306a36Sopenharmony_ci * 58362306a36Sopenharmony_ci * See ice_ptp_extend_32b_ts for a detailed explanation of the extension 58462306a36Sopenharmony_ci * algorithm. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_cistatic u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci const u64 mask = GENMASK_ULL(31, 0); 58962306a36Sopenharmony_ci unsigned long discard_time; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Discard the hardware timestamp if the cached PHC time is too old */ 59262306a36Sopenharmony_ci discard_time = pf->ptp.cached_phc_jiffies + msecs_to_jiffies(2000); 59362306a36Sopenharmony_ci if (time_is_before_jiffies(discard_time)) { 59462306a36Sopenharmony_ci pf->ptp.tx_hwtstamp_discarded++; 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return ice_ptp_extend_32b_ts(pf->ptp.cached_phc_time, 59962306a36Sopenharmony_ci (in_tstamp >> 8) & mask); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci/** 60362306a36Sopenharmony_ci * ice_ptp_is_tx_tracker_up - Check if Tx tracker is ready for new timestamps 60462306a36Sopenharmony_ci * @tx: the PTP Tx timestamp tracker to check 60562306a36Sopenharmony_ci * 60662306a36Sopenharmony_ci * Check that a given PTP Tx timestamp tracker is up, i.e. that it is ready 60762306a36Sopenharmony_ci * to accept new timestamp requests. 60862306a36Sopenharmony_ci * 60962306a36Sopenharmony_ci * Assumes the tx->lock spinlock is already held. 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_cistatic bool 61262306a36Sopenharmony_ciice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci lockdep_assert_held(&tx->lock); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return tx->init && !tx->calibrating; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci/** 62062306a36Sopenharmony_ci * ice_ptp_process_tx_tstamp - Process Tx timestamps for a port 62162306a36Sopenharmony_ci * @tx: the PTP Tx timestamp tracker 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * Process timestamps captured by the PHY associated with this port. To do 62462306a36Sopenharmony_ci * this, loop over each index with a waiting skb. 62562306a36Sopenharmony_ci * 62662306a36Sopenharmony_ci * If a given index has a valid timestamp, perform the following steps: 62762306a36Sopenharmony_ci * 62862306a36Sopenharmony_ci * 1) check that the timestamp request is not stale 62962306a36Sopenharmony_ci * 2) check that a timestamp is ready and available in the PHY memory bank 63062306a36Sopenharmony_ci * 3) read and copy the timestamp out of the PHY register 63162306a36Sopenharmony_ci * 4) unlock the index by clearing the associated in_use bit 63262306a36Sopenharmony_ci * 5) check if the timestamp is stale, and discard if so 63362306a36Sopenharmony_ci * 6) extend the 40 bit timestamp value to get a 64 bit timestamp value 63462306a36Sopenharmony_ci * 7) send this 64 bit timestamp to the stack 63562306a36Sopenharmony_ci * 63662306a36Sopenharmony_ci * Note that we do not hold the tracking lock while reading the Tx timestamp. 63762306a36Sopenharmony_ci * This is because reading the timestamp requires taking a mutex that might 63862306a36Sopenharmony_ci * sleep. 63962306a36Sopenharmony_ci * 64062306a36Sopenharmony_ci * The only place where we set in_use is when a new timestamp is initiated 64162306a36Sopenharmony_ci * with a slot index. This is only called in the hard xmit routine where an 64262306a36Sopenharmony_ci * SKB has a request flag set. The only places where we clear this bit is this 64362306a36Sopenharmony_ci * function, or during teardown when the Tx timestamp tracker is being 64462306a36Sopenharmony_ci * removed. A timestamp index will never be re-used until the in_use bit for 64562306a36Sopenharmony_ci * that index is cleared. 64662306a36Sopenharmony_ci * 64762306a36Sopenharmony_ci * If a Tx thread starts a new timestamp, we might not begin processing it 64862306a36Sopenharmony_ci * right away but we will notice it at the end when we re-queue the task. 64962306a36Sopenharmony_ci * 65062306a36Sopenharmony_ci * If a Tx thread starts a new timestamp just after this function exits, the 65162306a36Sopenharmony_ci * interrupt for that timestamp should re-trigger this function once 65262306a36Sopenharmony_ci * a timestamp is ready. 65362306a36Sopenharmony_ci * 65462306a36Sopenharmony_ci * In cases where the PTP hardware clock was directly adjusted, some 65562306a36Sopenharmony_ci * timestamps may not be able to safely use the timestamp extension math. In 65662306a36Sopenharmony_ci * this case, software will set the stale bit for any outstanding Tx 65762306a36Sopenharmony_ci * timestamps when the clock is adjusted. Then this function will discard 65862306a36Sopenharmony_ci * those captured timestamps instead of sending them to the stack. 65962306a36Sopenharmony_ci * 66062306a36Sopenharmony_ci * If a Tx packet has been waiting for more than 2 seconds, it is not possible 66162306a36Sopenharmony_ci * to correctly extend the timestamp using the cached PHC time. It is 66262306a36Sopenharmony_ci * extremely unlikely that a packet will ever take this long to timestamp. If 66362306a36Sopenharmony_ci * we detect a Tx timestamp request that has waited for this long we assume 66462306a36Sopenharmony_ci * the packet will never be sent by hardware and discard it without reading 66562306a36Sopenharmony_ci * the timestamp register. 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_cistatic void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct ice_ptp_port *ptp_port; 67062306a36Sopenharmony_ci struct ice_pf *pf; 67162306a36Sopenharmony_ci struct ice_hw *hw; 67262306a36Sopenharmony_ci u64 tstamp_ready; 67362306a36Sopenharmony_ci bool link_up; 67462306a36Sopenharmony_ci int err; 67562306a36Sopenharmony_ci u8 idx; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (!tx->init) 67862306a36Sopenharmony_ci return; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci ptp_port = container_of(tx, struct ice_ptp_port, tx); 68162306a36Sopenharmony_ci pf = ptp_port_to_pf(ptp_port); 68262306a36Sopenharmony_ci hw = &pf->hw; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* Read the Tx ready status first */ 68562306a36Sopenharmony_ci err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready); 68662306a36Sopenharmony_ci if (err) 68762306a36Sopenharmony_ci return; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* Drop packets if the link went down */ 69062306a36Sopenharmony_ci link_up = ptp_port->link_up; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci for_each_set_bit(idx, tx->in_use, tx->len) { 69362306a36Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps = {}; 69462306a36Sopenharmony_ci u8 phy_idx = idx + tx->offset; 69562306a36Sopenharmony_ci u64 raw_tstamp = 0, tstamp; 69662306a36Sopenharmony_ci bool drop_ts = !link_up; 69762306a36Sopenharmony_ci struct sk_buff *skb; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* Drop packets which have waited for more than 2 seconds */ 70062306a36Sopenharmony_ci if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) { 70162306a36Sopenharmony_ci drop_ts = true; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* Count the number of Tx timestamps that timed out */ 70462306a36Sopenharmony_ci pf->ptp.tx_hwtstamp_timeouts++; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* Only read a timestamp from the PHY if its marked as ready 70862306a36Sopenharmony_ci * by the tstamp_ready register. This avoids unnecessary 70962306a36Sopenharmony_ci * reading of timestamps which are not yet valid. This is 71062306a36Sopenharmony_ci * important as we must read all timestamps which are valid 71162306a36Sopenharmony_ci * and only timestamps which are valid during each interrupt. 71262306a36Sopenharmony_ci * If we do not, the hardware logic for generating a new 71362306a36Sopenharmony_ci * interrupt can get stuck on some devices. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci if (!(tstamp_ready & BIT_ULL(phy_idx))) { 71662306a36Sopenharmony_ci if (drop_ts) 71762306a36Sopenharmony_ci goto skip_ts_read; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci continue; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci err = ice_read_phy_tstamp(hw, tx->block, phy_idx, &raw_tstamp); 72562306a36Sopenharmony_ci if (err && !drop_ts) 72662306a36Sopenharmony_ci continue; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* For PHYs which don't implement a proper timestamp ready 73162306a36Sopenharmony_ci * bitmap, verify that the timestamp value is different 73262306a36Sopenharmony_ci * from the last cached timestamp. If it is not, skip this for 73362306a36Sopenharmony_ci * now assuming it hasn't yet been captured by hardware. 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_ci if (!drop_ts && tx->verify_cached && 73662306a36Sopenharmony_ci raw_tstamp == tx->tstamps[idx].cached_tstamp) 73762306a36Sopenharmony_ci continue; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* Discard any timestamp value without the valid bit set */ 74062306a36Sopenharmony_ci if (!(raw_tstamp & ICE_PTP_TS_VALID)) 74162306a36Sopenharmony_ci drop_ts = true; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ciskip_ts_read: 74462306a36Sopenharmony_ci spin_lock(&tx->lock); 74562306a36Sopenharmony_ci if (tx->verify_cached && raw_tstamp) 74662306a36Sopenharmony_ci tx->tstamps[idx].cached_tstamp = raw_tstamp; 74762306a36Sopenharmony_ci clear_bit(idx, tx->in_use); 74862306a36Sopenharmony_ci skb = tx->tstamps[idx].skb; 74962306a36Sopenharmony_ci tx->tstamps[idx].skb = NULL; 75062306a36Sopenharmony_ci if (test_and_clear_bit(idx, tx->stale)) 75162306a36Sopenharmony_ci drop_ts = true; 75262306a36Sopenharmony_ci spin_unlock(&tx->lock); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* It is unlikely but possible that the SKB will have been 75562306a36Sopenharmony_ci * flushed at this point due to link change or teardown. 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci if (!skb) 75862306a36Sopenharmony_ci continue; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (drop_ts) { 76162306a36Sopenharmony_ci dev_kfree_skb_any(skb); 76262306a36Sopenharmony_ci continue; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* Extend the timestamp using cached PHC time */ 76662306a36Sopenharmony_ci tstamp = ice_ptp_extend_40b_ts(pf, raw_tstamp); 76762306a36Sopenharmony_ci if (tstamp) { 76862306a36Sopenharmony_ci shhwtstamps.hwtstamp = ns_to_ktime(tstamp); 76962306a36Sopenharmony_ci ice_trace(tx_tstamp_complete, skb, idx); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci skb_tstamp_tx(skb, &shhwtstamps); 77362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci/** 77862306a36Sopenharmony_ci * ice_ptp_tx_tstamp - Process Tx timestamps for this function. 77962306a36Sopenharmony_ci * @tx: Tx tracking structure to initialize 78062306a36Sopenharmony_ci * 78162306a36Sopenharmony_ci * Returns: ICE_TX_TSTAMP_WORK_PENDING if there are any outstanding incomplete 78262306a36Sopenharmony_ci * Tx timestamps, or ICE_TX_TSTAMP_WORK_DONE otherwise. 78362306a36Sopenharmony_ci */ 78462306a36Sopenharmony_cistatic enum ice_tx_tstamp_work ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci bool more_timestamps; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (!tx->init) 78962306a36Sopenharmony_ci return ICE_TX_TSTAMP_WORK_DONE; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Process the Tx timestamp tracker */ 79262306a36Sopenharmony_ci ice_ptp_process_tx_tstamp(tx); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* Check if there are outstanding Tx timestamps */ 79562306a36Sopenharmony_ci spin_lock(&tx->lock); 79662306a36Sopenharmony_ci more_timestamps = tx->init && !bitmap_empty(tx->in_use, tx->len); 79762306a36Sopenharmony_ci spin_unlock(&tx->lock); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (more_timestamps) 80062306a36Sopenharmony_ci return ICE_TX_TSTAMP_WORK_PENDING; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return ICE_TX_TSTAMP_WORK_DONE; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci/** 80662306a36Sopenharmony_ci * ice_ptp_alloc_tx_tracker - Initialize tracking for Tx timestamps 80762306a36Sopenharmony_ci * @tx: Tx tracking structure to initialize 80862306a36Sopenharmony_ci * 80962306a36Sopenharmony_ci * Assumes that the length has already been initialized. Do not call directly, 81062306a36Sopenharmony_ci * use the ice_ptp_init_tx_* instead. 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_cistatic int 81362306a36Sopenharmony_ciice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci unsigned long *in_use, *stale; 81662306a36Sopenharmony_ci struct ice_tx_tstamp *tstamps; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci tstamps = kcalloc(tx->len, sizeof(*tstamps), GFP_KERNEL); 81962306a36Sopenharmony_ci in_use = bitmap_zalloc(tx->len, GFP_KERNEL); 82062306a36Sopenharmony_ci stale = bitmap_zalloc(tx->len, GFP_KERNEL); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (!tstamps || !in_use || !stale) { 82362306a36Sopenharmony_ci kfree(tstamps); 82462306a36Sopenharmony_ci bitmap_free(in_use); 82562306a36Sopenharmony_ci bitmap_free(stale); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci return -ENOMEM; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci tx->tstamps = tstamps; 83162306a36Sopenharmony_ci tx->in_use = in_use; 83262306a36Sopenharmony_ci tx->stale = stale; 83362306a36Sopenharmony_ci tx->init = 1; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci spin_lock_init(&tx->lock); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci/** 84162306a36Sopenharmony_ci * ice_ptp_flush_tx_tracker - Flush any remaining timestamps from the tracker 84262306a36Sopenharmony_ci * @pf: Board private structure 84362306a36Sopenharmony_ci * @tx: the tracker to flush 84462306a36Sopenharmony_ci * 84562306a36Sopenharmony_ci * Called during teardown when a Tx tracker is being removed. 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_cistatic void 84862306a36Sopenharmony_ciice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 85162306a36Sopenharmony_ci u64 tstamp_ready; 85262306a36Sopenharmony_ci int err; 85362306a36Sopenharmony_ci u8 idx; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready); 85662306a36Sopenharmony_ci if (err) { 85762306a36Sopenharmony_ci dev_dbg(ice_pf_to_dev(pf), "Failed to get the Tx tstamp ready bitmap for block %u, err %d\n", 85862306a36Sopenharmony_ci tx->block, err); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* If we fail to read the Tx timestamp ready bitmap just 86162306a36Sopenharmony_ci * skip clearing the PHY timestamps. 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci tstamp_ready = 0; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci for_each_set_bit(idx, tx->in_use, tx->len) { 86762306a36Sopenharmony_ci u8 phy_idx = idx + tx->offset; 86862306a36Sopenharmony_ci struct sk_buff *skb; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci /* In case this timestamp is ready, we need to clear it. */ 87162306a36Sopenharmony_ci if (!hw->reset_ongoing && (tstamp_ready & BIT_ULL(phy_idx))) 87262306a36Sopenharmony_ci ice_clear_phy_tstamp(hw, tx->block, phy_idx); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci spin_lock(&tx->lock); 87562306a36Sopenharmony_ci skb = tx->tstamps[idx].skb; 87662306a36Sopenharmony_ci tx->tstamps[idx].skb = NULL; 87762306a36Sopenharmony_ci clear_bit(idx, tx->in_use); 87862306a36Sopenharmony_ci clear_bit(idx, tx->stale); 87962306a36Sopenharmony_ci spin_unlock(&tx->lock); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* Count the number of Tx timestamps flushed */ 88262306a36Sopenharmony_ci pf->ptp.tx_hwtstamp_flushed++; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* Free the SKB after we've cleared the bit */ 88562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci/** 89062306a36Sopenharmony_ci * ice_ptp_mark_tx_tracker_stale - Mark unfinished timestamps as stale 89162306a36Sopenharmony_ci * @tx: the tracker to mark 89262306a36Sopenharmony_ci * 89362306a36Sopenharmony_ci * Mark currently outstanding Tx timestamps as stale. This prevents sending 89462306a36Sopenharmony_ci * their timestamp value to the stack. This is required to prevent extending 89562306a36Sopenharmony_ci * the 40bit hardware timestamp incorrectly. 89662306a36Sopenharmony_ci * 89762306a36Sopenharmony_ci * This should be called when the PTP clock is modified such as after a set 89862306a36Sopenharmony_ci * time request. 89962306a36Sopenharmony_ci */ 90062306a36Sopenharmony_cistatic void 90162306a36Sopenharmony_ciice_ptp_mark_tx_tracker_stale(struct ice_ptp_tx *tx) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci spin_lock(&tx->lock); 90462306a36Sopenharmony_ci bitmap_or(tx->stale, tx->stale, tx->in_use, tx->len); 90562306a36Sopenharmony_ci spin_unlock(&tx->lock); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci/** 90962306a36Sopenharmony_ci * ice_ptp_release_tx_tracker - Release allocated memory for Tx tracker 91062306a36Sopenharmony_ci * @pf: Board private structure 91162306a36Sopenharmony_ci * @tx: Tx tracking structure to release 91262306a36Sopenharmony_ci * 91362306a36Sopenharmony_ci * Free memory associated with the Tx timestamp tracker. 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_cistatic void 91662306a36Sopenharmony_ciice_ptp_release_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci spin_lock(&tx->lock); 91962306a36Sopenharmony_ci tx->init = 0; 92062306a36Sopenharmony_ci spin_unlock(&tx->lock); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* wait for potentially outstanding interrupt to complete */ 92362306a36Sopenharmony_ci synchronize_irq(pf->oicr_irq.virq); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci ice_ptp_flush_tx_tracker(pf, tx); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci kfree(tx->tstamps); 92862306a36Sopenharmony_ci tx->tstamps = NULL; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci bitmap_free(tx->in_use); 93162306a36Sopenharmony_ci tx->in_use = NULL; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci bitmap_free(tx->stale); 93462306a36Sopenharmony_ci tx->stale = NULL; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci tx->len = 0; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci/** 94062306a36Sopenharmony_ci * ice_ptp_init_tx_e822 - Initialize tracking for Tx timestamps 94162306a36Sopenharmony_ci * @pf: Board private structure 94262306a36Sopenharmony_ci * @tx: the Tx tracking structure to initialize 94362306a36Sopenharmony_ci * @port: the port this structure tracks 94462306a36Sopenharmony_ci * 94562306a36Sopenharmony_ci * Initialize the Tx timestamp tracker for this port. For generic MAC devices, 94662306a36Sopenharmony_ci * the timestamp block is shared for all ports in the same quad. To avoid 94762306a36Sopenharmony_ci * ports using the same timestamp index, logically break the block of 94862306a36Sopenharmony_ci * registers into chunks based on the port number. 94962306a36Sopenharmony_ci */ 95062306a36Sopenharmony_cistatic int 95162306a36Sopenharmony_ciice_ptp_init_tx_e822(struct ice_pf *pf, struct ice_ptp_tx *tx, u8 port) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci tx->block = port / ICE_PORTS_PER_QUAD; 95462306a36Sopenharmony_ci tx->offset = (port % ICE_PORTS_PER_QUAD) * INDEX_PER_PORT_E822; 95562306a36Sopenharmony_ci tx->len = INDEX_PER_PORT_E822; 95662306a36Sopenharmony_ci tx->verify_cached = 0; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci return ice_ptp_alloc_tx_tracker(tx); 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/** 96262306a36Sopenharmony_ci * ice_ptp_init_tx_e810 - Initialize tracking for Tx timestamps 96362306a36Sopenharmony_ci * @pf: Board private structure 96462306a36Sopenharmony_ci * @tx: the Tx tracking structure to initialize 96562306a36Sopenharmony_ci * 96662306a36Sopenharmony_ci * Initialize the Tx timestamp tracker for this PF. For E810 devices, each 96762306a36Sopenharmony_ci * port has its own block of timestamps, independent of the other ports. 96862306a36Sopenharmony_ci */ 96962306a36Sopenharmony_cistatic int 97062306a36Sopenharmony_ciice_ptp_init_tx_e810(struct ice_pf *pf, struct ice_ptp_tx *tx) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci tx->block = pf->hw.port_info->lport; 97362306a36Sopenharmony_ci tx->offset = 0; 97462306a36Sopenharmony_ci tx->len = INDEX_PER_PORT_E810; 97562306a36Sopenharmony_ci /* The E810 PHY does not provide a timestamp ready bitmap. Instead, 97662306a36Sopenharmony_ci * verify new timestamps against cached copy of the last read 97762306a36Sopenharmony_ci * timestamp. 97862306a36Sopenharmony_ci */ 97962306a36Sopenharmony_ci tx->verify_cached = 1; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci return ice_ptp_alloc_tx_tracker(tx); 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci/** 98562306a36Sopenharmony_ci * ice_ptp_update_cached_phctime - Update the cached PHC time values 98662306a36Sopenharmony_ci * @pf: Board specific private structure 98762306a36Sopenharmony_ci * 98862306a36Sopenharmony_ci * This function updates the system time values which are cached in the PF 98962306a36Sopenharmony_ci * structure and the Rx rings. 99062306a36Sopenharmony_ci * 99162306a36Sopenharmony_ci * This function must be called periodically to ensure that the cached value 99262306a36Sopenharmony_ci * is never more than 2 seconds old. 99362306a36Sopenharmony_ci * 99462306a36Sopenharmony_ci * Note that the cached copy in the PF PTP structure is always updated, even 99562306a36Sopenharmony_ci * if we can't update the copy in the Rx rings. 99662306a36Sopenharmony_ci * 99762306a36Sopenharmony_ci * Return: 99862306a36Sopenharmony_ci * * 0 - OK, successfully updated 99962306a36Sopenharmony_ci * * -EAGAIN - PF was busy, need to reschedule the update 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_cistatic int ice_ptp_update_cached_phctime(struct ice_pf *pf) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci struct device *dev = ice_pf_to_dev(pf); 100462306a36Sopenharmony_ci unsigned long update_before; 100562306a36Sopenharmony_ci u64 systime; 100662306a36Sopenharmony_ci int i; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci update_before = pf->ptp.cached_phc_jiffies + msecs_to_jiffies(2000); 100962306a36Sopenharmony_ci if (pf->ptp.cached_phc_time && 101062306a36Sopenharmony_ci time_is_before_jiffies(update_before)) { 101162306a36Sopenharmony_ci unsigned long time_taken = jiffies - pf->ptp.cached_phc_jiffies; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci dev_warn(dev, "%u msecs passed between update to cached PHC time\n", 101462306a36Sopenharmony_ci jiffies_to_msecs(time_taken)); 101562306a36Sopenharmony_ci pf->ptp.late_cached_phc_updates++; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci /* Read the current PHC time */ 101962306a36Sopenharmony_ci systime = ice_ptp_read_src_clk_reg(pf, NULL); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* Update the cached PHC time stored in the PF structure */ 102262306a36Sopenharmony_ci WRITE_ONCE(pf->ptp.cached_phc_time, systime); 102362306a36Sopenharmony_ci WRITE_ONCE(pf->ptp.cached_phc_jiffies, jiffies); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (test_and_set_bit(ICE_CFG_BUSY, pf->state)) 102662306a36Sopenharmony_ci return -EAGAIN; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci ice_for_each_vsi(pf, i) { 102962306a36Sopenharmony_ci struct ice_vsi *vsi = pf->vsi[i]; 103062306a36Sopenharmony_ci int j; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci if (!vsi) 103362306a36Sopenharmony_ci continue; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (vsi->type != ICE_VSI_PF) 103662306a36Sopenharmony_ci continue; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci ice_for_each_rxq(vsi, j) { 103962306a36Sopenharmony_ci if (!vsi->rx_rings[j]) 104062306a36Sopenharmony_ci continue; 104162306a36Sopenharmony_ci WRITE_ONCE(vsi->rx_rings[j]->cached_phctime, systime); 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci clear_bit(ICE_CFG_BUSY, pf->state); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci return 0; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci/** 105062306a36Sopenharmony_ci * ice_ptp_reset_cached_phctime - Reset cached PHC time after an update 105162306a36Sopenharmony_ci * @pf: Board specific private structure 105262306a36Sopenharmony_ci * 105362306a36Sopenharmony_ci * This function must be called when the cached PHC time is no longer valid, 105462306a36Sopenharmony_ci * such as after a time adjustment. It marks any currently outstanding Tx 105562306a36Sopenharmony_ci * timestamps as stale and updates the cached PHC time for both the PF and Rx 105662306a36Sopenharmony_ci * rings. 105762306a36Sopenharmony_ci * 105862306a36Sopenharmony_ci * If updating the PHC time cannot be done immediately, a warning message is 105962306a36Sopenharmony_ci * logged and the work item is scheduled immediately to minimize the window 106062306a36Sopenharmony_ci * with a wrong cached timestamp. 106162306a36Sopenharmony_ci */ 106262306a36Sopenharmony_cistatic void ice_ptp_reset_cached_phctime(struct ice_pf *pf) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci struct device *dev = ice_pf_to_dev(pf); 106562306a36Sopenharmony_ci int err; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* Update the cached PHC time immediately if possible, otherwise 106862306a36Sopenharmony_ci * schedule the work item to execute soon. 106962306a36Sopenharmony_ci */ 107062306a36Sopenharmony_ci err = ice_ptp_update_cached_phctime(pf); 107162306a36Sopenharmony_ci if (err) { 107262306a36Sopenharmony_ci /* If another thread is updating the Rx rings, we won't 107362306a36Sopenharmony_ci * properly reset them here. This could lead to reporting of 107462306a36Sopenharmony_ci * invalid timestamps, but there isn't much we can do. 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_ci dev_warn(dev, "%s: ICE_CFG_BUSY, unable to immediately update cached PHC time\n", 107762306a36Sopenharmony_ci __func__); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci /* Queue the work item to update the Rx rings when possible */ 108062306a36Sopenharmony_ci kthread_queue_delayed_work(pf->ptp.kworker, &pf->ptp.work, 108162306a36Sopenharmony_ci msecs_to_jiffies(10)); 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci /* Mark any outstanding timestamps as stale, since they might have 108562306a36Sopenharmony_ci * been captured in hardware before the time update. This could lead 108662306a36Sopenharmony_ci * to us extending them with the wrong cached value resulting in 108762306a36Sopenharmony_ci * incorrect timestamp values. 108862306a36Sopenharmony_ci */ 108962306a36Sopenharmony_ci ice_ptp_mark_tx_tracker_stale(&pf->ptp.port.tx); 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci/** 109362306a36Sopenharmony_ci * ice_ptp_read_time - Read the time from the device 109462306a36Sopenharmony_ci * @pf: Board private structure 109562306a36Sopenharmony_ci * @ts: timespec structure to hold the current time value 109662306a36Sopenharmony_ci * @sts: Optional parameter for holding a pair of system timestamps from 109762306a36Sopenharmony_ci * the system clock. Will be ignored if NULL is given. 109862306a36Sopenharmony_ci * 109962306a36Sopenharmony_ci * This function reads the source clock registers and stores them in a timespec. 110062306a36Sopenharmony_ci * However, since the registers are 64 bits of nanoseconds, we must convert the 110162306a36Sopenharmony_ci * result to a timespec before we can return. 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_cistatic void 110462306a36Sopenharmony_ciice_ptp_read_time(struct ice_pf *pf, struct timespec64 *ts, 110562306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci u64 time_ns = ice_ptp_read_src_clk_reg(pf, sts); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci *ts = ns_to_timespec64(time_ns); 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci/** 111362306a36Sopenharmony_ci * ice_ptp_write_init - Set PHC time to provided value 111462306a36Sopenharmony_ci * @pf: Board private structure 111562306a36Sopenharmony_ci * @ts: timespec structure that holds the new time value 111662306a36Sopenharmony_ci * 111762306a36Sopenharmony_ci * Set the PHC time to the specified time provided in the timespec. 111862306a36Sopenharmony_ci */ 111962306a36Sopenharmony_cistatic int ice_ptp_write_init(struct ice_pf *pf, struct timespec64 *ts) 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci u64 ns = timespec64_to_ns(ts); 112262306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci return ice_ptp_init_time(hw, ns); 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci/** 112862306a36Sopenharmony_ci * ice_ptp_write_adj - Adjust PHC clock time atomically 112962306a36Sopenharmony_ci * @pf: Board private structure 113062306a36Sopenharmony_ci * @adj: Adjustment in nanoseconds 113162306a36Sopenharmony_ci * 113262306a36Sopenharmony_ci * Perform an atomic adjustment of the PHC time by the specified number of 113362306a36Sopenharmony_ci * nanoseconds. 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_cistatic int ice_ptp_write_adj(struct ice_pf *pf, s32 adj) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci return ice_ptp_adj_clock(hw, adj); 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci/** 114362306a36Sopenharmony_ci * ice_base_incval - Get base timer increment value 114462306a36Sopenharmony_ci * @pf: Board private structure 114562306a36Sopenharmony_ci * 114662306a36Sopenharmony_ci * Look up the base timer increment value for this device. The base increment 114762306a36Sopenharmony_ci * value is used to define the nominal clock tick rate. This increment value 114862306a36Sopenharmony_ci * is programmed during device initialization. It is also used as the basis 114962306a36Sopenharmony_ci * for calculating adjustments using scaled_ppm. 115062306a36Sopenharmony_ci */ 115162306a36Sopenharmony_cistatic u64 ice_base_incval(struct ice_pf *pf) 115262306a36Sopenharmony_ci{ 115362306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 115462306a36Sopenharmony_ci u64 incval; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (ice_is_e810(hw)) 115762306a36Sopenharmony_ci incval = ICE_PTP_NOMINAL_INCVAL_E810; 115862306a36Sopenharmony_ci else if (ice_e822_time_ref(hw) < NUM_ICE_TIME_REF_FREQ) 115962306a36Sopenharmony_ci incval = ice_e822_nominal_incval(ice_e822_time_ref(hw)); 116062306a36Sopenharmony_ci else 116162306a36Sopenharmony_ci incval = UNKNOWN_INCVAL_E822; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci dev_dbg(ice_pf_to_dev(pf), "PTP: using base increment value of 0x%016llx\n", 116462306a36Sopenharmony_ci incval); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci return incval; 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci/** 117062306a36Sopenharmony_ci * ice_ptp_check_tx_fifo - Check whether Tx FIFO is in an OK state 117162306a36Sopenharmony_ci * @port: PTP port for which Tx FIFO is checked 117262306a36Sopenharmony_ci */ 117362306a36Sopenharmony_cistatic int ice_ptp_check_tx_fifo(struct ice_ptp_port *port) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci int quad = port->port_num / ICE_PORTS_PER_QUAD; 117662306a36Sopenharmony_ci int offs = port->port_num % ICE_PORTS_PER_QUAD; 117762306a36Sopenharmony_ci struct ice_pf *pf; 117862306a36Sopenharmony_ci struct ice_hw *hw; 117962306a36Sopenharmony_ci u32 val, phy_sts; 118062306a36Sopenharmony_ci int err; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci pf = ptp_port_to_pf(port); 118362306a36Sopenharmony_ci hw = &pf->hw; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci if (port->tx_fifo_busy_cnt == FIFO_OK) 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci /* need to read FIFO state */ 118962306a36Sopenharmony_ci if (offs == 0 || offs == 1) 119062306a36Sopenharmony_ci err = ice_read_quad_reg_e822(hw, quad, Q_REG_FIFO01_STATUS, 119162306a36Sopenharmony_ci &val); 119262306a36Sopenharmony_ci else 119362306a36Sopenharmony_ci err = ice_read_quad_reg_e822(hw, quad, Q_REG_FIFO23_STATUS, 119462306a36Sopenharmony_ci &val); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (err) { 119762306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP failed to check port %d Tx FIFO, err %d\n", 119862306a36Sopenharmony_ci port->port_num, err); 119962306a36Sopenharmony_ci return err; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if (offs & 0x1) 120362306a36Sopenharmony_ci phy_sts = (val & Q_REG_FIFO13_M) >> Q_REG_FIFO13_S; 120462306a36Sopenharmony_ci else 120562306a36Sopenharmony_ci phy_sts = (val & Q_REG_FIFO02_M) >> Q_REG_FIFO02_S; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (phy_sts & FIFO_EMPTY) { 120862306a36Sopenharmony_ci port->tx_fifo_busy_cnt = FIFO_OK; 120962306a36Sopenharmony_ci return 0; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci port->tx_fifo_busy_cnt++; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci dev_dbg(ice_pf_to_dev(pf), "Try %d, port %d FIFO not empty\n", 121562306a36Sopenharmony_ci port->tx_fifo_busy_cnt, port->port_num); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci if (port->tx_fifo_busy_cnt == ICE_PTP_FIFO_NUM_CHECKS) { 121862306a36Sopenharmony_ci dev_dbg(ice_pf_to_dev(pf), 121962306a36Sopenharmony_ci "Port %d Tx FIFO still not empty; resetting quad %d\n", 122062306a36Sopenharmony_ci port->port_num, quad); 122162306a36Sopenharmony_ci ice_ptp_reset_ts_memory_quad_e822(hw, quad); 122262306a36Sopenharmony_ci port->tx_fifo_busy_cnt = FIFO_OK; 122362306a36Sopenharmony_ci return 0; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci return -EAGAIN; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci/** 123062306a36Sopenharmony_ci * ice_ptp_wait_for_offsets - Check for valid Tx and Rx offsets 123162306a36Sopenharmony_ci * @work: Pointer to the kthread_work structure for this task 123262306a36Sopenharmony_ci * 123362306a36Sopenharmony_ci * Check whether hardware has completed measuring the Tx and Rx offset values 123462306a36Sopenharmony_ci * used to configure and enable vernier timestamp calibration. 123562306a36Sopenharmony_ci * 123662306a36Sopenharmony_ci * Once the offset in either direction is measured, configure the associated 123762306a36Sopenharmony_ci * registers with the calibrated offset values and enable timestamping. The Tx 123862306a36Sopenharmony_ci * and Rx directions are configured independently as soon as their associated 123962306a36Sopenharmony_ci * offsets are known. 124062306a36Sopenharmony_ci * 124162306a36Sopenharmony_ci * This function reschedules itself until both Tx and Rx calibration have 124262306a36Sopenharmony_ci * completed. 124362306a36Sopenharmony_ci */ 124462306a36Sopenharmony_cistatic void ice_ptp_wait_for_offsets(struct kthread_work *work) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct ice_ptp_port *port; 124762306a36Sopenharmony_ci struct ice_pf *pf; 124862306a36Sopenharmony_ci struct ice_hw *hw; 124962306a36Sopenharmony_ci int tx_err; 125062306a36Sopenharmony_ci int rx_err; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci port = container_of(work, struct ice_ptp_port, ov_work.work); 125362306a36Sopenharmony_ci pf = ptp_port_to_pf(port); 125462306a36Sopenharmony_ci hw = &pf->hw; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (ice_is_reset_in_progress(pf->state)) { 125762306a36Sopenharmony_ci /* wait for device driver to complete reset */ 125862306a36Sopenharmony_ci kthread_queue_delayed_work(pf->ptp.kworker, 125962306a36Sopenharmony_ci &port->ov_work, 126062306a36Sopenharmony_ci msecs_to_jiffies(100)); 126162306a36Sopenharmony_ci return; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci tx_err = ice_ptp_check_tx_fifo(port); 126562306a36Sopenharmony_ci if (!tx_err) 126662306a36Sopenharmony_ci tx_err = ice_phy_cfg_tx_offset_e822(hw, port->port_num); 126762306a36Sopenharmony_ci rx_err = ice_phy_cfg_rx_offset_e822(hw, port->port_num); 126862306a36Sopenharmony_ci if (tx_err || rx_err) { 126962306a36Sopenharmony_ci /* Tx and/or Rx offset not yet configured, try again later */ 127062306a36Sopenharmony_ci kthread_queue_delayed_work(pf->ptp.kworker, 127162306a36Sopenharmony_ci &port->ov_work, 127262306a36Sopenharmony_ci msecs_to_jiffies(100)); 127362306a36Sopenharmony_ci return; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci/** 127862306a36Sopenharmony_ci * ice_ptp_port_phy_stop - Stop timestamping for a PHY port 127962306a36Sopenharmony_ci * @ptp_port: PTP port to stop 128062306a36Sopenharmony_ci */ 128162306a36Sopenharmony_cistatic int 128262306a36Sopenharmony_ciice_ptp_port_phy_stop(struct ice_ptp_port *ptp_port) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci struct ice_pf *pf = ptp_port_to_pf(ptp_port); 128562306a36Sopenharmony_ci u8 port = ptp_port->port_num; 128662306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 128762306a36Sopenharmony_ci int err; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (ice_is_e810(hw)) 129062306a36Sopenharmony_ci return 0; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci mutex_lock(&ptp_port->ps_lock); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci kthread_cancel_delayed_work_sync(&ptp_port->ov_work); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci err = ice_stop_phy_timer_e822(hw, port, true); 129762306a36Sopenharmony_ci if (err) 129862306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP failed to set PHY port %d down, err %d\n", 129962306a36Sopenharmony_ci port, err); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci mutex_unlock(&ptp_port->ps_lock); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci return err; 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci/** 130762306a36Sopenharmony_ci * ice_ptp_port_phy_restart - (Re)start and calibrate PHY timestamping 130862306a36Sopenharmony_ci * @ptp_port: PTP port for which the PHY start is set 130962306a36Sopenharmony_ci * 131062306a36Sopenharmony_ci * Start the PHY timestamping block, and initiate Vernier timestamping 131162306a36Sopenharmony_ci * calibration. If timestamping cannot be calibrated (such as if link is down) 131262306a36Sopenharmony_ci * then disable the timestamping block instead. 131362306a36Sopenharmony_ci */ 131462306a36Sopenharmony_cistatic int 131562306a36Sopenharmony_ciice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port) 131662306a36Sopenharmony_ci{ 131762306a36Sopenharmony_ci struct ice_pf *pf = ptp_port_to_pf(ptp_port); 131862306a36Sopenharmony_ci u8 port = ptp_port->port_num; 131962306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 132062306a36Sopenharmony_ci int err; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci if (ice_is_e810(hw)) 132362306a36Sopenharmony_ci return 0; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci if (!ptp_port->link_up) 132662306a36Sopenharmony_ci return ice_ptp_port_phy_stop(ptp_port); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci mutex_lock(&ptp_port->ps_lock); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci kthread_cancel_delayed_work_sync(&ptp_port->ov_work); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci /* temporarily disable Tx timestamps while calibrating PHY offset */ 133362306a36Sopenharmony_ci spin_lock(&ptp_port->tx.lock); 133462306a36Sopenharmony_ci ptp_port->tx.calibrating = true; 133562306a36Sopenharmony_ci spin_unlock(&ptp_port->tx.lock); 133662306a36Sopenharmony_ci ptp_port->tx_fifo_busy_cnt = 0; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci /* Start the PHY timer in Vernier mode */ 133962306a36Sopenharmony_ci err = ice_start_phy_timer_e822(hw, port); 134062306a36Sopenharmony_ci if (err) 134162306a36Sopenharmony_ci goto out_unlock; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci /* Enable Tx timestamps right away */ 134462306a36Sopenharmony_ci spin_lock(&ptp_port->tx.lock); 134562306a36Sopenharmony_ci ptp_port->tx.calibrating = false; 134662306a36Sopenharmony_ci spin_unlock(&ptp_port->tx.lock); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci kthread_queue_delayed_work(pf->ptp.kworker, &ptp_port->ov_work, 0); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ciout_unlock: 135162306a36Sopenharmony_ci if (err) 135262306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP failed to set PHY port %d up, err %d\n", 135362306a36Sopenharmony_ci port, err); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci mutex_unlock(&ptp_port->ps_lock); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci return err; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci/** 136162306a36Sopenharmony_ci * ice_ptp_link_change - Reconfigure PTP after link status change 136262306a36Sopenharmony_ci * @pf: Board private structure 136362306a36Sopenharmony_ci * @port: Port for which the PHY start is set 136462306a36Sopenharmony_ci * @linkup: Link is up or down 136562306a36Sopenharmony_ci */ 136662306a36Sopenharmony_civoid ice_ptp_link_change(struct ice_pf *pf, u8 port, bool linkup) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci struct ice_ptp_port *ptp_port; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci if (!test_bit(ICE_FLAG_PTP, pf->flags)) 137162306a36Sopenharmony_ci return; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci if (WARN_ON_ONCE(port >= ICE_NUM_EXTERNAL_PORTS)) 137462306a36Sopenharmony_ci return; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci ptp_port = &pf->ptp.port; 137762306a36Sopenharmony_ci if (WARN_ON_ONCE(ptp_port->port_num != port)) 137862306a36Sopenharmony_ci return; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci /* Update cached link status for this port immediately */ 138162306a36Sopenharmony_ci ptp_port->link_up = linkup; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci /* E810 devices do not need to reconfigure the PHY */ 138462306a36Sopenharmony_ci if (ice_is_e810(&pf->hw)) 138562306a36Sopenharmony_ci return; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci ice_ptp_port_phy_restart(ptp_port); 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci/** 139162306a36Sopenharmony_ci * ice_ptp_tx_ena_intr - Enable or disable the Tx timestamp interrupt 139262306a36Sopenharmony_ci * @pf: PF private structure 139362306a36Sopenharmony_ci * @ena: bool value to enable or disable interrupt 139462306a36Sopenharmony_ci * @threshold: Minimum number of packets at which intr is triggered 139562306a36Sopenharmony_ci * 139662306a36Sopenharmony_ci * Utility function to enable or disable Tx timestamp interrupt and threshold 139762306a36Sopenharmony_ci */ 139862306a36Sopenharmony_cistatic int ice_ptp_tx_ena_intr(struct ice_pf *pf, bool ena, u32 threshold) 139962306a36Sopenharmony_ci{ 140062306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 140162306a36Sopenharmony_ci int err = 0; 140262306a36Sopenharmony_ci int quad; 140362306a36Sopenharmony_ci u32 val; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci ice_ptp_reset_ts_memory(hw); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci for (quad = 0; quad < ICE_MAX_QUAD; quad++) { 140862306a36Sopenharmony_ci err = ice_read_quad_reg_e822(hw, quad, Q_REG_TX_MEM_GBL_CFG, 140962306a36Sopenharmony_ci &val); 141062306a36Sopenharmony_ci if (err) 141162306a36Sopenharmony_ci break; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci if (ena) { 141462306a36Sopenharmony_ci val |= Q_REG_TX_MEM_GBL_CFG_INTR_ENA_M; 141562306a36Sopenharmony_ci val &= ~Q_REG_TX_MEM_GBL_CFG_INTR_THR_M; 141662306a36Sopenharmony_ci val |= ((threshold << Q_REG_TX_MEM_GBL_CFG_INTR_THR_S) & 141762306a36Sopenharmony_ci Q_REG_TX_MEM_GBL_CFG_INTR_THR_M); 141862306a36Sopenharmony_ci } else { 141962306a36Sopenharmony_ci val &= ~Q_REG_TX_MEM_GBL_CFG_INTR_ENA_M; 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci err = ice_write_quad_reg_e822(hw, quad, Q_REG_TX_MEM_GBL_CFG, 142362306a36Sopenharmony_ci val); 142462306a36Sopenharmony_ci if (err) 142562306a36Sopenharmony_ci break; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci if (err) 142962306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP failed in intr ena, err %d\n", 143062306a36Sopenharmony_ci err); 143162306a36Sopenharmony_ci return err; 143262306a36Sopenharmony_ci} 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci/** 143562306a36Sopenharmony_ci * ice_ptp_reset_phy_timestamping - Reset PHY timestamping block 143662306a36Sopenharmony_ci * @pf: Board private structure 143762306a36Sopenharmony_ci */ 143862306a36Sopenharmony_cistatic void ice_ptp_reset_phy_timestamping(struct ice_pf *pf) 143962306a36Sopenharmony_ci{ 144062306a36Sopenharmony_ci ice_ptp_port_phy_restart(&pf->ptp.port); 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci/** 144462306a36Sopenharmony_ci * ice_ptp_adjfine - Adjust clock increment rate 144562306a36Sopenharmony_ci * @info: the driver's PTP info structure 144662306a36Sopenharmony_ci * @scaled_ppm: Parts per million with 16-bit fractional field 144762306a36Sopenharmony_ci * 144862306a36Sopenharmony_ci * Adjust the frequency of the clock by the indicated scaled ppm from the 144962306a36Sopenharmony_ci * base frequency. 145062306a36Sopenharmony_ci */ 145162306a36Sopenharmony_cistatic int ice_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci struct ice_pf *pf = ptp_info_to_pf(info); 145462306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 145562306a36Sopenharmony_ci u64 incval; 145662306a36Sopenharmony_ci int err; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci incval = adjust_by_scaled_ppm(ice_base_incval(pf), scaled_ppm); 145962306a36Sopenharmony_ci err = ice_ptp_write_incval_locked(hw, incval); 146062306a36Sopenharmony_ci if (err) { 146162306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP failed to set incval, err %d\n", 146262306a36Sopenharmony_ci err); 146362306a36Sopenharmony_ci return -EIO; 146462306a36Sopenharmony_ci } 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci return 0; 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci/** 147062306a36Sopenharmony_ci * ice_ptp_extts_event - Process PTP external clock event 147162306a36Sopenharmony_ci * @pf: Board private structure 147262306a36Sopenharmony_ci */ 147362306a36Sopenharmony_civoid ice_ptp_extts_event(struct ice_pf *pf) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci struct ptp_clock_event event; 147662306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 147762306a36Sopenharmony_ci u8 chan, tmr_idx; 147862306a36Sopenharmony_ci u32 hi, lo; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 148162306a36Sopenharmony_ci /* Event time is captured by one of the two matched registers 148262306a36Sopenharmony_ci * GLTSYN_EVNT_L: 32 LSB of sampled time event 148362306a36Sopenharmony_ci * GLTSYN_EVNT_H: 32 MSB of sampled time event 148462306a36Sopenharmony_ci * Event is defined in GLTSYN_EVNT_0 register 148562306a36Sopenharmony_ci */ 148662306a36Sopenharmony_ci for (chan = 0; chan < GLTSYN_EVNT_H_IDX_MAX; chan++) { 148762306a36Sopenharmony_ci /* Check if channel is enabled */ 148862306a36Sopenharmony_ci if (pf->ptp.ext_ts_irq & (1 << chan)) { 148962306a36Sopenharmony_ci lo = rd32(hw, GLTSYN_EVNT_L(chan, tmr_idx)); 149062306a36Sopenharmony_ci hi = rd32(hw, GLTSYN_EVNT_H(chan, tmr_idx)); 149162306a36Sopenharmony_ci event.timestamp = (((u64)hi) << 32) | lo; 149262306a36Sopenharmony_ci event.type = PTP_CLOCK_EXTTS; 149362306a36Sopenharmony_ci event.index = chan; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci /* Fire event */ 149662306a36Sopenharmony_ci ptp_clock_event(pf->ptp.clock, &event); 149762306a36Sopenharmony_ci pf->ptp.ext_ts_irq &= ~(1 << chan); 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci} 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci/** 150362306a36Sopenharmony_ci * ice_ptp_cfg_extts - Configure EXTTS pin and channel 150462306a36Sopenharmony_ci * @pf: Board private structure 150562306a36Sopenharmony_ci * @ena: true to enable; false to disable 150662306a36Sopenharmony_ci * @chan: GPIO channel (0-3) 150762306a36Sopenharmony_ci * @gpio_pin: GPIO pin 150862306a36Sopenharmony_ci * @extts_flags: request flags from the ptp_extts_request.flags 150962306a36Sopenharmony_ci */ 151062306a36Sopenharmony_cistatic int 151162306a36Sopenharmony_ciice_ptp_cfg_extts(struct ice_pf *pf, bool ena, unsigned int chan, u32 gpio_pin, 151262306a36Sopenharmony_ci unsigned int extts_flags) 151362306a36Sopenharmony_ci{ 151462306a36Sopenharmony_ci u32 func, aux_reg, gpio_reg, irq_reg; 151562306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 151662306a36Sopenharmony_ci u8 tmr_idx; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (chan > (unsigned int)pf->ptp.info.n_ext_ts) 151962306a36Sopenharmony_ci return -EINVAL; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci irq_reg = rd32(hw, PFINT_OICR_ENA); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci if (ena) { 152662306a36Sopenharmony_ci /* Enable the interrupt */ 152762306a36Sopenharmony_ci irq_reg |= PFINT_OICR_TSYN_EVNT_M; 152862306a36Sopenharmony_ci aux_reg = GLTSYN_AUX_IN_0_INT_ENA_M; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci#define GLTSYN_AUX_IN_0_EVNTLVL_RISING_EDGE BIT(0) 153162306a36Sopenharmony_ci#define GLTSYN_AUX_IN_0_EVNTLVL_FALLING_EDGE BIT(1) 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci /* set event level to requested edge */ 153462306a36Sopenharmony_ci if (extts_flags & PTP_FALLING_EDGE) 153562306a36Sopenharmony_ci aux_reg |= GLTSYN_AUX_IN_0_EVNTLVL_FALLING_EDGE; 153662306a36Sopenharmony_ci if (extts_flags & PTP_RISING_EDGE) 153762306a36Sopenharmony_ci aux_reg |= GLTSYN_AUX_IN_0_EVNTLVL_RISING_EDGE; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci /* Write GPIO CTL reg. 154062306a36Sopenharmony_ci * 0x1 is input sampled by EVENT register(channel) 154162306a36Sopenharmony_ci * + num_in_channels * tmr_idx 154262306a36Sopenharmony_ci */ 154362306a36Sopenharmony_ci func = 1 + chan + (tmr_idx * 3); 154462306a36Sopenharmony_ci gpio_reg = ((func << GLGEN_GPIO_CTL_PIN_FUNC_S) & 154562306a36Sopenharmony_ci GLGEN_GPIO_CTL_PIN_FUNC_M); 154662306a36Sopenharmony_ci pf->ptp.ext_ts_chan |= (1 << chan); 154762306a36Sopenharmony_ci } else { 154862306a36Sopenharmony_ci /* clear the values we set to reset defaults */ 154962306a36Sopenharmony_ci aux_reg = 0; 155062306a36Sopenharmony_ci gpio_reg = 0; 155162306a36Sopenharmony_ci pf->ptp.ext_ts_chan &= ~(1 << chan); 155262306a36Sopenharmony_ci if (!pf->ptp.ext_ts_chan) 155362306a36Sopenharmony_ci irq_reg &= ~PFINT_OICR_TSYN_EVNT_M; 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci wr32(hw, PFINT_OICR_ENA, irq_reg); 155762306a36Sopenharmony_ci wr32(hw, GLTSYN_AUX_IN(chan, tmr_idx), aux_reg); 155862306a36Sopenharmony_ci wr32(hw, GLGEN_GPIO_CTL(gpio_pin), gpio_reg); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci return 0; 156162306a36Sopenharmony_ci} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci/** 156462306a36Sopenharmony_ci * ice_ptp_cfg_clkout - Configure clock to generate periodic wave 156562306a36Sopenharmony_ci * @pf: Board private structure 156662306a36Sopenharmony_ci * @chan: GPIO channel (0-3) 156762306a36Sopenharmony_ci * @config: desired periodic clk configuration. NULL will disable channel 156862306a36Sopenharmony_ci * @store: If set to true the values will be stored 156962306a36Sopenharmony_ci * 157062306a36Sopenharmony_ci * Configure the internal clock generator modules to generate the clock wave of 157162306a36Sopenharmony_ci * specified period. 157262306a36Sopenharmony_ci */ 157362306a36Sopenharmony_cistatic int ice_ptp_cfg_clkout(struct ice_pf *pf, unsigned int chan, 157462306a36Sopenharmony_ci struct ice_perout_channel *config, bool store) 157562306a36Sopenharmony_ci{ 157662306a36Sopenharmony_ci u64 current_time, period, start_time, phase; 157762306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 157862306a36Sopenharmony_ci u32 func, val, gpio_pin; 157962306a36Sopenharmony_ci u8 tmr_idx; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci /* 0. Reset mode & out_en in AUX_OUT */ 158462306a36Sopenharmony_ci wr32(hw, GLTSYN_AUX_OUT(chan, tmr_idx), 0); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci /* If we're disabling the output, clear out CLKO and TGT and keep 158762306a36Sopenharmony_ci * output level low 158862306a36Sopenharmony_ci */ 158962306a36Sopenharmony_ci if (!config || !config->ena) { 159062306a36Sopenharmony_ci wr32(hw, GLTSYN_CLKO(chan, tmr_idx), 0); 159162306a36Sopenharmony_ci wr32(hw, GLTSYN_TGT_L(chan, tmr_idx), 0); 159262306a36Sopenharmony_ci wr32(hw, GLTSYN_TGT_H(chan, tmr_idx), 0); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci val = GLGEN_GPIO_CTL_PIN_DIR_M; 159562306a36Sopenharmony_ci gpio_pin = pf->ptp.perout_channels[chan].gpio_pin; 159662306a36Sopenharmony_ci wr32(hw, GLGEN_GPIO_CTL(gpio_pin), val); 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci /* Store the value if requested */ 159962306a36Sopenharmony_ci if (store) 160062306a36Sopenharmony_ci memset(&pf->ptp.perout_channels[chan], 0, 160162306a36Sopenharmony_ci sizeof(struct ice_perout_channel)); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci return 0; 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci period = config->period; 160662306a36Sopenharmony_ci start_time = config->start_time; 160762306a36Sopenharmony_ci div64_u64_rem(start_time, period, &phase); 160862306a36Sopenharmony_ci gpio_pin = config->gpio_pin; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci /* 1. Write clkout with half of required period value */ 161162306a36Sopenharmony_ci if (period & 0x1) { 161262306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "CLK Period must be an even value\n"); 161362306a36Sopenharmony_ci goto err; 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci period >>= 1; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci /* For proper operation, the GLTSYN_CLKO must be larger than clock tick 161962306a36Sopenharmony_ci */ 162062306a36Sopenharmony_ci#define MIN_PULSE 3 162162306a36Sopenharmony_ci if (period <= MIN_PULSE || period > U32_MAX) { 162262306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "CLK Period must be > %d && < 2^33", 162362306a36Sopenharmony_ci MIN_PULSE * 2); 162462306a36Sopenharmony_ci goto err; 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci wr32(hw, GLTSYN_CLKO(chan, tmr_idx), lower_32_bits(period)); 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci /* Allow time for programming before start_time is hit */ 163062306a36Sopenharmony_ci current_time = ice_ptp_read_src_clk_reg(pf, NULL); 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci /* if start time is in the past start the timer at the nearest second 163362306a36Sopenharmony_ci * maintaining phase 163462306a36Sopenharmony_ci */ 163562306a36Sopenharmony_ci if (start_time < current_time) 163662306a36Sopenharmony_ci start_time = div64_u64(current_time + NSEC_PER_SEC - 1, 163762306a36Sopenharmony_ci NSEC_PER_SEC) * NSEC_PER_SEC + phase; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci if (ice_is_e810(hw)) 164062306a36Sopenharmony_ci start_time -= E810_OUT_PROP_DELAY_NS; 164162306a36Sopenharmony_ci else 164262306a36Sopenharmony_ci start_time -= ice_e822_pps_delay(ice_e822_time_ref(hw)); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci /* 2. Write TARGET time */ 164562306a36Sopenharmony_ci wr32(hw, GLTSYN_TGT_L(chan, tmr_idx), lower_32_bits(start_time)); 164662306a36Sopenharmony_ci wr32(hw, GLTSYN_TGT_H(chan, tmr_idx), upper_32_bits(start_time)); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci /* 3. Write AUX_OUT register */ 164962306a36Sopenharmony_ci val = GLTSYN_AUX_OUT_0_OUT_ENA_M | GLTSYN_AUX_OUT_0_OUTMOD_M; 165062306a36Sopenharmony_ci wr32(hw, GLTSYN_AUX_OUT(chan, tmr_idx), val); 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci /* 4. write GPIO CTL reg */ 165362306a36Sopenharmony_ci func = 8 + chan + (tmr_idx * 4); 165462306a36Sopenharmony_ci val = GLGEN_GPIO_CTL_PIN_DIR_M | 165562306a36Sopenharmony_ci ((func << GLGEN_GPIO_CTL_PIN_FUNC_S) & GLGEN_GPIO_CTL_PIN_FUNC_M); 165662306a36Sopenharmony_ci wr32(hw, GLGEN_GPIO_CTL(gpio_pin), val); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci /* Store the value if requested */ 165962306a36Sopenharmony_ci if (store) { 166062306a36Sopenharmony_ci memcpy(&pf->ptp.perout_channels[chan], config, 166162306a36Sopenharmony_ci sizeof(struct ice_perout_channel)); 166262306a36Sopenharmony_ci pf->ptp.perout_channels[chan].start_time = phase; 166362306a36Sopenharmony_ci } 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci return 0; 166662306a36Sopenharmony_cierr: 166762306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP failed to cfg per_clk\n"); 166862306a36Sopenharmony_ci return -EFAULT; 166962306a36Sopenharmony_ci} 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci/** 167262306a36Sopenharmony_ci * ice_ptp_disable_all_clkout - Disable all currently configured outputs 167362306a36Sopenharmony_ci * @pf: pointer to the PF structure 167462306a36Sopenharmony_ci * 167562306a36Sopenharmony_ci * Disable all currently configured clock outputs. This is necessary before 167662306a36Sopenharmony_ci * certain changes to the PTP hardware clock. Use ice_ptp_enable_all_clkout to 167762306a36Sopenharmony_ci * re-enable the clocks again. 167862306a36Sopenharmony_ci */ 167962306a36Sopenharmony_cistatic void ice_ptp_disable_all_clkout(struct ice_pf *pf) 168062306a36Sopenharmony_ci{ 168162306a36Sopenharmony_ci uint i; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci for (i = 0; i < pf->ptp.info.n_per_out; i++) 168462306a36Sopenharmony_ci if (pf->ptp.perout_channels[i].ena) 168562306a36Sopenharmony_ci ice_ptp_cfg_clkout(pf, i, NULL, false); 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci/** 168962306a36Sopenharmony_ci * ice_ptp_enable_all_clkout - Enable all configured periodic clock outputs 169062306a36Sopenharmony_ci * @pf: pointer to the PF structure 169162306a36Sopenharmony_ci * 169262306a36Sopenharmony_ci * Enable all currently configured clock outputs. Use this after 169362306a36Sopenharmony_ci * ice_ptp_disable_all_clkout to reconfigure the output signals according to 169462306a36Sopenharmony_ci * their configuration. 169562306a36Sopenharmony_ci */ 169662306a36Sopenharmony_cistatic void ice_ptp_enable_all_clkout(struct ice_pf *pf) 169762306a36Sopenharmony_ci{ 169862306a36Sopenharmony_ci uint i; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci for (i = 0; i < pf->ptp.info.n_per_out; i++) 170162306a36Sopenharmony_ci if (pf->ptp.perout_channels[i].ena) 170262306a36Sopenharmony_ci ice_ptp_cfg_clkout(pf, i, &pf->ptp.perout_channels[i], 170362306a36Sopenharmony_ci false); 170462306a36Sopenharmony_ci} 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci/** 170762306a36Sopenharmony_ci * ice_ptp_gpio_enable_e810 - Enable/disable ancillary features of PHC 170862306a36Sopenharmony_ci * @info: the driver's PTP info structure 170962306a36Sopenharmony_ci * @rq: The requested feature to change 171062306a36Sopenharmony_ci * @on: Enable/disable flag 171162306a36Sopenharmony_ci */ 171262306a36Sopenharmony_cistatic int 171362306a36Sopenharmony_ciice_ptp_gpio_enable_e810(struct ptp_clock_info *info, 171462306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 171562306a36Sopenharmony_ci{ 171662306a36Sopenharmony_ci struct ice_pf *pf = ptp_info_to_pf(info); 171762306a36Sopenharmony_ci struct ice_perout_channel clk_cfg = {0}; 171862306a36Sopenharmony_ci bool sma_pres = false; 171962306a36Sopenharmony_ci unsigned int chan; 172062306a36Sopenharmony_ci u32 gpio_pin; 172162306a36Sopenharmony_ci int err; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci if (ice_is_feature_supported(pf, ICE_F_SMA_CTRL)) 172462306a36Sopenharmony_ci sma_pres = true; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci switch (rq->type) { 172762306a36Sopenharmony_ci case PTP_CLK_REQ_PEROUT: 172862306a36Sopenharmony_ci chan = rq->perout.index; 172962306a36Sopenharmony_ci if (sma_pres) { 173062306a36Sopenharmony_ci if (chan == ice_pin_desc_e810t[SMA1].chan) 173162306a36Sopenharmony_ci clk_cfg.gpio_pin = GPIO_20; 173262306a36Sopenharmony_ci else if (chan == ice_pin_desc_e810t[SMA2].chan) 173362306a36Sopenharmony_ci clk_cfg.gpio_pin = GPIO_22; 173462306a36Sopenharmony_ci else 173562306a36Sopenharmony_ci return -1; 173662306a36Sopenharmony_ci } else if (ice_is_e810t(&pf->hw)) { 173762306a36Sopenharmony_ci if (chan == 0) 173862306a36Sopenharmony_ci clk_cfg.gpio_pin = GPIO_20; 173962306a36Sopenharmony_ci else 174062306a36Sopenharmony_ci clk_cfg.gpio_pin = GPIO_22; 174162306a36Sopenharmony_ci } else if (chan == PPS_CLK_GEN_CHAN) { 174262306a36Sopenharmony_ci clk_cfg.gpio_pin = PPS_PIN_INDEX; 174362306a36Sopenharmony_ci } else { 174462306a36Sopenharmony_ci clk_cfg.gpio_pin = chan; 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci clk_cfg.period = ((rq->perout.period.sec * NSEC_PER_SEC) + 174862306a36Sopenharmony_ci rq->perout.period.nsec); 174962306a36Sopenharmony_ci clk_cfg.start_time = ((rq->perout.start.sec * NSEC_PER_SEC) + 175062306a36Sopenharmony_ci rq->perout.start.nsec); 175162306a36Sopenharmony_ci clk_cfg.ena = !!on; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci err = ice_ptp_cfg_clkout(pf, chan, &clk_cfg, true); 175462306a36Sopenharmony_ci break; 175562306a36Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 175662306a36Sopenharmony_ci chan = rq->extts.index; 175762306a36Sopenharmony_ci if (sma_pres) { 175862306a36Sopenharmony_ci if (chan < ice_pin_desc_e810t[SMA2].chan) 175962306a36Sopenharmony_ci gpio_pin = GPIO_21; 176062306a36Sopenharmony_ci else 176162306a36Sopenharmony_ci gpio_pin = GPIO_23; 176262306a36Sopenharmony_ci } else if (ice_is_e810t(&pf->hw)) { 176362306a36Sopenharmony_ci if (chan == 0) 176462306a36Sopenharmony_ci gpio_pin = GPIO_21; 176562306a36Sopenharmony_ci else 176662306a36Sopenharmony_ci gpio_pin = GPIO_23; 176762306a36Sopenharmony_ci } else { 176862306a36Sopenharmony_ci gpio_pin = chan; 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci err = ice_ptp_cfg_extts(pf, !!on, chan, gpio_pin, 177262306a36Sopenharmony_ci rq->extts.flags); 177362306a36Sopenharmony_ci break; 177462306a36Sopenharmony_ci default: 177562306a36Sopenharmony_ci return -EOPNOTSUPP; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci return err; 177962306a36Sopenharmony_ci} 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci/** 178262306a36Sopenharmony_ci * ice_ptp_gpio_enable_e823 - Enable/disable ancillary features of PHC 178362306a36Sopenharmony_ci * @info: the driver's PTP info structure 178462306a36Sopenharmony_ci * @rq: The requested feature to change 178562306a36Sopenharmony_ci * @on: Enable/disable flag 178662306a36Sopenharmony_ci */ 178762306a36Sopenharmony_cistatic int ice_ptp_gpio_enable_e823(struct ptp_clock_info *info, 178862306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 178962306a36Sopenharmony_ci{ 179062306a36Sopenharmony_ci struct ice_pf *pf = ptp_info_to_pf(info); 179162306a36Sopenharmony_ci struct ice_perout_channel clk_cfg = {0}; 179262306a36Sopenharmony_ci int err; 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci switch (rq->type) { 179562306a36Sopenharmony_ci case PTP_CLK_REQ_PPS: 179662306a36Sopenharmony_ci clk_cfg.gpio_pin = PPS_PIN_INDEX; 179762306a36Sopenharmony_ci clk_cfg.period = NSEC_PER_SEC; 179862306a36Sopenharmony_ci clk_cfg.ena = !!on; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci err = ice_ptp_cfg_clkout(pf, PPS_CLK_GEN_CHAN, &clk_cfg, true); 180162306a36Sopenharmony_ci break; 180262306a36Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 180362306a36Sopenharmony_ci err = ice_ptp_cfg_extts(pf, !!on, rq->extts.index, 180462306a36Sopenharmony_ci TIME_SYNC_PIN_INDEX, rq->extts.flags); 180562306a36Sopenharmony_ci break; 180662306a36Sopenharmony_ci default: 180762306a36Sopenharmony_ci return -EOPNOTSUPP; 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci return err; 181162306a36Sopenharmony_ci} 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci/** 181462306a36Sopenharmony_ci * ice_ptp_gettimex64 - Get the time of the clock 181562306a36Sopenharmony_ci * @info: the driver's PTP info structure 181662306a36Sopenharmony_ci * @ts: timespec64 structure to hold the current time value 181762306a36Sopenharmony_ci * @sts: Optional parameter for holding a pair of system timestamps from 181862306a36Sopenharmony_ci * the system clock. Will be ignored if NULL is given. 181962306a36Sopenharmony_ci * 182062306a36Sopenharmony_ci * Read the device clock and return the correct value on ns, after converting it 182162306a36Sopenharmony_ci * into a timespec struct. 182262306a36Sopenharmony_ci */ 182362306a36Sopenharmony_cistatic int 182462306a36Sopenharmony_ciice_ptp_gettimex64(struct ptp_clock_info *info, struct timespec64 *ts, 182562306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 182662306a36Sopenharmony_ci{ 182762306a36Sopenharmony_ci struct ice_pf *pf = ptp_info_to_pf(info); 182862306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci if (!ice_ptp_lock(hw)) { 183162306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP failed to get time\n"); 183262306a36Sopenharmony_ci return -EBUSY; 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci ice_ptp_read_time(pf, ts, sts); 183662306a36Sopenharmony_ci ice_ptp_unlock(hw); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci return 0; 183962306a36Sopenharmony_ci} 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci/** 184262306a36Sopenharmony_ci * ice_ptp_settime64 - Set the time of the clock 184362306a36Sopenharmony_ci * @info: the driver's PTP info structure 184462306a36Sopenharmony_ci * @ts: timespec64 structure that holds the new time value 184562306a36Sopenharmony_ci * 184662306a36Sopenharmony_ci * Set the device clock to the user input value. The conversion from timespec 184762306a36Sopenharmony_ci * to ns happens in the write function. 184862306a36Sopenharmony_ci */ 184962306a36Sopenharmony_cistatic int 185062306a36Sopenharmony_ciice_ptp_settime64(struct ptp_clock_info *info, const struct timespec64 *ts) 185162306a36Sopenharmony_ci{ 185262306a36Sopenharmony_ci struct ice_pf *pf = ptp_info_to_pf(info); 185362306a36Sopenharmony_ci struct timespec64 ts64 = *ts; 185462306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 185562306a36Sopenharmony_ci int err; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci /* For Vernier mode, we need to recalibrate after new settime 185862306a36Sopenharmony_ci * Start with disabling timestamp block 185962306a36Sopenharmony_ci */ 186062306a36Sopenharmony_ci if (pf->ptp.port.link_up) 186162306a36Sopenharmony_ci ice_ptp_port_phy_stop(&pf->ptp.port); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci if (!ice_ptp_lock(hw)) { 186462306a36Sopenharmony_ci err = -EBUSY; 186562306a36Sopenharmony_ci goto exit; 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci /* Disable periodic outputs */ 186962306a36Sopenharmony_ci ice_ptp_disable_all_clkout(pf); 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci err = ice_ptp_write_init(pf, &ts64); 187262306a36Sopenharmony_ci ice_ptp_unlock(hw); 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci if (!err) 187562306a36Sopenharmony_ci ice_ptp_reset_cached_phctime(pf); 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci /* Reenable periodic outputs */ 187862306a36Sopenharmony_ci ice_ptp_enable_all_clkout(pf); 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci /* Recalibrate and re-enable timestamp block */ 188162306a36Sopenharmony_ci if (pf->ptp.port.link_up) 188262306a36Sopenharmony_ci ice_ptp_port_phy_restart(&pf->ptp.port); 188362306a36Sopenharmony_ciexit: 188462306a36Sopenharmony_ci if (err) { 188562306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP failed to set time %d\n", err); 188662306a36Sopenharmony_ci return err; 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci return 0; 189062306a36Sopenharmony_ci} 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci/** 189362306a36Sopenharmony_ci * ice_ptp_adjtime_nonatomic - Do a non-atomic clock adjustment 189462306a36Sopenharmony_ci * @info: the driver's PTP info structure 189562306a36Sopenharmony_ci * @delta: Offset in nanoseconds to adjust the time by 189662306a36Sopenharmony_ci */ 189762306a36Sopenharmony_cistatic int ice_ptp_adjtime_nonatomic(struct ptp_clock_info *info, s64 delta) 189862306a36Sopenharmony_ci{ 189962306a36Sopenharmony_ci struct timespec64 now, then; 190062306a36Sopenharmony_ci int ret; 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci then = ns_to_timespec64(delta); 190362306a36Sopenharmony_ci ret = ice_ptp_gettimex64(info, &now, NULL); 190462306a36Sopenharmony_ci if (ret) 190562306a36Sopenharmony_ci return ret; 190662306a36Sopenharmony_ci now = timespec64_add(now, then); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci return ice_ptp_settime64(info, (const struct timespec64 *)&now); 190962306a36Sopenharmony_ci} 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci/** 191262306a36Sopenharmony_ci * ice_ptp_adjtime - Adjust the time of the clock by the indicated delta 191362306a36Sopenharmony_ci * @info: the driver's PTP info structure 191462306a36Sopenharmony_ci * @delta: Offset in nanoseconds to adjust the time by 191562306a36Sopenharmony_ci */ 191662306a36Sopenharmony_cistatic int ice_ptp_adjtime(struct ptp_clock_info *info, s64 delta) 191762306a36Sopenharmony_ci{ 191862306a36Sopenharmony_ci struct ice_pf *pf = ptp_info_to_pf(info); 191962306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 192062306a36Sopenharmony_ci struct device *dev; 192162306a36Sopenharmony_ci int err; 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci dev = ice_pf_to_dev(pf); 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci /* Hardware only supports atomic adjustments using signed 32-bit 192662306a36Sopenharmony_ci * integers. For any adjustment outside this range, perform 192762306a36Sopenharmony_ci * a non-atomic get->adjust->set flow. 192862306a36Sopenharmony_ci */ 192962306a36Sopenharmony_ci if (delta > S32_MAX || delta < S32_MIN) { 193062306a36Sopenharmony_ci dev_dbg(dev, "delta = %lld, adjtime non-atomic\n", delta); 193162306a36Sopenharmony_ci return ice_ptp_adjtime_nonatomic(info, delta); 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci if (!ice_ptp_lock(hw)) { 193562306a36Sopenharmony_ci dev_err(dev, "PTP failed to acquire semaphore in adjtime\n"); 193662306a36Sopenharmony_ci return -EBUSY; 193762306a36Sopenharmony_ci } 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci /* Disable periodic outputs */ 194062306a36Sopenharmony_ci ice_ptp_disable_all_clkout(pf); 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci err = ice_ptp_write_adj(pf, delta); 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci /* Reenable periodic outputs */ 194562306a36Sopenharmony_ci ice_ptp_enable_all_clkout(pf); 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci ice_ptp_unlock(hw); 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci if (err) { 195062306a36Sopenharmony_ci dev_err(dev, "PTP failed to adjust time, err %d\n", err); 195162306a36Sopenharmony_ci return err; 195262306a36Sopenharmony_ci } 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci ice_ptp_reset_cached_phctime(pf); 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci return 0; 195762306a36Sopenharmony_ci} 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci#ifdef CONFIG_ICE_HWTS 196062306a36Sopenharmony_ci/** 196162306a36Sopenharmony_ci * ice_ptp_get_syncdevicetime - Get the cross time stamp info 196262306a36Sopenharmony_ci * @device: Current device time 196362306a36Sopenharmony_ci * @system: System counter value read synchronously with device time 196462306a36Sopenharmony_ci * @ctx: Context provided by timekeeping code 196562306a36Sopenharmony_ci * 196662306a36Sopenharmony_ci * Read device and system (ART) clock simultaneously and return the corrected 196762306a36Sopenharmony_ci * clock values in ns. 196862306a36Sopenharmony_ci */ 196962306a36Sopenharmony_cistatic int 197062306a36Sopenharmony_ciice_ptp_get_syncdevicetime(ktime_t *device, 197162306a36Sopenharmony_ci struct system_counterval_t *system, 197262306a36Sopenharmony_ci void *ctx) 197362306a36Sopenharmony_ci{ 197462306a36Sopenharmony_ci struct ice_pf *pf = (struct ice_pf *)ctx; 197562306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 197662306a36Sopenharmony_ci u32 hh_lock, hh_art_ctl; 197762306a36Sopenharmony_ci int i; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci /* Get the HW lock */ 198062306a36Sopenharmony_ci hh_lock = rd32(hw, PFHH_SEM + (PFTSYN_SEM_BYTES * hw->pf_id)); 198162306a36Sopenharmony_ci if (hh_lock & PFHH_SEM_BUSY_M) { 198262306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP failed to get hh lock\n"); 198362306a36Sopenharmony_ci return -EFAULT; 198462306a36Sopenharmony_ci } 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci /* Start the ART and device clock sync sequence */ 198762306a36Sopenharmony_ci hh_art_ctl = rd32(hw, GLHH_ART_CTL); 198862306a36Sopenharmony_ci hh_art_ctl = hh_art_ctl | GLHH_ART_CTL_ACTIVE_M; 198962306a36Sopenharmony_ci wr32(hw, GLHH_ART_CTL, hh_art_ctl); 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci#define MAX_HH_LOCK_TRIES 100 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci for (i = 0; i < MAX_HH_LOCK_TRIES; i++) { 199462306a36Sopenharmony_ci /* Wait for sync to complete */ 199562306a36Sopenharmony_ci hh_art_ctl = rd32(hw, GLHH_ART_CTL); 199662306a36Sopenharmony_ci if (hh_art_ctl & GLHH_ART_CTL_ACTIVE_M) { 199762306a36Sopenharmony_ci udelay(1); 199862306a36Sopenharmony_ci continue; 199962306a36Sopenharmony_ci } else { 200062306a36Sopenharmony_ci u32 hh_ts_lo, hh_ts_hi, tmr_idx; 200162306a36Sopenharmony_ci u64 hh_ts; 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc; 200462306a36Sopenharmony_ci /* Read ART time */ 200562306a36Sopenharmony_ci hh_ts_lo = rd32(hw, GLHH_ART_TIME_L); 200662306a36Sopenharmony_ci hh_ts_hi = rd32(hw, GLHH_ART_TIME_H); 200762306a36Sopenharmony_ci hh_ts = ((u64)hh_ts_hi << 32) | hh_ts_lo; 200862306a36Sopenharmony_ci *system = convert_art_ns_to_tsc(hh_ts); 200962306a36Sopenharmony_ci /* Read Device source clock time */ 201062306a36Sopenharmony_ci hh_ts_lo = rd32(hw, GLTSYN_HHTIME_L(tmr_idx)); 201162306a36Sopenharmony_ci hh_ts_hi = rd32(hw, GLTSYN_HHTIME_H(tmr_idx)); 201262306a36Sopenharmony_ci hh_ts = ((u64)hh_ts_hi << 32) | hh_ts_lo; 201362306a36Sopenharmony_ci *device = ns_to_ktime(hh_ts); 201462306a36Sopenharmony_ci break; 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci /* Release HW lock */ 201862306a36Sopenharmony_ci hh_lock = rd32(hw, PFHH_SEM + (PFTSYN_SEM_BYTES * hw->pf_id)); 201962306a36Sopenharmony_ci hh_lock = hh_lock & ~PFHH_SEM_BUSY_M; 202062306a36Sopenharmony_ci wr32(hw, PFHH_SEM + (PFTSYN_SEM_BYTES * hw->pf_id), hh_lock); 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci if (i == MAX_HH_LOCK_TRIES) 202362306a36Sopenharmony_ci return -ETIMEDOUT; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci return 0; 202662306a36Sopenharmony_ci} 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci/** 202962306a36Sopenharmony_ci * ice_ptp_getcrosststamp_e822 - Capture a device cross timestamp 203062306a36Sopenharmony_ci * @info: the driver's PTP info structure 203162306a36Sopenharmony_ci * @cts: The memory to fill the cross timestamp info 203262306a36Sopenharmony_ci * 203362306a36Sopenharmony_ci * Capture a cross timestamp between the ART and the device PTP hardware 203462306a36Sopenharmony_ci * clock. Fill the cross timestamp information and report it back to the 203562306a36Sopenharmony_ci * caller. 203662306a36Sopenharmony_ci * 203762306a36Sopenharmony_ci * This is only valid for E822 devices which have support for generating the 203862306a36Sopenharmony_ci * cross timestamp via PCIe PTM. 203962306a36Sopenharmony_ci * 204062306a36Sopenharmony_ci * In order to correctly correlate the ART timestamp back to the TSC time, the 204162306a36Sopenharmony_ci * CPU must have X86_FEATURE_TSC_KNOWN_FREQ. 204262306a36Sopenharmony_ci */ 204362306a36Sopenharmony_cistatic int 204462306a36Sopenharmony_ciice_ptp_getcrosststamp_e822(struct ptp_clock_info *info, 204562306a36Sopenharmony_ci struct system_device_crosststamp *cts) 204662306a36Sopenharmony_ci{ 204762306a36Sopenharmony_ci struct ice_pf *pf = ptp_info_to_pf(info); 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci return get_device_system_crosststamp(ice_ptp_get_syncdevicetime, 205062306a36Sopenharmony_ci pf, NULL, cts); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci#endif /* CONFIG_ICE_HWTS */ 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci/** 205562306a36Sopenharmony_ci * ice_ptp_get_ts_config - ioctl interface to read the timestamping config 205662306a36Sopenharmony_ci * @pf: Board private structure 205762306a36Sopenharmony_ci * @ifr: ioctl data 205862306a36Sopenharmony_ci * 205962306a36Sopenharmony_ci * Copy the timestamping config to user buffer 206062306a36Sopenharmony_ci */ 206162306a36Sopenharmony_ciint ice_ptp_get_ts_config(struct ice_pf *pf, struct ifreq *ifr) 206262306a36Sopenharmony_ci{ 206362306a36Sopenharmony_ci struct hwtstamp_config *config; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci if (!test_bit(ICE_FLAG_PTP, pf->flags)) 206662306a36Sopenharmony_ci return -EIO; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci config = &pf->ptp.tstamp_config; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? 207162306a36Sopenharmony_ci -EFAULT : 0; 207262306a36Sopenharmony_ci} 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci/** 207562306a36Sopenharmony_ci * ice_ptp_set_timestamp_mode - Setup driver for requested timestamp mode 207662306a36Sopenharmony_ci * @pf: Board private structure 207762306a36Sopenharmony_ci * @config: hwtstamp settings requested or saved 207862306a36Sopenharmony_ci */ 207962306a36Sopenharmony_cistatic int 208062306a36Sopenharmony_ciice_ptp_set_timestamp_mode(struct ice_pf *pf, struct hwtstamp_config *config) 208162306a36Sopenharmony_ci{ 208262306a36Sopenharmony_ci switch (config->tx_type) { 208362306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 208462306a36Sopenharmony_ci ice_set_tx_tstamp(pf, false); 208562306a36Sopenharmony_ci break; 208662306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 208762306a36Sopenharmony_ci ice_set_tx_tstamp(pf, true); 208862306a36Sopenharmony_ci break; 208962306a36Sopenharmony_ci default: 209062306a36Sopenharmony_ci return -ERANGE; 209162306a36Sopenharmony_ci } 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci switch (config->rx_filter) { 209462306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 209562306a36Sopenharmony_ci ice_set_rx_tstamp(pf, false); 209662306a36Sopenharmony_ci break; 209762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 209862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 209962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 210062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 210162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 210262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 210362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 210462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 210562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 210662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 210762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 210862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 210962306a36Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 211062306a36Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 211162306a36Sopenharmony_ci ice_set_rx_tstamp(pf, true); 211262306a36Sopenharmony_ci break; 211362306a36Sopenharmony_ci default: 211462306a36Sopenharmony_ci return -ERANGE; 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci return 0; 211862306a36Sopenharmony_ci} 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci/** 212162306a36Sopenharmony_ci * ice_ptp_set_ts_config - ioctl interface to control the timestamping 212262306a36Sopenharmony_ci * @pf: Board private structure 212362306a36Sopenharmony_ci * @ifr: ioctl data 212462306a36Sopenharmony_ci * 212562306a36Sopenharmony_ci * Get the user config and store it 212662306a36Sopenharmony_ci */ 212762306a36Sopenharmony_ciint ice_ptp_set_ts_config(struct ice_pf *pf, struct ifreq *ifr) 212862306a36Sopenharmony_ci{ 212962306a36Sopenharmony_ci struct hwtstamp_config config; 213062306a36Sopenharmony_ci int err; 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci if (!test_bit(ICE_FLAG_PTP, pf->flags)) 213362306a36Sopenharmony_ci return -EAGAIN; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 213662306a36Sopenharmony_ci return -EFAULT; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci err = ice_ptp_set_timestamp_mode(pf, &config); 213962306a36Sopenharmony_ci if (err) 214062306a36Sopenharmony_ci return err; 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci /* Return the actual configuration set */ 214362306a36Sopenharmony_ci config = pf->ptp.tstamp_config; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? 214662306a36Sopenharmony_ci -EFAULT : 0; 214762306a36Sopenharmony_ci} 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci/** 215062306a36Sopenharmony_ci * ice_ptp_rx_hwtstamp - Check for an Rx timestamp 215162306a36Sopenharmony_ci * @rx_ring: Ring to get the VSI info 215262306a36Sopenharmony_ci * @rx_desc: Receive descriptor 215362306a36Sopenharmony_ci * @skb: Particular skb to send timestamp with 215462306a36Sopenharmony_ci * 215562306a36Sopenharmony_ci * The driver receives a notification in the receive descriptor with timestamp. 215662306a36Sopenharmony_ci * The timestamp is in ns, so we must convert the result first. 215762306a36Sopenharmony_ci */ 215862306a36Sopenharmony_civoid 215962306a36Sopenharmony_ciice_ptp_rx_hwtstamp(struct ice_rx_ring *rx_ring, 216062306a36Sopenharmony_ci union ice_32b_rx_flex_desc *rx_desc, struct sk_buff *skb) 216162306a36Sopenharmony_ci{ 216262306a36Sopenharmony_ci struct skb_shared_hwtstamps *hwtstamps; 216362306a36Sopenharmony_ci u64 ts_ns, cached_time; 216462306a36Sopenharmony_ci u32 ts_high; 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci if (!(rx_desc->wb.time_stamp_low & ICE_PTP_TS_VALID)) 216762306a36Sopenharmony_ci return; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci cached_time = READ_ONCE(rx_ring->cached_phctime); 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci /* Do not report a timestamp if we don't have a cached PHC time */ 217262306a36Sopenharmony_ci if (!cached_time) 217362306a36Sopenharmony_ci return; 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci /* Use ice_ptp_extend_32b_ts directly, using the ring-specific cached 217662306a36Sopenharmony_ci * PHC value, rather than accessing the PF. This also allows us to 217762306a36Sopenharmony_ci * simply pass the upper 32bits of nanoseconds directly. Calling 217862306a36Sopenharmony_ci * ice_ptp_extend_40b_ts is unnecessary as it would just discard these 217962306a36Sopenharmony_ci * bits itself. 218062306a36Sopenharmony_ci */ 218162306a36Sopenharmony_ci ts_high = le32_to_cpu(rx_desc->wb.flex_ts.ts_high); 218262306a36Sopenharmony_ci ts_ns = ice_ptp_extend_32b_ts(cached_time, ts_high); 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci hwtstamps = skb_hwtstamps(skb); 218562306a36Sopenharmony_ci memset(hwtstamps, 0, sizeof(*hwtstamps)); 218662306a36Sopenharmony_ci hwtstamps->hwtstamp = ns_to_ktime(ts_ns); 218762306a36Sopenharmony_ci} 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci/** 219062306a36Sopenharmony_ci * ice_ptp_disable_sma_pins_e810t - Disable E810-T SMA pins 219162306a36Sopenharmony_ci * @pf: pointer to the PF structure 219262306a36Sopenharmony_ci * @info: PTP clock info structure 219362306a36Sopenharmony_ci * 219462306a36Sopenharmony_ci * Disable the OS access to the SMA pins. Called to clear out the OS 219562306a36Sopenharmony_ci * indications of pin support when we fail to setup the E810-T SMA control 219662306a36Sopenharmony_ci * register. 219762306a36Sopenharmony_ci */ 219862306a36Sopenharmony_cistatic void 219962306a36Sopenharmony_ciice_ptp_disable_sma_pins_e810t(struct ice_pf *pf, struct ptp_clock_info *info) 220062306a36Sopenharmony_ci{ 220162306a36Sopenharmony_ci struct device *dev = ice_pf_to_dev(pf); 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci dev_warn(dev, "Failed to configure E810-T SMA pin control\n"); 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci info->enable = NULL; 220662306a36Sopenharmony_ci info->verify = NULL; 220762306a36Sopenharmony_ci info->n_pins = 0; 220862306a36Sopenharmony_ci info->n_ext_ts = 0; 220962306a36Sopenharmony_ci info->n_per_out = 0; 221062306a36Sopenharmony_ci} 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci/** 221362306a36Sopenharmony_ci * ice_ptp_setup_sma_pins_e810t - Setup the SMA pins 221462306a36Sopenharmony_ci * @pf: pointer to the PF structure 221562306a36Sopenharmony_ci * @info: PTP clock info structure 221662306a36Sopenharmony_ci * 221762306a36Sopenharmony_ci * Finish setting up the SMA pins by allocating pin_config, and setting it up 221862306a36Sopenharmony_ci * according to the current status of the SMA. On failure, disable all of the 221962306a36Sopenharmony_ci * extended SMA pin support. 222062306a36Sopenharmony_ci */ 222162306a36Sopenharmony_cistatic void 222262306a36Sopenharmony_ciice_ptp_setup_sma_pins_e810t(struct ice_pf *pf, struct ptp_clock_info *info) 222362306a36Sopenharmony_ci{ 222462306a36Sopenharmony_ci struct device *dev = ice_pf_to_dev(pf); 222562306a36Sopenharmony_ci int err; 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci /* Allocate memory for kernel pins interface */ 222862306a36Sopenharmony_ci info->pin_config = devm_kcalloc(dev, info->n_pins, 222962306a36Sopenharmony_ci sizeof(*info->pin_config), GFP_KERNEL); 223062306a36Sopenharmony_ci if (!info->pin_config) { 223162306a36Sopenharmony_ci ice_ptp_disable_sma_pins_e810t(pf, info); 223262306a36Sopenharmony_ci return; 223362306a36Sopenharmony_ci } 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci /* Read current SMA status */ 223662306a36Sopenharmony_ci err = ice_get_sma_config_e810t(&pf->hw, info->pin_config); 223762306a36Sopenharmony_ci if (err) 223862306a36Sopenharmony_ci ice_ptp_disable_sma_pins_e810t(pf, info); 223962306a36Sopenharmony_ci} 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci/** 224262306a36Sopenharmony_ci * ice_ptp_setup_pins_e810 - Setup PTP pins in sysfs 224362306a36Sopenharmony_ci * @pf: pointer to the PF instance 224462306a36Sopenharmony_ci * @info: PTP clock capabilities 224562306a36Sopenharmony_ci */ 224662306a36Sopenharmony_cistatic void 224762306a36Sopenharmony_ciice_ptp_setup_pins_e810(struct ice_pf *pf, struct ptp_clock_info *info) 224862306a36Sopenharmony_ci{ 224962306a36Sopenharmony_ci if (ice_is_feature_supported(pf, ICE_F_SMA_CTRL)) { 225062306a36Sopenharmony_ci info->n_ext_ts = N_EXT_TS_E810; 225162306a36Sopenharmony_ci info->n_per_out = N_PER_OUT_E810T; 225262306a36Sopenharmony_ci info->n_pins = NUM_PTP_PINS_E810T; 225362306a36Sopenharmony_ci info->verify = ice_verify_pin_e810t; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci /* Complete setup of the SMA pins */ 225662306a36Sopenharmony_ci ice_ptp_setup_sma_pins_e810t(pf, info); 225762306a36Sopenharmony_ci } else if (ice_is_e810t(&pf->hw)) { 225862306a36Sopenharmony_ci info->n_ext_ts = N_EXT_TS_NO_SMA_E810T; 225962306a36Sopenharmony_ci info->n_per_out = N_PER_OUT_NO_SMA_E810T; 226062306a36Sopenharmony_ci } else { 226162306a36Sopenharmony_ci info->n_per_out = N_PER_OUT_E810; 226262306a36Sopenharmony_ci info->n_ext_ts = N_EXT_TS_E810; 226362306a36Sopenharmony_ci } 226462306a36Sopenharmony_ci} 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci/** 226762306a36Sopenharmony_ci * ice_ptp_setup_pins_e823 - Setup PTP pins in sysfs 226862306a36Sopenharmony_ci * @pf: pointer to the PF instance 226962306a36Sopenharmony_ci * @info: PTP clock capabilities 227062306a36Sopenharmony_ci */ 227162306a36Sopenharmony_cistatic void 227262306a36Sopenharmony_ciice_ptp_setup_pins_e823(struct ice_pf *pf, struct ptp_clock_info *info) 227362306a36Sopenharmony_ci{ 227462306a36Sopenharmony_ci info->pps = 1; 227562306a36Sopenharmony_ci info->n_per_out = 0; 227662306a36Sopenharmony_ci info->n_ext_ts = 1; 227762306a36Sopenharmony_ci} 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci/** 228062306a36Sopenharmony_ci * ice_ptp_set_funcs_e822 - Set specialized functions for E822 support 228162306a36Sopenharmony_ci * @pf: Board private structure 228262306a36Sopenharmony_ci * @info: PTP info to fill 228362306a36Sopenharmony_ci * 228462306a36Sopenharmony_ci * Assign functions to the PTP capabiltiies structure for E822 devices. 228562306a36Sopenharmony_ci * Functions which operate across all device families should be set directly 228662306a36Sopenharmony_ci * in ice_ptp_set_caps. Only add functions here which are distinct for E822 228762306a36Sopenharmony_ci * devices. 228862306a36Sopenharmony_ci */ 228962306a36Sopenharmony_cistatic void 229062306a36Sopenharmony_ciice_ptp_set_funcs_e822(struct ice_pf *pf, struct ptp_clock_info *info) 229162306a36Sopenharmony_ci{ 229262306a36Sopenharmony_ci#ifdef CONFIG_ICE_HWTS 229362306a36Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_ART) && 229462306a36Sopenharmony_ci boot_cpu_has(X86_FEATURE_TSC_KNOWN_FREQ)) 229562306a36Sopenharmony_ci info->getcrosststamp = ice_ptp_getcrosststamp_e822; 229662306a36Sopenharmony_ci#endif /* CONFIG_ICE_HWTS */ 229762306a36Sopenharmony_ci} 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci/** 230062306a36Sopenharmony_ci * ice_ptp_set_funcs_e810 - Set specialized functions for E810 support 230162306a36Sopenharmony_ci * @pf: Board private structure 230262306a36Sopenharmony_ci * @info: PTP info to fill 230362306a36Sopenharmony_ci * 230462306a36Sopenharmony_ci * Assign functions to the PTP capabiltiies structure for E810 devices. 230562306a36Sopenharmony_ci * Functions which operate across all device families should be set directly 230662306a36Sopenharmony_ci * in ice_ptp_set_caps. Only add functions here which are distinct for e810 230762306a36Sopenharmony_ci * devices. 230862306a36Sopenharmony_ci */ 230962306a36Sopenharmony_cistatic void 231062306a36Sopenharmony_ciice_ptp_set_funcs_e810(struct ice_pf *pf, struct ptp_clock_info *info) 231162306a36Sopenharmony_ci{ 231262306a36Sopenharmony_ci info->enable = ice_ptp_gpio_enable_e810; 231362306a36Sopenharmony_ci ice_ptp_setup_pins_e810(pf, info); 231462306a36Sopenharmony_ci} 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci/** 231762306a36Sopenharmony_ci * ice_ptp_set_funcs_e823 - Set specialized functions for E823 support 231862306a36Sopenharmony_ci * @pf: Board private structure 231962306a36Sopenharmony_ci * @info: PTP info to fill 232062306a36Sopenharmony_ci * 232162306a36Sopenharmony_ci * Assign functions to the PTP capabiltiies structure for E823 devices. 232262306a36Sopenharmony_ci * Functions which operate across all device families should be set directly 232362306a36Sopenharmony_ci * in ice_ptp_set_caps. Only add functions here which are distinct for e823 232462306a36Sopenharmony_ci * devices. 232562306a36Sopenharmony_ci */ 232662306a36Sopenharmony_cistatic void 232762306a36Sopenharmony_ciice_ptp_set_funcs_e823(struct ice_pf *pf, struct ptp_clock_info *info) 232862306a36Sopenharmony_ci{ 232962306a36Sopenharmony_ci info->enable = ice_ptp_gpio_enable_e823; 233062306a36Sopenharmony_ci ice_ptp_setup_pins_e823(pf, info); 233162306a36Sopenharmony_ci} 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci/** 233462306a36Sopenharmony_ci * ice_ptp_set_caps - Set PTP capabilities 233562306a36Sopenharmony_ci * @pf: Board private structure 233662306a36Sopenharmony_ci */ 233762306a36Sopenharmony_cistatic void ice_ptp_set_caps(struct ice_pf *pf) 233862306a36Sopenharmony_ci{ 233962306a36Sopenharmony_ci struct ptp_clock_info *info = &pf->ptp.info; 234062306a36Sopenharmony_ci struct device *dev = ice_pf_to_dev(pf); 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci snprintf(info->name, sizeof(info->name) - 1, "%s-%s-clk", 234362306a36Sopenharmony_ci dev_driver_string(dev), dev_name(dev)); 234462306a36Sopenharmony_ci info->owner = THIS_MODULE; 234562306a36Sopenharmony_ci info->max_adj = 100000000; 234662306a36Sopenharmony_ci info->adjtime = ice_ptp_adjtime; 234762306a36Sopenharmony_ci info->adjfine = ice_ptp_adjfine; 234862306a36Sopenharmony_ci info->gettimex64 = ice_ptp_gettimex64; 234962306a36Sopenharmony_ci info->settime64 = ice_ptp_settime64; 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci if (ice_is_e810(&pf->hw)) 235262306a36Sopenharmony_ci ice_ptp_set_funcs_e810(pf, info); 235362306a36Sopenharmony_ci else if (ice_is_e823(&pf->hw)) 235462306a36Sopenharmony_ci ice_ptp_set_funcs_e823(pf, info); 235562306a36Sopenharmony_ci else 235662306a36Sopenharmony_ci ice_ptp_set_funcs_e822(pf, info); 235762306a36Sopenharmony_ci} 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci/** 236062306a36Sopenharmony_ci * ice_ptp_create_clock - Create PTP clock device for userspace 236162306a36Sopenharmony_ci * @pf: Board private structure 236262306a36Sopenharmony_ci * 236362306a36Sopenharmony_ci * This function creates a new PTP clock device. It only creates one if we 236462306a36Sopenharmony_ci * don't already have one. Will return error if it can't create one, but success 236562306a36Sopenharmony_ci * if we already have a device. Should be used by ice_ptp_init to create clock 236662306a36Sopenharmony_ci * initially, and prevent global resets from creating new clock devices. 236762306a36Sopenharmony_ci */ 236862306a36Sopenharmony_cistatic long ice_ptp_create_clock(struct ice_pf *pf) 236962306a36Sopenharmony_ci{ 237062306a36Sopenharmony_ci struct ptp_clock_info *info; 237162306a36Sopenharmony_ci struct ptp_clock *clock; 237262306a36Sopenharmony_ci struct device *dev; 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci /* No need to create a clock device if we already have one */ 237562306a36Sopenharmony_ci if (pf->ptp.clock) 237662306a36Sopenharmony_ci return 0; 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci ice_ptp_set_caps(pf); 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci info = &pf->ptp.info; 238162306a36Sopenharmony_ci dev = ice_pf_to_dev(pf); 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci /* Attempt to register the clock before enabling the hardware. */ 238462306a36Sopenharmony_ci clock = ptp_clock_register(info, dev); 238562306a36Sopenharmony_ci if (IS_ERR(clock)) 238662306a36Sopenharmony_ci return PTR_ERR(clock); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci pf->ptp.clock = clock; 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci return 0; 239162306a36Sopenharmony_ci} 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci/** 239462306a36Sopenharmony_ci * ice_ptp_request_ts - Request an available Tx timestamp index 239562306a36Sopenharmony_ci * @tx: the PTP Tx timestamp tracker to request from 239662306a36Sopenharmony_ci * @skb: the SKB to associate with this timestamp request 239762306a36Sopenharmony_ci */ 239862306a36Sopenharmony_cis8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb) 239962306a36Sopenharmony_ci{ 240062306a36Sopenharmony_ci u8 idx; 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci spin_lock(&tx->lock); 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci /* Check that this tracker is accepting new timestamp requests */ 240562306a36Sopenharmony_ci if (!ice_ptp_is_tx_tracker_up(tx)) { 240662306a36Sopenharmony_ci spin_unlock(&tx->lock); 240762306a36Sopenharmony_ci return -1; 240862306a36Sopenharmony_ci } 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci /* Find and set the first available index */ 241162306a36Sopenharmony_ci idx = find_first_zero_bit(tx->in_use, tx->len); 241262306a36Sopenharmony_ci if (idx < tx->len) { 241362306a36Sopenharmony_ci /* We got a valid index that no other thread could have set. Store 241462306a36Sopenharmony_ci * a reference to the skb and the start time to allow discarding old 241562306a36Sopenharmony_ci * requests. 241662306a36Sopenharmony_ci */ 241762306a36Sopenharmony_ci set_bit(idx, tx->in_use); 241862306a36Sopenharmony_ci clear_bit(idx, tx->stale); 241962306a36Sopenharmony_ci tx->tstamps[idx].start = jiffies; 242062306a36Sopenharmony_ci tx->tstamps[idx].skb = skb_get(skb); 242162306a36Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 242262306a36Sopenharmony_ci ice_trace(tx_tstamp_request, skb, idx); 242362306a36Sopenharmony_ci } 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci spin_unlock(&tx->lock); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci /* return the appropriate PHY timestamp register index, -1 if no 242862306a36Sopenharmony_ci * indexes were available. 242962306a36Sopenharmony_ci */ 243062306a36Sopenharmony_ci if (idx >= tx->len) 243162306a36Sopenharmony_ci return -1; 243262306a36Sopenharmony_ci else 243362306a36Sopenharmony_ci return idx + tx->offset; 243462306a36Sopenharmony_ci} 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci/** 243762306a36Sopenharmony_ci * ice_ptp_process_ts - Process the PTP Tx timestamps 243862306a36Sopenharmony_ci * @pf: Board private structure 243962306a36Sopenharmony_ci * 244062306a36Sopenharmony_ci * Returns: ICE_TX_TSTAMP_WORK_PENDING if there are any outstanding Tx 244162306a36Sopenharmony_ci * timestamps that need processing, and ICE_TX_TSTAMP_WORK_DONE otherwise. 244262306a36Sopenharmony_ci */ 244362306a36Sopenharmony_cienum ice_tx_tstamp_work ice_ptp_process_ts(struct ice_pf *pf) 244462306a36Sopenharmony_ci{ 244562306a36Sopenharmony_ci return ice_ptp_tx_tstamp(&pf->ptp.port.tx); 244662306a36Sopenharmony_ci} 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_cistatic void ice_ptp_periodic_work(struct kthread_work *work) 244962306a36Sopenharmony_ci{ 245062306a36Sopenharmony_ci struct ice_ptp *ptp = container_of(work, struct ice_ptp, work.work); 245162306a36Sopenharmony_ci struct ice_pf *pf = container_of(ptp, struct ice_pf, ptp); 245262306a36Sopenharmony_ci int err; 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ci if (!test_bit(ICE_FLAG_PTP, pf->flags)) 245562306a36Sopenharmony_ci return; 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci err = ice_ptp_update_cached_phctime(pf); 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci /* Run twice a second or reschedule if phc update failed */ 246062306a36Sopenharmony_ci kthread_queue_delayed_work(ptp->kworker, &ptp->work, 246162306a36Sopenharmony_ci msecs_to_jiffies(err ? 10 : 500)); 246262306a36Sopenharmony_ci} 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci/** 246562306a36Sopenharmony_ci * ice_ptp_reset - Initialize PTP hardware clock support after reset 246662306a36Sopenharmony_ci * @pf: Board private structure 246762306a36Sopenharmony_ci */ 246862306a36Sopenharmony_civoid ice_ptp_reset(struct ice_pf *pf) 246962306a36Sopenharmony_ci{ 247062306a36Sopenharmony_ci struct ice_ptp *ptp = &pf->ptp; 247162306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 247262306a36Sopenharmony_ci struct timespec64 ts; 247362306a36Sopenharmony_ci int err, itr = 1; 247462306a36Sopenharmony_ci u64 time_diff; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci if (test_bit(ICE_PFR_REQ, pf->state)) 247762306a36Sopenharmony_ci goto pfr; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci if (!hw->func_caps.ts_func_info.src_tmr_owned) 248062306a36Sopenharmony_ci goto reset_ts; 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci err = ice_ptp_init_phc(hw); 248362306a36Sopenharmony_ci if (err) 248462306a36Sopenharmony_ci goto err; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci /* Acquire the global hardware lock */ 248762306a36Sopenharmony_ci if (!ice_ptp_lock(hw)) { 248862306a36Sopenharmony_ci err = -EBUSY; 248962306a36Sopenharmony_ci goto err; 249062306a36Sopenharmony_ci } 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci /* Write the increment time value to PHY and LAN */ 249362306a36Sopenharmony_ci err = ice_ptp_write_incval(hw, ice_base_incval(pf)); 249462306a36Sopenharmony_ci if (err) { 249562306a36Sopenharmony_ci ice_ptp_unlock(hw); 249662306a36Sopenharmony_ci goto err; 249762306a36Sopenharmony_ci } 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci /* Write the initial Time value to PHY and LAN using the cached PHC 250062306a36Sopenharmony_ci * time before the reset and time difference between stopping and 250162306a36Sopenharmony_ci * starting the clock. 250262306a36Sopenharmony_ci */ 250362306a36Sopenharmony_ci if (ptp->cached_phc_time) { 250462306a36Sopenharmony_ci time_diff = ktime_get_real_ns() - ptp->reset_time; 250562306a36Sopenharmony_ci ts = ns_to_timespec64(ptp->cached_phc_time + time_diff); 250662306a36Sopenharmony_ci } else { 250762306a36Sopenharmony_ci ts = ktime_to_timespec64(ktime_get_real()); 250862306a36Sopenharmony_ci } 250962306a36Sopenharmony_ci err = ice_ptp_write_init(pf, &ts); 251062306a36Sopenharmony_ci if (err) { 251162306a36Sopenharmony_ci ice_ptp_unlock(hw); 251262306a36Sopenharmony_ci goto err; 251362306a36Sopenharmony_ci } 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci /* Release the global hardware lock */ 251662306a36Sopenharmony_ci ice_ptp_unlock(hw); 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci if (!ice_is_e810(hw)) { 251962306a36Sopenharmony_ci /* Enable quad interrupts */ 252062306a36Sopenharmony_ci err = ice_ptp_tx_ena_intr(pf, true, itr); 252162306a36Sopenharmony_ci if (err) 252262306a36Sopenharmony_ci goto err; 252362306a36Sopenharmony_ci } 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_cireset_ts: 252662306a36Sopenharmony_ci /* Restart the PHY timestamping block */ 252762306a36Sopenharmony_ci ice_ptp_reset_phy_timestamping(pf); 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_cipfr: 253062306a36Sopenharmony_ci /* Init Tx structures */ 253162306a36Sopenharmony_ci if (ice_is_e810(&pf->hw)) { 253262306a36Sopenharmony_ci err = ice_ptp_init_tx_e810(pf, &ptp->port.tx); 253362306a36Sopenharmony_ci } else { 253462306a36Sopenharmony_ci kthread_init_delayed_work(&ptp->port.ov_work, 253562306a36Sopenharmony_ci ice_ptp_wait_for_offsets); 253662306a36Sopenharmony_ci err = ice_ptp_init_tx_e822(pf, &ptp->port.tx, 253762306a36Sopenharmony_ci ptp->port.port_num); 253862306a36Sopenharmony_ci } 253962306a36Sopenharmony_ci if (err) 254062306a36Sopenharmony_ci goto err; 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci set_bit(ICE_FLAG_PTP, pf->flags); 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci /* Start periodic work going */ 254562306a36Sopenharmony_ci kthread_queue_delayed_work(ptp->kworker, &ptp->work, 0); 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_ci dev_info(ice_pf_to_dev(pf), "PTP reset successful\n"); 254862306a36Sopenharmony_ci return; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_cierr: 255162306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP reset failed %d\n", err); 255262306a36Sopenharmony_ci} 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci/** 255562306a36Sopenharmony_ci * ice_ptp_prepare_for_reset - Prepare PTP for reset 255662306a36Sopenharmony_ci * @pf: Board private structure 255762306a36Sopenharmony_ci */ 255862306a36Sopenharmony_civoid ice_ptp_prepare_for_reset(struct ice_pf *pf) 255962306a36Sopenharmony_ci{ 256062306a36Sopenharmony_ci struct ice_ptp *ptp = &pf->ptp; 256162306a36Sopenharmony_ci u8 src_tmr; 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci clear_bit(ICE_FLAG_PTP, pf->flags); 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci /* Disable timestamping for both Tx and Rx */ 256662306a36Sopenharmony_ci ice_ptp_cfg_timestamp(pf, false); 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci kthread_cancel_delayed_work_sync(&ptp->work); 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci if (test_bit(ICE_PFR_REQ, pf->state)) 257162306a36Sopenharmony_ci return; 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx); 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci /* Disable periodic outputs */ 257662306a36Sopenharmony_ci ice_ptp_disable_all_clkout(pf); 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci src_tmr = ice_get_ptp_src_clock_index(&pf->hw); 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci /* Disable source clock */ 258162306a36Sopenharmony_ci wr32(&pf->hw, GLTSYN_ENA(src_tmr), (u32)~GLTSYN_ENA_TSYN_ENA_M); 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci /* Acquire PHC and system timer to restore after reset */ 258462306a36Sopenharmony_ci ptp->reset_time = ktime_get_real_ns(); 258562306a36Sopenharmony_ci} 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci/** 258862306a36Sopenharmony_ci * ice_ptp_init_owner - Initialize PTP_1588_CLOCK device 258962306a36Sopenharmony_ci * @pf: Board private structure 259062306a36Sopenharmony_ci * 259162306a36Sopenharmony_ci * Setup and initialize a PTP clock device that represents the device hardware 259262306a36Sopenharmony_ci * clock. Save the clock index for other functions connected to the same 259362306a36Sopenharmony_ci * hardware resource. 259462306a36Sopenharmony_ci */ 259562306a36Sopenharmony_cistatic int ice_ptp_init_owner(struct ice_pf *pf) 259662306a36Sopenharmony_ci{ 259762306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 259862306a36Sopenharmony_ci struct timespec64 ts; 259962306a36Sopenharmony_ci int err, itr = 1; 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci err = ice_ptp_init_phc(hw); 260262306a36Sopenharmony_ci if (err) { 260362306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "Failed to initialize PHC, err %d\n", 260462306a36Sopenharmony_ci err); 260562306a36Sopenharmony_ci return err; 260662306a36Sopenharmony_ci } 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci /* Acquire the global hardware lock */ 260962306a36Sopenharmony_ci if (!ice_ptp_lock(hw)) { 261062306a36Sopenharmony_ci err = -EBUSY; 261162306a36Sopenharmony_ci goto err_exit; 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci /* Write the increment time value to PHY and LAN */ 261562306a36Sopenharmony_ci err = ice_ptp_write_incval(hw, ice_base_incval(pf)); 261662306a36Sopenharmony_ci if (err) { 261762306a36Sopenharmony_ci ice_ptp_unlock(hw); 261862306a36Sopenharmony_ci goto err_exit; 261962306a36Sopenharmony_ci } 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci ts = ktime_to_timespec64(ktime_get_real()); 262262306a36Sopenharmony_ci /* Write the initial Time value to PHY and LAN */ 262362306a36Sopenharmony_ci err = ice_ptp_write_init(pf, &ts); 262462306a36Sopenharmony_ci if (err) { 262562306a36Sopenharmony_ci ice_ptp_unlock(hw); 262662306a36Sopenharmony_ci goto err_exit; 262762306a36Sopenharmony_ci } 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci /* Release the global hardware lock */ 263062306a36Sopenharmony_ci ice_ptp_unlock(hw); 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci if (!ice_is_e810(hw)) { 263362306a36Sopenharmony_ci /* Enable quad interrupts */ 263462306a36Sopenharmony_ci err = ice_ptp_tx_ena_intr(pf, true, itr); 263562306a36Sopenharmony_ci if (err) 263662306a36Sopenharmony_ci goto err_exit; 263762306a36Sopenharmony_ci } 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci /* Ensure we have a clock device */ 264062306a36Sopenharmony_ci err = ice_ptp_create_clock(pf); 264162306a36Sopenharmony_ci if (err) 264262306a36Sopenharmony_ci goto err_clk; 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci /* Store the PTP clock index for other PFs */ 264562306a36Sopenharmony_ci ice_set_ptp_clock_index(pf); 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_ci return 0; 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_cierr_clk: 265062306a36Sopenharmony_ci pf->ptp.clock = NULL; 265162306a36Sopenharmony_cierr_exit: 265262306a36Sopenharmony_ci return err; 265362306a36Sopenharmony_ci} 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci/** 265662306a36Sopenharmony_ci * ice_ptp_init_work - Initialize PTP work threads 265762306a36Sopenharmony_ci * @pf: Board private structure 265862306a36Sopenharmony_ci * @ptp: PF PTP structure 265962306a36Sopenharmony_ci */ 266062306a36Sopenharmony_cistatic int ice_ptp_init_work(struct ice_pf *pf, struct ice_ptp *ptp) 266162306a36Sopenharmony_ci{ 266262306a36Sopenharmony_ci struct kthread_worker *kworker; 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci /* Initialize work functions */ 266562306a36Sopenharmony_ci kthread_init_delayed_work(&ptp->work, ice_ptp_periodic_work); 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci /* Allocate a kworker for handling work required for the ports 266862306a36Sopenharmony_ci * connected to the PTP hardware clock. 266962306a36Sopenharmony_ci */ 267062306a36Sopenharmony_ci kworker = kthread_create_worker(0, "ice-ptp-%s", 267162306a36Sopenharmony_ci dev_name(ice_pf_to_dev(pf))); 267262306a36Sopenharmony_ci if (IS_ERR(kworker)) 267362306a36Sopenharmony_ci return PTR_ERR(kworker); 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci ptp->kworker = kworker; 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci /* Start periodic work going */ 267862306a36Sopenharmony_ci kthread_queue_delayed_work(ptp->kworker, &ptp->work, 0); 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci return 0; 268162306a36Sopenharmony_ci} 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_ci/** 268462306a36Sopenharmony_ci * ice_ptp_init_port - Initialize PTP port structure 268562306a36Sopenharmony_ci * @pf: Board private structure 268662306a36Sopenharmony_ci * @ptp_port: PTP port structure 268762306a36Sopenharmony_ci */ 268862306a36Sopenharmony_cistatic int ice_ptp_init_port(struct ice_pf *pf, struct ice_ptp_port *ptp_port) 268962306a36Sopenharmony_ci{ 269062306a36Sopenharmony_ci mutex_init(&ptp_port->ps_lock); 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci if (ice_is_e810(&pf->hw)) 269362306a36Sopenharmony_ci return ice_ptp_init_tx_e810(pf, &ptp_port->tx); 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci kthread_init_delayed_work(&ptp_port->ov_work, 269662306a36Sopenharmony_ci ice_ptp_wait_for_offsets); 269762306a36Sopenharmony_ci return ice_ptp_init_tx_e822(pf, &ptp_port->tx, ptp_port->port_num); 269862306a36Sopenharmony_ci} 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci/** 270162306a36Sopenharmony_ci * ice_ptp_init - Initialize PTP hardware clock support 270262306a36Sopenharmony_ci * @pf: Board private structure 270362306a36Sopenharmony_ci * 270462306a36Sopenharmony_ci * Set up the device for interacting with the PTP hardware clock for all 270562306a36Sopenharmony_ci * functions, both the function that owns the clock hardware, and the 270662306a36Sopenharmony_ci * functions connected to the clock hardware. 270762306a36Sopenharmony_ci * 270862306a36Sopenharmony_ci * The clock owner will allocate and register a ptp_clock with the 270962306a36Sopenharmony_ci * PTP_1588_CLOCK infrastructure. All functions allocate a kthread and work 271062306a36Sopenharmony_ci * items used for asynchronous work such as Tx timestamps and periodic work. 271162306a36Sopenharmony_ci */ 271262306a36Sopenharmony_civoid ice_ptp_init(struct ice_pf *pf) 271362306a36Sopenharmony_ci{ 271462306a36Sopenharmony_ci struct ice_ptp *ptp = &pf->ptp; 271562306a36Sopenharmony_ci struct ice_hw *hw = &pf->hw; 271662306a36Sopenharmony_ci int err; 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci /* If this function owns the clock hardware, it must allocate and 271962306a36Sopenharmony_ci * configure the PTP clock device to represent it. 272062306a36Sopenharmony_ci */ 272162306a36Sopenharmony_ci if (hw->func_caps.ts_func_info.src_tmr_owned) { 272262306a36Sopenharmony_ci err = ice_ptp_init_owner(pf); 272362306a36Sopenharmony_ci if (err) 272462306a36Sopenharmony_ci goto err; 272562306a36Sopenharmony_ci } 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci ptp->port.port_num = hw->pf_id; 272862306a36Sopenharmony_ci err = ice_ptp_init_port(pf, &ptp->port); 272962306a36Sopenharmony_ci if (err) 273062306a36Sopenharmony_ci goto err; 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci /* Start the PHY timestamping block */ 273362306a36Sopenharmony_ci ice_ptp_reset_phy_timestamping(pf); 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci set_bit(ICE_FLAG_PTP, pf->flags); 273662306a36Sopenharmony_ci err = ice_ptp_init_work(pf, ptp); 273762306a36Sopenharmony_ci if (err) 273862306a36Sopenharmony_ci goto err; 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci dev_info(ice_pf_to_dev(pf), "PTP init successful\n"); 274162306a36Sopenharmony_ci return; 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_cierr: 274462306a36Sopenharmony_ci /* If we registered a PTP clock, release it */ 274562306a36Sopenharmony_ci if (pf->ptp.clock) { 274662306a36Sopenharmony_ci ptp_clock_unregister(ptp->clock); 274762306a36Sopenharmony_ci pf->ptp.clock = NULL; 274862306a36Sopenharmony_ci } 274962306a36Sopenharmony_ci clear_bit(ICE_FLAG_PTP, pf->flags); 275062306a36Sopenharmony_ci dev_err(ice_pf_to_dev(pf), "PTP failed %d\n", err); 275162306a36Sopenharmony_ci} 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci/** 275462306a36Sopenharmony_ci * ice_ptp_release - Disable the driver/HW support and unregister the clock 275562306a36Sopenharmony_ci * @pf: Board private structure 275662306a36Sopenharmony_ci * 275762306a36Sopenharmony_ci * This function handles the cleanup work required from the initialization by 275862306a36Sopenharmony_ci * clearing out the important information and unregistering the clock 275962306a36Sopenharmony_ci */ 276062306a36Sopenharmony_civoid ice_ptp_release(struct ice_pf *pf) 276162306a36Sopenharmony_ci{ 276262306a36Sopenharmony_ci if (!test_bit(ICE_FLAG_PTP, pf->flags)) 276362306a36Sopenharmony_ci return; 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ci /* Disable timestamping for both Tx and Rx */ 276662306a36Sopenharmony_ci ice_ptp_cfg_timestamp(pf, false); 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx); 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci clear_bit(ICE_FLAG_PTP, pf->flags); 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci kthread_cancel_delayed_work_sync(&pf->ptp.work); 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci ice_ptp_port_phy_stop(&pf->ptp.port); 277562306a36Sopenharmony_ci mutex_destroy(&pf->ptp.port.ps_lock); 277662306a36Sopenharmony_ci if (pf->ptp.kworker) { 277762306a36Sopenharmony_ci kthread_destroy_worker(pf->ptp.kworker); 277862306a36Sopenharmony_ci pf->ptp.kworker = NULL; 277962306a36Sopenharmony_ci } 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci if (!pf->ptp.clock) 278262306a36Sopenharmony_ci return; 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci /* Disable periodic outputs */ 278562306a36Sopenharmony_ci ice_ptp_disable_all_clkout(pf); 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci ice_clear_ptp_clock_index(pf); 278862306a36Sopenharmony_ci ptp_clock_unregister(pf->ptp.clock); 278962306a36Sopenharmony_ci pf->ptp.clock = NULL; 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci dev_info(ice_pf_to_dev(pf), "Removed PTP clock\n"); 279262306a36Sopenharmony_ci} 2793