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