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};
7103cb4473SJacob Keller 	int status;
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 
7803cb4473SJacob Keller 	status = ice_sbq_rw_reg(hw, &msg);
7903cb4473SJacob Keller 	if (status) {
8003cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, status %d\n",
8103cb4473SJacob Keller 			  status);
8203cb4473SJacob Keller 		return status;
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};
10103cb4473SJacob Keller 	int status;
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 
10903cb4473SJacob Keller 	status = ice_sbq_rw_reg(hw, &msg);
11003cb4473SJacob Keller 	if (status) {
11103cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, status %d\n",
11203cb4473SJacob Keller 			  status);
11303cb4473SJacob Keller 		return status;
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;
13303cb4473SJacob Keller 	int status;
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 
13803cb4473SJacob Keller 	status = ice_read_phy_reg_e810(hw, lo_addr, &lo);
13903cb4473SJacob Keller 	if (status) {
14003cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to read low PTP timestamp register, status %d\n",
14103cb4473SJacob Keller 			  status);
14203cb4473SJacob Keller 		return status;
14303cb4473SJacob Keller 	}
14403cb4473SJacob Keller 
14503cb4473SJacob Keller 	status = ice_read_phy_reg_e810(hw, hi_addr, &hi);
14603cb4473SJacob Keller 	if (status) {
14703cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to read high PTP timestamp register, status %d\n",
14803cb4473SJacob Keller 			  status);
14903cb4473SJacob Keller 		return status;
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;
17203cb4473SJacob Keller 	int status;
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 
17703cb4473SJacob Keller 	status = ice_write_phy_reg_e810(hw, lo_addr, 0);
17803cb4473SJacob Keller 	if (status) {
17903cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to clear low PTP timestamp register, status %d\n",
18003cb4473SJacob Keller 			  status);
18103cb4473SJacob Keller 		return status;
18203cb4473SJacob Keller 	}
18303cb4473SJacob Keller 
18403cb4473SJacob Keller 	status = ice_write_phy_reg_e810(hw, hi_addr, 0);
18503cb4473SJacob Keller 	if (status) {
18603cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to clear high PTP timestamp register, status %d\n",
18703cb4473SJacob Keller 			  status);
18803cb4473SJacob Keller 		return status;
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 	int status;
20403cb4473SJacob Keller 	u8 tmr_idx;
20503cb4473SJacob Keller 
20603cb4473SJacob Keller 	tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
20703cb4473SJacob Keller 	status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_ENA(tmr_idx),
20803cb4473SJacob Keller 					GLTSYN_ENA_TSYN_ENA_M);
20903cb4473SJacob Keller 	if (status)
21003cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "PTP failed in ena_phy_time_syn %d\n",
21103cb4473SJacob Keller 			  status);
21203cb4473SJacob Keller 
21303cb4473SJacob Keller 	return status;
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 	int status;
23103cb4473SJacob Keller 	u8 tmr_idx;
23203cb4473SJacob Keller 
23303cb4473SJacob Keller 	tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
23403cb4473SJacob Keller 	status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHTIME_0(tmr_idx), 0);
23503cb4473SJacob Keller 	if (status) {
23603cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to write SHTIME_0, status %d\n",
23703cb4473SJacob Keller 			  status);
23803cb4473SJacob Keller 		return status;
23903cb4473SJacob Keller 	}
24003cb4473SJacob Keller 
24103cb4473SJacob Keller 	status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHTIME_L(tmr_idx), time);
24203cb4473SJacob Keller 	if (status) {
24303cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to write SHTIME_L, status %d\n",
24403cb4473SJacob Keller 			  status);
24503cb4473SJacob Keller 		return status;
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 	int status;
26703cb4473SJacob Keller 	u8 tmr_idx;
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 	 */
27403cb4473SJacob Keller 	status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_L(tmr_idx), 0);
27503cb4473SJacob Keller 	if (status) {
27603cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to write adj to PHY SHADJ_L, status %d\n",
27703cb4473SJacob Keller 			  status);
27803cb4473SJacob Keller 		return status;
27903cb4473SJacob Keller 	}
28003cb4473SJacob Keller 
28103cb4473SJacob Keller 	status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_H(tmr_idx), adj);
28203cb4473SJacob Keller 	if (status) {
28303cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to write adj to PHY SHADJ_H, status %d\n",
28403cb4473SJacob Keller 			  status);
28503cb4473SJacob Keller 		return status;
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 	int status;
30403cb4473SJacob Keller 	u8 tmr_idx;
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 
31003cb4473SJacob Keller 	status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_L(tmr_idx), low);
31103cb4473SJacob Keller 	if (status) {
31203cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to write incval to PHY SHADJ_L, status %d\n",
31303cb4473SJacob Keller 			  status);
31403cb4473SJacob Keller 		return status;
31503cb4473SJacob Keller 	}
31603cb4473SJacob Keller 
31703cb4473SJacob Keller 	status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_SHADJ_H(tmr_idx), high);
31803cb4473SJacob Keller 	if (status) {
31903cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to write incval PHY SHADJ_H, status %d\n",
32003cb4473SJacob Keller 			  status);
32103cb4473SJacob Keller 		return status;
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;
33803cb4473SJacob Keller 	int status;
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 */
35903cb4473SJacob Keller 	status = ice_read_phy_reg_e810(hw, ETH_GLTSYN_CMD, &val);
36003cb4473SJacob Keller 	if (status) {
36103cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to read GLTSYN_CMD, status %d\n", status);
36203cb4473SJacob Keller 		return status;
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 
36903cb4473SJacob Keller 	status = ice_write_phy_reg_e810(hw, ETH_GLTSYN_CMD, val);
37003cb4473SJacob Keller 	if (status) {
37103cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to write back GLTSYN_CMD, status %d\n", status);
37203cb4473SJacob Keller 		return status;
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 {
48303cb4473SJacob Keller 	int status;
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 */
48903cb4473SJacob Keller 	status = ice_ptp_port_cmd_e810(hw, cmd);
49003cb4473SJacob Keller 	if (status) {
49103cb4473SJacob Keller 		ice_debug(hw, ICE_DBG_PTP, "Failed to prepare PHY ports for timer command %u, status %d\n",
49203cb4473SJacob Keller 			  cmd, status);
49303cb4473SJacob Keller 		return status;
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 	int status;
52003cb4473SJacob Keller 	u8 tmr_idx;
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 */
53103cb4473SJacob Keller 	status = ice_ptp_prep_phy_time_e810(hw, time & 0xFFFFFFFF);
53203cb4473SJacob Keller 	if (status)
53303cb4473SJacob Keller 		return status;
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 	int status;
55503cb4473SJacob Keller 	u8 tmr_idx;
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 
56303cb4473SJacob Keller 	status = ice_ptp_prep_phy_incval_e810(hw, incval);
56403cb4473SJacob Keller 	if (status)
56503cb4473SJacob Keller 		return status;
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 {
57903cb4473SJacob Keller 	int status;
58003cb4473SJacob Keller 
58103cb4473SJacob Keller 	if (!ice_ptp_lock(hw))
58203cb4473SJacob Keller 		return -EBUSY;
58303cb4473SJacob Keller 
58403cb4473SJacob Keller 	status = ice_ptp_write_incval(hw, incval);
58503cb4473SJacob Keller 
58603cb4473SJacob Keller 	ice_ptp_unlock(hw);
58703cb4473SJacob Keller 
58803cb4473SJacob Keller 	return status;
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 	int status;
60703cb4473SJacob Keller 	u8 tmr_idx;
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 
61903cb4473SJacob Keller 	status = ice_ptp_prep_phy_adj_e810(hw, adj);
62003cb4473SJacob Keller 	if (status)
62103cb4473SJacob Keller 		return status;
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 }
652*885fe693SMaciej Machnikowski 
653*885fe693SMaciej Machnikowski /* E810T SMA functions
654*885fe693SMaciej Machnikowski  *
655*885fe693SMaciej Machnikowski  * The following functions operate specifically on E810T hardware and are used
656*885fe693SMaciej Machnikowski  * to access the extended GPIOs available.
657*885fe693SMaciej Machnikowski  */
658*885fe693SMaciej Machnikowski 
659*885fe693SMaciej Machnikowski /**
660*885fe693SMaciej Machnikowski  * ice_get_pca9575_handle
661*885fe693SMaciej Machnikowski  * @hw: pointer to the hw struct
662*885fe693SMaciej Machnikowski  * @pca9575_handle: GPIO controller's handle
663*885fe693SMaciej Machnikowski  *
664*885fe693SMaciej Machnikowski  * Find and return the GPIO controller's handle in the netlist.
665*885fe693SMaciej Machnikowski  * When found - the value will be cached in the hw structure and following calls
666*885fe693SMaciej Machnikowski  * will return cached value
667*885fe693SMaciej Machnikowski  */
668*885fe693SMaciej Machnikowski static int
669*885fe693SMaciej Machnikowski ice_get_pca9575_handle(struct ice_hw *hw, u16 *pca9575_handle)
670*885fe693SMaciej Machnikowski {
671*885fe693SMaciej Machnikowski 	struct ice_aqc_get_link_topo *cmd;
672*885fe693SMaciej Machnikowski 	struct ice_aq_desc desc;
673*885fe693SMaciej Machnikowski 	int status;
674*885fe693SMaciej Machnikowski 	u8 idx;
675*885fe693SMaciej Machnikowski 
676*885fe693SMaciej Machnikowski 	/* If handle was read previously return cached value */
677*885fe693SMaciej Machnikowski 	if (hw->io_expander_handle) {
678*885fe693SMaciej Machnikowski 		*pca9575_handle = hw->io_expander_handle;
679*885fe693SMaciej Machnikowski 		return 0;
680*885fe693SMaciej Machnikowski 	}
681*885fe693SMaciej Machnikowski 
682*885fe693SMaciej Machnikowski 	/* If handle was not detected read it from the netlist */
683*885fe693SMaciej Machnikowski 	cmd = &desc.params.get_link_topo;
684*885fe693SMaciej Machnikowski 	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_link_topo);
685*885fe693SMaciej Machnikowski 
686*885fe693SMaciej Machnikowski 	/* Set node type to GPIO controller */
687*885fe693SMaciej Machnikowski 	cmd->addr.topo_params.node_type_ctx =
688*885fe693SMaciej Machnikowski 		(ICE_AQC_LINK_TOPO_NODE_TYPE_M &
689*885fe693SMaciej Machnikowski 		 ICE_AQC_LINK_TOPO_NODE_TYPE_GPIO_CTRL);
690*885fe693SMaciej Machnikowski 
691*885fe693SMaciej Machnikowski #define SW_PCA9575_SFP_TOPO_IDX		2
692*885fe693SMaciej Machnikowski #define SW_PCA9575_QSFP_TOPO_IDX	1
693*885fe693SMaciej Machnikowski 
694*885fe693SMaciej Machnikowski 	/* Check if the SW IO expander controlling SMA exists in the netlist. */
695*885fe693SMaciej Machnikowski 	if (hw->device_id == ICE_DEV_ID_E810C_SFP)
696*885fe693SMaciej Machnikowski 		idx = SW_PCA9575_SFP_TOPO_IDX;
697*885fe693SMaciej Machnikowski 	else if (hw->device_id == ICE_DEV_ID_E810C_QSFP)
698*885fe693SMaciej Machnikowski 		idx = SW_PCA9575_QSFP_TOPO_IDX;
699*885fe693SMaciej Machnikowski 	else
700*885fe693SMaciej Machnikowski 		return -EOPNOTSUPP;
701*885fe693SMaciej Machnikowski 
702*885fe693SMaciej Machnikowski 	cmd->addr.topo_params.index = idx;
703*885fe693SMaciej Machnikowski 
704*885fe693SMaciej Machnikowski 	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
705*885fe693SMaciej Machnikowski 	if (status)
706*885fe693SMaciej Machnikowski 		return -EOPNOTSUPP;
707*885fe693SMaciej Machnikowski 
708*885fe693SMaciej Machnikowski 	/* Verify if we found the right IO expander type */
709*885fe693SMaciej Machnikowski 	if (desc.params.get_link_topo.node_part_num !=
710*885fe693SMaciej Machnikowski 		ICE_AQC_GET_LINK_TOPO_NODE_NR_PCA9575)
711*885fe693SMaciej Machnikowski 		return -EOPNOTSUPP;
712*885fe693SMaciej Machnikowski 
713*885fe693SMaciej Machnikowski 	/* If present save the handle and return it */
714*885fe693SMaciej Machnikowski 	hw->io_expander_handle =
715*885fe693SMaciej Machnikowski 		le16_to_cpu(desc.params.get_link_topo.addr.handle);
716*885fe693SMaciej Machnikowski 	*pca9575_handle = hw->io_expander_handle;
717*885fe693SMaciej Machnikowski 
718*885fe693SMaciej Machnikowski 	return 0;
719*885fe693SMaciej Machnikowski }
720*885fe693SMaciej Machnikowski 
721*885fe693SMaciej Machnikowski /**
722*885fe693SMaciej Machnikowski  * ice_read_sma_ctrl_e810t
723*885fe693SMaciej Machnikowski  * @hw: pointer to the hw struct
724*885fe693SMaciej Machnikowski  * @data: pointer to data to be read from the GPIO controller
725*885fe693SMaciej Machnikowski  *
726*885fe693SMaciej Machnikowski  * Read the SMA controller state. It is connected to pins 3-7 of Port 1 of the
727*885fe693SMaciej Machnikowski  * PCA9575 expander, so only bits 3-7 in data are valid.
728*885fe693SMaciej Machnikowski  */
729*885fe693SMaciej Machnikowski int ice_read_sma_ctrl_e810t(struct ice_hw *hw, u8 *data)
730*885fe693SMaciej Machnikowski {
731*885fe693SMaciej Machnikowski 	int status;
732*885fe693SMaciej Machnikowski 	u16 handle;
733*885fe693SMaciej Machnikowski 	u8 i;
734*885fe693SMaciej Machnikowski 
735*885fe693SMaciej Machnikowski 	status = ice_get_pca9575_handle(hw, &handle);
736*885fe693SMaciej Machnikowski 	if (status)
737*885fe693SMaciej Machnikowski 		return status;
738*885fe693SMaciej Machnikowski 
739*885fe693SMaciej Machnikowski 	*data = 0;
740*885fe693SMaciej Machnikowski 
741*885fe693SMaciej Machnikowski 	for (i = ICE_SMA_MIN_BIT_E810T; i <= ICE_SMA_MAX_BIT_E810T; i++) {
742*885fe693SMaciej Machnikowski 		bool pin;
743*885fe693SMaciej Machnikowski 
744*885fe693SMaciej Machnikowski 		status = ice_aq_get_gpio(hw, handle, i + ICE_PCA9575_P1_OFFSET,
745*885fe693SMaciej Machnikowski 					 &pin, NULL);
746*885fe693SMaciej Machnikowski 		if (status)
747*885fe693SMaciej Machnikowski 			break;
748*885fe693SMaciej Machnikowski 		*data |= (u8)(!pin) << i;
749*885fe693SMaciej Machnikowski 	}
750*885fe693SMaciej Machnikowski 
751*885fe693SMaciej Machnikowski 	return status;
752*885fe693SMaciej Machnikowski }
753*885fe693SMaciej Machnikowski 
754*885fe693SMaciej Machnikowski /**
755*885fe693SMaciej Machnikowski  * ice_write_sma_ctrl_e810t
756*885fe693SMaciej Machnikowski  * @hw: pointer to the hw struct
757*885fe693SMaciej Machnikowski  * @data: data to be written to the GPIO controller
758*885fe693SMaciej Machnikowski  *
759*885fe693SMaciej Machnikowski  * Write the data to the SMA controller. It is connected to pins 3-7 of Port 1
760*885fe693SMaciej Machnikowski  * of the PCA9575 expander, so only bits 3-7 in data are valid.
761*885fe693SMaciej Machnikowski  */
762*885fe693SMaciej Machnikowski int ice_write_sma_ctrl_e810t(struct ice_hw *hw, u8 data)
763*885fe693SMaciej Machnikowski {
764*885fe693SMaciej Machnikowski 	int status;
765*885fe693SMaciej Machnikowski 	u16 handle;
766*885fe693SMaciej Machnikowski 	u8 i;
767*885fe693SMaciej Machnikowski 
768*885fe693SMaciej Machnikowski 	status = ice_get_pca9575_handle(hw, &handle);
769*885fe693SMaciej Machnikowski 	if (status)
770*885fe693SMaciej Machnikowski 		return status;
771*885fe693SMaciej Machnikowski 
772*885fe693SMaciej Machnikowski 	for (i = ICE_SMA_MIN_BIT_E810T; i <= ICE_SMA_MAX_BIT_E810T; i++) {
773*885fe693SMaciej Machnikowski 		bool pin;
774*885fe693SMaciej Machnikowski 
775*885fe693SMaciej Machnikowski 		pin = !(data & (1 << i));
776*885fe693SMaciej Machnikowski 		status = ice_aq_set_gpio(hw, handle, i + ICE_PCA9575_P1_OFFSET,
777*885fe693SMaciej Machnikowski 					 pin, NULL);
778*885fe693SMaciej Machnikowski 		if (status)
779*885fe693SMaciej Machnikowski 			break;
780*885fe693SMaciej Machnikowski 	}
781*885fe693SMaciej Machnikowski 
782*885fe693SMaciej Machnikowski 	return status;
783*885fe693SMaciej Machnikowski }
784*885fe693SMaciej Machnikowski 
785*885fe693SMaciej Machnikowski /**
786*885fe693SMaciej Machnikowski  * ice_is_pca9575_present
787*885fe693SMaciej Machnikowski  * @hw: pointer to the hw struct
788*885fe693SMaciej Machnikowski  *
789*885fe693SMaciej Machnikowski  * Check if the SW IO expander is present in the netlist
790*885fe693SMaciej Machnikowski  */
791*885fe693SMaciej Machnikowski bool ice_is_pca9575_present(struct ice_hw *hw)
792*885fe693SMaciej Machnikowski {
793*885fe693SMaciej Machnikowski 	u16 handle = 0;
794*885fe693SMaciej Machnikowski 	int status;
795*885fe693SMaciej Machnikowski 
796*885fe693SMaciej Machnikowski 	if (!ice_is_e810t(hw))
797*885fe693SMaciej Machnikowski 		return false;
798*885fe693SMaciej Machnikowski 
799*885fe693SMaciej Machnikowski 	status = ice_get_pca9575_handle(hw, &handle);
800*885fe693SMaciej Machnikowski 
801*885fe693SMaciej Machnikowski 	return !status && handle;
802*885fe693SMaciej Machnikowski }
803