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