1*03cb4473SJacob Keller // SPDX-License-Identifier: GPL-2.0 2*03cb4473SJacob Keller /* Copyright (C) 2021, Intel Corporation. */ 3*03cb4473SJacob Keller 4*03cb4473SJacob Keller #include "ice_common.h" 5*03cb4473SJacob Keller #include "ice_ptp_hw.h" 6*03cb4473SJacob Keller 7*03cb4473SJacob Keller /* Low level functions for interacting with and managing the device clock used 8*03cb4473SJacob Keller * for the Precision Time Protocol. 9*03cb4473SJacob Keller * 10*03cb4473SJacob Keller * The ice hardware represents the current time using three registers: 11*03cb4473SJacob Keller * 12*03cb4473SJacob Keller * GLTSYN_TIME_H GLTSYN_TIME_L GLTSYN_TIME_R 13*03cb4473SJacob Keller * +---------------+ +---------------+ +---------------+ 14*03cb4473SJacob Keller * | 32 bits | | 32 bits | | 32 bits | 15*03cb4473SJacob Keller * +---------------+ +---------------+ +---------------+ 16*03cb4473SJacob Keller * 17*03cb4473SJacob Keller * The registers are incremented every clock tick using a 40bit increment 18*03cb4473SJacob Keller * value defined over two registers: 19*03cb4473SJacob Keller * 20*03cb4473SJacob Keller * GLTSYN_INCVAL_H GLTSYN_INCVAL_L 21*03cb4473SJacob Keller * +---------------+ +---------------+ 22*03cb4473SJacob Keller * | 8 bit s | | 32 bits | 23*03cb4473SJacob Keller * +---------------+ +---------------+ 24*03cb4473SJacob Keller * 25*03cb4473SJacob Keller * The increment value is added to the GLSTYN_TIME_R and GLSTYN_TIME_L 26*03cb4473SJacob Keller * registers every clock source tick. Depending on the specific device 27*03cb4473SJacob Keller * configuration, the clock source frequency could be one of a number of 28*03cb4473SJacob Keller * values. 29*03cb4473SJacob Keller * 30*03cb4473SJacob Keller * For E810 devices, the increment frequency is 812.5 MHz 31*03cb4473SJacob Keller * 32*03cb4473SJacob Keller * The hardware captures timestamps in the PHY for incoming packets, and for 33*03cb4473SJacob Keller * outgoing packets on request. To support this, the PHY maintains a timer 34*03cb4473SJacob Keller * that matches the lower 64 bits of the global source timer. 35*03cb4473SJacob Keller * 36*03cb4473SJacob Keller * In order to ensure that the PHY timers and the source timer are equivalent, 37*03cb4473SJacob Keller * shadow registers are used to prepare the desired initial values. A special 38*03cb4473SJacob Keller * sync command is issued to trigger copying from the shadow registers into 39*03cb4473SJacob Keller * the appropriate source and PHY registers simultaneously. 40*03cb4473SJacob Keller */ 41*03cb4473SJacob Keller 42*03cb4473SJacob Keller /** 43*03cb4473SJacob Keller * ice_get_ptp_src_clock_index - determine source clock index 44*03cb4473SJacob Keller * @hw: pointer to HW struct 45*03cb4473SJacob Keller * 46*03cb4473SJacob Keller * Determine the source clock index currently in use, based on device 47*03cb4473SJacob Keller * capabilities reported during initialization. 48*03cb4473SJacob Keller */ 49*03cb4473SJacob Keller u8 ice_get_ptp_src_clock_index(struct ice_hw *hw) 50*03cb4473SJacob Keller { 51*03cb4473SJacob Keller return hw->func_caps.ts_func_info.tmr_index_assoc; 52*03cb4473SJacob Keller } 53*03cb4473SJacob Keller 54*03cb4473SJacob Keller /* E810 functions 55*03cb4473SJacob Keller * 56*03cb4473SJacob Keller * The following functions operate on the E810 series devices which use 57*03cb4473SJacob Keller * a separate external PHY. 58*03cb4473SJacob Keller */ 59*03cb4473SJacob Keller 60*03cb4473SJacob Keller /** 61*03cb4473SJacob Keller * ice_read_phy_reg_e810 - Read register from external PHY on E810 62*03cb4473SJacob Keller * @hw: pointer to the HW struct 63*03cb4473SJacob Keller * @addr: the address to read from 64*03cb4473SJacob Keller * @val: On return, the value read from the PHY 65*03cb4473SJacob Keller * 66*03cb4473SJacob Keller * Read a register from the external PHY on the E810 device. 67*03cb4473SJacob Keller */ 68*03cb4473SJacob Keller static int ice_read_phy_reg_e810(struct ice_hw *hw, u32 addr, u32 *val) 69*03cb4473SJacob Keller { 70*03cb4473SJacob Keller struct ice_sbq_msg_input msg = {0}; 71*03cb4473SJacob Keller int status; 72*03cb4473SJacob Keller 73*03cb4473SJacob Keller msg.msg_addr_low = lower_16_bits(addr); 74*03cb4473SJacob Keller msg.msg_addr_high = upper_16_bits(addr); 75*03cb4473SJacob Keller msg.opcode = ice_sbq_msg_rd; 76*03cb4473SJacob Keller msg.dest_dev = rmn_0; 77*03cb4473SJacob Keller 78*03cb4473SJacob Keller status = ice_sbq_rw_reg(hw, &msg); 79*03cb4473SJacob Keller if (status) { 80*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, status %d\n", 81*03cb4473SJacob Keller status); 82*03cb4473SJacob Keller return status; 83*03cb4473SJacob Keller } 84*03cb4473SJacob Keller 85*03cb4473SJacob Keller *val = msg.data; 86*03cb4473SJacob Keller 87*03cb4473SJacob Keller return 0; 88*03cb4473SJacob Keller } 89*03cb4473SJacob Keller 90*03cb4473SJacob Keller /** 91*03cb4473SJacob Keller * ice_write_phy_reg_e810 - Write register on external PHY on E810 92*03cb4473SJacob Keller * @hw: pointer to the HW struct 93*03cb4473SJacob Keller * @addr: the address to writem to 94*03cb4473SJacob Keller * @val: the value to write to the PHY 95*03cb4473SJacob Keller * 96*03cb4473SJacob Keller * Write a value to a register of the external PHY on the E810 device. 97*03cb4473SJacob Keller */ 98*03cb4473SJacob Keller static int ice_write_phy_reg_e810(struct ice_hw *hw, u32 addr, u32 val) 99*03cb4473SJacob Keller { 100*03cb4473SJacob Keller struct ice_sbq_msg_input msg = {0}; 101*03cb4473SJacob Keller int status; 102*03cb4473SJacob Keller 103*03cb4473SJacob Keller msg.msg_addr_low = lower_16_bits(addr); 104*03cb4473SJacob Keller msg.msg_addr_high = upper_16_bits(addr); 105*03cb4473SJacob Keller msg.opcode = ice_sbq_msg_wr; 106*03cb4473SJacob Keller msg.dest_dev = rmn_0; 107*03cb4473SJacob Keller msg.data = val; 108*03cb4473SJacob Keller 109*03cb4473SJacob Keller status = ice_sbq_rw_reg(hw, &msg); 110*03cb4473SJacob Keller if (status) { 111*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, status %d\n", 112*03cb4473SJacob Keller status); 113*03cb4473SJacob Keller return status; 114*03cb4473SJacob Keller } 115*03cb4473SJacob Keller 116*03cb4473SJacob Keller return 0; 117*03cb4473SJacob Keller } 118*03cb4473SJacob Keller 119*03cb4473SJacob Keller /** 120*03cb4473SJacob Keller * ice_read_phy_tstamp_e810 - Read a PHY timestamp out of the external PHY 121*03cb4473SJacob Keller * @hw: pointer to the HW struct 122*03cb4473SJacob Keller * @lport: the lport to read from 123*03cb4473SJacob Keller * @idx: the timestamp index to read 124*03cb4473SJacob Keller * @tstamp: on return, the 40bit timestamp value 125*03cb4473SJacob Keller * 126*03cb4473SJacob Keller * Read a 40bit timestamp value out of the timestamp block of the external PHY 127*03cb4473SJacob Keller * on the E810 device. 128*03cb4473SJacob Keller */ 129*03cb4473SJacob Keller static int 130*03cb4473SJacob Keller ice_read_phy_tstamp_e810(struct ice_hw *hw, u8 lport, u8 idx, u64 *tstamp) 131*03cb4473SJacob Keller { 132*03cb4473SJacob Keller u32 lo_addr, hi_addr, lo, hi; 133*03cb4473SJacob Keller int status; 134*03cb4473SJacob Keller 135*03cb4473SJacob Keller lo_addr = TS_EXT(LOW_TX_MEMORY_BANK_START, lport, idx); 136*03cb4473SJacob Keller hi_addr = TS_EXT(HIGH_TX_MEMORY_BANK_START, lport, idx); 137*03cb4473SJacob Keller 138*03cb4473SJacob Keller status = ice_read_phy_reg_e810(hw, lo_addr, &lo); 139*03cb4473SJacob Keller if (status) { 140*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to read low PTP timestamp register, status %d\n", 141*03cb4473SJacob Keller status); 142*03cb4473SJacob Keller return status; 143*03cb4473SJacob Keller } 144*03cb4473SJacob Keller 145*03cb4473SJacob Keller status = ice_read_phy_reg_e810(hw, hi_addr, &hi); 146*03cb4473SJacob Keller if (status) { 147*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to read high PTP timestamp register, status %d\n", 148*03cb4473SJacob Keller status); 149*03cb4473SJacob Keller return status; 150*03cb4473SJacob Keller } 151*03cb4473SJacob Keller 152*03cb4473SJacob Keller /* For E810 devices, the timestamp is reported with the lower 32 bits 153*03cb4473SJacob Keller * in the low register, and the upper 8 bits in the high register. 154*03cb4473SJacob Keller */ 155*03cb4473SJacob Keller *tstamp = ((u64)hi) << TS_HIGH_S | ((u64)lo & TS_LOW_M); 156*03cb4473SJacob Keller 157*03cb4473SJacob Keller return 0; 158*03cb4473SJacob Keller } 159*03cb4473SJacob Keller 160*03cb4473SJacob Keller /** 161*03cb4473SJacob Keller * ice_clear_phy_tstamp_e810 - Clear a timestamp from the external PHY 162*03cb4473SJacob Keller * @hw: pointer to the HW struct 163*03cb4473SJacob Keller * @lport: the lport to read from 164*03cb4473SJacob Keller * @idx: the timestamp index to reset 165*03cb4473SJacob Keller * 166*03cb4473SJacob Keller * Clear a timestamp, resetting its valid bit, from the timestamp block of the 167*03cb4473SJacob Keller * external PHY on the E810 device. 168*03cb4473SJacob Keller */ 169*03cb4473SJacob Keller static int ice_clear_phy_tstamp_e810(struct ice_hw *hw, u8 lport, u8 idx) 170*03cb4473SJacob Keller { 171*03cb4473SJacob Keller u32 lo_addr, hi_addr; 172*03cb4473SJacob Keller int status; 173*03cb4473SJacob Keller 174*03cb4473SJacob Keller lo_addr = TS_EXT(LOW_TX_MEMORY_BANK_START, lport, idx); 175*03cb4473SJacob Keller hi_addr = TS_EXT(HIGH_TX_MEMORY_BANK_START, lport, idx); 176*03cb4473SJacob Keller 177*03cb4473SJacob Keller status = ice_write_phy_reg_e810(hw, lo_addr, 0); 178*03cb4473SJacob Keller if (status) { 179*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to clear low PTP timestamp register, status %d\n", 180*03cb4473SJacob Keller status); 181*03cb4473SJacob Keller return status; 182*03cb4473SJacob Keller } 183*03cb4473SJacob Keller 184*03cb4473SJacob Keller status = ice_write_phy_reg_e810(hw, hi_addr, 0); 185*03cb4473SJacob Keller if (status) { 186*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to clear high PTP timestamp register, status %d\n", 187*03cb4473SJacob Keller status); 188*03cb4473SJacob Keller return status; 189*03cb4473SJacob Keller } 190*03cb4473SJacob Keller 191*03cb4473SJacob Keller return 0; 192*03cb4473SJacob Keller } 193*03cb4473SJacob Keller 194*03cb4473SJacob Keller /** 195*03cb4473SJacob Keller * ice_ptp_init_phy_e810 - Enable PTP function on the external PHY 196*03cb4473SJacob Keller * @hw: pointer to HW struct 197*03cb4473SJacob Keller * 198*03cb4473SJacob Keller * Enable the timesync PTP functionality for the external PHY connected to 199*03cb4473SJacob Keller * this function. 200*03cb4473SJacob Keller */ 201*03cb4473SJacob Keller int ice_ptp_init_phy_e810(struct ice_hw *hw) 202*03cb4473SJacob Keller { 203*03cb4473SJacob Keller int status; 204*03cb4473SJacob Keller u8 tmr_idx; 205*03cb4473SJacob Keller 206*03cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 207*03cb4473SJacob Keller status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_ENA(tmr_idx), 208*03cb4473SJacob Keller GLTSYN_ENA_TSYN_ENA_M); 209*03cb4473SJacob Keller if (status) 210*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "PTP failed in ena_phy_time_syn %d\n", 211*03cb4473SJacob Keller status); 212*03cb4473SJacob Keller 213*03cb4473SJacob Keller return status; 214*03cb4473SJacob Keller } 215*03cb4473SJacob Keller 216*03cb4473SJacob Keller /** 217*03cb4473SJacob Keller * ice_ptp_prep_phy_time_e810 - Prepare PHY port with initial time 218*03cb4473SJacob Keller * @hw: Board private structure 219*03cb4473SJacob Keller * @time: Time to initialize the PHY port clock to 220*03cb4473SJacob Keller * 221*03cb4473SJacob Keller * Program the PHY port ETH_GLTSYN_SHTIME registers in preparation setting the 222*03cb4473SJacob Keller * initial clock time. The time will not actually be programmed until the 223*03cb4473SJacob Keller * driver issues an INIT_TIME command. 224*03cb4473SJacob Keller * 225*03cb4473SJacob Keller * The time value is the upper 32 bits of the PHY timer, usually in units of 226*03cb4473SJacob Keller * nominal nanoseconds. 227*03cb4473SJacob Keller */ 228*03cb4473SJacob Keller static int ice_ptp_prep_phy_time_e810(struct ice_hw *hw, u32 time) 229*03cb4473SJacob Keller { 230*03cb4473SJacob Keller int status; 231*03cb4473SJacob Keller u8 tmr_idx; 232*03cb4473SJacob Keller 233*03cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 234*03cb4473SJacob Keller status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHTIME_0(tmr_idx), 0); 235*03cb4473SJacob Keller if (status) { 236*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write SHTIME_0, status %d\n", 237*03cb4473SJacob Keller status); 238*03cb4473SJacob Keller return status; 239*03cb4473SJacob Keller } 240*03cb4473SJacob Keller 241*03cb4473SJacob Keller status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHTIME_L(tmr_idx), time); 242*03cb4473SJacob Keller if (status) { 243*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write SHTIME_L, status %d\n", 244*03cb4473SJacob Keller status); 245*03cb4473SJacob Keller return status; 246*03cb4473SJacob Keller } 247*03cb4473SJacob Keller 248*03cb4473SJacob Keller return 0; 249*03cb4473SJacob Keller } 250*03cb4473SJacob Keller 251*03cb4473SJacob Keller /** 252*03cb4473SJacob Keller * ice_ptp_prep_phy_adj_e810 - Prep PHY port for a time adjustment 253*03cb4473SJacob Keller * @hw: pointer to HW struct 254*03cb4473SJacob Keller * @adj: adjustment value to program 255*03cb4473SJacob Keller * 256*03cb4473SJacob Keller * Prepare the PHY port for an atomic adjustment by programming the PHY 257*03cb4473SJacob Keller * ETH_GLTSYN_SHADJ_L and ETH_GLTSYN_SHADJ_H registers. The actual adjustment 258*03cb4473SJacob Keller * is completed by issuing an ADJ_TIME sync command. 259*03cb4473SJacob Keller * 260*03cb4473SJacob Keller * The adjustment value only contains the portion used for the upper 32bits of 261*03cb4473SJacob Keller * the PHY timer, usually in units of nominal nanoseconds. Negative 262*03cb4473SJacob Keller * adjustments are supported using 2s complement arithmetic. 263*03cb4473SJacob Keller */ 264*03cb4473SJacob Keller static int ice_ptp_prep_phy_adj_e810(struct ice_hw *hw, s32 adj) 265*03cb4473SJacob Keller { 266*03cb4473SJacob Keller int status; 267*03cb4473SJacob Keller u8 tmr_idx; 268*03cb4473SJacob Keller 269*03cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 270*03cb4473SJacob Keller 271*03cb4473SJacob Keller /* Adjustments are represented as signed 2's complement values in 272*03cb4473SJacob Keller * nanoseconds. Sub-nanosecond adjustment is not supported. 273*03cb4473SJacob Keller */ 274*03cb4473SJacob Keller status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_L(tmr_idx), 0); 275*03cb4473SJacob Keller if (status) { 276*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write adj to PHY SHADJ_L, status %d\n", 277*03cb4473SJacob Keller status); 278*03cb4473SJacob Keller return status; 279*03cb4473SJacob Keller } 280*03cb4473SJacob Keller 281*03cb4473SJacob Keller status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_H(tmr_idx), adj); 282*03cb4473SJacob Keller if (status) { 283*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write adj to PHY SHADJ_H, status %d\n", 284*03cb4473SJacob Keller status); 285*03cb4473SJacob Keller return status; 286*03cb4473SJacob Keller } 287*03cb4473SJacob Keller 288*03cb4473SJacob Keller return 0; 289*03cb4473SJacob Keller } 290*03cb4473SJacob Keller 291*03cb4473SJacob Keller /** 292*03cb4473SJacob Keller * ice_ptp_prep_phy_incval_e810 - Prep PHY port increment value change 293*03cb4473SJacob Keller * @hw: pointer to HW struct 294*03cb4473SJacob Keller * @incval: The new 40bit increment value to prepare 295*03cb4473SJacob Keller * 296*03cb4473SJacob Keller * Prepare the PHY port for a new increment value by programming the PHY 297*03cb4473SJacob Keller * ETH_GLTSYN_SHADJ_L and ETH_GLTSYN_SHADJ_H registers. The actual change is 298*03cb4473SJacob Keller * completed by issuing an INIT_INCVAL command. 299*03cb4473SJacob Keller */ 300*03cb4473SJacob Keller static int ice_ptp_prep_phy_incval_e810(struct ice_hw *hw, u64 incval) 301*03cb4473SJacob Keller { 302*03cb4473SJacob Keller u32 high, low; 303*03cb4473SJacob Keller int status; 304*03cb4473SJacob Keller u8 tmr_idx; 305*03cb4473SJacob Keller 306*03cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 307*03cb4473SJacob Keller low = lower_32_bits(incval); 308*03cb4473SJacob Keller high = upper_32_bits(incval); 309*03cb4473SJacob Keller 310*03cb4473SJacob Keller status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_L(tmr_idx), low); 311*03cb4473SJacob Keller if (status) { 312*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write incval to PHY SHADJ_L, status %d\n", 313*03cb4473SJacob Keller status); 314*03cb4473SJacob Keller return status; 315*03cb4473SJacob Keller } 316*03cb4473SJacob Keller 317*03cb4473SJacob Keller status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_H(tmr_idx), high); 318*03cb4473SJacob Keller if (status) { 319*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write incval PHY SHADJ_H, status %d\n", 320*03cb4473SJacob Keller status); 321*03cb4473SJacob Keller return status; 322*03cb4473SJacob Keller } 323*03cb4473SJacob Keller 324*03cb4473SJacob Keller return 0; 325*03cb4473SJacob Keller } 326*03cb4473SJacob Keller 327*03cb4473SJacob Keller /** 328*03cb4473SJacob Keller * ice_ptp_port_cmd_e810 - Prepare all external PHYs for a timer command 329*03cb4473SJacob Keller * @hw: pointer to HW struct 330*03cb4473SJacob Keller * @cmd: Command to be sent to the port 331*03cb4473SJacob Keller * 332*03cb4473SJacob Keller * Prepare the external PHYs connected to this device for a timer sync 333*03cb4473SJacob Keller * command. 334*03cb4473SJacob Keller */ 335*03cb4473SJacob Keller static int ice_ptp_port_cmd_e810(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd) 336*03cb4473SJacob Keller { 337*03cb4473SJacob Keller u32 cmd_val, val; 338*03cb4473SJacob Keller int status; 339*03cb4473SJacob Keller 340*03cb4473SJacob Keller switch (cmd) { 341*03cb4473SJacob Keller case INIT_TIME: 342*03cb4473SJacob Keller cmd_val = GLTSYN_CMD_INIT_TIME; 343*03cb4473SJacob Keller break; 344*03cb4473SJacob Keller case INIT_INCVAL: 345*03cb4473SJacob Keller cmd_val = GLTSYN_CMD_INIT_INCVAL; 346*03cb4473SJacob Keller break; 347*03cb4473SJacob Keller case ADJ_TIME: 348*03cb4473SJacob Keller cmd_val = GLTSYN_CMD_ADJ_TIME; 349*03cb4473SJacob Keller break; 350*03cb4473SJacob Keller case READ_TIME: 351*03cb4473SJacob Keller cmd_val = GLTSYN_CMD_READ_TIME; 352*03cb4473SJacob Keller break; 353*03cb4473SJacob Keller case ADJ_TIME_AT_TIME: 354*03cb4473SJacob Keller cmd_val = GLTSYN_CMD_ADJ_INIT_TIME; 355*03cb4473SJacob Keller break; 356*03cb4473SJacob Keller } 357*03cb4473SJacob Keller 358*03cb4473SJacob Keller /* Read, modify, write */ 359*03cb4473SJacob Keller status = ice_read_phy_reg_e810(hw, ETH_GLTSYN_CMD, &val); 360*03cb4473SJacob Keller if (status) { 361*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to read GLTSYN_CMD, status %d\n", status); 362*03cb4473SJacob Keller return status; 363*03cb4473SJacob Keller } 364*03cb4473SJacob Keller 365*03cb4473SJacob Keller /* Modify necessary bits only and perform write */ 366*03cb4473SJacob Keller val &= ~TS_CMD_MASK_E810; 367*03cb4473SJacob Keller val |= cmd_val; 368*03cb4473SJacob Keller 369*03cb4473SJacob Keller status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_CMD, val); 370*03cb4473SJacob Keller if (status) { 371*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write back GLTSYN_CMD, status %d\n", status); 372*03cb4473SJacob Keller return status; 373*03cb4473SJacob Keller } 374*03cb4473SJacob Keller 375*03cb4473SJacob Keller return 0; 376*03cb4473SJacob Keller } 377*03cb4473SJacob Keller 378*03cb4473SJacob Keller /* Device agnostic functions 379*03cb4473SJacob Keller * 380*03cb4473SJacob Keller * The following functions implement useful behavior to hide the differences 381*03cb4473SJacob Keller * between E810 and other devices. They call the device-specific 382*03cb4473SJacob Keller * implementations where necessary. 383*03cb4473SJacob Keller * 384*03cb4473SJacob Keller * Currently, the driver only supports E810, but future work will enable 385*03cb4473SJacob Keller * support for E822-based devices. 386*03cb4473SJacob Keller */ 387*03cb4473SJacob Keller 388*03cb4473SJacob Keller /** 389*03cb4473SJacob Keller * ice_ptp_lock - Acquire PTP global semaphore register lock 390*03cb4473SJacob Keller * @hw: pointer to the HW struct 391*03cb4473SJacob Keller * 392*03cb4473SJacob Keller * Acquire the global PTP hardware semaphore lock. Returns true if the lock 393*03cb4473SJacob Keller * was acquired, false otherwise. 394*03cb4473SJacob Keller * 395*03cb4473SJacob Keller * The PFTSYN_SEM register sets the busy bit on read, returning the previous 396*03cb4473SJacob Keller * value. If software sees the busy bit cleared, this means that this function 397*03cb4473SJacob Keller * acquired the lock (and the busy bit is now set). If software sees the busy 398*03cb4473SJacob Keller * bit set, it means that another function acquired the lock. 399*03cb4473SJacob Keller * 400*03cb4473SJacob Keller * Software must clear the busy bit with a write to release the lock for other 401*03cb4473SJacob Keller * functions when done. 402*03cb4473SJacob Keller */ 403*03cb4473SJacob Keller bool ice_ptp_lock(struct ice_hw *hw) 404*03cb4473SJacob Keller { 405*03cb4473SJacob Keller u32 hw_lock; 406*03cb4473SJacob Keller int i; 407*03cb4473SJacob Keller 408*03cb4473SJacob Keller #define MAX_TRIES 5 409*03cb4473SJacob Keller 410*03cb4473SJacob Keller for (i = 0; i < MAX_TRIES; i++) { 411*03cb4473SJacob Keller hw_lock = rd32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id)); 412*03cb4473SJacob Keller hw_lock = hw_lock & PFTSYN_SEM_BUSY_M; 413*03cb4473SJacob Keller if (hw_lock) { 414*03cb4473SJacob Keller /* Somebody is holding the lock */ 415*03cb4473SJacob Keller usleep_range(10000, 20000); 416*03cb4473SJacob Keller continue; 417*03cb4473SJacob Keller } else { 418*03cb4473SJacob Keller break; 419*03cb4473SJacob Keller } 420*03cb4473SJacob Keller } 421*03cb4473SJacob Keller 422*03cb4473SJacob Keller return !hw_lock; 423*03cb4473SJacob Keller } 424*03cb4473SJacob Keller 425*03cb4473SJacob Keller /** 426*03cb4473SJacob Keller * ice_ptp_unlock - Release PTP global semaphore register lock 427*03cb4473SJacob Keller * @hw: pointer to the HW struct 428*03cb4473SJacob Keller * 429*03cb4473SJacob Keller * Release the global PTP hardware semaphore lock. This is done by writing to 430*03cb4473SJacob Keller * the PFTSYN_SEM register. 431*03cb4473SJacob Keller */ 432*03cb4473SJacob Keller void ice_ptp_unlock(struct ice_hw *hw) 433*03cb4473SJacob Keller { 434*03cb4473SJacob Keller wr32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id), 0); 435*03cb4473SJacob Keller } 436*03cb4473SJacob Keller 437*03cb4473SJacob Keller /** 438*03cb4473SJacob Keller * ice_ptp_src_cmd - Prepare source timer for a timer command 439*03cb4473SJacob Keller * @hw: pointer to HW structure 440*03cb4473SJacob Keller * @cmd: Timer command 441*03cb4473SJacob Keller * 442*03cb4473SJacob Keller * Prepare the source timer for an upcoming timer sync command. 443*03cb4473SJacob Keller */ 444*03cb4473SJacob Keller static void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd) 445*03cb4473SJacob Keller { 446*03cb4473SJacob Keller u32 cmd_val; 447*03cb4473SJacob Keller u8 tmr_idx; 448*03cb4473SJacob Keller 449*03cb4473SJacob Keller tmr_idx = ice_get_ptp_src_clock_index(hw); 450*03cb4473SJacob Keller cmd_val = tmr_idx << SEL_CPK_SRC; 451*03cb4473SJacob Keller 452*03cb4473SJacob Keller switch (cmd) { 453*03cb4473SJacob Keller case INIT_TIME: 454*03cb4473SJacob Keller cmd_val |= GLTSYN_CMD_INIT_TIME; 455*03cb4473SJacob Keller break; 456*03cb4473SJacob Keller case INIT_INCVAL: 457*03cb4473SJacob Keller cmd_val |= GLTSYN_CMD_INIT_INCVAL; 458*03cb4473SJacob Keller break; 459*03cb4473SJacob Keller case ADJ_TIME: 460*03cb4473SJacob Keller cmd_val |= GLTSYN_CMD_ADJ_TIME; 461*03cb4473SJacob Keller break; 462*03cb4473SJacob Keller case ADJ_TIME_AT_TIME: 463*03cb4473SJacob Keller cmd_val |= GLTSYN_CMD_ADJ_INIT_TIME; 464*03cb4473SJacob Keller break; 465*03cb4473SJacob Keller case READ_TIME: 466*03cb4473SJacob Keller cmd_val |= GLTSYN_CMD_READ_TIME; 467*03cb4473SJacob Keller break; 468*03cb4473SJacob Keller } 469*03cb4473SJacob Keller 470*03cb4473SJacob Keller wr32(hw, GLTSYN_CMD, cmd_val); 471*03cb4473SJacob Keller } 472*03cb4473SJacob Keller 473*03cb4473SJacob Keller /** 474*03cb4473SJacob Keller * ice_ptp_tmr_cmd - Prepare and trigger a timer sync command 475*03cb4473SJacob Keller * @hw: pointer to HW struct 476*03cb4473SJacob Keller * @cmd: the command to issue 477*03cb4473SJacob Keller * 478*03cb4473SJacob Keller * Prepare the source timer and PHY timers and then trigger the requested 479*03cb4473SJacob Keller * command. This causes the shadow registers previously written in preparation 480*03cb4473SJacob Keller * for the command to be synchronously applied to both the source and PHY 481*03cb4473SJacob Keller * timers. 482*03cb4473SJacob Keller */ 483*03cb4473SJacob Keller static int ice_ptp_tmr_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd) 484*03cb4473SJacob Keller { 485*03cb4473SJacob Keller int status; 486*03cb4473SJacob Keller 487*03cb4473SJacob Keller /* First, prepare the source timer */ 488*03cb4473SJacob Keller ice_ptp_src_cmd(hw, cmd); 489*03cb4473SJacob Keller 490*03cb4473SJacob Keller /* Next, prepare the ports */ 491*03cb4473SJacob Keller status = ice_ptp_port_cmd_e810(hw, cmd); 492*03cb4473SJacob Keller if (status) { 493*03cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to prepare PHY ports for timer command %u, status %d\n", 494*03cb4473SJacob Keller cmd, status); 495*03cb4473SJacob Keller return status; 496*03cb4473SJacob Keller } 497*03cb4473SJacob Keller 498*03cb4473SJacob Keller /* Write the sync command register to drive both source and PHY timer commands 499*03cb4473SJacob Keller * synchronously 500*03cb4473SJacob Keller */ 501*03cb4473SJacob Keller wr32(hw, GLTSYN_CMD_SYNC, SYNC_EXEC_CMD); 502*03cb4473SJacob Keller 503*03cb4473SJacob Keller return 0; 504*03cb4473SJacob Keller } 505*03cb4473SJacob Keller 506*03cb4473SJacob Keller /** 507*03cb4473SJacob Keller * ice_ptp_init_time - Initialize device time to provided value 508*03cb4473SJacob Keller * @hw: pointer to HW struct 509*03cb4473SJacob Keller * @time: 64bits of time (GLTSYN_TIME_L and GLTSYN_TIME_H) 510*03cb4473SJacob Keller * 511*03cb4473SJacob Keller * Initialize the device to the specified time provided. This requires a three 512*03cb4473SJacob Keller * step process: 513*03cb4473SJacob Keller * 514*03cb4473SJacob Keller * 1) write the new init time to the source timer shadow registers 515*03cb4473SJacob Keller * 2) write the new init time to the PHY timer shadow registers 516*03cb4473SJacob Keller * 3) issue an init_time timer command to synchronously switch both the source 517*03cb4473SJacob Keller * and port timers to the new init time value at the next clock cycle. 518*03cb4473SJacob Keller */ 519*03cb4473SJacob Keller int ice_ptp_init_time(struct ice_hw *hw, u64 time) 520*03cb4473SJacob Keller { 521*03cb4473SJacob Keller int status; 522*03cb4473SJacob Keller u8 tmr_idx; 523*03cb4473SJacob Keller 524*03cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 525*03cb4473SJacob Keller 526*03cb4473SJacob Keller /* Source timers */ 527*03cb4473SJacob Keller wr32(hw, GLTSYN_SHTIME_L(tmr_idx), lower_32_bits(time)); 528*03cb4473SJacob Keller wr32(hw, GLTSYN_SHTIME_H(tmr_idx), upper_32_bits(time)); 529*03cb4473SJacob Keller wr32(hw, GLTSYN_SHTIME_0(tmr_idx), 0); 530*03cb4473SJacob Keller 531*03cb4473SJacob Keller /* PHY timers */ 532*03cb4473SJacob Keller /* Fill Rx and Tx ports and send msg to PHY */ 533*03cb4473SJacob Keller status = ice_ptp_prep_phy_time_e810(hw, time & 0xFFFFFFFF); 534*03cb4473SJacob Keller if (status) 535*03cb4473SJacob Keller return status; 536*03cb4473SJacob Keller 537*03cb4473SJacob Keller return ice_ptp_tmr_cmd(hw, INIT_TIME); 538*03cb4473SJacob Keller } 539*03cb4473SJacob Keller 540*03cb4473SJacob Keller /** 541*03cb4473SJacob Keller * ice_ptp_write_incval - Program PHC with new increment value 542*03cb4473SJacob Keller * @hw: pointer to HW struct 543*03cb4473SJacob Keller * @incval: Source timer increment value per clock cycle 544*03cb4473SJacob Keller * 545*03cb4473SJacob Keller * Program the PHC with a new increment value. This requires a three-step 546*03cb4473SJacob Keller * process: 547*03cb4473SJacob Keller * 548*03cb4473SJacob Keller * 1) Write the increment value to the source timer shadow registers 549*03cb4473SJacob Keller * 2) Write the increment value to the PHY timer shadow registers 550*03cb4473SJacob Keller * 3) Issue an INIT_INCVAL timer command to synchronously switch both the 551*03cb4473SJacob Keller * source and port timers to the new increment value at the next clock 552*03cb4473SJacob Keller * cycle. 553*03cb4473SJacob Keller */ 554*03cb4473SJacob Keller int ice_ptp_write_incval(struct ice_hw *hw, u64 incval) 555*03cb4473SJacob Keller { 556*03cb4473SJacob Keller int status; 557*03cb4473SJacob Keller u8 tmr_idx; 558*03cb4473SJacob Keller 559*03cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 560*03cb4473SJacob Keller 561*03cb4473SJacob Keller /* Shadow Adjust */ 562*03cb4473SJacob Keller wr32(hw, GLTSYN_SHADJ_L(tmr_idx), lower_32_bits(incval)); 563*03cb4473SJacob Keller wr32(hw, GLTSYN_SHADJ_H(tmr_idx), upper_32_bits(incval)); 564*03cb4473SJacob Keller 565*03cb4473SJacob Keller status = ice_ptp_prep_phy_incval_e810(hw, incval); 566*03cb4473SJacob Keller if (status) 567*03cb4473SJacob Keller return status; 568*03cb4473SJacob Keller 569*03cb4473SJacob Keller return ice_ptp_tmr_cmd(hw, INIT_INCVAL); 570*03cb4473SJacob Keller } 571*03cb4473SJacob Keller 572*03cb4473SJacob Keller /** 573*03cb4473SJacob Keller * ice_ptp_write_incval_locked - Program new incval while holding semaphore 574*03cb4473SJacob Keller * @hw: pointer to HW struct 575*03cb4473SJacob Keller * @incval: Source timer increment value per clock cycle 576*03cb4473SJacob Keller * 577*03cb4473SJacob Keller * Program a new PHC incval while holding the PTP semaphore. 578*03cb4473SJacob Keller */ 579*03cb4473SJacob Keller int ice_ptp_write_incval_locked(struct ice_hw *hw, u64 incval) 580*03cb4473SJacob Keller { 581*03cb4473SJacob Keller int status; 582*03cb4473SJacob Keller 583*03cb4473SJacob Keller if (!ice_ptp_lock(hw)) 584*03cb4473SJacob Keller return -EBUSY; 585*03cb4473SJacob Keller 586*03cb4473SJacob Keller status = ice_ptp_write_incval(hw, incval); 587*03cb4473SJacob Keller 588*03cb4473SJacob Keller ice_ptp_unlock(hw); 589*03cb4473SJacob Keller 590*03cb4473SJacob Keller return status; 591*03cb4473SJacob Keller } 592*03cb4473SJacob Keller 593*03cb4473SJacob Keller /** 594*03cb4473SJacob Keller * ice_ptp_adj_clock - Adjust PHC clock time atomically 595*03cb4473SJacob Keller * @hw: pointer to HW struct 596*03cb4473SJacob Keller * @adj: Adjustment in nanoseconds 597*03cb4473SJacob Keller * 598*03cb4473SJacob Keller * Perform an atomic adjustment of the PHC time by the specified number of 599*03cb4473SJacob Keller * nanoseconds. This requires a three-step process: 600*03cb4473SJacob Keller * 601*03cb4473SJacob Keller * 1) Write the adjustment to the source timer shadow registers 602*03cb4473SJacob Keller * 2) Write the adjustment to the PHY timer shadow registers 603*03cb4473SJacob Keller * 3) Issue an ADJ_TIME timer command to synchronously apply the adjustment to 604*03cb4473SJacob Keller * both the source and port timers at the next clock cycle. 605*03cb4473SJacob Keller */ 606*03cb4473SJacob Keller int ice_ptp_adj_clock(struct ice_hw *hw, s32 adj) 607*03cb4473SJacob Keller { 608*03cb4473SJacob Keller int status; 609*03cb4473SJacob Keller u8 tmr_idx; 610*03cb4473SJacob Keller 611*03cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 612*03cb4473SJacob Keller 613*03cb4473SJacob Keller /* Write the desired clock adjustment into the GLTSYN_SHADJ register. 614*03cb4473SJacob Keller * For an ADJ_TIME command, this set of registers represents the value 615*03cb4473SJacob Keller * to add to the clock time. It supports subtraction by interpreting 616*03cb4473SJacob Keller * the value as a 2's complement integer. 617*03cb4473SJacob Keller */ 618*03cb4473SJacob Keller wr32(hw, GLTSYN_SHADJ_L(tmr_idx), 0); 619*03cb4473SJacob Keller wr32(hw, GLTSYN_SHADJ_H(tmr_idx), adj); 620*03cb4473SJacob Keller 621*03cb4473SJacob Keller status = ice_ptp_prep_phy_adj_e810(hw, adj); 622*03cb4473SJacob Keller if (status) 623*03cb4473SJacob Keller return status; 624*03cb4473SJacob Keller 625*03cb4473SJacob Keller return ice_ptp_tmr_cmd(hw, ADJ_TIME); 626*03cb4473SJacob Keller } 627*03cb4473SJacob Keller 628*03cb4473SJacob Keller /** 629*03cb4473SJacob Keller * ice_read_phy_tstamp - Read a PHY timestamp from the timestamo block 630*03cb4473SJacob Keller * @hw: pointer to the HW struct 631*03cb4473SJacob Keller * @block: the block to read from 632*03cb4473SJacob Keller * @idx: the timestamp index to read 633*03cb4473SJacob Keller * @tstamp: on return, the 40bit timestamp value 634*03cb4473SJacob Keller * 635*03cb4473SJacob Keller * Read a 40bit timestamp value out of the timestamp block. 636*03cb4473SJacob Keller */ 637*03cb4473SJacob Keller int ice_read_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx, u64 *tstamp) 638*03cb4473SJacob Keller { 639*03cb4473SJacob Keller return ice_read_phy_tstamp_e810(hw, block, idx, tstamp); 640*03cb4473SJacob Keller } 641*03cb4473SJacob Keller 642*03cb4473SJacob Keller /** 643*03cb4473SJacob Keller * ice_clear_phy_tstamp - Clear a timestamp from the timestamp block 644*03cb4473SJacob Keller * @hw: pointer to the HW struct 645*03cb4473SJacob Keller * @block: the block to read from 646*03cb4473SJacob Keller * @idx: the timestamp index to reset 647*03cb4473SJacob Keller * 648*03cb4473SJacob Keller * Clear a timestamp, resetting its valid bit, from the timestamp block. 649*03cb4473SJacob Keller */ 650*03cb4473SJacob Keller int ice_clear_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx) 651*03cb4473SJacob Keller { 652*03cb4473SJacob Keller return ice_clear_phy_tstamp_e810(hw, block, idx); 653*03cb4473SJacob Keller } 654