103cb4473SJacob Keller // SPDX-License-Identifier: GPL-2.0 203cb4473SJacob Keller /* Copyright (C) 2021, Intel Corporation. */ 303cb4473SJacob Keller 403cb4473SJacob Keller #include "ice_common.h" 503cb4473SJacob Keller #include "ice_ptp_hw.h" 603cb4473SJacob Keller 703cb4473SJacob Keller /* Low level functions for interacting with and managing the device clock used 803cb4473SJacob Keller * for the Precision Time Protocol. 903cb4473SJacob Keller * 1003cb4473SJacob Keller * The ice hardware represents the current time using three registers: 1103cb4473SJacob Keller * 1203cb4473SJacob Keller * GLTSYN_TIME_H GLTSYN_TIME_L GLTSYN_TIME_R 1303cb4473SJacob Keller * +---------------+ +---------------+ +---------------+ 1403cb4473SJacob Keller * | 32 bits | | 32 bits | | 32 bits | 1503cb4473SJacob Keller * +---------------+ +---------------+ +---------------+ 1603cb4473SJacob Keller * 1703cb4473SJacob Keller * The registers are incremented every clock tick using a 40bit increment 1803cb4473SJacob Keller * value defined over two registers: 1903cb4473SJacob Keller * 2003cb4473SJacob Keller * GLTSYN_INCVAL_H GLTSYN_INCVAL_L 2103cb4473SJacob Keller * +---------------+ +---------------+ 2203cb4473SJacob Keller * | 8 bit s | | 32 bits | 2303cb4473SJacob Keller * +---------------+ +---------------+ 2403cb4473SJacob Keller * 2503cb4473SJacob Keller * The increment value is added to the GLSTYN_TIME_R and GLSTYN_TIME_L 2603cb4473SJacob Keller * registers every clock source tick. Depending on the specific device 2703cb4473SJacob Keller * configuration, the clock source frequency could be one of a number of 2803cb4473SJacob Keller * values. 2903cb4473SJacob Keller * 3003cb4473SJacob Keller * For E810 devices, the increment frequency is 812.5 MHz 3103cb4473SJacob Keller * 3203cb4473SJacob Keller * The hardware captures timestamps in the PHY for incoming packets, and for 3303cb4473SJacob Keller * outgoing packets on request. To support this, the PHY maintains a timer 3403cb4473SJacob Keller * that matches the lower 64 bits of the global source timer. 3503cb4473SJacob Keller * 3603cb4473SJacob Keller * In order to ensure that the PHY timers and the source timer are equivalent, 3703cb4473SJacob Keller * shadow registers are used to prepare the desired initial values. A special 3803cb4473SJacob Keller * sync command is issued to trigger copying from the shadow registers into 3903cb4473SJacob Keller * the appropriate source and PHY registers simultaneously. 4003cb4473SJacob Keller */ 4103cb4473SJacob Keller 4203cb4473SJacob Keller /** 4303cb4473SJacob Keller * ice_get_ptp_src_clock_index - determine source clock index 4403cb4473SJacob Keller * @hw: pointer to HW struct 4503cb4473SJacob Keller * 4603cb4473SJacob Keller * Determine the source clock index currently in use, based on device 4703cb4473SJacob Keller * capabilities reported during initialization. 4803cb4473SJacob Keller */ 4903cb4473SJacob Keller u8 ice_get_ptp_src_clock_index(struct ice_hw *hw) 5003cb4473SJacob Keller { 5103cb4473SJacob Keller return hw->func_caps.ts_func_info.tmr_index_assoc; 5203cb4473SJacob Keller } 5303cb4473SJacob Keller 5403cb4473SJacob Keller /* E810 functions 5503cb4473SJacob Keller * 5603cb4473SJacob Keller * The following functions operate on the E810 series devices which use 5703cb4473SJacob Keller * a separate external PHY. 5803cb4473SJacob Keller */ 5903cb4473SJacob Keller 6003cb4473SJacob Keller /** 6103cb4473SJacob Keller * ice_read_phy_reg_e810 - Read register from external PHY on E810 6203cb4473SJacob Keller * @hw: pointer to the HW struct 6303cb4473SJacob Keller * @addr: the address to read from 6403cb4473SJacob Keller * @val: On return, the value read from the PHY 6503cb4473SJacob Keller * 6603cb4473SJacob Keller * Read a register from the external PHY on the E810 device. 6703cb4473SJacob Keller */ 6803cb4473SJacob Keller static int ice_read_phy_reg_e810(struct ice_hw *hw, u32 addr, u32 *val) 6903cb4473SJacob Keller { 7003cb4473SJacob Keller struct ice_sbq_msg_input msg = {0}; 71*39b28106SJacob Keller int err; 7203cb4473SJacob Keller 7303cb4473SJacob Keller msg.msg_addr_low = lower_16_bits(addr); 7403cb4473SJacob Keller msg.msg_addr_high = upper_16_bits(addr); 7503cb4473SJacob Keller msg.opcode = ice_sbq_msg_rd; 7603cb4473SJacob Keller msg.dest_dev = rmn_0; 7703cb4473SJacob Keller 78*39b28106SJacob Keller err = ice_sbq_rw_reg(hw, &msg); 79*39b28106SJacob Keller if (err) { 80*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n", 81*39b28106SJacob Keller err); 82*39b28106SJacob Keller return err; 8303cb4473SJacob Keller } 8403cb4473SJacob Keller 8503cb4473SJacob Keller *val = msg.data; 8603cb4473SJacob Keller 8703cb4473SJacob Keller return 0; 8803cb4473SJacob Keller } 8903cb4473SJacob Keller 9003cb4473SJacob Keller /** 9103cb4473SJacob Keller * ice_write_phy_reg_e810 - Write register on external PHY on E810 9203cb4473SJacob Keller * @hw: pointer to the HW struct 9303cb4473SJacob Keller * @addr: the address to writem to 9403cb4473SJacob Keller * @val: the value to write to the PHY 9503cb4473SJacob Keller * 9603cb4473SJacob Keller * Write a value to a register of the external PHY on the E810 device. 9703cb4473SJacob Keller */ 9803cb4473SJacob Keller static int ice_write_phy_reg_e810(struct ice_hw *hw, u32 addr, u32 val) 9903cb4473SJacob Keller { 10003cb4473SJacob Keller struct ice_sbq_msg_input msg = {0}; 101*39b28106SJacob Keller int err; 10203cb4473SJacob Keller 10303cb4473SJacob Keller msg.msg_addr_low = lower_16_bits(addr); 10403cb4473SJacob Keller msg.msg_addr_high = upper_16_bits(addr); 10503cb4473SJacob Keller msg.opcode = ice_sbq_msg_wr; 10603cb4473SJacob Keller msg.dest_dev = rmn_0; 10703cb4473SJacob Keller msg.data = val; 10803cb4473SJacob Keller 109*39b28106SJacob Keller err = ice_sbq_rw_reg(hw, &msg); 110*39b28106SJacob Keller if (err) { 111*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n", 112*39b28106SJacob Keller err); 113*39b28106SJacob Keller return err; 11403cb4473SJacob Keller } 11503cb4473SJacob Keller 11603cb4473SJacob Keller return 0; 11703cb4473SJacob Keller } 11803cb4473SJacob Keller 11903cb4473SJacob Keller /** 12003cb4473SJacob Keller * ice_read_phy_tstamp_e810 - Read a PHY timestamp out of the external PHY 12103cb4473SJacob Keller * @hw: pointer to the HW struct 12203cb4473SJacob Keller * @lport: the lport to read from 12303cb4473SJacob Keller * @idx: the timestamp index to read 12403cb4473SJacob Keller * @tstamp: on return, the 40bit timestamp value 12503cb4473SJacob Keller * 12603cb4473SJacob Keller * Read a 40bit timestamp value out of the timestamp block of the external PHY 12703cb4473SJacob Keller * on the E810 device. 12803cb4473SJacob Keller */ 12903cb4473SJacob Keller static int 13003cb4473SJacob Keller ice_read_phy_tstamp_e810(struct ice_hw *hw, u8 lport, u8 idx, u64 *tstamp) 13103cb4473SJacob Keller { 13203cb4473SJacob Keller u32 lo_addr, hi_addr, lo, hi; 133*39b28106SJacob Keller int err; 13403cb4473SJacob Keller 13503cb4473SJacob Keller lo_addr = TS_EXT(LOW_TX_MEMORY_BANK_START, lport, idx); 13603cb4473SJacob Keller hi_addr = TS_EXT(HIGH_TX_MEMORY_BANK_START, lport, idx); 13703cb4473SJacob Keller 138*39b28106SJacob Keller err = ice_read_phy_reg_e810(hw, lo_addr, &lo); 139*39b28106SJacob Keller if (err) { 140*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to read low PTP timestamp register, err %d\n", 141*39b28106SJacob Keller err); 142*39b28106SJacob Keller return err; 14303cb4473SJacob Keller } 14403cb4473SJacob Keller 145*39b28106SJacob Keller err = ice_read_phy_reg_e810(hw, hi_addr, &hi); 146*39b28106SJacob Keller if (err) { 147*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to read high PTP timestamp register, err %d\n", 148*39b28106SJacob Keller err); 149*39b28106SJacob Keller return err; 15003cb4473SJacob Keller } 15103cb4473SJacob Keller 15203cb4473SJacob Keller /* For E810 devices, the timestamp is reported with the lower 32 bits 15303cb4473SJacob Keller * in the low register, and the upper 8 bits in the high register. 15403cb4473SJacob Keller */ 15503cb4473SJacob Keller *tstamp = ((u64)hi) << TS_HIGH_S | ((u64)lo & TS_LOW_M); 15603cb4473SJacob Keller 15703cb4473SJacob Keller return 0; 15803cb4473SJacob Keller } 15903cb4473SJacob Keller 16003cb4473SJacob Keller /** 16103cb4473SJacob Keller * ice_clear_phy_tstamp_e810 - Clear a timestamp from the external PHY 16203cb4473SJacob Keller * @hw: pointer to the HW struct 16303cb4473SJacob Keller * @lport: the lport to read from 16403cb4473SJacob Keller * @idx: the timestamp index to reset 16503cb4473SJacob Keller * 16603cb4473SJacob Keller * Clear a timestamp, resetting its valid bit, from the timestamp block of the 16703cb4473SJacob Keller * external PHY on the E810 device. 16803cb4473SJacob Keller */ 16903cb4473SJacob Keller static int ice_clear_phy_tstamp_e810(struct ice_hw *hw, u8 lport, u8 idx) 17003cb4473SJacob Keller { 17103cb4473SJacob Keller u32 lo_addr, hi_addr; 172*39b28106SJacob Keller int err; 17303cb4473SJacob Keller 17403cb4473SJacob Keller lo_addr = TS_EXT(LOW_TX_MEMORY_BANK_START, lport, idx); 17503cb4473SJacob Keller hi_addr = TS_EXT(HIGH_TX_MEMORY_BANK_START, lport, idx); 17603cb4473SJacob Keller 177*39b28106SJacob Keller err = ice_write_phy_reg_e810(hw, lo_addr, 0); 178*39b28106SJacob Keller if (err) { 179*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to clear low PTP timestamp register, err %d\n", 180*39b28106SJacob Keller err); 181*39b28106SJacob Keller return err; 18203cb4473SJacob Keller } 18303cb4473SJacob Keller 184*39b28106SJacob Keller err = ice_write_phy_reg_e810(hw, hi_addr, 0); 185*39b28106SJacob Keller if (err) { 186*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to clear high PTP timestamp register, err %d\n", 187*39b28106SJacob Keller err); 188*39b28106SJacob Keller return err; 18903cb4473SJacob Keller } 19003cb4473SJacob Keller 19103cb4473SJacob Keller return 0; 19203cb4473SJacob Keller } 19303cb4473SJacob Keller 19403cb4473SJacob Keller /** 19503cb4473SJacob Keller * ice_ptp_init_phy_e810 - Enable PTP function on the external PHY 19603cb4473SJacob Keller * @hw: pointer to HW struct 19703cb4473SJacob Keller * 19803cb4473SJacob Keller * Enable the timesync PTP functionality for the external PHY connected to 19903cb4473SJacob Keller * this function. 20003cb4473SJacob Keller */ 20103cb4473SJacob Keller int ice_ptp_init_phy_e810(struct ice_hw *hw) 20203cb4473SJacob Keller { 20303cb4473SJacob Keller u8 tmr_idx; 204*39b28106SJacob Keller int err; 20503cb4473SJacob Keller 20603cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 207*39b28106SJacob Keller err = ice_write_phy_reg_e810(hw, ETH_GLTSYN_ENA(tmr_idx), 20803cb4473SJacob Keller GLTSYN_ENA_TSYN_ENA_M); 209*39b28106SJacob Keller if (err) 21003cb4473SJacob Keller ice_debug(hw, ICE_DBG_PTP, "PTP failed in ena_phy_time_syn %d\n", 211*39b28106SJacob Keller err); 21203cb4473SJacob Keller 213*39b28106SJacob Keller return err; 21403cb4473SJacob Keller } 21503cb4473SJacob Keller 21603cb4473SJacob Keller /** 21703cb4473SJacob Keller * ice_ptp_prep_phy_time_e810 - Prepare PHY port with initial time 21803cb4473SJacob Keller * @hw: Board private structure 21903cb4473SJacob Keller * @time: Time to initialize the PHY port clock to 22003cb4473SJacob Keller * 22103cb4473SJacob Keller * Program the PHY port ETH_GLTSYN_SHTIME registers in preparation setting the 22203cb4473SJacob Keller * initial clock time. The time will not actually be programmed until the 22303cb4473SJacob Keller * driver issues an INIT_TIME command. 22403cb4473SJacob Keller * 22503cb4473SJacob Keller * The time value is the upper 32 bits of the PHY timer, usually in units of 22603cb4473SJacob Keller * nominal nanoseconds. 22703cb4473SJacob Keller */ 22803cb4473SJacob Keller static int ice_ptp_prep_phy_time_e810(struct ice_hw *hw, u32 time) 22903cb4473SJacob Keller { 23003cb4473SJacob Keller u8 tmr_idx; 231*39b28106SJacob Keller int err; 23203cb4473SJacob Keller 23303cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 234*39b28106SJacob Keller err = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHTIME_0(tmr_idx), 0); 235*39b28106SJacob Keller if (err) { 236*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write SHTIME_0, err %d\n", 237*39b28106SJacob Keller err); 238*39b28106SJacob Keller return err; 23903cb4473SJacob Keller } 24003cb4473SJacob Keller 241*39b28106SJacob Keller err = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHTIME_L(tmr_idx), time); 242*39b28106SJacob Keller if (err) { 243*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write SHTIME_L, err %d\n", 244*39b28106SJacob Keller err); 245*39b28106SJacob Keller return err; 24603cb4473SJacob Keller } 24703cb4473SJacob Keller 24803cb4473SJacob Keller return 0; 24903cb4473SJacob Keller } 25003cb4473SJacob Keller 25103cb4473SJacob Keller /** 25203cb4473SJacob Keller * ice_ptp_prep_phy_adj_e810 - Prep PHY port for a time adjustment 25303cb4473SJacob Keller * @hw: pointer to HW struct 25403cb4473SJacob Keller * @adj: adjustment value to program 25503cb4473SJacob Keller * 25603cb4473SJacob Keller * Prepare the PHY port for an atomic adjustment by programming the PHY 25703cb4473SJacob Keller * ETH_GLTSYN_SHADJ_L and ETH_GLTSYN_SHADJ_H registers. The actual adjustment 25803cb4473SJacob Keller * is completed by issuing an ADJ_TIME sync command. 25903cb4473SJacob Keller * 26003cb4473SJacob Keller * The adjustment value only contains the portion used for the upper 32bits of 26103cb4473SJacob Keller * the PHY timer, usually in units of nominal nanoseconds. Negative 26203cb4473SJacob Keller * adjustments are supported using 2s complement arithmetic. 26303cb4473SJacob Keller */ 26403cb4473SJacob Keller static int ice_ptp_prep_phy_adj_e810(struct ice_hw *hw, s32 adj) 26503cb4473SJacob Keller { 26603cb4473SJacob Keller u8 tmr_idx; 267*39b28106SJacob Keller int err; 26803cb4473SJacob Keller 26903cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 27003cb4473SJacob Keller 27103cb4473SJacob Keller /* Adjustments are represented as signed 2's complement values in 27203cb4473SJacob Keller * nanoseconds. Sub-nanosecond adjustment is not supported. 27303cb4473SJacob Keller */ 274*39b28106SJacob Keller err = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_L(tmr_idx), 0); 275*39b28106SJacob Keller if (err) { 276*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write adj to PHY SHADJ_L, err %d\n", 277*39b28106SJacob Keller err); 278*39b28106SJacob Keller return err; 27903cb4473SJacob Keller } 28003cb4473SJacob Keller 281*39b28106SJacob Keller err = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_H(tmr_idx), adj); 282*39b28106SJacob Keller if (err) { 283*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write adj to PHY SHADJ_H, err %d\n", 284*39b28106SJacob Keller err); 285*39b28106SJacob Keller return err; 28603cb4473SJacob Keller } 28703cb4473SJacob Keller 28803cb4473SJacob Keller return 0; 28903cb4473SJacob Keller } 29003cb4473SJacob Keller 29103cb4473SJacob Keller /** 29203cb4473SJacob Keller * ice_ptp_prep_phy_incval_e810 - Prep PHY port increment value change 29303cb4473SJacob Keller * @hw: pointer to HW struct 29403cb4473SJacob Keller * @incval: The new 40bit increment value to prepare 29503cb4473SJacob Keller * 29603cb4473SJacob Keller * Prepare the PHY port for a new increment value by programming the PHY 29703cb4473SJacob Keller * ETH_GLTSYN_SHADJ_L and ETH_GLTSYN_SHADJ_H registers. The actual change is 29803cb4473SJacob Keller * completed by issuing an INIT_INCVAL command. 29903cb4473SJacob Keller */ 30003cb4473SJacob Keller static int ice_ptp_prep_phy_incval_e810(struct ice_hw *hw, u64 incval) 30103cb4473SJacob Keller { 30203cb4473SJacob Keller u32 high, low; 30303cb4473SJacob Keller u8 tmr_idx; 304*39b28106SJacob Keller int err; 30503cb4473SJacob Keller 30603cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 30703cb4473SJacob Keller low = lower_32_bits(incval); 30803cb4473SJacob Keller high = upper_32_bits(incval); 30903cb4473SJacob Keller 310*39b28106SJacob Keller err = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_L(tmr_idx), low); 311*39b28106SJacob Keller if (err) { 312*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write incval to PHY SHADJ_L, err %d\n", 313*39b28106SJacob Keller err); 314*39b28106SJacob Keller return err; 31503cb4473SJacob Keller } 31603cb4473SJacob Keller 317*39b28106SJacob Keller err = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_H(tmr_idx), high); 318*39b28106SJacob Keller if (err) { 319*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write incval PHY SHADJ_H, err %d\n", 320*39b28106SJacob Keller err); 321*39b28106SJacob Keller return err; 32203cb4473SJacob Keller } 32303cb4473SJacob Keller 32403cb4473SJacob Keller return 0; 32503cb4473SJacob Keller } 32603cb4473SJacob Keller 32703cb4473SJacob Keller /** 32803cb4473SJacob Keller * ice_ptp_port_cmd_e810 - Prepare all external PHYs for a timer command 32903cb4473SJacob Keller * @hw: pointer to HW struct 33003cb4473SJacob Keller * @cmd: Command to be sent to the port 33103cb4473SJacob Keller * 33203cb4473SJacob Keller * Prepare the external PHYs connected to this device for a timer sync 33303cb4473SJacob Keller * command. 33403cb4473SJacob Keller */ 33503cb4473SJacob Keller static int ice_ptp_port_cmd_e810(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd) 33603cb4473SJacob Keller { 33703cb4473SJacob Keller u32 cmd_val, val; 338*39b28106SJacob Keller int err; 33903cb4473SJacob Keller 34003cb4473SJacob Keller switch (cmd) { 34103cb4473SJacob Keller case INIT_TIME: 34203cb4473SJacob Keller cmd_val = GLTSYN_CMD_INIT_TIME; 34303cb4473SJacob Keller break; 34403cb4473SJacob Keller case INIT_INCVAL: 34503cb4473SJacob Keller cmd_val = GLTSYN_CMD_INIT_INCVAL; 34603cb4473SJacob Keller break; 34703cb4473SJacob Keller case ADJ_TIME: 34803cb4473SJacob Keller cmd_val = GLTSYN_CMD_ADJ_TIME; 34903cb4473SJacob Keller break; 35003cb4473SJacob Keller case READ_TIME: 35103cb4473SJacob Keller cmd_val = GLTSYN_CMD_READ_TIME; 35203cb4473SJacob Keller break; 35303cb4473SJacob Keller case ADJ_TIME_AT_TIME: 35403cb4473SJacob Keller cmd_val = GLTSYN_CMD_ADJ_INIT_TIME; 35503cb4473SJacob Keller break; 35603cb4473SJacob Keller } 35703cb4473SJacob Keller 35803cb4473SJacob Keller /* Read, modify, write */ 359*39b28106SJacob Keller err = ice_read_phy_reg_e810(hw, ETH_GLTSYN_CMD, &val); 360*39b28106SJacob Keller if (err) { 361*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to read GLTSYN_CMD, err %d\n", err); 362*39b28106SJacob Keller return err; 36303cb4473SJacob Keller } 36403cb4473SJacob Keller 36503cb4473SJacob Keller /* Modify necessary bits only and perform write */ 36603cb4473SJacob Keller val &= ~TS_CMD_MASK_E810; 36703cb4473SJacob Keller val |= cmd_val; 36803cb4473SJacob Keller 369*39b28106SJacob Keller err = ice_write_phy_reg_e810(hw, ETH_GLTSYN_CMD, val); 370*39b28106SJacob Keller if (err) { 371*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to write back GLTSYN_CMD, err %d\n", err); 372*39b28106SJacob Keller return err; 37303cb4473SJacob Keller } 37403cb4473SJacob Keller 37503cb4473SJacob Keller return 0; 37603cb4473SJacob Keller } 37703cb4473SJacob Keller 37803cb4473SJacob Keller /* Device agnostic functions 37903cb4473SJacob Keller * 38003cb4473SJacob Keller * The following functions implement useful behavior to hide the differences 38103cb4473SJacob Keller * between E810 and other devices. They call the device-specific 38203cb4473SJacob Keller * implementations where necessary. 38303cb4473SJacob Keller * 38403cb4473SJacob Keller * Currently, the driver only supports E810, but future work will enable 38503cb4473SJacob Keller * support for E822-based devices. 38603cb4473SJacob Keller */ 38703cb4473SJacob Keller 38803cb4473SJacob Keller /** 38903cb4473SJacob Keller * ice_ptp_lock - Acquire PTP global semaphore register lock 39003cb4473SJacob Keller * @hw: pointer to the HW struct 39103cb4473SJacob Keller * 39203cb4473SJacob Keller * Acquire the global PTP hardware semaphore lock. Returns true if the lock 39303cb4473SJacob Keller * was acquired, false otherwise. 39403cb4473SJacob Keller * 39503cb4473SJacob Keller * The PFTSYN_SEM register sets the busy bit on read, returning the previous 39603cb4473SJacob Keller * value. If software sees the busy bit cleared, this means that this function 39703cb4473SJacob Keller * acquired the lock (and the busy bit is now set). If software sees the busy 39803cb4473SJacob Keller * bit set, it means that another function acquired the lock. 39903cb4473SJacob Keller * 40003cb4473SJacob Keller * Software must clear the busy bit with a write to release the lock for other 40103cb4473SJacob Keller * functions when done. 40203cb4473SJacob Keller */ 40303cb4473SJacob Keller bool ice_ptp_lock(struct ice_hw *hw) 40403cb4473SJacob Keller { 40503cb4473SJacob Keller u32 hw_lock; 40603cb4473SJacob Keller int i; 40703cb4473SJacob Keller 40803cb4473SJacob Keller #define MAX_TRIES 5 40903cb4473SJacob Keller 41003cb4473SJacob Keller for (i = 0; i < MAX_TRIES; i++) { 41103cb4473SJacob Keller hw_lock = rd32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id)); 41203cb4473SJacob Keller hw_lock = hw_lock & PFTSYN_SEM_BUSY_M; 413587b839dSColin Ian King if (!hw_lock) 414587b839dSColin Ian King break; 415587b839dSColin Ian King 41603cb4473SJacob Keller /* Somebody is holding the lock */ 41703cb4473SJacob Keller usleep_range(10000, 20000); 41803cb4473SJacob Keller } 41903cb4473SJacob Keller 42003cb4473SJacob Keller return !hw_lock; 42103cb4473SJacob Keller } 42203cb4473SJacob Keller 42303cb4473SJacob Keller /** 42403cb4473SJacob Keller * ice_ptp_unlock - Release PTP global semaphore register lock 42503cb4473SJacob Keller * @hw: pointer to the HW struct 42603cb4473SJacob Keller * 42703cb4473SJacob Keller * Release the global PTP hardware semaphore lock. This is done by writing to 42803cb4473SJacob Keller * the PFTSYN_SEM register. 42903cb4473SJacob Keller */ 43003cb4473SJacob Keller void ice_ptp_unlock(struct ice_hw *hw) 43103cb4473SJacob Keller { 43203cb4473SJacob Keller wr32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id), 0); 43303cb4473SJacob Keller } 43403cb4473SJacob Keller 43503cb4473SJacob Keller /** 43603cb4473SJacob Keller * ice_ptp_src_cmd - Prepare source timer for a timer command 43703cb4473SJacob Keller * @hw: pointer to HW structure 43803cb4473SJacob Keller * @cmd: Timer command 43903cb4473SJacob Keller * 44003cb4473SJacob Keller * Prepare the source timer for an upcoming timer sync command. 44103cb4473SJacob Keller */ 44203cb4473SJacob Keller static void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd) 44303cb4473SJacob Keller { 44403cb4473SJacob Keller u32 cmd_val; 44503cb4473SJacob Keller u8 tmr_idx; 44603cb4473SJacob Keller 44703cb4473SJacob Keller tmr_idx = ice_get_ptp_src_clock_index(hw); 44803cb4473SJacob Keller cmd_val = tmr_idx << SEL_CPK_SRC; 44903cb4473SJacob Keller 45003cb4473SJacob Keller switch (cmd) { 45103cb4473SJacob Keller case INIT_TIME: 45203cb4473SJacob Keller cmd_val |= GLTSYN_CMD_INIT_TIME; 45303cb4473SJacob Keller break; 45403cb4473SJacob Keller case INIT_INCVAL: 45503cb4473SJacob Keller cmd_val |= GLTSYN_CMD_INIT_INCVAL; 45603cb4473SJacob Keller break; 45703cb4473SJacob Keller case ADJ_TIME: 45803cb4473SJacob Keller cmd_val |= GLTSYN_CMD_ADJ_TIME; 45903cb4473SJacob Keller break; 46003cb4473SJacob Keller case ADJ_TIME_AT_TIME: 46103cb4473SJacob Keller cmd_val |= GLTSYN_CMD_ADJ_INIT_TIME; 46203cb4473SJacob Keller break; 46303cb4473SJacob Keller case READ_TIME: 46403cb4473SJacob Keller cmd_val |= GLTSYN_CMD_READ_TIME; 46503cb4473SJacob Keller break; 46603cb4473SJacob Keller } 46703cb4473SJacob Keller 46803cb4473SJacob Keller wr32(hw, GLTSYN_CMD, cmd_val); 46903cb4473SJacob Keller } 47003cb4473SJacob Keller 47103cb4473SJacob Keller /** 47203cb4473SJacob Keller * ice_ptp_tmr_cmd - Prepare and trigger a timer sync command 47303cb4473SJacob Keller * @hw: pointer to HW struct 47403cb4473SJacob Keller * @cmd: the command to issue 47503cb4473SJacob Keller * 47603cb4473SJacob Keller * Prepare the source timer and PHY timers and then trigger the requested 47703cb4473SJacob Keller * command. This causes the shadow registers previously written in preparation 47803cb4473SJacob Keller * for the command to be synchronously applied to both the source and PHY 47903cb4473SJacob Keller * timers. 48003cb4473SJacob Keller */ 48103cb4473SJacob Keller static int ice_ptp_tmr_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd) 48203cb4473SJacob Keller { 483*39b28106SJacob Keller int err; 48403cb4473SJacob Keller 48503cb4473SJacob Keller /* First, prepare the source timer */ 48603cb4473SJacob Keller ice_ptp_src_cmd(hw, cmd); 48703cb4473SJacob Keller 48803cb4473SJacob Keller /* Next, prepare the ports */ 489*39b28106SJacob Keller err = ice_ptp_port_cmd_e810(hw, cmd); 490*39b28106SJacob Keller if (err) { 491*39b28106SJacob Keller ice_debug(hw, ICE_DBG_PTP, "Failed to prepare PHY ports for timer command %u, err %d\n", 492*39b28106SJacob Keller cmd, err); 493*39b28106SJacob Keller return err; 49403cb4473SJacob Keller } 49503cb4473SJacob Keller 49603cb4473SJacob Keller /* Write the sync command register to drive both source and PHY timer commands 49703cb4473SJacob Keller * synchronously 49803cb4473SJacob Keller */ 49903cb4473SJacob Keller wr32(hw, GLTSYN_CMD_SYNC, SYNC_EXEC_CMD); 50003cb4473SJacob Keller 50103cb4473SJacob Keller return 0; 50203cb4473SJacob Keller } 50303cb4473SJacob Keller 50403cb4473SJacob Keller /** 50503cb4473SJacob Keller * ice_ptp_init_time - Initialize device time to provided value 50603cb4473SJacob Keller * @hw: pointer to HW struct 50703cb4473SJacob Keller * @time: 64bits of time (GLTSYN_TIME_L and GLTSYN_TIME_H) 50803cb4473SJacob Keller * 50903cb4473SJacob Keller * Initialize the device to the specified time provided. This requires a three 51003cb4473SJacob Keller * step process: 51103cb4473SJacob Keller * 51203cb4473SJacob Keller * 1) write the new init time to the source timer shadow registers 51303cb4473SJacob Keller * 2) write the new init time to the PHY timer shadow registers 51403cb4473SJacob Keller * 3) issue an init_time timer command to synchronously switch both the source 51503cb4473SJacob Keller * and port timers to the new init time value at the next clock cycle. 51603cb4473SJacob Keller */ 51703cb4473SJacob Keller int ice_ptp_init_time(struct ice_hw *hw, u64 time) 51803cb4473SJacob Keller { 51903cb4473SJacob Keller u8 tmr_idx; 520*39b28106SJacob Keller int err; 52103cb4473SJacob Keller 52203cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 52303cb4473SJacob Keller 52403cb4473SJacob Keller /* Source timers */ 52503cb4473SJacob Keller wr32(hw, GLTSYN_SHTIME_L(tmr_idx), lower_32_bits(time)); 52603cb4473SJacob Keller wr32(hw, GLTSYN_SHTIME_H(tmr_idx), upper_32_bits(time)); 52703cb4473SJacob Keller wr32(hw, GLTSYN_SHTIME_0(tmr_idx), 0); 52803cb4473SJacob Keller 52903cb4473SJacob Keller /* PHY timers */ 53003cb4473SJacob Keller /* Fill Rx and Tx ports and send msg to PHY */ 531*39b28106SJacob Keller err = ice_ptp_prep_phy_time_e810(hw, time & 0xFFFFFFFF); 532*39b28106SJacob Keller if (err) 533*39b28106SJacob Keller return err; 53403cb4473SJacob Keller 53503cb4473SJacob Keller return ice_ptp_tmr_cmd(hw, INIT_TIME); 53603cb4473SJacob Keller } 53703cb4473SJacob Keller 53803cb4473SJacob Keller /** 53903cb4473SJacob Keller * ice_ptp_write_incval - Program PHC with new increment value 54003cb4473SJacob Keller * @hw: pointer to HW struct 54103cb4473SJacob Keller * @incval: Source timer increment value per clock cycle 54203cb4473SJacob Keller * 54303cb4473SJacob Keller * Program the PHC with a new increment value. This requires a three-step 54403cb4473SJacob Keller * process: 54503cb4473SJacob Keller * 54603cb4473SJacob Keller * 1) Write the increment value to the source timer shadow registers 54703cb4473SJacob Keller * 2) Write the increment value to the PHY timer shadow registers 54803cb4473SJacob Keller * 3) Issue an INIT_INCVAL timer command to synchronously switch both the 54903cb4473SJacob Keller * source and port timers to the new increment value at the next clock 55003cb4473SJacob Keller * cycle. 55103cb4473SJacob Keller */ 55203cb4473SJacob Keller int ice_ptp_write_incval(struct ice_hw *hw, u64 incval) 55303cb4473SJacob Keller { 55403cb4473SJacob Keller u8 tmr_idx; 555*39b28106SJacob Keller int err; 55603cb4473SJacob Keller 55703cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 55803cb4473SJacob Keller 55903cb4473SJacob Keller /* Shadow Adjust */ 56003cb4473SJacob Keller wr32(hw, GLTSYN_SHADJ_L(tmr_idx), lower_32_bits(incval)); 56103cb4473SJacob Keller wr32(hw, GLTSYN_SHADJ_H(tmr_idx), upper_32_bits(incval)); 56203cb4473SJacob Keller 563*39b28106SJacob Keller err = ice_ptp_prep_phy_incval_e810(hw, incval); 564*39b28106SJacob Keller if (err) 565*39b28106SJacob Keller return err; 56603cb4473SJacob Keller 56703cb4473SJacob Keller return ice_ptp_tmr_cmd(hw, INIT_INCVAL); 56803cb4473SJacob Keller } 56903cb4473SJacob Keller 57003cb4473SJacob Keller /** 57103cb4473SJacob Keller * ice_ptp_write_incval_locked - Program new incval while holding semaphore 57203cb4473SJacob Keller * @hw: pointer to HW struct 57303cb4473SJacob Keller * @incval: Source timer increment value per clock cycle 57403cb4473SJacob Keller * 57503cb4473SJacob Keller * Program a new PHC incval while holding the PTP semaphore. 57603cb4473SJacob Keller */ 57703cb4473SJacob Keller int ice_ptp_write_incval_locked(struct ice_hw *hw, u64 incval) 57803cb4473SJacob Keller { 579*39b28106SJacob Keller int err; 58003cb4473SJacob Keller 58103cb4473SJacob Keller if (!ice_ptp_lock(hw)) 58203cb4473SJacob Keller return -EBUSY; 58303cb4473SJacob Keller 584*39b28106SJacob Keller err = ice_ptp_write_incval(hw, incval); 58503cb4473SJacob Keller 58603cb4473SJacob Keller ice_ptp_unlock(hw); 58703cb4473SJacob Keller 588*39b28106SJacob Keller return err; 58903cb4473SJacob Keller } 59003cb4473SJacob Keller 59103cb4473SJacob Keller /** 59203cb4473SJacob Keller * ice_ptp_adj_clock - Adjust PHC clock time atomically 59303cb4473SJacob Keller * @hw: pointer to HW struct 59403cb4473SJacob Keller * @adj: Adjustment in nanoseconds 59503cb4473SJacob Keller * 59603cb4473SJacob Keller * Perform an atomic adjustment of the PHC time by the specified number of 59703cb4473SJacob Keller * nanoseconds. This requires a three-step process: 59803cb4473SJacob Keller * 59903cb4473SJacob Keller * 1) Write the adjustment to the source timer shadow registers 60003cb4473SJacob Keller * 2) Write the adjustment to the PHY timer shadow registers 60103cb4473SJacob Keller * 3) Issue an ADJ_TIME timer command to synchronously apply the adjustment to 60203cb4473SJacob Keller * both the source and port timers at the next clock cycle. 60303cb4473SJacob Keller */ 60403cb4473SJacob Keller int ice_ptp_adj_clock(struct ice_hw *hw, s32 adj) 60503cb4473SJacob Keller { 60603cb4473SJacob Keller u8 tmr_idx; 607*39b28106SJacob Keller int err; 60803cb4473SJacob Keller 60903cb4473SJacob Keller tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; 61003cb4473SJacob Keller 61103cb4473SJacob Keller /* Write the desired clock adjustment into the GLTSYN_SHADJ register. 61203cb4473SJacob Keller * For an ADJ_TIME command, this set of registers represents the value 61303cb4473SJacob Keller * to add to the clock time. It supports subtraction by interpreting 61403cb4473SJacob Keller * the value as a 2's complement integer. 61503cb4473SJacob Keller */ 61603cb4473SJacob Keller wr32(hw, GLTSYN_SHADJ_L(tmr_idx), 0); 61703cb4473SJacob Keller wr32(hw, GLTSYN_SHADJ_H(tmr_idx), adj); 61803cb4473SJacob Keller 619*39b28106SJacob Keller err = ice_ptp_prep_phy_adj_e810(hw, adj); 620*39b28106SJacob Keller if (err) 621*39b28106SJacob Keller return err; 62203cb4473SJacob Keller 62303cb4473SJacob Keller return ice_ptp_tmr_cmd(hw, ADJ_TIME); 62403cb4473SJacob Keller } 62503cb4473SJacob Keller 62603cb4473SJacob Keller /** 62703cb4473SJacob Keller * ice_read_phy_tstamp - Read a PHY timestamp from the timestamo block 62803cb4473SJacob Keller * @hw: pointer to the HW struct 62903cb4473SJacob Keller * @block: the block to read from 63003cb4473SJacob Keller * @idx: the timestamp index to read 63103cb4473SJacob Keller * @tstamp: on return, the 40bit timestamp value 63203cb4473SJacob Keller * 63303cb4473SJacob Keller * Read a 40bit timestamp value out of the timestamp block. 63403cb4473SJacob Keller */ 63503cb4473SJacob Keller int ice_read_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx, u64 *tstamp) 63603cb4473SJacob Keller { 63703cb4473SJacob Keller return ice_read_phy_tstamp_e810(hw, block, idx, tstamp); 63803cb4473SJacob Keller } 63903cb4473SJacob Keller 64003cb4473SJacob Keller /** 64103cb4473SJacob Keller * ice_clear_phy_tstamp - Clear a timestamp from the timestamp block 64203cb4473SJacob Keller * @hw: pointer to the HW struct 64303cb4473SJacob Keller * @block: the block to read from 64403cb4473SJacob Keller * @idx: the timestamp index to reset 64503cb4473SJacob Keller * 64603cb4473SJacob Keller * Clear a timestamp, resetting its valid bit, from the timestamp block. 64703cb4473SJacob Keller */ 64803cb4473SJacob Keller int ice_clear_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx) 64903cb4473SJacob Keller { 65003cb4473SJacob Keller return ice_clear_phy_tstamp_e810(hw, block, idx); 65103cb4473SJacob Keller } 652885fe693SMaciej Machnikowski 653885fe693SMaciej Machnikowski /* E810T SMA functions 654885fe693SMaciej Machnikowski * 655885fe693SMaciej Machnikowski * The following functions operate specifically on E810T hardware and are used 656885fe693SMaciej Machnikowski * to access the extended GPIOs available. 657885fe693SMaciej Machnikowski */ 658885fe693SMaciej Machnikowski 659885fe693SMaciej Machnikowski /** 660885fe693SMaciej Machnikowski * ice_get_pca9575_handle 661885fe693SMaciej Machnikowski * @hw: pointer to the hw struct 662885fe693SMaciej Machnikowski * @pca9575_handle: GPIO controller's handle 663885fe693SMaciej Machnikowski * 664885fe693SMaciej Machnikowski * Find and return the GPIO controller's handle in the netlist. 665885fe693SMaciej Machnikowski * When found - the value will be cached in the hw structure and following calls 666885fe693SMaciej Machnikowski * will return cached value 667885fe693SMaciej Machnikowski */ 668885fe693SMaciej Machnikowski static int 669885fe693SMaciej Machnikowski ice_get_pca9575_handle(struct ice_hw *hw, u16 *pca9575_handle) 670885fe693SMaciej Machnikowski { 671885fe693SMaciej Machnikowski struct ice_aqc_get_link_topo *cmd; 672885fe693SMaciej Machnikowski struct ice_aq_desc desc; 673885fe693SMaciej Machnikowski int status; 674885fe693SMaciej Machnikowski u8 idx; 675885fe693SMaciej Machnikowski 676885fe693SMaciej Machnikowski /* If handle was read previously return cached value */ 677885fe693SMaciej Machnikowski if (hw->io_expander_handle) { 678885fe693SMaciej Machnikowski *pca9575_handle = hw->io_expander_handle; 679885fe693SMaciej Machnikowski return 0; 680885fe693SMaciej Machnikowski } 681885fe693SMaciej Machnikowski 682885fe693SMaciej Machnikowski /* If handle was not detected read it from the netlist */ 683885fe693SMaciej Machnikowski cmd = &desc.params.get_link_topo; 684885fe693SMaciej Machnikowski ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_link_topo); 685885fe693SMaciej Machnikowski 686885fe693SMaciej Machnikowski /* Set node type to GPIO controller */ 687885fe693SMaciej Machnikowski cmd->addr.topo_params.node_type_ctx = 688885fe693SMaciej Machnikowski (ICE_AQC_LINK_TOPO_NODE_TYPE_M & 689885fe693SMaciej Machnikowski ICE_AQC_LINK_TOPO_NODE_TYPE_GPIO_CTRL); 690885fe693SMaciej Machnikowski 691885fe693SMaciej Machnikowski #define SW_PCA9575_SFP_TOPO_IDX 2 692885fe693SMaciej Machnikowski #define SW_PCA9575_QSFP_TOPO_IDX 1 693885fe693SMaciej Machnikowski 694885fe693SMaciej Machnikowski /* Check if the SW IO expander controlling SMA exists in the netlist. */ 695885fe693SMaciej Machnikowski if (hw->device_id == ICE_DEV_ID_E810C_SFP) 696885fe693SMaciej Machnikowski idx = SW_PCA9575_SFP_TOPO_IDX; 697885fe693SMaciej Machnikowski else if (hw->device_id == ICE_DEV_ID_E810C_QSFP) 698885fe693SMaciej Machnikowski idx = SW_PCA9575_QSFP_TOPO_IDX; 699885fe693SMaciej Machnikowski else 700885fe693SMaciej Machnikowski return -EOPNOTSUPP; 701885fe693SMaciej Machnikowski 702885fe693SMaciej Machnikowski cmd->addr.topo_params.index = idx; 703885fe693SMaciej Machnikowski 704885fe693SMaciej Machnikowski status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL); 705885fe693SMaciej Machnikowski if (status) 706885fe693SMaciej Machnikowski return -EOPNOTSUPP; 707885fe693SMaciej Machnikowski 708885fe693SMaciej Machnikowski /* Verify if we found the right IO expander type */ 709885fe693SMaciej Machnikowski if (desc.params.get_link_topo.node_part_num != 710885fe693SMaciej Machnikowski ICE_AQC_GET_LINK_TOPO_NODE_NR_PCA9575) 711885fe693SMaciej Machnikowski return -EOPNOTSUPP; 712885fe693SMaciej Machnikowski 713885fe693SMaciej Machnikowski /* If present save the handle and return it */ 714885fe693SMaciej Machnikowski hw->io_expander_handle = 715885fe693SMaciej Machnikowski le16_to_cpu(desc.params.get_link_topo.addr.handle); 716885fe693SMaciej Machnikowski *pca9575_handle = hw->io_expander_handle; 717885fe693SMaciej Machnikowski 718885fe693SMaciej Machnikowski return 0; 719885fe693SMaciej Machnikowski } 720885fe693SMaciej Machnikowski 721885fe693SMaciej Machnikowski /** 722885fe693SMaciej Machnikowski * ice_read_sma_ctrl_e810t 723885fe693SMaciej Machnikowski * @hw: pointer to the hw struct 724885fe693SMaciej Machnikowski * @data: pointer to data to be read from the GPIO controller 725885fe693SMaciej Machnikowski * 726885fe693SMaciej Machnikowski * Read the SMA controller state. It is connected to pins 3-7 of Port 1 of the 727885fe693SMaciej Machnikowski * PCA9575 expander, so only bits 3-7 in data are valid. 728885fe693SMaciej Machnikowski */ 729885fe693SMaciej Machnikowski int ice_read_sma_ctrl_e810t(struct ice_hw *hw, u8 *data) 730885fe693SMaciej Machnikowski { 731885fe693SMaciej Machnikowski int status; 732885fe693SMaciej Machnikowski u16 handle; 733885fe693SMaciej Machnikowski u8 i; 734885fe693SMaciej Machnikowski 735885fe693SMaciej Machnikowski status = ice_get_pca9575_handle(hw, &handle); 736885fe693SMaciej Machnikowski if (status) 737885fe693SMaciej Machnikowski return status; 738885fe693SMaciej Machnikowski 739885fe693SMaciej Machnikowski *data = 0; 740885fe693SMaciej Machnikowski 741885fe693SMaciej Machnikowski for (i = ICE_SMA_MIN_BIT_E810T; i <= ICE_SMA_MAX_BIT_E810T; i++) { 742885fe693SMaciej Machnikowski bool pin; 743885fe693SMaciej Machnikowski 744885fe693SMaciej Machnikowski status = ice_aq_get_gpio(hw, handle, i + ICE_PCA9575_P1_OFFSET, 745885fe693SMaciej Machnikowski &pin, NULL); 746885fe693SMaciej Machnikowski if (status) 747885fe693SMaciej Machnikowski break; 748885fe693SMaciej Machnikowski *data |= (u8)(!pin) << i; 749885fe693SMaciej Machnikowski } 750885fe693SMaciej Machnikowski 751885fe693SMaciej Machnikowski return status; 752885fe693SMaciej Machnikowski } 753885fe693SMaciej Machnikowski 754885fe693SMaciej Machnikowski /** 755885fe693SMaciej Machnikowski * ice_write_sma_ctrl_e810t 756885fe693SMaciej Machnikowski * @hw: pointer to the hw struct 757885fe693SMaciej Machnikowski * @data: data to be written to the GPIO controller 758885fe693SMaciej Machnikowski * 759885fe693SMaciej Machnikowski * Write the data to the SMA controller. It is connected to pins 3-7 of Port 1 760885fe693SMaciej Machnikowski * of the PCA9575 expander, so only bits 3-7 in data are valid. 761885fe693SMaciej Machnikowski */ 762885fe693SMaciej Machnikowski int ice_write_sma_ctrl_e810t(struct ice_hw *hw, u8 data) 763885fe693SMaciej Machnikowski { 764885fe693SMaciej Machnikowski int status; 765885fe693SMaciej Machnikowski u16 handle; 766885fe693SMaciej Machnikowski u8 i; 767885fe693SMaciej Machnikowski 768885fe693SMaciej Machnikowski status = ice_get_pca9575_handle(hw, &handle); 769885fe693SMaciej Machnikowski if (status) 770885fe693SMaciej Machnikowski return status; 771885fe693SMaciej Machnikowski 772885fe693SMaciej Machnikowski for (i = ICE_SMA_MIN_BIT_E810T; i <= ICE_SMA_MAX_BIT_E810T; i++) { 773885fe693SMaciej Machnikowski bool pin; 774885fe693SMaciej Machnikowski 775885fe693SMaciej Machnikowski pin = !(data & (1 << i)); 776885fe693SMaciej Machnikowski status = ice_aq_set_gpio(hw, handle, i + ICE_PCA9575_P1_OFFSET, 777885fe693SMaciej Machnikowski pin, NULL); 778885fe693SMaciej Machnikowski if (status) 779885fe693SMaciej Machnikowski break; 780885fe693SMaciej Machnikowski } 781885fe693SMaciej Machnikowski 782885fe693SMaciej Machnikowski return status; 783885fe693SMaciej Machnikowski } 784885fe693SMaciej Machnikowski 785885fe693SMaciej Machnikowski /** 786885fe693SMaciej Machnikowski * ice_is_pca9575_present 787885fe693SMaciej Machnikowski * @hw: pointer to the hw struct 788885fe693SMaciej Machnikowski * 789885fe693SMaciej Machnikowski * Check if the SW IO expander is present in the netlist 790885fe693SMaciej Machnikowski */ 791885fe693SMaciej Machnikowski bool ice_is_pca9575_present(struct ice_hw *hw) 792885fe693SMaciej Machnikowski { 793885fe693SMaciej Machnikowski u16 handle = 0; 794885fe693SMaciej Machnikowski int status; 795885fe693SMaciej Machnikowski 796885fe693SMaciej Machnikowski if (!ice_is_e810t(hw)) 797885fe693SMaciej Machnikowski return false; 798885fe693SMaciej Machnikowski 799885fe693SMaciej Machnikowski status = ice_get_pca9575_handle(hw, &handle); 800885fe693SMaciej Machnikowski 801885fe693SMaciej Machnikowski return !status && handle; 802885fe693SMaciej Machnikowski } 803